Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libview/Makefile.am6
-rw-r--r--libview/ev-view-presentation.c1415
-rw-r--r--libview/ev-view-presentation.h50
3 files changed, 1469 insertions, 2 deletions
diff --git a/libview/Makefile.am b/libview/Makefile.am
index 95d9f29..b4edc57 100644
--- a/libview/Makefile.am
+++ b/libview/Makefile.am
@@ -15,9 +15,10 @@ INST_H_FILES = \
ev-document-model.h \
ev-jobs.h \
ev-job-scheduler.h \
- ev-print-operation.h \
+ ev-print-operation.h \
ev-stock-icons.h \
ev-view.h \
+ ev-view-presentation.h \
ev-view-type-builtins.h
headerdir = $(includedir)/evince/$(EV_API_VERSION)/libview
@@ -30,7 +31,7 @@ libevview_la_SOURCES = \
ev-job-scheduler.c \
ev-page-cache.c \
ev-pixbuf-cache.c \
- ev-print-operation.c \
+ ev-print-operation.c \
ev-stock-icons.c \
ev-timeline.c \
ev-transition-animation.c \
@@ -38,6 +39,7 @@ libevview_la_SOURCES = \
ev-view-accessible.c \
ev-view-marshal.c \
ev-view-cursor.c \
+ ev-view-presentation.c \
ev-view-type-builtins.c \
$(NOINST_H_FILES) \
$(INST_H_FILES)
diff --git a/libview/ev-view-presentation.c b/libview/ev-view-presentation.c
new file mode 100644
index 0000000..8f91869
--- /dev/null
+++ b/libview/ev-view-presentation.c
@@ -0,0 +1,1415 @@
+/* ev-view-presentation.c
+ * this file is part of evince, a gnome document viewer
+ *
+ * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
+ *
+ * Evince is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Evince is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "ev-view-presentation.h"
+#include "ev-jobs.h"
+#include "ev-job-scheduler.h"
+#include "ev-transition-animation.h"
+#include "ev-view-cursor.h"
+#include "ev-page-cache.h"
+#include "ev-mapping.h"
+
+enum {
+ PROP_0,
+ PROP_DOCUMENT,
+ PROP_CURRENT_PAGE,
+ PROP_ROTATION
+};
+
+enum {
+ CHANGE_PAGE,
+ N_SIGNALS
+};
+
+typedef enum {
+ EV_PRESENTATION_NORMAL,
+ EV_PRESENTATION_BLACK,
+ EV_PRESENTATION_WHITE,
+ EV_PRESENTATION_END
+} EvPresentationState;
+
+struct _EvViewPresentation
+{
+ GtkWidget base;
+
+ guint current_page;
+ EvDocument *document;
+ guint rotation;
+ EvPresentationState state;
+ gdouble scale;
+
+ /* Cursors */
+ EvViewCursor cursor;
+ guint hide_cursor_timeout_id;
+
+ /* Goto Window */
+ GtkWidget *goto_window;
+ GtkWidget *goto_entry;
+
+ /* Page Transition */
+ guint trans_timeout_id;
+
+ /* Animations */
+ gboolean enable_animations;
+ EvTransitionAnimation *animation;
+
+ /* Links */
+ EvPageCache *page_cache;
+
+ EvJob *prev_job;
+ EvJob *curr_job;
+ EvJob *next_job;
+};
+
+struct _EvViewPresentationClass
+{
+ GtkWidgetClass base_class;
+
+ /* signals */
+ void (* change_page) (EvViewPresentation *pview,
+ GtkScrollType scroll);
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+static void ev_view_presentation_next_page (EvViewPresentation *pview);
+static void ev_view_presentation_previous_page (EvViewPresentation *pview);
+static void ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
+ gdouble x,
+ gdouble y);
+
+#define HIDE_CURSOR_TIMEOUT 5
+
+G_DEFINE_TYPE (EvViewPresentation, ev_view_presentation, GTK_TYPE_WIDGET)
+
+static void
+ev_view_presentation_set_normal (EvViewPresentation *pview)
+{
+ GtkWidget *widget = GTK_WIDGET (pview);
+
+ if (pview->state == EV_PRESENTATION_NORMAL)
+ return;
+
+ pview->state = EV_PRESENTATION_NORMAL;
+ gdk_window_set_background (widget->window, &widget->style->black);
+ gtk_widget_queue_draw (widget);
+}
+
+static void
+ev_view_presentation_set_black (EvViewPresentation *pview)
+{
+ GtkWidget *widget = GTK_WIDGET (pview);
+
+ if (pview->state == EV_PRESENTATION_BLACK)
+ return;
+
+ pview->state = EV_PRESENTATION_BLACK;
+ gdk_window_set_background (widget->window, &widget->style->black);
+ gtk_widget_queue_draw (widget);
+}
+
+static void
+ev_view_presentation_set_white (EvViewPresentation *pview)
+{
+ GtkWidget *widget = GTK_WIDGET (pview);
+
+ if (pview->state == EV_PRESENTATION_WHITE)
+ return;
+
+ pview->state = EV_PRESENTATION_WHITE;
+ gdk_window_set_background (widget->window, &widget->style->white);
+ gtk_widget_queue_draw (widget);
+}
+
+static void
+ev_view_presentation_set_end (EvViewPresentation *pview)
+{
+ GtkWidget *widget = GTK_WIDGET (pview);
+
+ if (pview->state == EV_PRESENTATION_END)
+ return;
+
+ pview->state = EV_PRESENTATION_END;
+ gtk_widget_queue_draw (widget);
+}
+
+static gdouble
+ev_view_presentation_get_scale_for_page (EvViewPresentation *pview,
+ guint page)
+{
+ gdouble width, height;
+
+ ev_document_get_page_size (pview->document, page, &width, &height);
+
+ if (pview->rotation == 90 || pview->rotation == 270)
+ return GTK_WIDGET (pview)->allocation.height / width;
+ else
+ return GTK_WIDGET (pview)->allocation.height / height;
+}
+
+static void
+ev_view_presentation_update_scale (EvViewPresentation *pview)
+{
+ if (ev_document_is_page_size_uniform (pview->document) && pview->scale != 0)
+ return;
+
+ pview->scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
+}
+
+static void
+ev_view_presentation_get_page_area (EvViewPresentation *pview,
+ GdkRectangle *area)
+{
+ GtkWidget *widget = GTK_WIDGET (pview);
+ gdouble width, height;
+
+ ev_document_get_page_size (pview->document,
+ pview->current_page,
+ &width, &height);
+
+ if (pview->rotation == 90 || pview->rotation == 270) {
+ gdouble tmp;
+
+ tmp = width;
+ width = height;
+ height = tmp;
+ }
+
+ width *= pview->scale;
+ height *= pview->scale;
+
+ area->x = (MAX (0, widget->allocation.width - width)) / 2;
+ area->y = (MAX (0, widget->allocation.height - height)) / 2;
+ area->width = width;
+ area->height = height;
+}
+
+/* Page Transition */
+static gboolean
+transition_next_page (EvViewPresentation *pview)
+{
+ ev_view_presentation_next_page (pview);
+
+ return FALSE;
+}
+
+static void
+ev_view_presentation_transition_stop (EvViewPresentation *pview)
+{
+ if (pview->trans_timeout_id > 0)
+ g_source_remove (pview->trans_timeout_id);
+ pview->trans_timeout_id = 0;
+}
+
+static void
+ev_view_presentation_transition_start (EvViewPresentation *pview)
+{
+ gdouble duration;
+
+ if (!EV_IS_DOCUMENT_TRANSITION (pview->document))
+ return;
+
+ ev_view_presentation_transition_stop (pview);
+
+ duration = ev_document_transition_get_page_duration (EV_DOCUMENT_TRANSITION (pview->document),
+ pview->current_page);
+ if (duration > 0) {
+ pview->trans_timeout_id =
+ g_timeout_add_seconds (duration,
+ (GSourceFunc) transition_next_page,
+ pview);
+ }
+}
+
+/* Animations */
+static void
+ev_view_presentation_animation_cancel (EvViewPresentation *pview)
+{
+ if (pview->animation) {
+ g_object_unref (pview->animation);
+ pview->animation = NULL;
+ }
+}
+
+static void
+ev_view_presentation_transition_animation_finish (EvViewPresentation *pview)
+{
+ ev_view_presentation_animation_cancel (pview);
+ ev_view_presentation_transition_start (pview);
+ gtk_widget_queue_draw (GTK_WIDGET (pview));
+}
+
+static void
+ev_view_presentation_transition_animation_frame (EvViewPresentation *pview,
+ gdouble progress)
+{
+ gtk_widget_queue_draw (GTK_WIDGET (pview));
+}
+
+static void
+ev_view_presentation_animation_start (EvViewPresentation *pview,
+ gint new_page)
+{
+ EvTransitionEffect *effect = NULL;
+ cairo_surface_t *surface;
+ gint jump;
+
+ if (!pview->enable_animations)
+ return;
+
+ if (pview->current_page == new_page)
+ return;
+
+ effect = ev_document_transition_get_effect (EV_DOCUMENT_TRANSITION (pview->document),
+ new_page);
+ if (!effect)
+ return;
+
+ pview->animation = ev_transition_animation_new (effect);
+
+ surface = EV_JOB_RENDER (pview->curr_job)->surface;
+ ev_transition_animation_set_origin_surface (pview->animation, surface);
+
+ jump = new_page - pview->current_page;
+ if (jump == -1)
+ surface = EV_JOB_RENDER (pview->prev_job)->surface;
+ else if (jump == 1)
+ surface = EV_JOB_RENDER (pview->next_job)->surface;
+ else
+ surface = NULL;
+ if (surface)
+ ev_transition_animation_set_dest_surface (pview->animation, surface);
+
+ g_signal_connect_swapped (pview->animation, "frame",
+ G_CALLBACK (ev_view_presentation_transition_animation_frame),
+ pview);
+ g_signal_connect_swapped (pview->animation, "finished",
+ G_CALLBACK (ev_view_presentation_transition_animation_finish),
+ pview);
+}
+
+/* Page Navigation */
+static void
+job_finished_cb (EvJob *job,
+ EvViewPresentation *pview)
+{
+ EvJobRender *job_render = EV_JOB_RENDER (job);
+
+ if (job != pview->curr_job)
+ return;
+
+ if (pview->animation) {
+ ev_transition_animation_set_dest_surface (pview->animation,
+ job_render->surface);
+ } else {
+ ev_view_presentation_transition_start (pview);
+ gtk_widget_queue_draw (GTK_WIDGET (pview));
+ }
+}
+
+static EvJob *
+ev_view_presentation_schedule_new_job (EvViewPresentation *pview,
+ gint page,
+ EvJobPriority priority)
+{
+ EvJob *job;
+ gdouble scale;
+
+ if (page < 0 || page >= ev_document_get_n_pages (pview->document))
+ return NULL;
+
+ if (ev_document_is_page_size_uniform (pview->document))
+ scale = pview->scale;
+ else
+ scale = ev_view_presentation_get_scale_for_page (pview, page);
+ job = ev_job_render_new (pview->document, page, pview->rotation, pview->scale, 0, 0);
+ g_signal_connect (job, "finished",
+ G_CALLBACK (job_finished_cb),
+ pview);
+ ev_job_scheduler_push_job (job, priority);
+
+ return job;
+}
+
+static void
+ev_view_presentation_delete_job (EvViewPresentation *pview,
+ EvJob *job)
+{
+ if (!job)
+ return;
+
+ g_signal_handlers_disconnect_by_func (job, job_finished_cb, pview);
+ ev_job_cancel (job);
+ g_object_unref (job);
+}
+
+static void
+ev_view_presentation_update_current_page (EvViewPresentation *pview,
+ guint page)
+{
+ gint jump;
+
+ if (page < 0 || page >= ev_document_get_n_pages (pview->document))
+ return;
+
+ ev_view_presentation_animation_cancel (pview);
+ ev_view_presentation_animation_start (pview, page);
+
+ jump = page - pview->current_page;
+
+ switch (jump) {
+ case 0:
+ if (!pview->curr_job)
+ pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
+ if (!pview->next_job)
+ pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
+ if (!pview->prev_job)
+ pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
+ break;
+ case -1:
+ ev_view_presentation_delete_job (pview, pview->next_job);
+ pview->next_job = pview->curr_job;
+ pview->curr_job = pview->prev_job;
+
+ if (!pview->curr_job)
+ pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
+ else
+ ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
+ pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
+ ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
+
+ break;
+ case 1:
+ ev_view_presentation_delete_job (pview, pview->prev_job);
+ pview->prev_job = pview->curr_job;
+ pview->curr_job = pview->next_job;
+
+ if (!pview->curr_job)
+ pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
+ else
+ ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
+ pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
+ ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
+
+ break;
+ case -2:
+ ev_view_presentation_delete_job (pview, pview->next_job);
+ ev_view_presentation_delete_job (pview, pview->curr_job);
+ pview->next_job = pview->prev_job;
+
+ pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
+ pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
+ if (!pview->next_job)
+ pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
+ else
+ ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
+ break;
+ case 2:
+ ev_view_presentation_delete_job (pview, pview->prev_job);
+ ev_view_presentation_delete_job (pview, pview->curr_job);
+ pview->prev_job = pview->next_job;
+
+ pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
+ pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
+ if (!pview->prev_job)
+ pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
+ else
+ ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
+ break;
+ default:
+ ev_view_presentation_delete_job (pview, pview->prev_job);
+ ev_view_presentation_delete_job (pview, pview->curr_job);
+ ev_view_presentation_delete_job (pview, pview->next_job);
+
+ pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
+ if (jump > 0) {
+ pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
+ pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
+ } else {
+ pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
+ pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
+ }
+ }
+
+ pview->current_page = page;
+ ev_view_presentation_update_scale (pview);
+ if (pview->page_cache)
+ ev_page_cache_set_page_range (pview->page_cache, page, page);
+
+ if (pview->cursor != EV_VIEW_CURSOR_HIDDEN) {
+ gint x, y;
+
+ gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
+ ev_view_presentation_set_cursor_for_location (pview, x, y);
+ }
+
+ if (EV_JOB_RENDER (pview->curr_job)->surface)
+ gtk_widget_queue_draw (GTK_WIDGET (pview));
+}
+
+static void
+ev_view_presentation_next_page (EvViewPresentation *pview)
+{
+ guint n_pages;
+ gint new_page;
+
+ switch (pview->state) {
+ case EV_PRESENTATION_BLACK:
+ case EV_PRESENTATION_WHITE:
+ ev_view_presentation_set_normal (pview);
+ case EV_PRESENTATION_END:
+ return;
+ case EV_PRESENTATION_NORMAL:
+ break;
+ }
+
+ n_pages = ev_document_get_n_pages (pview->document);
+ new_page = pview->current_page + 1;
+
+ if (new_page == n_pages)
+ ev_view_presentation_set_end (pview);
+ else
+ ev_view_presentation_update_current_page (pview, new_page);
+}
+
+static void
+ev_view_presentation_previous_page (EvViewPresentation *pview)
+{
+ gint new_page = 0;
+
+ switch (pview->state) {
+ case EV_PRESENTATION_BLACK:
+ case EV_PRESENTATION_WHITE:
+ ev_view_presentation_set_normal (pview);
+ return;
+ case EV_PRESENTATION_END:
+ pview->state = EV_PRESENTATION_NORMAL;
+ new_page = pview->current_page;
+ break;
+ case EV_PRESENTATION_NORMAL:
+ new_page = pview->current_page - 1;
+ break;
+ }
+
+ ev_view_presentation_update_current_page (pview, new_page);
+}
+
+/* Goto Window */
+#define KEY_IS_NUMERIC(keyval) \
+ ((keyval >= GDK_0 && keyval <= GDK_9) || (keyval >= GDK_KP_0 && keyval <= GDK_KP_9))
+
+/* Cut and paste from gtkwindow.c */
+static void
+send_focus_change (GtkWidget *widget,
+ gboolean in)
+{
+ GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
+
+ g_object_ref (widget);
+
+ if (in)
+ GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+ else
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ fevent->focus_change.type = GDK_FOCUS_CHANGE;
+ fevent->focus_change.window = g_object_ref (widget->window);
+ fevent->focus_change.in = in;
+
+ gtk_widget_event (widget, fevent);
+
+ g_object_notify (G_OBJECT (widget), "has-focus");
+
+ g_object_unref (widget);
+ gdk_event_free (fevent);
+}
+
+static void
+ev_view_presentation_goto_window_hide (EvViewPresentation *pview)
+{
+ /* send focus-in event */
+ send_focus_change (pview->goto_entry, FALSE);
+ gtk_widget_hide (pview->goto_window);
+ gtk_entry_set_text (GTK_ENTRY (pview->goto_entry), "");
+}
+
+static gboolean
+ev_view_presentation_goto_window_delete_event (GtkWidget *widget,
+ GdkEventAny *event,
+ EvViewPresentation *pview)
+{
+ ev_view_presentation_goto_window_hide (pview);
+
+ return TRUE;
+}
+
+static gboolean
+ev_view_presentation_goto_window_key_press_event (GtkWidget *widget,
+ GdkEventKey *event,
+ EvViewPresentation *pview)
+{
+ switch (event->keyval) {
+ case GDK_Escape:
+ case GDK_Tab:
+ case GDK_KP_Tab:
+ case GDK_ISO_Left_Tab:
+ ev_view_presentation_goto_window_hide (pview);
+ return TRUE;
+ case GDK_Return:
+ case GDK_KP_Enter:
+ case GDK_ISO_Enter:
+ case GDK_BackSpace:
+ case GDK_Delete:
+ return FALSE;
+ default:
+ if (!KEY_IS_NUMERIC (event->keyval))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+ev_view_presentation_goto_window_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ EvViewPresentation *pview)
+{
+ ev_view_presentation_goto_window_hide (pview);
+
+ return TRUE;
+}
+
+static void
+ev_view_presentation_goto_entry_activate (GtkEntry *entry,
+ EvViewPresentation *pview)
+{
+ const gchar *text;
+ gint page;
+
+ text = gtk_entry_get_text (entry);
+ page = atoi (text) - 1;
+
+ ev_view_presentation_goto_window_hide (pview);
+ ev_view_presentation_update_current_page (pview, page);
+}
+
+static void
+ev_view_presentation_goto_window_create (EvViewPresentation *pview)
+{
+ GtkWidget *frame, *hbox, *toplevel, *label;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (pview));
+
+ if (pview->goto_window) {
+ if (GTK_WINDOW (toplevel)->group)
+ gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
+ GTK_WINDOW (pview->goto_window));
+ else if (GTK_WINDOW (pview->goto_window)->group)
+ gtk_window_group_remove_window (GTK_WINDOW (pview->goto_window)->group,
+ GTK_WINDOW (pview->goto_window));
+ return;
+ }
+
+ pview->goto_window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_screen (GTK_WINDOW (pview->goto_window),
+ gtk_widget_get_screen (GTK_WIDGET (pview)));
+
+ if (GTK_WINDOW (toplevel)->group)
+ gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
+ GTK_WINDOW (pview->goto_window));
+
+ gtk_window_set_modal (GTK_WINDOW (pview->goto_window), TRUE);
+
+ g_signal_connect (pview->goto_window, "delete_event",
+ G_CALLBACK (ev_view_presentation_goto_window_delete_event),
+ pview);
+ g_signal_connect (pview->goto_window, "key_press_event",
+ G_CALLBACK (ev_view_presentation_goto_window_key_press_event),
+ pview);
+ g_signal_connect (pview->goto_window, "button_press_event",
+ G_CALLBACK (ev_view_presentation_goto_window_button_press_event),
+ pview);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
+ gtk_container_add (GTK_CONTAINER (pview->goto_window), frame);
+ gtk_widget_show (frame);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new (_("Jump to page:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 3);
+ gtk_widget_show (label);
+ gtk_widget_realize (label);
+
+ pview->goto_entry = gtk_entry_new ();
+ g_signal_connect (pview->goto_entry, "activate",
+ G_CALLBACK (ev_view_presentation_goto_entry_activate),
+ pview);
+ gtk_box_pack_start (GTK_BOX (hbox), pview->goto_entry, TRUE, TRUE, 0);
+ gtk_widget_show (pview->goto_entry);
+ gtk_widget_realize (pview->goto_entry);
+}
+
+static void
+ev_view_presentation_goto_entry_grab_focus (EvViewPresentation *pview)
+{
+ GtkWidgetClass *entry_parent_class;
+
+ entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (pview->goto_entry));
+ (entry_parent_class->grab_focus) (pview->goto_entry);
+
+ send_focus_change (pview->goto_entry, TRUE);
+}
+
+static void
+ev_view_presentation_goto_window_send_key_event (EvViewPresentation *pview,
+ GdkEvent *event)
+{
+ GdkEventKey *new_event;
+ GdkScreen *screen;
+
+ /* Move goto window off screen */
+ screen = gtk_widget_get_screen (GTK_WIDGET (pview));
+ gtk_window_move (GTK_WINDOW (pview->goto_window),
+ gdk_screen_get_width (screen) + 1,
+ gdk_screen_get_height (screen) + 1);
+ gtk_widget_show (pview->goto_window);
+
+ new_event = (GdkEventKey *) gdk_event_copy (event);
+ g_object_unref (new_event->window);
+ new_event->window = g_object_ref (pview->goto_window->window);
+ gtk_widget_realize (pview->goto_window);
+
+ gtk_widget_event (pview->goto_window, (GdkEvent *)new_event);
+ gdk_event_free ((GdkEvent *)new_event);
+ gtk_widget_hide (pview->goto_window);
+}
+
+/* Links */
+static gboolean
+ev_view_presentation_link_is_supported (EvViewPresentation *pview,
+ EvLink *link)
+{
+ EvLinkAction *action;
+
+ action = ev_link_get_action (link);
+ if (!action)
+ return FALSE;
+
+ switch (ev_link_action_get_action_type (action)) {
+ case EV_LINK_ACTION_TYPE_GOTO_DEST:
+ return ev_link_action_get_dest (action) != NULL;
+ case EV_LINK_ACTION_TYPE_NAMED:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+static EvLink *
+ev_view_presentation_get_link_at_location (EvViewPresentation *pview,
+ gdouble x,
+ gdouble y)
+{
+ GdkRectangle page_area;
+ GList *link_mapping;
+ EvLink *link;
+ gdouble width, height;
+ gdouble new_x, new_y;
+
+ if (!pview->page_cache)
+ return NULL;
+
+ ev_document_get_page_size (pview->document, pview->current_page, &width, &height);
+ ev_view_presentation_get_page_area (pview, &page_area);
+ x = (x - page_area.x) / pview->scale;
+ y = (y - page_area.y) / pview->scale;
+ switch (pview->rotation) {
+ case 0:
+ case 360:
+ new_x = x;
+ new_y = y;
+ break;
+ case 90:
+ new_x = y;
+ new_y = height - x;
+ break;
+ case 180:
+ new_x = width - x;
+ new_y = height - y;
+ break;
+ case 270:
+ new_x = width - y;
+ new_y = x;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ link_mapping = ev_page_cache_get_link_mapping (pview->page_cache, pview->current_page);
+
+ link = link_mapping ? ev_mapping_list_get_data (link_mapping, new_x, new_y) : NULL;
+
+ return link && ev_view_presentation_link_is_supported (pview, link) ? link : NULL;
+}
+
+static void
+ev_vew_presentation_goto_link_dest (EvViewPresentation *pview,
+ EvLink *link)
+{
+ EvLinkAction *action;
+
+ action = ev_link_get_action (link);
+
+ if (ev_link_action_get_action_type (action) == EV_LINK_ACTION_TYPE_NAMED) {
+ const gchar *name = ev_link_action_get_name (action);
+
+ if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
+ ev_view_presentation_update_current_page (pview, 0);
+ } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
+ ev_view_presentation_update_current_page (pview, pview->current_page - 1);
+ } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
+ ev_view_presentation_update_current_page (pview, pview->current_page + 1);
+ } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
+ gint n_pages;
+
+ n_pages = ev_document_get_n_pages (pview->document);
+ ev_view_presentation_update_current_page (pview, n_pages - 1);
+ }
+ } else {
+ EvLinkDest *dest;
+ gint page;
+
+ dest = ev_link_action_get_dest (action);
+ page = ev_document_links_get_dest_page (EV_DOCUMENT_LINKS (pview->document), dest);
+ ev_view_presentation_update_current_page (pview, page);
+ }
+}
+
+/* Cursors */
+static void
+ev_view_presentation_set_cursor (EvViewPresentation *pview,
+ EvViewCursor view_cursor)
+{
+ GtkWidget *widget;
+ GdkCursor *cursor;
+
+ if (pview->cursor == view_cursor)
+ return;
+
+ widget = GTK_WIDGET (pview);
+ if (!GTK_WIDGET_REALIZED (widget))
+ gtk_widget_realize (widget);
+
+ pview->cursor = view_cursor;
+
+ cursor = ev_view_cursor_new (gtk_widget_get_display (widget), view_cursor);
+ gdk_window_set_cursor (widget->window, cursor);
+ gdk_flush ();
+ if (cursor)
+ gdk_cursor_unref (cursor);
+}
+
+static void
+ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
+ gdouble x,
+ gdouble y)
+{
+ if (ev_view_presentation_get_link_at_location (pview, x, y))
+ ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_LINK);
+ else
+ ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_NORMAL);
+}
+
+static gboolean
+hide_cursor_timeout_cb (EvViewPresentation *pview)
+{
+ ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_HIDDEN);
+ pview->hide_cursor_timeout_id = 0;
+
+ return FALSE;
+}
+
+static void
+ev_view_presentation_hide_cursor_timeout_stop (EvViewPresentation *pview)
+{
+ if (pview->hide_cursor_timeout_id > 0)
+ g_source_remove (pview->hide_cursor_timeout_id);
+ pview->hide_cursor_timeout_id = 0;
+}
+
+static void
+ev_view_presentation_hide_cursor_timeout_start (EvViewPresentation *pview)
+{
+ ev_view_presentation_hide_cursor_timeout_stop (pview);
+ pview->hide_cursor_timeout_id =
+ g_timeout_add_seconds (HIDE_CURSOR_TIMEOUT,
+ (GSourceFunc)hide_cursor_timeout_cb,
+ pview);
+}
+
+static void
+ev_view_presentation_destroy (GtkObject *object)
+{
+ EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
+
+ if (pview->document) {
+ g_object_unref (pview->document);
+ pview->document = NULL;
+ }
+
+ ev_view_presentation_animation_cancel (pview);
+ ev_view_presentation_transition_stop (pview);
+ ev_view_presentation_hide_cursor_timeout_stop (pview);
+
+ if (pview->curr_job) {
+ ev_view_presentation_delete_job (pview, pview->curr_job);
+ pview->curr_job = NULL;
+ }
+
+ if (pview->prev_job) {
+ ev_view_presentation_delete_job (pview, pview->prev_job);
+ pview->prev_job = NULL;
+ }
+
+ if (pview->next_job) {
+ ev_view_presentation_delete_job (pview, pview->next_job);
+ pview->next_job = NULL;
+ }
+
+ if (pview->page_cache) {
+ g_object_unref (pview->page_cache);
+ pview->page_cache = NULL;
+ }
+
+ if (pview->goto_window) {
+ gtk_widget_destroy (pview->goto_window);
+ pview->goto_window = NULL;
+ pview->goto_entry = NULL;
+ }
+
+ GTK_OBJECT_CLASS (ev_view_presentation_parent_class)->destroy (object);
+}
+
+static void
+ev_view_presentation_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ requisition->width = 0;
+ requisition->height = 0;
+}
+
+static void
+ev_view_presentation_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
+ GdkScreen *screen = gtk_widget_get_screen (widget);
+
+ allocation->x = 0;
+ allocation->y = 0;
+ allocation->width = gdk_screen_get_width (screen);
+ allocation->height = gdk_screen_get_height (screen);
+
+ GTK_WIDGET_CLASS (ev_view_presentation_parent_class)->size_allocate (widget, allocation);
+
+ ev_view_presentation_update_scale (pview);
+
+ gtk_widget_queue_draw (widget);
+}
+
+static void
+ev_view_presentation_draw_end_page (EvViewPresentation *pview)
+{
+ GtkWidget *widget = GTK_WIDGET (pview);
+ PangoLayout *layout;
+ PangoFontDescription *font_desc;
+ gchar *markup;
+ GdkRectangle area = {0};
+ const gchar *text = _("End of presentation. Press Escape to exit.");
+
+ if (pview->state != EV_PRESENTATION_END)
+ return;
+
+ layout = gtk_widget_create_pango_layout (widget, NULL);
+ markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", text);
+ pango_layout_set_markup (layout, markup, -1);
+ g_free (markup);
+
+ font_desc = pango_font_description_new ();
+ pango_font_description_set_size (font_desc, 16 * PANGO_SCALE);
+ pango_layout_set_font_description (layout, font_desc);
+
+ area.width = widget->allocation.width;
+ area.height = widget->allocation.height;
+
+ gtk_paint_layout (widget->style,
+ widget->window,
+ GTK_WIDGET_STATE (widget),
+ FALSE,
+ &area,
+ widget,
+ NULL,
+ 15,
+ 15,
+ layout);
+
+ pango_font_description_free (font_desc);
+ g_object_unref (layout);
+}
+
+static gboolean
+ev_view_presentation_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
+ GdkRectangle page_area;
+ GdkRectangle overlap;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ switch (pview->state) {
+ case EV_PRESENTATION_END:
+ ev_view_presentation_draw_end_page (pview);
+ return FALSE;
+ case EV_PRESENTATION_BLACK:
+ case EV_PRESENTATION_WHITE:
+ return FALSE;
+ case EV_PRESENTATION_NORMAL:
+ break;
+ }
+
+ if (pview->animation) {
+ if (ev_transition_animation_ready (pview->animation)) {
+ ev_view_presentation_get_page_area (pview, &page_area);
+
+ cr = gdk_cairo_create (widget->window);
+
+ /* normalize to x=0, y=0 */
+ cairo_translate (cr, page_area.x, page_area.y);
+ page_area.x = page_area.y = 0;
+
+ ev_transition_animation_paint (pview->animation, cr, page_area);
+ cairo_destroy (cr);
+ }
+
+ return TRUE;
+ }
+
+ surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
+ if (!surface)
+ return FALSE;
+
+ ev_view_presentation_get_page_area (pview, &page_area);
+ if (gdk_rectangle_intersect (&page_area, &(event->area), &overlap)) {
+ cr = gdk_cairo_create (widget->window);
+
+ cairo_translate (cr, overlap.x, overlap.y);
+ cairo_surface_set_device_offset (surface,
+ overlap.x - page_area.x,
+ overlap.y - page_area.y);
+ cairo_set_source_surface (cr, surface, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+ev_view_presentation_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
+
+ if (pview->state == EV_PRESENTATION_END)
+ return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
+
+ switch (event->keyval) {
+ case GDK_b:
+ case GDK_B:
+ case GDK_period:
+ case GDK_KP_Decimal:
+ if (pview->state == EV_PRESENTATION_BLACK)
+ ev_view_presentation_set_normal (pview);
+ else
+ ev_view_presentation_set_black (pview);
+
+ return TRUE;
+ case GDK_w:
+ case GDK_W:
+ if (pview->state == EV_PRESENTATION_WHITE)
+ ev_view_presentation_set_normal (pview);
+ else
+ ev_view_presentation_set_white (pview);
+
+ return TRUE;
+ default:
+ break;
+ }
+
+ ev_view_presentation_set_normal (pview);
+
+ if (ev_document_get_n_pages (pview->document) > 1 && KEY_IS_NUMERIC (event->keyval)) {
+ gint x, y;
+
+ ev_view_presentation_goto_window_create (pview);
+ ev_view_presentation_goto_window_send_key_event (pview, (GdkEvent *)event);
+ gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
+ gtk_window_move (GTK_WINDOW (pview->goto_window), x, y);
+ gtk_widget_show (pview->goto_window);
+ ev_view_presentation_goto_entry_grab_focus (pview);
+
+ return TRUE;
+ }
+
+ return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
+}
+
+static gboolean
+ev_view_presentation_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
+
+ switch (event->button) {
+ case 1: {
+ EvLink *link;
+
+ link = ev_view_presentation_get_link_at_location (pview,
+ event->x,
+ event->y);
+ if (link)
+ ev_vew_presentation_goto_link_dest (pview, link);
+ else
+ ev_view_presentation_next_page (pview);
+ }
+ break;
+ case 3:
+ ev_view_presentation_previous_page (pview);
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static gint
+ev_view_presentation_focus_out (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
+
+ if (pview->goto_window)
+ ev_view_presentation_goto_window_hide (pview);
+
+ return FALSE;
+}
+
+static gboolean
+ev_view_presentation_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
+
+ ev_view_presentation_hide_cursor_timeout_start (pview);
+ ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
+
+ return FALSE;
+}
+
+static void
+ev_view_presentation_realize (GtkWidget *widget)
+{
+ GdkWindowAttr attributes;
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.event_mask = GDK_EXPOSURE_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_SCROLL_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK;
+
+ widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+ &attributes,
+ GDK_WA_X | GDK_WA_Y |
+ GDK_WA_COLORMAP |
+ GDK_WA_VISUAL);
+ gdk_window_set_user_data (widget->window, widget);
+ widget->style = gtk_style_attach (widget->style, widget->window);
+
+ gdk_window_set_background (widget->window, &widget->style->black);
+
+ gtk_widget_queue_resize (widget);
+}
+
+static void
+ev_view_presentation_change_page (EvViewPresentation *pview,
+ GtkScrollType scroll)
+{
+ switch (scroll) {
+ case GTK_SCROLL_PAGE_FORWARD:
+ ev_view_presentation_next_page (pview);
+ break;
+ case GTK_SCROLL_PAGE_BACKWARD:
+ ev_view_presentation_previous_page (pview);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static gboolean
+ev_view_presentation_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
+ guint state;
+
+ state = event->state & gtk_accelerator_get_default_mod_mask ();
+ if (state != 0)
+ return FALSE;
+
+ switch (event->direction) {
+ case GDK_SCROLL_DOWN:
+ case GDK_SCROLL_RIGHT:
+ ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
+ break;
+ case GDK_SCROLL_UP:
+ case GDK_SCROLL_LEFT:
+ ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
+ break;
+ }
+
+ return TRUE;
+}
+
+
+static void
+add_change_page_binding_keypad (GtkBindingSet *binding_set,
+ guint keyval,
+ GdkModifierType modifiers,
+ GtkScrollType scroll)
+{
+ guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
+
+ gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
+ "change_page", 1,
+ GTK_TYPE_SCROLL_TYPE, scroll);
+ gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
+ "change_page", 1,
+ GTK_TYPE_SCROLL_TYPE, scroll);
+}
+
+static void
+ev_view_presentation_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
+
+ switch (prop_id) {
+ case PROP_DOCUMENT:
+ pview->document = g_value_dup_object (value);
+ pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
+ break;
+ case PROP_CURRENT_PAGE:
+ pview->current_page = g_value_get_uint (value);
+ break;
+ case PROP_ROTATION:
+ pview->rotation = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static GObject *
+ev_view_presentation_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ EvViewPresentation *pview;
+ GtkAllocation a;
+
+ object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_params);
+ pview = EV_VIEW_PRESENTATION (object);
+
+ if (EV_IS_DOCUMENT_LINKS (pview->document)) {
+ pview->page_cache = ev_page_cache_new (pview->document);
+ ev_page_cache_set_flags (pview->page_cache, EV_PAGE_DATA_INCLUDE_LINKS);
+ }
+
+ /* Call allocate asap to update page scale */
+ ev_view_presentation_size_allocate (GTK_WIDGET (pview), &a);
+ ev_view_presentation_update_current_page (pview, pview->current_page);
+ ev_view_presentation_hide_cursor_timeout_start (pview);
+
+ return object;
+}
+
+static void
+ev_view_presentation_class_init (EvViewPresentationClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (klass);
+ GtkBindingSet *binding_set;
+
+ klass->change_page = ev_view_presentation_change_page;
+
+ widget_class->size_allocate = ev_view_presentation_size_allocate;
+ widget_class->size_request = ev_view_presentation_size_request;
+ widget_class->realize = ev_view_presentation_realize;
+ widget_class->expose_event = ev_view_presentation_expose_event;
+ widget_class->key_press_event = ev_view_presentation_key_press_event;
+ widget_class->button_release_event = ev_view_presentation_button_release_event;
+ widget_class->focus_out_event = ev_view_presentation_focus_out;
+ widget_class->motion_notify_event = ev_view_presentation_motion_notify_event;
+ widget_class->scroll_event = ev_view_presentation_scroll_event;
+
+ gtk_object_class->destroy = ev_view_presentation_destroy;
+
+ gobject_class->constructor = ev_view_presentation_constructor;
+ gobject_class->set_property = ev_view_presentation_set_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_DOCUMENT,
+ g_param_spec_object ("document",
+ "Document",
+ "Document",
+ EV_TYPE_DOCUMENT,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (gobject_class,
+ PROP_CURRENT_PAGE,
+ g_param_spec_uint ("current_page",
+ "Current Page",
+ "The current page",
+ 0, G_MAXUINT, 0,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (gobject_class,
+ PROP_ROTATION,
+ g_param_spec_uint ("rotation",
+ "Rotation",
+ "Current rotation angle",
+ 0, 360, 0,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ signals[CHANGE_PAGE] =
+ g_signal_new ("change_page",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__ENUM,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_SCROLL_TYPE);
+
+ binding_set = gtk_binding_set_by_class (klass);
+ add_change_page_binding_keypad (binding_set, GDK_Left, 0, GTK_SCROLL_PAGE_BACKWARD);
+ add_change_page_binding_keypad (binding_set, GDK_Right, 0, GTK_SCROLL_PAGE_FORWARD);
+ add_change_page_binding_keypad (binding_set, GDK_Up, 0, GTK_SCROLL_PAGE_BACKWARD);
+ add_change_page_binding_keypad (binding_set, GDK_Down, 0, GTK_SCROLL_PAGE_FORWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
+ "change_page", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
+ "change_page", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
+ "change_page", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
+ "change_page", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_J, 0,
+ "change_page", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_H, 0,
+ "change_page", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_L, 0,
+ "change_page", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_K, 0,
+ "change_page", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
+}
+
+static void
+ev_view_presentation_init (EvViewPresentation *pview)
+{
+ GTK_WIDGET_SET_FLAGS (pview, GTK_CAN_FOCUS);
+}
+
+GtkWidget *
+ev_view_presentation_new (EvDocument *document,
+ guint current_page,
+ guint rotation)
+{
+ g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
+ g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
+
+ return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
+ "document", document,
+ "current_page", current_page,
+ "rotation", rotation,
+ NULL));
+}
+
+guint
+ev_view_presentation_get_current_page (EvViewPresentation *pview)
+{
+ return pview->current_page;
+}
diff --git a/libview/ev-view-presentation.h b/libview/ev-view-presentation.h
new file mode 100644
index 0000000..9394da6
--- /dev/null
+++ b/libview/ev-view-presentation.h
@@ -0,0 +1,50 @@
+/* ev-view-presentation.h
+ * this file is part of evince, a gnome document viewer
+ *
+ * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
+ *
+ * Evince is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Evince is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__EV_EVINCE_VIEW_H_INSIDE__) && !defined (EVINCE_COMPILATION)
+#error "Only <evince-view.h> can be included directly."
+#endif
+
+#ifndef __EV_VIEW_PRESENTATION_H__
+#define __EV_VIEW_PRESENTATION_H__
+
+#include <gtk/gtk.h>
+
+#include <evince-document.h>
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_VIEW_PRESENTATION (ev_view_presentation_get_type ())
+#define EV_VIEW_PRESENTATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_VIEW_PRESENTATION, EvViewPresentation))
+#define EV_IS_VIEW_PRESENTATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_VIEW_PRESENTATION))
+
+typedef struct _EvViewPresentation EvViewPresentation;
+typedef struct _EvViewPresentationClass EvViewPresentationClass;
+
+GType ev_view_presentation_get_type (void) G_GNUC_CONST;
+
+GtkWidget *ev_view_presentation_new (EvDocument *document,
+ guint current_page,
+ guint rotation);
+guint ev_view_presentation_get_current_page (EvViewPresentation *pview);
+
+G_END_DECLS
+
+#endif /* __EV_VIEW_PRESENTATION_H__ */