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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, 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"
49 EV_PRESENTATION_NORMAL,
50 EV_PRESENTATION_BLACK,
51 EV_PRESENTATION_WHITE,
53 } EvPresentationState;
55 struct _EvViewPresentation
60 cairo_surface_t *current_surface;
63 EvPresentationState state;
69 guint hide_cursor_timeout_id;
72 GtkWidget *goto_window;
73 GtkWidget *goto_entry;
76 guint trans_timeout_id;
79 gboolean enable_animations;
80 EvTransitionAnimation *animation;
83 EvPageCache *page_cache;
90 struct _EvViewPresentationClass
92 GtkWidgetClass base_class;
95 void (* change_page) (EvViewPresentation *pview,
96 GtkScrollType scroll);
97 void (* finished) (EvViewPresentation *pview);
100 static guint signals[N_SIGNALS] = { 0 };
102 static void ev_view_presentation_next_page (EvViewPresentation *pview);
103 static void ev_view_presentation_previous_page (EvViewPresentation *pview);
104 static void ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
108 #define HIDE_CURSOR_TIMEOUT 5
110 G_DEFINE_TYPE (EvViewPresentation, ev_view_presentation, GTK_TYPE_WIDGET)
113 ev_view_presentation_set_normal (EvViewPresentation *pview)
115 GtkWidget *widget = GTK_WIDGET (pview);
117 if (pview->state == EV_PRESENTATION_NORMAL)
120 pview->state = EV_PRESENTATION_NORMAL;
121 gdk_window_set_background (widget->window, &widget->style->black);
122 gtk_widget_queue_draw (widget);
126 ev_view_presentation_set_black (EvViewPresentation *pview)
128 GtkWidget *widget = GTK_WIDGET (pview);
130 if (pview->state == EV_PRESENTATION_BLACK)
133 pview->state = EV_PRESENTATION_BLACK;
134 gdk_window_set_background (widget->window, &widget->style->black);
135 gtk_widget_queue_draw (widget);
139 ev_view_presentation_set_white (EvViewPresentation *pview)
141 GtkWidget *widget = GTK_WIDGET (pview);
143 if (pview->state == EV_PRESENTATION_WHITE)
146 pview->state = EV_PRESENTATION_WHITE;
147 gdk_window_set_background (widget->window, &widget->style->white);
148 gtk_widget_queue_draw (widget);
152 ev_view_presentation_set_end (EvViewPresentation *pview)
154 GtkWidget *widget = GTK_WIDGET (pview);
156 if (pview->state == EV_PRESENTATION_END)
159 pview->state = EV_PRESENTATION_END;
160 gtk_widget_queue_draw (widget);
164 ev_view_presentation_get_scale_for_page (EvViewPresentation *pview,
167 gdouble width, height;
169 ev_document_get_page_size (pview->document, page, &width, &height);
171 if (pview->rotation == 90 || pview->rotation == 270)
172 return pview->monitor_height / width;
174 return pview->monitor_height / height;
178 ev_view_presentation_update_scale (EvViewPresentation *pview)
180 if (ev_document_is_page_size_uniform (pview->document) && pview->scale != 0)
183 pview->scale = ev_view_presentation_get_scale_for_page (pview, pview->current_page);
187 ev_view_presentation_get_page_area (EvViewPresentation *pview,
190 GtkWidget *widget = GTK_WIDGET (pview);
191 gdouble doc_width, doc_height;
192 gint view_width, view_height;
194 ev_document_get_page_size (pview->document,
196 &doc_width, &doc_height);
198 if (pview->rotation == 90 || pview->rotation == 270) {
199 view_width = (gint)((doc_height * pview->scale) + 0.5);
200 view_height = (gint)((doc_width * pview->scale) + 0.5);
202 view_width = (gint)((doc_width * pview->scale) + 0.5);
203 view_height = (gint)((doc_height * pview->scale) + 0.5);
206 area->x = (MAX (0, widget->allocation.width - view_width)) / 2;
207 area->y = (MAX (0, widget->allocation.height - view_height)) / 2;
208 area->width = view_width;
209 area->height = view_height;
212 /* Page Transition */
214 transition_next_page (EvViewPresentation *pview)
216 ev_view_presentation_next_page (pview);
222 ev_view_presentation_transition_stop (EvViewPresentation *pview)
224 if (pview->trans_timeout_id > 0)
225 g_source_remove (pview->trans_timeout_id);
226 pview->trans_timeout_id = 0;
230 ev_view_presentation_transition_start (EvViewPresentation *pview)
234 if (!EV_IS_DOCUMENT_TRANSITION (pview->document))
237 ev_view_presentation_transition_stop (pview);
239 duration = ev_document_transition_get_page_duration (EV_DOCUMENT_TRANSITION (pview->document),
240 pview->current_page);
242 pview->trans_timeout_id =
243 g_timeout_add_seconds (duration,
244 (GSourceFunc) transition_next_page,
251 ev_view_presentation_animation_cancel (EvViewPresentation *pview)
253 if (pview->animation) {
254 g_object_unref (pview->animation);
255 pview->animation = NULL;
260 ev_view_presentation_transition_animation_finish (EvViewPresentation *pview)
262 ev_view_presentation_animation_cancel (pview);
263 ev_view_presentation_transition_start (pview);
264 gtk_widget_queue_draw (GTK_WIDGET (pview));
268 ev_view_presentation_transition_animation_frame (EvViewPresentation *pview,
271 gtk_widget_queue_draw (GTK_WIDGET (pview));
275 ev_view_presentation_animation_start (EvViewPresentation *pview,
278 EvTransitionEffect *effect = NULL;
279 cairo_surface_t *surface;
282 if (!pview->enable_animations)
285 if (pview->current_page == new_page)
288 effect = ev_document_transition_get_effect (EV_DOCUMENT_TRANSITION (pview->document),
293 pview->animation = ev_transition_animation_new (effect);
295 surface = EV_JOB_RENDER (pview->curr_job)->surface;
296 ev_transition_animation_set_origin_surface (pview->animation,
298 surface : pview->current_surface);
300 jump = new_page - pview->current_page;
302 surface = EV_JOB_RENDER (pview->prev_job)->surface;
304 surface = EV_JOB_RENDER (pview->next_job)->surface;
308 ev_transition_animation_set_dest_surface (pview->animation, surface);
310 g_signal_connect_swapped (pview->animation, "frame",
311 G_CALLBACK (ev_view_presentation_transition_animation_frame),
313 g_signal_connect_swapped (pview->animation, "finished",
314 G_CALLBACK (ev_view_presentation_transition_animation_finish),
318 /* Page Navigation */
320 job_finished_cb (EvJob *job,
321 EvViewPresentation *pview)
323 EvJobRender *job_render = EV_JOB_RENDER (job);
325 if (job != pview->curr_job)
328 if (pview->animation) {
329 ev_transition_animation_set_dest_surface (pview->animation,
330 job_render->surface);
332 ev_view_presentation_transition_start (pview);
333 gtk_widget_queue_draw (GTK_WIDGET (pview));
338 ev_view_presentation_schedule_new_job (EvViewPresentation *pview,
340 EvJobPriority priority)
345 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
348 if (ev_document_is_page_size_uniform (pview->document))
349 scale = pview->scale;
351 scale = ev_view_presentation_get_scale_for_page (pview, page);
352 job = ev_job_render_new (pview->document, page, pview->rotation, pview->scale, 0, 0);
353 g_signal_connect (job, "finished",
354 G_CALLBACK (job_finished_cb),
356 ev_job_scheduler_push_job (job, priority);
362 ev_view_presentation_delete_job (EvViewPresentation *pview,
368 g_signal_handlers_disconnect_by_func (job, job_finished_cb, pview);
370 g_object_unref (job);
374 ev_view_presentation_update_current_page (EvViewPresentation *pview,
379 if (page < 0 || page >= ev_document_get_n_pages (pview->document))
382 ev_view_presentation_animation_cancel (pview);
383 ev_view_presentation_animation_start (pview, page);
385 jump = page - pview->current_page;
389 if (!pview->curr_job)
390 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
391 if (!pview->next_job)
392 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
393 if (!pview->prev_job)
394 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
397 ev_view_presentation_delete_job (pview, pview->next_job);
398 pview->next_job = pview->curr_job;
399 pview->curr_job = pview->prev_job;
401 if (!pview->curr_job)
402 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
404 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
405 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
406 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
410 ev_view_presentation_delete_job (pview, pview->prev_job);
411 pview->prev_job = pview->curr_job;
412 pview->curr_job = pview->next_job;
414 if (!pview->curr_job)
415 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
417 ev_job_scheduler_update_job (pview->curr_job, EV_JOB_PRIORITY_URGENT);
418 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
419 ev_job_scheduler_update_job (pview->prev_job, EV_JOB_PRIORITY_LOW);
423 ev_view_presentation_delete_job (pview, pview->next_job);
424 ev_view_presentation_delete_job (pview, pview->curr_job);
425 pview->next_job = pview->prev_job;
427 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
428 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
429 if (!pview->next_job)
430 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
432 ev_job_scheduler_update_job (pview->next_job, EV_JOB_PRIORITY_LOW);
435 ev_view_presentation_delete_job (pview, pview->prev_job);
436 ev_view_presentation_delete_job (pview, pview->curr_job);
437 pview->prev_job = pview->next_job;
439 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
440 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
441 if (!pview->prev_job)
442 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
444 ev_job_scheduler_update_job (pview->prev_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 ev_view_presentation_delete_job (pview, pview->next_job);
451 pview->curr_job = ev_view_presentation_schedule_new_job (pview, page, EV_JOB_PRIORITY_URGENT);
453 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_HIGH);
454 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_LOW);
456 pview->prev_job = ev_view_presentation_schedule_new_job (pview, page - 1, EV_JOB_PRIORITY_HIGH);
457 pview->next_job = ev_view_presentation_schedule_new_job (pview, page + 1, EV_JOB_PRIORITY_LOW);
461 pview->current_page = page;
462 ev_view_presentation_update_scale (pview);
463 if (pview->page_cache)
464 ev_page_cache_set_page_range (pview->page_cache, page, page);
466 if (pview->cursor != EV_VIEW_CURSOR_HIDDEN) {
469 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
470 ev_view_presentation_set_cursor_for_location (pview, x, y);
473 if (EV_JOB_RENDER (pview->curr_job)->surface)
474 gtk_widget_queue_draw (GTK_WIDGET (pview));
478 ev_view_presentation_next_page (EvViewPresentation *pview)
483 switch (pview->state) {
484 case EV_PRESENTATION_BLACK:
485 case EV_PRESENTATION_WHITE:
486 ev_view_presentation_set_normal (pview);
487 case EV_PRESENTATION_END:
489 case EV_PRESENTATION_NORMAL:
493 n_pages = ev_document_get_n_pages (pview->document);
494 new_page = pview->current_page + 1;
496 if (new_page == n_pages)
497 ev_view_presentation_set_end (pview);
499 ev_view_presentation_update_current_page (pview, new_page);
503 ev_view_presentation_previous_page (EvViewPresentation *pview)
507 switch (pview->state) {
508 case EV_PRESENTATION_BLACK:
509 case EV_PRESENTATION_WHITE:
510 ev_view_presentation_set_normal (pview);
512 case EV_PRESENTATION_END:
513 pview->state = EV_PRESENTATION_NORMAL;
514 new_page = pview->current_page;
516 case EV_PRESENTATION_NORMAL:
517 new_page = pview->current_page - 1;
521 ev_view_presentation_update_current_page (pview, new_page);
525 #define KEY_IS_NUMERIC(keyval) \
526 ((keyval >= GDK_0 && keyval <= GDK_9) || (keyval >= GDK_KP_0 && keyval <= GDK_KP_9))
528 /* Cut and paste from gtkwindow.c */
530 send_focus_change (GtkWidget *widget,
533 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
535 g_object_ref (widget);
538 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
540 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
542 fevent->focus_change.type = GDK_FOCUS_CHANGE;
543 fevent->focus_change.window = g_object_ref (widget->window);
544 fevent->focus_change.in = in;
546 gtk_widget_event (widget, fevent);
548 g_object_notify (G_OBJECT (widget), "has-focus");
550 g_object_unref (widget);
551 gdk_event_free (fevent);
555 ev_view_presentation_goto_window_hide (EvViewPresentation *pview)
557 /* send focus-in event */
558 send_focus_change (pview->goto_entry, FALSE);
559 gtk_widget_hide (pview->goto_window);
560 gtk_entry_set_text (GTK_ENTRY (pview->goto_entry), "");
564 ev_view_presentation_goto_window_delete_event (GtkWidget *widget,
566 EvViewPresentation *pview)
568 ev_view_presentation_goto_window_hide (pview);
574 ev_view_presentation_goto_window_key_press_event (GtkWidget *widget,
576 EvViewPresentation *pview)
578 switch (event->keyval) {
582 case GDK_ISO_Left_Tab:
583 ev_view_presentation_goto_window_hide (pview);
592 if (!KEY_IS_NUMERIC (event->keyval))
600 ev_view_presentation_goto_window_button_press_event (GtkWidget *widget,
601 GdkEventButton *event,
602 EvViewPresentation *pview)
604 ev_view_presentation_goto_window_hide (pview);
610 ev_view_presentation_goto_entry_activate (GtkEntry *entry,
611 EvViewPresentation *pview)
616 text = gtk_entry_get_text (entry);
617 page = atoi (text) - 1;
619 ev_view_presentation_goto_window_hide (pview);
620 ev_view_presentation_update_current_page (pview, page);
624 ev_view_presentation_goto_window_create (EvViewPresentation *pview)
626 GtkWidget *frame, *hbox, *toplevel, *label;
628 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (pview));
630 if (pview->goto_window) {
631 if (GTK_WINDOW (toplevel)->group)
632 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
633 GTK_WINDOW (pview->goto_window));
634 else if (GTK_WINDOW (pview->goto_window)->group)
635 gtk_window_group_remove_window (GTK_WINDOW (pview->goto_window)->group,
636 GTK_WINDOW (pview->goto_window));
640 pview->goto_window = gtk_window_new (GTK_WINDOW_POPUP);
641 gtk_window_set_screen (GTK_WINDOW (pview->goto_window),
642 gtk_widget_get_screen (GTK_WIDGET (pview)));
644 if (GTK_WINDOW (toplevel)->group)
645 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
646 GTK_WINDOW (pview->goto_window));
648 gtk_window_set_modal (GTK_WINDOW (pview->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 = g_object_ref (pview->goto_window->window);
712 gtk_widget_realize (pview->goto_window);
714 gtk_widget_event (pview->goto_window, (GdkEvent *)new_event);
715 gdk_event_free ((GdkEvent *)new_event);
716 gtk_widget_hide (pview->goto_window);
721 ev_view_presentation_link_is_supported (EvViewPresentation *pview,
724 EvLinkAction *action;
726 action = ev_link_get_action (link);
730 switch (ev_link_action_get_action_type (action)) {
731 case EV_LINK_ACTION_TYPE_GOTO_DEST:
732 return ev_link_action_get_dest (action) != NULL;
733 case EV_LINK_ACTION_TYPE_NAMED:
743 ev_view_presentation_get_link_at_location (EvViewPresentation *pview,
747 GdkRectangle page_area;
750 gdouble width, height;
751 gdouble new_x, new_y;
753 if (!pview->page_cache)
756 ev_document_get_page_size (pview->document, pview->current_page, &width, &height);
757 ev_view_presentation_get_page_area (pview, &page_area);
758 x = (x - page_area.x) / pview->scale;
759 y = (y - page_area.y) / pview->scale;
760 switch (pview->rotation) {
779 g_assert_not_reached ();
782 link_mapping = ev_page_cache_get_link_mapping (pview->page_cache, pview->current_page);
784 link = link_mapping ? ev_mapping_list_get_data (link_mapping, new_x, new_y) : NULL;
786 return link && ev_view_presentation_link_is_supported (pview, link) ? link : NULL;
790 ev_vew_presentation_goto_link_dest (EvViewPresentation *pview,
793 EvLinkAction *action;
795 action = ev_link_get_action (link);
797 if (ev_link_action_get_action_type (action) == EV_LINK_ACTION_TYPE_NAMED) {
798 const gchar *name = ev_link_action_get_name (action);
800 if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
801 ev_view_presentation_update_current_page (pview, 0);
802 } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
803 ev_view_presentation_update_current_page (pview, pview->current_page - 1);
804 } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
805 ev_view_presentation_update_current_page (pview, pview->current_page + 1);
806 } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
809 n_pages = ev_document_get_n_pages (pview->document);
810 ev_view_presentation_update_current_page (pview, n_pages - 1);
816 dest = ev_link_action_get_dest (action);
817 page = ev_document_links_get_dest_page (EV_DOCUMENT_LINKS (pview->document), dest);
818 ev_view_presentation_update_current_page (pview, page);
824 ev_view_presentation_set_cursor (EvViewPresentation *pview,
825 EvViewCursor view_cursor)
830 if (pview->cursor == view_cursor)
833 widget = GTK_WIDGET (pview);
834 if (!GTK_WIDGET_REALIZED (widget))
835 gtk_widget_realize (widget);
837 pview->cursor = view_cursor;
839 cursor = ev_view_cursor_new (gtk_widget_get_display (widget), view_cursor);
840 gdk_window_set_cursor (widget->window, cursor);
843 gdk_cursor_unref (cursor);
847 ev_view_presentation_set_cursor_for_location (EvViewPresentation *pview,
851 if (ev_view_presentation_get_link_at_location (pview, x, y))
852 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_LINK);
854 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_NORMAL);
858 hide_cursor_timeout_cb (EvViewPresentation *pview)
860 ev_view_presentation_set_cursor (pview, EV_VIEW_CURSOR_HIDDEN);
861 pview->hide_cursor_timeout_id = 0;
867 ev_view_presentation_hide_cursor_timeout_stop (EvViewPresentation *pview)
869 if (pview->hide_cursor_timeout_id > 0)
870 g_source_remove (pview->hide_cursor_timeout_id);
871 pview->hide_cursor_timeout_id = 0;
875 ev_view_presentation_hide_cursor_timeout_start (EvViewPresentation *pview)
877 ev_view_presentation_hide_cursor_timeout_stop (pview);
878 pview->hide_cursor_timeout_id =
879 g_timeout_add_seconds (HIDE_CURSOR_TIMEOUT,
880 (GSourceFunc)hide_cursor_timeout_cb,
885 ev_view_presentation_update_current_surface (EvViewPresentation *pview,
886 cairo_surface_t *surface)
888 if (!surface || pview->current_surface == surface)
891 cairo_surface_reference (surface);
892 if (pview->current_surface)
893 cairo_surface_destroy (pview->current_surface);
894 pview->current_surface = surface;
898 ev_view_presentation_destroy (GtkObject *object)
900 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
902 if (pview->document) {
903 g_object_unref (pview->document);
904 pview->document = NULL;
907 ev_view_presentation_animation_cancel (pview);
908 ev_view_presentation_transition_stop (pview);
909 ev_view_presentation_hide_cursor_timeout_stop (pview);
911 if (pview->curr_job) {
912 ev_view_presentation_delete_job (pview, pview->curr_job);
913 pview->curr_job = NULL;
916 if (pview->prev_job) {
917 ev_view_presentation_delete_job (pview, pview->prev_job);
918 pview->prev_job = NULL;
921 if (pview->next_job) {
922 ev_view_presentation_delete_job (pview, pview->next_job);
923 pview->next_job = NULL;
926 if (pview->current_surface) {
927 cairo_surface_destroy (pview->current_surface);
928 pview->current_surface = NULL;
931 if (pview->page_cache) {
932 g_object_unref (pview->page_cache);
933 pview->page_cache = NULL;
936 if (pview->goto_window) {
937 gtk_widget_destroy (pview->goto_window);
938 pview->goto_window = NULL;
939 pview->goto_entry = NULL;
942 GTK_OBJECT_CLASS (ev_view_presentation_parent_class)->destroy (object);
946 ev_view_presentation_size_request (GtkWidget *widget,
947 GtkRequisition *requisition)
949 requisition->width = 0;
950 requisition->height = 0;
954 ev_view_presentation_size_allocate (GtkWidget *widget,
955 GtkAllocation *allocation)
957 GTK_WIDGET_CLASS (ev_view_presentation_parent_class)->size_allocate (widget, allocation);
959 widget->allocation = *allocation;
963 ev_view_presentation_draw_end_page (EvViewPresentation *pview)
965 GtkWidget *widget = GTK_WIDGET (pview);
967 PangoFontDescription *font_desc;
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 area.width = widget->allocation.width;
985 area.height = widget->allocation.height;
987 gtk_paint_layout (widget->style,
989 GTK_WIDGET_STATE (widget),
998 pango_font_description_free (font_desc);
999 g_object_unref (layout);
1003 ev_view_presentation_expose_event (GtkWidget *widget,
1004 GdkEventExpose *event)
1006 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1007 GdkRectangle page_area;
1008 GdkRectangle overlap;
1009 cairo_surface_t *surface;
1012 switch (pview->state) {
1013 case EV_PRESENTATION_END:
1014 ev_view_presentation_draw_end_page (pview);
1016 case EV_PRESENTATION_BLACK:
1017 case EV_PRESENTATION_WHITE:
1019 case EV_PRESENTATION_NORMAL:
1023 if (pview->animation) {
1024 if (ev_transition_animation_ready (pview->animation)) {
1025 ev_view_presentation_get_page_area (pview, &page_area);
1027 cr = gdk_cairo_create (widget->window);
1029 /* normalize to x=0, y=0 */
1030 cairo_translate (cr, page_area.x, page_area.y);
1031 page_area.x = page_area.y = 0;
1033 /* Try to fix rounding errors */
1036 ev_transition_animation_paint (pview->animation, cr, page_area);
1043 surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
1045 ev_view_presentation_update_current_surface (pview, surface);
1046 } else if (pview->current_surface) {
1047 surface = pview->current_surface;
1052 ev_view_presentation_get_page_area (pview, &page_area);
1053 if (gdk_rectangle_intersect (&page_area, &(event->area), &overlap)) {
1054 cr = gdk_cairo_create (widget->window);
1056 /* Try to fix rounding errors. See bug #438760 */
1057 if (overlap.width == page_area.width)
1060 cairo_rectangle (cr, overlap.x, overlap.y, overlap.width, overlap.height);
1061 cairo_set_source_surface (cr, surface, page_area.x, page_area.y);
1070 ev_view_presentation_key_press_event (GtkWidget *widget,
1073 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1075 if (pview->state == EV_PRESENTATION_END)
1076 return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
1078 switch (event->keyval) {
1082 case GDK_KP_Decimal:
1083 if (pview->state == EV_PRESENTATION_BLACK)
1084 ev_view_presentation_set_normal (pview);
1086 ev_view_presentation_set_black (pview);
1091 if (pview->state == EV_PRESENTATION_WHITE)
1092 ev_view_presentation_set_normal (pview);
1094 ev_view_presentation_set_white (pview);
1098 if (pview->state == EV_PRESENTATION_NORMAL) {
1099 ev_view_presentation_update_current_page (pview, 0);
1104 if (pview->state == EV_PRESENTATION_NORMAL) {
1107 page = ev_document_get_n_pages (pview->document) - 1;
1108 ev_view_presentation_update_current_page (pview, page);
1117 ev_view_presentation_set_normal (pview);
1119 if (ev_document_get_n_pages (pview->document) > 1 && KEY_IS_NUMERIC (event->keyval)) {
1122 ev_view_presentation_goto_window_create (pview);
1123 ev_view_presentation_goto_window_send_key_event (pview, (GdkEvent *)event);
1124 gtk_widget_get_pointer (GTK_WIDGET (pview), &x, &y);
1125 gtk_window_move (GTK_WINDOW (pview->goto_window), x, y);
1126 gtk_widget_show (pview->goto_window);
1127 ev_view_presentation_goto_entry_grab_focus (pview);
1132 return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
1136 ev_view_presentation_button_release_event (GtkWidget *widget,
1137 GdkEventButton *event)
1139 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1141 switch (event->button) {
1145 if (pview->state == EV_PRESENTATION_END) {
1146 g_signal_emit (pview, signals[FINISHED], 0, NULL);
1151 link = ev_view_presentation_get_link_at_location (pview,
1155 ev_vew_presentation_goto_link_dest (pview, link);
1157 ev_view_presentation_next_page (pview);
1161 ev_view_presentation_previous_page (pview);
1171 ev_view_presentation_focus_out (GtkWidget *widget,
1172 GdkEventFocus *event)
1174 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1176 if (pview->goto_window)
1177 ev_view_presentation_goto_window_hide (pview);
1183 ev_view_presentation_motion_notify_event (GtkWidget *widget,
1184 GdkEventMotion *event)
1186 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1188 ev_view_presentation_hide_cursor_timeout_start (pview);
1189 ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
1195 init_presentation (GtkWidget *widget)
1197 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1198 GdkScreen *screen = gtk_widget_get_screen (widget);
1199 GdkRectangle monitor;
1202 monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
1203 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1204 pview->monitor_height = monitor.height;
1206 ev_view_presentation_update_scale (pview);
1207 ev_view_presentation_update_current_page (pview, pview->current_page);
1208 ev_view_presentation_hide_cursor_timeout_start (pview);
1214 ev_view_presentation_realize (GtkWidget *widget)
1216 GdkWindowAttr attributes;
1218 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1220 attributes.window_type = GDK_WINDOW_CHILD;
1221 attributes.wclass = GDK_INPUT_OUTPUT;
1222 attributes.visual = gtk_widget_get_visual (widget);
1223 attributes.colormap = gtk_widget_get_colormap (widget);
1225 attributes.x = widget->allocation.x;
1226 attributes.y = widget->allocation.y;
1227 attributes.width = widget->allocation.width;
1228 attributes.height = widget->allocation.height;
1229 attributes.event_mask = GDK_EXPOSURE_MASK |
1230 GDK_BUTTON_PRESS_MASK |
1231 GDK_BUTTON_RELEASE_MASK |
1233 GDK_KEY_PRESS_MASK |
1234 GDK_POINTER_MOTION_MASK |
1235 GDK_POINTER_MOTION_HINT_MASK |
1236 GDK_ENTER_NOTIFY_MASK |
1237 GDK_LEAVE_NOTIFY_MASK;
1239 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1241 GDK_WA_X | GDK_WA_Y |
1244 gdk_window_set_user_data (widget->window, widget);
1245 widget->style = gtk_style_attach (widget->style, widget->window);
1247 gdk_window_set_background (widget->window, &widget->style->black);
1249 g_idle_add ((GSourceFunc)init_presentation, widget);
1253 ev_view_presentation_change_page (EvViewPresentation *pview,
1254 GtkScrollType scroll)
1257 case GTK_SCROLL_PAGE_FORWARD:
1258 ev_view_presentation_next_page (pview);
1260 case GTK_SCROLL_PAGE_BACKWARD:
1261 ev_view_presentation_previous_page (pview);
1264 g_assert_not_reached ();
1269 ev_view_presentation_scroll_event (GtkWidget *widget,
1270 GdkEventScroll *event)
1272 EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1275 state = event->state & gtk_accelerator_get_default_mod_mask ();
1279 switch (event->direction) {
1280 case GDK_SCROLL_DOWN:
1281 case GDK_SCROLL_RIGHT:
1282 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
1285 case GDK_SCROLL_LEFT:
1286 ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1295 add_change_page_binding_keypad (GtkBindingSet *binding_set,
1297 GdkModifierType modifiers,
1298 GtkScrollType scroll)
1300 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
1302 gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1304 GTK_TYPE_SCROLL_TYPE, scroll);
1305 gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1307 GTK_TYPE_SCROLL_TYPE, scroll);
1311 ev_view_presentation_set_property (GObject *object,
1313 const GValue *value,
1316 EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1320 pview->document = g_value_dup_object (value);
1321 pview->enable_animations = EV_IS_DOCUMENT_TRANSITION (pview->document);
1323 case PROP_CURRENT_PAGE:
1324 pview->current_page = g_value_get_uint (value);
1327 pview->rotation = g_value_get_uint (value);
1330 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1335 ev_view_presentation_constructor (GType type,
1336 guint n_construct_properties,
1337 GObjectConstructParam *construct_params)
1340 EvViewPresentation *pview;
1342 object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1343 n_construct_properties,
1345 pview = EV_VIEW_PRESENTATION (object);
1347 if (EV_IS_DOCUMENT_LINKS (pview->document)) {
1348 pview->page_cache = ev_page_cache_new (pview->document);
1349 ev_page_cache_set_flags (pview->page_cache, EV_PAGE_DATA_INCLUDE_LINKS);
1356 ev_view_presentation_class_init (EvViewPresentationClass *klass)
1358 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1359 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1360 GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (klass);
1361 GtkBindingSet *binding_set;
1363 klass->change_page = ev_view_presentation_change_page;
1365 widget_class->size_allocate = ev_view_presentation_size_allocate;
1366 widget_class->size_request = ev_view_presentation_size_request;
1367 widget_class->realize = ev_view_presentation_realize;
1368 widget_class->expose_event = ev_view_presentation_expose_event;
1369 widget_class->key_press_event = ev_view_presentation_key_press_event;
1370 widget_class->button_release_event = ev_view_presentation_button_release_event;
1371 widget_class->focus_out_event = ev_view_presentation_focus_out;
1372 widget_class->motion_notify_event = ev_view_presentation_motion_notify_event;
1373 widget_class->scroll_event = ev_view_presentation_scroll_event;
1375 gtk_object_class->destroy = ev_view_presentation_destroy;
1377 gobject_class->constructor = ev_view_presentation_constructor;
1378 gobject_class->set_property = ev_view_presentation_set_property;
1380 g_object_class_install_property (gobject_class,
1382 g_param_spec_object ("document",
1387 G_PARAM_CONSTRUCT_ONLY));
1388 g_object_class_install_property (gobject_class,
1390 g_param_spec_uint ("current_page",
1395 G_PARAM_CONSTRUCT_ONLY));
1396 g_object_class_install_property (gobject_class,
1398 g_param_spec_uint ("rotation",
1400 "Current rotation angle",
1403 G_PARAM_CONSTRUCT_ONLY));
1405 signals[CHANGE_PAGE] =
1406 g_signal_new ("change_page",
1407 G_OBJECT_CLASS_TYPE (gobject_class),
1408 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1409 G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
1411 g_cclosure_marshal_VOID__ENUM,
1413 GTK_TYPE_SCROLL_TYPE);
1415 g_signal_new ("finished",
1416 G_OBJECT_CLASS_TYPE (gobject_class),
1417 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1418 G_STRUCT_OFFSET (EvViewPresentationClass, finished),
1420 g_cclosure_marshal_VOID__VOID,
1424 binding_set = gtk_binding_set_by_class (klass);
1425 add_change_page_binding_keypad (binding_set, GDK_Left, 0, GTK_SCROLL_PAGE_BACKWARD);
1426 add_change_page_binding_keypad (binding_set, GDK_Right, 0, GTK_SCROLL_PAGE_FORWARD);
1427 add_change_page_binding_keypad (binding_set, GDK_Up, 0, GTK_SCROLL_PAGE_BACKWARD);
1428 add_change_page_binding_keypad (binding_set, GDK_Down, 0, GTK_SCROLL_PAGE_FORWARD);
1429 gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
1431 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1432 gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
1434 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1435 gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
1437 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1438 gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
1440 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1441 gtk_binding_entry_add_signal (binding_set, GDK_J, 0,
1443 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1444 gtk_binding_entry_add_signal (binding_set, GDK_H, 0,
1446 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1447 gtk_binding_entry_add_signal (binding_set, GDK_L, 0,
1449 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1450 gtk_binding_entry_add_signal (binding_set, GDK_K, 0,
1452 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1456 ev_view_presentation_init (EvViewPresentation *pview)
1458 GTK_WIDGET_SET_FLAGS (pview, GTK_CAN_FOCUS);
1462 ev_view_presentation_new (EvDocument *document,
1466 g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
1467 g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
1469 return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
1470 "document", document,
1471 "current_page", current_page,
1472 "rotation", rotation,
1477 ev_view_presentation_get_current_page (EvViewPresentation *pview)
1479 return pview->current_page;