]> www.fi.muni.cz Git - evince.git/blob - libview/ev-view-presentation.c
[presentation] Allow finish presentation by clicking on end page
[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         FINISHED,
45         N_SIGNALS
46 };
47
48 typedef enum {
49         EV_PRESENTATION_NORMAL,
50         EV_PRESENTATION_BLACK,
51         EV_PRESENTATION_WHITE,
52         EV_PRESENTATION_END
53 } EvPresentationState;
54
55 struct _EvViewPresentation
56 {
57         GtkWidget base;
58
59         guint                  current_page;
60         EvDocument            *document;
61         guint                  rotation;
62         EvPresentationState    state;
63         gdouble                scale;
64
65         /* Cursors */
66         EvViewCursor           cursor;
67         guint                  hide_cursor_timeout_id;
68
69         /* Goto Window */
70         GtkWidget             *goto_window;
71         GtkWidget             *goto_entry;
72
73         /* Page Transition */
74         guint                  trans_timeout_id;
75
76         /* Animations */
77         gboolean               enable_animations;
78         EvTransitionAnimation *animation;
79
80         /* Links */
81         EvPageCache           *page_cache;
82
83         EvJob *prev_job;
84         EvJob *curr_job;
85         EvJob *next_job;
86 };
87
88 struct _EvViewPresentationClass
89 {
90         GtkWidgetClass base_class;
91
92         /* signals */
93         void (* change_page) (EvViewPresentation *pview,
94                               GtkScrollType       scroll);
95         void (* finished)    (EvViewPresentation *pview);
96 };
97
98 static guint signals[N_SIGNALS] = { 0 };
99
100 static void ev_view_presentation_next_page               (EvViewPresentation *pview);
101 static void ev_view_presentation_previous_page           (EvViewPresentation *pview);
102 static void ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
103                                                           gdouble             x,
104                                                           gdouble             y);
105
106 #define HIDE_CURSOR_TIMEOUT 5
107
108 G_DEFINE_TYPE (EvViewPresentation, ev_view_presentation, GTK_TYPE_WIDGET)
109
110 static void
111 ev_view_presentation_set_normal (EvViewPresentation *pview)
112 {
113         GtkWidget *widget = GTK_WIDGET (pview);
114
115         if (pview->state == EV_PRESENTATION_NORMAL)
116                 return;
117
118         pview->state = EV_PRESENTATION_NORMAL;
119         gdk_window_set_background (widget->window, &widget->style->black);
120         gtk_widget_queue_draw (widget);
121 }
122
123 static void
124 ev_view_presentation_set_black (EvViewPresentation *pview)
125 {
126         GtkWidget *widget = GTK_WIDGET (pview);
127
128         if (pview->state == EV_PRESENTATION_BLACK)
129                 return;
130
131         pview->state = EV_PRESENTATION_BLACK;
132         gdk_window_set_background (widget->window, &widget->style->black);
133         gtk_widget_queue_draw (widget);
134 }
135
136 static void
137 ev_view_presentation_set_white (EvViewPresentation *pview)
138 {
139         GtkWidget *widget = GTK_WIDGET (pview);
140
141         if (pview->state == EV_PRESENTATION_WHITE)
142                 return;
143
144         pview->state = EV_PRESENTATION_WHITE;
145         gdk_window_set_background (widget->window, &widget->style->white);
146         gtk_widget_queue_draw (widget);
147 }
148
149 static void
150 ev_view_presentation_set_end (EvViewPresentation *pview)
151 {
152         GtkWidget *widget = GTK_WIDGET (pview);
153
154         if (pview->state == EV_PRESENTATION_END)
155                 return;
156
157         pview->state = EV_PRESENTATION_END;
158         gtk_widget_queue_draw (widget);
159 }
160
161 static gdouble
162 ev_view_presentation_get_scale_for_page (EvViewPresentation *pview,
163                                          guint               page)
164 {
165         gdouble width, height;
166
167         ev_document_get_page_size (pview->document, page, &width, &height);
168
169         if (pview->rotation == 90 || pview->rotation == 270)
170                 return GTK_WIDGET (pview)->allocation.height / width;
171         else
172                 return GTK_WIDGET (pview)->allocation.height / height;
173 }
174
175 static void
176 ev_view_presentation_update_scale (EvViewPresentation *pview)
177 {
178         if (ev_document_is_page_size_uniform (pview->document) && pview->scale != 0)
179                 return;
180
181         pview->scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
182 }
183
184 static void
185 ev_view_presentation_get_page_area (EvViewPresentation *pview,
186                                     GdkRectangle       *area)
187 {
188         GtkWidget *widget = GTK_WIDGET (pview);
189         gdouble    doc_width, doc_height;
190         gint       view_width, view_height;
191
192         ev_document_get_page_size (pview->document,
193                                    pview->current_page,
194                                    &doc_width, &doc_height);
195
196         if (pview->rotation == 90 || pview->rotation == 270) {
197                 view_width = (gint)((doc_height * pview->scale) + 0.5);
198                 view_height = (gint)((doc_width * pview->scale) + 0.5);
199         } else {
200                 view_width = (gint)((doc_width * pview->scale) + 0.5);
201                 view_height = (gint)((doc_height * pview->scale) + 0.5);
202         }
203
204         area->x = (MAX (0, widget->allocation.width - view_width)) / 2;
205         area->y = (MAX (0, widget->allocation.height - view_height)) / 2;
206         area->width = view_width;
207         area->height = view_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. Click 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                 if (pview->state == EV_PRESENTATION_END) {
1109                         g_signal_emit (pview, signals[FINISHED], 0, NULL);
1110
1111                         return FALSE;
1112                 }
1113
1114                 link = ev_view_presentation_get_link_at_location (pview,
1115                                                                   event->x,
1116                                                                   event->y);
1117                 if (link)
1118                         ev_vew_presentation_goto_link_dest (pview, link);
1119                 else
1120                         ev_view_presentation_next_page (pview);
1121         }
1122                 break;
1123         case 3:
1124                 ev_view_presentation_previous_page (pview);
1125                 break;
1126         default:
1127                 break;
1128         }
1129
1130         return FALSE;
1131 }
1132
1133 static gint
1134 ev_view_presentation_focus_out (GtkWidget     *widget,
1135                                 GdkEventFocus *event)
1136 {
1137         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1138
1139         if (pview->goto_window)
1140                 ev_view_presentation_goto_window_hide (pview);
1141
1142         return FALSE;
1143 }
1144
1145 static gboolean
1146 ev_view_presentation_motion_notify_event (GtkWidget      *widget,
1147                                           GdkEventMotion *event)
1148 {
1149         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1150
1151         ev_view_presentation_hide_cursor_timeout_start (pview);
1152         ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
1153
1154         return FALSE;
1155 }
1156
1157 static void
1158 ev_view_presentation_realize (GtkWidget *widget)
1159 {
1160         GdkWindowAttr attributes;
1161
1162         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1163
1164         attributes.window_type = GDK_WINDOW_CHILD;
1165         attributes.wclass = GDK_INPUT_OUTPUT;
1166         attributes.visual = gtk_widget_get_visual (widget);
1167         attributes.colormap = gtk_widget_get_colormap (widget);
1168
1169         attributes.x = widget->allocation.x;
1170         attributes.y = widget->allocation.y;
1171         attributes.width = widget->allocation.width;
1172         attributes.height = widget->allocation.height;
1173         attributes.event_mask = GDK_EXPOSURE_MASK |
1174                 GDK_BUTTON_PRESS_MASK |
1175                 GDK_BUTTON_RELEASE_MASK |
1176                 GDK_SCROLL_MASK |
1177                 GDK_KEY_PRESS_MASK |
1178                 GDK_POINTER_MOTION_MASK |
1179                 GDK_POINTER_MOTION_HINT_MASK |
1180                                 GDK_ENTER_NOTIFY_MASK |
1181                 GDK_LEAVE_NOTIFY_MASK;
1182
1183         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1184                                          &attributes,
1185                                          GDK_WA_X | GDK_WA_Y |
1186                                          GDK_WA_COLORMAP |
1187                                          GDK_WA_VISUAL);
1188         gdk_window_set_user_data (widget->window, widget);
1189         widget->style = gtk_style_attach (widget->style, widget->window);
1190
1191         gdk_window_set_background (widget->window, &widget->style->black);
1192
1193         gtk_widget_queue_resize (widget);
1194 }
1195
1196 static void
1197 ev_view_presentation_change_page (EvViewPresentation *pview,
1198                                   GtkScrollType       scroll)
1199 {
1200         switch (scroll) {
1201         case GTK_SCROLL_PAGE_FORWARD:
1202                 ev_view_presentation_next_page (pview);
1203                 break;
1204         case GTK_SCROLL_PAGE_BACKWARD:
1205                 ev_view_presentation_previous_page (pview);
1206                 break;
1207         default:
1208                 g_assert_not_reached ();
1209         }
1210 }
1211
1212 static gboolean
1213 ev_view_presentation_scroll_event (GtkWidget      *widget,
1214                                    GdkEventScroll *event)
1215 {
1216         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1217         guint               state;
1218
1219         state = event->state & gtk_accelerator_get_default_mod_mask ();
1220         if (state != 0)
1221                 return FALSE;
1222
1223         switch (event->direction) {
1224         case GDK_SCROLL_DOWN:
1225         case GDK_SCROLL_RIGHT:
1226                 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
1227                 break;
1228         case GDK_SCROLL_UP:
1229         case GDK_SCROLL_LEFT:
1230                 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1231                 break;
1232         }
1233
1234         return TRUE;
1235 }
1236
1237
1238 static void
1239 add_change_page_binding_keypad (GtkBindingSet  *binding_set,
1240                                 guint           keyval,
1241                                 GdkModifierType modifiers,
1242                                 GtkScrollType   scroll)
1243 {
1244         guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
1245
1246         gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1247                                       "change_page", 1,
1248                                       GTK_TYPE_SCROLL_TYPE, scroll);
1249         gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1250                                       "change_page", 1,
1251                                       GTK_TYPE_SCROLL_TYPE, scroll);
1252 }
1253
1254 static void
1255 ev_view_presentation_set_property (GObject      *object,
1256                                    guint         prop_id,
1257                                    const GValue *value,
1258                                    GParamSpec   *pspec)
1259 {
1260         EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1261
1262         switch (prop_id) {
1263         case PROP_DOCUMENT:
1264                 pview->document = g_value_dup_object (value);
1265                 pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
1266                 break;
1267         case PROP_CURRENT_PAGE:
1268                 pview->current_page = g_value_get_uint (value);
1269                 break;
1270         case PROP_ROTATION:
1271                 pview->rotation = g_value_get_uint (value);
1272                 break;
1273         default:
1274                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1275         }
1276 }
1277
1278 static GObject *
1279 ev_view_presentation_constructor (GType                  type,
1280                                   guint                  n_construct_properties,
1281                                   GObjectConstructParam *construct_params)
1282 {
1283         GObject            *object;
1284         EvViewPresentation *pview;
1285         GtkAllocation       a;
1286
1287         object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1288                                                                                   n_construct_properties,
1289                                                                                   construct_params);
1290         pview = EV_VIEW_PRESENTATION (object);
1291
1292         if (EV_IS_DOCUMENT_LINKS (pview->document)) {
1293                 pview->page_cache = ev_page_cache_new (pview->document);
1294                 ev_page_cache_set_flags (pview->page_cache, EV_PAGE_DATA_INCLUDE_LINKS);
1295         }
1296
1297         /* Call allocate asap to update page scale */
1298         ev_view_presentation_size_allocate (GTK_WIDGET (pview), &a);
1299         ev_view_presentation_update_current_page (pview, pview->current_page);
1300         ev_view_presentation_hide_cursor_timeout_start (pview);
1301
1302         return object;
1303 }
1304
1305 static void
1306 ev_view_presentation_class_init (EvViewPresentationClass *klass)
1307 {
1308         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1309         GObjectClass   *gobject_class = G_OBJECT_CLASS (klass);
1310         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (klass);
1311         GtkBindingSet  *binding_set;
1312
1313         klass->change_page = ev_view_presentation_change_page;
1314
1315         widget_class->size_allocate = ev_view_presentation_size_allocate;
1316         widget_class->size_request = ev_view_presentation_size_request;
1317         widget_class->realize = ev_view_presentation_realize;
1318         widget_class->expose_event = ev_view_presentation_expose_event;
1319         widget_class->key_press_event = ev_view_presentation_key_press_event;
1320         widget_class->button_release_event = ev_view_presentation_button_release_event;
1321         widget_class->focus_out_event = ev_view_presentation_focus_out;
1322         widget_class->motion_notify_event = ev_view_presentation_motion_notify_event;
1323         widget_class->scroll_event = ev_view_presentation_scroll_event;
1324
1325         gtk_object_class->destroy = ev_view_presentation_destroy;
1326
1327         gobject_class->constructor = ev_view_presentation_constructor;
1328         gobject_class->set_property = ev_view_presentation_set_property;
1329
1330         g_object_class_install_property (gobject_class,
1331                                          PROP_DOCUMENT,
1332                                          g_param_spec_object ("document",
1333                                                               "Document",
1334                                                               "Document",
1335                                                               EV_TYPE_DOCUMENT,
1336                                                               G_PARAM_WRITABLE |
1337                                                               G_PARAM_CONSTRUCT_ONLY));
1338         g_object_class_install_property (gobject_class,
1339                                          PROP_CURRENT_PAGE,
1340                                          g_param_spec_uint ("current_page",
1341                                                             "Current Page",
1342                                                             "The current page",
1343                                                             0, G_MAXUINT, 0,
1344                                                             G_PARAM_WRITABLE |
1345                                                             G_PARAM_CONSTRUCT_ONLY));
1346         g_object_class_install_property (gobject_class,
1347                                          PROP_ROTATION,
1348                                          g_param_spec_uint ("rotation",
1349                                                             "Rotation",
1350                                                             "Current rotation angle",
1351                                                             0, 360, 0,
1352                                                             G_PARAM_WRITABLE |
1353                                                             G_PARAM_CONSTRUCT_ONLY));
1354
1355         signals[CHANGE_PAGE] =
1356                 g_signal_new ("change_page",
1357                               G_OBJECT_CLASS_TYPE (gobject_class),
1358                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1359                               G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
1360                               NULL, NULL,
1361                               g_cclosure_marshal_VOID__ENUM,
1362                               G_TYPE_NONE, 1,
1363                               GTK_TYPE_SCROLL_TYPE);
1364         signals[FINISHED] =
1365                 g_signal_new ("finished",
1366                               G_OBJECT_CLASS_TYPE (gobject_class),
1367                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1368                               G_STRUCT_OFFSET (EvViewPresentationClass, finished),
1369                               NULL, NULL,
1370                               g_cclosure_marshal_VOID__VOID,
1371                               G_TYPE_NONE, 0,
1372                               G_TYPE_NONE);
1373
1374         binding_set = gtk_binding_set_by_class (klass);
1375         add_change_page_binding_keypad (binding_set, GDK_Left,  0, GTK_SCROLL_PAGE_BACKWARD);
1376         add_change_page_binding_keypad (binding_set, GDK_Right, 0, GTK_SCROLL_PAGE_FORWARD);
1377         add_change_page_binding_keypad (binding_set, GDK_Up,    0, GTK_SCROLL_PAGE_BACKWARD);
1378         add_change_page_binding_keypad (binding_set, GDK_Down,  0, GTK_SCROLL_PAGE_FORWARD);
1379         gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
1380                                       "change_page", 1,
1381                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1382         gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
1383                                       "change_page", 1,
1384                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1385         gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
1386                                       "change_page", 1,
1387                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1388         gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
1389                                       "change_page", 1,
1390                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1391         gtk_binding_entry_add_signal (binding_set, GDK_J, 0,
1392                                       "change_page", 1,
1393                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1394         gtk_binding_entry_add_signal (binding_set, GDK_H, 0,
1395                                       "change_page", 1,
1396                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1397         gtk_binding_entry_add_signal (binding_set, GDK_L, 0,
1398                                       "change_page", 1,
1399                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1400         gtk_binding_entry_add_signal (binding_set, GDK_K, 0,
1401                                       "change_page", 1,
1402                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1403 }
1404
1405 static void
1406 ev_view_presentation_init (EvViewPresentation *pview)
1407 {
1408         GTK_WIDGET_SET_FLAGS (pview, GTK_CAN_FOCUS);
1409 }
1410
1411 GtkWidget *
1412 ev_view_presentation_new (EvDocument *document,
1413                           guint       current_page,
1414                           guint       rotation)
1415 {
1416         g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
1417         g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
1418
1419         return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
1420                                          "document", document,
1421                                          "current_page", current_page,
1422                                          "rotation", rotation,
1423                                          NULL));
1424 }
1425
1426 guint
1427 ev_view_presentation_get_current_page (EvViewPresentation *pview)
1428 {
1429         return pview->current_page;
1430 }