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