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"
53 EV_PRESENTATION_NORMAL,
54 EV_PRESENTATION_BLACK,
55 EV_PRESENTATION_WHITE,
57 } EvPresentationState;
59 struct _EvViewPresentation
63 guint is_constructing : 1;
66 cairo_surface_t *current_surface;
69 gboolean inverted_colors;
70 EvPresentationState state;
77 guint hide_cursor_timeout_id;
80 GtkWidget *goto_window;
81 GtkWidget *goto_entry;
84 guint trans_timeout_id;
87 gboolean enable_animations;
88 EvTransitionAnimation *animation;
91 EvPageCache *page_cache;
98 struct _EvViewPresentationClass
100 GtkWidgetClass base_class;
103 void (* change_page) (EvViewPresentation *pview,
104 GtkScrollType scroll);
105 void (* finished) (EvViewPresentation *pview);
106 void (* external_link) (EvViewPresentation *pview,
107 EvLinkAction *action);
110 static guint signals[N_SIGNALS] = { 0 };
112 static void ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
116 #define HIDE_CURSOR_TIMEOUT 5
118 G_DEFINE_TYPE (EvViewPresentation, ev_view_presentation, GTK_TYPE_WIDGET)
120 static GdkRGBA black = { 0., 0., 0., 1. };
121 static GdkRGBA white = { 1., 1., 1., 1. };
124 ev_view_presentation_set_normal (EvViewPresentation *pview)
126 GtkWidget *widget = GTK_WIDGET (pview);
128 if (pview->state == EV_PRESENTATION_NORMAL)
131 pview->state = EV_PRESENTATION_NORMAL;
132 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
133 gtk_widget_queue_draw (widget);
137 ev_view_presentation_set_black (EvViewPresentation *pview)
139 GtkWidget *widget = GTK_WIDGET (pview);
141 if (pview->state == EV_PRESENTATION_BLACK)
144 pview->state = EV_PRESENTATION_BLACK;
145 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
146 gtk_widget_queue_draw (widget);
150 ev_view_presentation_set_white (EvViewPresentation *pview)
152 GtkWidget *widget = GTK_WIDGET (pview);
154 if (pview->state == EV_PRESENTATION_WHITE)
157 pview->state = EV_PRESENTATION_WHITE;
158 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &white);
159 gtk_widget_queue_draw (widget);
163 ev_view_presentation_set_end (EvViewPresentation *pview)
165 GtkWidget *widget = GTK_WIDGET (pview);
167 if (pview->state == EV_PRESENTATION_END)
170 pview->state = EV_PRESENTATION_END;
171 gtk_widget_queue_draw (widget);
175 ev_view_presentation_get_scale_for_page (EvViewPresentation *pview,
178 if (!ev_document_is_page_size_uniform (pview->document) || pview->scale == 0) {
179 gdouble width, height;
181 ev_document_get_page_size (pview->document, page, &width, &height);
182 if (pview->rotation == 90 || pview->rotation == 270) {
189 pview->scale = MIN (pview->monitor_width / width, pview->monitor_height / height);
196 ev_view_presentation_get_page_area (EvViewPresentation *pview,
199 GtkWidget *widget = GTK_WIDGET (pview);
200 GtkAllocation allocation;
201 gdouble doc_width, doc_height;
202 gint view_width, view_height;
205 ev_document_get_page_size (pview->document,
207 &doc_width, &doc_height);
208 scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
210 if (pview->rotation == 90 || pview->rotation == 270) {
211 view_width = (gint)((doc_height * scale) + 0.5);
212 view_height = (gint)((doc_width * scale) + 0.5);
214 view_width = (gint)((doc_width * scale) + 0.5);
215 view_height = (gint)((doc_height * scale) + 0.5);
218 gtk_widget_get_allocation (widget, &allocation);
220 area->x = (MAX (0, allocation.width - view_width)) / 2;
221 area->y = (MAX (0, allocation.height - view_height)) / 2;
222 area->width = view_width;
223 area->height = view_height;
226 /* Page Transition */
228 transition_next_page (EvViewPresentation *pview)
230 ev_view_presentation_next_page (pview);
236 ev_view_presentation_transition_stop (EvViewPresentation *pview)
238 if (pview->trans_timeout_id > 0)
239 g_source_remove (pview->trans_timeout_id);
240 pview->trans_timeout_id = 0;
244 ev_view_presentation_transition_start (EvViewPresentation *pview)
248 if (!EV_IS_DOCUMENT_TRANSITION (pview->document))
251 ev_view_presentation_transition_stop (pview);
253 duration = ev_document_transition_get_page_duration (EV_DOCUMENT_TRANSITION (pview->document),
254 pview->current_page);
256 pview->trans_timeout_id =
257 g_timeout_add_seconds (duration,
258 (GSourceFunc) transition_next_page,
265 ev_view_presentation_animation_cancel (EvViewPresentation *pview)
267 if (pview->animation) {
268 g_object_unref (pview->animation);
269 pview->animation = NULL;
274 ev_view_presentation_transition_animation_finish (EvViewPresentation *pview)
276 ev_view_presentation_animation_cancel (pview);
277 ev_view_presentation_transition_start (pview);
278 gtk_widget_queue_draw (GTK_WIDGET (pview));
282 ev_view_presentation_transition_animation_frame (EvViewPresentation *pview,
285 gtk_widget_queue_draw (GTK_WIDGET (pview));
289 ev_view_presentation_animation_start (EvViewPresentation *pview,
292 EvTransitionEffect *effect = NULL;
293 cairo_surface_t *surface;
296 if (!pview->enable_animations)
299 if (pview->current_page == new_page)
302 effect = ev_document_transition_get_effect (EV_DOCUMENT_TRANSITION (pview->document),
307 pview->animation = ev_transition_animation_new (effect);
309 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
310 ev_transition_animation_set_origin_surface (pview->animation,
312 surface : pview->current_surface);
314 jump = new_page - pview->current_page;
316 surface = pview->prev_job ? EV_JOB_RENDER (pview->prev_job)->surface : NULL;
318 surface = pview->next_job ? EV_JOB_RENDER (pview->next_job)->surface : NULL;
322 ev_transition_animation_set_dest_surface (pview->animation, surface);
324 g_signal_connect_swapped (pview->animation, "frame",
325 G_CALLBACK (ev_view_presentation_transition_animation_frame),
327 g_signal_connect_swapped (pview->animation, "finished",
328 G_CALLBACK (ev_view_presentation_transition_animation_finish),
332 /* Page Navigation */
334 job_finished_cb (EvJob *job,
335 EvViewPresentation *pview)
337 EvJobRender *job_render = EV_JOB_RENDER (job);
339 if (pview->inverted_colors)
340 ev_document_misc_invert_surface (job_render->surface);
342 if (job != pview->curr_job)
345 if (pview->animation) {
346 ev_transition_animation_set_dest_surface (pview->animation,
347 job_render->surface);
349 ev_view_presentation_transition_start (pview);
350 gtk_widget_queue_draw (GTK_WIDGET (pview));
355 ev_view_presentation_schedule_new_job (EvViewPresentation *pview,
357 EvJobPriority priority)
362 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
365 scale = ev_view_presentation_get_scale_for_page (pview, page);
366 job = ev_job_render_new (pview->document, page, pview->rotation, scale, 0, 0);
367 g_signal_connect (job, "finished",
368 G_CALLBACK (job_finished_cb),
370 ev_job_scheduler_push_job (job, priority);
376 ev_view_presentation_delete_job (EvViewPresentation *pview,
382 g_signal_handlers_disconnect_by_func (job, job_finished_cb, pview);
384 g_object_unref (job);
388 ev_view_presentation_reset_jobs (EvViewPresentation *pview)
390 if (pview->curr_job) {
391 ev_view_presentation_delete_job (pview, pview->curr_job);
392 pview->curr_job = NULL;
395 if (pview->prev_job) {
396 ev_view_presentation_delete_job (pview, pview->prev_job);
397 pview->prev_job = NULL;
400 if (pview->next_job) {
401 ev_view_presentation_delete_job (pview, pview->next_job);
402 pview->next_job = NULL;
407 ev_view_presentation_update_current_page (EvViewPresentation *pview,
412 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
415 ev_view_presentation_animation_cancel (pview);
416 ev_view_presentation_animation_start (pview, page);
418 jump = page - pview->current_page;
422 if (!pview->curr_job)
423 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
424 if (!pview->next_job)
425 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
426 if (!pview->prev_job)
427 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
430 ev_view_presentation_delete_job (pview, pview->next_job);
431 pview->next_job = pview->curr_job;
432 pview->curr_job = pview->prev_job;
434 if (!pview->curr_job)
435 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
437 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
438 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
439 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
443 ev_view_presentation_delete_job (pview, pview->prev_job);
444 pview->prev_job = pview->curr_job;
445 pview->curr_job = pview->next_job;
447 if (!pview->curr_job)
448 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
450 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
451 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
452 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
456 ev_view_presentation_delete_job (pview, pview->next_job);
457 ev_view_presentation_delete_job (pview, pview->curr_job);
458 pview->next_job = pview->prev_job;
460 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
461 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
462 if (!pview->next_job)
463 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
465 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
468 ev_view_presentation_delete_job (pview, pview->prev_job);
469 ev_view_presentation_delete_job (pview, pview->curr_job);
470 pview->prev_job = pview->next_job;
472 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
473 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
474 if (!pview->prev_job)
475 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
477 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
480 ev_view_presentation_delete_job (pview, pview->prev_job);
481 ev_view_presentation_delete_job (pview, pview->curr_job);
482 ev_view_presentation_delete_job (pview, pview->next_job);
484 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
486 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
487 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
489 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
490 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
494 pview->current_page = page;
495 g_object_notify (G_OBJECT(pview), "page");
497 if (pview->page_cache)
498 ev_page_cache_set_page_range (pview->page_cache, page, page);
500 if (pview->cursor != EV_VIEW_CURSOR_HIDDEN) {
503 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
504 ev_view_presentation_set_cursor_for_location (pview, x, y);
507 if (EV_JOB_RENDER (pview->curr_job)->surface)
508 gtk_widget_queue_draw (GTK_WIDGET (pview));
512 ev_view_presentation_set_page (EvViewPresentation *pview, gint new_page)
516 n_pages = ev_document_get_n_pages (pview->document);
518 if (new_page >= n_pages)
519 ev_view_presentation_set_end (pview);
521 ev_view_presentation_update_current_page (pview, new_page);
525 ev_view_presentation_next_page (EvViewPresentation *pview)
530 switch (pview->state) {
531 case EV_PRESENTATION_BLACK:
532 case EV_PRESENTATION_WHITE:
533 ev_view_presentation_set_normal (pview);
534 case EV_PRESENTATION_END:
536 case EV_PRESENTATION_NORMAL:
540 n_pages = ev_document_get_n_pages (pview->document);
541 new_page = pview->current_page + 1;
543 if (new_page == n_pages)
544 ev_view_presentation_set_end (pview);
546 ev_view_presentation_update_current_page (pview, new_page);
550 ev_view_presentation_previous_page (EvViewPresentation *pview)
554 switch (pview->state) {
555 case EV_PRESENTATION_BLACK:
556 case EV_PRESENTATION_WHITE:
557 ev_view_presentation_set_normal (pview);
559 case EV_PRESENTATION_END:
560 pview->state = EV_PRESENTATION_NORMAL;
561 new_page = pview->current_page;
563 case EV_PRESENTATION_NORMAL:
564 new_page = pview->current_page - 1;
568 ev_view_presentation_update_current_page (pview, new_page);
572 #define KEY_IS_NUMERIC(keyval) \
573 ((keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9) || (keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9))
575 /* Cut and paste from gtkwindow.c */
577 send_focus_change (GtkWidget *widget,
580 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
582 fevent->focus_change.type = GDK_FOCUS_CHANGE;
583 fevent->focus_change.window = gtk_widget_get_window (widget);
584 fevent->focus_change.in = in;
585 if (fevent->focus_change.window)
586 g_object_ref (fevent->focus_change.window);
588 gtk_widget_send_focus_change (widget, fevent);
590 gdk_event_free (fevent);
594 ev_view_presentation_goto_window_hide (EvViewPresentation *pview)
596 /* send focus-in event */
597 send_focus_change (pview->goto_entry, FALSE);
598 gtk_widget_hide (pview->goto_window);
599 gtk_entry_set_text (GTK_ENTRY (pview->goto_entry), "");
603 ev_view_presentation_goto_window_delete_event (GtkWidget *widget,
605 EvViewPresentation *pview)
607 ev_view_presentation_goto_window_hide (pview);
613 ev_view_presentation_goto_window_key_press_event (GtkWidget *widget,
615 EvViewPresentation *pview)
617 switch (event->keyval) {
621 case GDK_KEY_ISO_Left_Tab:
622 ev_view_presentation_goto_window_hide (pview);
625 case GDK_KEY_KP_Enter:
626 case GDK_KEY_ISO_Enter:
627 case GDK_KEY_BackSpace:
631 if (!KEY_IS_NUMERIC (event->keyval))
639 ev_view_presentation_goto_window_button_press_event (GtkWidget *widget,
640 GdkEventButton *event,
641 EvViewPresentation *pview)
643 ev_view_presentation_goto_window_hide (pview);
649 ev_view_presentation_goto_entry_activate (GtkEntry *entry,
650 EvViewPresentation *pview)
655 text = gtk_entry_get_text (entry);
656 page = atoi (text) - 1;
658 ev_view_presentation_goto_window_hide (pview);
659 ev_view_presentation_update_current_page (pview, page);
663 ev_view_presentation_goto_window_create (EvViewPresentation *pview)
665 GtkWidget *frame, *hbox, *label;
666 GtkWindow *toplevel, *goto_window;
668 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (pview)));
670 if (pview->goto_window) {
671 goto_window = GTK_WINDOW (pview->goto_window);
672 if (gtk_window_has_group (toplevel))
673 gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
674 else if (gtk_window_has_group (goto_window))
675 gtk_window_group_remove_window (gtk_window_get_group (goto_window), goto_window);
680 pview->goto_window = gtk_window_new (GTK_WINDOW_POPUP);
681 goto_window = GTK_WINDOW (pview->goto_window);
682 gtk_window_set_screen (goto_window, gtk_widget_get_screen (GTK_WIDGET (pview)));
684 if (gtk_window_has_group (toplevel))
685 gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
687 gtk_window_set_modal (goto_window, TRUE);
689 g_signal_connect (pview->goto_window, "delete_event",
690 G_CALLBACK (ev_view_presentation_goto_window_delete_event),
692 g_signal_connect (pview->goto_window, "key_press_event",
693 G_CALLBACK (ev_view_presentation_goto_window_key_press_event),
695 g_signal_connect (pview->goto_window, "button_press_event",
696 G_CALLBACK (ev_view_presentation_goto_window_button_press_event),
699 frame = gtk_frame_new (NULL);
700 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
701 gtk_container_add (GTK_CONTAINER (pview->goto_window), frame);
702 gtk_widget_show (frame);
704 hbox = gtk_hbox_new (FALSE, 0);
705 gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
706 gtk_container_add (GTK_CONTAINER (frame), hbox);
707 gtk_widget_show (hbox);
709 label = gtk_label_new (_("Jump to page:"));
710 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 3);
711 gtk_widget_show (label);
712 gtk_widget_realize (label);
714 pview->goto_entry = gtk_entry_new ();
715 g_signal_connect (pview->goto_entry, "activate",
716 G_CALLBACK (ev_view_presentation_goto_entry_activate),
718 gtk_box_pack_start (GTK_BOX (hbox), pview->goto_entry, TRUE, TRUE, 0);
719 gtk_widget_show (pview->goto_entry);
720 gtk_widget_realize (pview->goto_entry);
724 ev_view_presentation_goto_entry_grab_focus (EvViewPresentation *pview)
726 GtkWidgetClass *entry_parent_class;
728 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (pview->goto_entry));
729 (entry_parent_class->grab_focus) (pview->goto_entry);
731 send_focus_change (pview->goto_entry, TRUE);
735 ev_view_presentation_goto_window_send_key_event (EvViewPresentation *pview,
738 GdkEventKey *new_event;
741 /* Move goto window off screen */
742 screen = gtk_widget_get_screen (GTK_WIDGET (pview));
743 gtk_window_move (GTK_WINDOW (pview->goto_window),
744 gdk_screen_get_width (screen) + 1,
745 gdk_screen_get_height (screen) + 1);
746 gtk_widget_show (pview->goto_window);
748 new_event = (GdkEventKey *) gdk_event_copy (event);
749 g_object_unref (new_event->window);
750 new_event->window = gtk_widget_get_window (pview->goto_window);
751 if (new_event->window)
752 g_object_ref (new_event->window);
753 gtk_widget_realize (pview->goto_window);
755 gtk_widget_event (pview->goto_window, (GdkEvent *)new_event);
756 gdk_event_free ((GdkEvent *)new_event);
757 gtk_widget_hide (pview->goto_window);
762 ev_view_presentation_link_is_supported (EvViewPresentation *pview,
765 EvLinkAction *action;
767 action = ev_link_get_action (link);
771 switch (ev_link_action_get_action_type (action)) {
772 case EV_LINK_ACTION_TYPE_GOTO_DEST:
773 return ev_link_action_get_dest (action) != NULL;
774 case EV_LINK_ACTION_TYPE_NAMED:
775 case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
776 case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
777 case EV_LINK_ACTION_TYPE_LAUNCH:
787 ev_view_presentation_get_link_at_location (EvViewPresentation *pview,
791 GdkRectangle page_area;
792 EvMappingList *link_mapping;
794 gdouble width, height;
795 gdouble new_x, new_y;
798 if (!pview->page_cache)
801 ev_document_get_page_size (pview->document, pview->current_page, &width, &height);
802 ev_view_presentation_get_page_area (pview, &page_area);
803 scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
804 x = (x - page_area.x) / scale;
805 y = (y - page_area.y) / scale;
806 switch (pview->rotation) {
825 g_assert_not_reached ();
828 link_mapping = ev_page_cache_get_link_mapping (pview->page_cache, pview->current_page);
830 link = link_mapping ? ev_mapping_list_get_data (link_mapping, new_x, new_y) : NULL;
832 return link && ev_view_presentation_link_is_supported (pview, link) ? link : NULL;
836 ev_vew_presentation_handle_link (EvViewPresentation *pview,
839 EvLinkAction *action;
841 action = ev_link_get_action (link);
843 switch (ev_link_action_get_action_type (action)) {
844 case EV_LINK_ACTION_TYPE_NAMED: {
845 const gchar *name = ev_link_action_get_name (action);
847 if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
848 ev_view_presentation_update_current_page (pview, 0);
849 } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
850 ev_view_presentation_update_current_page (pview, pview->current_page - 1);
851 } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
852 ev_view_presentation_update_current_page (pview, pview->current_page + 1);
853 } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
856 n_pages = ev_document_get_n_pages (pview->document);
857 ev_view_presentation_update_current_page (pview, n_pages - 1);
862 case EV_LINK_ACTION_TYPE_GOTO_DEST: {
866 dest = ev_link_action_get_dest (action);
867 page = ev_document_links_get_dest_page (EV_DOCUMENT_LINKS (pview->document), dest);
868 ev_view_presentation_update_current_page (pview, page);
871 case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
872 case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
873 case EV_LINK_ACTION_TYPE_LAUNCH:
874 g_signal_emit (pview, signals[SIGNAL_EXTERNAL_LINK], 0, action);
883 ev_view_presentation_set_cursor (EvViewPresentation *pview,
884 EvViewCursor view_cursor)
889 if (pview->cursor == view_cursor)
892 widget = GTK_WIDGET (pview);
893 if (!gtk_widget_get_realized (widget))
894 gtk_widget_realize (widget);
896 pview->cursor = view_cursor;
898 cursor = ev_view_cursor_new (gtk_widget_get_display (widget), view_cursor);
899 gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
902 g_object_unref (cursor);
906 ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
910 if (ev_view_presentation_get_link_at_location (pview, x, y))
911 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_LINK);
913 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_NORMAL);
917 hide_cursor_timeout_cb (EvViewPresentation *pview)
919 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_HIDDEN);
920 pview->hide_cursor_timeout_id = 0;
926 ev_view_presentation_hide_cursor_timeout_stop (EvViewPresentation *pview)
928 if (pview->hide_cursor_timeout_id > 0)
929 g_source_remove (pview->hide_cursor_timeout_id);
930 pview->hide_cursor_timeout_id = 0;
934 ev_view_presentation_hide_cursor_timeout_start (EvViewPresentation *pview)
936 ev_view_presentation_hide_cursor_timeout_stop (pview);
937 pview->hide_cursor_timeout_id =
938 g_timeout_add_seconds (HIDE_CURSOR_TIMEOUT,
939 (GSourceFunc)hide_cursor_timeout_cb,
944 ev_view_presentation_update_current_surface (EvViewPresentation *pview,
945 cairo_surface_t *surface)
947 if (!surface || pview->current_surface == surface)
950 cairo_surface_reference (surface);
951 if (pview->current_surface)
952 cairo_surface_destroy (pview->current_surface);
953 pview->current_surface = surface;
957 ev_view_presentation_dispose (GObject *object)
959 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
961 if (pview->document) {
962 g_object_unref (pview->document);
963 pview->document = NULL;
966 ev_view_presentation_animation_cancel (pview);
967 ev_view_presentation_transition_stop (pview);
968 ev_view_presentation_hide_cursor_timeout_stop (pview);
969 ev_view_presentation_reset_jobs (pview);
971 if (pview->current_surface) {
972 cairo_surface_destroy (pview->current_surface);
973 pview->current_surface = NULL;
976 if (pview->page_cache) {
977 g_object_unref (pview->page_cache);
978 pview->page_cache = NULL;
981 if (pview->goto_window) {
982 gtk_widget_destroy (pview->goto_window);
983 pview->goto_window = NULL;
984 pview->goto_entry = NULL;
987 G_OBJECT_CLASS (ev_view_presentation_parent_class)->dispose (object);
991 ev_view_presentation_get_preferred_width (GtkWidget *widget,
995 *minimum = *natural = 0;
999 ev_view_presentation_get_preferred_height (GtkWidget *widget,
1003 *minimum = *natural = 0;
1007 ev_view_presentation_draw_end_page (EvViewPresentation *pview,
1010 GtkWidget *widget = GTK_WIDGET (pview);
1011 PangoLayout *layout;
1012 PangoFontDescription *font_desc;
1014 GtkAllocation allocation;
1015 GdkRectangle area = {0};
1016 const gchar *text = _("End of presentation. Click to exit.");
1018 if (pview->state != EV_PRESENTATION_END)
1021 layout = gtk_widget_create_pango_layout (widget, NULL);
1022 markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", text);
1023 pango_layout_set_markup (layout, markup, -1);
1026 font_desc = pango_font_description_new ();
1027 pango_font_description_set_size (font_desc, 16 * PANGO_SCALE);
1028 pango_layout_set_font_description (layout, font_desc);
1030 gtk_widget_get_allocation (widget, &allocation);
1031 area.width = allocation.width;
1032 area.height = allocation.height;
1034 gtk_render_layout (gtk_widget_get_style_context (widget),
1035 cr, 15, 15, layout);
1037 pango_font_description_free (font_desc);
1038 g_object_unref (layout);
1042 ev_view_presentation_draw (GtkWidget *widget,
1045 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1046 GdkRectangle page_area;
1047 GdkRectangle overlap;
1048 cairo_surface_t *surface;
1049 GdkRectangle clip_rect;
1051 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
1054 switch (pview->state) {
1055 case EV_PRESENTATION_END:
1056 ev_view_presentation_draw_end_page (pview, cr);
1058 case EV_PRESENTATION_BLACK:
1059 case EV_PRESENTATION_WHITE:
1061 case EV_PRESENTATION_NORMAL:
1065 if (pview->animation) {
1066 if (ev_transition_animation_ready (pview->animation)) {
1067 ev_view_presentation_get_page_area (pview, &page_area);
1071 /* normalize to x=0, y=0 */
1072 cairo_translate (cr, page_area.x, page_area.y);
1073 page_area.x = page_area.y = 0;
1075 /* Try to fix rounding errors */
1078 ev_transition_animation_paint (pview->animation, cr, page_area);
1086 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
1088 ev_view_presentation_update_current_surface (pview, surface);
1089 } else if (pview->current_surface) {
1090 surface = pview->current_surface;
1095 ev_view_presentation_get_page_area (pview, &page_area);
1096 if (gdk_rectangle_intersect (&page_area, &clip_rect, &overlap)) {
1099 /* Try to fix rounding errors. See bug #438760 */
1100 if (overlap.width == page_area.width)
1103 cairo_rectangle (cr, overlap.x, overlap.y, overlap.width, overlap.height);
1104 cairo_set_source_surface (cr, surface, page_area.x, page_area.y);
1114 ev_view_presentation_key_press_event (GtkWidget *widget,
1117 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1119 if (pview->state == EV_PRESENTATION_END)
1120 return gtk_bindings_activate_event (G_OBJECT (widget), event);
1122 switch (event->keyval) {
1125 case GDK_KEY_period:
1126 case GDK_KEY_KP_Decimal:
1127 if (pview->state == EV_PRESENTATION_BLACK)
1128 ev_view_presentation_set_normal (pview);
1130 ev_view_presentation_set_black (pview);
1135 if (pview->state == EV_PRESENTATION_WHITE)
1136 ev_view_presentation_set_normal (pview);
1138 ev_view_presentation_set_white (pview);
1142 if (pview->state == EV_PRESENTATION_NORMAL) {
1143 ev_view_presentation_update_current_page (pview, 0);
1148 if (pview->state == EV_PRESENTATION_NORMAL) {
1151 page = ev_document_get_n_pages (pview->document) - 1;
1152 ev_view_presentation_update_current_page (pview, page);
1161 ev_view_presentation_set_normal (pview);
1163 if (ev_document_get_n_pages (pview->document) > 1 && KEY_IS_NUMERIC (event->keyval)) {
1166 ev_view_presentation_goto_window_create (pview);
1167 ev_view_presentation_goto_window_send_key_event (pview, (GdkEvent *)event);
1168 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
1169 gtk_window_move (GTK_WINDOW (pview->goto_window), x, y);
1170 gtk_widget_show (pview->goto_window);
1171 ev_view_presentation_goto_entry_grab_focus (pview);
1176 return gtk_bindings_activate_event (G_OBJECT (widget), event);
1180 ev_view_presentation_button_release_event (GtkWidget *widget,
1181 GdkEventButton *event)
1183 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1185 switch (event->button) {
1189 if (pview->state == EV_PRESENTATION_END) {
1190 g_signal_emit (pview, signals[FINISHED], 0, NULL);
1195 link = ev_view_presentation_get_link_at_location (pview,
1199 ev_vew_presentation_handle_link (pview, link);
1201 ev_view_presentation_next_page (pview);
1205 ev_view_presentation_previous_page (pview);
1215 ev_view_presentation_focus_out (GtkWidget *widget,
1216 GdkEventFocus *event)
1218 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1220 if (pview->goto_window)
1221 ev_view_presentation_goto_window_hide (pview);
1227 ev_view_presentation_motion_notify_event (GtkWidget *widget,
1228 GdkEventMotion *event)
1230 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1232 ev_view_presentation_hide_cursor_timeout_start (pview);
1233 ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
1238 void ev_view_presentation_resize_cb (EvViewPresentation *pview,
1241 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET(pview));
1242 GdkRectangle monitor;
1245 monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (GTK_WIDGET(pview)));
1246 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1247 g_printf("%d %d\n",monitor.width,monitor.height);
1251 init_presentation (GtkWidget *widget)
1253 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1254 GdkScreen *screen = gtk_widget_get_screen (widget);
1255 GdkRectangle monitor;
1258 g_signal_connect_swapped(G_OBJECT(screen), "size-changed",
1259 G_CALLBACK(ev_view_presentation_resize_cb),G_OBJECT(pview));
1262 monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget));
1263 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1264 pview->monitor_width = monitor.width;
1265 pview->monitor_height = monitor.height;
1267 ev_view_presentation_update_current_page (pview, pview->current_page);
1268 ev_view_presentation_hide_cursor_timeout_start (pview);
1274 ev_view_presentation_realize (GtkWidget *widget)
1277 GdkWindowAttr attributes;
1278 GtkAllocation allocation;
1280 gtk_widget_set_realized (widget, TRUE);
1282 attributes.window_type = GDK_WINDOW_CHILD;
1283 attributes.wclass = GDK_INPUT_OUTPUT;
1284 attributes.visual = gtk_widget_get_visual (widget);
1286 gtk_widget_get_allocation (widget, &allocation);
1287 attributes.x = allocation.x;
1288 attributes.y = allocation.y;
1289 attributes.width = allocation.width;
1290 attributes.height = allocation.height;
1291 attributes.event_mask = GDK_EXPOSURE_MASK |
1292 GDK_BUTTON_PRESS_MASK |
1293 GDK_BUTTON_RELEASE_MASK |
1295 GDK_KEY_PRESS_MASK |
1296 GDK_POINTER_MOTION_MASK |
1297 GDK_POINTER_MOTION_HINT_MASK |
1298 GDK_ENTER_NOTIFY_MASK |
1299 GDK_LEAVE_NOTIFY_MASK;
1301 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1303 GDK_WA_X | GDK_WA_Y |
1306 gdk_window_set_user_data (window, widget);
1307 gtk_widget_set_window (widget, window);
1308 gtk_style_context_set_background (gtk_widget_get_style_context (widget),
1311 g_idle_add ((GSourceFunc)init_presentation, widget);
1315 ev_view_presentation_change_page (EvViewPresentation *pview,
1316 GtkScrollType scroll)
1319 case GTK_SCROLL_PAGE_FORWARD:
1320 ev_view_presentation_next_page (pview);
1322 case GTK_SCROLL_PAGE_BACKWARD:
1323 ev_view_presentation_previous_page (pview);
1326 g_assert_not_reached ();
1331 ev_view_presentation_scroll_event (GtkWidget *widget,
1332 GdkEventScroll *event)
1334 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1337 state = event->state & gtk_accelerator_get_default_mod_mask ();
1341 switch (event->direction) {
1342 case GDK_SCROLL_DOWN:
1343 case GDK_SCROLL_RIGHT:
1344 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
1347 case GDK_SCROLL_LEFT:
1348 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1357 add_change_page_binding_keypad (GtkBindingSet *binding_set,
1359 GdkModifierType modifiers,
1360 GtkScrollType scroll)
1362 guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
1364 gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1366 GTK_TYPE_SCROLL_TYPE, scroll);
1367 gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1369 GTK_TYPE_SCROLL_TYPE, scroll);
1373 ev_view_presentation_set_property (GObject *object,
1375 const GValue *value,
1378 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1382 pview->document = g_value_dup_object (value);
1383 pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
1385 case PROP_CURRENT_PAGE:
1386 pview->current_page = g_value_get_uint (value);
1389 pview->current_page = g_value_get_uint (value);
1392 ev_view_presentation_set_rotation (pview, g_value_get_uint (value));
1394 case PROP_INVERTED_COLORS:
1395 pview->inverted_colors = g_value_get_boolean (value);
1398 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1403 ev_view_presentation_get_property (GObject *object,
1408 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1412 g_value_set_uint (value, ev_view_presentation_get_current_page (pview));
1415 g_value_set_uint (value, ev_view_presentation_get_rotation (pview));
1418 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1423 ev_view_presentation_constructor (GType type,
1424 guint n_construct_properties,
1425 GObjectConstructParam *construct_params)
1428 EvViewPresentation *pview;
1430 object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1431 n_construct_properties,
1433 pview = EV_VIEW_PRESENTATION (object);
1434 pview->is_constructing = FALSE;
1436 if (EV_IS_DOCUMENT_LINKS (pview->document)) {
1437 pview->page_cache = ev_page_cache_new (pview->document);
1438 ev_page_cache_set_flags (pview->page_cache, EV_PAGE_DATA_INCLUDE_LINKS);
1445 ev_view_presentation_class_init (EvViewPresentationClass *klass)
1447 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1448 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1449 GtkBindingSet *binding_set;
1450 GtkCssProvider *provider;
1452 klass->change_page = ev_view_presentation_change_page;
1454 gobject_class->dispose = ev_view_presentation_dispose;
1456 widget_class->get_preferred_width = ev_view_presentation_get_preferred_width;
1457 widget_class->get_preferred_height = ev_view_presentation_get_preferred_height;
1458 widget_class->realize = ev_view_presentation_realize;
1459 widget_class->draw = ev_view_presentation_draw;
1460 widget_class->key_press_event = ev_view_presentation_key_press_event;
1461 widget_class->button_release_event = ev_view_presentation_button_release_event;
1462 widget_class->focus_out_event = ev_view_presentation_focus_out;
1463 widget_class->motion_notify_event = ev_view_presentation_motion_notify_event;
1464 widget_class->scroll_event = ev_view_presentation_scroll_event;
1466 gobject_class->constructor = ev_view_presentation_constructor;
1467 gobject_class->set_property = ev_view_presentation_set_property;
1468 gobject_class->get_property = ev_view_presentation_get_property;
1470 g_object_class_install_property (gobject_class,
1472 g_param_spec_object ("document",
1477 G_PARAM_CONSTRUCT_ONLY));
1478 g_object_class_install_property (gobject_class,
1480 g_param_spec_uint ("page",
1484 G_PARAM_READWRITE));
1485 g_object_class_install_property (gobject_class,
1487 g_param_spec_uint ("current_page",
1492 G_PARAM_CONSTRUCT_ONLY));
1493 g_object_class_install_property (gobject_class,
1495 g_param_spec_uint ("rotation",
1497 "Current rotation angle",
1500 G_PARAM_CONSTRUCT));
1501 g_object_class_install_property (gobject_class,
1502 PROP_INVERTED_COLORS,
1503 g_param_spec_boolean ("inverted_colors",
1505 "Whether presentation is displayed with inverted colors",
1508 G_PARAM_CONSTRUCT_ONLY));
1510 signals[CHANGE_PAGE] =
1511 g_signal_new ("change_page",
1512 G_OBJECT_CLASS_TYPE (gobject_class),
1513 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1514 G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
1516 g_cclosure_marshal_VOID__ENUM,
1518 GTK_TYPE_SCROLL_TYPE);
1521 g_signal_new ("finished",
1522 G_OBJECT_CLASS_TYPE (gobject_class),
1523 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1524 G_STRUCT_OFFSET (EvViewPresentationClass, finished),
1526 g_cclosure_marshal_VOID__VOID,
1529 signals[SIGNAL_EXTERNAL_LINK] =
1530 g_signal_new ("external-link",
1531 G_TYPE_FROM_CLASS (gobject_class),
1532 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1533 G_STRUCT_OFFSET (EvViewPresentationClass, external_link),
1535 g_cclosure_marshal_VOID__OBJECT,
1539 binding_set = gtk_binding_set_by_class (klass);
1540 add_change_page_binding_keypad (binding_set, GDK_KEY_Left, 0, GTK_SCROLL_PAGE_BACKWARD);
1541 add_change_page_binding_keypad (binding_set, GDK_KEY_Right, 0, GTK_SCROLL_PAGE_FORWARD);
1542 add_change_page_binding_keypad (binding_set, GDK_KEY_Up, 0, GTK_SCROLL_PAGE_BACKWARD);
1543 add_change_page_binding_keypad (binding_set, GDK_KEY_Down, 0, GTK_SCROLL_PAGE_FORWARD);
1544 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
1546 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1547 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0,
1549 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1550 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
1552 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1553 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
1555 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1556 gtk_binding_entry_add_signal (binding_set, GDK_KEY_J, 0,
1558 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1559 gtk_binding_entry_add_signal (binding_set, GDK_KEY_H, 0,
1561 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1562 gtk_binding_entry_add_signal (binding_set, GDK_KEY_L, 0,
1564 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1565 gtk_binding_entry_add_signal (binding_set, GDK_KEY_K, 0,
1567 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1569 provider = gtk_css_provider_new ();
1570 gtk_css_provider_load_from_data (provider,
1571 "EvViewPresentation {\n"
1572 " background-color: black; }",
1574 gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
1575 GTK_STYLE_PROVIDER (provider),
1576 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1577 g_object_unref (provider);
1581 ev_view_presentation_init (EvViewPresentation *pview)
1583 gtk_widget_set_can_focus (GTK_WIDGET (pview), TRUE);
1584 pview->is_constructing = TRUE;
1588 ev_view_presentation_new (EvDocument *document,
1591 gboolean inverted_colors)
1593 g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
1594 g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
1596 return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
1597 "document", document,
1598 "current_page", current_page,
1599 "rotation", rotation,
1600 "inverted_colors", inverted_colors,
1605 ev_view_presentation_get_current_page (EvViewPresentation *pview)
1607 return pview->current_page;
1611 ev_view_presentation_set_rotation (EvViewPresentation *pview,
1614 if (rotation >= 360)
1616 else if (rotation < 0)
1619 if (pview->rotation == rotation)
1622 pview->rotation = rotation;
1623 g_object_notify (G_OBJECT (pview), "rotation");
1624 if (pview->is_constructing)
1628 ev_view_presentation_reset_jobs (pview);
1629 ev_view_presentation_update_current_page (pview, pview->current_page);
1633 ev_view_presentation_get_rotation (EvViewPresentation *pview)
1635 return pview->rotation;