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"
50 EV_PRESENTATION_NORMAL,
51 EV_PRESENTATION_BLACK,
52 EV_PRESENTATION_WHITE,
54 } EvPresentationState;
56 struct _EvViewPresentation
60 guint is_constructing : 1;
63 cairo_surface_t *current_surface;
66 gboolean inverted_colors;
67 EvPresentationState state;
74 guint hide_cursor_timeout_id;
77 GtkWidget *goto_window;
78 GtkWidget *goto_entry;
81 guint trans_timeout_id;
84 gboolean enable_animations;
85 EvTransitionAnimation *animation;
88 EvPageCache *page_cache;
95 struct _EvViewPresentationClass
97 GtkWidgetClass base_class;
100 void (* change_page) (EvViewPresentation *pview,
101 GtkScrollType scroll);
102 void (* finished) (EvViewPresentation *pview);
103 void (* external_link) (EvViewPresentation *pview,
104 EvLinkAction *action);
107 static guint signals[N_SIGNALS] = { 0 };
109 static void ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
113 #define HIDE_CURSOR_TIMEOUT 5
115 G_DEFINE_TYPE (EvViewPresentation, ev_view_presentation, GTK_TYPE_WIDGET)
117 static GdkRGBA black = { 0., 0., 0., 1. };
118 static GdkRGBA white = { 1., 1., 1., 1. };
121 ev_view_presentation_set_normal (EvViewPresentation *pview)
123 GtkWidget *widget = GTK_WIDGET (pview);
125 if (pview->state == EV_PRESENTATION_NORMAL)
128 pview->state = EV_PRESENTATION_NORMAL;
129 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
130 gtk_widget_queue_draw (widget);
134 ev_view_presentation_set_black (EvViewPresentation *pview)
136 GtkWidget *widget = GTK_WIDGET (pview);
138 if (pview->state == EV_PRESENTATION_BLACK)
141 pview->state = EV_PRESENTATION_BLACK;
142 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
143 gtk_widget_queue_draw (widget);
147 ev_view_presentation_set_white (EvViewPresentation *pview)
149 GtkWidget *widget = GTK_WIDGET (pview);
151 if (pview->state == EV_PRESENTATION_WHITE)
154 pview->state = EV_PRESENTATION_WHITE;
155 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &white);
156 gtk_widget_queue_draw (widget);
160 ev_view_presentation_set_end (EvViewPresentation *pview)
162 GtkWidget *widget = GTK_WIDGET (pview);
164 if (pview->state == EV_PRESENTATION_END)
167 pview->state = EV_PRESENTATION_END;
168 gtk_widget_queue_draw (widget);
172 ev_view_presentation_get_scale_for_page (EvViewPresentation *pview,
175 if (!ev_document_is_page_size_uniform (pview->document) || pview->scale == 0) {
176 gdouble width, height;
178 ev_document_get_page_size (pview->document, page, &width, &height);
179 if (pview->rotation == 90 || pview->rotation == 270) {
186 pview->scale = MIN (pview->monitor_width / width, pview->monitor_height / height);
193 ev_view_presentation_get_page_area (EvViewPresentation *pview,
196 GtkWidget *widget = GTK_WIDGET (pview);
197 GtkAllocation allocation;
198 gdouble doc_width, doc_height;
199 gint view_width, view_height;
202 ev_document_get_page_size (pview->document,
204 &doc_width, &doc_height);
205 scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
207 if (pview->rotation == 90 || pview->rotation == 270) {
208 view_width = (gint)((doc_height * scale) + 0.5);
209 view_height = (gint)((doc_width * scale) + 0.5);
211 view_width = (gint)((doc_width * scale) + 0.5);
212 view_height = (gint)((doc_height * scale) + 0.5);
215 gtk_widget_get_allocation (widget, &allocation);
217 area->x = (MAX (0, allocation.width - view_width)) / 2;
218 area->y = (MAX (0, allocation.height - view_height)) / 2;
219 area->width = view_width;
220 area->height = view_height;
223 /* Page Transition */
225 transition_next_page (EvViewPresentation *pview)
227 ev_view_presentation_next_page (pview);
233 ev_view_presentation_transition_stop (EvViewPresentation *pview)
235 if (pview->trans_timeout_id > 0)
236 g_source_remove (pview->trans_timeout_id);
237 pview->trans_timeout_id = 0;
241 ev_view_presentation_transition_start (EvViewPresentation *pview)
245 if (!EV_IS_DOCUMENT_TRANSITION (pview->document))
248 ev_view_presentation_transition_stop (pview);
250 duration = ev_document_transition_get_page_duration (EV_DOCUMENT_TRANSITION (pview->document),
251 pview->current_page);
253 pview->trans_timeout_id =
254 g_timeout_add_seconds (duration,
255 (GSourceFunc) transition_next_page,
262 ev_view_presentation_animation_cancel (EvViewPresentation *pview)
264 if (pview->animation) {
265 g_object_unref (pview->animation);
266 pview->animation = NULL;
271 ev_view_presentation_transition_animation_finish (EvViewPresentation *pview)
273 ev_view_presentation_animation_cancel (pview);
274 ev_view_presentation_transition_start (pview);
275 gtk_widget_queue_draw (GTK_WIDGET (pview));
279 ev_view_presentation_transition_animation_frame (EvViewPresentation *pview,
282 gtk_widget_queue_draw (GTK_WIDGET (pview));
286 ev_view_presentation_animation_start (EvViewPresentation *pview,
289 EvTransitionEffect *effect = NULL;
290 cairo_surface_t *surface;
293 if (!pview->enable_animations)
296 if (pview->current_page == new_page)
299 effect = ev_document_transition_get_effect (EV_DOCUMENT_TRANSITION (pview->document),
304 pview->animation = ev_transition_animation_new (effect);
306 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
307 ev_transition_animation_set_origin_surface (pview->animation,
309 surface : pview->current_surface);
311 jump = new_page - pview->current_page;
313 surface = pview->prev_job ? EV_JOB_RENDER (pview->prev_job)->surface : NULL;
315 surface = pview->next_job ? EV_JOB_RENDER (pview->next_job)->surface : NULL;
319 ev_transition_animation_set_dest_surface (pview->animation, surface);
321 g_signal_connect_swapped (pview->animation, "frame",
322 G_CALLBACK (ev_view_presentation_transition_animation_frame),
324 g_signal_connect_swapped (pview->animation, "finished",
325 G_CALLBACK (ev_view_presentation_transition_animation_finish),
329 /* Page Navigation */
331 job_finished_cb (EvJob *job,
332 EvViewPresentation *pview)
334 EvJobRender *job_render = EV_JOB_RENDER (job);
336 if (pview->inverted_colors)
337 ev_document_misc_invert_surface (job_render->surface);
339 if (job != pview->curr_job)
342 if (pview->animation) {
343 ev_transition_animation_set_dest_surface (pview->animation,
344 job_render->surface);
346 ev_view_presentation_transition_start (pview);
347 gtk_widget_queue_draw (GTK_WIDGET (pview));
352 ev_view_presentation_schedule_new_job (EvViewPresentation *pview,
354 EvJobPriority priority)
359 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
362 scale = ev_view_presentation_get_scale_for_page (pview, page);
363 job = ev_job_render_new (pview->document, page, pview->rotation, scale, 0, 0);
364 g_signal_connect (job, "finished",
365 G_CALLBACK (job_finished_cb),
367 ev_job_scheduler_push_job (job, priority);
373 ev_view_presentation_delete_job (EvViewPresentation *pview,
379 g_signal_handlers_disconnect_by_func (job, job_finished_cb, pview);
381 g_object_unref (job);
385 ev_view_presentation_reset_jobs (EvViewPresentation *pview)
387 if (pview->curr_job) {
388 ev_view_presentation_delete_job (pview, pview->curr_job);
389 pview->curr_job = NULL;
392 if (pview->prev_job) {
393 ev_view_presentation_delete_job (pview, pview->prev_job);
394 pview->prev_job = NULL;
397 if (pview->next_job) {
398 ev_view_presentation_delete_job (pview, pview->next_job);
399 pview->next_job = NULL;
404 ev_view_presentation_update_current_page (EvViewPresentation *pview,
409 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
412 ev_view_presentation_animation_cancel (pview);
413 ev_view_presentation_animation_start (pview, page);
415 jump = page - pview->current_page;
419 if (!pview->curr_job)
420 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
421 if (!pview->next_job)
422 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
423 if (!pview->prev_job)
424 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
427 ev_view_presentation_delete_job (pview, pview->next_job);
428 pview->next_job = pview->curr_job;
429 pview->curr_job = pview->prev_job;
431 if (!pview->curr_job)
432 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
434 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
435 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
436 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
440 ev_view_presentation_delete_job (pview, pview->prev_job);
441 pview->prev_job = pview->curr_job;
442 pview->curr_job = pview->next_job;
444 if (!pview->curr_job)
445 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
447 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
448 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
449 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
453 ev_view_presentation_delete_job (pview, pview->next_job);
454 ev_view_presentation_delete_job (pview, pview->curr_job);
455 pview->next_job = pview->prev_job;
457 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
458 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
459 if (!pview->next_job)
460 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
462 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
465 ev_view_presentation_delete_job (pview, pview->prev_job);
466 ev_view_presentation_delete_job (pview, pview->curr_job);
467 pview->prev_job = pview->next_job;
469 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
470 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
471 if (!pview->prev_job)
472 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
474 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
477 ev_view_presentation_delete_job (pview, pview->prev_job);
478 ev_view_presentation_delete_job (pview, pview->curr_job);
479 ev_view_presentation_delete_job (pview, pview->next_job);
481 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
483 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
484 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
486 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
487 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
491 pview->current_page = page;
493 if (pview->page_cache)
494 ev_page_cache_set_page_range (pview->page_cache, page, page);
496 if (pview->cursor != EV_VIEW_CURSOR_HIDDEN) {
499 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
500 ev_view_presentation_set_cursor_for_location (pview, x, y);
503 if (EV_JOB_RENDER (pview->curr_job)->surface)
504 gtk_widget_queue_draw (GTK_WIDGET (pview));
508 ev_view_presentation_next_page (EvViewPresentation *pview)
513 switch (pview->state) {
514 case EV_PRESENTATION_BLACK:
515 case EV_PRESENTATION_WHITE:
516 ev_view_presentation_set_normal (pview);
517 case EV_PRESENTATION_END:
519 case EV_PRESENTATION_NORMAL:
523 n_pages = ev_document_get_n_pages (pview->document);
524 new_page = pview->current_page + 1;
526 if (new_page == n_pages)
527 ev_view_presentation_set_end (pview);
529 ev_view_presentation_update_current_page (pview, new_page);
533 ev_view_presentation_previous_page (EvViewPresentation *pview)
537 switch (pview->state) {
538 case EV_PRESENTATION_BLACK:
539 case EV_PRESENTATION_WHITE:
540 ev_view_presentation_set_normal (pview);
542 case EV_PRESENTATION_END:
543 pview->state = EV_PRESENTATION_NORMAL;
544 new_page = pview->current_page;
546 case EV_PRESENTATION_NORMAL:
547 new_page = pview->current_page - 1;
551 ev_view_presentation_update_current_page (pview, new_page);
555 #define KEY_IS_NUMERIC(keyval) \
556 ((keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9) || (keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9))
558 /* Cut and paste from gtkwindow.c */
560 send_focus_change (GtkWidget *widget,
563 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
565 fevent->focus_change.type = GDK_FOCUS_CHANGE;
566 fevent->focus_change.window = gtk_widget_get_window (widget);
567 fevent->focus_change.in = in;
568 if (fevent->focus_change.window)
569 g_object_ref (fevent->focus_change.window);
571 gtk_widget_send_focus_change (widget, fevent);
573 gdk_event_free (fevent);
577 ev_view_presentation_goto_window_hide (EvViewPresentation *pview)
579 /* send focus-in event */
580 send_focus_change (pview->goto_entry, FALSE);
581 gtk_widget_hide (pview->goto_window);
582 gtk_entry_set_text (GTK_ENTRY (pview->goto_entry), "");
586 ev_view_presentation_goto_window_delete_event (GtkWidget *widget,
588 EvViewPresentation *pview)
590 ev_view_presentation_goto_window_hide (pview);
596 ev_view_presentation_goto_window_key_press_event (GtkWidget *widget,
598 EvViewPresentation *pview)
600 switch (event->keyval) {
604 case GDK_KEY_ISO_Left_Tab:
605 ev_view_presentation_goto_window_hide (pview);
608 case GDK_KEY_KP_Enter:
609 case GDK_KEY_ISO_Enter:
610 case GDK_KEY_BackSpace:
614 if (!KEY_IS_NUMERIC (event->keyval))
622 ev_view_presentation_goto_window_button_press_event (GtkWidget *widget,
623 GdkEventButton *event,
624 EvViewPresentation *pview)
626 ev_view_presentation_goto_window_hide (pview);
632 ev_view_presentation_goto_entry_activate (GtkEntry *entry,
633 EvViewPresentation *pview)
638 text = gtk_entry_get_text (entry);
639 page = atoi (text) - 1;
641 ev_view_presentation_goto_window_hide (pview);
642 ev_view_presentation_update_current_page (pview, page);
646 ev_view_presentation_goto_window_create (EvViewPresentation *pview)
648 GtkWidget *frame, *hbox, *label;
649 GtkWindow *toplevel, *goto_window;
651 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (pview)));
653 if (pview->goto_window) {
654 goto_window = GTK_WINDOW (pview->goto_window);
655 if (gtk_window_has_group (toplevel))
656 gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
657 else if (gtk_window_has_group (goto_window))
658 gtk_window_group_remove_window (gtk_window_get_group (goto_window), goto_window);
663 pview->goto_window = gtk_window_new (GTK_WINDOW_POPUP);
664 goto_window = GTK_WINDOW (pview->goto_window);
665 gtk_window_set_screen (goto_window, gtk_widget_get_screen (GTK_WIDGET (pview)));
667 if (gtk_window_has_group (toplevel))
668 gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
670 gtk_window_set_modal (goto_window, TRUE);
672 g_signal_connect (pview->goto_window, "delete_event",
673 G_CALLBACK (ev_view_presentation_goto_window_delete_event),
675 g_signal_connect (pview->goto_window, "key_press_event",
676 G_CALLBACK (ev_view_presentation_goto_window_key_press_event),
678 g_signal_connect (pview->goto_window, "button_press_event",
679 G_CALLBACK (ev_view_presentation_goto_window_button_press_event),
682 frame = gtk_frame_new (NULL);
683 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
684 gtk_container_add (GTK_CONTAINER (pview->goto_window), frame);
685 gtk_widget_show (frame);
687 hbox = gtk_hbox_new (FALSE, 0);
688 gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
689 gtk_container_add (GTK_CONTAINER (frame), hbox);
690 gtk_widget_show (hbox);
692 label = gtk_label_new (_("Jump to page:"));
693 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 3);
694 gtk_widget_show (label);
695 gtk_widget_realize (label);
697 pview->goto_entry = gtk_entry_new ();
698 g_signal_connect (pview->goto_entry, "activate",
699 G_CALLBACK (ev_view_presentation_goto_entry_activate),
701 gtk_box_pack_start (GTK_BOX (hbox), pview->goto_entry, TRUE, TRUE, 0);
702 gtk_widget_show (pview->goto_entry);
703 gtk_widget_realize (pview->goto_entry);
707 ev_view_presentation_goto_entry_grab_focus (EvViewPresentation *pview)
709 GtkWidgetClass *entry_parent_class;
711 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (pview->goto_entry));
712 (entry_parent_class->grab_focus) (pview->goto_entry);
714 send_focus_change (pview->goto_entry, TRUE);
718 ev_view_presentation_goto_window_send_key_event (EvViewPresentation *pview,
721 GdkEventKey *new_event;
724 /* Move goto window off screen */
725 screen = gtk_widget_get_screen (GTK_WIDGET (pview));
726 gtk_window_move (GTK_WINDOW (pview->goto_window),
727 gdk_screen_get_width (screen) + 1,
728 gdk_screen_get_height (screen) + 1);
729 gtk_widget_show (pview->goto_window);
731 new_event = (GdkEventKey *) gdk_event_copy (event);
732 g_object_unref (new_event->window);
733 new_event->window = gtk_widget_get_window (pview->goto_window);
734 if (new_event->window)
735 g_object_ref (new_event->window);
736 gtk_widget_realize (pview->goto_window);
738 gtk_widget_event (pview->goto_window, (GdkEvent *)new_event);
739 gdk_event_free ((GdkEvent *)new_event);
740 gtk_widget_hide (pview->goto_window);
745 ev_view_presentation_link_is_supported (EvViewPresentation *pview,
748 EvLinkAction *action;
750 action = ev_link_get_action (link);
754 switch (ev_link_action_get_action_type (action)) {
755 case EV_LINK_ACTION_TYPE_GOTO_DEST:
756 return ev_link_action_get_dest (action) != NULL;
757 case EV_LINK_ACTION_TYPE_NAMED:
758 case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
759 case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
760 case EV_LINK_ACTION_TYPE_LAUNCH:
770 ev_view_presentation_get_link_at_location (EvViewPresentation *pview,
774 GdkRectangle page_area;
775 EvMappingList *link_mapping;
777 gdouble width, height;
778 gdouble new_x, new_y;
781 if (!pview->page_cache)
784 ev_document_get_page_size (pview->document, pview->current_page, &width, &height);
785 ev_view_presentation_get_page_area (pview, &page_area);
786 scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
787 x = (x - page_area.x) / scale;
788 y = (y - page_area.y) / scale;
789 switch (pview->rotation) {
808 g_assert_not_reached ();
811 link_mapping = ev_page_cache_get_link_mapping (pview->page_cache, pview->current_page);
813 link = link_mapping ? ev_mapping_list_get_data (link_mapping, new_x, new_y) : NULL;
815 return link && ev_view_presentation_link_is_supported (pview, link) ? link : NULL;
819 ev_vew_presentation_handle_link (EvViewPresentation *pview,
822 EvLinkAction *action;
824 action = ev_link_get_action (link);
826 switch (ev_link_action_get_action_type (action)) {
827 case EV_LINK_ACTION_TYPE_NAMED: {
828 const gchar *name = ev_link_action_get_name (action);
830 if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
831 ev_view_presentation_update_current_page (pview, 0);
832 } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
833 ev_view_presentation_update_current_page (pview, pview->current_page - 1);
834 } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
835 ev_view_presentation_update_current_page (pview, pview->current_page + 1);
836 } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
839 n_pages = ev_document_get_n_pages (pview->document);
840 ev_view_presentation_update_current_page (pview, n_pages - 1);
845 case EV_LINK_ACTION_TYPE_GOTO_DEST: {
849 dest = ev_link_action_get_dest (action);
850 page = ev_document_links_get_dest_page (EV_DOCUMENT_LINKS (pview->document), dest);
851 ev_view_presentation_update_current_page (pview, page);
854 case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
855 case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
856 case EV_LINK_ACTION_TYPE_LAUNCH:
857 g_signal_emit (pview, signals[SIGNAL_EXTERNAL_LINK], 0, action);
866 ev_view_presentation_set_cursor (EvViewPresentation *pview,
867 EvViewCursor view_cursor)
872 if (pview->cursor == view_cursor)
875 widget = GTK_WIDGET (pview);
876 if (!gtk_widget_get_realized (widget))
877 gtk_widget_realize (widget);
879 pview->cursor = view_cursor;
881 cursor = ev_view_cursor_new (gtk_widget_get_display (widget), view_cursor);
882 gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
885 g_object_unref (cursor);
889 ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
893 if (ev_view_presentation_get_link_at_location (pview, x, y))
894 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_LINK);
896 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_NORMAL);
900 hide_cursor_timeout_cb (EvViewPresentation *pview)
902 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_HIDDEN);
903 pview->hide_cursor_timeout_id = 0;
909 ev_view_presentation_hide_cursor_timeout_stop (EvViewPresentation *pview)
911 if (pview->hide_cursor_timeout_id > 0)
912 g_source_remove (pview->hide_cursor_timeout_id);
913 pview->hide_cursor_timeout_id = 0;
917 ev_view_presentation_hide_cursor_timeout_start (EvViewPresentation *pview)
919 ev_view_presentation_hide_cursor_timeout_stop (pview);
920 pview->hide_cursor_timeout_id =
921 g_timeout_add_seconds (HIDE_CURSOR_TIMEOUT,
922 (GSourceFunc)hide_cursor_timeout_cb,
927 ev_view_presentation_update_current_surface (EvViewPresentation *pview,
928 cairo_surface_t *surface)
930 if (!surface || pview->current_surface == surface)
933 cairo_surface_reference (surface);
934 if (pview->current_surface)
935 cairo_surface_destroy (pview->current_surface);
936 pview->current_surface = surface;
940 ev_view_presentation_dispose (GObject *object)
942 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
944 if (pview->document) {
945 g_object_unref (pview->document);
946 pview->document = NULL;
949 ev_view_presentation_animation_cancel (pview);
950 ev_view_presentation_transition_stop (pview);
951 ev_view_presentation_hide_cursor_timeout_stop (pview);
952 ev_view_presentation_reset_jobs (pview);
954 if (pview->current_surface) {
955 cairo_surface_destroy (pview->current_surface);
956 pview->current_surface = NULL;
959 if (pview->page_cache) {
960 g_object_unref (pview->page_cache);
961 pview->page_cache = NULL;
964 if (pview->goto_window) {
965 gtk_widget_destroy (pview->goto_window);
966 pview->goto_window = NULL;
967 pview->goto_entry = NULL;
970 G_OBJECT_CLASS (ev_view_presentation_parent_class)->dispose (object);
974 ev_view_presentation_get_preferred_width (GtkWidget *widget,
978 *minimum = *natural = 0;
982 ev_view_presentation_get_preferred_height (GtkWidget *widget,
986 *minimum = *natural = 0;
990 ev_view_presentation_draw_end_page (EvViewPresentation *pview,
993 GtkWidget *widget = GTK_WIDGET (pview);
995 PangoFontDescription *font_desc;
997 GtkAllocation allocation;
998 GdkRectangle area = {0};
999 const gchar *text = _("End of presentation. Click to exit.");
1001 if (pview->state != EV_PRESENTATION_END)
1004 layout = gtk_widget_create_pango_layout (widget, NULL);
1005 markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", text);
1006 pango_layout_set_markup (layout, markup, -1);
1009 font_desc = pango_font_description_new ();
1010 pango_font_description_set_size (font_desc, 16 * PANGO_SCALE);
1011 pango_layout_set_font_description (layout, font_desc);
1013 gtk_widget_get_allocation (widget, &allocation);
1014 area.width = allocation.width;
1015 area.height = allocation.height;
1017 gtk_render_layout (gtk_widget_get_style_context (widget),
1018 cr, 15, 15, layout);
1020 pango_font_description_free (font_desc);
1021 g_object_unref (layout);
1025 ev_view_presentation_draw (GtkWidget *widget,
1028 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1029 GdkRectangle page_area;
1030 GdkRectangle overlap;
1031 cairo_surface_t *surface;
1032 GdkRectangle clip_rect;
1034 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
1037 switch (pview->state) {
1038 case EV_PRESENTATION_END:
1039 ev_view_presentation_draw_end_page (pview, cr);
1041 case EV_PRESENTATION_BLACK:
1042 case EV_PRESENTATION_WHITE:
1044 case EV_PRESENTATION_NORMAL:
1048 if (pview->animation) {
1049 if (ev_transition_animation_ready (pview->animation)) {
1050 ev_view_presentation_get_page_area (pview, &page_area);
1054 /* normalize to x=0, y=0 */
1055 cairo_translate (cr, page_area.x, page_area.y);
1056 page_area.x = page_area.y = 0;
1058 /* Try to fix rounding errors */
1061 ev_transition_animation_paint (pview->animation, cr, page_area);
1069 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
1071 ev_view_presentation_update_current_surface (pview, surface);
1072 } else if (pview->current_surface) {
1073 surface = pview->current_surface;
1078 ev_view_presentation_get_page_area (pview, &page_area);
1079 if (gdk_rectangle_intersect (&page_area, &clip_rect, &overlap)) {
1082 /* Try to fix rounding errors. See bug #438760 */
1083 if (overlap.width == page_area.width)
1086 cairo_rectangle (cr, overlap.x, overlap.y, overlap.width, overlap.height);
1087 cairo_set_source_surface (cr, surface, page_area.x, page_area.y);
1097 ev_view_presentation_key_press_event (GtkWidget *widget,
1100 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1102 if (pview->state == EV_PRESENTATION_END)
1103 return gtk_bindings_activate_event (G_OBJECT (widget), event);
1105 switch (event->keyval) {
1108 case GDK_KEY_period:
1109 case GDK_KEY_KP_Decimal:
1110 if (pview->state == EV_PRESENTATION_BLACK)
1111 ev_view_presentation_set_normal (pview);
1113 ev_view_presentation_set_black (pview);
1118 if (pview->state == EV_PRESENTATION_WHITE)
1119 ev_view_presentation_set_normal (pview);
1121 ev_view_presentation_set_white (pview);
1125 if (pview->state == EV_PRESENTATION_NORMAL) {
1126 ev_view_presentation_update_current_page (pview, 0);
1131 if (pview->state == EV_PRESENTATION_NORMAL) {
1134 page = ev_document_get_n_pages (pview->document) - 1;
1135 ev_view_presentation_update_current_page (pview, page);
1144 ev_view_presentation_set_normal (pview);
1146 if (ev_document_get_n_pages (pview->document) > 1 && KEY_IS_NUMERIC (event->keyval)) {
1149 ev_view_presentation_goto_window_create (pview);
1150 ev_view_presentation_goto_window_send_key_event (pview, (GdkEvent *)event);
1151 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
1152 gtk_window_move (GTK_WINDOW (pview->goto_window), x, y);
1153 gtk_widget_show (pview->goto_window);
1154 ev_view_presentation_goto_entry_grab_focus (pview);
1159 return gtk_bindings_activate_event (G_OBJECT (widget), event);
1163 ev_view_presentation_button_release_event (GtkWidget *widget,
1164 GdkEventButton *event)
1166 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1168 switch (event->button) {
1172 if (pview->state == EV_PRESENTATION_END) {
1173 g_signal_emit (pview, signals[FINISHED], 0, NULL);
1178 link = ev_view_presentation_get_link_at_location (pview,
1182 ev_vew_presentation_handle_link (pview, link);
1184 ev_view_presentation_next_page (pview);
1188 ev_view_presentation_previous_page (pview);
1198 ev_view_presentation_focus_out (GtkWidget *widget,
1199 GdkEventFocus *event)
1201 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1203 if (pview->goto_window)
1204 ev_view_presentation_goto_window_hide (pview);
1210 ev_view_presentation_motion_notify_event (GtkWidget *widget,
1211 GdkEventMotion *event)
1213 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1215 ev_view_presentation_hide_cursor_timeout_start (pview);
1216 ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
1222 init_presentation (GtkWidget *widget)
1224 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1225 GdkScreen *screen = gtk_widget_get_screen (widget);
1226 GdkRectangle monitor;
1229 monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget));
1230 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1231 pview->monitor_width = monitor.width;
1232 pview->monitor_height = monitor.height;
1234 ev_view_presentation_update_current_page (pview, pview->current_page);
1235 ev_view_presentation_hide_cursor_timeout_start (pview);
1241 ev_view_presentation_realize (GtkWidget *widget)
1244 GdkWindowAttr attributes;
1245 GtkAllocation allocation;
1247 gtk_widget_set_realized (widget, TRUE);
1249 attributes.window_type = GDK_WINDOW_CHILD;
1250 attributes.wclass = GDK_INPUT_OUTPUT;
1251 attributes.visual = gtk_widget_get_visual (widget);
1253 gtk_widget_get_allocation (widget, &allocation);
1254 attributes.x = allocation.x;
1255 attributes.y = allocation.y;
1256 attributes.width = allocation.width;
1257 attributes.height = allocation.height;
1258 attributes.event_mask = GDK_EXPOSURE_MASK |
1259 GDK_BUTTON_PRESS_MASK |
1260 GDK_BUTTON_RELEASE_MASK |
1262 GDK_KEY_PRESS_MASK |
1263 GDK_POINTER_MOTION_MASK |
1264 GDK_POINTER_MOTION_HINT_MASK |
1265 GDK_ENTER_NOTIFY_MASK |
1266 GDK_LEAVE_NOTIFY_MASK;
1268 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1270 GDK_WA_X | GDK_WA_Y |
1273 gdk_window_set_user_data (window, widget);
1274 gtk_widget_set_window (widget, window);
1275 gtk_style_context_set_background (gtk_widget_get_style_context (widget),
1278 g_idle_add ((GSourceFunc)init_presentation, widget);
1282 ev_view_presentation_change_page (EvViewPresentation *pview,
1283 GtkScrollType scroll)
1286 case GTK_SCROLL_PAGE_FORWARD:
1287 ev_view_presentation_next_page (pview);
1289 case GTK_SCROLL_PAGE_BACKWARD:
1290 ev_view_presentation_previous_page (pview);
1293 g_assert_not_reached ();
1298 ev_view_presentation_scroll_event (GtkWidget *widget,
1299 GdkEventScroll *event)
1301 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1304 state = event->state & gtk_accelerator_get_default_mod_mask ();
1308 switch (event->direction) {
1309 case GDK_SCROLL_DOWN:
1310 case GDK_SCROLL_RIGHT:
1311 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
1314 case GDK_SCROLL_LEFT:
1315 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1324 add_change_page_binding_keypad (GtkBindingSet *binding_set,
1326 GdkModifierType modifiers,
1327 GtkScrollType scroll)
1329 guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
1331 gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1333 GTK_TYPE_SCROLL_TYPE, scroll);
1334 gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1336 GTK_TYPE_SCROLL_TYPE, scroll);
1340 ev_view_presentation_set_property (GObject *object,
1342 const GValue *value,
1345 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1349 pview->document = g_value_dup_object (value);
1350 pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
1352 case PROP_CURRENT_PAGE:
1353 pview->current_page = g_value_get_uint (value);
1356 ev_view_presentation_set_rotation (pview, g_value_get_uint (value));
1358 case PROP_INVERTED_COLORS:
1359 pview->inverted_colors = g_value_get_boolean (value);
1362 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1367 ev_view_presentation_get_property (GObject *object,
1372 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1376 g_value_set_uint (value, ev_view_presentation_get_rotation (pview));
1379 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1384 ev_view_presentation_constructor (GType type,
1385 guint n_construct_properties,
1386 GObjectConstructParam *construct_params)
1389 EvViewPresentation *pview;
1391 object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1392 n_construct_properties,
1394 pview = EV_VIEW_PRESENTATION (object);
1395 pview->is_constructing = FALSE;
1397 if (EV_IS_DOCUMENT_LINKS (pview->document)) {
1398 pview->page_cache = ev_page_cache_new (pview->document);
1399 ev_page_cache_set_flags (pview->page_cache, EV_PAGE_DATA_INCLUDE_LINKS);
1406 ev_view_presentation_class_init (EvViewPresentationClass *klass)
1408 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1409 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1410 GtkBindingSet *binding_set;
1411 GtkCssProvider *provider;
1413 klass->change_page = ev_view_presentation_change_page;
1415 gobject_class->dispose = ev_view_presentation_dispose;
1417 widget_class->get_preferred_width = ev_view_presentation_get_preferred_width;
1418 widget_class->get_preferred_height = ev_view_presentation_get_preferred_height;
1419 widget_class->realize = ev_view_presentation_realize;
1420 widget_class->draw = ev_view_presentation_draw;
1421 widget_class->key_press_event = ev_view_presentation_key_press_event;
1422 widget_class->button_release_event = ev_view_presentation_button_release_event;
1423 widget_class->focus_out_event = ev_view_presentation_focus_out;
1424 widget_class->motion_notify_event = ev_view_presentation_motion_notify_event;
1425 widget_class->scroll_event = ev_view_presentation_scroll_event;
1427 gobject_class->constructor = ev_view_presentation_constructor;
1428 gobject_class->set_property = ev_view_presentation_set_property;
1429 gobject_class->get_property = ev_view_presentation_get_property;
1431 g_object_class_install_property (gobject_class,
1433 g_param_spec_object ("document",
1438 G_PARAM_CONSTRUCT_ONLY));
1439 g_object_class_install_property (gobject_class,
1441 g_param_spec_uint ("current_page",
1446 G_PARAM_CONSTRUCT_ONLY));
1447 g_object_class_install_property (gobject_class,
1449 g_param_spec_uint ("rotation",
1451 "Current rotation angle",
1454 G_PARAM_CONSTRUCT));
1455 g_object_class_install_property (gobject_class,
1456 PROP_INVERTED_COLORS,
1457 g_param_spec_boolean ("inverted_colors",
1459 "Whether presentation is displayed with inverted colors",
1462 G_PARAM_CONSTRUCT_ONLY));
1464 signals[CHANGE_PAGE] =
1465 g_signal_new ("change_page",
1466 G_OBJECT_CLASS_TYPE (gobject_class),
1467 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1468 G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
1470 g_cclosure_marshal_VOID__ENUM,
1472 GTK_TYPE_SCROLL_TYPE);
1474 g_signal_new ("finished",
1475 G_OBJECT_CLASS_TYPE (gobject_class),
1476 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1477 G_STRUCT_OFFSET (EvViewPresentationClass, finished),
1479 g_cclosure_marshal_VOID__VOID,
1482 signals[SIGNAL_EXTERNAL_LINK] =
1483 g_signal_new ("external-link",
1484 G_TYPE_FROM_CLASS (gobject_class),
1485 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1486 G_STRUCT_OFFSET (EvViewPresentationClass, external_link),
1488 g_cclosure_marshal_VOID__OBJECT,
1492 binding_set = gtk_binding_set_by_class (klass);
1493 add_change_page_binding_keypad (binding_set, GDK_KEY_Left, 0, GTK_SCROLL_PAGE_BACKWARD);
1494 add_change_page_binding_keypad (binding_set, GDK_KEY_Right, 0, GTK_SCROLL_PAGE_FORWARD);
1495 add_change_page_binding_keypad (binding_set, GDK_KEY_Up, 0, GTK_SCROLL_PAGE_BACKWARD);
1496 add_change_page_binding_keypad (binding_set, GDK_KEY_Down, 0, GTK_SCROLL_PAGE_FORWARD);
1497 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
1499 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1500 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0,
1502 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1503 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
1505 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1506 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
1508 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1509 gtk_binding_entry_add_signal (binding_set, GDK_KEY_J, 0,
1511 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1512 gtk_binding_entry_add_signal (binding_set, GDK_KEY_H, 0,
1514 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1515 gtk_binding_entry_add_signal (binding_set, GDK_KEY_L, 0,
1517 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1518 gtk_binding_entry_add_signal (binding_set, GDK_KEY_K, 0,
1520 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1522 provider = gtk_css_provider_new ();
1523 gtk_css_provider_load_from_data (provider,
1524 "EvViewPresentation {\n"
1525 " background-color: black; }",
1527 gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
1528 GTK_STYLE_PROVIDER (provider),
1529 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1530 g_object_unref (provider);
1534 ev_view_presentation_init (EvViewPresentation *pview)
1536 gtk_widget_set_can_focus (GTK_WIDGET (pview), TRUE);
1537 pview->is_constructing = TRUE;
1541 ev_view_presentation_new (EvDocument *document,
1544 gboolean inverted_colors)
1546 g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
1547 g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
1549 return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
1550 "document", document,
1551 "current_page", current_page,
1552 "rotation", rotation,
1553 "inverted_colors", inverted_colors,
1558 ev_view_presentation_get_current_page (EvViewPresentation *pview)
1560 return pview->current_page;
1564 ev_view_presentation_set_rotation (EvViewPresentation *pview,
1567 if (rotation >= 360)
1569 else if (rotation < 0)
1572 if (pview->rotation == rotation)
1575 pview->rotation = rotation;
1576 g_object_notify (G_OBJECT (pview), "rotation");
1577 if (pview->is_constructing)
1581 ev_view_presentation_reset_jobs (pview);
1582 ev_view_presentation_update_current_page (pview, pview->current_page);
1586 ev_view_presentation_get_rotation (EvViewPresentation *pview)
1588 return pview->rotation;