Commit 26160504 authored by Daniel García Moreno's avatar Daniel García Moreno

Added pagination to the GepubWidget

I've added a property to set pagination on/off so the webkitwebview will
be shown with or without scroll.

The pagination is done with the column-width css property, setting the
"body" with the widget width and height and the column-width with the
same widget width, so we change the scroll from vertical to horizontal.
Using this combined with "overflow" css property and with the "scrollTo"
javascript function we can control the page showed.

The position in the book we've the chapter number and the the position
inside this chapter, the position is a percentage that we can get and
set with "gepub_widget_(g/s)et_pos". We store a percentage to make it
valid if the widget size changes, the page is not the same, but the
position is always valid.

To make this work well we need to remove margin and padding from the
body tag, because this affects the column-width.

https://bugzilla.gnome.org/show_bug.cgi?id=768002
parent 0da0ba35
......@@ -19,6 +19,7 @@
#include <config.h>
#include <gtk/gtk.h>
#include <JavaScriptCore/JSValueRef.h>
#include "gepub-widget.h"
......@@ -26,6 +27,11 @@ struct _GepubWidget {
WebKitWebView parent;
GepubDoc *doc;
gboolean paginate;
gint chapter_length; // real chapter length
gint chapter_pos; // position in the chapter, a percentage based on chapter_length
gint length;
gint init_chapter_pos;
};
struct _GepubWidgetClass {
......@@ -35,6 +41,10 @@ struct _GepubWidgetClass {
enum {
PROP_0,
PROP_DOC,
PROP_PAGINATE,
PROP_CHAPTER,
PROP_N_CHAPTERS,
PROP_CHAPTER_POS,
NUM_PROPS
};
......@@ -42,6 +52,147 @@ static GParamSpec *properties[NUM_PROPS] = { NULL, };
G_DEFINE_TYPE (GepubWidget, gepub_widget, WEBKIT_TYPE_WEB_VIEW)
static void
scroll_to_chapter_pos (GepubWidget *widget) {
gchar *script = g_strdup_printf("document.querySelector('body').scrollTo(%d, 0)", widget->chapter_pos);
webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (widget), script, NULL, NULL, NULL);
g_free(script);
}
static void
adjust_chapter_pos (GepubWidget *widget)
{
// integer division to make a page start
gint page = widget->chapter_pos / widget->length;
gint next = page + 1;
gint d1 = widget->chapter_pos - (widget->length * page);
gint d2 = (widget->length * next) - widget->chapter_pos;
if (d1 < d2) {
widget->chapter_pos = widget->length * page;
} else {
widget->chapter_pos = widget->length * next;
}
scroll_to_chapter_pos (widget);
}
static void
pagination_initialize_finished (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
WebKitJavascriptResult *js_result;
JSValueRef value;
JSGlobalContextRef context;
GError *error = NULL;
GepubWidget *widget = GEPUB_WIDGET (user_data);
js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error);
if (!js_result) {
g_warning ("Error running javascript: %s", error->message);
g_error_free (error);
return;
}
context = webkit_javascript_result_get_global_context (js_result);
value = webkit_javascript_result_get_value (js_result);
if (JSValueIsNumber (context, value)) {
double n;
n = JSValueToNumber (context, value, NULL);
widget->chapter_length = (int)n;
if (widget->init_chapter_pos) {
widget->chapter_pos = widget->init_chapter_pos * widget->chapter_length / 100;
if (widget->chapter_pos > (widget->chapter_length - widget->length)) {
widget->chapter_pos = (widget->chapter_length - widget->length);
}
widget->init_chapter_pos = 0;
}
if (widget->chapter_pos) {
adjust_chapter_pos (widget);
}
} else {
g_warning ("Error running javascript: unexpected return value");
}
webkit_javascript_result_unref (js_result);
}
static void
get_length_finished (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
WebKitJavascriptResult *js_result;
JSValueRef value;
JSGlobalContextRef context;
GError *error = NULL;
GepubWidget *widget = GEPUB_WIDGET (user_data);
js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error);
if (!js_result) {
g_warning ("Error running javascript: %s", error->message);
g_error_free (error);
return;
}
context = webkit_javascript_result_get_global_context (js_result);
value = webkit_javascript_result_get_value (js_result);
if (JSValueIsNumber (context, value)) {
double n;
n = JSValueToNumber (context, value, NULL);
widget->length = (int)n;
} else {
g_warning ("Error running javascript: unexpected return value");
}
webkit_javascript_result_unref (js_result);
}
static void
reload_length_cb (GtkWidget *widget,
GdkRectangle *allocation,
gpointer user_data)
{
GepubWidget *gwidget = GEPUB_WIDGET (widget);
WebKitWebView *web_view = WEBKIT_WEB_VIEW (widget);
webkit_web_view_run_javascript (web_view,
"window.innerWidth",
NULL, get_length_finished, (gpointer)widget);
if (gwidget->paginate) {
webkit_web_view_run_javascript (web_view,
// TODO: Adjusts to show a little margin at least
"document.querySelector('body').setAttribute('style', '"
"overflow: hidden;"
"column-gap: 0px;"
"margin-left: 0px;"
"margin-right: 0px;"
"padding-left: 0px;"
"padding-right: 0px;"
"');"
"document.querySelector('body').style.columnWidth = window.innerWidth+'px';"
"document.querySelector('body').style.height = window.innerHeight+'px';"
"document.querySelector('body').scrollWidth",
NULL, pagination_initialize_finished, (gpointer)widget);
}
}
static void
docready_cb (WebKitWebView *web_view,
WebKitLoadEvent load_event,
gpointer user_data)
{
GepubWidget *widget = GEPUB_WIDGET (web_view);
if (load_event == WEBKIT_LOAD_FINISHED) {
reload_length_cb (GTK_WIDGET (widget), NULL, NULL);
}
}
static void
resource_callback (WebKitURISchemeRequest *request, gpointer user_data)
{
......@@ -87,6 +238,15 @@ gepub_widget_set_property (GObject *object,
case PROP_DOC:
gepub_widget_set_doc (widget, g_value_get_object (value));
break;
case PROP_PAGINATE:
gepub_widget_set_pagination (widget, g_value_get_boolean (value));
break;
case PROP_CHAPTER:
gepub_doc_set_page (widget->doc, g_value_get_int (value));
break;
case PROP_CHAPTER_POS:
gepub_widget_set_pos (widget, g_value_get_float (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -105,6 +265,18 @@ gepub_widget_get_property (GObject *object,
case PROP_DOC:
g_value_set_object (value, gepub_widget_get_doc (widget));
break;
case PROP_PAGINATE:
g_value_set_boolean (value, widget->paginate);
break;
case PROP_CHAPTER:
g_value_set_int (value, gepub_doc_get_page (widget->doc));
break;
case PROP_N_CHAPTERS:
g_value_set_int (value, gepub_doc_get_n_pages (widget->doc));
break;
case PROP_CHAPTER_POS:
g_value_set_float (value, gepub_widget_get_pos (widget));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -124,6 +296,11 @@ gepub_widget_finalize (GObject *object)
static void
gepub_widget_init (GepubWidget *widget)
{
widget->chapter_length = 0;
widget->paginate = FALSE;
widget->chapter_pos = 0;
widget->length = 0;
widget->init_chapter_pos = 0;
}
static void
......@@ -136,6 +313,8 @@ gepub_widget_constructed (GObject *object)
ctx = webkit_web_view_get_context (WEBKIT_WEB_VIEW (widget));
webkit_web_context_register_uri_scheme (ctx, "epub", resource_callback, widget, NULL);
g_signal_connect (widget, "load-changed", G_CALLBACK (docready_cb), NULL);
g_signal_connect (widget, "size-allocate", G_CALLBACK (reload_length_cb), NULL);
}
static void
......@@ -156,6 +335,34 @@ gepub_widget_class_init (GepubWidgetClass *klass)
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
properties[PROP_PAGINATE] =
g_param_spec_boolean ("paginate",
"paginate",
"If the widget should paginate",
FALSE,
G_PARAM_READWRITE);
properties[PROP_CHAPTER] =
g_param_spec_int ("chapter",
"Current chapter",
"Current chapter in the doc",
-1, G_MAXINT, 0,
G_PARAM_READWRITE);
properties[PROP_N_CHAPTERS] =
g_param_spec_int ("nchapters",
"Number of chapters in the doc",
"Number of chapters in the doc",
-1, G_MAXINT, 0,
G_PARAM_READABLE);
properties[PROP_CHAPTER_POS] =
g_param_spec_float ("chapter_pos",
"Current position in chapter",
"Current position in chapter",
0, 100, 0,
G_PARAM_READWRITE);
g_object_class_install_properties (object_class, NUM_PROPS, properties);
}
......@@ -190,12 +397,15 @@ reload_current_chapter (GepubWidget *widget)
{
GBytes *current;
widget->chapter_length = 0;
widget->chapter_pos = 0;
widget->length = 0;
current = gepub_doc_get_current_with_epub_uris (widget->doc);
webkit_web_view_load_bytes (WEBKIT_WEB_VIEW (widget),
current,
gepub_doc_get_current_mime (widget->doc),
"UTF-8", NULL);
g_bytes_unref (current);
}
......@@ -232,3 +442,178 @@ gepub_widget_set_doc (GepubWidget *widget,
g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_DOC]);
}
/**
* gepub_widget_set_pagination:
* @widget: a #GepubWidget
* @p: true if the widget should paginate
*
* Enable or disable pagination
*/
void
gepub_widget_set_pagination (GepubWidget *widget,
gboolean p)
{
widget->paginate = p;
reload_current_chapter (widget);
}
/**
* gepub_widget_get_n_chapters:
* @widget: a #GepubWidget
*
* Returns: the number of chapters in the document
*/
gint
gepub_widget_get_n_chapters (GepubWidget *widget)
{
g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
return gepub_doc_get_n_pages (widget->doc);
}
/**
* gepub_widget_get_chapter:
* @widget: a #GepubWidget
*
* Returns: the current chapter in the document
*/
gint
gepub_widget_get_chapter (GepubWidget *widget)
{
g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
return gepub_doc_get_page (widget->doc);
}
/**
* gepub_widget_get_chapter_length:
* @widget: a #GepubWidget
*
* Returns: the current chapter length
*/
gint
gepub_widget_get_chapter_length (GepubWidget *widget)
{
g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
return widget->chapter_length;
}
/**
* gepub_widget_set_chapter:
* @widget: a #GepubWidget
*
* Sets the current chapter in the doc
*/
void
gepub_widget_set_chapter (GepubWidget *widget,
gint index)
{
g_return_if_fail (GEPUB_IS_DOC (widget->doc));
return gepub_doc_set_page (widget->doc, index);
}
/**
* gepub_widget_chapter_next:
* @widget: a #GepubWidget
*
* Returns: TRUE on success, FALSE if there's no next chapter
*/
gboolean
gepub_widget_chapter_next (GepubWidget *widget)
{
g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
return gepub_doc_go_next (widget->doc);
}
/**
* gepub_widget_chapter_prev:
* @widget: a #GepubWidget
*
* Returns: TRUE on success, FALSE if there's no prev chapter
*/
gboolean
gepub_widget_chapter_prev (GepubWidget *widget)
{
g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
return gepub_doc_go_prev (widget->doc);
}
/**
* gepub_widget_page_next:
* @widget: a #GepubWidget
*
* Returns: TRUE on success, FALSE if there's no next page
*/
gboolean
gepub_widget_page_next (GepubWidget *widget)
{
g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
widget->chapter_pos = widget->chapter_pos + widget->length;
if (widget->chapter_pos > (widget->chapter_length - widget->length)) {
widget->chapter_pos = (widget->chapter_length - widget->length);
return gepub_doc_go_next (widget->doc);
}
scroll_to_chapter_pos (widget);
g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]);
return TRUE;
}
/**
* gepub_widget_page_prev:
* @widget: a #GepubWidget
*
* Returns: TRUE on success, FALSE if there's no next page
*/
gboolean
gepub_widget_page_prev (GepubWidget *widget)
{
g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
widget->chapter_pos = widget->chapter_pos - widget->length;
if (widget->chapter_pos < 0) {
widget->init_chapter_pos = 100;
return gepub_doc_go_prev (widget->doc);
}
scroll_to_chapter_pos (widget);
g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]);
return TRUE;
}
/**
* gepub_widget_get_pos:
* @widget: a #GepubWidget
*
* Returns: the current position in the chapter
*/
gfloat
gepub_widget_get_pos (GepubWidget *widget)
{
g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
if (!widget->chapter_length) {
return 0;
}
return widget->chapter_pos * 100 / (float)(widget->chapter_length);
}
/**
* gepub_widget_set_pos:
* @widget: a #GepubWidget
*
* Sets the current position in the chapter
*/
void
gepub_widget_set_pos (GepubWidget *widget,
gfloat index)
{
g_return_if_fail (GEPUB_IS_DOC (widget->doc));
widget->chapter_pos = index * widget->chapter_length / 100;
adjust_chapter_pos (widget);
g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]);
}
......@@ -46,6 +46,22 @@ GepubDoc *gepub_widget_get_doc (GepubWidget *wid
void gepub_widget_set_doc (GepubWidget *widget,
GepubDoc *doc);
void gepub_widget_set_pagination (GepubWidget *widget, gboolean p);
gint gepub_widget_get_n_chapters (GepubWidget *widget);
gint gepub_widget_get_chapter (GepubWidget *widget);
gint gepub_widget_get_chapter_length (GepubWidget *widget);
void gepub_widget_set_chapter (GepubWidget *widget,
gint index);
gboolean gepub_widget_chapter_next (GepubWidget *widget);
gboolean gepub_widget_chapter_prev (GepubWidget *widget);
gfloat gepub_widget_get_pos (GepubWidget *widget);
void gepub_widget_set_pos (GepubWidget *widget,
gfloat index);
gboolean gepub_widget_page_next (GepubWidget *widget);
gboolean gepub_widget_page_prev (GepubWidget *widget);
G_END_DECLS
#endif /* __GEPUB_WIDGET_H__ */
......
......@@ -8,6 +8,7 @@ gchar *buf2 = NULL;
gchar *tmpbuf;
GtkTextBuffer *page_buffer;
GtkWidget *PAGE_LABEL;
#define PTEST1(...) printf (__VA_ARGS__)
#define PTEST2(...) buf = g_strdup_printf (__VA_ARGS__);\
......@@ -19,6 +20,14 @@ GtkTextBuffer *page_buffer;
#define TEST(f,arg...) PTEST ("\n### TESTING " #f " ###\n\n"); f (arg); PTEST ("\n\n");
static void
reload_current_chapter (GepubWidget *widget)
{
gchar *txt = g_strdup_printf ("%02.2f", gepub_widget_get_pos (widget));
gtk_label_set_text (GTK_LABEL (PAGE_LABEL), txt);
g_free (txt);
}
void
update_text (GepubDoc *doc)
{
......@@ -69,14 +78,22 @@ void
button_pressed (GtkButton *button, GepubWidget *widget)
{
GepubDoc *doc = gepub_widget_get_doc (widget);
printf("CLICKED %s\n", gtk_button_get_label (button));
if (!strcmp (gtk_button_get_label (button), "prev")) {
if (!strcmp (gtk_button_get_label (button), "< chapter")) {
gepub_doc_go_prev (doc);
} else {
} else if (!strcmp (gtk_button_get_label (button), "chapter >")) {
gepub_doc_go_next (doc);
} else if (!strcmp (gtk_button_get_label (button), "paginated")) {
gboolean b = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
gepub_widget_set_pagination (widget, b);
} else if (!strcmp (gtk_button_get_label (button), "< page")) {
gepub_widget_page_prev (widget);
} else if (!strcmp (gtk_button_get_label (button), "page >")) {
gepub_widget_page_next (widget);
}
update_text (doc);
print_replaced_text (doc);
//print_replaced_text (doc);
}
void
......@@ -245,6 +262,11 @@ main (int argc, char **argv)
GtkWidget *b_next;
GtkWidget *b_prev;
GtkWidget *p_next;
GtkWidget *p_prev;
GtkWidget *paginate;
GtkTextBuffer *buffer;
GepubDoc *doc;
......@@ -258,7 +280,7 @@ main (int argc, char **argv)
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "destroy", (GCallback)gtk_main_quit, NULL);
gtk_widget_set_size_request (GTK_WIDGET (window), 800, 500);
gtk_widget_set_size_request (GTK_WIDGET (window), 1200, 800);
vpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
gtk_container_add (GTK_CONTAINER (window), vpaned);
......@@ -285,12 +307,33 @@ main (int argc, char **argv)
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
b_prev = gtk_button_new_with_label ("prev");
b_prev = gtk_button_new_with_label ("< chapter");
g_signal_connect (b_prev, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget));
b_next = gtk_button_new_with_label ("next");
b_next = gtk_button_new_with_label ("chapter >");
g_signal_connect (b_next, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget));
p_prev = gtk_button_new_with_label ("< page");
g_signal_connect (p_prev, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget));
p_next = gtk_button_new_with_label ("page >");
g_signal_connect (p_next, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget));
PAGE_LABEL = gtk_label_new ("0");
g_signal_connect_swapped (widget, "notify",
G_CALLBACK (reload_current_chapter), widget);
paginate = gtk_check_button_new_with_label ("paginated");
g_signal_connect (paginate, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget));
gtk_container_add (GTK_CONTAINER (hbox), b_prev);
gtk_container_add (GTK_CONTAINER (hbox), b_next);
gtk_container_add (GTK_CONTAINER (hbox), p_prev);
gtk_container_add (GTK_CONTAINER (hbox), p_next);
gtk_container_add (GTK_CONTAINER (hbox), PAGE_LABEL);
gtk_container_add (GTK_CONTAINER (hbox), paginate);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 5);
gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 5);
......@@ -300,7 +343,7 @@ main (int argc, char **argv)
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 5);
gtk_widget_set_size_request (GTK_WIDGET (vbox), 400, 500);
gtk_widget_set_size_request (GTK_WIDGET (vbox), 600, 500);
gtk_paned_add1 (GTK_PANED (vpaned), vbox);
gtk_paned_add2 (GTK_PANED (vpaned), widget);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment