1 /* ev-view-presentation.c
2 * this file is part of evince, a gnome document viewer
4 * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
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.
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.
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.
24 #include <glib/gi18n-lib.h>
25 #include <gdk/gdkkeysyms.h>
27 #include "ev-view-presentation.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"
49 EV_PRESENTATION_NORMAL,
50 EV_PRESENTATION_BLACK,
51 EV_PRESENTATION_WHITE,
53 } EvPresentationState;
55 struct _EvViewPresentation
60 cairo_surface_t *current_surface;
63 gboolean inverted_colors;
64 EvPresentationState state;
71 guint hide_cursor_timeout_id;
74 GtkWidget *goto_window;
75 GtkWidget *goto_entry;
78 guint trans_timeout_id;
81 gboolean enable_animations;
82 EvTransitionAnimation *animation;
85 EvPageCache *page_cache;
92 struct _EvViewPresentationClass
94 GtkWidgetClass base_class;
97 void (* change_page) (EvViewPresentation *pview,
98 GtkScrollType scroll);
99 void (* finished) (EvViewPresentation *pview);
102 static guint signals[N_SIGNALS] = { 0 };
104 static void ev_view_presentation_next_page (EvViewPresentation *pview);
105 static void ev_view_presentation_previous_page (EvViewPresentation *pview);
106 static void ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
110 #define HIDE_CURSOR_TIMEOUT 5
112 G_DEFINE_TYPE (EvViewPresentation, ev_view_presentation, GTK_TYPE_WIDGET)
114 static GdkRGBA black = { 0., 0., 0., 1. };
115 static GdkRGBA white = { 1., 1., 1., 1. };
118 ev_view_presentation_set_normal (EvViewPresentation *pview)
120 GtkWidget *widget = GTK_WIDGET (pview);
122 if (pview->state == EV_PRESENTATION_NORMAL)
125 pview->state = EV_PRESENTATION_NORMAL;
126 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
127 gtk_widget_queue_draw (widget);
131 ev_view_presentation_set_black (EvViewPresentation *pview)
133 GtkWidget *widget = GTK_WIDGET (pview);
135 if (pview->state == EV_PRESENTATION_BLACK)
138 pview->state = EV_PRESENTATION_BLACK;
139 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
140 gtk_widget_queue_draw (widget);
144 ev_view_presentation_set_white (EvViewPresentation *pview)
146 GtkWidget *widget = GTK_WIDGET (pview);
148 if (pview->state == EV_PRESENTATION_WHITE)
151 pview->state = EV_PRESENTATION_WHITE;
152 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &white);
153 gtk_widget_queue_draw (widget);
157 ev_view_presentation_set_end (EvViewPresentation *pview)
159 GtkWidget *widget = GTK_WIDGET (pview);
161 if (pview->state == EV_PRESENTATION_END)
164 pview->state = EV_PRESENTATION_END;
165 gtk_widget_queue_draw (widget);
169 ev_view_presentation_get_scale_for_page (EvViewPresentation *pview,
172 if (!ev_document_is_page_size_uniform (pview->document) || pview->scale == 0) {
173 gdouble width, height;
175 ev_document_get_page_size (pview->document, page, &width, &height);
176 if (pview->rotation == 90 || pview->rotation == 270) {
183 pview->scale = MIN (pview->monitor_width / width, pview->monitor_height / height);
190 ev_view_presentation_get_page_area (EvViewPresentation *pview,
193 GtkWidget *widget = GTK_WIDGET (pview);
194 GtkAllocation allocation;
195 gdouble doc_width, doc_height;
196 gint view_width, view_height;
199 ev_document_get_page_size (pview->document,
201 &doc_width, &doc_height);
202 scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
204 if (pview->rotation == 90 || pview->rotation == 270) {
205 view_width = (gint)((doc_height * scale) + 0.5);
206 view_height = (gint)((doc_width * scale) + 0.5);
208 view_width = (gint)((doc_width * scale) + 0.5);
209 view_height = (gint)((doc_height * scale) + 0.5);
212 gtk_widget_get_allocation (widget, &allocation);
214 area->x = (MAX (0, allocation.width - view_width)) / 2;
215 area->y = (MAX (0, allocation.height - view_height)) / 2;
216 area->width = view_width;
217 area->height = view_height;
220 /* Page Transition */
222 transition_next_page (EvViewPresentation *pview)
224 ev_view_presentation_next_page (pview);
230 ev_view_presentation_transition_stop (EvViewPresentation *pview)
232 if (pview->trans_timeout_id > 0)
233 g_source_remove (pview->trans_timeout_id);
234 pview->trans_timeout_id = 0;
238 ev_view_presentation_transition_start (EvViewPresentation *pview)
242 if (!EV_IS_DOCUMENT_TRANSITION (pview->document))
245 ev_view_presentation_transition_stop (pview);
247 duration = ev_document_transition_get_page_duration (EV_DOCUMENT_TRANSITION (pview->document),
248 pview->current_page);
250 pview->trans_timeout_id =
251 g_timeout_add_seconds (duration,
252 (GSourceFunc) transition_next_page,
259 ev_view_presentation_animation_cancel (EvViewPresentation *pview)
261 if (pview->animation) {
262 g_object_unref (pview->animation);
263 pview->animation = NULL;
268 ev_view_presentation_transition_animation_finish (EvViewPresentation *pview)
270 ev_view_presentation_animation_cancel (pview);
271 ev_view_presentation_transition_start (pview);
272 gtk_widget_queue_draw (GTK_WIDGET (pview));
276 ev_view_presentation_transition_animation_frame (EvViewPresentation *pview,
279 gtk_widget_queue_draw (GTK_WIDGET (pview));
283 ev_view_presentation_animation_start (EvViewPresentation *pview,
286 EvTransitionEffect *effect = NULL;
287 cairo_surface_t *surface;
290 if (!pview->enable_animations)
293 if (pview->current_page == new_page)
296 effect = ev_document_transition_get_effect (EV_DOCUMENT_TRANSITION (pview->document),
301 pview->animation = ev_transition_animation_new (effect);
303 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
304 ev_transition_animation_set_origin_surface (pview->animation,
306 surface : pview->current_surface);
308 jump = new_page - pview->current_page;
310 surface = pview->prev_job ? EV_JOB_RENDER (pview->prev_job)->surface : NULL;
312 surface = pview->next_job ? EV_JOB_RENDER (pview->next_job)->surface : NULL;
316 ev_transition_animation_set_dest_surface (pview->animation, surface);
318 g_signal_connect_swapped (pview->animation, "frame",
319 G_CALLBACK (ev_view_presentation_transition_animation_frame),
321 g_signal_connect_swapped (pview->animation, "finished",
322 G_CALLBACK (ev_view_presentation_transition_animation_finish),
326 /* Page Navigation */
328 job_finished_cb (EvJob *job,
329 EvViewPresentation *pview)
331 EvJobRender *job_render = EV_JOB_RENDER (job);
333 if (pview->inverted_colors)
334 ev_document_misc_invert_surface (job_render->surface);
336 if (job != pview->curr_job)
339 if (pview->animation) {
340 ev_transition_animation_set_dest_surface (pview->animation,
341 job_render->surface);
343 ev_view_presentation_transition_start (pview);
344 gtk_widget_queue_draw (GTK_WIDGET (pview));
349 ev_view_presentation_schedule_new_job (EvViewPresentation *pview,
351 EvJobPriority priority)
356 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
359 scale = ev_view_presentation_get_scale_for_page (pview, page);
360 job = ev_job_render_new (pview->document, page, pview->rotation, scale, 0, 0);
361 g_signal_connect (job, "finished",
362 G_CALLBACK (job_finished_cb),
364 ev_job_scheduler_push_job (job, priority);
370 ev_view_presentation_delete_job (EvViewPresentation *pview,
376 g_signal_handlers_disconnect_by_func (job, job_finished_cb, pview);
378 g_object_unref (job);
382 ev_view_presentation_update_current_page (EvViewPresentation *pview,
387 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
390 ev_view_presentation_animation_cancel (pview);
391 ev_view_presentation_animation_start (pview, page);
393 jump = page - pview->current_page;
397 if (!pview->curr_job)
398 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
399 if (!pview->next_job)
400 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
401 if (!pview->prev_job)
402 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
405 ev_view_presentation_delete_job (pview, pview->next_job);
406 pview->next_job = pview->curr_job;
407 pview->curr_job = pview->prev_job;
409 if (!pview->curr_job)
410 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
412 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
413 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
414 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
418 ev_view_presentation_delete_job (pview, pview->prev_job);
419 pview->prev_job = pview->curr_job;
420 pview->curr_job = pview->next_job;
422 if (!pview->curr_job)
423 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
425 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
426 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
427 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
431 ev_view_presentation_delete_job (pview, pview->next_job);
432 ev_view_presentation_delete_job (pview, pview->curr_job);
433 pview->next_job = pview->prev_job;
435 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
436 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
437 if (!pview->next_job)
438 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
440 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
443 ev_view_presentation_delete_job (pview, pview->prev_job);
444 ev_view_presentation_delete_job (pview, pview->curr_job);
445 pview->prev_job = pview->next_job;
447 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
448 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
449 if (!pview->prev_job)
450 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
452 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
455 ev_view_presentation_delete_job (pview, pview->prev_job);
456 ev_view_presentation_delete_job (pview, pview->curr_job);
457 ev_view_presentation_delete_job (pview, pview->next_job);
459 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
461 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
462 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
464 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
465 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
469 pview->current_page = page;
471 if (pview->page_cache)
472 ev_page_cache_set_page_range (pview->page_cache, page, page);
474 if (pview->cursor != EV_VIEW_CURSOR_HIDDEN) {
477 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
478 ev_view_presentation_set_cursor_for_location (pview, x, y);
481 if (EV_JOB_RENDER (pview->curr_job)->surface)
482 gtk_widget_queue_draw (GTK_WIDGET (pview));
486 ev_view_presentation_next_page (EvViewPresentation *pview)
491 switch (pview->state) {
492 case EV_PRESENTATION_BLACK:
493 case EV_PRESENTATION_WHITE:
494 ev_view_presentation_set_normal (pview);
495 case EV_PRESENTATION_END:
497 case EV_PRESENTATION_NORMAL:
501 n_pages = ev_document_get_n_pages (pview->document);
502 new_page = pview->current_page + 1;
504 if (new_page == n_pages)
505 ev_view_presentation_set_end (pview);
507 ev_view_presentation_update_current_page (pview, new_page);
511 ev_view_presentation_previous_page (EvViewPresentation *pview)
515 switch (pview->state) {
516 case EV_PRESENTATION_BLACK:
517 case EV_PRESENTATION_WHITE:
518 ev_view_presentation_set_normal (pview);
520 case EV_PRESENTATION_END:
521 pview->state = EV_PRESENTATION_NORMAL;
522 new_page = pview->current_page;
524 case EV_PRESENTATION_NORMAL:
525 new_page = pview->current_page - 1;
529 ev_view_presentation_update_current_page (pview, new_page);
533 #define KEY_IS_NUMERIC(keyval) \
534 ((keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9) || (keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9))
536 /* Cut and paste from gtkwindow.c */
538 send_focus_change (GtkWidget *widget,
541 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
543 fevent->focus_change.type = GDK_FOCUS_CHANGE;
544 fevent->focus_change.window = gtk_widget_get_window (widget);
545 fevent->focus_change.in = in;
546 if (fevent->focus_change.window)
547 g_object_ref (fevent->focus_change.window);
549 gtk_widget_send_focus_change (widget, fevent);
551 gdk_event_free (fevent);
555 ev_view_presentation_goto_window_hide (EvViewPresentation *pview)
557 /* send focus-in event */
558 send_focus_change (pview->goto_entry, FALSE);
559 gtk_widget_hide (pview->goto_window);
560 gtk_entry_set_text (GTK_ENTRY (pview->goto_entry), "");
564 ev_view_presentation_goto_window_delete_event (GtkWidget *widget,
566 EvViewPresentation *pview)
568 ev_view_presentation_goto_window_hide (pview);
574 ev_view_presentation_goto_window_key_press_event (GtkWidget *widget,
576 EvViewPresentation *pview)
578 switch (event->keyval) {
582 case GDK_KEY_ISO_Left_Tab:
583 ev_view_presentation_goto_window_hide (pview);
586 case GDK_KEY_KP_Enter:
587 case GDK_KEY_ISO_Enter:
588 case GDK_KEY_BackSpace:
592 if (!KEY_IS_NUMERIC (event->keyval))
600 ev_view_presentation_goto_window_button_press_event (GtkWidget *widget,
601 GdkEventButton *event,
602 EvViewPresentation *pview)
604 ev_view_presentation_goto_window_hide (pview);
610 ev_view_presentation_goto_entry_activate (GtkEntry *entry,
611 EvViewPresentation *pview)
616 text = gtk_entry_get_text (entry);
617 page = atoi (text) - 1;
619 ev_view_presentation_goto_window_hide (pview);
620 ev_view_presentation_update_current_page (pview, page);
624 ev_view_presentation_goto_window_create (EvViewPresentation *pview)
626 GtkWidget *frame, *hbox, *label;
627 GtkWindow *toplevel, *goto_window;
629 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (pview)));
630 goto_window = GTK_WINDOW (pview->goto_window);
632 if (pview->goto_window) {
633 if (gtk_window_has_group (toplevel))
634 gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
635 else if (gtk_window_has_group (goto_window))
636 gtk_window_group_remove_window (gtk_window_get_group (goto_window), goto_window);
641 pview->goto_window = gtk_window_new (GTK_WINDOW_POPUP);
642 gtk_window_set_screen (goto_window, gtk_widget_get_screen (GTK_WIDGET (pview)));
644 if (gtk_window_has_group (toplevel))
645 gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
647 gtk_window_set_modal (goto_window, TRUE);
649 g_signal_connect (pview->goto_window, "delete_event",
650 G_CALLBACK (ev_view_presentation_goto_window_delete_event),
652 g_signal_connect (pview->goto_window, "key_press_event",
653 G_CALLBACK (ev_view_presentation_goto_window_key_press_event),
655 g_signal_connect (pview->goto_window, "button_press_event",
656 G_CALLBACK (ev_view_presentation_goto_window_button_press_event),
659 frame = gtk_frame_new (NULL);
660 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
661 gtk_container_add (GTK_CONTAINER (pview->goto_window), frame);
662 gtk_widget_show (frame);
664 hbox = gtk_hbox_new (FALSE, 0);
665 gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
666 gtk_container_add (GTK_CONTAINER (frame), hbox);
667 gtk_widget_show (hbox);
669 label = gtk_label_new (_("Jump to page:"));
670 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 3);
671 gtk_widget_show (label);
672 gtk_widget_realize (label);
674 pview->goto_entry = gtk_entry_new ();
675 g_signal_connect (pview->goto_entry, "activate",
676 G_CALLBACK (ev_view_presentation_goto_entry_activate),
678 gtk_box_pack_start (GTK_BOX (hbox), pview->goto_entry, TRUE, TRUE, 0);
679 gtk_widget_show (pview->goto_entry);
680 gtk_widget_realize (pview->goto_entry);
684 ev_view_presentation_goto_entry_grab_focus (EvViewPresentation *pview)
686 GtkWidgetClass *entry_parent_class;
688 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (pview->goto_entry));
689 (entry_parent_class->grab_focus) (pview->goto_entry);
691 send_focus_change (pview->goto_entry, TRUE);
695 ev_view_presentation_goto_window_send_key_event (EvViewPresentation *pview,
698 GdkEventKey *new_event;
701 /* Move goto window off screen */
702 screen = gtk_widget_get_screen (GTK_WIDGET (pview));
703 gtk_window_move (GTK_WINDOW (pview->goto_window),
704 gdk_screen_get_width (screen) + 1,
705 gdk_screen_get_height (screen) + 1);
706 gtk_widget_show (pview->goto_window);
708 new_event = (GdkEventKey *) gdk_event_copy (event);
709 g_object_unref (new_event->window);
710 new_event->window = gtk_widget_get_window (pview->goto_window);
711 if (new_event->window)
712 g_object_ref (new_event->window);
713 gtk_widget_realize (pview->goto_window);
715 gtk_widget_event (pview->goto_window, (GdkEvent *)new_event);
716 gdk_event_free ((GdkEvent *)new_event);
717 gtk_widget_hide (pview->goto_window);
722 ev_view_presentation_link_is_supported (EvViewPresentation *pview,
725 EvLinkAction *action;
727 action = ev_link_get_action (link);
731 switch (ev_link_action_get_action_type (action)) {
732 case EV_LINK_ACTION_TYPE_GOTO_DEST:
733 return ev_link_action_get_dest (action) != NULL;
734 case EV_LINK_ACTION_TYPE_NAMED:
744 ev_view_presentation_get_link_at_location (EvViewPresentation *pview,
748 GdkRectangle page_area;
749 EvMappingList *link_mapping;
751 gdouble width, height;
752 gdouble new_x, new_y;
755 if (!pview->page_cache)
758 ev_document_get_page_size (pview->document, pview->current_page, &width, &height);
759 ev_view_presentation_get_page_area (pview, &page_area);
760 scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
761 x = (x - page_area.x) / scale;
762 y = (y - page_area.y) / scale;
763 switch (pview->rotation) {
782 g_assert_not_reached ();
785 link_mapping = ev_page_cache_get_link_mapping (pview->page_cache, pview->current_page);
787 link = link_mapping ? ev_mapping_list_get_data (link_mapping, new_x, new_y) : NULL;
789 return link && ev_view_presentation_link_is_supported (pview, link) ? link : NULL;
793 ev_vew_presentation_goto_link_dest (EvViewPresentation *pview,
796 EvLinkAction *action;
798 action = ev_link_get_action (link);
800 if (ev_link_action_get_action_type (action) == EV_LINK_ACTION_TYPE_NAMED) {
801 const gchar *name = ev_link_action_get_name (action);
803 if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
804 ev_view_presentation_update_current_page (pview, 0);
805 } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
806 ev_view_presentation_update_current_page (pview, pview->current_page - 1);
807 } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
808 ev_view_presentation_update_current_page (pview, pview->current_page + 1);
809 } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
812 n_pages = ev_document_get_n_pages (pview->document);
813 ev_view_presentation_update_current_page (pview, n_pages - 1);
819 dest = ev_link_action_get_dest (action);
820 page = ev_document_links_get_dest_page (EV_DOCUMENT_LINKS (pview->document), dest);
821 ev_view_presentation_update_current_page (pview, page);
827 ev_view_presentation_set_cursor (EvViewPresentation *pview,
828 EvViewCursor view_cursor)
833 if (pview->cursor == view_cursor)
836 widget = GTK_WIDGET (pview);
837 if (!gtk_widget_get_realized (widget))
838 gtk_widget_realize (widget);
840 pview->cursor = view_cursor;
842 cursor = ev_view_cursor_new (gtk_widget_get_display (widget), view_cursor);
843 gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
846 gdk_cursor_unref (cursor);
850 ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
854 if (ev_view_presentation_get_link_at_location (pview, x, y))
855 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_LINK);
857 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_NORMAL);
861 hide_cursor_timeout_cb (EvViewPresentation *pview)
863 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_HIDDEN);
864 pview->hide_cursor_timeout_id = 0;
870 ev_view_presentation_hide_cursor_timeout_stop (EvViewPresentation *pview)
872 if (pview->hide_cursor_timeout_id > 0)
873 g_source_remove (pview->hide_cursor_timeout_id);
874 pview->hide_cursor_timeout_id = 0;
878 ev_view_presentation_hide_cursor_timeout_start (EvViewPresentation *pview)
880 ev_view_presentation_hide_cursor_timeout_stop (pview);
881 pview->hide_cursor_timeout_id =
882 g_timeout_add_seconds (HIDE_CURSOR_TIMEOUT,
883 (GSourceFunc)hide_cursor_timeout_cb,
888 ev_view_presentation_update_current_surface (EvViewPresentation *pview,
889 cairo_surface_t *surface)
891 if (!surface || pview->current_surface == surface)
894 cairo_surface_reference (surface);
895 if (pview->current_surface)
896 cairo_surface_destroy (pview->current_surface);
897 pview->current_surface = surface;
901 ev_view_presentation_dispose (GObject *object)
903 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
905 if (pview->document) {
906 g_object_unref (pview->document);
907 pview->document = NULL;
910 ev_view_presentation_animation_cancel (pview);
911 ev_view_presentation_transition_stop (pview);
912 ev_view_presentation_hide_cursor_timeout_stop (pview);
914 if (pview->curr_job) {
915 ev_view_presentation_delete_job (pview, pview->curr_job);
916 pview->curr_job = NULL;
919 if (pview->prev_job) {
920 ev_view_presentation_delete_job (pview, pview->prev_job);
921 pview->prev_job = NULL;
924 if (pview->next_job) {
925 ev_view_presentation_delete_job (pview, pview->next_job);
926 pview->next_job = NULL;
929 if (pview->current_surface) {
930 cairo_surface_destroy (pview->current_surface);
931 pview->current_surface = NULL;
934 if (pview->page_cache) {
935 g_object_unref (pview->page_cache);
936 pview->page_cache = NULL;
939 if (pview->goto_window) {
940 gtk_widget_destroy (pview->goto_window);
941 pview->goto_window = NULL;
942 pview->goto_entry = NULL;
945 G_OBJECT_CLASS (ev_view_presentation_parent_class)->dispose (object);
949 ev_view_presentation_get_preferred_width (GtkWidget *widget,
953 *minimum = *natural = 0;
957 ev_view_presentation_get_preferred_height (GtkWidget *widget,
961 *minimum = *natural = 0;
965 ev_view_presentation_draw_end_page (EvViewPresentation *pview,
968 GtkWidget *widget = GTK_WIDGET (pview);
970 PangoFontDescription *font_desc;
972 GtkAllocation allocation;
973 GdkRectangle area = {0};
974 const gchar *text = _("End of presentation. Click to exit.");
976 if (pview->state != EV_PRESENTATION_END)
979 layout = gtk_widget_create_pango_layout (widget, NULL);
980 markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", text);
981 pango_layout_set_markup (layout, markup, -1);
984 font_desc = pango_font_description_new ();
985 pango_font_description_set_size (font_desc, 16 * PANGO_SCALE);
986 pango_layout_set_font_description (layout, font_desc);
988 gtk_widget_get_allocation (widget, &allocation);
989 area.width = allocation.width;
990 area.height = allocation.height;
992 gtk_render_layout (gtk_widget_get_style_context (widget),
995 pango_font_description_free (font_desc);
996 g_object_unref (layout);
1000 ev_view_presentation_draw (GtkWidget *widget,
1003 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1004 GdkRectangle page_area;
1005 GdkRectangle overlap;
1006 cairo_surface_t *surface;
1007 GdkRectangle clip_rect;
1009 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
1012 switch (pview->state) {
1013 case EV_PRESENTATION_END:
1014 ev_view_presentation_draw_end_page (pview, cr);
1016 case EV_PRESENTATION_BLACK:
1017 case EV_PRESENTATION_WHITE:
1019 case EV_PRESENTATION_NORMAL:
1023 if (pview->animation) {
1024 if (ev_transition_animation_ready (pview->animation)) {
1025 ev_view_presentation_get_page_area (pview, &page_area);
1029 /* normalize to x=0, y=0 */
1030 cairo_translate (cr, page_area.x, page_area.y);
1031 page_area.x = page_area.y = 0;
1033 /* Try to fix rounding errors */
1036 ev_transition_animation_paint (pview->animation, cr, page_area);
1044 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
1046 ev_view_presentation_update_current_surface (pview, surface);
1047 } else if (pview->current_surface) {
1048 surface = pview->current_surface;
1053 ev_view_presentation_get_page_area (pview, &page_area);
1054 if (gdk_rectangle_intersect (&page_area, &clip_rect, &overlap)) {
1057 /* Try to fix rounding errors. See bug #438760 */
1058 if (overlap.width == page_area.width)
1061 cairo_rectangle (cr, overlap.x, overlap.y, overlap.width, overlap.height);
1062 cairo_set_source_surface (cr, surface, page_area.x, page_area.y);
1072 ev_view_presentation_key_press_event (GtkWidget *widget,
1075 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1077 if (pview->state == EV_PRESENTATION_END)
1078 return gtk_bindings_activate_event (G_OBJECT (widget), event);
1080 switch (event->keyval) {
1083 case GDK_KEY_period:
1084 case GDK_KEY_KP_Decimal:
1085 if (pview->state == EV_PRESENTATION_BLACK)
1086 ev_view_presentation_set_normal (pview);
1088 ev_view_presentation_set_black (pview);
1093 if (pview->state == EV_PRESENTATION_WHITE)
1094 ev_view_presentation_set_normal (pview);
1096 ev_view_presentation_set_white (pview);
1100 if (pview->state == EV_PRESENTATION_NORMAL) {
1101 ev_view_presentation_update_current_page (pview, 0);
1106 if (pview->state == EV_PRESENTATION_NORMAL) {
1109 page = ev_document_get_n_pages (pview->document) - 1;
1110 ev_view_presentation_update_current_page (pview, page);
1119 ev_view_presentation_set_normal (pview);
1121 if (ev_document_get_n_pages (pview->document) > 1 && KEY_IS_NUMERIC (event->keyval)) {
1124 ev_view_presentation_goto_window_create (pview);
1125 ev_view_presentation_goto_window_send_key_event (pview, (GdkEvent *)event);
1126 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
1127 gtk_window_move (GTK_WINDOW (pview->goto_window), x, y);
1128 gtk_widget_show (pview->goto_window);
1129 ev_view_presentation_goto_entry_grab_focus (pview);
1134 return gtk_bindings_activate_event (G_OBJECT (widget), event);
1138 ev_view_presentation_button_release_event (GtkWidget *widget,
1139 GdkEventButton *event)
1141 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1143 switch (event->button) {
1147 if (pview->state == EV_PRESENTATION_END) {
1148 g_signal_emit (pview, signals[FINISHED], 0, NULL);
1153 link = ev_view_presentation_get_link_at_location (pview,
1157 ev_vew_presentation_goto_link_dest (pview, link);
1159 ev_view_presentation_next_page (pview);
1163 ev_view_presentation_previous_page (pview);
1173 ev_view_presentation_focus_out (GtkWidget *widget,
1174 GdkEventFocus *event)
1176 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1178 if (pview->goto_window)
1179 ev_view_presentation_goto_window_hide (pview);
1185 ev_view_presentation_motion_notify_event (GtkWidget *widget,
1186 GdkEventMotion *event)
1188 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1190 ev_view_presentation_hide_cursor_timeout_start (pview);
1191 ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
1197 init_presentation (GtkWidget *widget)
1199 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1200 GdkScreen *screen = gtk_widget_get_screen (widget);
1201 GdkRectangle monitor;
1204 monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget));
1205 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1206 pview->monitor_width = monitor.width;
1207 pview->monitor_height = monitor.height;
1209 ev_view_presentation_update_current_page (pview, pview->current_page);
1210 ev_view_presentation_hide_cursor_timeout_start (pview);
1216 ev_view_presentation_realize (GtkWidget *widget)
1219 GdkWindowAttr attributes;
1220 GtkAllocation allocation;
1222 gtk_widget_set_realized (widget, TRUE);
1224 attributes.window_type = GDK_WINDOW_CHILD;
1225 attributes.wclass = GDK_INPUT_OUTPUT;
1226 attributes.visual = gtk_widget_get_visual (widget);
1228 gtk_widget_get_allocation (widget, &allocation);
1229 attributes.x = allocation.x;
1230 attributes.y = allocation.y;
1231 attributes.width = allocation.width;
1232 attributes.height = allocation.height;
1233 attributes.event_mask = GDK_EXPOSURE_MASK |
1234 GDK_BUTTON_PRESS_MASK |
1235 GDK_BUTTON_RELEASE_MASK |
1237 GDK_KEY_PRESS_MASK |
1238 GDK_POINTER_MOTION_MASK |
1239 GDK_POINTER_MOTION_HINT_MASK |
1240 GDK_ENTER_NOTIFY_MASK |
1241 GDK_LEAVE_NOTIFY_MASK;
1243 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1245 GDK_WA_X | GDK_WA_Y |
1248 gdk_window_set_user_data (window, widget);
1249 gtk_widget_set_window (widget, window);
1251 gdk_window_set_background_rgba (window, &black);
1253 g_idle_add ((GSourceFunc)init_presentation, widget);
1257 ev_view_presentation_change_page (EvViewPresentation *pview,
1258 GtkScrollType scroll)
1261 case GTK_SCROLL_PAGE_FORWARD:
1262 ev_view_presentation_next_page (pview);
1264 case GTK_SCROLL_PAGE_BACKWARD:
1265 ev_view_presentation_previous_page (pview);
1268 g_assert_not_reached ();
1273 ev_view_presentation_scroll_event (GtkWidget *widget,
1274 GdkEventScroll *event)
1276 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1279 state = event->state & gtk_accelerator_get_default_mod_mask ();
1283 switch (event->direction) {
1284 case GDK_SCROLL_DOWN:
1285 case GDK_SCROLL_RIGHT:
1286 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
1289 case GDK_SCROLL_LEFT:
1290 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1299 add_change_page_binding_keypad (GtkBindingSet *binding_set,
1301 GdkModifierType modifiers,
1302 GtkScrollType scroll)
1304 guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
1306 gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1308 GTK_TYPE_SCROLL_TYPE, scroll);
1309 gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1311 GTK_TYPE_SCROLL_TYPE, scroll);
1315 ev_view_presentation_set_property (GObject *object,
1317 const GValue *value,
1320 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1324 pview->document = g_value_dup_object (value);
1325 pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
1327 case PROP_CURRENT_PAGE:
1328 pview->current_page = g_value_get_uint (value);
1331 pview->rotation = g_value_get_uint (value);
1333 case PROP_INVERTED_COLORS:
1334 pview->inverted_colors = g_value_get_boolean (value);
1337 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1342 ev_view_presentation_constructor (GType type,
1343 guint n_construct_properties,
1344 GObjectConstructParam *construct_params)
1347 EvViewPresentation *pview;
1349 object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1350 n_construct_properties,
1352 pview = EV_VIEW_PRESENTATION (object);
1354 if (EV_IS_DOCUMENT_LINKS (pview->document)) {
1355 pview->page_cache = ev_page_cache_new (pview->document);
1356 ev_page_cache_set_flags (pview->page_cache, EV_PAGE_DATA_INCLUDE_LINKS);
1363 ev_view_presentation_class_init (EvViewPresentationClass *klass)
1365 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1366 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1367 GtkBindingSet *binding_set;
1369 klass->change_page = ev_view_presentation_change_page;
1371 gobject_class->dispose = ev_view_presentation_dispose;
1373 widget_class->get_preferred_width = ev_view_presentation_get_preferred_width;
1374 widget_class->get_preferred_height = ev_view_presentation_get_preferred_height;
1375 widget_class->realize = ev_view_presentation_realize;
1376 widget_class->draw = ev_view_presentation_draw;
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;
1383 gobject_class->constructor = ev_view_presentation_constructor;
1384 gobject_class->set_property = ev_view_presentation_set_property;
1386 g_object_class_install_property (gobject_class,
1388 g_param_spec_object ("document",
1393 G_PARAM_CONSTRUCT_ONLY));
1394 g_object_class_install_property (gobject_class,
1396 g_param_spec_uint ("current_page",
1401 G_PARAM_CONSTRUCT_ONLY));
1402 g_object_class_install_property (gobject_class,
1404 g_param_spec_uint ("rotation",
1406 "Current rotation angle",
1409 G_PARAM_CONSTRUCT_ONLY));
1410 g_object_class_install_property (gobject_class,
1411 PROP_INVERTED_COLORS,
1412 g_param_spec_boolean ("inverted_colors",
1414 "Whether presentation is displayed with inverted colors",
1417 G_PARAM_CONSTRUCT_ONLY));
1419 signals[CHANGE_PAGE] =
1420 g_signal_new ("change_page",
1421 G_OBJECT_CLASS_TYPE (gobject_class),
1422 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1423 G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
1425 g_cclosure_marshal_VOID__ENUM,
1427 GTK_TYPE_SCROLL_TYPE);
1429 g_signal_new ("finished",
1430 G_OBJECT_CLASS_TYPE (gobject_class),
1431 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1432 G_STRUCT_OFFSET (EvViewPresentationClass, finished),
1434 g_cclosure_marshal_VOID__VOID,
1438 binding_set = gtk_binding_set_by_class (klass);
1439 add_change_page_binding_keypad (binding_set, GDK_KEY_Left, 0, GTK_SCROLL_PAGE_BACKWARD);
1440 add_change_page_binding_keypad (binding_set, GDK_KEY_Right, 0, GTK_SCROLL_PAGE_FORWARD);
1441 add_change_page_binding_keypad (binding_set, GDK_KEY_Up, 0, GTK_SCROLL_PAGE_BACKWARD);
1442 add_change_page_binding_keypad (binding_set, GDK_KEY_Down, 0, GTK_SCROLL_PAGE_FORWARD);
1443 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
1445 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1446 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0,
1448 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1449 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
1451 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1452 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
1454 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1455 gtk_binding_entry_add_signal (binding_set, GDK_KEY_J, 0,
1457 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1458 gtk_binding_entry_add_signal (binding_set, GDK_KEY_H, 0,
1460 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1461 gtk_binding_entry_add_signal (binding_set, GDK_KEY_L, 0,
1463 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1464 gtk_binding_entry_add_signal (binding_set, GDK_KEY_K, 0,
1466 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1470 ev_view_presentation_init (EvViewPresentation *pview)
1472 gtk_widget_set_can_focus (GTK_WIDGET (pview), TRUE);
1476 ev_view_presentation_new (EvDocument *document,
1479 gboolean inverted_colors)
1481 g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
1482 g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
1484 return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
1485 "document", document,
1486 "current_page", current_page,
1487 "rotation", rotation,
1488 "inverted_colors", inverted_colors,
1493 ev_view_presentation_get_current_page (EvViewPresentation *pview)
1495 return pview->current_page;