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
61 cairo_surface_t *current_surface;
64 gboolean inverted_colors;
65 EvPresentationState state;
72 guint hide_cursor_timeout_id;
75 GtkWidget *goto_window;
76 GtkWidget *goto_entry;
79 guint trans_timeout_id;
82 gboolean enable_animations;
83 EvTransitionAnimation *animation;
86 EvPageCache *page_cache;
93 struct _EvViewPresentationClass
95 GtkWidgetClass base_class;
98 void (* change_page) (EvViewPresentation *pview,
99 GtkScrollType scroll);
100 void (* finished) (EvViewPresentation *pview);
101 void (* external_link) (EvViewPresentation *pview,
102 EvLinkAction *action);
105 static guint signals[N_SIGNALS] = { 0 };
107 static void ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
111 #define HIDE_CURSOR_TIMEOUT 5
113 G_DEFINE_TYPE (EvViewPresentation, ev_view_presentation, GTK_TYPE_WIDGET)
115 static GdkRGBA black = { 0., 0., 0., 1. };
116 static GdkRGBA white = { 1., 1., 1., 1. };
119 ev_view_presentation_set_normal (EvViewPresentation *pview)
121 GtkWidget *widget = GTK_WIDGET (pview);
123 if (pview->state == EV_PRESENTATION_NORMAL)
126 pview->state = EV_PRESENTATION_NORMAL;
127 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
128 gtk_widget_queue_draw (widget);
132 ev_view_presentation_set_black (EvViewPresentation *pview)
134 GtkWidget *widget = GTK_WIDGET (pview);
136 if (pview->state == EV_PRESENTATION_BLACK)
139 pview->state = EV_PRESENTATION_BLACK;
140 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
141 gtk_widget_queue_draw (widget);
145 ev_view_presentation_set_white (EvViewPresentation *pview)
147 GtkWidget *widget = GTK_WIDGET (pview);
149 if (pview->state == EV_PRESENTATION_WHITE)
152 pview->state = EV_PRESENTATION_WHITE;
153 gdk_window_set_background_rgba (gtk_widget_get_window (widget), &white);
154 gtk_widget_queue_draw (widget);
158 ev_view_presentation_set_end (EvViewPresentation *pview)
160 GtkWidget *widget = GTK_WIDGET (pview);
162 if (pview->state == EV_PRESENTATION_END)
165 pview->state = EV_PRESENTATION_END;
166 gtk_widget_queue_draw (widget);
170 ev_view_presentation_get_scale_for_page (EvViewPresentation *pview,
173 if (!ev_document_is_page_size_uniform (pview->document) || pview->scale == 0) {
174 gdouble width, height;
176 ev_document_get_page_size (pview->document, page, &width, &height);
177 if (pview->rotation == 90 || pview->rotation == 270) {
184 pview->scale = MIN (pview->monitor_width / width, pview->monitor_height / height);
191 ev_view_presentation_get_page_area (EvViewPresentation *pview,
194 GtkWidget *widget = GTK_WIDGET (pview);
195 GtkAllocation allocation;
196 gdouble doc_width, doc_height;
197 gint view_width, view_height;
200 ev_document_get_page_size (pview->document,
202 &doc_width, &doc_height);
203 scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
205 if (pview->rotation == 90 || pview->rotation == 270) {
206 view_width = (gint)((doc_height * scale) + 0.5);
207 view_height = (gint)((doc_width * scale) + 0.5);
209 view_width = (gint)((doc_width * scale) + 0.5);
210 view_height = (gint)((doc_height * scale) + 0.5);
213 gtk_widget_get_allocation (widget, &allocation);
215 area->x = (MAX (0, allocation.width - view_width)) / 2;
216 area->y = (MAX (0, allocation.height - view_height)) / 2;
217 area->width = view_width;
218 area->height = view_height;
221 /* Page Transition */
223 transition_next_page (EvViewPresentation *pview)
225 ev_view_presentation_next_page (pview);
231 ev_view_presentation_transition_stop (EvViewPresentation *pview)
233 if (pview->trans_timeout_id > 0)
234 g_source_remove (pview->trans_timeout_id);
235 pview->trans_timeout_id = 0;
239 ev_view_presentation_transition_start (EvViewPresentation *pview)
243 if (!EV_IS_DOCUMENT_TRANSITION (pview->document))
246 ev_view_presentation_transition_stop (pview);
248 duration = ev_document_transition_get_page_duration (EV_DOCUMENT_TRANSITION (pview->document),
249 pview->current_page);
251 pview->trans_timeout_id =
252 g_timeout_add_seconds (duration,
253 (GSourceFunc) transition_next_page,
260 ev_view_presentation_animation_cancel (EvViewPresentation *pview)
262 if (pview->animation) {
263 g_object_unref (pview->animation);
264 pview->animation = NULL;
269 ev_view_presentation_transition_animation_finish (EvViewPresentation *pview)
271 ev_view_presentation_animation_cancel (pview);
272 ev_view_presentation_transition_start (pview);
273 gtk_widget_queue_draw (GTK_WIDGET (pview));
277 ev_view_presentation_transition_animation_frame (EvViewPresentation *pview,
280 gtk_widget_queue_draw (GTK_WIDGET (pview));
284 ev_view_presentation_animation_start (EvViewPresentation *pview,
287 EvTransitionEffect *effect = NULL;
288 cairo_surface_t *surface;
291 if (!pview->enable_animations)
294 if (pview->current_page == new_page)
297 effect = ev_document_transition_get_effect (EV_DOCUMENT_TRANSITION (pview->document),
302 pview->animation = ev_transition_animation_new (effect);
304 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
305 ev_transition_animation_set_origin_surface (pview->animation,
307 surface : pview->current_surface);
309 jump = new_page - pview->current_page;
311 surface = pview->prev_job ? EV_JOB_RENDER (pview->prev_job)->surface : NULL;
313 surface = pview->next_job ? EV_JOB_RENDER (pview->next_job)->surface : NULL;
317 ev_transition_animation_set_dest_surface (pview->animation, surface);
319 g_signal_connect_swapped (pview->animation, "frame",
320 G_CALLBACK (ev_view_presentation_transition_animation_frame),
322 g_signal_connect_swapped (pview->animation, "finished",
323 G_CALLBACK (ev_view_presentation_transition_animation_finish),
327 /* Page Navigation */
329 job_finished_cb (EvJob *job,
330 EvViewPresentation *pview)
332 EvJobRender *job_render = EV_JOB_RENDER (job);
334 if (pview->inverted_colors)
335 ev_document_misc_invert_surface (job_render->surface);
337 if (job != pview->curr_job)
340 if (pview->animation) {
341 ev_transition_animation_set_dest_surface (pview->animation,
342 job_render->surface);
344 ev_view_presentation_transition_start (pview);
345 gtk_widget_queue_draw (GTK_WIDGET (pview));
350 ev_view_presentation_schedule_new_job (EvViewPresentation *pview,
352 EvJobPriority priority)
357 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
360 scale = ev_view_presentation_get_scale_for_page (pview, page);
361 job = ev_job_render_new (pview->document, page, pview->rotation, scale, 0, 0);
362 g_signal_connect (job, "finished",
363 G_CALLBACK (job_finished_cb),
365 ev_job_scheduler_push_job (job, priority);
371 ev_view_presentation_delete_job (EvViewPresentation *pview,
377 g_signal_handlers_disconnect_by_func (job, job_finished_cb, pview);
379 g_object_unref (job);
383 ev_view_presentation_update_current_page (EvViewPresentation *pview,
388 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
391 ev_view_presentation_animation_cancel (pview);
392 ev_view_presentation_animation_start (pview, page);
394 jump = page - pview->current_page;
398 if (!pview->curr_job)
399 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
400 if (!pview->next_job)
401 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
402 if (!pview->prev_job)
403 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
406 ev_view_presentation_delete_job (pview, pview->next_job);
407 pview->next_job = pview->curr_job;
408 pview->curr_job = pview->prev_job;
410 if (!pview->curr_job)
411 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
413 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
414 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
415 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
419 ev_view_presentation_delete_job (pview, pview->prev_job);
420 pview->prev_job = pview->curr_job;
421 pview->curr_job = pview->next_job;
423 if (!pview->curr_job)
424 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
426 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
427 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
428 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
432 ev_view_presentation_delete_job (pview, pview->next_job);
433 ev_view_presentation_delete_job (pview, pview->curr_job);
434 pview->next_job = pview->prev_job;
436 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
437 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
438 if (!pview->next_job)
439 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
441 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
444 ev_view_presentation_delete_job (pview, pview->prev_job);
445 ev_view_presentation_delete_job (pview, pview->curr_job);
446 pview->prev_job = pview->next_job;
448 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
449 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
450 if (!pview->prev_job)
451 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
453 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
456 ev_view_presentation_delete_job (pview, pview->prev_job);
457 ev_view_presentation_delete_job (pview, pview->curr_job);
458 ev_view_presentation_delete_job (pview, pview->next_job);
460 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
462 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
463 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
465 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
466 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
470 pview->current_page = page;
472 if (pview->page_cache)
473 ev_page_cache_set_page_range (pview->page_cache, page, page);
475 if (pview->cursor != EV_VIEW_CURSOR_HIDDEN) {
478 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
479 ev_view_presentation_set_cursor_for_location (pview, x, y);
482 if (EV_JOB_RENDER (pview->curr_job)->surface)
483 gtk_widget_queue_draw (GTK_WIDGET (pview));
487 ev_view_presentation_next_page (EvViewPresentation *pview)
492 switch (pview->state) {
493 case EV_PRESENTATION_BLACK:
494 case EV_PRESENTATION_WHITE:
495 ev_view_presentation_set_normal (pview);
496 case EV_PRESENTATION_END:
498 case EV_PRESENTATION_NORMAL:
502 n_pages = ev_document_get_n_pages (pview->document);
503 new_page = pview->current_page + 1;
505 if (new_page == n_pages)
506 ev_view_presentation_set_end (pview);
508 ev_view_presentation_update_current_page (pview, new_page);
512 ev_view_presentation_previous_page (EvViewPresentation *pview)
516 switch (pview->state) {
517 case EV_PRESENTATION_BLACK:
518 case EV_PRESENTATION_WHITE:
519 ev_view_presentation_set_normal (pview);
521 case EV_PRESENTATION_END:
522 pview->state = EV_PRESENTATION_NORMAL;
523 new_page = pview->current_page;
525 case EV_PRESENTATION_NORMAL:
526 new_page = pview->current_page - 1;
530 ev_view_presentation_update_current_page (pview, new_page);
534 #define KEY_IS_NUMERIC(keyval) \
535 ((keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9) || (keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9))
537 /* Cut and paste from gtkwindow.c */
539 send_focus_change (GtkWidget *widget,
542 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
544 fevent->focus_change.type = GDK_FOCUS_CHANGE;
545 fevent->focus_change.window = gtk_widget_get_window (widget);
546 fevent->focus_change.in = in;
547 if (fevent->focus_change.window)
548 g_object_ref (fevent->focus_change.window);
550 gtk_widget_send_focus_change (widget, fevent);
552 gdk_event_free (fevent);
556 ev_view_presentation_goto_window_hide (EvViewPresentation *pview)
558 /* send focus-in event */
559 send_focus_change (pview->goto_entry, FALSE);
560 gtk_widget_hide (pview->goto_window);
561 gtk_entry_set_text (GTK_ENTRY (pview->goto_entry), "");
565 ev_view_presentation_goto_window_delete_event (GtkWidget *widget,
567 EvViewPresentation *pview)
569 ev_view_presentation_goto_window_hide (pview);
575 ev_view_presentation_goto_window_key_press_event (GtkWidget *widget,
577 EvViewPresentation *pview)
579 switch (event->keyval) {
583 case GDK_KEY_ISO_Left_Tab:
584 ev_view_presentation_goto_window_hide (pview);
587 case GDK_KEY_KP_Enter:
588 case GDK_KEY_ISO_Enter:
589 case GDK_KEY_BackSpace:
593 if (!KEY_IS_NUMERIC (event->keyval))
601 ev_view_presentation_goto_window_button_press_event (GtkWidget *widget,
602 GdkEventButton *event,
603 EvViewPresentation *pview)
605 ev_view_presentation_goto_window_hide (pview);
611 ev_view_presentation_goto_entry_activate (GtkEntry *entry,
612 EvViewPresentation *pview)
617 text = gtk_entry_get_text (entry);
618 page = atoi (text) - 1;
620 ev_view_presentation_goto_window_hide (pview);
621 ev_view_presentation_update_current_page (pview, page);
625 ev_view_presentation_goto_window_create (EvViewPresentation *pview)
627 GtkWidget *frame, *hbox, *label;
628 GtkWindow *toplevel, *goto_window;
630 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (pview)));
631 goto_window = GTK_WINDOW (pview->goto_window);
633 if (pview->goto_window) {
634 if (gtk_window_has_group (toplevel))
635 gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
636 else if (gtk_window_has_group (goto_window))
637 gtk_window_group_remove_window (gtk_window_get_group (goto_window), goto_window);
642 pview->goto_window = gtk_window_new (GTK_WINDOW_POPUP);
643 gtk_window_set_screen (goto_window, gtk_widget_get_screen (GTK_WIDGET (pview)));
645 if (gtk_window_has_group (toplevel))
646 gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
648 gtk_window_set_modal (goto_window, TRUE);
650 g_signal_connect (pview->goto_window, "delete_event",
651 G_CALLBACK (ev_view_presentation_goto_window_delete_event),
653 g_signal_connect (pview->goto_window, "key_press_event",
654 G_CALLBACK (ev_view_presentation_goto_window_key_press_event),
656 g_signal_connect (pview->goto_window, "button_press_event",
657 G_CALLBACK (ev_view_presentation_goto_window_button_press_event),
660 frame = gtk_frame_new (NULL);
661 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
662 gtk_container_add (GTK_CONTAINER (pview->goto_window), frame);
663 gtk_widget_show (frame);
665 hbox = gtk_hbox_new (FALSE, 0);
666 gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
667 gtk_container_add (GTK_CONTAINER (frame), hbox);
668 gtk_widget_show (hbox);
670 label = gtk_label_new (_("Jump to page:"));
671 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 3);
672 gtk_widget_show (label);
673 gtk_widget_realize (label);
675 pview->goto_entry = gtk_entry_new ();
676 g_signal_connect (pview->goto_entry, "activate",
677 G_CALLBACK (ev_view_presentation_goto_entry_activate),
679 gtk_box_pack_start (GTK_BOX (hbox), pview->goto_entry, TRUE, TRUE, 0);
680 gtk_widget_show (pview->goto_entry);
681 gtk_widget_realize (pview->goto_entry);
685 ev_view_presentation_goto_entry_grab_focus (EvViewPresentation *pview)
687 GtkWidgetClass *entry_parent_class;
689 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (pview->goto_entry));
690 (entry_parent_class->grab_focus) (pview->goto_entry);
692 send_focus_change (pview->goto_entry, TRUE);
696 ev_view_presentation_goto_window_send_key_event (EvViewPresentation *pview,
699 GdkEventKey *new_event;
702 /* Move goto window off screen */
703 screen = gtk_widget_get_screen (GTK_WIDGET (pview));
704 gtk_window_move (GTK_WINDOW (pview->goto_window),
705 gdk_screen_get_width (screen) + 1,
706 gdk_screen_get_height (screen) + 1);
707 gtk_widget_show (pview->goto_window);
709 new_event = (GdkEventKey *) gdk_event_copy (event);
710 g_object_unref (new_event->window);
711 new_event->window = gtk_widget_get_window (pview->goto_window);
712 if (new_event->window)
713 g_object_ref (new_event->window);
714 gtk_widget_realize (pview->goto_window);
716 gtk_widget_event (pview->goto_window, (GdkEvent *)new_event);
717 gdk_event_free ((GdkEvent *)new_event);
718 gtk_widget_hide (pview->goto_window);
723 ev_view_presentation_link_is_supported (EvViewPresentation *pview,
726 EvLinkAction *action;
728 action = ev_link_get_action (link);
732 switch (ev_link_action_get_action_type (action)) {
733 case EV_LINK_ACTION_TYPE_GOTO_DEST:
734 return ev_link_action_get_dest (action) != NULL;
735 case EV_LINK_ACTION_TYPE_NAMED:
736 case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
737 case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
738 case EV_LINK_ACTION_TYPE_LAUNCH:
748 ev_view_presentation_get_link_at_location (EvViewPresentation *pview,
752 GdkRectangle page_area;
753 EvMappingList *link_mapping;
755 gdouble width, height;
756 gdouble new_x, new_y;
759 if (!pview->page_cache)
762 ev_document_get_page_size (pview->document, pview->current_page, &width, &height);
763 ev_view_presentation_get_page_area (pview, &page_area);
764 scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
765 x = (x - page_area.x) / scale;
766 y = (y - page_area.y) / scale;
767 switch (pview->rotation) {
786 g_assert_not_reached ();
789 link_mapping = ev_page_cache_get_link_mapping (pview->page_cache, pview->current_page);
791 link = link_mapping ? ev_mapping_list_get_data (link_mapping, new_x, new_y) : NULL;
793 return link && ev_view_presentation_link_is_supported (pview, link) ? link : NULL;
797 ev_vew_presentation_handle_link (EvViewPresentation *pview,
800 EvLinkAction *action;
802 action = ev_link_get_action (link);
804 switch (ev_link_action_get_action_type (action)) {
805 case EV_LINK_ACTION_TYPE_NAMED: {
806 const gchar *name = ev_link_action_get_name (action);
808 if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
809 ev_view_presentation_update_current_page (pview, 0);
810 } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
811 ev_view_presentation_update_current_page (pview, pview->current_page - 1);
812 } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
813 ev_view_presentation_update_current_page (pview, pview->current_page + 1);
814 } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
817 n_pages = ev_document_get_n_pages (pview->document);
818 ev_view_presentation_update_current_page (pview, n_pages - 1);
823 case EV_LINK_ACTION_TYPE_GOTO_DEST: {
827 dest = ev_link_action_get_dest (action);
828 page = ev_document_links_get_dest_page (EV_DOCUMENT_LINKS (pview->document), dest);
829 ev_view_presentation_update_current_page (pview, page);
832 case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
833 case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
834 case EV_LINK_ACTION_TYPE_LAUNCH:
835 g_signal_emit (pview, signals[SIGNAL_EXTERNAL_LINK], 0, action);
844 ev_view_presentation_set_cursor (EvViewPresentation *pview,
845 EvViewCursor view_cursor)
850 if (pview->cursor == view_cursor)
853 widget = GTK_WIDGET (pview);
854 if (!gtk_widget_get_realized (widget))
855 gtk_widget_realize (widget);
857 pview->cursor = view_cursor;
859 cursor = ev_view_cursor_new (gtk_widget_get_display (widget), view_cursor);
860 gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
863 g_object_unref (cursor);
867 ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
871 if (ev_view_presentation_get_link_at_location (pview, x, y))
872 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_LINK);
874 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_NORMAL);
878 hide_cursor_timeout_cb (EvViewPresentation *pview)
880 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_HIDDEN);
881 pview->hide_cursor_timeout_id = 0;
887 ev_view_presentation_hide_cursor_timeout_stop (EvViewPresentation *pview)
889 if (pview->hide_cursor_timeout_id > 0)
890 g_source_remove (pview->hide_cursor_timeout_id);
891 pview->hide_cursor_timeout_id = 0;
895 ev_view_presentation_hide_cursor_timeout_start (EvViewPresentation *pview)
897 ev_view_presentation_hide_cursor_timeout_stop (pview);
898 pview->hide_cursor_timeout_id =
899 g_timeout_add_seconds (HIDE_CURSOR_TIMEOUT,
900 (GSourceFunc)hide_cursor_timeout_cb,
905 ev_view_presentation_update_current_surface (EvViewPresentation *pview,
906 cairo_surface_t *surface)
908 if (!surface || pview->current_surface == surface)
911 cairo_surface_reference (surface);
912 if (pview->current_surface)
913 cairo_surface_destroy (pview->current_surface);
914 pview->current_surface = surface;
918 ev_view_presentation_dispose (GObject *object)
920 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
922 if (pview->document) {
923 g_object_unref (pview->document);
924 pview->document = NULL;
927 ev_view_presentation_animation_cancel (pview);
928 ev_view_presentation_transition_stop (pview);
929 ev_view_presentation_hide_cursor_timeout_stop (pview);
931 if (pview->curr_job) {
932 ev_view_presentation_delete_job (pview, pview->curr_job);
933 pview->curr_job = NULL;
936 if (pview->prev_job) {
937 ev_view_presentation_delete_job (pview, pview->prev_job);
938 pview->prev_job = NULL;
941 if (pview->next_job) {
942 ev_view_presentation_delete_job (pview, pview->next_job);
943 pview->next_job = NULL;
946 if (pview->current_surface) {
947 cairo_surface_destroy (pview->current_surface);
948 pview->current_surface = NULL;
951 if (pview->page_cache) {
952 g_object_unref (pview->page_cache);
953 pview->page_cache = NULL;
956 if (pview->goto_window) {
957 gtk_widget_destroy (pview->goto_window);
958 pview->goto_window = NULL;
959 pview->goto_entry = NULL;
962 G_OBJECT_CLASS (ev_view_presentation_parent_class)->dispose (object);
966 ev_view_presentation_get_preferred_width (GtkWidget *widget,
970 *minimum = *natural = 0;
974 ev_view_presentation_get_preferred_height (GtkWidget *widget,
978 *minimum = *natural = 0;
982 ev_view_presentation_draw_end_page (EvViewPresentation *pview,
985 GtkWidget *widget = GTK_WIDGET (pview);
987 PangoFontDescription *font_desc;
989 GtkAllocation allocation;
990 GdkRectangle area = {0};
991 const gchar *text = _("End of presentation. Click to exit.");
993 if (pview->state != EV_PRESENTATION_END)
996 layout = gtk_widget_create_pango_layout (widget, NULL);
997 markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", text);
998 pango_layout_set_markup (layout, markup, -1);
1001 font_desc = pango_font_description_new ();
1002 pango_font_description_set_size (font_desc, 16 * PANGO_SCALE);
1003 pango_layout_set_font_description (layout, font_desc);
1005 gtk_widget_get_allocation (widget, &allocation);
1006 area.width = allocation.width;
1007 area.height = allocation.height;
1009 gtk_render_layout (gtk_widget_get_style_context (widget),
1010 cr, 15, 15, layout);
1012 pango_font_description_free (font_desc);
1013 g_object_unref (layout);
1017 ev_view_presentation_draw (GtkWidget *widget,
1020 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1021 GdkRectangle page_area;
1022 GdkRectangle overlap;
1023 cairo_surface_t *surface;
1024 GdkRectangle clip_rect;
1026 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
1029 switch (pview->state) {
1030 case EV_PRESENTATION_END:
1031 ev_view_presentation_draw_end_page (pview, cr);
1033 case EV_PRESENTATION_BLACK:
1034 case EV_PRESENTATION_WHITE:
1036 case EV_PRESENTATION_NORMAL:
1040 if (pview->animation) {
1041 if (ev_transition_animation_ready (pview->animation)) {
1042 ev_view_presentation_get_page_area (pview, &page_area);
1046 /* normalize to x=0, y=0 */
1047 cairo_translate (cr, page_area.x, page_area.y);
1048 page_area.x = page_area.y = 0;
1050 /* Try to fix rounding errors */
1053 ev_transition_animation_paint (pview->animation, cr, page_area);
1061 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
1063 ev_view_presentation_update_current_surface (pview, surface);
1064 } else if (pview->current_surface) {
1065 surface = pview->current_surface;
1070 ev_view_presentation_get_page_area (pview, &page_area);
1071 if (gdk_rectangle_intersect (&page_area, &clip_rect, &overlap)) {
1074 /* Try to fix rounding errors. See bug #438760 */
1075 if (overlap.width == page_area.width)
1078 cairo_rectangle (cr, overlap.x, overlap.y, overlap.width, overlap.height);
1079 cairo_set_source_surface (cr, surface, page_area.x, page_area.y);
1089 ev_view_presentation_key_press_event (GtkWidget *widget,
1092 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1094 if (pview->state == EV_PRESENTATION_END)
1095 return gtk_bindings_activate_event (G_OBJECT (widget), event);
1097 switch (event->keyval) {
1100 case GDK_KEY_period:
1101 case GDK_KEY_KP_Decimal:
1102 if (pview->state == EV_PRESENTATION_BLACK)
1103 ev_view_presentation_set_normal (pview);
1105 ev_view_presentation_set_black (pview);
1110 if (pview->state == EV_PRESENTATION_WHITE)
1111 ev_view_presentation_set_normal (pview);
1113 ev_view_presentation_set_white (pview);
1117 if (pview->state == EV_PRESENTATION_NORMAL) {
1118 ev_view_presentation_update_current_page (pview, 0);
1123 if (pview->state == EV_PRESENTATION_NORMAL) {
1126 page = ev_document_get_n_pages (pview->document) - 1;
1127 ev_view_presentation_update_current_page (pview, page);
1136 ev_view_presentation_set_normal (pview);
1138 if (ev_document_get_n_pages (pview->document) > 1 && KEY_IS_NUMERIC (event->keyval)) {
1141 ev_view_presentation_goto_window_create (pview);
1142 ev_view_presentation_goto_window_send_key_event (pview, (GdkEvent *)event);
1143 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
1144 gtk_window_move (GTK_WINDOW (pview->goto_window), x, y);
1145 gtk_widget_show (pview->goto_window);
1146 ev_view_presentation_goto_entry_grab_focus (pview);
1151 return gtk_bindings_activate_event (G_OBJECT (widget), event);
1155 ev_view_presentation_button_release_event (GtkWidget *widget,
1156 GdkEventButton *event)
1158 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1160 switch (event->button) {
1164 if (pview->state == EV_PRESENTATION_END) {
1165 g_signal_emit (pview, signals[FINISHED], 0, NULL);
1170 link = ev_view_presentation_get_link_at_location (pview,
1174 ev_vew_presentation_handle_link (pview, link);
1176 ev_view_presentation_next_page (pview);
1180 ev_view_presentation_previous_page (pview);
1190 ev_view_presentation_focus_out (GtkWidget *widget,
1191 GdkEventFocus *event)
1193 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1195 if (pview->goto_window)
1196 ev_view_presentation_goto_window_hide (pview);
1202 ev_view_presentation_motion_notify_event (GtkWidget *widget,
1203 GdkEventMotion *event)
1205 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1207 ev_view_presentation_hide_cursor_timeout_start (pview);
1208 ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
1214 init_presentation (GtkWidget *widget)
1216 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1217 GdkScreen *screen = gtk_widget_get_screen (widget);
1218 GdkRectangle monitor;
1221 monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget));
1222 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1223 pview->monitor_width = monitor.width;
1224 pview->monitor_height = monitor.height;
1226 ev_view_presentation_update_current_page (pview, pview->current_page);
1227 ev_view_presentation_hide_cursor_timeout_start (pview);
1233 ev_view_presentation_realize (GtkWidget *widget)
1236 GdkWindowAttr attributes;
1237 GtkAllocation allocation;
1239 gtk_widget_set_realized (widget, TRUE);
1241 attributes.window_type = GDK_WINDOW_CHILD;
1242 attributes.wclass = GDK_INPUT_OUTPUT;
1243 attributes.visual = gtk_widget_get_visual (widget);
1245 gtk_widget_get_allocation (widget, &allocation);
1246 attributes.x = allocation.x;
1247 attributes.y = allocation.y;
1248 attributes.width = allocation.width;
1249 attributes.height = allocation.height;
1250 attributes.event_mask = GDK_EXPOSURE_MASK |
1251 GDK_BUTTON_PRESS_MASK |
1252 GDK_BUTTON_RELEASE_MASK |
1254 GDK_KEY_PRESS_MASK |
1255 GDK_POINTER_MOTION_MASK |
1256 GDK_POINTER_MOTION_HINT_MASK |
1257 GDK_ENTER_NOTIFY_MASK |
1258 GDK_LEAVE_NOTIFY_MASK;
1260 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1262 GDK_WA_X | GDK_WA_Y |
1265 gdk_window_set_user_data (window, widget);
1266 gtk_widget_set_window (widget, window);
1267 gtk_style_context_set_background (gtk_widget_get_style_context (widget),
1270 g_idle_add ((GSourceFunc)init_presentation, widget);
1274 ev_view_presentation_change_page (EvViewPresentation *pview,
1275 GtkScrollType scroll)
1278 case GTK_SCROLL_PAGE_FORWARD:
1279 ev_view_presentation_next_page (pview);
1281 case GTK_SCROLL_PAGE_BACKWARD:
1282 ev_view_presentation_previous_page (pview);
1285 g_assert_not_reached ();
1290 ev_view_presentation_scroll_event (GtkWidget *widget,
1291 GdkEventScroll *event)
1293 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1296 state = event->state & gtk_accelerator_get_default_mod_mask ();
1300 switch (event->direction) {
1301 case GDK_SCROLL_DOWN:
1302 case GDK_SCROLL_RIGHT:
1303 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
1306 case GDK_SCROLL_LEFT:
1307 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1316 add_change_page_binding_keypad (GtkBindingSet *binding_set,
1318 GdkModifierType modifiers,
1319 GtkScrollType scroll)
1321 guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
1323 gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1325 GTK_TYPE_SCROLL_TYPE, scroll);
1326 gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1328 GTK_TYPE_SCROLL_TYPE, scroll);
1332 ev_view_presentation_set_property (GObject *object,
1334 const GValue *value,
1337 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1341 pview->document = g_value_dup_object (value);
1342 pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
1344 case PROP_CURRENT_PAGE:
1345 pview->current_page = g_value_get_uint (value);
1348 pview->rotation = g_value_get_uint (value);
1350 case PROP_INVERTED_COLORS:
1351 pview->inverted_colors = g_value_get_boolean (value);
1354 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1359 ev_view_presentation_constructor (GType type,
1360 guint n_construct_properties,
1361 GObjectConstructParam *construct_params)
1364 EvViewPresentation *pview;
1366 object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1367 n_construct_properties,
1369 pview = EV_VIEW_PRESENTATION (object);
1371 if (EV_IS_DOCUMENT_LINKS (pview->document)) {
1372 pview->page_cache = ev_page_cache_new (pview->document);
1373 ev_page_cache_set_flags (pview->page_cache, EV_PAGE_DATA_INCLUDE_LINKS);
1380 ev_view_presentation_class_init (EvViewPresentationClass *klass)
1382 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1383 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1384 GtkBindingSet *binding_set;
1385 GtkCssProvider *provider;
1387 klass->change_page = ev_view_presentation_change_page;
1389 gobject_class->dispose = ev_view_presentation_dispose;
1391 widget_class->get_preferred_width = ev_view_presentation_get_preferred_width;
1392 widget_class->get_preferred_height = ev_view_presentation_get_preferred_height;
1393 widget_class->realize = ev_view_presentation_realize;
1394 widget_class->draw = ev_view_presentation_draw;
1395 widget_class->key_press_event = ev_view_presentation_key_press_event;
1396 widget_class->button_release_event = ev_view_presentation_button_release_event;
1397 widget_class->focus_out_event = ev_view_presentation_focus_out;
1398 widget_class->motion_notify_event = ev_view_presentation_motion_notify_event;
1399 widget_class->scroll_event = ev_view_presentation_scroll_event;
1401 gobject_class->constructor = ev_view_presentation_constructor;
1402 gobject_class->set_property = ev_view_presentation_set_property;
1404 g_object_class_install_property (gobject_class,
1406 g_param_spec_object ("document",
1411 G_PARAM_CONSTRUCT_ONLY));
1412 g_object_class_install_property (gobject_class,
1414 g_param_spec_uint ("current_page",
1419 G_PARAM_CONSTRUCT_ONLY));
1420 g_object_class_install_property (gobject_class,
1422 g_param_spec_uint ("rotation",
1424 "Current rotation angle",
1427 G_PARAM_CONSTRUCT_ONLY));
1428 g_object_class_install_property (gobject_class,
1429 PROP_INVERTED_COLORS,
1430 g_param_spec_boolean ("inverted_colors",
1432 "Whether presentation is displayed with inverted colors",
1435 G_PARAM_CONSTRUCT_ONLY));
1437 signals[CHANGE_PAGE] =
1438 g_signal_new ("change_page",
1439 G_OBJECT_CLASS_TYPE (gobject_class),
1440 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1441 G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
1443 g_cclosure_marshal_VOID__ENUM,
1445 GTK_TYPE_SCROLL_TYPE);
1447 g_signal_new ("finished",
1448 G_OBJECT_CLASS_TYPE (gobject_class),
1449 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1450 G_STRUCT_OFFSET (EvViewPresentationClass, finished),
1452 g_cclosure_marshal_VOID__VOID,
1455 signals[SIGNAL_EXTERNAL_LINK] =
1456 g_signal_new ("external-link",
1457 G_TYPE_FROM_CLASS (gobject_class),
1458 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1459 G_STRUCT_OFFSET (EvViewPresentationClass, external_link),
1461 g_cclosure_marshal_VOID__OBJECT,
1465 binding_set = gtk_binding_set_by_class (klass);
1466 add_change_page_binding_keypad (binding_set, GDK_KEY_Left, 0, GTK_SCROLL_PAGE_BACKWARD);
1467 add_change_page_binding_keypad (binding_set, GDK_KEY_Right, 0, GTK_SCROLL_PAGE_FORWARD);
1468 add_change_page_binding_keypad (binding_set, GDK_KEY_Up, 0, GTK_SCROLL_PAGE_BACKWARD);
1469 add_change_page_binding_keypad (binding_set, GDK_KEY_Down, 0, GTK_SCROLL_PAGE_FORWARD);
1470 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
1472 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1473 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0,
1475 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1476 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
1478 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1479 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
1481 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1482 gtk_binding_entry_add_signal (binding_set, GDK_KEY_J, 0,
1484 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1485 gtk_binding_entry_add_signal (binding_set, GDK_KEY_H, 0,
1487 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1488 gtk_binding_entry_add_signal (binding_set, GDK_KEY_L, 0,
1490 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1491 gtk_binding_entry_add_signal (binding_set, GDK_KEY_K, 0,
1493 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1495 provider = gtk_css_provider_new ();
1496 gtk_css_provider_load_from_data (provider,
1497 "EvViewPresentation {\n"
1498 " background-color: black; }",
1500 gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
1501 GTK_STYLE_PROVIDER (provider),
1502 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1503 g_object_unref (provider);
1507 ev_view_presentation_init (EvViewPresentation *pview)
1509 gtk_widget_set_can_focus (GTK_WIDGET (pview), TRUE);
1513 ev_view_presentation_new (EvDocument *document,
1516 gboolean inverted_colors)
1518 g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
1519 g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
1521 return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
1522 "document", document,
1523 "current_page", current_page,
1524 "rotation", rotation,
1525 "inverted_colors", inverted_colors,
1530 ev_view_presentation_get_current_page (EvViewPresentation *pview)
1532 return pview->current_page;