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