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