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