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"
33 #include "ev-view-marshal.h"
52 EV_PRESENTATION_NORMAL,
53 EV_PRESENTATION_BLACK,
54 EV_PRESENTATION_WHITE,
56 } EvPresentationState;
58 struct _EvViewPresentation
62 guint is_constructing : 1;
65 cairo_surface_t *current_surface;
68 gboolean inverted_colors;
69 EvPresentationState state;
76 guint hide_cursor_timeout_id;
79 GtkWidget *goto_window;
80 GtkWidget *goto_entry;
83 guint trans_timeout_id;
86 gboolean enable_animations;
87 EvTransitionAnimation *animation;
90 EvPageCache *page_cache;
97 struct _EvViewPresentationClass
99 GtkWidgetClass base_class;
102 void (* page_changed) (EvViewPresentation *pview,
105 void (* change_page) (EvViewPresentation *pview,
106 GtkScrollType scroll);
107 void (* finished) (EvViewPresentation *pview);
108 void (* external_link) (EvViewPresentation *pview,
109 EvLinkAction *action);
112 static guint signals[N_SIGNALS] = { 0 };
114 static void ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
118 #define HIDE_CURSOR_TIMEOUT 5
120 G_DEFINE_TYPE (EvViewPresentation, ev_view_presentation, GTK_TYPE_WIDGET)
122 static GdkRGBA black = { 0., 0., 0., 1. };
123 static GdkRGBA white = { 1., 1., 1., 1. };
126 ev_view_presentation_set_normal (EvViewPresentation *pview)
128 GtkWidget *widget = GTK_WIDGET (pview);
130 if (pview->state == EV_PRESENTATION_NORMAL)
133 pview->state = EV_PRESENTATION_NORMAL;
134 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
135 gtk_widget_queue_draw (widget);
139 ev_view_presentation_set_black (EvViewPresentation *pview)
141 GtkWidget *widget = GTK_WIDGET (pview);
143 if (pview->state == EV_PRESENTATION_BLACK)
146 pview->state = EV_PRESENTATION_BLACK;
147 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
148 gtk_widget_queue_draw (widget);
152 ev_view_presentation_set_white (EvViewPresentation *pview)
154 GtkWidget *widget = GTK_WIDGET (pview);
156 if (pview->state == EV_PRESENTATION_WHITE)
159 pview->state = EV_PRESENTATION_WHITE;
160 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &white);
161 gtk_widget_queue_draw (widget);
165 ev_view_presentation_set_end (EvViewPresentation *pview)
167 GtkWidget *widget = GTK_WIDGET (pview);
169 if (pview->state == EV_PRESENTATION_END)
172 pview->state = EV_PRESENTATION_END;
173 gtk_widget_queue_draw (widget);
177 ev_view_presentation_get_scale_for_page (EvViewPresentation *pview,
180 if (!ev_document_is_page_size_uniform (pview->document) || pview->scale == 0) {
181 gdouble width, height;
183 ev_document_get_page_size (pview->document, page, &width, &height);
184 if (pview->rotation == 90 || pview->rotation == 270) {
191 pview->scale = MIN (pview->monitor_width / width, pview->monitor_height / height);
198 ev_view_presentation_get_page_area (EvViewPresentation *pview,
201 GtkWidget *widget = GTK_WIDGET (pview);
202 GtkAllocation allocation;
203 gdouble doc_width, doc_height;
204 gint view_width, view_height;
207 ev_document_get_page_size (pview->document,
209 &doc_width, &doc_height);
210 scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
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);
216 view_width = (gint)((doc_width * scale) + 0.5);
217 view_height = (gint)((doc_height * scale) + 0.5);
220 gtk_widget_get_allocation (widget, &allocation);
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;
228 /* Page Transition */
230 transition_next_page (EvViewPresentation *pview)
232 ev_view_presentation_next_page (pview);
238 ev_view_presentation_transition_stop (EvViewPresentation *pview)
240 if (pview->trans_timeout_id > 0)
241 g_source_remove (pview->trans_timeout_id);
242 pview->trans_timeout_id = 0;
246 ev_view_presentation_transition_start (EvViewPresentation *pview)
250 if (!EV_IS_DOCUMENT_TRANSITION (pview->document))
253 ev_view_presentation_transition_stop (pview);
255 duration = ev_document_transition_get_page_duration (EV_DOCUMENT_TRANSITION (pview->document),
256 pview->current_page);
258 pview->trans_timeout_id =
259 g_timeout_add_seconds (duration,
260 (GSourceFunc) transition_next_page,
267 ev_view_presentation_animation_cancel (EvViewPresentation *pview)
269 if (pview->animation) {
270 g_object_unref (pview->animation);
271 pview->animation = NULL;
276 ev_view_presentation_transition_animation_finish (EvViewPresentation *pview)
278 ev_view_presentation_animation_cancel (pview);
279 ev_view_presentation_transition_start (pview);
280 gtk_widget_queue_draw (GTK_WIDGET (pview));
284 ev_view_presentation_transition_animation_frame (EvViewPresentation *pview,
287 gtk_widget_queue_draw (GTK_WIDGET (pview));
291 ev_view_presentation_animation_start (EvViewPresentation *pview,
294 EvTransitionEffect *effect = NULL;
295 cairo_surface_t *surface;
298 if (!pview->enable_animations)
301 if (pview->current_page == new_page)
304 effect = ev_document_transition_get_effect (EV_DOCUMENT_TRANSITION (pview->document),
309 pview->animation = ev_transition_animation_new (effect);
311 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
312 ev_transition_animation_set_origin_surface (pview->animation,
314 surface : pview->current_surface);
316 jump = new_page - pview->current_page;
318 surface = pview->prev_job ? EV_JOB_RENDER (pview->prev_job)->surface : NULL;
320 surface = pview->next_job ? EV_JOB_RENDER (pview->next_job)->surface : NULL;
324 ev_transition_animation_set_dest_surface (pview->animation, surface);
326 g_signal_connect_swapped (pview->animation, "frame",
327 G_CALLBACK (ev_view_presentation_transition_animation_frame),
329 g_signal_connect_swapped (pview->animation, "finished",
330 G_CALLBACK (ev_view_presentation_transition_animation_finish),
334 /* Page Navigation */
336 job_finished_cb (EvJob *job,
337 EvViewPresentation *pview)
339 EvJobRender *job_render = EV_JOB_RENDER (job);
341 if (pview->inverted_colors)
342 ev_document_misc_invert_surface (job_render->surface);
344 if (job != pview->curr_job)
347 if (pview->animation) {
348 ev_transition_animation_set_dest_surface (pview->animation,
349 job_render->surface);
351 ev_view_presentation_transition_start (pview);
352 gtk_widget_queue_draw (GTK_WIDGET (pview));
357 ev_view_presentation_schedule_new_job (EvViewPresentation *pview,
359 EvJobPriority priority)
364 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
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),
372 ev_job_scheduler_push_job (job, priority);
378 ev_view_presentation_delete_job (EvViewPresentation *pview,
384 g_signal_handlers_disconnect_by_func (job, job_finished_cb, pview);
386 g_object_unref (job);
390 ev_view_presentation_reset_jobs (EvViewPresentation *pview)
392 if (pview->curr_job) {
393 ev_view_presentation_delete_job (pview, pview->curr_job);
394 pview->curr_job = NULL;
397 if (pview->prev_job) {
398 ev_view_presentation_delete_job (pview, pview->prev_job);
399 pview->prev_job = NULL;
402 if (pview->next_job) {
403 ev_view_presentation_delete_job (pview, pview->next_job);
404 pview->next_job = NULL;
409 ev_view_presentation_update_current_page (EvViewPresentation *pview,
414 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
417 ev_view_presentation_animation_cancel (pview);
418 ev_view_presentation_animation_start (pview, page);
420 jump = page - pview->current_page;
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);
432 ev_view_presentation_delete_job (pview, pview->next_job);
433 pview->next_job = pview->curr_job;
434 pview->curr_job = pview->prev_job;
436 if (!pview->curr_job)
437 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
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);
445 ev_view_presentation_delete_job (pview, pview->prev_job);
446 pview->prev_job = pview->curr_job;
447 pview->curr_job = pview->next_job;
449 if (!pview->curr_job)
450 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
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);
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;
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);
467 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
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;
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);
479 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
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);
486 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
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);
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);
496 g_signal_emit (pview, signals[PAGE_CHANGED], 0, pview->current_page);
498 pview->current_page = page;
500 if (pview->page_cache)
501 ev_page_cache_set_page_range (pview->page_cache, page, page);
503 if (pview->cursor != EV_VIEW_CURSOR_HIDDEN) {
506 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
507 ev_view_presentation_set_cursor_for_location (pview, x, y);
510 if (EV_JOB_RENDER (pview->curr_job)->surface)
511 gtk_widget_queue_draw (GTK_WIDGET (pview));
515 ev_view_presentation_set_page (EvViewPresentation *pview, gint new_page)
519 n_pages = ev_document_get_n_pages (pview->document);
521 if (new_page >= n_pages)
522 ev_view_presentation_set_end (pview);
524 ev_view_presentation_update_current_page (pview, new_page);
528 ev_view_presentation_next_page (EvViewPresentation *pview)
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:
539 case EV_PRESENTATION_NORMAL:
543 n_pages = ev_document_get_n_pages (pview->document);
544 new_page = pview->current_page + 1;
546 if (new_page == n_pages)
547 ev_view_presentation_set_end (pview);
549 ev_view_presentation_update_current_page (pview, new_page);
553 ev_view_presentation_previous_page (EvViewPresentation *pview)
557 switch (pview->state) {
558 case EV_PRESENTATION_BLACK:
559 case EV_PRESENTATION_WHITE:
560 ev_view_presentation_set_normal (pview);
562 case EV_PRESENTATION_END:
563 pview->state = EV_PRESENTATION_NORMAL;
564 new_page = pview->current_page;
566 case EV_PRESENTATION_NORMAL:
567 new_page = pview->current_page - 1;
571 ev_view_presentation_update_current_page (pview, new_page);
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))
578 /* Cut and paste from gtkwindow.c */
580 send_focus_change (GtkWidget *widget,
583 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
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);
591 gtk_widget_send_focus_change (widget, fevent);
593 gdk_event_free (fevent);
597 ev_view_presentation_goto_window_hide (EvViewPresentation *pview)
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), "");
606 ev_view_presentation_goto_window_delete_event (GtkWidget *widget,
608 EvViewPresentation *pview)
610 ev_view_presentation_goto_window_hide (pview);
616 ev_view_presentation_goto_window_key_press_event (GtkWidget *widget,
618 EvViewPresentation *pview)
620 switch (event->keyval) {
624 case GDK_KEY_ISO_Left_Tab:
625 ev_view_presentation_goto_window_hide (pview);
628 case GDK_KEY_KP_Enter:
629 case GDK_KEY_ISO_Enter:
630 case GDK_KEY_BackSpace:
634 if (!KEY_IS_NUMERIC (event->keyval))
642 ev_view_presentation_goto_window_button_press_event (GtkWidget *widget,
643 GdkEventButton *event,
644 EvViewPresentation *pview)
646 ev_view_presentation_goto_window_hide (pview);
652 ev_view_presentation_goto_entry_activate (GtkEntry *entry,
653 EvViewPresentation *pview)
658 text = gtk_entry_get_text (entry);
659 page = atoi (text) - 1;
661 ev_view_presentation_goto_window_hide (pview);
662 ev_view_presentation_update_current_page (pview, page);
666 ev_view_presentation_goto_window_create (EvViewPresentation *pview)
668 GtkWidget *frame, *hbox, *label;
669 GtkWindow *toplevel, *goto_window;
671 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (pview)));
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);
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)));
687 if (gtk_window_has_group (toplevel))
688 gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
690 gtk_window_set_modal (goto_window, TRUE);
692 g_signal_connect (pview->goto_window, "delete_event",
693 G_CALLBACK (ev_view_presentation_goto_window_delete_event),
695 g_signal_connect (pview->goto_window, "key_press_event",
696 G_CALLBACK (ev_view_presentation_goto_window_key_press_event),
698 g_signal_connect (pview->goto_window, "button_press_event",
699 G_CALLBACK (ev_view_presentation_goto_window_button_press_event),
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);
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);
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);
717 pview->goto_entry = gtk_entry_new ();
718 g_signal_connect (pview->goto_entry, "activate",
719 G_CALLBACK (ev_view_presentation_goto_entry_activate),
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);
727 ev_view_presentation_goto_entry_grab_focus (EvViewPresentation *pview)
729 GtkWidgetClass *entry_parent_class;
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);
734 send_focus_change (pview->goto_entry, TRUE);
738 ev_view_presentation_goto_window_send_key_event (EvViewPresentation *pview,
741 GdkEventKey *new_event;
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);
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);
758 gtk_widget_event (pview->goto_window, (GdkEvent *)new_event);
759 gdk_event_free ((GdkEvent *)new_event);
760 gtk_widget_hide (pview->goto_window);
765 ev_view_presentation_link_is_supported (EvViewPresentation *pview,
768 EvLinkAction *action;
770 action = ev_link_get_action (link);
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:
790 ev_view_presentation_get_link_at_location (EvViewPresentation *pview,
794 GdkRectangle page_area;
795 EvMappingList *link_mapping;
797 gdouble width, height;
798 gdouble new_x, new_y;
801 if (!pview->page_cache)
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) {
828 g_assert_not_reached ();
831 link_mapping = ev_page_cache_get_link_mapping (pview->page_cache, pview->current_page);
833 link = link_mapping ? ev_mapping_list_get_data (link_mapping, new_x, new_y) : NULL;
835 return link && ev_view_presentation_link_is_supported (pview, link) ? link : NULL;
839 ev_vew_presentation_handle_link (EvViewPresentation *pview,
842 EvLinkAction *action;
844 action = ev_link_get_action (link);
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);
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) {
859 n_pages = ev_document_get_n_pages (pview->document);
860 ev_view_presentation_update_current_page (pview, n_pages - 1);
865 case EV_LINK_ACTION_TYPE_GOTO_DEST: {
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);
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);
886 ev_view_presentation_set_cursor (EvViewPresentation *pview,
887 EvViewCursor view_cursor)
892 if (pview->cursor == view_cursor)
895 widget = GTK_WIDGET (pview);
896 if (!gtk_widget_get_realized (widget))
897 gtk_widget_realize (widget);
899 pview->cursor = view_cursor;
901 cursor = ev_view_cursor_new (gtk_widget_get_display (widget), view_cursor);
902 gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
905 g_object_unref (cursor);
909 ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
913 if (ev_view_presentation_get_link_at_location (pview, x, y))
914 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_LINK);
916 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_NORMAL);
920 hide_cursor_timeout_cb (EvViewPresentation *pview)
922 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_HIDDEN);
923 pview->hide_cursor_timeout_id = 0;
929 ev_view_presentation_hide_cursor_timeout_stop (EvViewPresentation *pview)
931 if (pview->hide_cursor_timeout_id > 0)
932 g_source_remove (pview->hide_cursor_timeout_id);
933 pview->hide_cursor_timeout_id = 0;
937 ev_view_presentation_hide_cursor_timeout_start (EvViewPresentation *pview)
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,
947 ev_view_presentation_update_current_surface (EvViewPresentation *pview,
948 cairo_surface_t *surface)
950 if (!surface || pview->current_surface == surface)
953 cairo_surface_reference (surface);
954 if (pview->current_surface)
955 cairo_surface_destroy (pview->current_surface);
956 pview->current_surface = surface;
960 ev_view_presentation_dispose (GObject *object)
962 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
964 if (pview->document) {
965 g_object_unref (pview->document);
966 pview->document = NULL;
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);
974 if (pview->current_surface) {
975 cairo_surface_destroy (pview->current_surface);
976 pview->current_surface = NULL;
979 if (pview->page_cache) {
980 g_object_unref (pview->page_cache);
981 pview->page_cache = NULL;
984 if (pview->goto_window) {
985 gtk_widget_destroy (pview->goto_window);
986 pview->goto_window = NULL;
987 pview->goto_entry = NULL;
990 G_OBJECT_CLASS (ev_view_presentation_parent_class)->dispose (object);
994 ev_view_presentation_get_preferred_width (GtkWidget *widget,
998 *minimum = *natural = 0;
1002 ev_view_presentation_get_preferred_height (GtkWidget *widget,
1006 *minimum = *natural = 0;
1010 ev_view_presentation_draw_end_page (EvViewPresentation *pview,
1013 GtkWidget *widget = GTK_WIDGET (pview);
1014 PangoLayout *layout;
1015 PangoFontDescription *font_desc;
1017 GtkAllocation allocation;
1018 GdkRectangle area = {0};
1019 const gchar *text = _("End of presentation. Click to exit.");
1021 if (pview->state != EV_PRESENTATION_END)
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);
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);
1033 gtk_widget_get_allocation (widget, &allocation);
1034 area.width = allocation.width;
1035 area.height = allocation.height;
1037 gtk_render_layout (gtk_widget_get_style_context (widget),
1038 cr, 15, 15, layout);
1040 pango_font_description_free (font_desc);
1041 g_object_unref (layout);
1045 ev_view_presentation_draw (GtkWidget *widget,
1048 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1049 GdkRectangle page_area;
1050 GdkRectangle overlap;
1051 cairo_surface_t *surface;
1052 GdkRectangle clip_rect;
1054 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
1057 switch (pview->state) {
1058 case EV_PRESENTATION_END:
1059 ev_view_presentation_draw_end_page (pview, cr);
1061 case EV_PRESENTATION_BLACK:
1062 case EV_PRESENTATION_WHITE:
1064 case EV_PRESENTATION_NORMAL:
1068 if (pview->animation) {
1069 if (ev_transition_animation_ready (pview->animation)) {
1070 ev_view_presentation_get_page_area (pview, &page_area);
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;
1078 /* Try to fix rounding errors */
1081 ev_transition_animation_paint (pview->animation, cr, page_area);
1089 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
1091 ev_view_presentation_update_current_surface (pview, surface);
1092 } else if (pview->current_surface) {
1093 surface = pview->current_surface;
1098 ev_view_presentation_get_page_area (pview, &page_area);
1099 if (gdk_rectangle_intersect (&page_area, &clip_rect, &overlap)) {
1102 /* Try to fix rounding errors. See bug #438760 */
1103 if (overlap.width == page_area.width)
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);
1117 ev_view_presentation_key_press_event (GtkWidget *widget,
1120 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1122 if (pview->state == EV_PRESENTATION_END)
1123 return gtk_bindings_activate_event (G_OBJECT (widget), event);
1125 switch (event->keyval) {
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);
1133 ev_view_presentation_set_black (pview);
1138 if (pview->state == EV_PRESENTATION_WHITE)
1139 ev_view_presentation_set_normal (pview);
1141 ev_view_presentation_set_white (pview);
1145 if (pview->state == EV_PRESENTATION_NORMAL) {
1146 ev_view_presentation_update_current_page (pview, 0);
1151 if (pview->state == EV_PRESENTATION_NORMAL) {
1154 page = ev_document_get_n_pages (pview->document) - 1;
1155 ev_view_presentation_update_current_page (pview, page);
1164 ev_view_presentation_set_normal (pview);
1166 if (ev_document_get_n_pages (pview->document) > 1 && KEY_IS_NUMERIC (event->keyval)) {
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);
1179 return gtk_bindings_activate_event (G_OBJECT (widget), event);
1183 ev_view_presentation_button_release_event (GtkWidget *widget,
1184 GdkEventButton *event)
1186 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1188 switch (event->button) {
1192 if (pview->state == EV_PRESENTATION_END) {
1193 g_signal_emit (pview, signals[FINISHED], 0, NULL);
1198 link = ev_view_presentation_get_link_at_location (pview,
1202 ev_vew_presentation_handle_link (pview, link);
1204 ev_view_presentation_next_page (pview);
1208 ev_view_presentation_previous_page (pview);
1218 ev_view_presentation_focus_out (GtkWidget *widget,
1219 GdkEventFocus *event)
1221 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1223 if (pview->goto_window)
1224 ev_view_presentation_goto_window_hide (pview);
1230 ev_view_presentation_motion_notify_event (GtkWidget *widget,
1231 GdkEventMotion *event)
1233 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1235 ev_view_presentation_hide_cursor_timeout_start (pview);
1236 ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
1242 init_presentation (GtkWidget *widget)
1244 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1245 GdkScreen *screen = gtk_widget_get_screen (widget);
1246 GdkRectangle monitor;
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;
1254 ev_view_presentation_update_current_page (pview, pview->current_page);
1255 ev_view_presentation_hide_cursor_timeout_start (pview);
1261 ev_view_presentation_realize (GtkWidget *widget)
1264 GdkWindowAttr attributes;
1265 GtkAllocation allocation;
1267 gtk_widget_set_realized (widget, TRUE);
1269 attributes.window_type = GDK_WINDOW_CHILD;
1270 attributes.wclass = GDK_INPUT_OUTPUT;
1271 attributes.visual = gtk_widget_get_visual (widget);
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 |
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;
1288 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1290 GDK_WA_X | GDK_WA_Y |
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),
1298 g_idle_add ((GSourceFunc)init_presentation, widget);
1302 ev_view_presentation_change_page (EvViewPresentation *pview,
1303 GtkScrollType scroll)
1306 case GTK_SCROLL_PAGE_FORWARD:
1307 ev_view_presentation_next_page (pview);
1309 case GTK_SCROLL_PAGE_BACKWARD:
1310 ev_view_presentation_previous_page (pview);
1313 g_assert_not_reached ();
1318 ev_view_presentation_scroll_event (GtkWidget *widget,
1319 GdkEventScroll *event)
1321 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1324 state = event->state & gtk_accelerator_get_default_mod_mask ();
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);
1334 case GDK_SCROLL_LEFT:
1335 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1344 add_change_page_binding_keypad (GtkBindingSet *binding_set,
1346 GdkModifierType modifiers,
1347 GtkScrollType scroll)
1349 guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
1351 gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1353 GTK_TYPE_SCROLL_TYPE, scroll);
1354 gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1356 GTK_TYPE_SCROLL_TYPE, scroll);
1360 ev_view_presentation_set_property (GObject *object,
1362 const GValue *value,
1365 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1369 pview->document = g_value_dup_object (value);
1370 pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
1372 case PROP_CURRENT_PAGE:
1373 pview->current_page = g_value_get_uint (value);
1376 ev_view_presentation_set_rotation (pview, g_value_get_uint (value));
1378 case PROP_INVERTED_COLORS:
1379 pview->inverted_colors = g_value_get_boolean (value);
1382 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1387 ev_view_presentation_get_property (GObject *object,
1392 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1396 g_value_set_uint (value, ev_view_presentation_get_rotation (pview));
1399 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1404 ev_view_presentation_constructor (GType type,
1405 guint n_construct_properties,
1406 GObjectConstructParam *construct_params)
1409 EvViewPresentation *pview;
1411 object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1412 n_construct_properties,
1414 pview = EV_VIEW_PRESENTATION (object);
1415 pview->is_constructing = FALSE;
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);
1426 ev_view_presentation_class_init (EvViewPresentationClass *klass)
1428 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1429 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1430 GtkBindingSet *binding_set;
1431 GtkCssProvider *provider;
1433 klass->change_page = ev_view_presentation_change_page;
1435 gobject_class->dispose = ev_view_presentation_dispose;
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;
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;
1451 g_object_class_install_property (gobject_class,
1453 g_param_spec_object ("document",
1458 G_PARAM_CONSTRUCT_ONLY));
1459 g_object_class_install_property (gobject_class,
1461 g_param_spec_uint ("current_page",
1466 G_PARAM_CONSTRUCT_ONLY));
1467 g_object_class_install_property (gobject_class,
1469 g_param_spec_uint ("rotation",
1471 "Current rotation angle",
1474 G_PARAM_CONSTRUCT));
1475 g_object_class_install_property (gobject_class,
1476 PROP_INVERTED_COLORS,
1477 g_param_spec_boolean ("inverted_colors",
1479 "Whether presentation is displayed with inverted colors",
1482 G_PARAM_CONSTRUCT_ONLY));
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),
1490 g_cclosure_marshal_VOID__ENUM,
1492 GTK_TYPE_SCROLL_TYPE);
1493 signals [PAGE_CHANGED] =
1494 g_signal_new ("page-changed",
1495 EV_TYPE_VIEW_PRESENTATION,
1497 G_STRUCT_OFFSET (EvViewPresentationClass, page_changed),
1499 g_cclosure_marshal_VOID__INT,
1501 G_TYPE_INT, G_TYPE_INT);
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),
1508 g_cclosure_marshal_VOID__VOID,
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),
1517 g_cclosure_marshal_VOID__OBJECT,
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,
1528 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1529 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0,
1531 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1532 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
1534 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1535 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
1537 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1538 gtk_binding_entry_add_signal (binding_set, GDK_KEY_J, 0,
1540 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1541 gtk_binding_entry_add_signal (binding_set, GDK_KEY_H, 0,
1543 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1544 gtk_binding_entry_add_signal (binding_set, GDK_KEY_L, 0,
1546 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1547 gtk_binding_entry_add_signal (binding_set, GDK_KEY_K, 0,
1549 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1551 provider = gtk_css_provider_new ();
1552 gtk_css_provider_load_from_data (provider,
1553 "EvViewPresentation {\n"
1554 " background-color: black; }",
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);
1563 ev_view_presentation_init (EvViewPresentation *pview)
1565 gtk_widget_set_can_focus (GTK_WIDGET (pview), TRUE);
1566 pview->is_constructing = TRUE;
1570 ev_view_presentation_new (EvDocument *document,
1573 gboolean inverted_colors)
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);
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,
1587 ev_view_presentation_get_current_page (EvViewPresentation *pview)
1589 return pview->current_page;
1593 ev_view_presentation_set_rotation (EvViewPresentation *pview,
1596 if (rotation >= 360)
1598 else if (rotation < 0)
1601 if (pview->rotation == rotation)
1604 pview->rotation = rotation;
1605 g_object_notify (G_OBJECT (pview), "rotation");
1606 if (pview->is_constructing)
1610 ev_view_presentation_reset_jobs (pview);
1611 ev_view_presentation_update_current_page (pview, pview->current_page);
1615 ev_view_presentation_get_rotation (EvViewPresentation *pview)
1617 return pview->rotation;