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