1 /* ev-view-presentation.c
2 * this file is part of evince, a gnome document viewer
4 * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
6 * Evince is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Evince is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include <glib/gi18n-lib.h>
25 #include <gdk/gdkkeysyms.h>
27 #include "ev-view-presentation.h"
29 #include "ev-job-scheduler.h"
30 #include "ev-transition-animation.h"
31 #include "ev-view-cursor.h"
32 #include "ev-page-cache.h"
33 #include "ev-mapping.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);
103 static guint signals[N_SIGNALS] = { 0 };
105 static void ev_view_presentation_next_page (EvViewPresentation *pview);
106 static void ev_view_presentation_previous_page (EvViewPresentation *pview);
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)
116 ev_view_presentation_set_normal (EvViewPresentation *pview)
118 GtkWidget *widget = GTK_WIDGET (pview);
121 if (pview->state == EV_PRESENTATION_NORMAL)
124 pview->state = EV_PRESENTATION_NORMAL;
125 style = gtk_widget_get_style (widget);
126 gdk_window_set_background (gtk_widget_get_window (widget), &style->black);
127 gtk_widget_queue_draw (widget);
131 ev_view_presentation_set_black (EvViewPresentation *pview)
133 GtkWidget *widget = GTK_WIDGET (pview);
136 if (pview->state == EV_PRESENTATION_BLACK)
139 pview->state = EV_PRESENTATION_BLACK;
140 style = gtk_widget_get_style (widget);
141 gdk_window_set_background (gtk_widget_get_window (widget), &style->black);
142 gtk_widget_queue_draw (widget);
146 ev_view_presentation_set_white (EvViewPresentation *pview)
148 GtkWidget *widget = GTK_WIDGET (pview);
151 if (pview->state == EV_PRESENTATION_WHITE)
154 pview->state = EV_PRESENTATION_WHITE;
155 style = gtk_widget_get_style (widget);
156 gdk_window_set_background (gtk_widget_get_window (widget), &style->white);
157 gtk_widget_queue_draw (widget);
161 ev_view_presentation_set_end (EvViewPresentation *pview)
163 GtkWidget *widget = GTK_WIDGET (pview);
165 if (pview->state == EV_PRESENTATION_END)
168 pview->state = EV_PRESENTATION_END;
169 gtk_widget_queue_draw (widget);
173 ev_view_presentation_get_scale_for_page (EvViewPresentation *pview,
176 if (!ev_document_is_page_size_uniform (pview->document) || pview->scale == 0) {
177 gdouble width, height;
179 ev_document_get_page_size (pview->document, page, &width, &height);
180 if (pview->rotation == 90 || pview->rotation == 270) {
187 pview->scale = MIN (pview->monitor_width / width, pview->monitor_height / height);
194 ev_view_presentation_get_page_area (EvViewPresentation *pview,
197 GtkWidget *widget = GTK_WIDGET (pview);
198 GtkAllocation allocation;
199 gdouble doc_width, doc_height;
200 gint view_width, view_height;
203 ev_document_get_page_size (pview->document,
205 &doc_width, &doc_height);
206 scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
208 if (pview->rotation == 90 || pview->rotation == 270) {
209 view_width = (gint)((doc_height * scale) + 0.5);
210 view_height = (gint)((doc_width * scale) + 0.5);
212 view_width = (gint)((doc_width * scale) + 0.5);
213 view_height = (gint)((doc_height * scale) + 0.5);
216 gtk_widget_get_allocation (widget, &allocation);
218 area->x = (MAX (0, allocation.width - view_width)) / 2;
219 area->y = (MAX (0, allocation.height - view_height)) / 2;
220 area->width = view_width;
221 area->height = view_height;
224 /* Page Transition */
226 transition_next_page (EvViewPresentation *pview)
228 ev_view_presentation_next_page (pview);
234 ev_view_presentation_transition_stop (EvViewPresentation *pview)
236 if (pview->trans_timeout_id > 0)
237 g_source_remove (pview->trans_timeout_id);
238 pview->trans_timeout_id = 0;
242 ev_view_presentation_transition_start (EvViewPresentation *pview)
246 if (!EV_IS_DOCUMENT_TRANSITION (pview->document))
249 ev_view_presentation_transition_stop (pview);
251 duration = ev_document_transition_get_page_duration (EV_DOCUMENT_TRANSITION (pview->document),
252 pview->current_page);
254 pview->trans_timeout_id =
255 g_timeout_add_seconds (duration,
256 (GSourceFunc) transition_next_page,
263 ev_view_presentation_animation_cancel (EvViewPresentation *pview)
265 if (pview->animation) {
266 g_object_unref (pview->animation);
267 pview->animation = NULL;
272 ev_view_presentation_transition_animation_finish (EvViewPresentation *pview)
274 ev_view_presentation_animation_cancel (pview);
275 ev_view_presentation_transition_start (pview);
276 gtk_widget_queue_draw (GTK_WIDGET (pview));
280 ev_view_presentation_transition_animation_frame (EvViewPresentation *pview,
283 gtk_widget_queue_draw (GTK_WIDGET (pview));
287 ev_view_presentation_animation_start (EvViewPresentation *pview,
290 EvTransitionEffect *effect = NULL;
291 cairo_surface_t *surface;
294 if (!pview->enable_animations)
297 if (pview->current_page == new_page)
300 effect = ev_document_transition_get_effect (EV_DOCUMENT_TRANSITION (pview->document),
305 pview->animation = ev_transition_animation_new (effect);
307 surface = EV_JOB_RENDER (pview->curr_job)->surface;
308 ev_transition_animation_set_origin_surface (pview->animation,
310 surface : pview->current_surface);
312 jump = new_page - pview->current_page;
314 surface = EV_JOB_RENDER (pview->prev_job)->surface;
316 surface = EV_JOB_RENDER (pview->next_job)->surface;
320 ev_transition_animation_set_dest_surface (pview->animation, surface);
322 g_signal_connect_swapped (pview->animation, "frame",
323 G_CALLBACK (ev_view_presentation_transition_animation_frame),
325 g_signal_connect_swapped (pview->animation, "finished",
326 G_CALLBACK (ev_view_presentation_transition_animation_finish),
330 /* Page Navigation */
332 job_finished_cb (EvJob *job,
333 EvViewPresentation *pview)
335 EvJobRender *job_render = EV_JOB_RENDER (job);
337 if (pview->inverted_colors)
338 ev_document_misc_invert_surface (job_render->surface);
340 if (job != pview->curr_job)
343 if (pview->animation) {
344 ev_transition_animation_set_dest_surface (pview->animation,
345 job_render->surface);
347 ev_view_presentation_transition_start (pview);
348 gtk_widget_queue_draw (GTK_WIDGET (pview));
353 ev_view_presentation_schedule_new_job (EvViewPresentation *pview,
355 EvJobPriority priority)
360 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
363 scale = ev_view_presentation_get_scale_for_page (pview, page);
364 job = ev_job_render_new (pview->document, page, pview->rotation, scale, 0, 0);
365 g_signal_connect (job, "finished",
366 G_CALLBACK (job_finished_cb),
368 ev_job_scheduler_push_job (job, priority);
374 ev_view_presentation_delete_job (EvViewPresentation *pview,
380 g_signal_handlers_disconnect_by_func (job, job_finished_cb, pview);
382 g_object_unref (job);
386 ev_view_presentation_update_current_page (EvViewPresentation *pview,
391 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
394 ev_view_presentation_animation_cancel (pview);
395 ev_view_presentation_animation_start (pview, page);
397 jump = page - pview->current_page;
401 if (!pview->curr_job)
402 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
403 if (!pview->next_job)
404 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
405 if (!pview->prev_job)
406 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
409 ev_view_presentation_delete_job (pview, pview->next_job);
410 pview->next_job = pview->curr_job;
411 pview->curr_job = pview->prev_job;
413 if (!pview->curr_job)
414 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
416 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
417 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
418 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
422 ev_view_presentation_delete_job (pview, pview->prev_job);
423 pview->prev_job = pview->curr_job;
424 pview->curr_job = pview->next_job;
426 if (!pview->curr_job)
427 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
429 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
430 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
431 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
435 ev_view_presentation_delete_job (pview, pview->next_job);
436 ev_view_presentation_delete_job (pview, pview->curr_job);
437 pview->next_job = pview->prev_job;
439 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
440 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
441 if (!pview->next_job)
442 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
444 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
447 ev_view_presentation_delete_job (pview, pview->prev_job);
448 ev_view_presentation_delete_job (pview, pview->curr_job);
449 pview->prev_job = pview->next_job;
451 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
452 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
453 if (!pview->prev_job)
454 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
456 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
459 ev_view_presentation_delete_job (pview, pview->prev_job);
460 ev_view_presentation_delete_job (pview, pview->curr_job);
461 ev_view_presentation_delete_job (pview, pview->next_job);
463 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
465 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
466 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
468 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
469 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
473 pview->current_page = page;
475 if (pview->page_cache)
476 ev_page_cache_set_page_range (pview->page_cache, page, page);
478 if (pview->cursor != EV_VIEW_CURSOR_HIDDEN) {
481 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
482 ev_view_presentation_set_cursor_for_location (pview, x, y);
485 if (EV_JOB_RENDER (pview->curr_job)->surface)
486 gtk_widget_queue_draw (GTK_WIDGET (pview));
490 ev_view_presentation_next_page (EvViewPresentation *pview)
495 switch (pview->state) {
496 case EV_PRESENTATION_BLACK:
497 case EV_PRESENTATION_WHITE:
498 ev_view_presentation_set_normal (pview);
499 case EV_PRESENTATION_END:
501 case EV_PRESENTATION_NORMAL:
505 n_pages = ev_document_get_n_pages (pview->document);
506 new_page = pview->current_page + 1;
508 if (new_page == n_pages)
509 ev_view_presentation_set_end (pview);
511 ev_view_presentation_update_current_page (pview, new_page);
515 ev_view_presentation_previous_page (EvViewPresentation *pview)
519 switch (pview->state) {
520 case EV_PRESENTATION_BLACK:
521 case EV_PRESENTATION_WHITE:
522 ev_view_presentation_set_normal (pview);
524 case EV_PRESENTATION_END:
525 pview->state = EV_PRESENTATION_NORMAL;
526 new_page = pview->current_page;
528 case EV_PRESENTATION_NORMAL:
529 new_page = pview->current_page - 1;
533 ev_view_presentation_update_current_page (pview, new_page);
537 #define KEY_IS_NUMERIC(keyval) \
538 ((keyval >= GDK_0 && keyval <= GDK_9) || (keyval >= GDK_KP_0 && keyval <= GDK_KP_9))
540 /* Cut and paste from gtkwindow.c */
542 send_focus_change (GtkWidget *widget,
545 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
547 fevent->focus_change.type = GDK_FOCUS_CHANGE;
548 fevent->focus_change.window = gtk_widget_get_window (widget);
549 fevent->focus_change.in = in;
550 if (fevent->focus_change.window)
551 g_object_ref (fevent->focus_change.window);
553 gtk_widget_send_focus_change (widget, fevent);
555 gdk_event_free (fevent);
559 ev_view_presentation_goto_window_hide (EvViewPresentation *pview)
561 /* send focus-in event */
562 send_focus_change (pview->goto_entry, FALSE);
563 gtk_widget_hide (pview->goto_window);
564 gtk_entry_set_text (GTK_ENTRY (pview->goto_entry), "");
568 ev_view_presentation_goto_window_delete_event (GtkWidget *widget,
570 EvViewPresentation *pview)
572 ev_view_presentation_goto_window_hide (pview);
578 ev_view_presentation_goto_window_key_press_event (GtkWidget *widget,
580 EvViewPresentation *pview)
582 switch (event->keyval) {
586 case GDK_ISO_Left_Tab:
587 ev_view_presentation_goto_window_hide (pview);
596 if (!KEY_IS_NUMERIC (event->keyval))
604 ev_view_presentation_goto_window_button_press_event (GtkWidget *widget,
605 GdkEventButton *event,
606 EvViewPresentation *pview)
608 ev_view_presentation_goto_window_hide (pview);
614 ev_view_presentation_goto_entry_activate (GtkEntry *entry,
615 EvViewPresentation *pview)
620 text = gtk_entry_get_text (entry);
621 page = atoi (text) - 1;
623 ev_view_presentation_goto_window_hide (pview);
624 ev_view_presentation_update_current_page (pview, page);
628 ev_view_presentation_goto_window_create (EvViewPresentation *pview)
630 GtkWidget *frame, *hbox, *toplevel, *label;
632 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (pview));
634 if (pview->goto_window) {
635 if (GTK_WINDOW (toplevel)->group)
636 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
637 GTK_WINDOW (pview->goto_window));
638 else if (GTK_WINDOW (pview->goto_window)->group)
639 gtk_window_group_remove_window (GTK_WINDOW (pview->goto_window)->group,
640 GTK_WINDOW (pview->goto_window));
644 pview->goto_window = gtk_window_new (GTK_WINDOW_POPUP);
645 gtk_window_set_screen (GTK_WINDOW (pview->goto_window),
646 gtk_widget_get_screen (GTK_WIDGET (pview)));
648 if (GTK_WINDOW (toplevel)->group)
649 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
650 GTK_WINDOW (pview->goto_window));
652 gtk_window_set_modal (GTK_WINDOW (pview->goto_window), TRUE);
654 g_signal_connect (pview->goto_window, "delete_event",
655 G_CALLBACK (ev_view_presentation_goto_window_delete_event),
657 g_signal_connect (pview->goto_window, "key_press_event",
658 G_CALLBACK (ev_view_presentation_goto_window_key_press_event),
660 g_signal_connect (pview->goto_window, "button_press_event",
661 G_CALLBACK (ev_view_presentation_goto_window_button_press_event),
664 frame = gtk_frame_new (NULL);
665 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
666 gtk_container_add (GTK_CONTAINER (pview->goto_window), frame);
667 gtk_widget_show (frame);
669 hbox = gtk_hbox_new (FALSE, 0);
670 gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
671 gtk_container_add (GTK_CONTAINER (frame), hbox);
672 gtk_widget_show (hbox);
674 label = gtk_label_new (_("Jump to page:"));
675 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 3);
676 gtk_widget_show (label);
677 gtk_widget_realize (label);
679 pview->goto_entry = gtk_entry_new ();
680 g_signal_connect (pview->goto_entry, "activate",
681 G_CALLBACK (ev_view_presentation_goto_entry_activate),
683 gtk_box_pack_start (GTK_BOX (hbox), pview->goto_entry, TRUE, TRUE, 0);
684 gtk_widget_show (pview->goto_entry);
685 gtk_widget_realize (pview->goto_entry);
689 ev_view_presentation_goto_entry_grab_focus (EvViewPresentation *pview)
691 GtkWidgetClass *entry_parent_class;
693 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (pview->goto_entry));
694 (entry_parent_class->grab_focus) (pview->goto_entry);
696 send_focus_change (pview->goto_entry, TRUE);
700 ev_view_presentation_goto_window_send_key_event (EvViewPresentation *pview,
703 GdkEventKey *new_event;
706 /* Move goto window off screen */
707 screen = gtk_widget_get_screen (GTK_WIDGET (pview));
708 gtk_window_move (GTK_WINDOW (pview->goto_window),
709 gdk_screen_get_width (screen) + 1,
710 gdk_screen_get_height (screen) + 1);
711 gtk_widget_show (pview->goto_window);
713 new_event = (GdkEventKey *) gdk_event_copy (event);
714 g_object_unref (new_event->window);
715 new_event->window = gtk_widget_get_window (pview->goto_window);
716 if (new_event->window)
717 g_object_ref (new_event->window);
718 gtk_widget_realize (pview->goto_window);
720 gtk_widget_event (pview->goto_window, (GdkEvent *)new_event);
721 gdk_event_free ((GdkEvent *)new_event);
722 gtk_widget_hide (pview->goto_window);
727 ev_view_presentation_link_is_supported (EvViewPresentation *pview,
730 EvLinkAction *action;
732 action = ev_link_get_action (link);
736 switch (ev_link_action_get_action_type (action)) {
737 case EV_LINK_ACTION_TYPE_GOTO_DEST:
738 return ev_link_action_get_dest (action) != NULL;
739 case EV_LINK_ACTION_TYPE_NAMED:
749 ev_view_presentation_get_link_at_location (EvViewPresentation *pview,
753 GdkRectangle page_area;
756 gdouble width, height;
757 gdouble new_x, new_y;
760 if (!pview->page_cache)
763 ev_document_get_page_size (pview->document, pview->current_page, &width, &height);
764 ev_view_presentation_get_page_area (pview, &page_area);
765 scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
766 x = (x - page_area.x) / scale;
767 y = (y - page_area.y) / scale;
768 switch (pview->rotation) {
787 g_assert_not_reached ();
790 link_mapping = ev_page_cache_get_link_mapping (pview->page_cache, pview->current_page);
792 link = link_mapping ? ev_mapping_list_get_data (link_mapping, new_x, new_y) : NULL;
794 return link && ev_view_presentation_link_is_supported (pview, link) ? link : NULL;
798 ev_vew_presentation_goto_link_dest (EvViewPresentation *pview,
801 EvLinkAction *action;
803 action = ev_link_get_action (link);
805 if (ev_link_action_get_action_type (action) == 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);
824 dest = ev_link_action_get_dest (action);
825 page = ev_document_links_get_dest_page (EV_DOCUMENT_LINKS (pview->document), dest);
826 ev_view_presentation_update_current_page (pview, page);
832 ev_view_presentation_set_cursor (EvViewPresentation *pview,
833 EvViewCursor view_cursor)
838 if (pview->cursor == view_cursor)
841 widget = GTK_WIDGET (pview);
842 if (!gtk_widget_get_realized (widget))
843 gtk_widget_realize (widget);
845 pview->cursor = view_cursor;
847 cursor = ev_view_cursor_new (gtk_widget_get_display (widget), view_cursor);
848 gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
851 gdk_cursor_unref (cursor);
855 ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
859 if (ev_view_presentation_get_link_at_location (pview, x, y))
860 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_LINK);
862 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_NORMAL);
866 hide_cursor_timeout_cb (EvViewPresentation *pview)
868 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_HIDDEN);
869 pview->hide_cursor_timeout_id = 0;
875 ev_view_presentation_hide_cursor_timeout_stop (EvViewPresentation *pview)
877 if (pview->hide_cursor_timeout_id > 0)
878 g_source_remove (pview->hide_cursor_timeout_id);
879 pview->hide_cursor_timeout_id = 0;
883 ev_view_presentation_hide_cursor_timeout_start (EvViewPresentation *pview)
885 ev_view_presentation_hide_cursor_timeout_stop (pview);
886 pview->hide_cursor_timeout_id =
887 g_timeout_add_seconds (HIDE_CURSOR_TIMEOUT,
888 (GSourceFunc)hide_cursor_timeout_cb,
893 ev_view_presentation_update_current_surface (EvViewPresentation *pview,
894 cairo_surface_t *surface)
896 if (!surface || pview->current_surface == surface)
899 cairo_surface_reference (surface);
900 if (pview->current_surface)
901 cairo_surface_destroy (pview->current_surface);
902 pview->current_surface = surface;
906 ev_view_presentation_destroy (GtkObject *object)
908 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
910 if (pview->document) {
911 g_object_unref (pview->document);
912 pview->document = NULL;
915 ev_view_presentation_animation_cancel (pview);
916 ev_view_presentation_transition_stop (pview);
917 ev_view_presentation_hide_cursor_timeout_stop (pview);
919 if (pview->curr_job) {
920 ev_view_presentation_delete_job (pview, pview->curr_job);
921 pview->curr_job = NULL;
924 if (pview->prev_job) {
925 ev_view_presentation_delete_job (pview, pview->prev_job);
926 pview->prev_job = NULL;
929 if (pview->next_job) {
930 ev_view_presentation_delete_job (pview, pview->next_job);
931 pview->next_job = NULL;
934 if (pview->current_surface) {
935 cairo_surface_destroy (pview->current_surface);
936 pview->current_surface = NULL;
939 if (pview->page_cache) {
940 g_object_unref (pview->page_cache);
941 pview->page_cache = NULL;
944 if (pview->goto_window) {
945 gtk_widget_destroy (pview->goto_window);
946 pview->goto_window = NULL;
947 pview->goto_entry = NULL;
950 GTK_OBJECT_CLASS (ev_view_presentation_parent_class)->destroy (object);
954 ev_view_presentation_size_request (GtkWidget *widget,
955 GtkRequisition *requisition)
957 requisition->width = 0;
958 requisition->height = 0;
962 ev_view_presentation_draw_end_page (EvViewPresentation *pview)
964 GtkWidget *widget = GTK_WIDGET (pview);
966 PangoFontDescription *font_desc;
968 GtkAllocation allocation;
969 GdkRectangle area = {0};
970 const gchar *text = _("End of presentation. Click to exit.");
972 if (pview->state != EV_PRESENTATION_END)
975 layout = gtk_widget_create_pango_layout (widget, NULL);
976 markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", text);
977 pango_layout_set_markup (layout, markup, -1);
980 font_desc = pango_font_description_new ();
981 pango_font_description_set_size (font_desc, 16 * PANGO_SCALE);
982 pango_layout_set_font_description (layout, font_desc);
984 gtk_widget_get_allocation (widget, &allocation);
985 area.width = allocation.width;
986 area.height = allocation.height;
988 gtk_paint_layout (gtk_widget_get_style (widget),
989 gtk_widget_get_window (widget),
990 gtk_widget_get_state (widget),
999 pango_font_description_free (font_desc);
1000 g_object_unref (layout);
1004 ev_view_presentation_expose_event (GtkWidget *widget,
1005 GdkEventExpose *event)
1007 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1008 GdkRectangle page_area;
1009 GdkRectangle overlap;
1010 cairo_surface_t *surface;
1013 switch (pview->state) {
1014 case EV_PRESENTATION_END:
1015 ev_view_presentation_draw_end_page (pview);
1017 case EV_PRESENTATION_BLACK:
1018 case EV_PRESENTATION_WHITE:
1020 case EV_PRESENTATION_NORMAL:
1024 if (pview->animation) {
1025 if (ev_transition_animation_ready (pview->animation)) {
1026 ev_view_presentation_get_page_area (pview, &page_area);
1028 cr = gdk_cairo_create (gtk_widget_get_window (widget));
1030 /* normalize to x=0, y=0 */
1031 cairo_translate (cr, page_area.x, page_area.y);
1032 page_area.x = page_area.y = 0;
1034 /* Try to fix rounding errors */
1037 ev_transition_animation_paint (pview->animation, cr, page_area);
1044 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
1046 ev_view_presentation_update_current_surface (pview, surface);
1047 } else if (pview->current_surface) {
1048 surface = pview->current_surface;
1053 ev_view_presentation_get_page_area (pview, &page_area);
1054 if (gdk_rectangle_intersect (&page_area, &(event->area), &overlap)) {
1055 cr = gdk_cairo_create (gtk_widget_get_window (widget));
1057 /* Try to fix rounding errors. See bug #438760 */
1058 if (overlap.width == page_area.width)
1061 cairo_rectangle (cr, overlap.x, overlap.y, overlap.width, overlap.height);
1062 cairo_set_source_surface (cr, surface, page_area.x, page_area.y);
1071 ev_view_presentation_key_press_event (GtkWidget *widget,
1074 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1076 if (pview->state == EV_PRESENTATION_END)
1077 return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
1079 switch (event->keyval) {
1083 case GDK_KP_Decimal:
1084 if (pview->state == EV_PRESENTATION_BLACK)
1085 ev_view_presentation_set_normal (pview);
1087 ev_view_presentation_set_black (pview);
1092 if (pview->state == EV_PRESENTATION_WHITE)
1093 ev_view_presentation_set_normal (pview);
1095 ev_view_presentation_set_white (pview);
1099 if (pview->state == EV_PRESENTATION_NORMAL) {
1100 ev_view_presentation_update_current_page (pview, 0);
1105 if (pview->state == EV_PRESENTATION_NORMAL) {
1108 page = ev_document_get_n_pages (pview->document) - 1;
1109 ev_view_presentation_update_current_page (pview, page);
1118 ev_view_presentation_set_normal (pview);
1120 if (ev_document_get_n_pages (pview->document) > 1 && KEY_IS_NUMERIC (event->keyval)) {
1123 ev_view_presentation_goto_window_create (pview);
1124 ev_view_presentation_goto_window_send_key_event (pview, (GdkEvent *)event);
1125 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
1126 gtk_window_move (GTK_WINDOW (pview->goto_window), x, y);
1127 gtk_widget_show (pview->goto_window);
1128 ev_view_presentation_goto_entry_grab_focus (pview);
1133 return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
1137 ev_view_presentation_button_release_event (GtkWidget *widget,
1138 GdkEventButton *event)
1140 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1142 switch (event->button) {
1146 if (pview->state == EV_PRESENTATION_END) {
1147 g_signal_emit (pview, signals[FINISHED], 0, NULL);
1152 link = ev_view_presentation_get_link_at_location (pview,
1156 ev_vew_presentation_goto_link_dest (pview, link);
1158 ev_view_presentation_next_page (pview);
1162 ev_view_presentation_previous_page (pview);
1172 ev_view_presentation_focus_out (GtkWidget *widget,
1173 GdkEventFocus *event)
1175 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1177 if (pview->goto_window)
1178 ev_view_presentation_goto_window_hide (pview);
1184 ev_view_presentation_motion_notify_event (GtkWidget *widget,
1185 GdkEventMotion *event)
1187 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1189 ev_view_presentation_hide_cursor_timeout_start (pview);
1190 ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
1196 init_presentation (GtkWidget *widget)
1198 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1199 GdkScreen *screen = gtk_widget_get_screen (widget);
1200 GdkRectangle monitor;
1203 monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget));
1204 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1205 pview->monitor_width = monitor.width;
1206 pview->monitor_height = monitor.height;
1208 ev_view_presentation_update_current_page (pview, pview->current_page);
1209 ev_view_presentation_hide_cursor_timeout_start (pview);
1215 ev_view_presentation_realize (GtkWidget *widget)
1219 GdkWindowAttr attributes;
1220 GtkAllocation allocation;
1222 gtk_widget_set_realized (widget, TRUE);
1224 attributes.window_type = GDK_WINDOW_CHILD;
1225 attributes.wclass = GDK_INPUT_OUTPUT;
1226 attributes.visual = gtk_widget_get_visual (widget);
1227 attributes.colormap = gtk_widget_get_colormap (widget);
1229 gtk_widget_get_allocation (widget, &allocation);
1230 attributes.x = allocation.x;
1231 attributes.y = allocation.y;
1232 attributes.width = allocation.width;
1233 attributes.height = allocation.height;
1234 attributes.event_mask = GDK_EXPOSURE_MASK |
1235 GDK_BUTTON_PRESS_MASK |
1236 GDK_BUTTON_RELEASE_MASK |
1238 GDK_KEY_PRESS_MASK |
1239 GDK_POINTER_MOTION_MASK |
1240 GDK_POINTER_MOTION_HINT_MASK |
1241 GDK_ENTER_NOTIFY_MASK |
1242 GDK_LEAVE_NOTIFY_MASK;
1244 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1246 GDK_WA_X | GDK_WA_Y |
1250 gdk_window_set_user_data (window, widget);
1251 gtk_widget_set_window (widget, window);
1253 gtk_widget_style_attach (widget);
1254 style = gtk_widget_get_style (widget);
1255 gdk_window_set_background (window, &style->black);
1257 g_idle_add ((GSourceFunc)init_presentation, widget);
1261 ev_view_presentation_change_page (EvViewPresentation *pview,
1262 GtkScrollType scroll)
1265 case GTK_SCROLL_PAGE_FORWARD:
1266 ev_view_presentation_next_page (pview);
1268 case GTK_SCROLL_PAGE_BACKWARD:
1269 ev_view_presentation_previous_page (pview);
1272 g_assert_not_reached ();
1277 ev_view_presentation_scroll_event (GtkWidget *widget,
1278 GdkEventScroll *event)
1280 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1283 state = event->state & gtk_accelerator_get_default_mod_mask ();
1287 switch (event->direction) {
1288 case GDK_SCROLL_DOWN:
1289 case GDK_SCROLL_RIGHT:
1290 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
1293 case GDK_SCROLL_LEFT:
1294 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1303 add_change_page_binding_keypad (GtkBindingSet *binding_set,
1305 GdkModifierType modifiers,
1306 GtkScrollType scroll)
1308 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
1310 gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1312 GTK_TYPE_SCROLL_TYPE, scroll);
1313 gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1315 GTK_TYPE_SCROLL_TYPE, scroll);
1319 ev_view_presentation_set_property (GObject *object,
1321 const GValue *value,
1324 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1328 pview->document = g_value_dup_object (value);
1329 pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
1331 case PROP_CURRENT_PAGE:
1332 pview->current_page = g_value_get_uint (value);
1335 pview->rotation = g_value_get_uint (value);
1337 case PROP_INVERTED_COLORS:
1338 pview->inverted_colors = g_value_get_boolean (value);
1341 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1346 ev_view_presentation_constructor (GType type,
1347 guint n_construct_properties,
1348 GObjectConstructParam *construct_params)
1351 EvViewPresentation *pview;
1353 object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1354 n_construct_properties,
1356 pview = EV_VIEW_PRESENTATION (object);
1358 if (EV_IS_DOCUMENT_LINKS (pview->document)) {
1359 pview->page_cache = ev_page_cache_new (pview->document);
1360 ev_page_cache_set_flags (pview->page_cache, EV_PAGE_DATA_INCLUDE_LINKS);
1367 ev_view_presentation_class_init (EvViewPresentationClass *klass)
1369 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1370 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1371 GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (klass);
1372 GtkBindingSet *binding_set;
1374 klass->change_page = ev_view_presentation_change_page;
1376 widget_class->size_request = ev_view_presentation_size_request;
1377 widget_class->realize = ev_view_presentation_realize;
1378 widget_class->expose_event = ev_view_presentation_expose_event;
1379 widget_class->key_press_event = ev_view_presentation_key_press_event;
1380 widget_class->button_release_event = ev_view_presentation_button_release_event;
1381 widget_class->focus_out_event = ev_view_presentation_focus_out;
1382 widget_class->motion_notify_event = ev_view_presentation_motion_notify_event;
1383 widget_class->scroll_event = ev_view_presentation_scroll_event;
1385 gtk_object_class->destroy = ev_view_presentation_destroy;
1387 gobject_class->constructor = ev_view_presentation_constructor;
1388 gobject_class->set_property = ev_view_presentation_set_property;
1390 g_object_class_install_property (gobject_class,
1392 g_param_spec_object ("document",
1397 G_PARAM_CONSTRUCT_ONLY));
1398 g_object_class_install_property (gobject_class,
1400 g_param_spec_uint ("current_page",
1405 G_PARAM_CONSTRUCT_ONLY));
1406 g_object_class_install_property (gobject_class,
1408 g_param_spec_uint ("rotation",
1410 "Current rotation angle",
1413 G_PARAM_CONSTRUCT_ONLY));
1414 g_object_class_install_property (gobject_class,
1415 PROP_INVERTED_COLORS,
1416 g_param_spec_boolean ("inverted_colors",
1418 "Whether presentation is displayed with inverted colors",
1421 G_PARAM_CONSTRUCT_ONLY));
1423 signals[CHANGE_PAGE] =
1424 g_signal_new ("change_page",
1425 G_OBJECT_CLASS_TYPE (gobject_class),
1426 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1427 G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
1429 g_cclosure_marshal_VOID__ENUM,
1431 GTK_TYPE_SCROLL_TYPE);
1433 g_signal_new ("finished",
1434 G_OBJECT_CLASS_TYPE (gobject_class),
1435 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1436 G_STRUCT_OFFSET (EvViewPresentationClass, finished),
1438 g_cclosure_marshal_VOID__VOID,
1442 binding_set = gtk_binding_set_by_class (klass);
1443 add_change_page_binding_keypad (binding_set, GDK_Left, 0, GTK_SCROLL_PAGE_BACKWARD);
1444 add_change_page_binding_keypad (binding_set, GDK_Right, 0, GTK_SCROLL_PAGE_FORWARD);
1445 add_change_page_binding_keypad (binding_set, GDK_Up, 0, GTK_SCROLL_PAGE_BACKWARD);
1446 add_change_page_binding_keypad (binding_set, GDK_Down, 0, GTK_SCROLL_PAGE_FORWARD);
1447 gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
1449 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1450 gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
1452 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1453 gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
1455 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1456 gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
1458 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1459 gtk_binding_entry_add_signal (binding_set, GDK_J, 0,
1461 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1462 gtk_binding_entry_add_signal (binding_set, GDK_H, 0,
1464 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1465 gtk_binding_entry_add_signal (binding_set, GDK_L, 0,
1467 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1468 gtk_binding_entry_add_signal (binding_set, GDK_K, 0,
1470 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1474 ev_view_presentation_init (EvViewPresentation *pview)
1476 gtk_widget_set_can_focus (GTK_WIDGET (pview), TRUE);
1480 ev_view_presentation_new (EvDocument *document,
1483 gboolean inverted_colors)
1485 g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
1486 g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
1488 return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
1489 "document", document,
1490 "current_page", current_page,
1491 "rotation", rotation,
1492 "inverted_colors", inverted_colors,
1497 ev_view_presentation_get_current_page (EvViewPresentation *pview)
1499 return pview->current_page;