]> www.fi.muni.cz Git - evince.git/blob - libview/ev-view-presentation.c
3612904ae025cd539d3d5edb2b094cb3a414b739
[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 gboolean
1239 init_presentation (GtkWidget *widget)
1240 {
1241         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1242         GdkScreen          *screen = gtk_widget_get_screen (widget);
1243         GdkRectangle        monitor;
1244         gint                monitor_num;
1245
1246         monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget));
1247         gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1248         pview->monitor_width = monitor.width;
1249         pview->monitor_height = monitor.height;
1250
1251         ev_view_presentation_update_current_page (pview, pview->current_page);
1252         ev_view_presentation_hide_cursor_timeout_start (pview);
1253
1254         return FALSE;
1255 }
1256
1257 static void
1258 ev_view_presentation_realize (GtkWidget *widget)
1259 {
1260         GdkWindow     *window;
1261         GdkWindowAttr  attributes;
1262         GtkAllocation  allocation;
1263
1264         gtk_widget_set_realized (widget, TRUE);
1265
1266         attributes.window_type = GDK_WINDOW_CHILD;
1267         attributes.wclass = GDK_INPUT_OUTPUT;
1268         attributes.visual = gtk_widget_get_visual (widget);
1269
1270         gtk_widget_get_allocation (widget, &allocation);
1271         attributes.x = allocation.x;
1272         attributes.y = allocation.y;
1273         attributes.width = allocation.width;
1274         attributes.height = allocation.height;
1275         attributes.event_mask = GDK_EXPOSURE_MASK |
1276                 GDK_BUTTON_PRESS_MASK |
1277                 GDK_BUTTON_RELEASE_MASK |
1278                 GDK_SCROLL_MASK |
1279                 GDK_KEY_PRESS_MASK |
1280                 GDK_POINTER_MOTION_MASK |
1281                 GDK_POINTER_MOTION_HINT_MASK |
1282                 GDK_ENTER_NOTIFY_MASK |
1283                 GDK_LEAVE_NOTIFY_MASK;
1284
1285         window = gdk_window_new (gtk_widget_get_parent_window (widget),
1286                                  &attributes,
1287                                  GDK_WA_X | GDK_WA_Y |
1288                                  GDK_WA_VISUAL);
1289
1290         gdk_window_set_user_data (window, widget);
1291         gtk_widget_set_window (widget, window);
1292         gtk_style_context_set_background (gtk_widget_get_style_context (widget),
1293                                           window);
1294
1295         g_idle_add ((GSourceFunc)init_presentation, widget);
1296 }
1297
1298 static void
1299 ev_view_presentation_change_page (EvViewPresentation *pview,
1300                                   GtkScrollType       scroll)
1301 {
1302         switch (scroll) {
1303         case GTK_SCROLL_PAGE_FORWARD:
1304                 ev_view_presentation_next_page (pview);
1305                 break;
1306         case GTK_SCROLL_PAGE_BACKWARD:
1307                 ev_view_presentation_previous_page (pview);
1308                 break;
1309         default:
1310                 g_assert_not_reached ();
1311         }
1312 }
1313
1314 static gboolean
1315 ev_view_presentation_scroll_event (GtkWidget      *widget,
1316                                    GdkEventScroll *event)
1317 {
1318         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1319         guint               state;
1320
1321         state = event->state & gtk_accelerator_get_default_mod_mask ();
1322         if (state != 0)
1323                 return FALSE;
1324
1325         switch (event->direction) {
1326         case GDK_SCROLL_DOWN:
1327         case GDK_SCROLL_RIGHT:
1328                 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
1329                 break;
1330         case GDK_SCROLL_UP:
1331         case GDK_SCROLL_LEFT:
1332                 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1333                 break;
1334         }
1335
1336         return TRUE;
1337 }
1338
1339
1340 static void
1341 add_change_page_binding_keypad (GtkBindingSet  *binding_set,
1342                                 guint           keyval,
1343                                 GdkModifierType modifiers,
1344                                 GtkScrollType   scroll)
1345 {
1346         guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
1347
1348         gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1349                                       "change_page", 1,
1350                                       GTK_TYPE_SCROLL_TYPE, scroll);
1351         gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1352                                       "change_page", 1,
1353                                       GTK_TYPE_SCROLL_TYPE, scroll);
1354 }
1355
1356 static void
1357 ev_view_presentation_set_property (GObject      *object,
1358                                    guint         prop_id,
1359                                    const GValue *value,
1360                                    GParamSpec   *pspec)
1361 {
1362         EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1363
1364         switch (prop_id) {
1365         case PROP_DOCUMENT:
1366                 pview->document = g_value_dup_object (value);
1367                 pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
1368                 break;
1369         case PROP_CURRENT_PAGE:
1370                 pview->current_page = g_value_get_uint (value);
1371                 break;
1372         case PROP_PAGE:
1373                 pview->current_page = g_value_get_uint (value);
1374                 break;
1375         case PROP_ROTATION:
1376                 ev_view_presentation_set_rotation (pview, g_value_get_uint (value));
1377                 break;
1378         case PROP_INVERTED_COLORS:
1379                 pview->inverted_colors = g_value_get_boolean (value);
1380                 break;
1381         default:
1382                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1383         }
1384 }
1385
1386 static void
1387 ev_view_presentation_get_property (GObject    *object,
1388                                    guint       prop_id,
1389                                    GValue     *value,
1390                                    GParamSpec *pspec)
1391 {
1392         EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1393
1394         switch (prop_id) {
1395                 case PROP_PAGE:
1396                                 g_value_set_uint (value, ev_view_presentation_get_current_page (pview));
1397                                 break;
1398         case PROP_ROTATION:
1399                 g_value_set_uint (value, ev_view_presentation_get_rotation (pview));
1400                 break;
1401         default:
1402                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1403         }
1404 }
1405
1406 static GObject *
1407 ev_view_presentation_constructor (GType                  type,
1408                                                                   guint                  n_construct_properties,
1409                                                                   GObjectConstructParam *construct_params)
1410 {
1411         GObject            *object;
1412         EvViewPresentation *pview;
1413
1414         object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1415                                                                                   n_construct_properties,
1416                                                                                   construct_params);
1417         pview = EV_VIEW_PRESENTATION (object);
1418         pview->is_constructing = FALSE;
1419
1420         if (EV_IS_DOCUMENT_LINKS (pview->document)) {
1421                 pview->page_cache = ev_page_cache_new (pview->document);
1422                 ev_page_cache_set_flags (pview->page_cache, EV_PAGE_DATA_INCLUDE_LINKS);
1423         }
1424
1425         return object;
1426 }
1427
1428 static void
1429 ev_view_presentation_class_init (EvViewPresentationClass *klass)
1430 {
1431         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1432         GObjectClass   *gobject_class = G_OBJECT_CLASS (klass);
1433         GtkBindingSet  *binding_set;
1434         GtkCssProvider *provider;
1435
1436         klass->change_page = ev_view_presentation_change_page;
1437
1438         gobject_class->dispose = ev_view_presentation_dispose;
1439
1440         widget_class->get_preferred_width = ev_view_presentation_get_preferred_width;
1441         widget_class->get_preferred_height = ev_view_presentation_get_preferred_height;
1442         widget_class->realize = ev_view_presentation_realize;
1443         widget_class->draw = ev_view_presentation_draw;
1444         widget_class->key_press_event = ev_view_presentation_key_press_event;
1445         widget_class->button_release_event = ev_view_presentation_button_release_event;
1446         widget_class->focus_out_event = ev_view_presentation_focus_out;
1447         widget_class->motion_notify_event = ev_view_presentation_motion_notify_event;
1448         widget_class->scroll_event = ev_view_presentation_scroll_event;
1449
1450         gobject_class->constructor = ev_view_presentation_constructor;
1451         gobject_class->set_property = ev_view_presentation_set_property;
1452         gobject_class->get_property = ev_view_presentation_get_property;
1453
1454         g_object_class_install_property (gobject_class,
1455                                          PROP_DOCUMENT,
1456                                          g_param_spec_object ("document",
1457                                                               "Document",
1458                                                               "Document",
1459                                                               EV_TYPE_DOCUMENT,
1460                                                               G_PARAM_WRITABLE |
1461                                                               G_PARAM_CONSTRUCT_ONLY));
1462         g_object_class_install_property (gobject_class,
1463                                          PROP_PAGE,
1464                                          g_param_spec_uint ("page",
1465                                                             "Current Page",
1466                                                             "The current page",
1467                                                             0, G_MAXUINT, 0,
1468                                                                 G_PARAM_READWRITE));
1469         g_object_class_install_property (gobject_class,
1470                                          PROP_CURRENT_PAGE,
1471                                          g_param_spec_uint ("current_page",
1472                                                             "Current Page",
1473                                                             "The current page",
1474                                                             0, G_MAXUINT, 0,
1475                                                                 G_PARAM_WRITABLE |
1476                                                             G_PARAM_CONSTRUCT_ONLY));
1477         g_object_class_install_property (gobject_class,
1478                                          PROP_ROTATION,
1479                                          g_param_spec_uint ("rotation",
1480                                                             "Rotation",
1481                                                             "Current rotation angle",
1482                                                             0, 360, 0,
1483                                                             G_PARAM_READWRITE |
1484                                                             G_PARAM_CONSTRUCT));
1485         g_object_class_install_property (gobject_class,
1486                                          PROP_INVERTED_COLORS,
1487                                          g_param_spec_boolean ("inverted_colors",
1488                                                                "Inverted Colors",
1489                                                                "Whether presentation is displayed with inverted colors",
1490                                                                FALSE,
1491                                                                G_PARAM_WRITABLE |
1492                                                                G_PARAM_CONSTRUCT_ONLY));
1493
1494         signals[CHANGE_PAGE] =
1495                 g_signal_new ("change_page",
1496                               G_OBJECT_CLASS_TYPE (gobject_class),
1497                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1498                               G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
1499                               NULL, NULL,
1500                               g_cclosure_marshal_VOID__ENUM,
1501                               G_TYPE_NONE, 1,
1502                               GTK_TYPE_SCROLL_TYPE);
1503
1504         signals[FINISHED] =
1505                 g_signal_new ("finished",
1506                               G_OBJECT_CLASS_TYPE (gobject_class),
1507                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1508                               G_STRUCT_OFFSET (EvViewPresentationClass, finished),
1509                               NULL, NULL,
1510                               g_cclosure_marshal_VOID__VOID,
1511                               G_TYPE_NONE, 0,
1512                               G_TYPE_NONE);
1513         signals[SIGNAL_EXTERNAL_LINK] =
1514                 g_signal_new ("external-link",
1515                               G_TYPE_FROM_CLASS (gobject_class),
1516                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1517                               G_STRUCT_OFFSET (EvViewPresentationClass, external_link),
1518                               NULL, NULL,
1519                               g_cclosure_marshal_VOID__OBJECT,
1520                               G_TYPE_NONE, 1,
1521                               G_TYPE_OBJECT);
1522
1523         binding_set = gtk_binding_set_by_class (klass);
1524         add_change_page_binding_keypad (binding_set, GDK_KEY_Left,  0, GTK_SCROLL_PAGE_BACKWARD);
1525         add_change_page_binding_keypad (binding_set, GDK_KEY_Right, 0, GTK_SCROLL_PAGE_FORWARD);
1526         add_change_page_binding_keypad (binding_set, GDK_KEY_Up,    0, GTK_SCROLL_PAGE_BACKWARD);
1527         add_change_page_binding_keypad (binding_set, GDK_KEY_Down,  0, GTK_SCROLL_PAGE_FORWARD);
1528         gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
1529                                       "change_page", 1,
1530                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1531         gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0,
1532                                       "change_page", 1,
1533                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1534         gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
1535                                       "change_page", 1,
1536                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1537         gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
1538                                       "change_page", 1,
1539                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1540         gtk_binding_entry_add_signal (binding_set, GDK_KEY_J, 0,
1541                                       "change_page", 1,
1542                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1543         gtk_binding_entry_add_signal (binding_set, GDK_KEY_H, 0,
1544                                       "change_page", 1,
1545                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1546         gtk_binding_entry_add_signal (binding_set, GDK_KEY_L, 0,
1547                                       "change_page", 1,
1548                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1549         gtk_binding_entry_add_signal (binding_set, GDK_KEY_K, 0,
1550                                       "change_page", 1,
1551                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1552
1553         provider = gtk_css_provider_new ();
1554         gtk_css_provider_load_from_data (provider,
1555                                          "EvViewPresentation {\n"
1556                                          " background-color: black; }",
1557                                          -1, NULL);
1558         gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
1559                                                    GTK_STYLE_PROVIDER (provider),
1560                                                    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1561         g_object_unref (provider);
1562 }
1563
1564 static void
1565 ev_view_presentation_init (EvViewPresentation *pview)
1566 {
1567         gtk_widget_set_can_focus (GTK_WIDGET (pview), TRUE);
1568         pview->is_constructing = TRUE;
1569 }
1570
1571 GtkWidget *
1572 ev_view_presentation_new (EvDocument *document,
1573                           guint       current_page,
1574                           guint       rotation,
1575                           gboolean    inverted_colors)
1576 {
1577         g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
1578         g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
1579
1580         return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
1581                                          "document", document,
1582                                          "current_page", current_page,
1583                                          "rotation", rotation,
1584                                          "inverted_colors", inverted_colors,
1585                                          NULL));
1586 }
1587
1588 guint
1589 ev_view_presentation_get_current_page (EvViewPresentation *pview)
1590 {
1591         return pview->current_page;
1592 }
1593
1594 void
1595 ev_view_presentation_set_rotation (EvViewPresentation *pview,
1596                                    gint                rotation)
1597 {
1598         if (rotation >= 360)
1599                 rotation -= 360;
1600         else if (rotation < 0)
1601                 rotation += 360;
1602
1603         if (pview->rotation == rotation)
1604                 return;
1605
1606         pview->rotation = rotation;
1607         g_object_notify (G_OBJECT (pview), "rotation");
1608         if (pview->is_constructing)
1609                 return;
1610
1611         pview->scale = 0;
1612         ev_view_presentation_reset_jobs (pview);
1613         ev_view_presentation_update_current_page (pview, pview->current_page);
1614 }
1615
1616 guint
1617 ev_view_presentation_get_rotation (EvViewPresentation *pview)
1618 {
1619         return pview->rotation;
1620 }