]> www.fi.muni.cz Git - evince.git/blob - libview/ev-view-presentation.c
[libview] Add EvViewPresentation to implement presentation mode
[evince.git] / libview / ev-view-presentation.c
1 /* ev-view-presentation.c
2  *  this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
5  *
6  * Evince is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Evince is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <glib/gi18n-lib.h>
25 #include <gdk/gdkkeysyms.h>
26
27 #include "ev-view-presentation.h"
28 #include "ev-jobs.h"
29 #include "ev-job-scheduler.h"
30 #include "ev-transition-animation.h"
31 #include "ev-view-cursor.h"
32 #include "ev-page-cache.h"
33 #include "ev-mapping.h"
34
35 enum {
36         PROP_0,
37         PROP_DOCUMENT,
38         PROP_CURRENT_PAGE,
39         PROP_ROTATION
40 };
41
42 enum {
43         CHANGE_PAGE,
44         N_SIGNALS
45 };
46
47 typedef enum {
48         EV_PRESENTATION_NORMAL,
49         EV_PRESENTATION_BLACK,
50         EV_PRESENTATION_WHITE,
51         EV_PRESENTATION_END
52 } EvPresentationState;
53
54 struct _EvViewPresentation
55 {
56         GtkWidget base;
57
58         guint                  current_page;
59         EvDocument            *document;
60         guint                  rotation;
61         EvPresentationState    state;
62         gdouble                scale;
63
64         /* Cursors */
65         EvViewCursor           cursor;
66         guint                  hide_cursor_timeout_id;
67
68         /* Goto Window */
69         GtkWidget             *goto_window;
70         GtkWidget             *goto_entry;
71
72         /* Page Transition */
73         guint                  trans_timeout_id;
74
75         /* Animations */
76         gboolean               enable_animations;
77         EvTransitionAnimation *animation;
78
79         /* Links */
80         EvPageCache           *page_cache;
81
82         EvJob *prev_job;
83         EvJob *curr_job;
84         EvJob *next_job;
85 };
86
87 struct _EvViewPresentationClass
88 {
89         GtkWidgetClass base_class;
90
91         /* signals */
92         void (* change_page) (EvViewPresentation *pview,
93                               GtkScrollType       scroll);
94 };
95
96 static guint signals[N_SIGNALS] = { 0 };
97
98 static void ev_view_presentation_next_page               (EvViewPresentation *pview);
99 static void ev_view_presentation_previous_page           (EvViewPresentation *pview);
100 static void ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
101                                                           gdouble             x,
102                                                           gdouble             y);
103
104 #define HIDE_CURSOR_TIMEOUT 5
105
106 G_DEFINE_TYPE (EvViewPresentation, ev_view_presentation, GTK_TYPE_WIDGET)
107
108 static void
109 ev_view_presentation_set_normal (EvViewPresentation *pview)
110 {
111         GtkWidget *widget = GTK_WIDGET (pview);
112
113         if (pview->state == EV_PRESENTATION_NORMAL)
114                 return;
115
116         pview->state = EV_PRESENTATION_NORMAL;
117         gdk_window_set_background (widget->window, &widget->style->black);
118         gtk_widget_queue_draw (widget);
119 }
120
121 static void
122 ev_view_presentation_set_black (EvViewPresentation *pview)
123 {
124         GtkWidget *widget = GTK_WIDGET (pview);
125
126         if (pview->state == EV_PRESENTATION_BLACK)
127                 return;
128
129         pview->state = EV_PRESENTATION_BLACK;
130         gdk_window_set_background (widget->window, &widget->style->black);
131         gtk_widget_queue_draw (widget);
132 }
133
134 static void
135 ev_view_presentation_set_white (EvViewPresentation *pview)
136 {
137         GtkWidget *widget = GTK_WIDGET (pview);
138
139         if (pview->state == EV_PRESENTATION_WHITE)
140                 return;
141
142         pview->state = EV_PRESENTATION_WHITE;
143         gdk_window_set_background (widget->window, &widget->style->white);
144         gtk_widget_queue_draw (widget);
145 }
146
147 static void
148 ev_view_presentation_set_end (EvViewPresentation *pview)
149 {
150         GtkWidget *widget = GTK_WIDGET (pview);
151
152         if (pview->state == EV_PRESENTATION_END)
153                 return;
154
155         pview->state = EV_PRESENTATION_END;
156         gtk_widget_queue_draw (widget);
157 }
158
159 static gdouble
160 ev_view_presentation_get_scale_for_page (EvViewPresentation *pview,
161                                          guint               page)
162 {
163         gdouble width, height;
164
165         ev_document_get_page_size (pview->document, page, &width, &height);
166
167         if (pview->rotation == 90 || pview->rotation == 270)
168                 return GTK_WIDGET (pview)->allocation.height / width;
169         else
170                 return GTK_WIDGET (pview)->allocation.height / height;
171 }
172
173 static void
174 ev_view_presentation_update_scale (EvViewPresentation *pview)
175 {
176         if (ev_document_is_page_size_uniform (pview->document) && pview->scale != 0)
177                 return;
178
179         pview->scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
180 }
181
182 static void
183 ev_view_presentation_get_page_area (EvViewPresentation *pview,
184                                     GdkRectangle       *area)
185 {
186         GtkWidget *widget = GTK_WIDGET (pview);
187         gdouble    width, height;
188
189         ev_document_get_page_size (pview->document,
190                                    pview->current_page,
191                                    &width, &height);
192
193         if (pview->rotation == 90 || pview->rotation == 270) {
194                 gdouble tmp;
195
196                 tmp = width;
197                 width = height;
198                 height = tmp;
199         }
200
201         width *= pview->scale;
202         height *= pview->scale;
203
204         area->x = (MAX (0, widget->allocation.width - width)) / 2;
205         area->y = (MAX (0, widget->allocation.height - height)) / 2;
206         area->width = width;
207         area->height = height;
208 }
209
210 /* Page Transition */
211 static gboolean
212 transition_next_page (EvViewPresentation *pview)
213 {
214         ev_view_presentation_next_page (pview);
215
216         return FALSE;
217 }
218
219 static void
220 ev_view_presentation_transition_stop (EvViewPresentation *pview)
221 {
222         if (pview->trans_timeout_id > 0)
223                 g_source_remove (pview->trans_timeout_id);
224         pview->trans_timeout_id = 0;
225 }
226
227 static void
228 ev_view_presentation_transition_start (EvViewPresentation *pview)
229 {
230         gdouble duration;
231
232         if (!EV_IS_DOCUMENT_TRANSITION (pview->document))
233                 return;
234
235         ev_view_presentation_transition_stop (pview);
236
237         duration = ev_document_transition_get_page_duration (EV_DOCUMENT_TRANSITION (pview->document),
238                                                              pview->current_page);
239         if (duration > 0) {
240                         pview->trans_timeout_id =
241                                 g_timeout_add_seconds (duration,
242                                                        (GSourceFunc) transition_next_page,
243                                                        pview);
244         }
245 }
246
247 /* Animations */
248 static void
249 ev_view_presentation_animation_cancel (EvViewPresentation *pview)
250 {
251         if (pview->animation) {
252                 g_object_unref (pview->animation);
253                 pview->animation = NULL;
254         }
255 }
256
257 static void
258 ev_view_presentation_transition_animation_finish (EvViewPresentation *pview)
259 {
260         ev_view_presentation_animation_cancel (pview);
261         ev_view_presentation_transition_start (pview);
262         gtk_widget_queue_draw (GTK_WIDGET (pview));
263 }
264
265 static void
266 ev_view_presentation_transition_animation_frame (EvViewPresentation *pview,
267                                                  gdouble             progress)
268 {
269         gtk_widget_queue_draw (GTK_WIDGET (pview));
270 }
271
272 static void
273 ev_view_presentation_animation_start (EvViewPresentation *pview,
274                                       gint                new_page)
275 {
276         EvTransitionEffect *effect = NULL;
277         cairo_surface_t    *surface;
278         gint                jump;
279
280         if (!pview->enable_animations)
281                 return;
282
283         if (pview->current_page == new_page)
284                 return;
285
286         effect = ev_document_transition_get_effect (EV_DOCUMENT_TRANSITION (pview->document),
287                                                     new_page);
288         if (!effect)
289                 return;
290
291         pview->animation = ev_transition_animation_new (effect);
292
293         surface = EV_JOB_RENDER (pview->curr_job)->surface;
294         ev_transition_animation_set_origin_surface (pview->animation, surface);
295
296         jump = new_page - pview->current_page;
297         if (jump == -1)
298                 surface = EV_JOB_RENDER (pview->prev_job)->surface;
299         else if (jump == 1)
300                 surface = EV_JOB_RENDER (pview->next_job)->surface;
301         else
302                 surface = NULL;
303         if (surface)
304                 ev_transition_animation_set_dest_surface (pview->animation, surface);
305
306         g_signal_connect_swapped (pview->animation, "frame",
307                                   G_CALLBACK (ev_view_presentation_transition_animation_frame),
308                                   pview);
309         g_signal_connect_swapped (pview->animation, "finished",
310                                   G_CALLBACK (ev_view_presentation_transition_animation_finish),
311                                   pview);
312 }
313
314 /* Page Navigation */
315 static void
316 job_finished_cb (EvJob              *job,
317                  EvViewPresentation *pview)
318 {
319         EvJobRender *job_render = EV_JOB_RENDER (job);
320
321         if (job != pview->curr_job)
322                 return;
323
324         if (pview->animation) {
325                 ev_transition_animation_set_dest_surface (pview->animation,
326                                                           job_render->surface);
327         } else {
328                 ev_view_presentation_transition_start (pview);
329                 gtk_widget_queue_draw (GTK_WIDGET (pview));
330         }
331 }
332
333 static EvJob *
334 ev_view_presentation_schedule_new_job (EvViewPresentation *pview,
335                                        gint                page,
336                                        EvJobPriority       priority)
337 {
338         EvJob  *job;
339         gdouble scale;
340
341         if (page < 0 || page >= ev_document_get_n_pages (pview->document))
342                 return NULL;
343
344         if (ev_document_is_page_size_uniform (pview->document))
345                 scale = pview->scale;
346         else
347                 scale = ev_view_presentation_get_scale_for_page (pview, page);
348         job = ev_job_render_new (pview->document, page, pview->rotation, pview->scale, 0, 0);
349         g_signal_connect (job, "finished",
350                           G_CALLBACK (job_finished_cb),
351                           pview);
352         ev_job_scheduler_push_job (job, priority);
353
354         return job;
355 }
356
357 static void
358 ev_view_presentation_delete_job (EvViewPresentation *pview,
359                                  EvJob              *job)
360 {
361         if (!job)
362                 return;
363
364         g_signal_handlers_disconnect_by_func (job, job_finished_cb, pview);
365         ev_job_cancel (job);
366         g_object_unref (job);
367 }
368
369 static void
370 ev_view_presentation_update_current_page (EvViewPresentation *pview,
371                                           guint               page)
372 {
373         gint jump;
374
375         if (page < 0 || page >= ev_document_get_n_pages (pview->document))
376                 return;
377
378         ev_view_presentation_animation_cancel (pview);
379         ev_view_presentation_animation_start (pview, page);
380
381         jump = page - pview->current_page;
382
383         switch (jump) {
384         case 0:
385                 if (!pview->curr_job)
386                         pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
387                 if (!pview->next_job)
388                         pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
389                 if (!pview->prev_job)
390                         pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
391                 break;
392         case -1:
393                 ev_view_presentation_delete_job (pview, pview->next_job);
394                 pview->next_job = pview->curr_job;
395                 pview->curr_job = pview->prev_job;
396
397                 if (!pview->curr_job)
398                         pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
399                 else
400                         ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
401                 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
402                 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
403
404                 break;
405         case 1:
406                 ev_view_presentation_delete_job (pview, pview->prev_job);
407                 pview->prev_job = pview->curr_job;
408                 pview->curr_job = pview->next_job;
409
410                 if (!pview->curr_job)
411                         pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
412                 else
413                         ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
414                 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
415                 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
416
417                 break;
418         case -2:
419                 ev_view_presentation_delete_job (pview, pview->next_job);
420                 ev_view_presentation_delete_job (pview, pview->curr_job);
421                 pview->next_job = pview->prev_job;
422
423                 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
424                 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
425                 if (!pview->next_job)
426                         pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
427                 else
428                         ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
429                 break;
430         case 2:
431                 ev_view_presentation_delete_job (pview, pview->prev_job);
432                 ev_view_presentation_delete_job (pview, pview->curr_job);
433                 pview->prev_job = pview->next_job;
434
435                 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
436                 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
437                 if (!pview->prev_job)
438                         pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
439                 else
440                         ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
441                 break;
442         default:
443                 ev_view_presentation_delete_job (pview, pview->prev_job);
444                 ev_view_presentation_delete_job (pview, pview->curr_job);
445                 ev_view_presentation_delete_job (pview, pview->next_job);
446
447                 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
448                 if (jump > 0) {
449                         pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
450                         pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
451                 } else {
452                         pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
453                         pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
454                 }
455         }
456
457         pview->current_page = page;
458         ev_view_presentation_update_scale (pview);
459         if (pview->page_cache)
460                 ev_page_cache_set_page_range (pview->page_cache, page, page);
461
462         if (pview->cursor != EV_VIEW_CURSOR_HIDDEN) {
463                 gint x, y;
464
465                 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
466                 ev_view_presentation_set_cursor_for_location (pview, x, y);
467         }
468
469         if (EV_JOB_RENDER (pview->curr_job)->surface)
470                 gtk_widget_queue_draw (GTK_WIDGET (pview));
471 }
472
473 static void
474 ev_view_presentation_next_page (EvViewPresentation *pview)
475 {
476         guint n_pages;
477         gint  new_page;
478
479         switch (pview->state) {
480         case EV_PRESENTATION_BLACK:
481         case EV_PRESENTATION_WHITE:
482                 ev_view_presentation_set_normal (pview);
483         case EV_PRESENTATION_END:
484                 return;
485         case EV_PRESENTATION_NORMAL:
486                 break;
487         }
488
489         n_pages = ev_document_get_n_pages (pview->document);
490         new_page = pview->current_page + 1;
491
492         if (new_page == n_pages)
493                 ev_view_presentation_set_end (pview);
494         else
495                 ev_view_presentation_update_current_page (pview, new_page);
496 }
497
498 static void
499 ev_view_presentation_previous_page (EvViewPresentation *pview)
500 {
501         gint new_page = 0;
502
503         switch (pview->state) {
504         case EV_PRESENTATION_BLACK:
505         case EV_PRESENTATION_WHITE:
506                 ev_view_presentation_set_normal (pview);
507                 return;
508         case EV_PRESENTATION_END:
509                 pview->state = EV_PRESENTATION_NORMAL;
510                 new_page = pview->current_page;
511                 break;
512         case EV_PRESENTATION_NORMAL:
513                 new_page = pview->current_page - 1;
514                 break;
515         }
516
517         ev_view_presentation_update_current_page (pview, new_page);
518 }
519
520 /* Goto Window */
521 #define KEY_IS_NUMERIC(keyval) \
522         ((keyval >= GDK_0 && keyval <= GDK_9) || (keyval >= GDK_KP_0 && keyval <= GDK_KP_9))
523
524 /* Cut and paste from gtkwindow.c */
525 static void
526 send_focus_change (GtkWidget *widget,
527                    gboolean   in)
528 {
529         GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
530
531         g_object_ref (widget);
532
533         if (in)
534                 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
535         else
536                 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
537
538         fevent->focus_change.type = GDK_FOCUS_CHANGE;
539         fevent->focus_change.window = g_object_ref (widget->window);
540         fevent->focus_change.in = in;
541
542         gtk_widget_event (widget, fevent);
543
544         g_object_notify (G_OBJECT (widget), "has-focus");
545
546         g_object_unref (widget);
547         gdk_event_free (fevent);
548 }
549
550 static void
551 ev_view_presentation_goto_window_hide (EvViewPresentation *pview)
552 {
553         /* send focus-in event */
554         send_focus_change (pview->goto_entry, FALSE);
555         gtk_widget_hide (pview->goto_window);
556         gtk_entry_set_text (GTK_ENTRY (pview->goto_entry), "");
557 }
558
559 static gboolean
560 ev_view_presentation_goto_window_delete_event (GtkWidget          *widget,
561                                                GdkEventAny        *event,
562                                                EvViewPresentation *pview)
563 {
564         ev_view_presentation_goto_window_hide (pview);
565
566         return TRUE;
567 }
568
569 static gboolean
570 ev_view_presentation_goto_window_key_press_event (GtkWidget          *widget,
571                                                   GdkEventKey        *event,
572                                                   EvViewPresentation *pview)
573 {
574         switch (event->keyval) {
575         case GDK_Escape:
576         case GDK_Tab:
577         case GDK_KP_Tab:
578         case GDK_ISO_Left_Tab:
579                 ev_view_presentation_goto_window_hide (pview);
580                 return TRUE;
581         case GDK_Return:
582         case GDK_KP_Enter:
583         case GDK_ISO_Enter:
584         case GDK_BackSpace:
585         case GDK_Delete:
586                 return FALSE;
587         default:
588                 if (!KEY_IS_NUMERIC (event->keyval))
589                         return TRUE;
590         }
591
592         return FALSE;
593 }
594
595 static gboolean
596 ev_view_presentation_goto_window_button_press_event (GtkWidget          *widget,
597                                                      GdkEventButton     *event,
598                                                      EvViewPresentation *pview)
599 {
600         ev_view_presentation_goto_window_hide (pview);
601
602         return TRUE;
603 }
604
605 static void
606 ev_view_presentation_goto_entry_activate (GtkEntry           *entry,
607                                           EvViewPresentation *pview)
608 {
609         const gchar *text;
610         gint         page;
611
612         text = gtk_entry_get_text (entry);
613         page = atoi (text) - 1;
614
615         ev_view_presentation_goto_window_hide (pview);
616         ev_view_presentation_update_current_page (pview, page);
617 }
618
619 static void
620 ev_view_presentation_goto_window_create (EvViewPresentation *pview)
621 {
622         GtkWidget *frame, *hbox, *toplevel, *label;
623
624         toplevel = gtk_widget_get_toplevel (GTK_WIDGET (pview));
625
626         if (pview->goto_window) {
627                 if (GTK_WINDOW (toplevel)->group)
628                         gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
629                                                      GTK_WINDOW (pview->goto_window));
630                 else if (GTK_WINDOW (pview->goto_window)->group)
631                         gtk_window_group_remove_window (GTK_WINDOW (pview->goto_window)->group,
632                                                         GTK_WINDOW (pview->goto_window));
633                 return;
634         }
635
636         pview->goto_window = gtk_window_new (GTK_WINDOW_POPUP);
637         gtk_window_set_screen (GTK_WINDOW (pview->goto_window),
638                                gtk_widget_get_screen (GTK_WIDGET (pview)));
639
640         if (GTK_WINDOW (toplevel)->group)
641                 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
642                                              GTK_WINDOW (pview->goto_window));
643
644         gtk_window_set_modal (GTK_WINDOW (pview->goto_window), TRUE);
645
646         g_signal_connect (pview->goto_window, "delete_event",
647                           G_CALLBACK (ev_view_presentation_goto_window_delete_event),
648                           pview);
649         g_signal_connect (pview->goto_window, "key_press_event",
650                           G_CALLBACK (ev_view_presentation_goto_window_key_press_event),
651                           pview);
652         g_signal_connect (pview->goto_window, "button_press_event",
653                           G_CALLBACK (ev_view_presentation_goto_window_button_press_event),
654                           pview);
655
656         frame = gtk_frame_new (NULL);
657         gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
658         gtk_container_add (GTK_CONTAINER (pview->goto_window), frame);
659         gtk_widget_show (frame);
660
661         hbox = gtk_hbox_new (FALSE, 0);
662         gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
663         gtk_container_add (GTK_CONTAINER (frame), hbox);
664         gtk_widget_show (hbox);
665
666         label = gtk_label_new (_("Jump to page:"));
667         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 3);
668         gtk_widget_show (label);
669         gtk_widget_realize (label);
670
671         pview->goto_entry = gtk_entry_new ();
672         g_signal_connect (pview->goto_entry, "activate",
673                           G_CALLBACK (ev_view_presentation_goto_entry_activate),
674                           pview);
675         gtk_box_pack_start (GTK_BOX (hbox), pview->goto_entry, TRUE, TRUE, 0);
676         gtk_widget_show (pview->goto_entry);
677         gtk_widget_realize (pview->goto_entry);
678 }
679
680 static void
681 ev_view_presentation_goto_entry_grab_focus (EvViewPresentation *pview)
682 {
683         GtkWidgetClass *entry_parent_class;
684
685         entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (pview->goto_entry));
686         (entry_parent_class->grab_focus) (pview->goto_entry);
687
688         send_focus_change (pview->goto_entry, TRUE);
689 }
690
691 static void
692 ev_view_presentation_goto_window_send_key_event (EvViewPresentation *pview,
693                                                  GdkEvent           *event)
694 {
695         GdkEventKey *new_event;
696         GdkScreen   *screen;
697
698         /* Move goto window off screen */
699         screen = gtk_widget_get_screen (GTK_WIDGET (pview));
700         gtk_window_move (GTK_WINDOW (pview->goto_window),
701                          gdk_screen_get_width (screen) + 1,
702                          gdk_screen_get_height (screen) + 1);
703         gtk_widget_show (pview->goto_window);
704
705         new_event = (GdkEventKey *) gdk_event_copy (event);
706         g_object_unref (new_event->window);
707         new_event->window = g_object_ref (pview->goto_window->window);
708         gtk_widget_realize (pview->goto_window);
709
710         gtk_widget_event (pview->goto_window, (GdkEvent *)new_event);
711         gdk_event_free ((GdkEvent *)new_event);
712         gtk_widget_hide (pview->goto_window);
713 }
714
715 /* Links */
716 static gboolean
717 ev_view_presentation_link_is_supported (EvViewPresentation *pview,
718                                         EvLink             *link)
719 {
720         EvLinkAction *action;
721
722         action = ev_link_get_action (link);
723         if (!action)
724                 return FALSE;
725
726         switch (ev_link_action_get_action_type (action)) {
727         case EV_LINK_ACTION_TYPE_GOTO_DEST:
728                 return ev_link_action_get_dest (action) != NULL;
729         case EV_LINK_ACTION_TYPE_NAMED:
730                 return TRUE;
731         default:
732                 return FALSE;
733         }
734
735         return FALSE;
736 }
737
738 static EvLink *
739 ev_view_presentation_get_link_at_location (EvViewPresentation *pview,
740                                            gdouble             x,
741                                            gdouble             y)
742 {
743         GdkRectangle page_area;
744         GList       *link_mapping;
745         EvLink      *link;
746         gdouble      width, height;
747         gdouble      new_x, new_y;
748
749         if (!pview->page_cache)
750                 return NULL;
751
752         ev_document_get_page_size (pview->document, pview->current_page, &width, &height);
753         ev_view_presentation_get_page_area (pview, &page_area);
754         x = (x - page_area.x) / pview->scale;
755         y = (y - page_area.y) / pview->scale;
756         switch (pview->rotation) {
757         case 0:
758         case 360:
759                 new_x = x;
760                 new_y = y;
761                 break;
762         case 90:
763                 new_x = y;
764                 new_y = height - x;
765                 break;
766         case 180:
767                 new_x = width - x;
768                 new_y = height - y;
769                 break;
770         case 270:
771                 new_x = width - y;
772                 new_y = x;
773                 break;
774         default:
775                 g_assert_not_reached ();
776         }
777
778         link_mapping = ev_page_cache_get_link_mapping (pview->page_cache, pview->current_page);
779
780         link = link_mapping ? ev_mapping_list_get_data (link_mapping, new_x, new_y) : NULL;
781
782         return link && ev_view_presentation_link_is_supported (pview, link) ? link : NULL;
783 }
784
785 static void
786 ev_vew_presentation_goto_link_dest (EvViewPresentation *pview,
787                                     EvLink             *link)
788 {
789         EvLinkAction *action;
790
791         action = ev_link_get_action (link);
792
793         if (ev_link_action_get_action_type (action) == EV_LINK_ACTION_TYPE_NAMED) {
794                 const gchar *name = ev_link_action_get_name (action);
795
796                 if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
797                         ev_view_presentation_update_current_page (pview, 0);
798                 } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
799                         ev_view_presentation_update_current_page (pview, pview->current_page - 1);
800                 } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
801                         ev_view_presentation_update_current_page (pview, pview->current_page + 1);
802                 } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
803                         gint n_pages;
804
805                         n_pages = ev_document_get_n_pages (pview->document);
806                         ev_view_presentation_update_current_page (pview, n_pages - 1);
807                 }
808         } else {
809                 EvLinkDest *dest;
810                 gint        page;
811
812                 dest = ev_link_action_get_dest (action);
813                 page = ev_document_links_get_dest_page (EV_DOCUMENT_LINKS (pview->document), dest);
814                 ev_view_presentation_update_current_page (pview, page);
815         }
816 }
817
818 /* Cursors */
819 static void
820 ev_view_presentation_set_cursor (EvViewPresentation *pview,
821                                  EvViewCursor        view_cursor)
822 {
823         GtkWidget  *widget;
824         GdkCursor  *cursor;
825
826         if (pview->cursor == view_cursor)
827                 return;
828
829         widget = GTK_WIDGET (pview);
830         if (!GTK_WIDGET_REALIZED (widget))
831                 gtk_widget_realize (widget);
832
833         pview->cursor = view_cursor;
834
835         cursor = ev_view_cursor_new (gtk_widget_get_display (widget), view_cursor);
836         gdk_window_set_cursor (widget->window, cursor);
837         gdk_flush ();
838         if (cursor)
839                 gdk_cursor_unref (cursor);
840 }
841
842 static void
843 ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
844                                               gdouble             x,
845                                               gdouble             y)
846 {
847         if (ev_view_presentation_get_link_at_location (pview, x, y))
848                 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_LINK);
849         else
850                 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_NORMAL);
851 }
852
853 static gboolean
854 hide_cursor_timeout_cb (EvViewPresentation *pview)
855 {
856         ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_HIDDEN);
857         pview->hide_cursor_timeout_id = 0;
858
859         return FALSE;
860 }
861
862 static void
863 ev_view_presentation_hide_cursor_timeout_stop (EvViewPresentation *pview)
864 {
865         if (pview->hide_cursor_timeout_id > 0)
866                 g_source_remove (pview->hide_cursor_timeout_id);
867         pview->hide_cursor_timeout_id = 0;
868 }
869
870 static void
871 ev_view_presentation_hide_cursor_timeout_start (EvViewPresentation *pview)
872 {
873         ev_view_presentation_hide_cursor_timeout_stop (pview);
874         pview->hide_cursor_timeout_id =
875                 g_timeout_add_seconds (HIDE_CURSOR_TIMEOUT,
876                                        (GSourceFunc)hide_cursor_timeout_cb,
877                                        pview);
878 }
879
880 static void
881 ev_view_presentation_destroy (GtkObject *object)
882 {
883         EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
884
885         if (pview->document) {
886                 g_object_unref (pview->document);
887                 pview->document = NULL;
888         }
889
890         ev_view_presentation_animation_cancel (pview);
891         ev_view_presentation_transition_stop (pview);
892         ev_view_presentation_hide_cursor_timeout_stop (pview);
893
894         if (pview->curr_job) {
895                 ev_view_presentation_delete_job (pview, pview->curr_job);
896                 pview->curr_job = NULL;
897         }
898
899         if (pview->prev_job) {
900                 ev_view_presentation_delete_job (pview, pview->prev_job);
901                 pview->prev_job = NULL;
902         }
903
904         if (pview->next_job) {
905                 ev_view_presentation_delete_job (pview, pview->next_job);
906                 pview->next_job = NULL;
907         }
908
909         if (pview->page_cache) {
910                 g_object_unref (pview->page_cache);
911                 pview->page_cache = NULL;
912         }
913
914         if (pview->goto_window) {
915                 gtk_widget_destroy (pview->goto_window);
916                 pview->goto_window = NULL;
917                 pview->goto_entry = NULL;
918         }
919
920         GTK_OBJECT_CLASS (ev_view_presentation_parent_class)->destroy (object);
921 }
922
923 static void
924 ev_view_presentation_size_request (GtkWidget      *widget,
925                                    GtkRequisition *requisition)
926 {
927         requisition->width = 0;
928         requisition->height = 0;
929 }
930
931 static void
932 ev_view_presentation_size_allocate (GtkWidget     *widget,
933                                     GtkAllocation *allocation)
934 {
935         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
936         GdkScreen          *screen = gtk_widget_get_screen (widget);
937
938         allocation->x = 0;
939         allocation->y = 0;
940         allocation->width = gdk_screen_get_width (screen);
941         allocation->height = gdk_screen_get_height (screen);
942
943         GTK_WIDGET_CLASS (ev_view_presentation_parent_class)->size_allocate (widget, allocation);
944
945         ev_view_presentation_update_scale (pview);
946
947         gtk_widget_queue_draw (widget);
948 }
949
950 static void
951 ev_view_presentation_draw_end_page (EvViewPresentation *pview)
952 {
953         GtkWidget *widget = GTK_WIDGET (pview);
954         PangoLayout *layout;
955         PangoFontDescription *font_desc;
956         gchar *markup;
957         GdkRectangle area = {0};
958         const gchar *text = _("End of presentation. Press Escape to exit.");
959
960         if (pview->state != EV_PRESENTATION_END)
961                 return;
962
963         layout = gtk_widget_create_pango_layout (widget, NULL);
964         markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", text);
965         pango_layout_set_markup (layout, markup, -1);
966         g_free (markup);
967
968         font_desc = pango_font_description_new ();
969         pango_font_description_set_size (font_desc, 16 * PANGO_SCALE);
970         pango_layout_set_font_description (layout, font_desc);
971
972         area.width = widget->allocation.width;
973         area.height = widget->allocation.height;
974
975         gtk_paint_layout (widget->style,
976                           widget->window,
977                           GTK_WIDGET_STATE (widget),
978                           FALSE,
979                           &area,
980                           widget,
981                           NULL,
982                           15,
983                           15,
984                           layout);
985
986         pango_font_description_free (font_desc);
987         g_object_unref (layout);
988 }
989
990 static gboolean
991 ev_view_presentation_expose_event (GtkWidget      *widget,
992                                    GdkEventExpose *event)
993 {
994         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
995         GdkRectangle        page_area;
996         GdkRectangle        overlap;
997         cairo_surface_t    *surface;
998         cairo_t            *cr;
999
1000         switch (pview->state) {
1001         case EV_PRESENTATION_END:
1002                 ev_view_presentation_draw_end_page (pview);
1003                 return FALSE;
1004         case EV_PRESENTATION_BLACK:
1005         case EV_PRESENTATION_WHITE:
1006                 return FALSE;
1007         case EV_PRESENTATION_NORMAL:
1008                 break;
1009         }
1010
1011         if (pview->animation) {
1012                 if (ev_transition_animation_ready (pview->animation)) {
1013                         ev_view_presentation_get_page_area (pview, &page_area);
1014
1015                         cr = gdk_cairo_create (widget->window);
1016
1017                         /* normalize to x=0, y=0 */
1018                         cairo_translate (cr, page_area.x, page_area.y);
1019                         page_area.x = page_area.y = 0;
1020
1021                         ev_transition_animation_paint (pview->animation, cr, page_area);
1022                         cairo_destroy (cr);
1023                 }
1024
1025                 return TRUE;
1026         }
1027
1028         surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
1029         if (!surface)
1030                 return FALSE;
1031
1032         ev_view_presentation_get_page_area (pview, &page_area);
1033         if (gdk_rectangle_intersect (&page_area, &(event->area), &overlap)) {
1034                 cr = gdk_cairo_create (widget->window);
1035
1036                 cairo_translate (cr, overlap.x, overlap.y);
1037                 cairo_surface_set_device_offset (surface,
1038                                                  overlap.x - page_area.x,
1039                                                  overlap.y - page_area.y);
1040                 cairo_set_source_surface (cr, surface, 0, 0);
1041                 cairo_paint (cr);
1042                 cairo_destroy (cr);
1043         }
1044
1045         return FALSE;
1046 }
1047
1048 static gboolean
1049 ev_view_presentation_key_press_event (GtkWidget   *widget,
1050                                       GdkEventKey *event)
1051 {
1052         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1053
1054         if (pview->state == EV_PRESENTATION_END)
1055                 return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
1056
1057         switch (event->keyval) {
1058         case GDK_b:
1059         case GDK_B:
1060         case GDK_period:
1061         case GDK_KP_Decimal:
1062                 if (pview->state == EV_PRESENTATION_BLACK)
1063                         ev_view_presentation_set_normal (pview);
1064                 else
1065                         ev_view_presentation_set_black (pview);
1066
1067                 return TRUE;
1068         case GDK_w:
1069         case GDK_W:
1070                 if (pview->state == EV_PRESENTATION_WHITE)
1071                         ev_view_presentation_set_normal (pview);
1072                 else
1073                         ev_view_presentation_set_white (pview);
1074
1075                 return TRUE;
1076         default:
1077                 break;
1078         }
1079
1080         ev_view_presentation_set_normal (pview);
1081
1082         if (ev_document_get_n_pages (pview->document) > 1 && KEY_IS_NUMERIC (event->keyval)) {
1083                 gint x, y;
1084
1085                 ev_view_presentation_goto_window_create (pview);
1086                 ev_view_presentation_goto_window_send_key_event (pview, (GdkEvent *)event);
1087                 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
1088                 gtk_window_move (GTK_WINDOW (pview->goto_window), x, y);
1089                 gtk_widget_show (pview->goto_window);
1090                 ev_view_presentation_goto_entry_grab_focus (pview);
1091
1092                 return TRUE;
1093         }
1094
1095         return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
1096 }
1097
1098 static gboolean
1099 ev_view_presentation_button_release_event (GtkWidget      *widget,
1100                                            GdkEventButton *event)
1101 {
1102         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1103
1104         switch (event->button) {
1105         case 1: {
1106                 EvLink *link;
1107
1108                 link = ev_view_presentation_get_link_at_location (pview,
1109                                                                   event->x,
1110                                                                   event->y);
1111                 if (link)
1112                         ev_vew_presentation_goto_link_dest (pview, link);
1113                 else
1114                         ev_view_presentation_next_page (pview);
1115         }
1116                 break;
1117         case 3:
1118                 ev_view_presentation_previous_page (pview);
1119                 break;
1120         default:
1121                 break;
1122         }
1123
1124         return FALSE;
1125 }
1126
1127 static gint
1128 ev_view_presentation_focus_out (GtkWidget     *widget,
1129                                 GdkEventFocus *event)
1130 {
1131         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1132
1133         if (pview->goto_window)
1134                 ev_view_presentation_goto_window_hide (pview);
1135
1136         return FALSE;
1137 }
1138
1139 static gboolean
1140 ev_view_presentation_motion_notify_event (GtkWidget      *widget,
1141                                           GdkEventMotion *event)
1142 {
1143         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1144
1145         ev_view_presentation_hide_cursor_timeout_start (pview);
1146         ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
1147
1148         return FALSE;
1149 }
1150
1151 static void
1152 ev_view_presentation_realize (GtkWidget *widget)
1153 {
1154         GdkWindowAttr attributes;
1155
1156         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1157
1158         attributes.window_type = GDK_WINDOW_CHILD;
1159         attributes.wclass = GDK_INPUT_OUTPUT;
1160         attributes.visual = gtk_widget_get_visual (widget);
1161         attributes.colormap = gtk_widget_get_colormap (widget);
1162
1163         attributes.x = widget->allocation.x;
1164         attributes.y = widget->allocation.y;
1165         attributes.width = widget->allocation.width;
1166         attributes.height = widget->allocation.height;
1167         attributes.event_mask = GDK_EXPOSURE_MASK |
1168                 GDK_BUTTON_PRESS_MASK |
1169                 GDK_BUTTON_RELEASE_MASK |
1170                 GDK_SCROLL_MASK |
1171                 GDK_KEY_PRESS_MASK |
1172                 GDK_POINTER_MOTION_MASK |
1173                 GDK_POINTER_MOTION_HINT_MASK |
1174                                 GDK_ENTER_NOTIFY_MASK |
1175                 GDK_LEAVE_NOTIFY_MASK;
1176
1177         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1178                                          &attributes,
1179                                          GDK_WA_X | GDK_WA_Y |
1180                                          GDK_WA_COLORMAP |
1181                                          GDK_WA_VISUAL);
1182         gdk_window_set_user_data (widget->window, widget);
1183         widget->style = gtk_style_attach (widget->style, widget->window);
1184
1185         gdk_window_set_background (widget->window, &widget->style->black);
1186
1187         gtk_widget_queue_resize (widget);
1188 }
1189
1190 static void
1191 ev_view_presentation_change_page (EvViewPresentation *pview,
1192                                   GtkScrollType       scroll)
1193 {
1194         switch (scroll) {
1195         case GTK_SCROLL_PAGE_FORWARD:
1196                 ev_view_presentation_next_page (pview);
1197                 break;
1198         case GTK_SCROLL_PAGE_BACKWARD:
1199                 ev_view_presentation_previous_page (pview);
1200                 break;
1201         default:
1202                 g_assert_not_reached ();
1203         }
1204 }
1205
1206 static gboolean
1207 ev_view_presentation_scroll_event (GtkWidget      *widget,
1208                                    GdkEventScroll *event)
1209 {
1210         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1211         guint               state;
1212
1213         state = event->state & gtk_accelerator_get_default_mod_mask ();
1214         if (state != 0)
1215                 return FALSE;
1216
1217         switch (event->direction) {
1218         case GDK_SCROLL_DOWN:
1219         case GDK_SCROLL_RIGHT:
1220                 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
1221                 break;
1222         case GDK_SCROLL_UP:
1223         case GDK_SCROLL_LEFT:
1224                 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1225                 break;
1226         }
1227
1228         return TRUE;
1229 }
1230
1231
1232 static void
1233 add_change_page_binding_keypad (GtkBindingSet  *binding_set,
1234                                 guint           keyval,
1235                                 GdkModifierType modifiers,
1236                                 GtkScrollType   scroll)
1237 {
1238         guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
1239
1240         gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1241                                       "change_page", 1,
1242                                       GTK_TYPE_SCROLL_TYPE, scroll);
1243         gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1244                                       "change_page", 1,
1245                                       GTK_TYPE_SCROLL_TYPE, scroll);
1246 }
1247
1248 static void
1249 ev_view_presentation_set_property (GObject      *object,
1250                                    guint         prop_id,
1251                                    const GValue *value,
1252                                    GParamSpec   *pspec)
1253 {
1254         EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1255
1256         switch (prop_id) {
1257         case PROP_DOCUMENT:
1258                 pview->document = g_value_dup_object (value);
1259                 pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
1260                 break;
1261         case PROP_CURRENT_PAGE:
1262                 pview->current_page = g_value_get_uint (value);
1263                 break;
1264         case PROP_ROTATION:
1265                 pview->rotation = g_value_get_uint (value);
1266                 break;
1267         default:
1268                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1269         }
1270 }
1271
1272 static GObject *
1273 ev_view_presentation_constructor (GType                  type,
1274                                   guint                  n_construct_properties,
1275                                   GObjectConstructParam *construct_params)
1276 {
1277         GObject            *object;
1278         EvViewPresentation *pview;
1279         GtkAllocation       a;
1280
1281         object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1282                                                                                   n_construct_properties,
1283                                                                                   construct_params);
1284         pview = EV_VIEW_PRESENTATION (object);
1285
1286         if (EV_IS_DOCUMENT_LINKS (pview->document)) {
1287                 pview->page_cache = ev_page_cache_new (pview->document);
1288                 ev_page_cache_set_flags (pview->page_cache, EV_PAGE_DATA_INCLUDE_LINKS);
1289         }
1290
1291         /* Call allocate asap to update page scale */
1292         ev_view_presentation_size_allocate (GTK_WIDGET (pview), &a);
1293         ev_view_presentation_update_current_page (pview, pview->current_page);
1294         ev_view_presentation_hide_cursor_timeout_start (pview);
1295
1296         return object;
1297 }
1298
1299 static void
1300 ev_view_presentation_class_init (EvViewPresentationClass *klass)
1301 {
1302         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1303         GObjectClass   *gobject_class = G_OBJECT_CLASS (klass);
1304         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (klass);
1305         GtkBindingSet  *binding_set;
1306
1307         klass->change_page = ev_view_presentation_change_page;
1308
1309         widget_class->size_allocate = ev_view_presentation_size_allocate;
1310         widget_class->size_request = ev_view_presentation_size_request;
1311         widget_class->realize = ev_view_presentation_realize;
1312         widget_class->expose_event = ev_view_presentation_expose_event;
1313         widget_class->key_press_event = ev_view_presentation_key_press_event;
1314         widget_class->button_release_event = ev_view_presentation_button_release_event;
1315         widget_class->focus_out_event = ev_view_presentation_focus_out;
1316         widget_class->motion_notify_event = ev_view_presentation_motion_notify_event;
1317         widget_class->scroll_event = ev_view_presentation_scroll_event;
1318
1319         gtk_object_class->destroy = ev_view_presentation_destroy;
1320
1321         gobject_class->constructor = ev_view_presentation_constructor;
1322         gobject_class->set_property = ev_view_presentation_set_property;
1323
1324         g_object_class_install_property (gobject_class,
1325                                          PROP_DOCUMENT,
1326                                          g_param_spec_object ("document",
1327                                                               "Document",
1328                                                               "Document",
1329                                                               EV_TYPE_DOCUMENT,
1330                                                               G_PARAM_WRITABLE |
1331                                                               G_PARAM_CONSTRUCT_ONLY));
1332         g_object_class_install_property (gobject_class,
1333                                          PROP_CURRENT_PAGE,
1334                                          g_param_spec_uint ("current_page",
1335                                                             "Current Page",
1336                                                             "The current page",
1337                                                             0, G_MAXUINT, 0,
1338                                                             G_PARAM_WRITABLE |
1339                                                             G_PARAM_CONSTRUCT_ONLY));
1340         g_object_class_install_property (gobject_class,
1341                                          PROP_ROTATION,
1342                                          g_param_spec_uint ("rotation",
1343                                                             "Rotation",
1344                                                             "Current rotation angle",
1345                                                             0, 360, 0,
1346                                                             G_PARAM_WRITABLE |
1347                                                             G_PARAM_CONSTRUCT_ONLY));
1348
1349         signals[CHANGE_PAGE] =
1350                 g_signal_new ("change_page",
1351                               G_OBJECT_CLASS_TYPE (gobject_class),
1352                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1353                               G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
1354                               NULL, NULL,
1355                               g_cclosure_marshal_VOID__ENUM,
1356                               G_TYPE_NONE, 1,
1357                               GTK_TYPE_SCROLL_TYPE);
1358
1359         binding_set = gtk_binding_set_by_class (klass);
1360         add_change_page_binding_keypad (binding_set, GDK_Left,  0, GTK_SCROLL_PAGE_BACKWARD);
1361         add_change_page_binding_keypad (binding_set, GDK_Right, 0, GTK_SCROLL_PAGE_FORWARD);
1362         add_change_page_binding_keypad (binding_set, GDK_Up,    0, GTK_SCROLL_PAGE_BACKWARD);
1363         add_change_page_binding_keypad (binding_set, GDK_Down,  0, GTK_SCROLL_PAGE_FORWARD);
1364         gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
1365                                       "change_page", 1,
1366                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1367         gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
1368                                       "change_page", 1,
1369                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1370         gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
1371                                       "change_page", 1,
1372                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1373         gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
1374                                       "change_page", 1,
1375                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1376         gtk_binding_entry_add_signal (binding_set, GDK_J, 0,
1377                                       "change_page", 1,
1378                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1379         gtk_binding_entry_add_signal (binding_set, GDK_H, 0,
1380                                       "change_page", 1,
1381                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1382         gtk_binding_entry_add_signal (binding_set, GDK_L, 0,
1383                                       "change_page", 1,
1384                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1385         gtk_binding_entry_add_signal (binding_set, GDK_K, 0,
1386                                       "change_page", 1,
1387                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1388 }
1389
1390 static void
1391 ev_view_presentation_init (EvViewPresentation *pview)
1392 {
1393         GTK_WIDGET_SET_FLAGS (pview, GTK_CAN_FOCUS);
1394 }
1395
1396 GtkWidget *
1397 ev_view_presentation_new (EvDocument *document,
1398                           guint       current_page,
1399                           guint       rotation)
1400 {
1401         g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
1402         g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
1403
1404         return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
1405                                          "document", document,
1406                                          "current_page", current_page,
1407                                          "rotation", rotation,
1408                                          NULL));
1409 }
1410
1411 guint
1412 ev_view_presentation_get_current_page (EvViewPresentation *pview)
1413 {
1414         return pview->current_page;
1415 }