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