]> www.fi.muni.cz Git - evince.git/blob - libview/ev-view-presentation.c
working with signals
[evince.git] / libview / ev-view-presentation.c
1 /* ev-view-presentation.c
2  *  this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
5  *
6  * Evince is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Evince is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <glib/gi18n-lib.h>
25 #include <gdk/gdkkeysyms.h>
26
27 #include "ev-view-presentation.h"
28 #include "ev-jobs.h"
29 #include "ev-job-scheduler.h"
30 #include "ev-transition-animation.h"
31 #include "ev-view-cursor.h"
32 #include "ev-page-cache.h"
33
34 enum {
35         PROP_0,
36         PROP_DOCUMENT,
37         PROP_CURRENT_PAGE,
38         PROP_ROTATION,
39         PROP_INVERTED_COLORS
40 };
41
42 enum {
43         PAGE_CHANGED,
44         CHANGE_PAGE,
45         FINISHED,
46         SIGNAL_EXTERNAL_LINK,
47         N_SIGNALS
48 };
49
50 typedef enum {
51         EV_PRESENTATION_NORMAL,
52         EV_PRESENTATION_BLACK,
53         EV_PRESENTATION_WHITE,
54         EV_PRESENTATION_END
55 } EvPresentationState;
56
57 struct _EvViewPresentation
58 {
59         GtkWidget base;
60
61         guint                  is_constructing : 1;
62
63         guint                  current_page;
64         cairo_surface_t       *current_surface;
65         EvDocument            *document;
66         guint                  rotation;
67         gboolean               inverted_colors;
68         EvPresentationState    state;
69         gdouble                scale;
70         gint                   monitor_width;
71         gint                   monitor_height;
72
73         /* Cursors */
74         EvViewCursor           cursor;
75         guint                  hide_cursor_timeout_id;
76
77         /* Goto Window */
78         GtkWidget             *goto_window;
79         GtkWidget             *goto_entry;
80
81         /* Page Transition */
82         guint                  trans_timeout_id;
83
84         /* Animations */
85         gboolean               enable_animations;
86         EvTransitionAnimation *animation;
87
88         /* Links */
89         EvPageCache           *page_cache;
90
91         EvJob *prev_job;
92         EvJob *curr_job;
93         EvJob *next_job;
94 };
95
96 struct _EvViewPresentationClass
97 {
98         GtkWidgetClass base_class;
99
100         /* signals */
101         void (* page_changed)  (EvViewPresentation *pview,
102                                 gint                page);
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         g_signal_emit (pview, signals[PAGE_CHANGED], 0, page);
495         g_printf("pages: %d %d\n",page,pview->current_page;
496         pview->current_page = page;
497
498         if (pview->page_cache)
499                 ev_page_cache_set_page_range (pview->page_cache, page, page);
500
501         if (pview->cursor != EV_VIEW_CURSOR_HIDDEN) {
502                 gint x, y;
503
504                 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
505                 ev_view_presentation_set_cursor_for_location (pview, x, y);
506         }
507
508         if (EV_JOB_RENDER (pview->curr_job)->surface)
509                 gtk_widget_queue_draw (GTK_WIDGET (pview));
510 }
511
512 void
513 ev_view_presentation_set_page (EvViewPresentation *pview, gint new_page)
514 {
515         guint n_pages;
516
517         n_pages = ev_document_get_n_pages (pview->document);
518
519         if (new_page >= n_pages)
520                 ev_view_presentation_set_end (pview);
521         else
522                 ev_view_presentation_update_current_page (pview, new_page);
523 }
524
525 void
526 ev_view_presentation_next_page (EvViewPresentation *pview)
527 {
528         guint n_pages;
529         gint  new_page;
530
531         switch (pview->state) {
532         case EV_PRESENTATION_BLACK:
533         case EV_PRESENTATION_WHITE:
534                 ev_view_presentation_set_normal (pview);
535         case EV_PRESENTATION_END:
536                 return;
537         case EV_PRESENTATION_NORMAL:
538                 break;
539         }
540
541         n_pages = ev_document_get_n_pages (pview->document);
542         new_page = pview->current_page + 1;
543
544         if (new_page == n_pages)
545                 ev_view_presentation_set_end (pview);
546         else
547                 ev_view_presentation_update_current_page (pview, new_page);
548 }
549
550 void
551 ev_view_presentation_previous_page (EvViewPresentation *pview)
552 {
553         gint new_page = 0;
554
555         switch (pview->state) {
556         case EV_PRESENTATION_BLACK:
557         case EV_PRESENTATION_WHITE:
558                 ev_view_presentation_set_normal (pview);
559                 return;
560         case EV_PRESENTATION_END:
561                 pview->state = EV_PRESENTATION_NORMAL;
562                 new_page = pview->current_page;
563                 break;
564         case EV_PRESENTATION_NORMAL:
565                 new_page = pview->current_page - 1;
566                 break;
567         }
568
569         ev_view_presentation_update_current_page (pview, new_page);
570 }
571
572 /* Goto Window */
573 #define KEY_IS_NUMERIC(keyval) \
574         ((keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9) || (keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9))
575
576 /* Cut and paste from gtkwindow.c */
577 static void
578 send_focus_change (GtkWidget *widget,
579                    gboolean   in)
580 {
581         GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
582
583         fevent->focus_change.type = GDK_FOCUS_CHANGE;
584         fevent->focus_change.window = gtk_widget_get_window (widget);
585         fevent->focus_change.in = in;
586         if (fevent->focus_change.window)
587                 g_object_ref (fevent->focus_change.window);
588
589         gtk_widget_send_focus_change (widget, fevent);
590
591         gdk_event_free (fevent);
592 }
593
594 static void
595 ev_view_presentation_goto_window_hide (EvViewPresentation *pview)
596 {
597         /* send focus-in event */
598         send_focus_change (pview->goto_entry, FALSE);
599         gtk_widget_hide (pview->goto_window);
600         gtk_entry_set_text (GTK_ENTRY (pview->goto_entry), "");
601 }
602
603 static gboolean
604 ev_view_presentation_goto_window_delete_event (GtkWidget          *widget,
605                                                GdkEventAny        *event,
606                                                EvViewPresentation *pview)
607 {
608         ev_view_presentation_goto_window_hide (pview);
609
610         return TRUE;
611 }
612
613 static gboolean
614 ev_view_presentation_goto_window_key_press_event (GtkWidget          *widget,
615                                                   GdkEventKey        *event,
616                                                   EvViewPresentation *pview)
617 {
618         switch (event->keyval) {
619         case GDK_KEY_Escape:
620         case GDK_KEY_Tab:
621         case GDK_KEY_KP_Tab:
622         case GDK_KEY_ISO_Left_Tab:
623                 ev_view_presentation_goto_window_hide (pview);
624                 return TRUE;
625         case GDK_KEY_Return:
626         case GDK_KEY_KP_Enter:
627         case GDK_KEY_ISO_Enter:
628         case GDK_KEY_BackSpace:
629         case GDK_KEY_Delete:
630                 return FALSE;
631         default:
632                 if (!KEY_IS_NUMERIC (event->keyval))
633                         return TRUE;
634         }
635
636         return FALSE;
637 }
638
639 static gboolean
640 ev_view_presentation_goto_window_button_press_event (GtkWidget          *widget,
641                                                      GdkEventButton     *event,
642                                                      EvViewPresentation *pview)
643 {
644         ev_view_presentation_goto_window_hide (pview);
645
646         return TRUE;
647 }
648
649 static void
650 ev_view_presentation_goto_entry_activate (GtkEntry           *entry,
651                                           EvViewPresentation *pview)
652 {
653         const gchar *text;
654         gint         page;
655
656         text = gtk_entry_get_text (entry);
657         page = atoi (text) - 1;
658
659         ev_view_presentation_goto_window_hide (pview);
660         ev_view_presentation_update_current_page (pview, page);
661 }
662
663 static void
664 ev_view_presentation_goto_window_create (EvViewPresentation *pview)
665 {
666         GtkWidget *frame, *hbox, *label;
667         GtkWindow *toplevel, *goto_window;
668
669         toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (pview)));
670
671         if (pview->goto_window) {
672                 goto_window = GTK_WINDOW (pview->goto_window);
673                 if (gtk_window_has_group (toplevel))
674                         gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
675                 else if (gtk_window_has_group (goto_window))
676                         gtk_window_group_remove_window (gtk_window_get_group (goto_window), goto_window);
677
678                 return;
679         }
680
681         pview->goto_window = gtk_window_new (GTK_WINDOW_POPUP);
682         goto_window = GTK_WINDOW (pview->goto_window);
683         gtk_window_set_screen (goto_window, gtk_widget_get_screen (GTK_WIDGET (pview)));
684
685         if (gtk_window_has_group (toplevel))
686                 gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
687
688         gtk_window_set_modal (goto_window, TRUE);
689
690         g_signal_connect (pview->goto_window, "delete_event",
691                           G_CALLBACK (ev_view_presentation_goto_window_delete_event),
692                           pview);
693         g_signal_connect (pview->goto_window, "key_press_event",
694                           G_CALLBACK (ev_view_presentation_goto_window_key_press_event),
695                           pview);
696         g_signal_connect (pview->goto_window, "button_press_event",
697                           G_CALLBACK (ev_view_presentation_goto_window_button_press_event),
698                           pview);
699
700         frame = gtk_frame_new (NULL);
701         gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
702         gtk_container_add (GTK_CONTAINER (pview->goto_window), frame);
703         gtk_widget_show (frame);
704
705         hbox = gtk_hbox_new (FALSE, 0);
706         gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
707         gtk_container_add (GTK_CONTAINER (frame), hbox);
708         gtk_widget_show (hbox);
709
710         label = gtk_label_new (_("Jump to page:"));
711         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 3);
712         gtk_widget_show (label);
713         gtk_widget_realize (label);
714
715         pview->goto_entry = gtk_entry_new ();
716         g_signal_connect (pview->goto_entry, "activate",
717                           G_CALLBACK (ev_view_presentation_goto_entry_activate),
718                           pview);
719         gtk_box_pack_start (GTK_BOX (hbox), pview->goto_entry, TRUE, TRUE, 0);
720         gtk_widget_show (pview->goto_entry);
721         gtk_widget_realize (pview->goto_entry);
722 }
723
724 static void
725 ev_view_presentation_goto_entry_grab_focus (EvViewPresentation *pview)
726 {
727         GtkWidgetClass *entry_parent_class;
728
729         entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (pview->goto_entry));
730         (entry_parent_class->grab_focus) (pview->goto_entry);
731
732         send_focus_change (pview->goto_entry, TRUE);
733 }
734
735 static void
736 ev_view_presentation_goto_window_send_key_event (EvViewPresentation *pview,
737                                                  GdkEvent           *event)
738 {
739         GdkEventKey *new_event;
740         GdkScreen   *screen;
741
742         /* Move goto window off screen */
743         screen = gtk_widget_get_screen (GTK_WIDGET (pview));
744         gtk_window_move (GTK_WINDOW (pview->goto_window),
745                          gdk_screen_get_width (screen) + 1,
746                          gdk_screen_get_height (screen) + 1);
747         gtk_widget_show (pview->goto_window);
748
749         new_event = (GdkEventKey *) gdk_event_copy (event);
750         g_object_unref (new_event->window);
751         new_event->window = gtk_widget_get_window (pview->goto_window);
752         if (new_event->window)
753                 g_object_ref (new_event->window);
754         gtk_widget_realize (pview->goto_window);
755
756         gtk_widget_event (pview->goto_window, (GdkEvent *)new_event);
757         gdk_event_free ((GdkEvent *)new_event);
758         gtk_widget_hide (pview->goto_window);
759 }
760
761 /* Links */
762 static gboolean
763 ev_view_presentation_link_is_supported (EvViewPresentation *pview,
764                                         EvLink             *link)
765 {
766         EvLinkAction *action;
767
768         action = ev_link_get_action (link);
769         if (!action)
770                 return FALSE;
771
772         switch (ev_link_action_get_action_type (action)) {
773         case EV_LINK_ACTION_TYPE_GOTO_DEST:
774                 return ev_link_action_get_dest (action) != NULL;
775         case EV_LINK_ACTION_TYPE_NAMED:
776         case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
777         case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
778         case EV_LINK_ACTION_TYPE_LAUNCH:
779                 return TRUE;
780         default:
781                 return FALSE;
782         }
783
784         return FALSE;
785 }
786
787 static EvLink *
788 ev_view_presentation_get_link_at_location (EvViewPresentation *pview,
789                                            gdouble             x,
790                                            gdouble             y)
791 {
792         GdkRectangle   page_area;
793         EvMappingList *link_mapping;
794         EvLink        *link;
795         gdouble        width, height;
796         gdouble        new_x, new_y;
797         gdouble        scale;
798
799         if (!pview->page_cache)
800                 return NULL;
801
802         ev_document_get_page_size (pview->document, pview->current_page, &width, &height);
803         ev_view_presentation_get_page_area (pview, &page_area);
804         scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
805         x = (x - page_area.x) / scale;
806         y = (y - page_area.y) / scale;
807         switch (pview->rotation) {
808         case 0:
809         case 360:
810                 new_x = x;
811                 new_y = y;
812                 break;
813         case 90:
814                 new_x = y;
815                 new_y = height - x;
816                 break;
817         case 180:
818                 new_x = width - x;
819                 new_y = height - y;
820                 break;
821         case 270:
822                 new_x = width - y;
823                 new_y = x;
824                 break;
825         default:
826                 g_assert_not_reached ();
827         }
828
829         link_mapping = ev_page_cache_get_link_mapping (pview->page_cache, pview->current_page);
830
831         link = link_mapping ? ev_mapping_list_get_data (link_mapping, new_x, new_y) : NULL;
832
833         return link && ev_view_presentation_link_is_supported (pview, link) ? link : NULL;
834 }
835
836 static void
837 ev_vew_presentation_handle_link (EvViewPresentation *pview,
838                                  EvLink             *link)
839 {
840         EvLinkAction *action;
841
842         action = ev_link_get_action (link);
843
844         switch (ev_link_action_get_action_type (action)) {
845         case EV_LINK_ACTION_TYPE_NAMED: {
846                 const gchar *name = ev_link_action_get_name (action);
847
848                 if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
849                         ev_view_presentation_update_current_page (pview, 0);
850                 } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
851                         ev_view_presentation_update_current_page (pview, pview->current_page - 1);
852                 } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
853                         ev_view_presentation_update_current_page (pview, pview->current_page + 1);
854                 } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
855                         gint n_pages;
856
857                         n_pages = ev_document_get_n_pages (pview->document);
858                         ev_view_presentation_update_current_page (pview, n_pages - 1);
859                 }
860         }
861                 break;
862
863         case EV_LINK_ACTION_TYPE_GOTO_DEST: {
864                 EvLinkDest *dest;
865                 gint        page;
866
867                 dest = ev_link_action_get_dest (action);
868                 page = ev_document_links_get_dest_page (EV_DOCUMENT_LINKS (pview->document), dest);
869                 ev_view_presentation_update_current_page (pview, page);
870         }
871                 break;
872         case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
873         case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
874         case EV_LINK_ACTION_TYPE_LAUNCH:
875                 g_signal_emit (pview, signals[SIGNAL_EXTERNAL_LINK], 0, action);
876                 break;
877         default:
878                 break;
879         }
880 }
881
882 /* Cursors */
883 static void
884 ev_view_presentation_set_cursor (EvViewPresentation *pview,
885                                  EvViewCursor        view_cursor)
886 {
887         GtkWidget  *widget;
888         GdkCursor  *cursor;
889
890         if (pview->cursor == view_cursor)
891                 return;
892
893         widget = GTK_WIDGET (pview);
894         if (!gtk_widget_get_realized (widget))
895                 gtk_widget_realize (widget);
896
897         pview->cursor = view_cursor;
898
899         cursor = ev_view_cursor_new (gtk_widget_get_display (widget), view_cursor);
900         gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
901         gdk_flush ();
902         if (cursor)
903                 g_object_unref (cursor);
904 }
905
906 static void
907 ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
908                                               gdouble             x,
909                                               gdouble             y)
910 {
911         if (ev_view_presentation_get_link_at_location (pview, x, y))
912                 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_LINK);
913         else
914                 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_NORMAL);
915 }
916
917 static gboolean
918 hide_cursor_timeout_cb (EvViewPresentation *pview)
919 {
920         ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_HIDDEN);
921         pview->hide_cursor_timeout_id = 0;
922
923         return FALSE;
924 }
925
926 static void
927 ev_view_presentation_hide_cursor_timeout_stop (EvViewPresentation *pview)
928 {
929         if (pview->hide_cursor_timeout_id > 0)
930                 g_source_remove (pview->hide_cursor_timeout_id);
931         pview->hide_cursor_timeout_id = 0;
932 }
933
934 static void
935 ev_view_presentation_hide_cursor_timeout_start (EvViewPresentation *pview)
936 {
937         ev_view_presentation_hide_cursor_timeout_stop (pview);
938         pview->hide_cursor_timeout_id =
939                 g_timeout_add_seconds (HIDE_CURSOR_TIMEOUT,
940                                        (GSourceFunc)hide_cursor_timeout_cb,
941                                        pview);
942 }
943
944 static void
945 ev_view_presentation_update_current_surface (EvViewPresentation *pview,
946                                              cairo_surface_t    *surface)
947 {
948         if (!surface || pview->current_surface == surface)
949                 return;
950
951         cairo_surface_reference (surface);
952         if (pview->current_surface)
953                 cairo_surface_destroy (pview->current_surface);
954         pview->current_surface = surface;
955 }
956
957 static void
958 ev_view_presentation_dispose (GObject *object)
959 {
960         EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
961
962         if (pview->document) {
963                 g_object_unref (pview->document);
964                 pview->document = NULL;
965         }
966
967         ev_view_presentation_animation_cancel (pview);
968         ev_view_presentation_transition_stop (pview);
969         ev_view_presentation_hide_cursor_timeout_stop (pview);
970         ev_view_presentation_reset_jobs (pview);
971
972         if (pview->current_surface) {
973                 cairo_surface_destroy (pview->current_surface);
974                 pview->current_surface = NULL;
975         }
976
977         if (pview->page_cache) {
978                 g_object_unref (pview->page_cache);
979                 pview->page_cache = NULL;
980         }
981
982         if (pview->goto_window) {
983                 gtk_widget_destroy (pview->goto_window);
984                 pview->goto_window = NULL;
985                 pview->goto_entry = NULL;
986         }
987
988         G_OBJECT_CLASS (ev_view_presentation_parent_class)->dispose (object);
989 }
990
991 static void
992 ev_view_presentation_get_preferred_width (GtkWidget *widget,
993                                           gint      *minimum,
994                                           gint      *natural)
995 {
996         *minimum = *natural = 0;
997 }
998
999 static void
1000 ev_view_presentation_get_preferred_height (GtkWidget *widget,
1001                                            gint      *minimum,
1002                                            gint      *natural)
1003 {
1004         *minimum = *natural = 0;
1005 }
1006
1007 static void
1008 ev_view_presentation_draw_end_page (EvViewPresentation *pview,
1009                                     cairo_t *cr)
1010 {
1011         GtkWidget *widget = GTK_WIDGET (pview);
1012         PangoLayout *layout;
1013         PangoFontDescription *font_desc;
1014         gchar *markup;
1015         GtkAllocation allocation;
1016         GdkRectangle area = {0};
1017         const gchar *text = _("End of presentation. Click to exit.");
1018
1019         if (pview->state != EV_PRESENTATION_END)
1020                 return;
1021
1022         layout = gtk_widget_create_pango_layout (widget, NULL);
1023         markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", text);
1024         pango_layout_set_markup (layout, markup, -1);
1025         g_free (markup);
1026
1027         font_desc = pango_font_description_new ();
1028         pango_font_description_set_size (font_desc, 16 * PANGO_SCALE);
1029         pango_layout_set_font_description (layout, font_desc);
1030
1031         gtk_widget_get_allocation (widget, &allocation);
1032         area.width = allocation.width;
1033         area.height = allocation.height;
1034
1035         gtk_render_layout (gtk_widget_get_style_context (widget),
1036                            cr, 15, 15, layout);
1037
1038         pango_font_description_free (font_desc);
1039         g_object_unref (layout);
1040 }
1041
1042 static gboolean
1043 ev_view_presentation_draw (GtkWidget *widget,
1044                            cairo_t   *cr)
1045 {
1046         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1047         GdkRectangle        page_area;
1048         GdkRectangle        overlap;
1049         cairo_surface_t    *surface;
1050         GdkRectangle        clip_rect;
1051
1052         if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
1053                 return FALSE;
1054
1055         switch (pview->state) {
1056         case EV_PRESENTATION_END:
1057                 ev_view_presentation_draw_end_page (pview, cr);
1058                 return FALSE;
1059         case EV_PRESENTATION_BLACK:
1060         case EV_PRESENTATION_WHITE:
1061                 return FALSE;
1062         case EV_PRESENTATION_NORMAL:
1063                 break;
1064         }
1065
1066         if (pview->animation) {
1067                 if (ev_transition_animation_ready (pview->animation)) {
1068                         ev_view_presentation_get_page_area (pview, &page_area);
1069
1070                         cairo_save (cr);
1071
1072                         /* normalize to x=0, y=0 */
1073                         cairo_translate (cr, page_area.x, page_area.y);
1074                         page_area.x = page_area.y = 0;
1075
1076                         /* Try to fix rounding errors */
1077                         page_area.width--;
1078
1079                         ev_transition_animation_paint (pview->animation, cr, page_area);
1080
1081                         cairo_restore (cr);
1082                 }
1083
1084                 return TRUE;
1085         }
1086
1087         surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
1088         if (surface) {
1089                 ev_view_presentation_update_current_surface (pview, surface);
1090         } else if (pview->current_surface) {
1091                 surface = pview->current_surface;
1092         } else {
1093                 return FALSE;
1094         }
1095
1096         ev_view_presentation_get_page_area (pview, &page_area);
1097         if (gdk_rectangle_intersect (&page_area, &clip_rect, &overlap)) {
1098                 cairo_save (cr);
1099
1100                 /* Try to fix rounding errors. See bug #438760 */
1101                 if (overlap.width == page_area.width)
1102                         overlap.width--;
1103
1104                 cairo_rectangle (cr, overlap.x, overlap.y, overlap.width, overlap.height);
1105                 cairo_set_source_surface (cr, surface, page_area.x, page_area.y);
1106                 cairo_fill (cr);
1107
1108                 cairo_restore (cr);
1109         }
1110
1111         return FALSE;
1112 }
1113
1114 static gboolean
1115 ev_view_presentation_key_press_event (GtkWidget   *widget,
1116                                       GdkEventKey *event)
1117 {
1118         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1119
1120         if (pview->state == EV_PRESENTATION_END)
1121                 return gtk_bindings_activate_event (G_OBJECT (widget), event);
1122
1123         switch (event->keyval) {
1124         case GDK_KEY_b:
1125         case GDK_KEY_B:
1126         case GDK_KEY_period:
1127         case GDK_KEY_KP_Decimal:
1128                 if (pview->state == EV_PRESENTATION_BLACK)
1129                         ev_view_presentation_set_normal (pview);
1130                 else
1131                         ev_view_presentation_set_black (pview);
1132
1133                 return TRUE;
1134         case GDK_KEY_w:
1135         case GDK_KEY_W:
1136                 if (pview->state == EV_PRESENTATION_WHITE)
1137                         ev_view_presentation_set_normal (pview);
1138                 else
1139                         ev_view_presentation_set_white (pview);
1140
1141                 return TRUE;
1142         case GDK_KEY_Home:
1143                 if (pview->state == EV_PRESENTATION_NORMAL) {
1144                         ev_view_presentation_update_current_page (pview, 0);
1145                         return TRUE;
1146                 }
1147                 break;
1148         case GDK_KEY_End:
1149                 if (pview->state == EV_PRESENTATION_NORMAL) {
1150                         gint page;
1151
1152                         page = ev_document_get_n_pages (pview->document) - 1;
1153                         ev_view_presentation_update_current_page (pview, page);
1154
1155                         return TRUE;
1156                 }
1157                 break;
1158         default:
1159                 break;
1160         }
1161
1162         ev_view_presentation_set_normal (pview);
1163
1164         if (ev_document_get_n_pages (pview->document) > 1 && KEY_IS_NUMERIC (event->keyval)) {
1165                 gint x, y;
1166
1167                 ev_view_presentation_goto_window_create (pview);
1168                 ev_view_presentation_goto_window_send_key_event (pview, (GdkEvent *)event);
1169                 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
1170                 gtk_window_move (GTK_WINDOW (pview->goto_window), x, y);
1171                 gtk_widget_show (pview->goto_window);
1172                 ev_view_presentation_goto_entry_grab_focus (pview);
1173
1174                 return TRUE;
1175         }
1176
1177         return gtk_bindings_activate_event (G_OBJECT (widget), event);
1178 }
1179
1180 static gboolean
1181 ev_view_presentation_button_release_event (GtkWidget      *widget,
1182                                            GdkEventButton *event)
1183 {
1184         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1185
1186         switch (event->button) {
1187         case 1: {
1188                 EvLink *link;
1189
1190                 if (pview->state == EV_PRESENTATION_END) {
1191                         g_signal_emit (pview, signals[FINISHED], 0, NULL);
1192
1193                         return FALSE;
1194                 }
1195
1196                 link = ev_view_presentation_get_link_at_location (pview,
1197                                                                   event->x,
1198                                                                   event->y);
1199                 if (link)
1200                         ev_vew_presentation_handle_link (pview, link);
1201                 else
1202                         ev_view_presentation_next_page (pview);
1203         }
1204                 break;
1205         case 3:
1206                 ev_view_presentation_previous_page (pview);
1207                 break;
1208         default:
1209                 break;
1210         }
1211
1212         return FALSE;
1213 }
1214
1215 static gint
1216 ev_view_presentation_focus_out (GtkWidget     *widget,
1217                                 GdkEventFocus *event)
1218 {
1219         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1220
1221         if (pview->goto_window)
1222                 ev_view_presentation_goto_window_hide (pview);
1223
1224         return FALSE;
1225 }
1226
1227 static gboolean
1228 ev_view_presentation_motion_notify_event (GtkWidget      *widget,
1229                                           GdkEventMotion *event)
1230 {
1231         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1232
1233         ev_view_presentation_hide_cursor_timeout_start (pview);
1234         ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
1235
1236         return FALSE;
1237 }
1238
1239 static gboolean
1240 init_presentation (GtkWidget *widget)
1241 {
1242         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1243         GdkScreen          *screen = gtk_widget_get_screen (widget);
1244         GdkRectangle        monitor;
1245         gint                monitor_num;
1246
1247         monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget));
1248         gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1249         pview->monitor_width = monitor.width;
1250         pview->monitor_height = monitor.height;
1251
1252         ev_view_presentation_update_current_page (pview, pview->current_page);
1253         ev_view_presentation_hide_cursor_timeout_start (pview);
1254
1255         return FALSE;
1256 }
1257
1258 static void
1259 ev_view_presentation_realize (GtkWidget *widget)
1260 {
1261         GdkWindow     *window;
1262         GdkWindowAttr  attributes;
1263         GtkAllocation  allocation;
1264
1265         gtk_widget_set_realized (widget, TRUE);
1266
1267         attributes.window_type = GDK_WINDOW_CHILD;
1268         attributes.wclass = GDK_INPUT_OUTPUT;
1269         attributes.visual = gtk_widget_get_visual (widget);
1270
1271         gtk_widget_get_allocation (widget, &allocation);
1272         attributes.x = allocation.x;
1273         attributes.y = allocation.y;
1274         attributes.width = allocation.width;
1275         attributes.height = allocation.height;
1276         attributes.event_mask = GDK_EXPOSURE_MASK |
1277                 GDK_BUTTON_PRESS_MASK |
1278                 GDK_BUTTON_RELEASE_MASK |
1279                 GDK_SCROLL_MASK |
1280                 GDK_KEY_PRESS_MASK |
1281                 GDK_POINTER_MOTION_MASK |
1282                 GDK_POINTER_MOTION_HINT_MASK |
1283                 GDK_ENTER_NOTIFY_MASK |
1284                 GDK_LEAVE_NOTIFY_MASK;
1285
1286         window = gdk_window_new (gtk_widget_get_parent_window (widget),
1287                                  &attributes,
1288                                  GDK_WA_X | GDK_WA_Y |
1289                                  GDK_WA_VISUAL);
1290
1291         gdk_window_set_user_data (window, widget);
1292         gtk_widget_set_window (widget, window);
1293         gtk_style_context_set_background (gtk_widget_get_style_context (widget),
1294                                           window);
1295
1296         g_idle_add ((GSourceFunc)init_presentation, widget);
1297 }
1298
1299 static void
1300 ev_view_presentation_change_page (EvViewPresentation *pview,
1301                                   GtkScrollType       scroll)
1302 {
1303         switch (scroll) {
1304         case GTK_SCROLL_PAGE_FORWARD:
1305                 ev_view_presentation_next_page (pview);
1306                 break;
1307         case GTK_SCROLL_PAGE_BACKWARD:
1308                 ev_view_presentation_previous_page (pview);
1309                 break;
1310         default:
1311                 g_assert_not_reached ();
1312         }
1313 }
1314
1315 static gboolean
1316 ev_view_presentation_scroll_event (GtkWidget      *widget,
1317                                    GdkEventScroll *event)
1318 {
1319         EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1320         guint               state;
1321
1322         state = event->state & gtk_accelerator_get_default_mod_mask ();
1323         if (state != 0)
1324                 return FALSE;
1325
1326         switch (event->direction) {
1327         case GDK_SCROLL_DOWN:
1328         case GDK_SCROLL_RIGHT:
1329                 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
1330                 break;
1331         case GDK_SCROLL_UP:
1332         case GDK_SCROLL_LEFT:
1333                 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1334                 break;
1335         }
1336
1337         return TRUE;
1338 }
1339
1340
1341 static void
1342 add_change_page_binding_keypad (GtkBindingSet  *binding_set,
1343                                 guint           keyval,
1344                                 GdkModifierType modifiers,
1345                                 GtkScrollType   scroll)
1346 {
1347         guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
1348
1349         gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1350                                       "change_page", 1,
1351                                       GTK_TYPE_SCROLL_TYPE, scroll);
1352         gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1353                                       "change_page", 1,
1354                                       GTK_TYPE_SCROLL_TYPE, scroll);
1355 }
1356
1357 static void
1358 ev_view_presentation_set_property (GObject      *object,
1359                                    guint         prop_id,
1360                                    const GValue *value,
1361                                    GParamSpec   *pspec)
1362 {
1363         EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1364
1365         switch (prop_id) {
1366         case PROP_DOCUMENT:
1367                 pview->document = g_value_dup_object (value);
1368                 pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
1369                 break;
1370         case PROP_CURRENT_PAGE:
1371                 pview->current_page = g_value_get_uint (value);
1372                 break;
1373         case PROP_ROTATION:
1374                 ev_view_presentation_set_rotation (pview, g_value_get_uint (value));
1375                 break;
1376         case PROP_INVERTED_COLORS:
1377                 pview->inverted_colors = g_value_get_boolean (value);
1378                 break;
1379         default:
1380                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1381         }
1382 }
1383
1384 static void
1385 ev_view_presentation_get_property (GObject    *object,
1386                                    guint       prop_id,
1387                                    GValue     *value,
1388                                    GParamSpec *pspec)
1389 {
1390         EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1391
1392         switch (prop_id) {
1393         case PROP_ROTATION:
1394                 g_value_set_uint (value, ev_view_presentation_get_rotation (pview));
1395                 break;
1396         default:
1397                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1398         }
1399 }
1400
1401 static GObject *
1402 ev_view_presentation_constructor (GType                  type,
1403                                                                   guint                  n_construct_properties,
1404                                                                   GObjectConstructParam *construct_params)
1405 {
1406         GObject            *object;
1407         EvViewPresentation *pview;
1408
1409         object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1410                                                                                   n_construct_properties,
1411                                                                                   construct_params);
1412         pview = EV_VIEW_PRESENTATION (object);
1413         pview->is_constructing = FALSE;
1414
1415         if (EV_IS_DOCUMENT_LINKS (pview->document)) {
1416                 pview->page_cache = ev_page_cache_new (pview->document);
1417                 ev_page_cache_set_flags (pview->page_cache, EV_PAGE_DATA_INCLUDE_LINKS);
1418         }
1419
1420         return object;
1421 }
1422
1423 static void
1424 ev_view_presentation_class_init (EvViewPresentationClass *klass)
1425 {
1426         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1427         GObjectClass   *gobject_class = G_OBJECT_CLASS (klass);
1428         GtkBindingSet  *binding_set;
1429         GtkCssProvider *provider;
1430
1431         klass->change_page = ev_view_presentation_change_page;
1432
1433         gobject_class->dispose = ev_view_presentation_dispose;
1434
1435         widget_class->get_preferred_width = ev_view_presentation_get_preferred_width;
1436         widget_class->get_preferred_height = ev_view_presentation_get_preferred_height;
1437         widget_class->realize = ev_view_presentation_realize;
1438         widget_class->draw = ev_view_presentation_draw;
1439         widget_class->key_press_event = ev_view_presentation_key_press_event;
1440         widget_class->button_release_event = ev_view_presentation_button_release_event;
1441         widget_class->focus_out_event = ev_view_presentation_focus_out;
1442         widget_class->motion_notify_event = ev_view_presentation_motion_notify_event;
1443         widget_class->scroll_event = ev_view_presentation_scroll_event;
1444
1445         gobject_class->constructor = ev_view_presentation_constructor;
1446         gobject_class->set_property = ev_view_presentation_set_property;
1447         gobject_class->get_property = ev_view_presentation_get_property;
1448
1449         g_object_class_install_property (gobject_class,
1450                                          PROP_DOCUMENT,
1451                                          g_param_spec_object ("document",
1452                                                               "Document",
1453                                                               "Document",
1454                                                               EV_TYPE_DOCUMENT,
1455                                                               G_PARAM_WRITABLE |
1456                                                               G_PARAM_CONSTRUCT_ONLY));
1457         g_object_class_install_property (gobject_class,
1458                                          PROP_CURRENT_PAGE,
1459                                          g_param_spec_uint ("current_page",
1460                                                             "Current Page",
1461                                                             "The current page",
1462                                                             0, G_MAXUINT, 0,
1463                                                             G_PARAM_WRITABLE |
1464                                                             G_PARAM_CONSTRUCT_ONLY));
1465         g_object_class_install_property (gobject_class,
1466                                          PROP_ROTATION,
1467                                          g_param_spec_uint ("rotation",
1468                                                             "Rotation",
1469                                                             "Current rotation angle",
1470                                                             0, 360, 0,
1471                                                             G_PARAM_READWRITE |
1472                                                             G_PARAM_CONSTRUCT));
1473         g_object_class_install_property (gobject_class,
1474                                          PROP_INVERTED_COLORS,
1475                                          g_param_spec_boolean ("inverted_colors",
1476                                                                "Inverted Colors",
1477                                                                "Whether presentation is displayed with inverted colors",
1478                                                                FALSE,
1479                                                                G_PARAM_WRITABLE |
1480                                                                G_PARAM_CONSTRUCT_ONLY));
1481
1482         signals[CHANGE_PAGE] =
1483                 g_signal_new ("change_page",
1484                               G_OBJECT_CLASS_TYPE (gobject_class),
1485                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1486                               G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
1487                               NULL, NULL,
1488                               g_cclosure_marshal_VOID__ENUM,
1489                               G_TYPE_NONE, 1,
1490                               GTK_TYPE_SCROLL_TYPE);
1491         signals [PAGE_CHANGED] =
1492                 g_signal_new ("page-changed",
1493                               EV_TYPE_VIEW_PRESENTATION,
1494                               G_SIGNAL_RUN_LAST,
1495                               G_STRUCT_OFFSET (EvViewPresentationClass, page_changed),
1496                               NULL, NULL,
1497                               g_cclosure_marshal_VOID__INT,
1498                               G_TYPE_NONE, 2,
1499                               G_TYPE_INT, G_TYPE_INT);
1500         signals[FINISHED] =
1501                 g_signal_new ("finished",
1502                               G_OBJECT_CLASS_TYPE (gobject_class),
1503                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1504                               G_STRUCT_OFFSET (EvViewPresentationClass, finished),
1505                               NULL, NULL,
1506                               g_cclosure_marshal_VOID__VOID,
1507                               G_TYPE_NONE, 0,
1508                               G_TYPE_NONE);
1509         signals[SIGNAL_EXTERNAL_LINK] =
1510                 g_signal_new ("external-link",
1511                               G_TYPE_FROM_CLASS (gobject_class),
1512                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1513                               G_STRUCT_OFFSET (EvViewPresentationClass, external_link),
1514                               NULL, NULL,
1515                               g_cclosure_marshal_VOID__OBJECT,
1516                               G_TYPE_NONE, 1,
1517                               G_TYPE_OBJECT);
1518
1519         binding_set = gtk_binding_set_by_class (klass);
1520         add_change_page_binding_keypad (binding_set, GDK_KEY_Left,  0, GTK_SCROLL_PAGE_BACKWARD);
1521         add_change_page_binding_keypad (binding_set, GDK_KEY_Right, 0, GTK_SCROLL_PAGE_FORWARD);
1522         add_change_page_binding_keypad (binding_set, GDK_KEY_Up,    0, GTK_SCROLL_PAGE_BACKWARD);
1523         add_change_page_binding_keypad (binding_set, GDK_KEY_Down,  0, GTK_SCROLL_PAGE_FORWARD);
1524         gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
1525                                       "change_page", 1,
1526                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1527         gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0,
1528                                       "change_page", 1,
1529                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1530         gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
1531                                       "change_page", 1,
1532                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1533         gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
1534                                       "change_page", 1,
1535                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1536         gtk_binding_entry_add_signal (binding_set, GDK_KEY_J, 0,
1537                                       "change_page", 1,
1538                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1539         gtk_binding_entry_add_signal (binding_set, GDK_KEY_H, 0,
1540                                       "change_page", 1,
1541                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1542         gtk_binding_entry_add_signal (binding_set, GDK_KEY_L, 0,
1543                                       "change_page", 1,
1544                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1545         gtk_binding_entry_add_signal (binding_set, GDK_KEY_K, 0,
1546                                       "change_page", 1,
1547                                       GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1548
1549         provider = gtk_css_provider_new ();
1550         gtk_css_provider_load_from_data (provider,
1551                                          "EvViewPresentation {\n"
1552                                          " background-color: black; }",
1553                                          -1, NULL);
1554         gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
1555                                                    GTK_STYLE_PROVIDER (provider),
1556                                                    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1557         g_object_unref (provider);
1558 }
1559
1560 static void
1561 ev_view_presentation_init (EvViewPresentation *pview)
1562 {
1563         gtk_widget_set_can_focus (GTK_WIDGET (pview), TRUE);
1564         pview->is_constructing = TRUE;
1565 }
1566
1567 GtkWidget *
1568 ev_view_presentation_new (EvDocument *document,
1569                           guint       current_page,
1570                           guint       rotation,
1571                           gboolean    inverted_colors)
1572 {
1573         g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
1574         g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
1575
1576         return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
1577                                          "document", document,
1578                                          "current_page", current_page,
1579                                          "rotation", rotation,
1580                                          "inverted_colors", inverted_colors,
1581                                          NULL));
1582 }
1583
1584 guint
1585 ev_view_presentation_get_current_page (EvViewPresentation *pview)
1586 {
1587         return pview->current_page;
1588 }
1589
1590 void
1591 ev_view_presentation_set_rotation (EvViewPresentation *pview,
1592                                    gint                rotation)
1593 {
1594         if (rotation >= 360)
1595                 rotation -= 360;
1596         else if (rotation < 0)
1597                 rotation += 360;
1598
1599         if (pview->rotation == rotation)
1600                 return;
1601
1602         pview->rotation = rotation;
1603         g_object_notify (G_OBJECT (pview), "rotation");
1604         if (pview->is_constructing)
1605                 return;
1606
1607         pview->scale = 0;
1608         ev_view_presentation_reset_jobs (pview);
1609         ev_view_presentation_update_current_page (pview, pview->current_page);
1610 }
1611
1612 guint
1613 ev_view_presentation_get_rotation (EvViewPresentation *pview)
1614 {
1615         return pview->rotation;
1616 }