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