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