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