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