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