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