1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /* this file is part of evince, a gnome document viewer
4 * Copyright (C) 2004 Red Hat, Inc
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.
27 #include <glib/gi18n-lib.h>
29 #include <gdk/gdkkeysyms.h>
31 #include "ev-mapping.h"
32 #include "ev-document-forms.h"
33 #include "ev-document-images.h"
34 #include "ev-document-links.h"
35 #include "ev-document-misc.h"
36 #include "ev-document-transition.h"
37 #include "ev-page-cache.h"
38 #include "ev-pixbuf-cache.h"
39 #include "ev-transition-animation.h"
40 #include "ev-view-marshal.h"
41 #include "ev-document-annotations.h"
42 #include "ev-annotation-window.h"
44 #include "ev-view-accessible.h"
45 #include "ev-view-private.h"
46 #include "ev-view-type-builtins.h"
48 #define EV_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass))
49 #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW))
50 #define EV_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_VIEW, EvViewClass))
65 SIGNAL_BINDING_ACTIVATED,
79 static guint signals[N_SIGNALS];
84 } EvViewFindDirection;
86 #define ZOOM_IN_FACTOR 1.2
87 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)
89 #define MIN_SCALE 0.05409
92 #define SCROLL_TIME 150
95 static void scroll_to_current_page (EvView *view,
96 GtkOrientation orientation);
97 static void ev_view_set_scroll_adjustments (GtkLayout *layout,
98 GtkAdjustment *hadjustment,
99 GtkAdjustment *vadjustment);
100 static void view_update_range_and_current_page (EvView *view);
101 static void set_scroll_adjustment (EvView *view,
102 GtkOrientation orientation,
103 GtkAdjustment *adjustment);
104 static void add_scroll_binding_keypad (GtkBindingSet *binding_set,
106 GdkModifierType modifiers,
107 GtkScrollType scroll,
108 gboolean horizontal);
109 static void ensure_rectangle_is_visible (EvView *view,
112 /*** Geometry computations ***/
113 static void compute_border (EvView *view,
117 static void get_page_y_offset (EvView *view,
121 static gboolean get_page_extents (EvView *view,
123 GdkRectangle *page_area,
125 static void view_rect_to_doc_rect (EvView *view,
126 GdkRectangle *view_rect,
127 GdkRectangle *page_area,
128 EvRectangle *doc_rect);
129 static void doc_rect_to_view_rect (EvView *view,
131 EvRectangle *doc_rect,
132 GdkRectangle *view_rect);
133 static void find_page_at_location (EvView *view,
139 static gboolean doc_point_to_view_point (EvView *view,
142 GdkPoint *view_point);
144 static EvLink * ev_view_get_link_at_location (EvView *view,
147 static char* tip_from_link (EvView *view,
150 static EvFormField *ev_view_get_form_field_at_location (EvView *view,
154 /*** Annotations ***/
155 static EvAnnotation *ev_view_get_annotation_at_location (EvView *view,
158 static void show_annotation_windows (EvView *view,
160 static void hide_annotation_windows (EvView *view,
162 /*** GtkWidget implementation ***/
163 static void ev_view_size_request_continuous_dual_page (EvView *view,
164 GtkRequisition *requisition);
165 static void ev_view_size_request_continuous (EvView *view,
166 GtkRequisition *requisition);
167 static void ev_view_size_request_dual_page (EvView *view,
168 GtkRequisition *requisition);
169 static void ev_view_size_request_single_page (EvView *view,
170 GtkRequisition *requisition);
171 static void ev_view_size_request (GtkWidget *widget,
172 GtkRequisition *requisition);
173 static void ev_view_size_allocate (GtkWidget *widget,
174 GtkAllocation *allocation);
175 static void ev_view_realize (GtkWidget *widget);
176 static gboolean ev_view_scroll_event (GtkWidget *widget,
177 GdkEventScroll *event);
178 static gboolean ev_view_expose_event (GtkWidget *widget,
179 GdkEventExpose *event);
180 static gboolean ev_view_popup_menu (GtkWidget *widget);
181 static gboolean ev_view_button_press_event (GtkWidget *widget,
182 GdkEventButton *event);
183 static gboolean ev_view_motion_notify_event (GtkWidget *widget,
184 GdkEventMotion *event);
185 static gboolean ev_view_button_release_event (GtkWidget *widget,
186 GdkEventButton *event);
187 static gboolean ev_view_enter_notify_event (GtkWidget *widget,
188 GdkEventCrossing *event);
189 static gboolean ev_view_leave_notify_event (GtkWidget *widget,
190 GdkEventCrossing *event);
191 static void ev_view_style_set (GtkWidget *widget,
192 GtkStyle *old_style);
193 static void ev_view_remove_all (EvView *view);
195 static AtkObject *ev_view_get_accessible (GtkWidget *widget);
198 static guint32 ev_gdk_color_to_rgb (const GdkColor *color);
199 static void draw_rubberband (GtkWidget *widget,
201 const GdkRectangle *rect,
203 static void highlight_find_results (EvView *view,
205 static void draw_one_page (EvView *view,
208 GdkRectangle *page_area,
210 GdkRectangle *expose_area,
211 gboolean *page_ready);
212 static void draw_loading_text (EvView *view,
213 GdkRectangle *page_area,
214 GdkRectangle *expose_area);
215 static void ev_view_reload_page (EvView *view,
220 static void job_finished_cb (EvPixbufCache *pixbuf_cache,
223 static void page_changed_cb (EvPageCache *page_cache,
226 static void on_adjustment_value_changed (GtkAdjustment *adjustment,
230 static void ev_view_finalize (GObject *object);
231 static void ev_view_destroy (GtkObject *object);
232 static void ev_view_set_property (GObject *object,
236 static void ev_view_get_property (GObject *object,
240 static void ev_view_class_init (EvViewClass *class);
241 static void ev_view_init (EvView *view);
243 /*** Zoom and sizing ***/
244 static double zoom_for_size_fit_width (int doc_width,
249 static double zoom_for_size_fit_height (int doc_width,
254 static double zoom_for_size_best_fit (int doc_width,
260 static void ev_view_zoom_for_size_presentation (EvView *view,
263 static void ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
268 static void ev_view_zoom_for_size_continuous (EvView *view,
273 static void ev_view_zoom_for_size_dual_page (EvView *view,
278 static void ev_view_zoom_for_size_single_page (EvView *view,
284 static GdkCursor* ev_view_create_invisible_cursor (void);
285 static void ev_view_set_cursor (EvView *view,
286 EvViewCursor new_cursor);
287 static void ev_view_handle_cursor_over_xy (EvView *view,
292 static gint ev_view_find_get_n_results (EvView *view,
294 static EvRectangle *ev_view_find_get_result (EvView *view,
297 static void jump_to_find_result (EvView *view);
298 static void jump_to_find_page (EvView *view,
299 EvViewFindDirection direction,
303 static void compute_selections (EvView *view,
304 EvSelectionStyle style,
307 static void clear_selection (EvView *view);
308 static void clear_link_selected (EvView *view);
309 static void selection_free (EvViewSelection *selection);
310 static char* get_selected_text (EvView *ev_view);
311 static void ev_view_primary_get_cb (GtkClipboard *clipboard,
312 GtkSelectionData *selection_data,
315 static void ev_view_primary_clear_cb (GtkClipboard *clipboard,
317 static void ev_view_update_primary_selection (EvView *ev_view);
319 /*** Presentation ***/
320 static void ev_view_presentation_transition_start (EvView *ev_view);
321 static void ev_view_presentation_transition_stop (EvView *ev_view);
324 G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_LAYOUT)
327 scroll_to_current_page (EvView *view, GtkOrientation orientation)
331 if (view->document == NULL) {
335 doc_point_to_view_point (view, view->current_page, &view->pending_point, &view_point);
337 if (orientation == GTK_ORIENTATION_VERTICAL) {
338 view->pending_point.y = 0;
340 view->pending_point.x = 0;
343 if (orientation == GTK_ORIENTATION_VERTICAL) {
344 if (view->continuous) {
345 gtk_adjustment_clamp_page (view->vadjustment,
346 view_point.y - view->spacing / 2,
347 view_point.y + view->vadjustment->page_size);
349 gtk_adjustment_set_value (view->vadjustment,
351 view->vadjustment->lower,
352 view->vadjustment->upper -
353 view->vadjustment->page_size));
356 if (view->dual_page) {
357 gtk_adjustment_clamp_page (view->hadjustment,
359 view_point.x + view->hadjustment->page_size);
361 gtk_adjustment_set_value (view->hadjustment,
363 view->hadjustment->lower,
364 view->hadjustment->upper -
365 view->hadjustment->page_size));
371 view_set_adjustment_values (EvView *view,
372 GtkOrientation orientation)
374 GtkWidget *widget = GTK_WIDGET (view);
375 GtkAdjustment *adjustment;
382 if (orientation == GTK_ORIENTATION_HORIZONTAL) {
383 requisition = widget->requisition.width;
384 allocation = widget->allocation.width;
385 adjustment = view->hadjustment;
387 requisition = widget->requisition.height;
388 allocation = widget->allocation.height;
389 adjustment = view->vadjustment;
396 switch (view->pending_scroll) {
397 case SCROLL_TO_KEEP_POSITION:
398 case SCROLL_TO_FIND_LOCATION:
399 factor = (adjustment->value) / adjustment->upper;
401 case SCROLL_TO_PAGE_POSITION:
403 case SCROLL_TO_CENTER:
404 factor = (adjustment->value + adjustment->page_size * 0.5) / adjustment->upper;
408 adjustment->page_size = allocation;
409 adjustment->step_increment = allocation * 0.1;
410 adjustment->page_increment = allocation * 0.9;
411 adjustment->lower = 0;
412 adjustment->upper = MAX (allocation, requisition);
415 * We add 0.5 to the values before to average out our rounding errors.
417 switch (view->pending_scroll) {
418 case SCROLL_TO_KEEP_POSITION:
419 case SCROLL_TO_FIND_LOCATION:
420 new_value = CLAMP (adjustment->upper * factor + 0.5, 0, adjustment->upper - adjustment->page_size);
421 gtk_adjustment_set_value (adjustment, (int)new_value);
423 case SCROLL_TO_PAGE_POSITION:
424 scroll_to_current_page (view, orientation);
426 case SCROLL_TO_CENTER:
427 new_value = CLAMP (adjustment->upper * factor - adjustment->page_size * 0.5 + 0.5,
428 0, adjustment->upper - adjustment->page_size);
429 gtk_adjustment_set_value (adjustment, (int)new_value);
433 gtk_adjustment_changed (adjustment);
437 view_update_range_and_current_page (EvView *view)
440 gint best_current_page = -1;
441 gint start = view->start_page;
442 gint end = view->end_page;
444 /* Presentation trumps all other modes */
445 if (view->presentation) {
446 view->start_page = view->current_page;
447 view->end_page = view->current_page;
448 } else if (view->continuous) {
449 GdkRectangle current_area, unused, page_area;
451 gboolean found = FALSE;
452 gint area_max = -1, area;
455 if (!(view->vadjustment && view->hadjustment))
458 current_area.x = view->hadjustment->value;
459 current_area.width = view->hadjustment->upper;
460 current_area.y = view->vadjustment->value;
461 current_area.height = view->vadjustment->page_size;
463 for (i = 0; i < ev_page_cache_get_n_pages (view->page_cache); i++) {
465 get_page_extents (view, i, &page_area, &border);
467 if (gdk_rectangle_intersect (¤t_area, &page_area, &unused)) {
468 area = unused.width * unused.height;
472 view->start_page = i;
474 best_current_page = i;
476 if (area > area_max) {
477 best_current_page = (area == area_max) ? MIN (i, best_current_page) : i;
482 } else if (found && view->current_page <= view->end_page) {
487 } else if (view->dual_page) {
488 if (view->current_page % 2 == ev_page_cache_get_dual_even_left (view->page_cache)) {
489 view->start_page = view->current_page;
490 if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache))
491 view->end_page = view->start_page + 1;
493 view->end_page = view->start_page;
495 if (view->current_page < 1)
496 view->start_page = view->current_page;
498 view->start_page = view->current_page - 1;
499 view->end_page = view->current_page;
502 view->start_page = view->current_page;
503 view->end_page = view->current_page;
506 best_current_page = MAX (best_current_page, view->start_page);
507 current_page = ev_page_cache_get_current_page (view->page_cache);
509 if ((current_page != best_current_page) && (view->pending_scroll == SCROLL_TO_KEEP_POSITION)) {
510 view->current_page = best_current_page;
511 ev_page_cache_set_current_page (view->page_cache, best_current_page);
514 if (start != view->start_page || end != view->end_page) {
517 for (i = start; i < view->start_page; i++) {
518 hide_annotation_windows (view, i);
521 for (i = end; i > view->end_page; i--) {
522 hide_annotation_windows (view, i);
526 ev_pixbuf_cache_set_page_range (view->pixbuf_cache,
531 view->selection_info.selections);
535 set_scroll_adjustment (EvView *view,
536 GtkOrientation orientation,
537 GtkAdjustment *adjustment)
539 GtkAdjustment **to_set;
541 if (orientation == GTK_ORIENTATION_HORIZONTAL)
542 to_set = &view->hadjustment;
544 to_set = &view->vadjustment;
546 if (*to_set != adjustment) {
548 g_signal_handlers_disconnect_by_func (*to_set,
549 (gpointer) on_adjustment_value_changed,
551 g_object_unref (*to_set);
554 *to_set = adjustment;
555 view_set_adjustment_values (view, orientation);
558 g_object_ref (*to_set);
559 g_signal_connect (*to_set, "value_changed",
560 G_CALLBACK (on_adjustment_value_changed), view);
566 ev_view_set_scroll_adjustments (GtkLayout *layout,
567 GtkAdjustment *hadjustment,
568 GtkAdjustment *vadjustment)
570 EvView *view = EV_VIEW (layout);
572 set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
573 set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
575 on_adjustment_value_changed (NULL, view);
579 add_scroll_binding_keypad (GtkBindingSet *binding_set,
581 GdkModifierType modifiers,
582 GtkScrollType scroll,
585 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
587 gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
588 "binding_activated", 2,
589 GTK_TYPE_SCROLL_TYPE, scroll,
590 G_TYPE_BOOLEAN, horizontal);
591 gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
592 "binding_activated", 2,
593 GTK_TYPE_SCROLL_TYPE, scroll,
594 G_TYPE_BOOLEAN, horizontal);
598 ev_view_scroll (EvView *view,
599 GtkScrollType scroll,
602 GtkAdjustment *adjustment;
603 double value, increment;
604 gboolean first_page = FALSE;
605 gboolean last_page = FALSE;
607 view->jump_to_find_result = FALSE;
609 if (view->presentation || view->sizing_mode == EV_SIZING_BEST_FIT) {
611 case GTK_SCROLL_PAGE_BACKWARD:
612 case GTK_SCROLL_STEP_BACKWARD:
613 ev_view_previous_page (view);
615 case GTK_SCROLL_PAGE_FORWARD:
616 case GTK_SCROLL_STEP_FORWARD:
617 ev_view_next_page (view);
625 /* Assign values for increment and vertical adjustment */
626 adjustment = horizontal ? view->hadjustment : view->vadjustment;
627 increment = adjustment->page_size * 0.75;
628 value = adjustment->value;
630 /* Assign boolean for first and last page */
631 if (view->current_page == 0)
633 if (view->current_page == ev_page_cache_get_n_pages (view->page_cache) - 1)
637 case GTK_SCROLL_PAGE_BACKWARD:
638 /* Do not jump backwards if at the first page */
639 if (value == (adjustment->lower) && first_page) {
641 /* At the top of a page, assign the upper bound limit of previous page */
642 } else if (value == (adjustment->lower)) {
643 value = adjustment->upper - adjustment->page_size;
644 ev_view_previous_page (view);
645 /* Jump to the top */
647 value = MAX (value - increment, adjustment->lower);
650 case GTK_SCROLL_PAGE_FORWARD:
651 /* Do not jump forward if at the last page */
652 if (value == (adjustment->upper - adjustment->page_size) && last_page) {
654 /* At the bottom of a page, assign the lower bound limit of next page */
655 } else if (value == (adjustment->upper - adjustment->page_size)) {
657 ev_view_next_page (view);
658 /* Jump to the bottom */
660 value = MIN (value + increment, adjustment->upper - adjustment->page_size);
663 case GTK_SCROLL_STEP_BACKWARD:
664 value -= adjustment->step_increment;
666 case GTK_SCROLL_STEP_FORWARD:
667 value += adjustment->step_increment;
669 case GTK_SCROLL_STEP_DOWN:
670 value -= adjustment->step_increment / 10;
672 case GTK_SCROLL_STEP_UP:
673 value += adjustment->step_increment / 10;
679 value = CLAMP (value, adjustment->lower,
680 adjustment->upper - adjustment->page_size);
682 gtk_adjustment_set_value (adjustment, value);
688 ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect)
690 GtkWidget *widget = GTK_WIDGET (view);
691 GtkAdjustment *adjustment;
694 view->pending_scroll = SCROLL_TO_FIND_LOCATION;
696 adjustment = view->vadjustment;
698 if (rect->y < adjustment->value) {
699 value = MAX (adjustment->lower, rect->y - MARGIN);
700 gtk_adjustment_set_value (view->vadjustment, value);
701 } else if (rect->y + rect->height >
702 adjustment->value + widget->allocation.height) {
703 value = MIN (adjustment->upper, rect->y + rect->height -
704 widget->allocation.height + MARGIN);
705 gtk_adjustment_set_value (view->vadjustment, value);
708 adjustment = view->hadjustment;
710 if (rect->x < adjustment->value) {
711 value = MAX (adjustment->lower, rect->x - MARGIN);
712 gtk_adjustment_set_value (view->hadjustment, value);
713 } else if (rect->x + rect->height >
714 adjustment->value + widget->allocation.width) {
715 value = MIN (adjustment->upper, rect->x + rect->width -
716 widget->allocation.width + MARGIN);
717 gtk_adjustment_set_value (view->hadjustment, value);
720 gtk_widget_queue_resize (GTK_WIDGET (view));
723 /*** Geometry computations ***/
726 compute_border (EvView *view, int width, int height, GtkBorder *border)
728 if (view->presentation) {
734 ev_document_misc_get_page_border_size (width, height, border);
739 get_page_y_offset (EvView *view, int page, double zoom, int *y_offset)
741 int max_width, offset;
744 g_return_if_fail (y_offset != NULL);
746 ev_page_cache_get_max_width (view->page_cache, view->rotation, zoom, &max_width);
748 compute_border (view, max_width, max_width, &border);
750 if (view->dual_page) {
751 ev_page_cache_get_height_to_page (view->page_cache, page,
752 view->rotation, zoom, NULL, &offset);
753 offset += ((page + ev_page_cache_get_dual_even_left (view->page_cache)) / 2 + 1) * view->spacing + ((page + ev_page_cache_get_dual_even_left (view->page_cache)) / 2 ) * (border.top + border.bottom);
755 ev_page_cache_get_height_to_page (view->page_cache, page,
756 view->rotation, zoom, &offset, NULL);
757 offset += (page + 1) * view->spacing + page * (border.top + border.bottom);
765 get_page_extents (EvView *view,
767 GdkRectangle *page_area,
773 widget = GTK_WIDGET (view);
775 /* Get the size of the page */
776 ev_page_cache_get_size (view->page_cache, page,
780 compute_border (view, width, height, border);
781 page_area->width = width + border->left + border->right;
782 page_area->height = height + border->top + border->bottom;
784 if (view->presentation) {
785 page_area->x = (MAX (0, widget->allocation.width - width))/2;
786 page_area->y = (MAX (0, widget->allocation.height - height))/2;
787 } else if (view->continuous) {
791 ev_page_cache_get_max_width (view->page_cache, view->rotation,
792 view->scale, &max_width);
793 max_width = max_width + border->left + border->right;
794 /* Get the location of the bounding box */
795 if (view->dual_page) {
796 x = view->spacing + ((page % 2 == ev_page_cache_get_dual_even_left (view->page_cache)) ? 0 : 1) * (max_width + view->spacing);
797 x = x + MAX (0, widget->allocation.width - (max_width * 2 + view->spacing * 3)) / 2;
798 if (page % 2 == ev_page_cache_get_dual_even_left (view->page_cache))
799 x = x + (max_width - width - border->left - border->right);
802 x = x + MAX (0, widget->allocation.width - (width + view->spacing * 2)) / 2;
805 get_page_y_offset (view, page, view->scale, &y);
811 if (view->dual_page) {
812 gint width_2, height_2;
813 gint max_width = width;
814 gint max_height = height;
815 GtkBorder overall_border;
818 other_page = (page % 2 == ev_page_cache_get_dual_even_left (view->page_cache)) ? page + 1: page - 1;
820 /* First, we get the bounding box of the two pages */
821 if (other_page < ev_page_cache_get_n_pages (view->page_cache)
822 && (0 <= other_page)) {
823 ev_page_cache_get_size (view->page_cache,
827 &width_2, &height_2);
830 if (height_2 > height)
831 max_height = height_2;
833 compute_border (view, max_width, max_height, &overall_border);
835 /* Find the offsets */
839 /* Adjust for being the left or right page */
840 if (page % 2 == ev_page_cache_get_dual_even_left (view->page_cache))
841 x = x + max_width - width;
843 x = x + (max_width + overall_border.left + overall_border.right) + view->spacing;
845 y = y + (max_height - height)/2;
847 /* Adjust for extra allocation */
848 x = x + MAX (0, widget->allocation.width -
849 ((max_width + overall_border.left + overall_border.right) * 2 + view->spacing * 3))/2;
850 y = y + MAX (0, widget->allocation.height - (height + view->spacing * 2))/2;
855 /* Adjust for extra allocation */
856 x = x + MAX (0, widget->allocation.width - (width + border->left + border->right + view->spacing * 2))/2;
857 y = y + MAX (0, widget->allocation.height - (height + border->top + border->bottom + view->spacing * 2))/2;
868 view_point_to_doc_point (EvView *view,
869 GdkPoint *view_point,
870 GdkRectangle *page_area,
874 *doc_point_x = (double) (view_point->x - page_area->x) / view->scale;
875 *doc_point_y = (double) (view_point->y - page_area->y) / view->scale;
879 view_rect_to_doc_rect (EvView *view,
880 GdkRectangle *view_rect,
881 GdkRectangle *page_area,
882 EvRectangle *doc_rect)
884 doc_rect->x1 = (double) (view_rect->x - page_area->x) / view->scale;
885 doc_rect->y1 = (double) (view_rect->y - page_area->y) / view->scale;
886 doc_rect->x2 = doc_rect->x1 + (double) view_rect->width / view->scale;
887 doc_rect->y2 = doc_rect->y1 + (double) view_rect->height / view->scale;
891 doc_point_to_view_point (EvView *view,
894 GdkPoint *view_point)
896 GdkRectangle page_area;
898 double x, y, view_x, view_y;
901 ev_page_cache_get_size (view->page_cache, page,
906 if (view->rotation == 0) {
909 } else if (view->rotation == 90) {
910 x = width - doc_point->y;
912 } else if (view->rotation == 180) {
913 x = width - doc_point->x;
914 y = height - doc_point->y;
915 } else if (view->rotation == 270) {
917 y = height - doc_point->x;
919 g_assert_not_reached ();
922 get_page_extents (view, page, &page_area, &border);
924 view_x = x * view->scale;
925 view_y = y * view->scale;
926 view_point->x = view_x + page_area.x;
927 view_point->y = view_y + page_area.y;
929 return (view_x > 0 && view_x <= page_area.width &&
930 view_y > 0 && view_y <= page_area.height);
934 doc_rect_to_view_rect (EvView *view,
936 EvRectangle *doc_rect,
937 GdkRectangle *view_rect)
939 GdkRectangle page_area;
944 ev_page_cache_get_size (view->page_cache, page,
949 if (view->rotation == 0) {
952 w = doc_rect->x2 - doc_rect->x1;
953 h = doc_rect->y2 - doc_rect->y1;
954 } else if (view->rotation == 90) {
955 x = width - doc_rect->y2;
957 w = doc_rect->y2 - doc_rect->y1;
958 h = doc_rect->x2 - doc_rect->x1;
959 } else if (view->rotation == 180) {
960 x = width - doc_rect->x2;
961 y = height - doc_rect->y2;
962 w = doc_rect->x2 - doc_rect->x1;
963 h = doc_rect->y2 - doc_rect->y1;
964 } else if (view->rotation == 270) {
966 y = height - doc_rect->x2;
967 w = doc_rect->y2 - doc_rect->y1;
968 h = doc_rect->x2 - doc_rect->x1;
970 g_assert_not_reached ();
973 get_page_extents (view, page, &page_area, &border);
975 view_rect->x = x * view->scale + page_area.x;
976 view_rect->y = y * view->scale + page_area.y;
977 view_rect->width = w * view->scale;
978 view_rect->height = h * view->scale;
982 find_page_at_location (EvView *view,
991 if (view->document == NULL)
998 for (i = view->start_page; i <= view->end_page; i++) {
999 GdkRectangle page_area;
1002 if (! get_page_extents (view, i, &page_area, &border))
1005 if ((x >= page_area.x + border.left) &&
1006 (x < page_area.x + page_area.width - border.right) &&
1007 (y >= page_area.y + border.top) &&
1008 (y < page_area.y + page_area.height - border.bottom)) {
1010 *x_offset = x - (page_area.x + border.left);
1011 *y_offset = y - (page_area.y + border.top);
1020 location_in_text (EvView *view,
1026 gint x_offset = 0, y_offset = 0;
1028 find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
1033 region = ev_pixbuf_cache_get_text_mapping (view->pixbuf_cache, page);
1036 return gdk_region_point_in (region, x_offset / view->scale, y_offset / view->scale);
1042 ev_view_get_width (EvView *view)
1044 return GTK_WIDGET (view)->allocation.width;
1048 ev_view_get_height (EvView *view)
1050 return GTK_WIDGET (view)->allocation.height;
1054 location_in_selected_text (EvView *view,
1059 gint x_offset = 0, y_offset = 0;
1060 EvViewSelection *selection;
1063 for (l = view->selection_info.selections; l != NULL; l = l->next) {
1064 selection = (EvViewSelection *)l->data;
1066 find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
1068 if (page != selection->page)
1071 if (selection->covered_region &&
1072 gdk_region_point_in (selection->covered_region, x_offset, y_offset))
1080 get_doc_point_from_offset (EvView *view,
1090 ev_page_cache_get_size (view->page_cache, page,
1095 x_offset = x_offset / view->scale;
1096 y_offset = y_offset / view->scale;
1098 if (view->rotation == 0) {
1101 } else if (view->rotation == 90) {
1103 y = width - x_offset;
1104 } else if (view->rotation == 180) {
1105 x = width - x_offset;
1106 y = height - y_offset;
1107 } else if (view->rotation == 270) {
1108 x = height - y_offset;
1111 g_assert_not_reached ();
1121 get_doc_point_from_location (EvView *view,
1128 gint x_offset = 0, y_offset = 0;
1130 x += view->scroll_x;
1131 y += view->scroll_y;
1132 find_page_at_location (view, x, y, page, &x_offset, &y_offset);
1136 return get_doc_point_from_offset (view, *page, x_offset, y_offset, x_new, y_new);
1141 ev_view_get_link_at_location (EvView *view,
1146 gint x_new = 0, y_new = 0;
1147 GList *link_mapping;
1149 if (!EV_IS_DOCUMENT_LINKS (view->document))
1152 if (!get_doc_point_from_location (view, x, y, &page, &x_new, &y_new))
1155 link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page);
1158 return ev_mapping_list_get_data (link_mapping, x_new, y_new);
1164 goto_fitr_dest (EvView *view, EvLinkDest *dest)
1167 gdouble zoom, left, top;
1168 gboolean change_left, change_top;
1170 left = ev_link_dest_get_left (dest, &change_left);
1171 top = ev_link_dest_get_top (dest, &change_top);
1173 zoom = zoom_for_size_best_fit (ev_link_dest_get_right (dest) - left,
1174 ev_link_dest_get_bottom (dest) - top,
1175 ev_view_get_width (view),
1176 ev_view_get_height (view), 0, 0);
1178 ev_view_set_sizing_mode (view, EV_SIZING_FREE);
1179 ev_view_set_zoom (view, zoom, FALSE);
1181 doc_point.x = change_left ? left : 0;
1182 doc_point.y = change_top ? top : 0;
1184 view->current_page = ev_link_dest_get_page (dest);
1185 if (change_left || change_top)
1186 view->pending_point = doc_point;
1187 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
1189 gtk_widget_queue_resize (GTK_WIDGET (view));
1193 goto_fitv_dest (EvView *view, EvLinkDest *dest)
1196 int doc_width, doc_height, page;
1198 gboolean change_left;
1200 page = ev_link_dest_get_page (dest);
1201 ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height);
1203 left = ev_link_dest_get_left (dest, &change_left);
1204 doc_point.x = change_left ? left : 0;
1207 zoom = zoom_for_size_fit_height (doc_width - doc_point.x , doc_height,
1208 ev_view_get_width (view),
1209 ev_view_get_height (view), 0);
1211 ev_view_set_sizing_mode (view, EV_SIZING_FREE);
1212 ev_view_set_zoom (view, zoom, FALSE);
1214 view->current_page = page;
1216 view->pending_point = doc_point;
1217 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
1219 gtk_widget_queue_resize (GTK_WIDGET (view));
1223 goto_fith_dest (EvView *view, EvLinkDest *dest)
1226 int doc_width, doc_height, page;
1228 gboolean change_top;
1230 page = ev_link_dest_get_page (dest);
1231 ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height);
1233 top = ev_link_dest_get_top (dest, &change_top);
1236 doc_point.y = change_top ? top : 0;
1238 zoom = zoom_for_size_fit_width (doc_width, top,
1239 ev_view_get_width (view),
1240 ev_view_get_height (view), 0);
1242 ev_view_set_sizing_mode (view, EV_SIZING_FIT_WIDTH);
1243 ev_view_set_zoom (view, zoom, FALSE);
1245 view->current_page = page;
1247 view->pending_point = doc_point;
1248 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
1250 gtk_widget_queue_resize (GTK_WIDGET (view));
1254 goto_fit_dest (EvView *view, EvLinkDest *dest)
1257 int doc_width, doc_height;
1260 page = ev_link_dest_get_page (dest);
1261 ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height);
1263 zoom = zoom_for_size_best_fit (doc_width, doc_height, ev_view_get_width (view),
1264 ev_view_get_height (view), 0, 0);
1266 ev_view_set_sizing_mode (view, EV_SIZING_BEST_FIT);
1267 ev_view_set_zoom (view, zoom, FALSE);
1269 view->current_page = page;
1270 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
1272 gtk_widget_queue_resize (GTK_WIDGET (view));
1276 goto_xyz_dest (EvView *view, EvLinkDest *dest)
1280 gdouble zoom, left, top;
1281 gboolean change_zoom, change_left, change_top;
1283 zoom = ev_link_dest_get_zoom (dest, &change_zoom);
1284 page = ev_link_dest_get_page (dest);
1286 if (change_zoom && zoom > 1) {
1287 ev_view_set_sizing_mode (view, EV_SIZING_FREE);
1288 ev_view_set_zoom (view, zoom, FALSE);
1291 left = ev_link_dest_get_left (dest, &change_left);
1292 top = ev_link_dest_get_top (dest, &change_top);
1294 doc_point.x = change_left ? left : 0;
1295 doc_point.y = change_top ? top : 0;
1297 view->current_page = page;
1298 if (change_left || change_top)
1299 view->pending_point = doc_point;
1300 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
1302 gtk_widget_queue_resize (GTK_WIDGET (view));
1306 goto_dest (EvView *view, EvLinkDest *dest)
1308 EvLinkDestType type;
1309 int page, n_pages, current_page;
1311 page = ev_link_dest_get_page (dest);
1312 n_pages = ev_page_cache_get_n_pages (view->page_cache);
1314 if (page < 0 || page >= n_pages)
1317 current_page = view->current_page;
1319 type = ev_link_dest_get_dest_type (dest);
1322 case EV_LINK_DEST_TYPE_PAGE:
1323 ev_page_cache_set_current_page (view->page_cache, page);
1325 case EV_LINK_DEST_TYPE_FIT:
1326 goto_fit_dest (view, dest);
1328 case EV_LINK_DEST_TYPE_FITH:
1329 goto_fith_dest (view, dest);
1331 case EV_LINK_DEST_TYPE_FITV:
1332 goto_fitv_dest (view, dest);
1334 case EV_LINK_DEST_TYPE_FITR:
1335 goto_fitr_dest (view, dest);
1337 case EV_LINK_DEST_TYPE_XYZ:
1338 goto_xyz_dest (view, dest);
1340 case EV_LINK_DEST_TYPE_PAGE_LABEL:
1341 ev_page_cache_set_page_label (view->page_cache, ev_link_dest_get_page_label (dest));
1344 g_assert_not_reached ();
1347 if (current_page != view->current_page)
1348 ev_page_cache_set_current_page (view->page_cache,
1349 view->current_page);
1353 ev_view_goto_dest (EvView *view, EvLinkDest *dest)
1355 EvLinkDestType type;
1357 type = ev_link_dest_get_dest_type (dest);
1359 if (type == EV_LINK_DEST_TYPE_NAMED) {
1361 const gchar *named_dest;
1363 named_dest = ev_link_dest_get_named_dest (dest);
1364 dest2 = ev_document_links_find_link_dest (EV_DOCUMENT_LINKS (view->document),
1367 goto_dest (view, dest2);
1368 g_object_unref (dest2);
1374 goto_dest (view, dest);
1378 ev_view_handle_link (EvView *view, EvLink *link)
1380 EvLinkAction *action = NULL;
1381 EvLinkActionType type;
1383 action = ev_link_get_action (link);
1387 type = ev_link_action_get_action_type (action);
1390 case EV_LINK_ACTION_TYPE_GOTO_DEST: {
1393 g_signal_emit (view, signals[SIGNAL_HANDLE_LINK], 0, link);
1395 dest = ev_link_action_get_dest (action);
1396 ev_view_goto_dest (view, dest);
1399 case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
1400 case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
1401 case EV_LINK_ACTION_TYPE_LAUNCH:
1402 case EV_LINK_ACTION_TYPE_NAMED:
1403 g_signal_emit (view, signals[SIGNAL_EXTERNAL_LINK], 0, action);
1409 ev_view_page_label_from_dest (EvView *view, EvLinkDest *dest)
1411 EvLinkDestType type;
1414 type = ev_link_dest_get_dest_type (dest);
1417 case EV_LINK_DEST_TYPE_NAMED: {
1419 const gchar *named_dest;
1421 named_dest = ev_link_dest_get_named_dest (dest);
1422 dest2 = ev_document_links_find_link_dest (EV_DOCUMENT_LINKS (view->document),
1425 msg = ev_page_cache_get_page_label (view->page_cache,
1426 ev_link_dest_get_page (dest2));
1427 g_object_unref (dest2);
1432 case EV_LINK_DEST_TYPE_PAGE_LABEL: {
1433 msg = g_strdup (ev_link_dest_get_page_label (dest));
1437 msg = ev_page_cache_get_page_label (view->page_cache,
1438 ev_link_dest_get_page (dest));
1445 tip_from_action_named (EvLinkAction *action)
1447 const gchar *name = ev_link_action_get_name (action);
1449 if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
1450 return g_strdup (_("Go to first page"));
1451 } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
1452 return g_strdup (_("Go to previous page"));
1453 } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
1454 return g_strdup (_("Go to next page"));
1455 } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
1456 return g_strdup (_("Go to last page"));
1457 } else if (g_ascii_strcasecmp (name, "GoToPage") == 0) {
1458 return g_strdup (_("Go to page"));
1459 } else if (g_ascii_strcasecmp (name, "Find") == 0) {
1460 return g_strdup (_("Find"));
1467 tip_from_link (EvView *view, EvLink *link)
1469 EvLinkAction *action;
1470 EvLinkActionType type;
1475 action = ev_link_get_action (link);
1476 title = ev_link_get_title (link);
1479 return title ? g_strdup (title) : NULL;
1481 type = ev_link_action_get_action_type (action);
1484 case EV_LINK_ACTION_TYPE_GOTO_DEST:
1485 page_label = ev_view_page_label_from_dest (view,
1486 ev_link_action_get_dest (action));
1488 msg = g_strdup_printf (_("Go to page %s"), page_label);
1489 g_free (page_label);
1492 case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
1494 msg = g_strdup_printf (_("Go to %s on file “%s”"), title,
1495 ev_link_action_get_filename (action));
1497 msg = g_strdup_printf (_("Go to file “%s”"),
1498 ev_link_action_get_filename (action));
1501 case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
1502 msg = g_strdup (ev_link_action_get_uri (action));
1504 case EV_LINK_ACTION_TYPE_LAUNCH:
1505 msg = g_strdup_printf (_("Launch %s"),
1506 ev_link_action_get_filename (action));
1508 case EV_LINK_ACTION_TYPE_NAMED:
1509 msg = tip_from_action_named (action);
1513 msg = g_strdup (title);
1521 ev_view_handle_cursor_over_xy (EvView *view, gint x, gint y)
1525 EvAnnotation *annot = NULL;
1527 if (view->cursor == EV_VIEW_CURSOR_HIDDEN)
1530 if (view->drag_info.in_drag) {
1531 if (view->cursor != EV_VIEW_CURSOR_DRAG)
1532 ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG);
1536 if (view->scroll_info.autoscrolling) {
1537 if (view->cursor != EV_VIEW_CURSOR_AUTOSCROLL)
1538 ev_view_set_cursor (view, EV_VIEW_CURSOR_AUTOSCROLL);
1542 link = ev_view_get_link_at_location (view, x, y);
1544 ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
1545 } else if ((field = ev_view_get_form_field_at_location (view, x, y))) {
1546 if (field->is_read_only) {
1547 if (view->cursor == EV_VIEW_CURSOR_LINK ||
1548 view->cursor == EV_VIEW_CURSOR_IBEAM ||
1549 view->cursor == EV_VIEW_CURSOR_DRAG)
1550 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1551 } else if (EV_IS_FORM_FIELD_TEXT (field)) {
1552 ev_view_set_cursor (view, EV_VIEW_CURSOR_IBEAM);
1554 ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
1556 } else if ((annot = ev_view_get_annotation_at_location (view, x, y))) {
1557 ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
1558 } else if (location_in_text (view, x + view->scroll_x, y + view->scroll_y)) {
1559 ev_view_set_cursor (view, EV_VIEW_CURSOR_IBEAM);
1561 if (view->cursor == EV_VIEW_CURSOR_LINK ||
1562 view->cursor == EV_VIEW_CURSOR_IBEAM ||
1563 view->cursor == EV_VIEW_CURSOR_DRAG ||
1564 view->cursor == EV_VIEW_CURSOR_AUTOSCROLL)
1565 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1569 g_object_set (view, "has-tooltip", TRUE, NULL);
1574 ev_view_get_image_at_location (EvView *view,
1579 gint x_new = 0, y_new = 0;
1580 GList *image_mapping;
1582 if (!EV_IS_DOCUMENT_IMAGES (view->document))
1585 if (!get_doc_point_from_location (view, x, y, &page, &x_new, &y_new))
1588 image_mapping = ev_pixbuf_cache_get_image_mapping (view->pixbuf_cache, page);
1591 return ev_mapping_list_get_data (image_mapping, x_new, y_new);
1597 static EvFormField *
1598 ev_view_get_form_field_at_location (EvView *view,
1603 gint x_new = 0, y_new = 0;
1604 GList *forms_mapping;
1606 if (!EV_IS_DOCUMENT_FORMS (view->document))
1609 if (!get_doc_point_from_location (view, x, y, &page, &x_new, &y_new))
1612 forms_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache, page);
1615 return ev_mapping_list_get_data (forms_mapping, x_new, y_new);
1621 ev_view_form_field_get_region (EvView *view,
1624 GdkRectangle view_area;
1625 GList *forms_mapping;
1628 forms_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache,
1629 field->page->index);
1630 mapping = ev_mapping_list_find (forms_mapping, field);
1631 doc_rect_to_view_rect (view, field->page->index, &mapping->area, &view_area);
1632 view_area.x -= view->scroll_x;
1633 view_area.y -= view->scroll_y;
1635 return gdk_region_rectangle (&view_area);
1639 ev_view_forms_remove_widgets (EvView *view)
1641 ev_view_remove_all (view);
1647 ev_view_form_field_destroy (GtkWidget *widget,
1650 g_idle_add ((GSourceFunc)ev_view_forms_remove_widgets, view);
1654 ev_view_form_field_button_create_widget (EvView *view,
1657 EvFormFieldButton *field_button = EV_FORM_FIELD_BUTTON (field);
1658 GdkRegion *field_region = NULL;
1660 switch (field_button->type) {
1661 case EV_FORM_FIELD_BUTTON_PUSH:
1663 case EV_FORM_FIELD_BUTTON_CHECK:
1664 case EV_FORM_FIELD_BUTTON_RADIO: {
1666 GList *forms_mapping, *l;
1668 state = ev_document_forms_form_field_button_get_state (EV_DOCUMENT_FORMS (view->document),
1671 /* FIXME: it actually depends on NoToggleToOff flags */
1672 if (field_button->type == EV_FORM_FIELD_BUTTON_RADIO &&
1673 state && field_button->state)
1676 field_region = ev_view_form_field_get_region (view, field);
1678 /* For radio buttons and checkbox buttons that are in a set
1679 * we need to update also the region for the current selected item
1681 forms_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache,
1682 field->page->index);
1683 for (l = forms_mapping; l; l = g_list_next (l)) {
1684 EvFormField *button = ((EvMapping *)(l->data))->data;
1685 GdkRegion *button_region;
1687 if (button->id == field->id)
1690 /* FIXME: only buttons in the same group should be updated */
1691 if (!EV_IS_FORM_FIELD_BUTTON (button) ||
1692 EV_FORM_FIELD_BUTTON (button)->type != field_button->type ||
1693 EV_FORM_FIELD_BUTTON (button)->state != TRUE)
1696 button_region = ev_view_form_field_get_region (view, button);
1697 gdk_region_union (field_region, button_region);
1698 gdk_region_destroy (button_region);
1701 ev_document_forms_form_field_button_set_state (EV_DOCUMENT_FORMS (view->document),
1703 field_button->state = !state;
1708 ev_view_reload_page (view, field->page->index, field_region);
1709 gdk_region_destroy (field_region);
1715 ev_view_form_field_text_save (EvView *view,
1720 if (!view->document)
1723 field = g_object_get_data (G_OBJECT (widget), "form-field");
1725 if (field->changed) {
1726 EvFormFieldText *field_text = EV_FORM_FIELD_TEXT (field);
1727 GdkRegion *field_region;
1729 field_region = ev_view_form_field_get_region (view, field);
1731 ev_document_forms_form_field_text_set_text (EV_DOCUMENT_FORMS (view->document),
1732 field, field_text->text);
1733 field->changed = FALSE;
1734 ev_view_reload_page (view, field->page->index, field_region);
1735 gdk_region_destroy (field_region);
1740 ev_view_form_field_text_changed (GtkWidget *widget,
1743 EvFormFieldText *field_text = EV_FORM_FIELD_TEXT (field);
1746 if (GTK_IS_ENTRY (widget)) {
1747 text = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
1748 } else if (GTK_IS_TEXT_BUFFER (widget)) {
1749 GtkTextIter start, end;
1751 gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (widget), &start, &end);
1752 text = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (widget),
1753 &start, &end, FALSE);
1756 if (!field_text->text ||
1757 (field_text->text && g_ascii_strcasecmp (field_text->text, text) != 0)) {
1758 g_free (field_text->text);
1759 field_text->text = text;
1760 field->changed = TRUE;
1765 ev_view_form_field_text_create_widget (EvView *view,
1768 EvFormFieldText *field_text = EV_FORM_FIELD_TEXT (field);
1769 GtkWidget *text = NULL;
1772 txt = ev_document_forms_form_field_text_get_text (EV_DOCUMENT_FORMS (view->document),
1775 switch (field_text->type) {
1776 case EV_FORM_FIELD_TEXT_FILE_SELECT:
1778 case EV_FORM_FIELD_TEXT_NORMAL:
1779 text = gtk_entry_new ();
1780 gtk_entry_set_has_frame (GTK_ENTRY (text), FALSE);
1781 gtk_entry_set_max_length (GTK_ENTRY (text), field_text->max_len);
1782 gtk_entry_set_visibility (GTK_ENTRY (text), !field_text->is_password);
1785 gtk_entry_set_text (GTK_ENTRY (text), txt);
1789 g_signal_connect (text, "changed",
1790 G_CALLBACK (ev_view_form_field_text_changed),
1792 g_signal_connect_after (text, "activate",
1793 G_CALLBACK (ev_view_form_field_destroy),
1796 case EV_FORM_FIELD_TEXT_MULTILINE: {
1797 GtkTextBuffer *buffer;
1799 text = gtk_text_view_new ();
1800 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text));
1803 gtk_text_buffer_set_text (buffer, txt, -1);
1807 g_signal_connect (buffer, "changed",
1808 G_CALLBACK (ev_view_form_field_text_changed),
1814 g_object_weak_ref (G_OBJECT (text),
1815 (GWeakNotify)ev_view_form_field_text_save,
1822 ev_view_form_field_choice_save (EvView *view,
1827 if (!view->document)
1830 field = g_object_get_data (G_OBJECT (widget), "form-field");
1832 if (field->changed) {
1834 EvFormFieldChoice *field_choice = EV_FORM_FIELD_CHOICE (field);
1835 GdkRegion *field_region;
1837 field_region = ev_view_form_field_get_region (view, field);
1839 if (field_choice->is_editable) {
1840 ev_document_forms_form_field_choice_set_text (EV_DOCUMENT_FORMS (view->document),
1841 field, field_choice->text);
1843 ev_document_forms_form_field_choice_unselect_all (EV_DOCUMENT_FORMS (view->document), field);
1844 for (l = field_choice->selected_items; l; l = g_list_next (l)) {
1845 ev_document_forms_form_field_choice_select_item (EV_DOCUMENT_FORMS (view->document),
1847 GPOINTER_TO_INT (l->data));
1850 field->changed = FALSE;
1851 ev_view_reload_page (view, field->page->index, field_region);
1852 gdk_region_destroy (field_region);
1857 ev_view_form_field_choice_changed (GtkWidget *widget,
1860 EvFormFieldChoice *field_choice = EV_FORM_FIELD_CHOICE (field);
1862 if (GTK_IS_COMBO_BOX (widget)) {
1865 item = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
1866 if (!field_choice->selected_items ||
1867 GPOINTER_TO_INT (field_choice->selected_items->data) != item) {
1868 g_list_free (field_choice->selected_items);
1869 field_choice->selected_items = NULL;
1870 field_choice->selected_items = g_list_prepend (field_choice->selected_items,
1871 GINT_TO_POINTER (item));
1872 field->changed = TRUE;
1875 if (GTK_IS_COMBO_BOX_ENTRY (widget)) {
1878 text = gtk_combo_box_get_active_text (GTK_COMBO_BOX (widget));
1879 if (!field_choice->text ||
1880 (field_choice->text && g_ascii_strcasecmp (field_choice->text, text) != 0)) {
1881 g_free (field_choice->text);
1882 field_choice->text = text;
1883 field->changed = TRUE;
1886 } else if (GTK_IS_TREE_SELECTION (widget)) {
1887 GtkTreeSelection *selection = GTK_TREE_SELECTION (widget);
1888 GtkTreeModel *model;
1891 items = gtk_tree_selection_get_selected_rows (selection, &model);
1892 g_list_free (field_choice->selected_items);
1893 field_choice->selected_items = NULL;
1895 for (l = items; l && l->data; l = g_list_next (l)) {
1897 GtkTreePath *path = (GtkTreePath *)l->data;
1900 gtk_tree_model_get_iter (model, &iter, path);
1901 gtk_tree_model_get (model, &iter, 1, &item, -1);
1903 field_choice->selected_items = g_list_prepend (field_choice->selected_items,
1904 GINT_TO_POINTER (item));
1906 gtk_tree_path_free (path);
1909 g_list_free (items);
1911 field->changed = TRUE;
1916 ev_view_form_field_choice_create_widget (EvView *view,
1919 EvFormFieldChoice *field_choice = EV_FORM_FIELD_CHOICE (field);
1921 GtkTreeModel *model;
1923 gint selected_item = 0;
1925 n_items = ev_document_forms_form_field_choice_get_n_items (EV_DOCUMENT_FORMS (view->document),
1927 model = GTK_TREE_MODEL (gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT));
1928 for (i = 0; i < n_items; i++) {
1932 item = ev_document_forms_form_field_choice_get_item (EV_DOCUMENT_FORMS (view->document),
1934 if (ev_document_forms_form_field_choice_is_item_selected (
1935 EV_DOCUMENT_FORMS (view->document), field, i)) {
1937 /* FIXME: we need a get_selected_items function in poppler */
1938 field_choice->selected_items = g_list_prepend (field_choice->selected_items,
1939 GINT_TO_POINTER (i));
1943 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1944 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1952 if (field_choice->type == EV_FORM_FIELD_CHOICE_LIST) {
1953 GtkCellRenderer *renderer;
1954 GtkWidget *tree_view;
1955 GtkTreeSelection *selection;
1957 tree_view = gtk_tree_view_new_with_model (model);
1958 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
1960 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
1961 if (field_choice->multi_select) {
1962 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1965 /* TODO: set selected items */
1967 renderer = gtk_cell_renderer_text_new ();
1968 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view),
1974 choice = gtk_scrolled_window_new (NULL, NULL);
1975 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (choice),
1976 GTK_POLICY_AUTOMATIC,
1977 GTK_POLICY_AUTOMATIC);
1978 gtk_container_add (GTK_CONTAINER (choice), tree_view);
1979 gtk_widget_show (tree_view);
1981 g_signal_connect (selection, "changed",
1982 G_CALLBACK (ev_view_form_field_choice_changed),
1984 g_signal_connect_after (selection, "changed",
1985 G_CALLBACK (ev_view_form_field_destroy),
1987 } else if (field_choice->is_editable) { /* ComboBoxEntry */
1990 choice = gtk_combo_box_entry_new_with_model (model, 0);
1991 text = ev_document_forms_form_field_choice_get_text (EV_DOCUMENT_FORMS (view->document), field);
1993 gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (choice))), text);
1997 g_signal_connect (choice, "changed",
1998 G_CALLBACK (ev_view_form_field_choice_changed),
2000 g_signal_connect_after (GTK_BIN(choice)->child, "activate",
2001 G_CALLBACK (ev_view_form_field_destroy),
2003 } else { /* ComboBoxText */
2004 GtkCellRenderer *renderer;
2006 choice = gtk_combo_box_new_with_model (model);
2007 renderer = gtk_cell_renderer_text_new ();
2008 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (choice),
2010 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (choice),
2014 gtk_combo_box_set_active (GTK_COMBO_BOX (choice), selected_item);
2015 gtk_combo_box_popup (GTK_COMBO_BOX (choice));
2017 g_signal_connect (choice, "changed",
2018 G_CALLBACK (ev_view_form_field_choice_changed),
2020 g_signal_connect_after (choice, "changed",
2021 G_CALLBACK (ev_view_form_field_destroy),
2025 g_object_unref (model);
2027 g_object_weak_ref (G_OBJECT (choice),
2028 (GWeakNotify)ev_view_form_field_choice_save,
2035 ev_view_handle_form_field (EvView *view,
2040 GtkWidget *field_widget = NULL;
2041 GList *form_field_mapping;
2043 GdkRectangle view_area;
2045 if (field->is_read_only)
2048 if (EV_IS_FORM_FIELD_BUTTON (field)) {
2049 field_widget = ev_view_form_field_button_create_widget (view, field);
2050 } else if (EV_IS_FORM_FIELD_TEXT (field)) {
2051 field_widget = ev_view_form_field_text_create_widget (view, field);
2052 } else if (EV_IS_FORM_FIELD_CHOICE (field)) {
2053 field_widget = ev_view_form_field_choice_create_widget (view, field);
2054 } else if (EV_IS_FORM_FIELD_SIGNATURE (field)) {
2058 /* Form field doesn't require a widget */
2062 g_object_set_data_full (G_OBJECT (field_widget), "form-field",
2063 g_object_ref (field),
2064 (GDestroyNotify)g_object_unref);
2066 form_field_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache, field->page->index);
2067 mapping = ev_mapping_list_find (form_field_mapping, field);
2069 doc_rect_to_view_rect (view, field->page->index, &mapping->area, &view_area);
2070 view_area.x -= view->scroll_x;
2071 view_area.y -= view->scroll_y;
2073 gtk_layout_put (GTK_LAYOUT (view), field_widget, view_area.x, view_area.y);
2074 gtk_widget_show (field_widget);
2075 gtk_widget_grab_focus (field_widget);
2079 static EvViewWindowChild *
2080 ev_view_get_window_child (EvView *view,
2083 GList *children = view->window_children;
2086 EvViewWindowChild *child;
2088 child = (EvViewWindowChild *)children->data;
2089 children = children->next;
2091 if (child->window == window)
2099 ev_view_window_child_move (EvView *view,
2100 EvViewWindowChild *child,
2106 gtk_window_move (GTK_WINDOW (child->window),
2107 MAX (child->parent_x, x),
2108 MAX (child->parent_y, y));
2112 ev_view_window_child_move_with_parent (EvView *view,
2115 EvViewWindowChild *child;
2116 gint root_x, root_y;
2118 child = ev_view_get_window_child (view, window);
2119 gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (view)),
2121 if (root_x != child->parent_x || root_y != child->parent_y) {
2122 gint dest_x, dest_y;
2124 dest_x = child->x + (root_x - child->parent_x);
2125 dest_y = child->y + (root_y - child->parent_y);
2126 child->parent_x = root_x;
2127 child->parent_y = root_y;
2128 ev_view_window_child_move (view, child, dest_x, dest_y);
2131 if (child->visible && !GTK_WIDGET_VISIBLE (window))
2132 gtk_widget_show (window);
2136 ev_view_window_child_put (EvView *view,
2144 EvViewWindowChild *child;
2145 gint root_x, root_y;
2147 gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (view)),
2150 child = g_new0 (EvViewWindowChild, 1);
2151 child->window = window;
2153 child->orig_x = orig_x;
2154 child->orig_y = orig_y;
2155 child->parent_x = root_x;
2156 child->parent_y = root_y;
2157 child->visible = ev_annotation_window_is_open (EV_ANNOTATION_WINDOW (window));
2158 ev_view_window_child_move (view, child, x + root_x, y + root_y);
2161 gtk_widget_show (window);
2163 gtk_widget_hide (window);
2165 view->window_children = g_list_append (view->window_children, child);
2168 static EvViewWindowChild *
2169 ev_view_find_window_child_for_annot (EvView *view,
2171 EvAnnotation *annot)
2173 GList *children = view->window_children;
2176 EvViewWindowChild *child;
2177 EvAnnotation *wannot;
2179 child = (EvViewWindowChild *)children->data;
2180 children = children->next;
2182 if (child->page != page)
2185 wannot = ev_annotation_window_get_annotation (EV_ANNOTATION_WINDOW (child->window));
2186 if (wannot == annot || strcmp (wannot->name, annot->name) == 0)
2194 ev_view_window_children_free (EvView *view)
2198 if (!view->window_children)
2201 for (l = view->window_children; l && l->data; l = g_list_next (l)) {
2202 EvViewWindowChild *child;
2204 child = (EvViewWindowChild *)l->data;
2205 gtk_widget_destroy (GTK_WIDGET (child->window));
2208 g_list_free (view->window_children);
2209 view->window_children = NULL;
2210 view->window_child_focus = NULL;
2214 annotation_window_grab_focus (GtkWidget *widget,
2217 if (view->window_child_focus)
2218 ev_annotation_window_ungrab_focus (EV_ANNOTATION_WINDOW (view->window_child_focus->window));
2219 view->window_child_focus = ev_view_get_window_child (view, widget);
2223 annotation_window_closed (EvAnnotationWindow *window,
2226 EvViewWindowChild *child;
2228 child = ev_view_get_window_child (view, GTK_WIDGET (window));
2229 child->visible = FALSE;
2233 annotation_window_moved (EvAnnotationWindow *window,
2238 EvViewWindowChild *child;
2239 GdkRectangle page_area;
2241 GdkRectangle view_rect;
2242 EvRectangle doc_rect;
2245 child = ev_view_get_window_child (view, GTK_WIDGET (window));
2246 if (child->x == x && child->y == y)
2249 child->moved = TRUE;
2253 /* Window has been moved by the user,
2254 * we have to set a new origin in doc coords
2256 gtk_window_get_size (GTK_WINDOW (window), &width, &height);
2257 view_rect.x = (x - child->parent_x) + view->scroll_x;
2258 view_rect.y = (y - child->parent_y) + view->scroll_y;
2259 view_rect.width = width;
2260 view_rect.height = height;
2262 get_page_extents (view, child->page, &page_area, &border);
2263 view_rect_to_doc_rect (view, &view_rect, &page_area, &doc_rect);
2264 child->orig_x = doc_rect.x1;
2265 child->orig_y = doc_rect.y1;
2269 ev_view_annotation_save (EvView *view,
2270 EvAnnotation *annot)
2272 if (!view->document)
2275 if (!annot->changed)
2278 ev_document_annotations_annotation_set_contents (EV_DOCUMENT_ANNOTATIONS (view->document),
2279 annot, annot->contents);
2280 annot->changed = FALSE;
2284 show_annotation_windows (EvView *view,
2290 parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view)));
2292 annots = ev_pixbuf_cache_get_annots_mapping (view->pixbuf_cache, page);
2294 for (l = annots; l && l->data; l = g_list_next (l)) {
2295 EvAnnotation *annot;
2296 EvViewWindowChild *child;
2298 EvRectangle *doc_rect;
2299 GdkRectangle view_rect;
2301 annot = ((EvMapping *)(l->data))->data;
2303 if (!EV_IS_ANNOTATION_MARKUP (annot))
2306 window = g_object_get_data (G_OBJECT (annot), "popup");
2308 ev_view_window_child_move_with_parent (view, window);
2312 /* Look if we already have a popup for this annot */
2313 child = ev_view_find_window_child_for_annot (view, page, annot);
2314 window = child ? child->window : NULL;
2316 ev_annotation_window_set_annotation (EV_ANNOTATION_WINDOW (window), annot);
2317 g_object_set_data (G_OBJECT (annot), "popup", window);
2318 ev_view_window_child_move_with_parent (view, window);
2320 window = ev_annotation_window_new (annot, parent);
2321 g_signal_connect (window, "grab_focus",
2322 G_CALLBACK (annotation_window_grab_focus),
2324 g_signal_connect (window, "closed",
2325 G_CALLBACK (annotation_window_closed),
2327 g_signal_connect (window, "moved",
2328 G_CALLBACK (annotation_window_moved),
2330 g_object_set_data (G_OBJECT (annot), "popup", window);
2332 doc_rect = (EvRectangle *)ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (window));
2333 doc_rect_to_view_rect (view, page, doc_rect, &view_rect);
2334 view_rect.x -= view->scroll_x;
2335 view_rect.y -= view->scroll_y;
2337 ev_view_window_child_put (view, window, page,
2338 view_rect.x, view_rect.y,
2339 doc_rect->x1, doc_rect->y1);
2341 g_object_weak_ref (G_OBJECT (annot),
2342 (GWeakNotify)ev_view_annotation_save,
2349 hide_annotation_windows (EvView *view,
2354 annots = ev_pixbuf_cache_get_annots_mapping (view->pixbuf_cache, page);
2356 for (l = annots; l && l->data; l = g_list_next (l)) {
2357 EvAnnotation *annot;
2360 annot = ((EvMapping *)(l->data))->data;
2362 if (!EV_IS_ANNOTATION_MARKUP (annot))
2365 window = g_object_get_data (G_OBJECT (annot), "popup");
2367 gtk_widget_hide (window);
2371 static EvAnnotation *
2372 ev_view_get_annotation_at_location (EvView *view,
2377 gint x_new = 0, y_new = 0;
2378 GList *annotations_mapping;
2380 if (!EV_IS_DOCUMENT_ANNOTATIONS (view->document))
2383 if (!get_doc_point_from_location (view, x, y, &page, &x_new, &y_new))
2386 annotations_mapping = ev_pixbuf_cache_get_annots_mapping (view->pixbuf_cache, page);
2388 if (annotations_mapping)
2389 return ev_mapping_list_get_data (annotations_mapping, x_new, y_new);
2395 ev_view_handle_annotation (EvView *view,
2396 EvAnnotation *annot,
2400 if (EV_IS_ANNOTATION_MARKUP (annot)) {
2403 window = g_object_get_data (G_OBJECT (annot), "popup");
2405 EvViewWindowChild *child;
2407 child = ev_view_get_window_child (view, window);
2408 if (!child->visible) {
2409 child->visible = TRUE;
2410 ev_view_window_child_move (view, child, child->x, child->y);
2411 gtk_widget_show (window);
2417 /*** GtkWidget implementation ***/
2420 ev_view_size_request_continuous_dual_page (EvView *view,
2421 GtkRequisition *requisition)
2427 ev_page_cache_get_max_width (view->page_cache, view->rotation,
2428 view->scale, &max_width);
2429 compute_border (view, max_width, max_width, &border);
2431 n_pages = ev_page_cache_get_n_pages (view->page_cache) + 1;
2433 requisition->width = (max_width + border.left + border.right) * 2 + (view->spacing * 3);
2434 get_page_y_offset (view, n_pages, view->scale, &requisition->height);
2436 if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
2437 requisition->width = 1;
2438 } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
2439 requisition->width = 1;
2440 /* FIXME: This could actually be set on one page docs or docs
2441 * with a strange aspect ratio. */
2442 /* requisition->height = 1;*/
2447 ev_view_size_request_continuous (EvView *view,
2448 GtkRequisition *requisition)
2455 ev_page_cache_get_max_width (view->page_cache, view->rotation,
2456 view->scale, &max_width);
2457 n_pages = ev_page_cache_get_n_pages (view->page_cache);
2458 compute_border (view, max_width, max_width, &border);
2460 requisition->width = max_width + (view->spacing * 2) + border.left + border.right;
2461 get_page_y_offset (view, n_pages, view->scale, &requisition->height);
2463 if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
2464 requisition->width = 1;
2465 } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
2466 requisition->width = 1;
2467 /* FIXME: This could actually be set on one page docs or docs
2468 * with a strange aspect ratio. */
2469 /* requisition->height = 1;*/
2474 ev_view_size_request_dual_page (EvView *view,
2475 GtkRequisition *requisition)
2480 /* Find the largest of the two. */
2481 ev_page_cache_get_size (view->page_cache,
2486 if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache)) {
2487 gint width_2, height_2;
2488 ev_page_cache_get_size (view->page_cache,
2489 view->current_page + 1,
2492 &width_2, &height_2);
2493 if (width_2 > width) {
2498 compute_border (view, width, height, &border);
2500 requisition->width = ((width + border.left + border.right) * 2) +
2501 (view->spacing * 3);
2502 requisition->height = (height + border.top + border.bottom) +
2503 (view->spacing * 2);
2505 if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
2506 requisition->width = 1;
2507 } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
2508 requisition->width = 1;
2509 requisition->height = 1;
2514 ev_view_size_request_single_page (EvView *view,
2515 GtkRequisition *requisition)
2520 ev_page_cache_get_size (view->page_cache,
2525 compute_border (view, width, height, &border);
2527 requisition->width = width + border.left + border.right + (2 * view->spacing);
2528 requisition->height = height + border.top + border.bottom + (2 * view->spacing);
2530 if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
2531 requisition->width = 1;
2532 requisition->height = height + border.top + border.bottom + (2 * view->spacing);
2533 } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
2534 requisition->width = 1;
2535 requisition->height = 1;
2540 ev_view_size_request (GtkWidget *widget,
2541 GtkRequisition *requisition)
2543 EvView *view = EV_VIEW (widget);
2545 if (view->document == NULL) {
2546 requisition->width = 1;
2547 requisition->height = 1;
2551 if (view->presentation) {
2552 requisition->width = 1;
2553 requisition->height = 1;
2557 if (view->continuous && view->dual_page)
2558 ev_view_size_request_continuous_dual_page (view, requisition);
2559 else if (view->continuous)
2560 ev_view_size_request_continuous (view, requisition);
2561 else if (view->dual_page)
2562 ev_view_size_request_dual_page (view, requisition);
2564 ev_view_size_request_single_page (view, requisition);
2568 ev_view_size_allocate (GtkWidget *widget,
2569 GtkAllocation *allocation)
2571 EvView *view = EV_VIEW (widget);
2572 GList *children, *l;
2573 gint root_x, root_y;
2575 GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
2577 if (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
2578 view->sizing_mode == EV_SIZING_BEST_FIT) {
2579 g_signal_emit (view, signals[SIGNAL_ZOOM_INVALID], 0);
2580 ev_view_size_request (widget, &widget->requisition);
2583 view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
2584 view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
2587 view_update_range_and_current_page (view);
2589 view->pending_scroll = SCROLL_TO_KEEP_POSITION;
2590 view->pending_resize = FALSE;
2592 children = gtk_container_get_children (GTK_CONTAINER (widget));
2593 for (l = children; l && l->data; l = g_list_next (l)) {
2595 GdkRectangle view_area;
2596 GList *form_field_mapping;
2598 GtkAllocation child_allocation;
2599 GtkRequisition child_requisition;
2600 GtkWidget *child = (GtkWidget *)l->data;
2602 field = g_object_get_data (G_OBJECT (child), "form-field");
2606 form_field_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache,
2607 field->page->index);
2608 mapping = ev_mapping_list_find (form_field_mapping, field);
2610 doc_rect_to_view_rect (view, field->page->index, &mapping->area, &view_area);
2611 view_area.x -= view->scroll_x;
2612 view_area.y -= view->scroll_y;
2614 gtk_widget_size_request (child, &child_requisition);
2615 if (child_requisition.width != view_area.width ||
2616 child_requisition.height != view_area.height)
2617 gtk_widget_set_size_request (child, view_area.width, view_area.height);
2619 gtk_container_child_get (GTK_CONTAINER (widget),
2621 "x", &child_allocation.x,
2622 "y", &child_allocation.y,
2624 if (child_allocation.x != view_area.x ||
2625 child_allocation.y != view_area.y) {
2626 gtk_layout_move (GTK_LAYOUT (widget), child, view_area.x, view_area.y);
2629 g_list_free (children);
2631 if (view->window_children)
2632 gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (view)),
2635 for (l = view->window_children; l && l->data; l = g_list_next (l)) {
2636 EvViewWindowChild *child;
2637 EvRectangle doc_rect;
2638 GdkRectangle view_rect;
2640 child = (EvViewWindowChild *)l->data;
2642 doc_rect = *ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (child->window));
2644 doc_rect.x1 = child->orig_x;
2645 doc_rect.y1 = child->orig_y;
2647 doc_rect_to_view_rect (view, child->page, &doc_rect, &view_rect);
2648 view_rect.x -= view->scroll_x;
2649 view_rect.y -= view->scroll_y;
2651 if (view_rect.x != child->orig_x || view_rect.y != child->orig_y) {
2652 child->parent_x = root_x;
2653 child->parent_y = root_y;
2654 ev_view_window_child_move (view, child, view_rect.x + root_x, view_rect.y + root_y);
2660 ev_view_realize (GtkWidget *widget)
2662 EvView *view = EV_VIEW (widget);
2664 if (GTK_WIDGET_CLASS (ev_view_parent_class)->realize)
2665 (* GTK_WIDGET_CLASS (ev_view_parent_class)->realize) (widget);
2667 gdk_window_set_events (view->layout.bin_window,
2668 (gdk_window_get_events (view->layout.bin_window) |
2670 GDK_BUTTON_PRESS_MASK |
2671 GDK_BUTTON_RELEASE_MASK |
2673 GDK_KEY_PRESS_MASK |
2674 GDK_POINTER_MOTION_MASK |
2675 GDK_POINTER_MOTION_HINT_MASK |
2676 GDK_ENTER_NOTIFY_MASK |
2677 GDK_LEAVE_NOTIFY_MASK));
2679 if (view->presentation)
2680 gdk_window_set_background (view->layout.bin_window, &widget->style->black);
2682 gdk_window_set_background (view->layout.bin_window, &widget->style->mid [GTK_STATE_NORMAL]);
2686 ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event)
2688 EvView *view = EV_VIEW (widget);
2691 state = event->state & gtk_accelerator_get_default_mod_mask ();
2693 if (state == GDK_CONTROL_MASK && view->presentation == FALSE) {
2694 ev_view_set_sizing_mode (view, EV_SIZING_FREE);
2696 if (event->direction == GDK_SCROLL_UP ||
2697 event->direction == GDK_SCROLL_LEFT) {
2698 if (ev_view_can_zoom_in (view)) {
2699 ev_view_zoom_in (view);
2702 if (ev_view_can_zoom_out (view)) {
2703 ev_view_zoom_out (view);
2710 view->jump_to_find_result = FALSE;
2712 /* Shift+Wheel scrolls the in the perpendicular direction */
2713 if (state & GDK_SHIFT_MASK) {
2714 if (event->direction == GDK_SCROLL_UP)
2715 event->direction = GDK_SCROLL_LEFT;
2716 else if (event->direction == GDK_SCROLL_LEFT)
2717 event->direction = GDK_SCROLL_UP;
2718 else if (event->direction == GDK_SCROLL_DOWN)
2719 event->direction = GDK_SCROLL_RIGHT;
2720 else if (event->direction == GDK_SCROLL_RIGHT)
2721 event->direction = GDK_SCROLL_DOWN;
2723 event->state &= ~GDK_SHIFT_MASK;
2724 state &= ~GDK_SHIFT_MASK;
2727 if (state == 0 && view->presentation) {
2728 switch (event->direction) {
2729 case GDK_SCROLL_DOWN:
2730 case GDK_SCROLL_RIGHT:
2731 ev_view_next_page (view);
2734 case GDK_SCROLL_LEFT:
2735 ev_view_previous_page (view);
2745 static EvViewSelection *
2746 find_selection_for_page (EvView *view,
2751 for (list = view->selection_info.selections; list != NULL; list = list->next) {
2752 EvViewSelection *selection;
2754 selection = (EvViewSelection *) list->data;
2756 if (selection->page == page)
2764 draw_end_presentation_page (EvView *view,
2765 GdkRectangle *page_area)
2767 PangoLayout *layout;
2768 PangoFontDescription *font_desc;
2770 const gchar *text = _("End of presentation. Press Escape to exit.");
2772 if (view->presentation_state != EV_PRESENTATION_END)
2775 layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), NULL);
2776 markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", text);
2777 pango_layout_set_markup (layout, markup, -1);
2780 font_desc = pango_font_description_new ();
2781 pango_font_description_set_size (font_desc, 16 * PANGO_SCALE);
2782 pango_layout_set_font_description (layout, font_desc);
2784 gtk_paint_layout (GTK_WIDGET (view)->style,
2785 view->layout.bin_window,
2786 GTK_WIDGET_STATE (view),
2795 pango_font_description_free (font_desc);
2796 g_object_unref (layout);
2800 ev_view_expose_event (GtkWidget *widget,
2801 GdkEventExpose *event)
2803 EvView *view = EV_VIEW (widget);
2807 if (view->animation) {
2808 if (ev_transition_animation_ready (view->animation)) {
2809 GdkRectangle page_area;
2812 if (get_page_extents (view, view->current_page, &page_area, &border)) {
2813 cr = gdk_cairo_create (view->layout.bin_window);
2815 /* normalize to x=0, y=0 */
2816 cairo_translate (cr, page_area.x, page_area.y);
2817 page_area.x = page_area.y = 0;
2819 ev_transition_animation_paint (view->animation, cr, page_area);
2827 if (view->presentation) {
2828 switch (view->presentation_state) {
2829 case EV_PRESENTATION_END: {
2830 GdkRectangle area = {0};
2832 area.width = widget->allocation.width;
2833 area.height = widget->allocation.height;
2835 draw_end_presentation_page (view, &area);
2838 case EV_PRESENTATION_BLACK:
2839 case EV_PRESENTATION_WHITE:
2841 case EV_PRESENTATION_NORMAL:
2845 } else if (view->loading) {
2846 GdkRectangle area = {0};
2848 area.width = widget->allocation.width;
2849 area.height = widget->allocation.height;
2851 draw_loading_text (view,
2856 if (view->document == NULL)
2859 cr = gdk_cairo_create (view->layout.bin_window);
2861 for (i = view->start_page; i <= view->end_page; i++) {
2862 GdkRectangle page_area;
2864 gboolean page_ready = TRUE;
2866 if (!get_page_extents (view, i, &page_area, &border))
2869 page_area.x -= view->scroll_x;
2870 page_area.y -= view->scroll_y;
2872 draw_one_page (view, i, cr, &page_area, &border, &(event->area), &page_ready);
2874 if (page_ready && view->find_pages && view->highlight_find_results)
2875 highlight_find_results (view, i);
2876 if (page_ready && EV_IS_DOCUMENT_ANNOTATIONS (view->document))
2877 show_annotation_windows (view, i);
2882 if (GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event)
2883 (* GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event) (widget, event);
2889 ev_view_do_popup_menu (EvView *view,
2896 image = ev_view_get_image_at_location (view, x, y);
2898 g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, image);
2902 link = ev_view_get_link_at_location (view, x, y);
2904 g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, link);
2908 g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, NULL);
2914 ev_view_popup_menu (GtkWidget *widget)
2918 gtk_widget_get_pointer (widget, &x, &y);
2919 return ev_view_do_popup_menu (EV_VIEW (widget), x, y);
2923 get_link_area (EvView *view,
2929 GList *link_mapping;
2932 gint x_offset = 0, y_offset = 0;
2934 x += view->scroll_x;
2935 y += view->scroll_y;
2937 find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
2939 link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page);
2940 mapping = ev_mapping_list_find (link_mapping, link);
2942 doc_rect_to_view_rect (view, page, &mapping->area, area);
2943 area->y -= view->scroll_y;
2947 get_annot_area (EvView *view,
2950 EvAnnotation *annot,
2953 GList *annots_mapping;
2956 gint x_offset = 0, y_offset = 0;
2958 x += view->scroll_x;
2959 y += view->scroll_y;
2961 find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
2963 annots_mapping = ev_pixbuf_cache_get_annots_mapping (view->pixbuf_cache, page);
2964 mapping = ev_mapping_list_find (annots_mapping, annot);
2966 doc_rect_to_view_rect (view, page, &mapping->area, area);
2967 area->y -= view->scroll_y;
2971 ev_view_query_tooltip (GtkWidget *widget,
2974 gboolean keyboard_tip,
2975 GtkTooltip *tooltip)
2977 EvView *view = EV_VIEW (widget);
2979 EvAnnotation *annot;
2982 annot = ev_view_get_annotation_at_location (view, x, y);
2983 if (annot && annot->contents) {
2984 GdkRectangle annot_area;
2986 get_annot_area (view, x, y, annot, &annot_area);
2987 gtk_tooltip_set_text (tooltip, annot->contents);
2988 gtk_tooltip_set_tip_area (tooltip, &annot_area);
2993 link = ev_view_get_link_at_location (view, x, y);
2997 text = tip_from_link (view, link);
2998 if (text && g_utf8_validate (text, -1, NULL)) {
2999 GdkRectangle link_area;
3001 get_link_area (view, x, y, link, &link_area);
3002 gtk_tooltip_set_text (tooltip, text);
3003 gtk_tooltip_set_tip_area (tooltip, &link_area);
3011 start_selection_for_event (EvView *view,
3012 GdkEventButton *event)
3014 EvSelectionStyle style;
3016 clear_selection (view);
3018 view->selection_info.start.x = event->x + view->scroll_x;
3019 view->selection_info.start.y = event->y + view->scroll_y;
3021 switch (event->type) {
3022 case GDK_2BUTTON_PRESS:
3023 style = EV_SELECTION_STYLE_WORD;
3025 case GDK_3BUTTON_PRESS:
3026 style = EV_SELECTION_STYLE_LINE;
3029 style = EV_SELECTION_STYLE_GLYPH;
3033 view->selection_info.style = style;
3037 ev_view_button_press_event (GtkWidget *widget,
3038 GdkEventButton *event)
3040 EvView *view = EV_VIEW (widget);
3042 if (!view->document)
3045 if (!GTK_WIDGET_HAS_FOCUS (widget)) {
3046 gtk_widget_grab_focus (widget);
3049 if (view->window_child_focus) {
3050 EvAnnotationWindow *window;
3051 EvAnnotation *annot;
3053 window = EV_ANNOTATION_WINDOW (view->window_child_focus->window);
3054 annot = ev_annotation_window_get_annotation (window);
3055 ev_annotation_window_ungrab_focus (window);
3056 ev_view_annotation_save (view, annot);
3057 view->window_child_focus = NULL;
3060 view->pressed_button = event->button;
3061 view->selection_info.in_drag = FALSE;
3063 if (view->scroll_info.autoscrolling)
3066 switch (event->button) {
3069 EvAnnotation *annot;
3072 if (EV_IS_SELECTION (view->document) && view->selection_info.selections) {
3073 if (event->type == GDK_3BUTTON_PRESS) {
3074 start_selection_for_event (view, event);
3075 } else if (location_in_selected_text (view,
3076 event->x + view->scroll_x,
3077 event->y + view->scroll_y)) {
3078 view->selection_info.in_drag = TRUE;
3080 start_selection_for_event (view, event);
3083 gtk_widget_queue_draw (widget);
3084 } else if ((annot = ev_view_get_annotation_at_location (view, event->x, event->y))) {
3085 ev_view_handle_annotation (view, annot, event->x, event->y);
3086 } else if ((field = ev_view_get_form_field_at_location (view, event->x, event->y))) {
3087 ev_view_remove_all (view);
3088 ev_view_handle_form_field (view, field, event->x, event->y);
3089 } else if (!location_in_text (view, event->x + view->scroll_x, event->y + view->scroll_y) &&
3090 (image = ev_view_get_image_at_location (view, event->x, event->y))) {
3091 if (view->image_dnd_info.image)
3092 g_object_unref (view->image_dnd_info.image);
3093 view->image_dnd_info.image = g_object_ref (image);
3094 view->image_dnd_info.in_drag = TRUE;
3096 view->image_dnd_info.start.x = event->x + view->scroll_x;
3097 view->image_dnd_info.start.y = event->y + view->scroll_y;
3099 ev_view_remove_all (view);
3101 if (EV_IS_SELECTION (view->document))
3102 start_selection_for_event (view, event);
3107 /* use root coordinates as reference point because
3108 * scrolling changes window relative coordinates */
3109 view->drag_info.start.x = event->x_root;
3110 view->drag_info.start.y = event->y_root;
3111 view->drag_info.hadj = gtk_adjustment_get_value (view->hadjustment);
3112 view->drag_info.vadj = gtk_adjustment_get_value (view->vadjustment);
3114 ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG);
3118 view->scroll_info.start_y = event->y;
3119 return ev_view_do_popup_menu (view, event->x, event->y);
3126 ev_view_remove_all (EvView *view)
3128 GList *children, *child;
3130 children = gtk_container_get_children (GTK_CONTAINER (view));
3131 for (child = children; child && child->data; child = g_list_next (child)) {
3132 gtk_container_remove (GTK_CONTAINER (view),
3133 GTK_WIDGET (child->data));
3135 g_list_free (children);
3138 /*** Drag and Drop ***/
3140 ev_view_drag_data_get (GtkWidget *widget,
3141 GdkDragContext *context,
3142 GtkSelectionData *selection_data,
3146 EvView *view = EV_VIEW (widget);
3149 case TARGET_DND_TEXT:
3150 if (EV_IS_SELECTION (view->document) &&
3151 view->selection_info.selections) {
3154 text = get_selected_text (view);
3155 gtk_selection_data_set_text (selection_data,
3161 case TARGET_DND_IMAGE:
3162 if (view->image_dnd_info.image) {
3165 ev_document_doc_mutex_lock ();
3166 pixbuf = ev_document_images_get_image (EV_DOCUMENT_IMAGES (view->document),
3167 view->image_dnd_info.image);
3168 ev_document_doc_mutex_unlock ();
3170 gtk_selection_data_set_pixbuf (selection_data, pixbuf);
3171 g_object_unref (pixbuf);
3174 case TARGET_DND_URI:
3175 if (view->image_dnd_info.image) {
3177 const gchar *tmp_uri;
3180 ev_document_doc_mutex_lock ();
3181 pixbuf = ev_document_images_get_image (EV_DOCUMENT_IMAGES (view->document),
3182 view->image_dnd_info.image);
3183 ev_document_doc_mutex_unlock ();
3185 tmp_uri = ev_image_save_tmp (view->image_dnd_info.image, pixbuf);
3186 g_object_unref (pixbuf);
3188 uris[0] = (gchar *)tmp_uri;
3190 gtk_selection_data_set_uris (selection_data, uris);
3196 ev_view_drag_motion (GtkWidget *widget,
3197 GdkDragContext *context,
3202 if (gtk_drag_get_source_widget (context) == widget)
3203 gdk_drag_status (context, 0, time);
3205 gdk_drag_status (context, context->suggested_action, time);
3211 selection_update_idle_cb (EvView *view)
3213 compute_selections (view,
3214 view->selection_info.style,
3215 &view->selection_info.start,
3217 view->selection_update_id = 0;
3222 selection_scroll_timeout_cb (EvView *view)
3224 gint x, y, shift = 0;
3225 GtkWidget *widget = GTK_WIDGET (view);
3227 gtk_widget_get_pointer (widget, &x, &y);
3229 if (y > widget->allocation.height) {
3230 shift = (y - widget->allocation.height) / 2;
3236 gtk_adjustment_set_value (view->vadjustment,
3237 CLAMP (view->vadjustment->value + shift,
3238 view->vadjustment->lower,
3239 view->vadjustment->upper -
3240 view->vadjustment->page_size));
3242 if (x > widget->allocation.width) {
3243 shift = (x - widget->allocation.width) / 2;
3249 gtk_adjustment_set_value (view->hadjustment,
3250 CLAMP (view->hadjustment->value + shift,
3251 view->hadjustment->lower,
3252 view->hadjustment->upper -
3253 view->hadjustment->page_size));
3259 ev_view_drag_update_momentum (EvView *view)
3262 if (!view->drag_info.in_drag)
3265 for (i = DRAG_HISTORY - 1; i > 0; i--) {
3266 view->drag_info.buffer[i].x = view->drag_info.buffer[i-1].x;
3267 view->drag_info.buffer[i].y = view->drag_info.buffer[i-1].y;
3270 /* Momentum is a moving average of 10ms granularity over
3271 * the last 100ms with each 10ms stored in buffer.
3274 view->drag_info.momentum.x = (view->drag_info.buffer[DRAG_HISTORY - 1].x - view->drag_info.buffer[0].x);
3275 view->drag_info.momentum.y = (view->drag_info.buffer[DRAG_HISTORY - 1].y - view->drag_info.buffer[0].y);
3281 ev_view_scroll_drag_release (EvView *view)
3283 gdouble dhadj_value, dvadj_value;
3284 gdouble oldhadjustment, oldvadjustment;
3286 view->drag_info.momentum.x /= 1.2;
3287 view->drag_info.momentum.y /= 1.2; /* Alter these constants to change "friction" */
3289 dhadj_value = view->hadjustment->page_size *
3290 (gdouble)view->drag_info.momentum.x / GTK_WIDGET (view)->allocation.width;
3291 dvadj_value = view->vadjustment->page_size *
3292 (gdouble)view->drag_info.momentum.y / GTK_WIDGET (view)->allocation.height;
3294 oldhadjustment = gtk_adjustment_get_value (view->hadjustment);
3295 oldvadjustment = gtk_adjustment_get_value (view->vadjustment);
3297 /* When we reach the edges, we need either to absorb some momentum and bounce by
3298 * multiplying it on -0.5 or stop scrolling by setting momentum to 0. */
3299 if (((oldhadjustment + dhadj_value) > (view->hadjustment->upper - view->hadjustment->page_size)) ||
3300 ((oldhadjustment + dhadj_value) < 0))
3301 view->drag_info.momentum.x = 0;
3302 if (((oldvadjustment + dvadj_value) > (view->vadjustment->upper - view->vadjustment->page_size)) ||
3303 ((oldvadjustment + dvadj_value) < 0))
3304 view->drag_info.momentum.y = 0;
3306 gtk_adjustment_set_value (view->hadjustment,
3307 MIN (oldhadjustment + dhadj_value,
3308 view->hadjustment->upper - view->hadjustment->page_size));
3309 gtk_adjustment_set_value (view->vadjustment,
3310 MIN (oldvadjustment + dvadj_value,
3311 view->vadjustment->upper - view->vadjustment->page_size));
3313 if (((view->drag_info.momentum.x < 1) && (view->drag_info.momentum.x > -1)) &&
3314 ((view->drag_info.momentum.y < 1) && (view->drag_info.momentum.y > -1)))
3321 ev_view_motion_notify_event (GtkWidget *widget,
3322 GdkEventMotion *event)
3324 EvView *view = EV_VIEW (widget);
3327 if (!view->document)
3331 if (event->is_hint || event->window != view->layout.bin_window) {
3332 gtk_widget_get_pointer (widget, &x, &y);
3338 if (view->scroll_info.autoscrolling) {
3339 view->scroll_info.last_y = y;
3343 if (view->selection_info.in_drag) {
3344 if (gtk_drag_check_threshold (widget,
3345 view->selection_info.start.x,
3346 view->selection_info.start.y,
3348 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
3350 gtk_target_list_add_text_targets (target_list, TARGET_DND_TEXT);
3352 gtk_drag_begin (widget, target_list,
3354 1, (GdkEvent *)event);
3356 view->selection_info.in_drag = FALSE;
3358 gtk_target_list_unref (target_list);
3362 } else if (view->image_dnd_info.in_drag) {
3363 if (gtk_drag_check_threshold (widget,
3364 view->selection_info.start.x,
3365 view->selection_info.start.y,
3367 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
3369 gtk_target_list_add_uri_targets (target_list, TARGET_DND_URI);
3370 gtk_target_list_add_image_targets (target_list, TARGET_DND_IMAGE, TRUE);
3372 gtk_drag_begin (widget, target_list,
3374 1, (GdkEvent *)event);
3376 view->image_dnd_info.in_drag = FALSE;
3378 gtk_target_list_unref (target_list);
3384 switch (view->pressed_button) {
3386 /* For the Evince 0.4.x release, we limit selection to un-rotated
3389 if (view->rotation != 0)
3392 /* Schedule timeout to scroll during selection and additionally
3393 * scroll once to allow arbitrary speed. */
3394 if (!view->selection_scroll_id)
3395 view->selection_scroll_id = g_timeout_add (SCROLL_TIME,
3396 (GSourceFunc)selection_scroll_timeout_cb,
3399 selection_scroll_timeout_cb (view);
3401 view->selection_info.in_selection = TRUE;
3402 view->motion.x = x + view->scroll_x;
3403 view->motion.y = y + view->scroll_y;
3405 /* Queue an idle to handle the motion. We do this because
3406 * handling any selection events in the motion could be slower
3407 * than new motion events reach us. We always put it in the
3408 * idle to make sure we catch up and don't visibly lag the
3410 if (!view->selection_update_id)
3411 view->selection_update_id = g_idle_add ((GSourceFunc)selection_update_idle_cb, view);
3415 if (!view->drag_info.in_drag) {
3419 start = gtk_drag_check_threshold (widget,
3420 view->drag_info.start.x,
3421 view->drag_info.start.y,
3424 view->drag_info.in_drag = start;
3425 view->drag_info.drag_timeout_id = g_timeout_add (10,
3426 (GSourceFunc)ev_view_drag_update_momentum, view);
3427 /* Set 100 to choose how long it takes to build up momentum */
3428 /* Clear out previous momentum info: */
3429 for (i = 0; i < DRAG_HISTORY; i++) {
3430 view->drag_info.buffer[i].x = event->x;
3431 view->drag_info.buffer[i].y = event->y;
3433 view->drag_info.momentum.x = 0;
3434 view->drag_info.momentum.y = 0;
3437 if (view->drag_info.in_drag) {
3439 gdouble dhadj_value, dvadj_value;
3441 view->drag_info.buffer[0].x = event->x;
3442 view->drag_info.buffer[0].y = event->y;
3444 dx = event->x_root - view->drag_info.start.x;
3445 dy = event->y_root - view->drag_info.start.y;
3447 dhadj_value = view->hadjustment->page_size *
3448 (gdouble)dx / widget->allocation.width;
3449 dvadj_value = view->vadjustment->page_size *
3450 (gdouble)dy / widget->allocation.height;
3452 /* clamp scrolling to visible area */
3453 gtk_adjustment_set_value (view->hadjustment,
3454 MIN(view->drag_info.hadj - dhadj_value,
3455 view->hadjustment->upper -
3456 view->hadjustment->page_size));
3457 gtk_adjustment_set_value (view->vadjustment,
3458 MIN(view->drag_info.vadj - dvadj_value,
3459 view->vadjustment->upper -
3460 view->vadjustment->page_size));
3467 ev_view_handle_cursor_over_xy (view, x, y);
3474 ev_view_button_release_event (GtkWidget *widget,
3475 GdkEventButton *event)
3477 EvView *view = EV_VIEW (widget);
3478 EvLink *link = NULL;
3480 view->image_dnd_info.in_drag = FALSE;
3482 if (view->scroll_info.autoscrolling) {
3483 ev_view_autoscroll_stop (view);
3484 view->pressed_button = -1;
3489 if (view->drag_info.in_drag) {
3490 view->drag_info.release_timeout_id =
3492 (GSourceFunc)ev_view_scroll_drag_release, view);
3495 if (view->document && !view->drag_info.in_drag && view->pressed_button != 3) {
3496 link = ev_view_get_link_at_location (view, event->x, event->y);
3499 view->drag_info.in_drag = FALSE;
3501 if (view->pressed_button == 2) {
3502 ev_view_handle_cursor_over_xy (view, event->x, event->y);
3505 view->pressed_button = -1;
3507 if (view->selection_scroll_id) {
3508 g_source_remove (view->selection_scroll_id);
3509 view->selection_scroll_id = 0;
3511 if (view->selection_update_id) {
3512 g_source_remove (view->selection_update_id);
3513 view->selection_update_id = 0;
3516 if (!view->selection_info.in_selection &&
3517 view->selection_info.style != EV_SELECTION_STYLE_GLYPH) {
3518 compute_selections (view,
3519 view->selection_info.style,
3520 &(view->selection_info.start),
3521 &(view->selection_info.start));
3524 if (view->selection_info.selections) {
3525 clear_link_selected (view);
3526 ev_view_update_primary_selection (view);
3528 if (view->selection_info.in_drag) {
3529 clear_selection (view);
3530 gtk_widget_queue_draw (widget);
3533 view->selection_info.in_drag = FALSE;
3535 if (event->button == 2) {
3536 EvLinkAction *action;
3537 EvLinkActionType type;
3539 action = ev_link_get_action (link);
3543 type = ev_link_action_get_action_type (action);
3544 if (type == EV_LINK_ACTION_TYPE_GOTO_DEST) {
3545 g_signal_emit (view,
3546 signals[SIGNAL_EXTERNAL_LINK],
3550 ev_view_handle_link (view, link);
3552 } else if (view->presentation) {
3553 switch (event->button) {
3555 ev_view_next_page (view);
3558 ev_view_previous_page (view);
3567 /* Cut and paste from gtkwindow.c */
3569 send_focus_change (GtkWidget *widget,
3572 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
3574 g_object_ref (widget);
3577 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
3579 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
3581 fevent->focus_change.type = GDK_FOCUS_CHANGE;
3582 fevent->focus_change.window = g_object_ref (widget->window);
3583 fevent->focus_change.in = in;
3585 gtk_widget_event (widget, fevent);
3587 g_object_notify (G_OBJECT (widget), "has-focus");
3589 g_object_unref (widget);
3590 gdk_event_free (fevent);
3594 ev_view_goto_window_hide (EvView *view)
3596 /* send focus-in event */
3597 send_focus_change (view->goto_entry, FALSE);
3598 gtk_widget_hide (view->goto_window);
3599 gtk_entry_set_text (GTK_ENTRY (view->goto_entry), "");
3603 ev_view_goto_window_delete_event (GtkWidget *widget,
3607 ev_view_goto_window_hide (view);
3613 key_is_numeric (guint keyval)
3615 return ((keyval >= GDK_0 && keyval <= GDK_9) ||
3616 (keyval >= GDK_KP_0 && keyval <= GDK_KP_9));
3620 ev_view_goto_window_key_press_event (GtkWidget *widget,
3624 switch (event->keyval) {
3628 case GDK_ISO_Left_Tab:
3629 ev_view_goto_window_hide (view);
3638 if (!key_is_numeric (event->keyval))
3646 ev_view_goto_window_button_press_event (GtkWidget *widget,
3647 GdkEventButton *event,
3650 ev_view_goto_window_hide (view);
3656 ev_view_goto_entry_activate (GtkEntry *entry,
3662 text = gtk_entry_get_text (entry);
3663 page = atoi (text) - 1;
3665 ev_view_goto_window_hide (view);
3668 page < ev_page_cache_get_n_pages (view->page_cache))
3669 ev_page_cache_set_current_page (view->page_cache, page);
3673 ev_view_goto_window_create (EvView *view)
3675 GtkWidget *frame, *hbox, *toplevel, *label;
3677 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
3679 if (view->goto_window) {
3680 if (GTK_WINDOW (toplevel)->group)
3681 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
3682 GTK_WINDOW (view->goto_window));
3683 else if (GTK_WINDOW (view->goto_window)->group)
3684 gtk_window_group_remove_window (GTK_WINDOW (view->goto_window)->group,
3685 GTK_WINDOW (view->goto_window));
3689 view->goto_window = gtk_window_new (GTK_WINDOW_POPUP);
3690 gtk_window_set_screen (GTK_WINDOW (view->goto_window),
3691 gtk_widget_get_screen (GTK_WIDGET (view)));
3693 if (GTK_WINDOW (toplevel)->group)
3694 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
3695 GTK_WINDOW (view->goto_window));
3697 gtk_window_set_modal (GTK_WINDOW (view->goto_window), TRUE);
3699 g_signal_connect (view->goto_window, "delete_event",
3700 G_CALLBACK (ev_view_goto_window_delete_event),
3702 g_signal_connect (view->goto_window, "key_press_event",
3703 G_CALLBACK (ev_view_goto_window_key_press_event),
3705 g_signal_connect (view->goto_window, "button_press_event",
3706 G_CALLBACK (ev_view_goto_window_button_press_event),
3709 frame = gtk_frame_new (NULL);
3710 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
3711 gtk_container_add (GTK_CONTAINER (view->goto_window), frame);
3712 gtk_widget_show (frame);
3714 hbox = gtk_hbox_new (FALSE, 0);
3715 gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
3716 gtk_container_add (GTK_CONTAINER (frame), hbox);
3717 gtk_widget_show (hbox);
3719 label = gtk_label_new(_("Jump to page:"));
3720 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 3);
3721 gtk_widget_show (label);
3722 gtk_widget_realize (label);
3724 view->goto_entry = gtk_entry_new ();
3725 g_signal_connect (view->goto_entry, "activate",
3726 G_CALLBACK (ev_view_goto_entry_activate),
3728 gtk_box_pack_start (GTK_BOX (hbox), view->goto_entry, TRUE, TRUE, 0);
3729 gtk_widget_show (view->goto_entry);
3730 gtk_widget_realize (view->goto_entry);
3734 ev_view_goto_entry_grab_focus (EvView *view)
3736 GtkWidgetClass *entry_parent_class;
3738 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (view->goto_entry));
3739 (entry_parent_class->grab_focus) (view->goto_entry);
3741 send_focus_change (view->goto_entry, TRUE);
3745 ev_view_goto_window_send_key_event (EvView *view,
3748 GdkEventKey *new_event;
3751 /* Move goto window off screen */
3752 screen = gtk_widget_get_screen (GTK_WIDGET (view));
3753 gtk_window_move (GTK_WINDOW (view->goto_window),
3754 gdk_screen_get_width (screen) + 1,
3755 gdk_screen_get_height (screen) + 1);
3756 gtk_widget_show (view->goto_window);
3758 new_event = (GdkEventKey *) gdk_event_copy (event);
3759 g_object_unref (new_event->window);
3760 new_event->window = g_object_ref (view->goto_window->window);
3761 gtk_widget_realize (view->goto_window);
3763 gtk_widget_event (view->goto_window, (GdkEvent *)new_event);
3764 gdk_event_free ((GdkEvent *)new_event);
3765 gtk_widget_hide (view->goto_window);
3769 ev_view_key_press_event (GtkWidget *widget,
3772 EvView *view = EV_VIEW (widget);
3773 EvPresentationState current;
3775 if (!view->document)
3778 if (!GTK_WIDGET_HAS_FOCUS (widget)) {
3779 /* Forward key events to current focused window child */
3780 if (view->window_child_focus) {
3781 GdkEventKey *new_event;
3784 new_event = (GdkEventKey *) gdk_event_copy ((GdkEvent *)event);
3785 g_object_unref (new_event->window);
3786 new_event->window = g_object_ref (view->window_child_focus->window->window);
3787 gtk_widget_realize (view->window_child_focus->window);
3788 handled = gtk_widget_event (view->window_child_focus->window, (GdkEvent *)new_event);
3789 gdk_event_free ((GdkEvent *)new_event);
3797 if (!view->presentation ||
3798 view->presentation_state == EV_PRESENTATION_END)
3799 return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
3802 current = view->presentation_state;
3804 switch (event->keyval) {
3808 case GDK_KP_Decimal:
3809 view->presentation_state =
3810 (view->presentation_state == EV_PRESENTATION_BLACK) ?
3811 EV_PRESENTATION_NORMAL : EV_PRESENTATION_BLACK;
3815 view->presentation_state =
3816 (view->presentation_state == EV_PRESENTATION_WHITE) ?
3817 EV_PRESENTATION_NORMAL : EV_PRESENTATION_WHITE;
3820 if (view->presentation_state == EV_PRESENTATION_BLACK ||
3821 view->presentation_state == EV_PRESENTATION_WHITE) {
3822 view->presentation_state = EV_PRESENTATION_NORMAL;
3826 if (current == view->presentation_state) {
3827 if (ev_page_cache_get_n_pages (view->page_cache) > 1 &&
3828 key_is_numeric (event->keyval)) {
3831 ev_view_goto_window_create (view);
3832 ev_view_goto_window_send_key_event (view, (GdkEvent *)event);
3833 gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y);
3834 gtk_window_move (GTK_WINDOW (view->goto_window), x, y);
3835 gtk_widget_show (view->goto_window);
3836 ev_view_goto_entry_grab_focus (view);
3841 return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
3844 switch (view->presentation_state) {
3845 case EV_PRESENTATION_NORMAL:
3846 case EV_PRESENTATION_BLACK:
3847 gdk_window_set_background (view->layout.bin_window,
3848 &widget->style->black);
3850 case EV_PRESENTATION_WHITE:
3851 gdk_window_set_background (view->layout.bin_window,
3852 &widget->style->white);
3855 return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
3858 gtk_widget_queue_draw (widget);
3863 ev_view_focus_in (GtkWidget *widget,
3864 GdkEventFocus *event)
3866 if (EV_VIEW (widget)->pixbuf_cache)
3867 ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
3868 gtk_widget_queue_draw (widget);
3874 ev_view_focus_out (GtkWidget *widget,
3875 GdkEventFocus *event)
3877 if (EV_VIEW (widget)->goto_window)
3878 ev_view_goto_window_hide (EV_VIEW (widget));
3880 if (EV_VIEW (widget)->pixbuf_cache)
3881 ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
3882 gtk_widget_queue_draw (widget);
3888 ev_view_leave_notify_event (GtkWidget *widget, GdkEventCrossing *event)
3890 EvView *view = EV_VIEW (widget);
3892 if (view->cursor != EV_VIEW_CURSOR_NORMAL)
3893 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
3899 ev_view_enter_notify_event (GtkWidget *widget, GdkEventCrossing *event)
3901 EvView *view = EV_VIEW (widget);
3903 ev_view_handle_cursor_over_xy (view, event->x, event->y);
3909 ev_view_style_set (GtkWidget *widget,
3910 GtkStyle *old_style)
3912 if (EV_VIEW (widget)->pixbuf_cache)
3913 ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
3915 GTK_WIDGET_CLASS (ev_view_parent_class)->style_set (widget, old_style);
3921 ev_gdk_color_to_rgb (const GdkColor *color)
3924 result = (0xff0000 | (color->red & 0xff00));
3926 result |= ((color->green & 0xff00) | (color->blue >> 8));
3931 draw_rubberband (GtkWidget *widget, GdkWindow *window,
3932 const GdkRectangle *rect, guchar alpha)
3936 GdkColor *fill_color_gdk;
3939 fill_color_gdk = gdk_color_copy (>K_WIDGET (widget)->style->base[GTK_STATE_SELECTED]);
3940 fill_color = ev_gdk_color_to_rgb (fill_color_gdk) << 8 | alpha;
3942 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
3943 rect->width, rect->height);
3944 gdk_pixbuf_fill (pixbuf, fill_color);
3946 gdk_draw_pixbuf (window, NULL, pixbuf,
3948 rect->x - EV_VIEW (widget)->scroll_x, rect->y - EV_VIEW (widget)->scroll_y,
3949 rect->width, rect->height,
3950 GDK_RGB_DITHER_NONE,
3953 g_object_unref (pixbuf);
3955 gc = gdk_gc_new (window);
3956 gdk_gc_set_rgb_fg_color (gc, fill_color_gdk);
3957 gdk_draw_rectangle (window, gc, FALSE,
3958 rect->x - EV_VIEW (widget)->scroll_x, rect->y - EV_VIEW (widget)->scroll_y,
3961 g_object_unref (gc);
3963 gdk_color_free (fill_color_gdk);
3968 highlight_find_results (EvView *view, int page)
3970 gint i, n_results = 0;
3972 n_results = ev_view_find_get_n_results (view, page);
3974 for (i = 0; i < n_results; i++) {
3975 EvRectangle *rectangle;
3976 GdkRectangle view_rectangle;
3979 if (i == view->find_result && page == view->current_page) {
3985 rectangle = ev_view_find_get_result (view, page, i);
3986 doc_rect_to_view_rect (view, page, rectangle, &view_rectangle);
3987 draw_rubberband (GTK_WIDGET (view), view->layout.bin_window,
3988 &view_rectangle, alpha);
3993 draw_loading_text (EvView *view,
3994 GdkRectangle *page_area,
3995 GdkRectangle *expose_area)
4000 if (!view->loading_text) {
4001 const gchar *loading_text = _("Loading...");
4002 PangoLayout *layout;
4003 PangoFontDescription *font_desc;
4004 PangoRectangle logical_rect;
4008 ev_document_fc_mutex_lock ();
4010 layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), loading_text);
4012 font_desc = pango_font_description_new ();
4014 /* We set the font to be 10 points, get the size, and scale appropriately */
4015 pango_font_description_set_size (font_desc, 10 * PANGO_SCALE);
4016 pango_layout_set_font_description (layout, font_desc);
4017 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
4019 target_width = MAX (page_area->width / 2, 1);
4020 real_scale = ((double)target_width / (double) logical_rect.width) * (PANGO_SCALE * 10);
4021 pango_font_description_set_size (font_desc, (int)real_scale);
4022 pango_layout_set_font_description (layout, font_desc);
4023 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
4025 view->loading_text = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
4027 logical_rect.height);
4028 cr = cairo_create (view->loading_text);
4029 cairo_set_source_rgb (cr,
4033 pango_cairo_show_layout (cr, layout);
4036 pango_font_description_free (font_desc);
4037 g_object_unref (layout);
4039 ev_document_fc_mutex_unlock ();
4042 width = (page_area->width - cairo_image_surface_get_width (view->loading_text)) / 2;
4043 height = (page_area->height - cairo_image_surface_get_height (view->loading_text)) / 2;
4045 cr = gdk_cairo_create (view->layout.bin_window);
4046 cairo_translate (cr,
4047 page_area->x + width,
4048 page_area->y + height);
4049 cairo_set_source_surface (cr, view->loading_text, 0, 0);
4055 draw_one_page (EvView *view,
4058 GdkRectangle *page_area,
4060 GdkRectangle *expose_area,
4061 gboolean *page_ready)
4063 GdkRectangle overlap;
4064 GdkRectangle real_page_area;
4066 g_assert (view->document);
4068 if (! gdk_rectangle_intersect (page_area, expose_area, &overlap))
4071 /* Render the document itself */
4072 real_page_area = *page_area;
4074 real_page_area.x += border->left;
4075 real_page_area.y += border->top;
4076 real_page_area.width -= (border->left + border->right);
4077 real_page_area.height -= (border->top + border->bottom);
4080 if (!view->presentation) {
4083 current_page = ev_page_cache_get_current_page (view->page_cache);
4084 ev_document_misc_paint_one_page (view->layout.bin_window,
4087 page == current_page);
4090 if (gdk_rectangle_intersect (&real_page_area, expose_area, &overlap)) {
4092 gint page_width, page_height;
4093 cairo_surface_t *page_surface = NULL;
4094 gint selection_width, selection_height;
4095 cairo_surface_t *selection_surface = NULL;
4097 page_surface = ev_pixbuf_cache_get_surface (view->pixbuf_cache, page);
4099 if (!page_surface) {
4100 if (!view->presentation) {
4101 draw_loading_text (view,
4106 *page_ready = FALSE;
4111 ev_page_cache_get_size (view->page_cache,
4112 page, view->rotation,
4116 page_width = cairo_image_surface_get_width (page_surface);
4117 page_height = cairo_image_surface_get_height (page_surface);
4120 cairo_translate (cr, overlap.x, overlap.y);
4122 if (width != page_width || height != page_height) {
4123 cairo_pattern_set_filter (cairo_get_source (cr),
4126 (gdouble)width / page_width,
4127 (gdouble)height / page_height);
4130 cairo_surface_set_device_offset (page_surface,
4131 overlap.x - real_page_area.x,
4132 overlap.y - real_page_area.y);
4133 cairo_set_source_surface (cr, page_surface, 0, 0);
4137 /* Get the selection pixbuf iff we have something to draw */
4138 if (find_selection_for_page (view, page) &&
4139 view->selection_mode == EV_VIEW_SELECTION_TEXT) {
4141 ev_pixbuf_cache_get_selection_surface (view->pixbuf_cache,
4147 if (!selection_surface) {
4151 selection_width = cairo_image_surface_get_width (selection_surface);
4152 selection_height = cairo_image_surface_get_height (selection_surface);
4155 cairo_translate (cr, overlap.x, overlap.y);
4157 if (width != selection_width || height != selection_height) {
4158 cairo_pattern_set_filter (cairo_get_source (cr),
4161 (gdouble)width / selection_width,
4162 (gdouble)height / selection_height);
4165 cairo_surface_set_device_offset (selection_surface,
4166 overlap.x - real_page_area.x,
4167 overlap.y - real_page_area.y);
4168 cairo_set_source_surface (cr, selection_surface, 0, 0);
4174 /*** GObject functions ***/
4177 ev_view_finalize (GObject *object)
4179 EvView *view = EV_VIEW (object);
4181 clear_selection (view);
4182 clear_link_selected (view);
4184 if (view->image_dnd_info.image)
4185 g_object_unref (view->image_dnd_info.image);
4186 view->image_dnd_info.image = NULL;
4188 G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
4192 ev_view_destroy (GtkObject *object)
4194 EvView *view = EV_VIEW (object);
4196 if (view->document) {
4197 g_object_unref (view->document);
4198 view->document = NULL;
4201 if (view->pixbuf_cache) {
4202 g_object_unref (view->pixbuf_cache);
4203 view->pixbuf_cache = NULL;
4206 if (view->goto_window) {
4207 gtk_widget_destroy (view->goto_window);
4208 view->goto_window = NULL;
4209 view->goto_entry = NULL;
4212 ev_view_window_children_free (view);
4214 if (view->selection_scroll_id) {
4215 g_source_remove (view->selection_scroll_id);
4216 view->selection_scroll_id = 0;
4219 if (view->selection_update_id) {
4220 g_source_remove (view->selection_update_id);
4221 view->selection_update_id = 0;
4224 if (view->loading_text) {
4225 cairo_surface_destroy (view->loading_text);
4226 view->loading_text = NULL;
4229 if (view->scroll_info.timeout_id) {
4230 g_source_remove (view->scroll_info.timeout_id);
4231 view->scroll_info.timeout_id = 0;
4234 if (view->drag_info.drag_timeout_id) {
4235 g_source_remove (view->drag_info.drag_timeout_id);
4236 view->drag_info.drag_timeout_id = 0;
4239 if (view->drag_info.release_timeout_id) {
4240 g_source_remove (view->drag_info.release_timeout_id);
4241 view->drag_info.release_timeout_id = 0;
4244 ev_view_presentation_transition_stop (view);
4246 ev_view_set_scroll_adjustments (GTK_LAYOUT (view), NULL, NULL);
4248 GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
4252 ev_view_set_property (GObject *object,
4254 const GValue *value,
4257 EvView *view = EV_VIEW (object);
4260 case PROP_CONTINUOUS:
4261 ev_view_set_continuous (view, g_value_get_boolean (value));
4263 case PROP_DUAL_PAGE:
4264 ev_view_set_dual_page (view, g_value_get_boolean (value));
4266 case PROP_FULLSCREEN:
4267 ev_view_set_fullscreen (view, g_value_get_boolean (value));
4269 case PROP_PRESENTATION:
4270 ev_view_set_presentation (view, g_value_get_boolean (value));
4272 case PROP_SIZING_MODE:
4273 ev_view_set_sizing_mode (view, g_value_get_enum (value));
4276 ev_view_set_zoom (view, g_value_get_double (value), FALSE);
4279 ev_view_set_rotation (view, g_value_get_int (value));
4282 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4287 ev_view_get_accessible (GtkWidget *widget)
4289 static gboolean first_time = TRUE;
4292 AtkObjectFactory *factory;
4293 AtkRegistry *registry;
4295 GType derived_atk_type;
4298 * Figure out whether accessibility is enabled by looking at the
4299 * type of the accessible object which would be created for
4300 * the parent type of EvView.
4302 derived_type = g_type_parent (EV_TYPE_VIEW);
4304 registry = atk_get_default_registry ();
4305 factory = atk_registry_get_factory (registry,
4307 derived_atk_type = atk_object_factory_get_accessible_type (factory);
4308 if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
4309 atk_registry_set_factory_type (registry,
4311 ev_view_accessible_factory_get_type ());
4314 return GTK_WIDGET_CLASS (ev_view_parent_class)->get_accessible (widget);
4318 ev_view_get_property (GObject *object,
4323 EvView *view = EV_VIEW (object);
4326 case PROP_CONTINUOUS:
4327 g_value_set_boolean (value, view->continuous);
4329 case PROP_DUAL_PAGE:
4330 g_value_set_boolean (value, view->dual_page);
4332 case PROP_FULLSCREEN:
4333 g_value_set_boolean (value, view->fullscreen);
4335 case PROP_PRESENTATION:
4336 g_value_set_boolean (value, view->presentation);
4338 case PROP_SIZING_MODE:
4339 g_value_set_enum (value, view->sizing_mode);
4342 g_value_set_double (value, view->scale);
4345 g_value_set_int (value, view->rotation);
4347 case PROP_HAS_SELECTION:
4348 g_value_set_boolean (value,
4349 view->selection_info.selections != NULL);
4352 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4357 ev_view_class_init (EvViewClass *class)
4359 GObjectClass *object_class = G_OBJECT_CLASS (class);
4360 GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
4361 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
4362 GtkLayoutClass *layout_class = GTK_LAYOUT_CLASS (class);
4363 GtkBindingSet *binding_set;
4365 object_class->finalize = ev_view_finalize;
4366 object_class->set_property = ev_view_set_property;
4367 object_class->get_property = ev_view_get_property;
4369 widget_class->expose_event = ev_view_expose_event;
4370 widget_class->button_press_event = ev_view_button_press_event;
4371 widget_class->motion_notify_event = ev_view_motion_notify_event;
4372 widget_class->button_release_event = ev_view_button_release_event;
4373 widget_class->key_press_event = ev_view_key_press_event;
4374 widget_class->focus_in_event = ev_view_focus_in;
4375 widget_class->focus_out_event = ev_view_focus_out;
4376 widget_class->get_accessible = ev_view_get_accessible;
4377 widget_class->size_request = ev_view_size_request;
4378 widget_class->size_allocate = ev_view_size_allocate;
4379 widget_class->realize = ev_view_realize;
4380 widget_class->scroll_event = ev_view_scroll_event;
4381 widget_class->enter_notify_event = ev_view_enter_notify_event;
4382 widget_class->leave_notify_event = ev_view_leave_notify_event;
4383 widget_class->style_set = ev_view_style_set;
4384 widget_class->drag_data_get = ev_view_drag_data_get;
4385 widget_class->drag_motion = ev_view_drag_motion;
4386 widget_class->popup_menu = ev_view_popup_menu;
4387 widget_class->query_tooltip = ev_view_query_tooltip;
4389 gtk_object_class->destroy = ev_view_destroy;
4391 layout_class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
4393 class->binding_activated = ev_view_scroll;
4395 signals[SIGNAL_BINDING_ACTIVATED] = g_signal_new ("binding_activated",
4396 G_TYPE_FROM_CLASS (object_class),
4397 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
4398 G_STRUCT_OFFSET (EvViewClass, binding_activated),
4400 ev_view_marshal_VOID__ENUM_BOOLEAN,
4402 GTK_TYPE_SCROLL_TYPE,
4405 signals[SIGNAL_ZOOM_INVALID] = g_signal_new ("zoom-invalid",
4406 G_TYPE_FROM_CLASS (object_class),
4407 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
4408 G_STRUCT_OFFSET (EvViewClass, zoom_invalid),
4410 g_cclosure_marshal_VOID__VOID,
4411 G_TYPE_NONE, 0, G_TYPE_NONE);
4412 signals[SIGNAL_HANDLE_LINK] = g_signal_new ("handle-link",
4413 G_TYPE_FROM_CLASS (object_class),
4414 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
4415 G_STRUCT_OFFSET (EvViewClass, handle_link),
4417 g_cclosure_marshal_VOID__OBJECT,
4420 signals[SIGNAL_EXTERNAL_LINK] = g_signal_new ("external-link",
4421 G_TYPE_FROM_CLASS (object_class),
4422 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
4423 G_STRUCT_OFFSET (EvViewClass, external_link),
4425 g_cclosure_marshal_VOID__OBJECT,
4428 signals[SIGNAL_POPUP_MENU] = g_signal_new ("popup",
4429 G_TYPE_FROM_CLASS (object_class),
4430 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
4431 G_STRUCT_OFFSET (EvViewClass, popup_menu),
4433 g_cclosure_marshal_VOID__OBJECT,
4438 g_object_class_install_property (object_class,
4440 g_param_spec_boolean ("continuous",
4442 "Continuous scrolling mode",
4444 G_PARAM_READWRITE));
4446 g_object_class_install_property (object_class,
4448 g_param_spec_boolean ("dual-page",
4450 "Two pages visible at once",
4452 G_PARAM_READWRITE));
4453 g_object_class_install_property (object_class,
4455 g_param_spec_boolean ("fullscreen",
4457 "Draw page in a fullscreen fashion",
4459 G_PARAM_READWRITE));
4460 g_object_class_install_property (object_class,
4462 g_param_spec_boolean ("presentation",
4464 "Draw page in presentation mode",
4466 G_PARAM_READWRITE));
4468 g_object_class_install_property (object_class,
4470 g_param_spec_enum ("sizing-mode",
4473 EV_TYPE_SIZING_MODE,
4474 EV_SIZING_FIT_WIDTH,
4475 G_PARAM_READWRITE));
4477 g_object_class_install_property (object_class,
4479 g_param_spec_double ("zoom",
4485 G_PARAM_READWRITE));
4486 g_object_class_install_property (object_class,
4488 g_param_spec_double ("rotation",
4494 G_PARAM_READWRITE));
4495 g_object_class_install_property (object_class,
4497 g_param_spec_boolean ("has-selection",
4499 "The view has selections",
4503 binding_set = gtk_binding_set_by_class (class);
4505 add_scroll_binding_keypad (binding_set, GDK_Left, 0, GTK_SCROLL_STEP_BACKWARD, TRUE);
4506 add_scroll_binding_keypad (binding_set, GDK_Right, 0, GTK_SCROLL_STEP_FORWARD, TRUE);
4507 add_scroll_binding_keypad (binding_set, GDK_Left, GDK_MOD1_MASK, GTK_SCROLL_STEP_DOWN, TRUE);
4508 add_scroll_binding_keypad (binding_set, GDK_Right, GDK_MOD1_MASK, GTK_SCROLL_STEP_UP, TRUE);
4509 add_scroll_binding_keypad (binding_set, GDK_Up, 0, GTK_SCROLL_STEP_BACKWARD, FALSE);
4510 add_scroll_binding_keypad (binding_set, GDK_Down, 0, GTK_SCROLL_STEP_FORWARD, FALSE);
4511 add_scroll_binding_keypad (binding_set, GDK_Up, GDK_MOD1_MASK, GTK_SCROLL_STEP_DOWN, FALSE);
4512 add_scroll_binding_keypad (binding_set, GDK_Down, GDK_MOD1_MASK, GTK_SCROLL_STEP_UP, FALSE);
4513 gtk_binding_entry_add_signal (binding_set, GDK_H, 0, "binding_activated", 2, GTK_TYPE_SCROLL_TYPE,
4514 GTK_SCROLL_STEP_BACKWARD, G_TYPE_BOOLEAN, TRUE);
4515 gtk_binding_entry_add_signal (binding_set, GDK_J, 0, "binding_activated", 2, GTK_TYPE_SCROLL_TYPE,
4516 GTK_SCROLL_STEP_FORWARD, G_TYPE_BOOLEAN, FALSE);
4517 gtk_binding_entry_add_signal (binding_set, GDK_K, 0, "binding_activated", 2, GTK_TYPE_SCROLL_TYPE,
4518 GTK_SCROLL_STEP_BACKWARD, G_TYPE_BOOLEAN, FALSE);
4519 gtk_binding_entry_add_signal (binding_set, GDK_L, 0, "binding_activated", 2, GTK_TYPE_SCROLL_TYPE,
4520 GTK_SCROLL_STEP_FORWARD, G_TYPE_BOOLEAN, TRUE);
4525 ev_view_init (EvView *view)
4527 GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
4529 view->start_page = -1;
4530 view->end_page = -1;
4533 view->current_page = 0;
4534 view->pressed_button = -1;
4535 view->cursor = EV_VIEW_CURSOR_NORMAL;
4536 view->drag_info.in_drag = FALSE;
4537 view->scroll_info.autoscrolling = FALSE;
4538 view->selection_info.selections = NULL;
4539 view->selection_info.in_selection = FALSE;
4540 view->selection_info.in_drag = FALSE;
4541 view->selection_mode = EV_VIEW_SELECTION_TEXT;
4542 view->continuous = TRUE;
4543 view->dual_page = FALSE;
4544 view->presentation = FALSE;
4545 view->presentation_state = EV_PRESENTATION_NORMAL;
4546 view->fullscreen = FALSE;
4547 view->sizing_mode = EV_SIZING_FIT_WIDTH;
4548 view->pending_scroll = SCROLL_TO_KEEP_POSITION;
4549 view->jump_to_find_result = TRUE;
4550 view->highlight_find_results = FALSE;
4552 gtk_layout_set_hadjustment (GTK_LAYOUT (view), NULL);
4553 gtk_layout_set_vadjustment (GTK_LAYOUT (view), NULL);
4559 ev_view_change_page (EvView *view,
4561 gboolean start_transition)
4565 view->current_page = new_page;
4566 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
4568 if (view->presentation && start_transition)
4569 ev_view_presentation_transition_start (view);
4571 gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y);
4572 ev_view_handle_cursor_over_xy (view, x, y);
4574 gtk_widget_queue_resize (GTK_WIDGET (view));
4578 ev_view_transition_animation_finish (EvTransitionAnimation *animation,
4581 g_object_unref (view->animation);
4582 view->animation = NULL;
4583 ev_view_change_page (view, view->current_page, TRUE);
4587 * ev_view_transition_animation_cancel:
4588 * @animation: Animation to finish
4591 * Does almost the same as cancel, but without scheduling the transition.
4595 ev_view_transition_animation_cancel (EvTransitionAnimation *animation,
4598 g_object_unref (view->animation);
4599 view->animation = NULL;
4600 ev_view_change_page (view, view->current_page, FALSE);
4604 ev_view_transition_animation_frame (EvTransitionAnimation *animation,
4608 gtk_widget_queue_draw (GTK_WIDGET (view));
4612 ev_view_presentation_animation_start (EvView *view,
4615 EvTransitionEffect *effect = NULL;
4616 cairo_surface_t *surface;
4618 if (EV_IS_DOCUMENT_TRANSITION (view->document))
4619 effect = ev_document_transition_get_effect (EV_DOCUMENT_TRANSITION (view->document),
4620 view->current_page);
4624 surface = ev_pixbuf_cache_get_surface (view->pixbuf_cache, view->current_page);
4625 view->animation = ev_transition_animation_new (effect);
4626 ev_transition_animation_set_origin_surface (view->animation, surface);
4628 g_signal_connect (view->animation, "frame",
4629 G_CALLBACK (ev_view_transition_animation_frame), view);
4630 g_signal_connect (view->animation, "finished",
4631 G_CALLBACK (ev_view_transition_animation_finish), view);
4635 job_finished_cb (EvPixbufCache *pixbuf_cache,
4639 if (view->animation) {
4640 cairo_surface_t *surface;
4642 surface = ev_pixbuf_cache_get_surface (pixbuf_cache, view->current_page);
4643 ev_transition_animation_set_dest_surface (view->animation, surface);
4647 gdk_window_invalidate_region (view->layout.bin_window,
4650 gtk_widget_queue_draw (GTK_WIDGET (view));
4655 page_changed_cb (EvPageCache *page_cache,
4659 if (view->current_page != new_page) {
4660 if (view->presentation)
4661 ev_view_presentation_animation_start (view, new_page);
4663 ev_view_change_page (view, new_page, TRUE);
4665 gtk_widget_queue_draw (GTK_WIDGET (view));
4668 view->find_result = 0;
4672 on_adjustment_value_changed (GtkAdjustment *adjustment,
4677 GList *children, *l;
4679 if (! GTK_WIDGET_REALIZED (view))
4682 if (view->hadjustment) {
4683 dx = view->scroll_x - (int) view->hadjustment->value;
4684 view->scroll_x = (int) view->hadjustment->value;
4689 if (view->vadjustment) {
4690 dy = view->scroll_y - (int) view->vadjustment->value;
4691 view->scroll_y = (int) view->vadjustment->value;
4696 children = gtk_container_get_children (GTK_CONTAINER (view));
4697 for (l = children; l && l->data; l = g_list_next (l)) {
4698 gint child_x, child_y;
4699 GtkWidget *child = (GtkWidget *)l->data;
4701 gtk_container_child_get (GTK_CONTAINER (view),
4706 gtk_layout_move (GTK_LAYOUT (view), child, child_x + dx, child_y + dy);
4708 g_list_free (children);
4710 for (l = view->window_children; l && l->data; l = g_list_next (l)) {
4711 EvViewWindowChild *child;
4713 child = (EvViewWindowChild *)l->data;
4715 ev_view_window_child_move (view, child, child->x + dx, child->y + dy);
4718 if (view->pending_resize)
4719 gtk_widget_queue_draw (GTK_WIDGET (view));
4721 gdk_window_scroll (view->layout.bin_window, dx, dy);
4723 gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y);
4724 ev_view_handle_cursor_over_xy (view, x, y);
4727 view_update_range_and_current_page (view);
4735 view = g_object_new (EV_TYPE_VIEW, NULL);
4741 setup_caches (EvView *view)
4743 view->page_cache = ev_page_cache_get (view->document);
4744 g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
4745 view->pixbuf_cache = ev_pixbuf_cache_new (GTK_WIDGET (view), view->document);
4746 g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
4747 page_changed_cb (view->page_cache,
4748 ev_page_cache_get_current_page (view->page_cache),
4753 clear_caches (EvView *view)
4755 if (view->pixbuf_cache) {
4756 g_object_unref (view->pixbuf_cache);
4757 view->pixbuf_cache = NULL;
4760 if (view->page_cache) {
4761 view->page_cache = NULL;
4766 ev_view_set_loading (EvView *view,
4769 view->loading = loading;
4770 gtk_widget_queue_draw (GTK_WIDGET (view));
4774 ev_view_autoscroll_cb (EvView *view)
4776 gdouble speed, value;
4778 /* If the user stops autoscrolling, autoscrolling will be
4779 * set to false but the timeout will continue; stop the timeout: */
4780 if (!view->scroll_info.autoscrolling) {
4781 view->scroll_info.timeout_id = 0;
4785 if (view->scroll_info.last_y > view->scroll_info.start_y &&
4786 (view->scroll_info.last_y < view->scroll_info.start_y))
4789 /* Replace 100 with your speed of choice: The lower the faster.
4790 * Replace 3 with another speed of choice: The higher, the faster it accelerated
4791 * based on the distance of the starting point from the mouse
4792 * (All also effected by the timeout interval of this callback) */
4794 if (view->scroll_info.start_y > view->scroll_info.last_y)
4795 speed = -pow ((((gdouble)view->scroll_info.start_y - view->scroll_info.last_y) / 100), 3);
4797 speed = pow ((((gdouble)view->scroll_info.last_y - view->scroll_info.start_y) / 100), 3);
4799 value = gtk_adjustment_get_value (view->vadjustment);
4800 value = CLAMP (value + speed, 0, view->vadjustment->upper - view->vadjustment->page_size);
4801 gtk_adjustment_set_value (view->vadjustment, value);
4808 ev_view_autoscroll_start (EvView *view)
4812 g_return_if_fail (EV_IS_VIEW (view));
4814 if (view->scroll_info.autoscrolling)
4817 view->scroll_info.autoscrolling = TRUE;
4818 view->scroll_info.timeout_id =
4819 g_timeout_add (20, (GSourceFunc)ev_view_autoscroll_cb,
4822 gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y);
4823 ev_view_handle_cursor_over_xy (view, x, y);
4827 ev_view_autoscroll_stop (EvView *view)
4831 g_return_if_fail (EV_IS_VIEW (view));
4833 if (!view->scroll_info.autoscrolling)
4836 view->scroll_info.autoscrolling = FALSE;
4837 if (view->scroll_info.timeout_id) {
4838 g_source_remove (view->scroll_info.timeout_id);
4839 view->scroll_info.timeout_id = 0;
4842 gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y);
4843 ev_view_handle_cursor_over_xy (view, x, y);
4847 ev_view_set_document (EvView *view,
4848 EvDocument *document)
4850 g_return_if_fail (EV_IS_VIEW (view));
4852 view->loading = FALSE;
4854 if (document != view->document) {
4855 clear_caches (view);
4857 if (view->document) {
4858 g_object_unref (view->document);
4859 view->page_cache = NULL;
4862 view->document = document;
4863 view->find_result = 0;
4865 if (view->document) {
4866 g_object_ref (view->document);
4867 setup_caches (view);
4870 view_update_range_and_current_page (view);
4872 gtk_widget_queue_resize (GTK_WIDGET (view));
4877 ev_view_reload_page (EvView *view,
4881 ev_pixbuf_cache_reload_page (view->pixbuf_cache,
4889 ev_view_reload (EvView *view)
4891 ev_pixbuf_cache_clear (view->pixbuf_cache);
4892 view_update_range_and_current_page (view);
4895 /*** Zoom and sizing mode ***/
4897 #define EPSILON 0.0000001
4899 ev_view_set_zoom (EvView *view,
4906 scale = view->scale * factor;
4910 scale = CLAMP (scale,
4911 view->sizing_mode == EV_SIZING_FREE ? view->min_scale : 0,
4914 if (scale == view->scale)
4917 if (ABS (view->scale - scale) < EPSILON)
4920 if (view->loading_text) {
4921 cairo_surface_destroy (view->loading_text);
4922 view->loading_text = NULL;
4925 view->scale = scale;
4926 view->pending_resize = TRUE;
4928 gtk_widget_queue_resize (GTK_WIDGET (view));
4930 g_object_notify (G_OBJECT (view), "zoom");
4934 ev_view_get_zoom (EvView *view)
4940 ev_view_set_screen_dpi (EvView *view,
4943 g_return_if_fail (EV_IS_VIEW (view));
4944 g_return_if_fail (dpi > 0);
4947 view->min_scale = MIN_SCALE * dpi / 72.0;
4948 view->max_scale = MAX_SCALE * dpi / 72.0;
4952 ev_view_get_continuous (EvView *view)
4954 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
4956 return view->continuous;
4960 ev_view_set_continuous (EvView *view,
4961 gboolean continuous)
4963 g_return_if_fail (EV_IS_VIEW (view));
4965 continuous = continuous != FALSE;
4967 if (view->continuous != continuous) {
4968 view->continuous = continuous;
4969 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
4970 gtk_widget_queue_resize (GTK_WIDGET (view));
4973 g_object_notify (G_OBJECT (view), "continuous");
4977 ev_view_get_dual_page (EvView *view)
4979 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
4981 return view->dual_page;
4985 ev_view_set_dual_page (EvView *view,
4988 g_return_if_fail (EV_IS_VIEW (view));
4990 dual_page = dual_page != FALSE;
4992 if (view->dual_page == dual_page)
4995 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
4996 view->dual_page = dual_page;
4997 /* FIXME: if we're keeping the pixbuf cache around, we should extend the
4998 * preload_cache_size to be 2 if dual_page is set.
5000 gtk_widget_queue_resize (GTK_WIDGET (view));
5002 g_object_notify (G_OBJECT (view), "dual-page");
5006 ev_view_set_fullscreen (EvView *view,
5007 gboolean fullscreen)
5009 g_return_if_fail (EV_IS_VIEW (view));
5011 fullscreen = fullscreen != FALSE;
5013 if (view->fullscreen == fullscreen)
5016 view->fullscreen = fullscreen;
5017 gtk_widget_queue_resize (GTK_WIDGET (view));
5019 g_object_notify (G_OBJECT (view), "fullscreen");
5023 ev_view_get_fullscreen (EvView *view)
5025 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
5027 return view->fullscreen;
5031 ev_view_set_presentation (EvView *view,
5032 gboolean presentation)
5034 g_return_if_fail (EV_IS_VIEW (view));
5036 presentation = presentation != FALSE;
5038 if (view->presentation == presentation)
5042 view->presentation_state = EV_PRESENTATION_NORMAL;
5044 view->presentation = presentation;
5045 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
5048 view->sizing_mode_saved = view->sizing_mode;
5049 view->scale_saved = view->scale;
5050 ev_view_set_sizing_mode (view, EV_SIZING_BEST_FIT);
5052 ev_view_set_sizing_mode (view, view->sizing_mode_saved);
5053 ev_view_set_zoom (view, view->scale_saved, FALSE);
5056 gtk_widget_queue_resize (GTK_WIDGET (view));
5059 ev_view_presentation_transition_start (view);
5061 ev_view_presentation_transition_stop (view);
5063 if (view->animation) {
5064 /* stop any running animation */
5065 ev_view_transition_animation_cancel (view->animation, view);
5069 if (GTK_WIDGET_REALIZED (view)) {
5070 if (view->presentation)
5071 gdk_window_set_background (view->layout.bin_window,
5072 >K_WIDGET (view)->style->black);
5074 gdk_window_set_background (view->layout.bin_window,
5075 >K_WIDGET (view)->style->mid [GTK_STATE_NORMAL]);
5078 g_object_notify (G_OBJECT (view), "presentation");
5082 ev_view_get_presentation (EvView *view)
5084 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
5086 return view->presentation;
5090 transition_next_page (EvView *view)
5092 ev_view_next_page (view);
5098 ev_view_presentation_transition_stop (EvView *view)
5100 if (view->trans_timeout_id > 0)
5101 g_source_remove (view->trans_timeout_id);
5102 view->trans_timeout_id = 0;
5106 ev_view_presentation_transition_start (EvView *view)
5110 if (!EV_IS_DOCUMENT_TRANSITION (view->document))
5113 ev_view_presentation_transition_stop (view);
5115 duration = ev_document_transition_get_page_duration (EV_DOCUMENT_TRANSITION (view->document),
5116 view->current_page);
5118 view->trans_timeout_id =
5119 g_timeout_add_seconds (duration,
5120 (GSourceFunc) transition_next_page,
5126 ev_view_set_sizing_mode (EvView *view,
5127 EvSizingMode sizing_mode)
5129 g_return_if_fail (EV_IS_VIEW (view));
5131 if (view->sizing_mode == sizing_mode)
5134 view->sizing_mode = sizing_mode;
5135 gtk_widget_queue_resize (GTK_WIDGET (view));
5137 g_object_notify (G_OBJECT (view), "sizing-mode");
5141 ev_view_get_sizing_mode (EvView *view)
5143 g_return_val_if_fail (EV_IS_VIEW (view), EV_SIZING_FREE);
5145 return view->sizing_mode;
5149 ev_view_can_zoom_in (EvView *view)
5151 return view->scale * ZOOM_IN_FACTOR <= view->max_scale;
5155 ev_view_can_zoom_out (EvView *view)
5157 return view->scale * ZOOM_OUT_FACTOR >= view->min_scale;
5161 ev_view_zoom_in (EvView *view)
5163 g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
5165 if (view->presentation)
5168 view->pending_scroll = SCROLL_TO_CENTER;
5169 ev_view_set_zoom (view, ZOOM_IN_FACTOR, TRUE);
5173 ev_view_zoom_out (EvView *view)
5175 g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
5177 if (view->presentation)
5180 view->pending_scroll = SCROLL_TO_CENTER;
5181 ev_view_set_zoom (view, ZOOM_OUT_FACTOR, TRUE);
5185 ev_view_rotate_right (EvView *view)
5187 int rotation = view->rotation + 90;
5189 if (rotation >= 360) {
5193 ev_view_set_rotation (view, rotation);
5197 ev_view_rotate_left (EvView *view)
5199 int rotation = view->rotation - 90;
5205 ev_view_set_rotation (view, rotation);
5209 ev_view_set_rotation (EvView *view, int rotation)
5211 view->rotation = rotation;
5213 if (view->pixbuf_cache) {
5214 ev_pixbuf_cache_clear (view->pixbuf_cache);
5215 gtk_widget_queue_resize (GTK_WIDGET (view));
5219 clear_selection (view);
5221 g_object_notify (G_OBJECT (view), "rotation");
5225 ev_view_get_rotation (EvView *view)
5227 return view->rotation;
5231 zoom_for_size_fit_width (int doc_width,
5239 scale = (double)target_width / doc_width;
5241 if (doc_height * scale > target_height)
5242 scale = (double) (target_width - vsb_width) / doc_width;
5248 zoom_for_size_fit_height (int doc_width,
5256 scale = (double)target_height / doc_height;
5258 if (doc_width * scale > target_width)
5259 scale = (double) (target_height - vsb_height) / doc_height;
5265 zoom_for_size_best_fit (int doc_width,
5275 w_scale = (double)target_width / doc_width;
5276 h_scale = (double)target_height / doc_height;
5278 if (doc_height * w_scale > target_height)
5279 w_scale = (double) (target_width - vsb_width) / doc_width;
5280 if (doc_width * h_scale > target_width)
5281 h_scale = (double) (target_height - hsb_width) / doc_height;
5283 return MIN (w_scale, h_scale);
5288 ev_view_zoom_for_size_presentation (EvView *view,
5292 int doc_width, doc_height;
5295 ev_page_cache_get_size (view->page_cache,
5301 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, 0, 0);
5302 ev_view_set_zoom (view, scale, FALSE);
5306 ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
5312 int doc_width, doc_height;
5316 ev_page_cache_get_max_width (view->page_cache,
5320 ev_page_cache_get_max_height (view->page_cache,
5324 compute_border (view, doc_width, doc_height, &border);
5326 doc_width = doc_width * 2;
5327 width -= (2 * (border.left + border.right) + 3 * view->spacing);
5328 height -= (border.top + border.bottom + 2 * view->spacing - 1);
5330 /* FIXME: We really need to calculate the overall height here, not the
5331 * page height. We assume there's always a vertical scrollbar for
5332 * now. We need to fix this. */
5333 if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
5334 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
5335 else if (view->sizing_mode == EV_SIZING_BEST_FIT)
5336 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
5338 g_assert_not_reached ();
5340 ev_view_set_zoom (view, scale, FALSE);
5344 ev_view_zoom_for_size_continuous (EvView *view,
5350 int doc_width, doc_height;
5354 ev_page_cache_get_max_width (view->page_cache,
5358 ev_page_cache_get_max_height (view->page_cache,
5362 compute_border (view, doc_width, doc_height, &border);
5364 width -= (border.left + border.right + 2 * view->spacing);
5365 height -= (border.top + border.bottom + 2 * view->spacing - 1);
5367 /* FIXME: We really need to calculate the overall height here, not the
5368 * page height. We assume there's always a vertical scrollbar for
5369 * now. We need to fix this. */
5370 if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
5371 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
5372 else if (view->sizing_mode == EV_SIZING_BEST_FIT)
5373 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
5375 g_assert_not_reached ();
5377 ev_view_set_zoom (view, scale, FALSE);
5381 ev_view_zoom_for_size_dual_page (EvView *view,
5388 gint doc_width, doc_height;
5392 other_page = view->current_page ^ 1;
5394 /* Find the largest of the two. */
5395 ev_page_cache_get_size (view->page_cache,
5399 &doc_width, &doc_height);
5401 if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
5402 gint width_2, height_2;
5403 ev_page_cache_get_size (view->page_cache,
5407 &width_2, &height_2);
5408 if (width_2 > doc_width)
5409 doc_width = width_2;
5410 if (height_2 > doc_height)
5411 doc_height = height_2;
5413 compute_border (view, doc_width, doc_height, &border);
5415 doc_width = doc_width * 2;
5416 width -= ((border.left + border.right)* 2 + 3 * view->spacing);
5417 height -= (border.top + border.bottom + 2 * view->spacing);
5419 if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
5420 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
5421 else if (view->sizing_mode == EV_SIZING_BEST_FIT)
5422 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
5424 g_assert_not_reached ();
5426 ev_view_set_zoom (view, scale, FALSE);
5430 ev_view_zoom_for_size_single_page (EvView *view,
5436 int doc_width, doc_height;
5440 ev_page_cache_get_size (view->page_cache,
5446 /* Get an approximate border */
5447 compute_border (view, width, height, &border);
5449 width -= (border.left + border.right + 2 * view->spacing);
5450 height -= (border.top + border.bottom + 2 * view->spacing);
5452 if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
5453 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
5454 else if (view->sizing_mode == EV_SIZING_BEST_FIT)
5455 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
5457 g_assert_not_reached ();
5459 ev_view_set_zoom (view, scale, FALSE);
5463 ev_view_set_zoom_for_size (EvView *view,
5469 g_return_if_fail (EV_IS_VIEW (view));
5470 g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
5471 view->sizing_mode == EV_SIZING_BEST_FIT);
5472 g_return_if_fail (width >= 0);
5473 g_return_if_fail (height >= 0);
5475 if (view->document == NULL)
5478 if (view->presentation)
5479 ev_view_zoom_for_size_presentation (view, width, height);
5480 else if (view->continuous && view->dual_page)
5481 ev_view_zoom_for_size_continuous_and_dual_page (view, width, height, vsb_width, hsb_height);
5482 else if (view->continuous)
5483 ev_view_zoom_for_size_continuous (view, width, height, vsb_width, hsb_height);
5484 else if (view->dual_page)
5485 ev_view_zoom_for_size_dual_page (view, width, height, vsb_width, hsb_height);
5487 ev_view_zoom_for_size_single_page (view, width, height, vsb_width, hsb_height);
5492 ev_view_find_get_n_results (EvView *view, gint page)
5494 return view->find_pages ? g_list_length (view->find_pages[page]) : 0;
5497 static EvRectangle *
5498 ev_view_find_get_result (EvView *view, gint page, gint result)
5500 return view->find_pages ? (EvRectangle *) g_list_nth_data (view->find_pages[page], result) : NULL;
5504 jump_to_find_result (EvView *view)
5507 gint page = view->current_page;
5509 n_results = ev_view_find_get_n_results (view, page);
5511 if (n_results > 0 && view->find_result < n_results) {
5513 GdkRectangle view_rect;
5515 rect = ev_view_find_get_result (view, page, view->find_result);
5516 doc_rect_to_view_rect (view, page, rect, &view_rect);
5517 ensure_rectangle_is_visible (view, &view_rect);
5518 view->jump_to_find_result = FALSE;
5524 * @view: #EvView instance
5525 * @direction: Direction to look
5526 * @shift: Shift from current page
5528 * Jumps to the first page that has occurences of searched word.
5529 * Uses a direction where to look and a shift from current page.
5533 jump_to_find_page (EvView *view, EvViewFindDirection direction, gint shift)
5537 n_pages = ev_page_cache_get_n_pages (view->page_cache);
5539 for (i = 0; i < n_pages; i++) {
5542 if (direction == EV_VIEW_FIND_NEXT)
5543 page = view->current_page + i;
5545 page = view->current_page - i;
5548 if (page >= n_pages) {
5549 page = page - n_pages;
5550 } else if (page < 0)
5551 page = page + n_pages;
5553 if (ev_view_find_get_n_results (view, page) > 0) {
5554 ev_page_cache_set_current_page (view->page_cache, page);
5561 ev_view_find_changed (EvView *view, GList **results, gint page)
5563 view->find_pages = results;
5565 if (view->jump_to_find_result == TRUE) {
5566 jump_to_find_page (view, EV_VIEW_FIND_NEXT, 0);
5567 jump_to_find_result (view);
5570 if (view->current_page == page)
5571 gtk_widget_queue_draw (GTK_WIDGET (view));
5575 ev_view_find_next (EvView *view)
5579 n_results = ev_view_find_get_n_results (view, view->current_page);
5580 view->find_result++;
5582 if (view->find_result >= n_results) {
5583 view->find_result = 0;
5584 jump_to_find_page (view, EV_VIEW_FIND_NEXT, 1);
5585 jump_to_find_result (view);
5587 jump_to_find_result (view);
5588 gtk_widget_queue_draw (GTK_WIDGET (view));
5593 ev_view_find_previous (EvView *view)
5595 view->find_result--;
5597 if (view->find_result < 0) {
5598 jump_to_find_page (view, EV_VIEW_FIND_PREV, -1);
5599 view->find_result = MAX (0, ev_view_find_get_n_results (view, view->current_page) - 1);
5600 jump_to_find_result (view);
5602 jump_to_find_result (view);
5603 gtk_widget_queue_draw (GTK_WIDGET (view));
5608 ev_view_find_search_changed (EvView *view)
5610 /* search string has changed, focus on new search result */
5611 view->jump_to_find_result = TRUE;
5612 view->find_pages = NULL;
5616 ev_view_find_set_highlight_search (EvView *view, gboolean value)
5618 view->highlight_find_results = value;
5619 gtk_widget_queue_draw (GTK_WIDGET (view));
5623 ev_view_find_cancel (EvView *view)
5625 view->find_pages = NULL;
5628 /*** Selections ***/
5630 /* compute_new_selection_rect/text calculates the area currently selected by
5631 * view_rect. each handles a different mode;
5634 compute_new_selection_rect (EvView *view,
5638 GdkRectangle view_rect;
5642 g_assert (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE);
5644 view_rect.x = MIN (start->x, stop->x);
5645 view_rect.y = MIN (start->y, stop->y);
5646 view_rect.width = MAX (start->x, stop->x) - view_rect.x;
5647 view_rect.width = MAX (start->y, stop->y) - view_rect.y;
5649 n_pages = ev_page_cache_get_n_pages (view->page_cache);
5651 for (i = 0; i < n_pages; i++) {
5652 GdkRectangle page_area;
5655 if (get_page_extents (view, i, &page_area, &border)) {
5656 GdkRectangle overlap;
5658 if (gdk_rectangle_intersect (&page_area, &view_rect, &overlap)) {
5659 EvViewSelection *selection;
5661 selection = g_new0 (EvViewSelection, 1);
5662 selection->page = i;
5663 view_rect_to_doc_rect (view, &overlap, &page_area,
5664 &(selection->rect));
5666 list = g_list_append (list, selection);
5675 gdk_rectangle_point_in (GdkRectangle *rectangle,
5678 return rectangle->x <= point->x &&
5679 rectangle->y <= point->y &&
5680 point->x < rectangle->x + rectangle->width &&
5681 point->y < rectangle->y + rectangle->height;
5685 compute_new_selection_text (EvView *view,
5686 EvSelectionStyle style,
5690 int n_pages, i, first, last;
5692 EvViewSelection *selection;
5694 int start_page, end_page;
5696 g_assert (view->selection_mode == EV_VIEW_SELECTION_TEXT);
5698 n_pages = ev_page_cache_get_n_pages (view->page_cache);
5700 /* First figure out the range of pages the selection
5704 if (view->continuous) {
5707 } else if (view->dual_page) {
5708 start_page = view->start_page;
5709 end_page = view->end_page + 1;
5711 start_page = view->current_page;
5712 end_page = view->current_page + 1;
5715 for (i = start_page; i < end_page; i++) {
5716 GdkRectangle page_area;
5719 get_page_extents (view, i, &page_area, &border);
5720 if (gdk_rectangle_point_in (&page_area, start) ||
5721 gdk_rectangle_point_in (&page_area, stop)) {
5722 if (first == n_pages)
5729 /* Now create a list of EvViewSelection's for the affected
5730 * pages. This could be an empty list, a list of just one
5731 * page or a number of pages.*/
5732 for (i = first; i < last + 1; i++) {
5733 GdkRectangle page_area;
5737 ev_page_cache_get_size (view->page_cache, i,
5739 1.0, &width, &height);
5741 selection = g_new0 (EvViewSelection, 1);
5742 selection->page = i;
5743 selection->style = style;
5744 selection->rect.x1 = selection->rect.y1 = 0;
5745 selection->rect.x2 = width;
5746 selection->rect.y2 = height;
5748 get_page_extents (view, i, &page_area, &border);
5750 if (gdk_rectangle_point_in (&page_area, start))
5756 view_point_to_doc_point (view, point, &page_area,
5757 &selection->rect.x1,
5758 &selection->rect.y1);
5760 /* If the selection is contained within just one page,
5761 * make sure we don't write 'start' into both points
5762 * in selection->rect. */
5767 view_point_to_doc_point (view, point, &page_area,
5768 &selection->rect.x2,
5769 &selection->rect.y2);
5771 list = g_list_append (list, selection);
5777 /* This function takes the newly calculated list, and figures out which regions
5778 * have changed. It then queues a redraw approporiately.
5781 merge_selection_region (EvView *view,
5785 GList *new_list_ptr, *old_list_ptr;
5787 /* Update the selection */
5788 old_list = ev_pixbuf_cache_get_selection_list (view->pixbuf_cache);
5789 g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL);
5790 g_list_free (view->selection_info.selections);
5791 view->selection_info.selections = new_list;
5792 ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, new_list);
5793 g_object_notify (G_OBJECT (view), "has-selection");
5795 new_list_ptr = new_list;
5796 old_list_ptr = old_list;
5798 while (new_list_ptr || old_list_ptr) {
5799 EvViewSelection *old_sel, *new_sel;
5801 GdkRegion *region = NULL;
5803 new_sel = (new_list_ptr) ? (new_list_ptr->data) : NULL;
5804 old_sel = (old_list_ptr) ? (old_list_ptr->data) : NULL;
5806 /* Assume that the lists are in order, and we run through them
5807 * comparing them, one page at a time. We come out with the
5808 * first page we see. */
5809 if (new_sel && old_sel) {
5810 if (new_sel->page < old_sel->page) {
5811 new_list_ptr = new_list_ptr->next;
5813 } else if (new_sel->page > old_sel->page) {
5814 old_list_ptr = old_list_ptr->next;
5817 new_list_ptr = new_list_ptr->next;
5818 old_list_ptr = old_list_ptr->next;
5820 } else if (new_sel) {
5821 new_list_ptr = new_list_ptr->next;
5822 } else if (old_sel) {
5823 old_list_ptr = old_list_ptr->next;
5826 g_assert (new_sel || old_sel);
5828 /* is the page we're looking at on the screen?*/
5829 cur_page = new_sel ? new_sel->page : old_sel->page;
5830 if (cur_page < view->start_page || cur_page > view->end_page)
5833 /* seed the cache with a new page. We are going to need the new
5836 GdkRegion *tmp_region = NULL;
5838 ev_pixbuf_cache_get_selection_surface (view->pixbuf_cache,
5844 new_sel->covered_region = gdk_region_copy (tmp_region);
5848 /* Now we figure out what needs redrawing */
5849 if (old_sel && new_sel) {
5850 if (old_sel->covered_region && new_sel->covered_region) {
5851 /* We only want to redraw the areas that have
5852 * changed, so we xor the old and new regions
5853 * and redraw if it's different */
5854 region = gdk_region_copy (old_sel->covered_region);
5855 gdk_region_xor (region, new_sel->covered_region);
5856 if (gdk_region_empty (region)) {
5857 gdk_region_destroy (region);
5860 } else if (old_sel->covered_region) {
5861 region = gdk_region_copy (old_sel->covered_region);
5862 } else if (new_sel->covered_region) {
5863 region = gdk_region_copy (new_sel->covered_region);
5865 } else if (old_sel && !new_sel) {
5866 if (old_sel->covered_region && !gdk_region_empty (old_sel->covered_region)) {
5867 region = gdk_region_copy (old_sel->covered_region);
5869 } else if (!old_sel && new_sel) {
5870 if (new_sel->covered_region && !gdk_region_empty (new_sel->covered_region)) {
5871 region = gdk_region_copy (new_sel->covered_region);
5874 g_assert_not_reached ();
5877 /* Redraw the damaged region! */
5879 GdkRectangle page_area;
5882 /* I don't know why but the region is smaller
5883 * than expected. This hack fixes it, I guess
5884 * 10 pixels more won't hurt
5886 gdk_region_shrink (region, -5, -5);
5888 get_page_extents (view, cur_page, &page_area, &border);
5889 gdk_region_offset (region,
5890 page_area.x + border.left - view->scroll_x,
5891 page_area.y + border.top - view->scroll_y);
5892 gdk_window_invalidate_region (view->layout.bin_window, region, TRUE);
5893 gdk_region_destroy (region);
5897 /* Free the old list, now that we're done with it. */
5898 g_list_foreach (old_list, (GFunc) selection_free, NULL);
5899 g_list_free (old_list);
5903 compute_selections (EvView *view,
5904 EvSelectionStyle style,
5910 if (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE)
5911 list = compute_new_selection_rect (view, start, stop);
5913 list = compute_new_selection_text (view, style, start, stop);
5914 merge_selection_region (view, list);
5917 /* Free's the selection. It's up to the caller to queue redraws if needed.
5920 selection_free (EvViewSelection *selection)
5922 if (selection->covered_region)
5923 gdk_region_destroy (selection->covered_region);
5928 clear_selection (EvView *view)
5930 g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL);
5931 g_list_free (view->selection_info.selections);
5932 view->selection_info.selections = NULL;
5933 view->selection_info.in_selection = FALSE;
5934 if (view->pixbuf_cache)
5935 ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, NULL);
5936 g_object_notify (G_OBJECT (view), "has-selection");
5940 ev_view_select_all (EvView *view)
5942 GList *selections = NULL;
5945 /* Disable selection on rotated pages for the 0.4.0 series */
5946 if (view->rotation != 0)
5949 clear_selection (view);
5951 n_pages = ev_page_cache_get_n_pages (view->page_cache);
5952 for (i = 0; i < n_pages; i++) {
5954 EvViewSelection *selection;
5956 ev_page_cache_get_size (view->page_cache,
5959 1.0, &width, &height);
5961 selection = g_new0 (EvViewSelection, 1);
5962 selection->page = i;
5963 selection->style = EV_SELECTION_STYLE_GLYPH;
5964 selection->rect.x1 = selection->rect.y1 = 0;
5965 selection->rect.x2 = width;
5966 selection->rect.y2 = height;
5968 selections = g_list_append (selections, selection);
5971 merge_selection_region (view, selections);
5972 gtk_widget_queue_draw (GTK_WIDGET (view));
5976 ev_view_get_has_selection (EvView *view)
5978 return view->selection_info.selections != NULL;
5982 get_selected_text (EvView *view)
5986 gchar *normalized_text;
5987 EvRenderContext *rc;
5989 text = g_string_new (NULL);
5990 rc = ev_render_context_new (NULL, view->rotation, view->scale);
5992 ev_document_doc_mutex_lock ();
5994 for (l = view->selection_info.selections; l != NULL; l = l->next) {
5995 EvViewSelection *selection = (EvViewSelection *)l->data;
5999 page = ev_document_get_page (view->document, selection->page);
6000 ev_render_context_set_page (rc, page);
6001 g_object_unref (page);
6003 tmp = ev_selection_get_selected_text (EV_SELECTION (view->document),
6004 rc, selection->style,
6005 &(selection->rect));
6007 g_string_append (text, tmp);
6011 g_object_unref (rc);
6013 ev_document_doc_mutex_unlock ();
6015 normalized_text = g_utf8_normalize (text->str, text->len, G_NORMALIZE_NFKC);
6016 g_string_free (text, TRUE);
6017 return normalized_text;
6021 ev_view_clipboard_copy (EvView *view,
6024 GtkClipboard *clipboard;
6026 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view),
6027 GDK_SELECTION_CLIPBOARD);
6028 gtk_clipboard_set_text (clipboard, text, -1);
6032 ev_view_copy (EvView *ev_view)
6036 if (!EV_IS_SELECTION (ev_view->document))
6039 text = get_selected_text (ev_view);
6040 ev_view_clipboard_copy (ev_view, text);
6045 ev_view_primary_get_cb (GtkClipboard *clipboard,
6046 GtkSelectionData *selection_data,
6050 EvView *ev_view = EV_VIEW (data);
6052 if (ev_view->link_selected) {
6053 gtk_selection_data_set_text (selection_data,
6054 ev_link_action_get_uri (ev_view->link_selected),
6056 } else if (EV_IS_SELECTION (ev_view->document) &&
6057 ev_view->selection_info.selections) {
6060 text = get_selected_text (ev_view);
6062 gtk_selection_data_set_text (selection_data, text, -1);
6069 ev_view_primary_clear_cb (GtkClipboard *clipboard,
6072 EvView *view = EV_VIEW (data);
6074 clear_selection (view);
6075 clear_link_selected (view);
6079 ev_view_update_primary_selection (EvView *ev_view)
6081 GtkClipboard *clipboard;
6083 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
6084 GDK_SELECTION_PRIMARY);
6086 if (ev_view->selection_info.selections || ev_view->link_selected) {
6087 GtkTargetList *target_list;
6088 GtkTargetEntry *targets;
6091 target_list = gtk_target_list_new (NULL, 0);
6092 gtk_target_list_add_text_targets (target_list, 0);
6093 targets = gtk_target_table_new_from_list (target_list, &n_targets);
6094 gtk_target_list_unref (target_list);
6096 if (!gtk_clipboard_set_with_owner (clipboard,
6098 ev_view_primary_get_cb,
6099 ev_view_primary_clear_cb,
6100 G_OBJECT (ev_view)))
6101 ev_view_primary_clear_cb (clipboard, ev_view);
6103 gtk_target_table_free (targets, n_targets);
6105 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
6106 gtk_clipboard_clear (clipboard);
6111 clear_link_selected (EvView *view)
6113 if (view->link_selected) {
6114 g_object_unref (view->link_selected);
6115 view->link_selected = NULL;
6120 ev_view_copy_link_address (EvView *view,
6121 EvLinkAction *action)
6123 clear_link_selected (view);
6125 ev_view_clipboard_copy (view, ev_link_action_get_uri (action));
6127 view->link_selected = g_object_ref (action);
6128 ev_view_update_primary_selection (view);
6131 /*** Cursor operations ***/
6134 ev_view_create_invisible_cursor(void)
6137 GdkColor black = { 0, 0, 0, 0 };
6138 static char bits[] = { 0x00 };
6140 empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
6142 return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
6146 ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
6148 GdkCursor *cursor = NULL;
6149 GdkDisplay *display;
6152 if (view->cursor == new_cursor) {
6156 widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
6157 display = gtk_widget_get_display (widget);
6158 view->cursor = new_cursor;
6160 switch (new_cursor) {
6161 case EV_VIEW_CURSOR_NORMAL:
6162 gdk_window_set_cursor (view->layout.bin_window, NULL);
6164 case EV_VIEW_CURSOR_IBEAM:
6165 cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
6167 case EV_VIEW_CURSOR_LINK:
6168 cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
6170 case EV_VIEW_CURSOR_WAIT:
6171 cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
6173 case EV_VIEW_CURSOR_HIDDEN:
6174 cursor = ev_view_create_invisible_cursor ();
6176 case EV_VIEW_CURSOR_DRAG:
6177 cursor = gdk_cursor_new_for_display (display, GDK_FLEUR);
6179 case EV_VIEW_CURSOR_AUTOSCROLL:
6180 cursor = gdk_cursor_new_for_display (display, GDK_DOUBLE_ARROW);
6185 gdk_window_set_cursor (view->layout.bin_window, cursor);
6186 gdk_cursor_unref (cursor);
6192 ev_view_hide_cursor (EvView *view)
6194 ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN);
6198 ev_view_show_cursor (EvView *view)
6200 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
6204 ev_view_reset_presentation_state (EvView *view)
6206 if (!view->presentation ||
6207 view->presentation_state == EV_PRESENTATION_NORMAL)
6210 view->presentation_state = EV_PRESENTATION_NORMAL;
6211 gdk_window_set_background (view->layout.bin_window,
6212 >K_WIDGET (view)->style->black);
6213 gtk_widget_queue_draw (GTK_WIDGET (view));
6217 ev_view_next_page (EvView *view)
6221 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
6223 if (!view->page_cache)
6226 if (view->presentation &&
6227 (view->presentation_state == EV_PRESENTATION_BLACK ||
6228 view->presentation_state == EV_PRESENTATION_WHITE)) {
6229 ev_view_reset_presentation_state (view);
6233 if (view->animation) {
6234 ev_view_transition_animation_cancel (view->animation, view);
6237 ev_view_presentation_transition_stop (view);
6238 ev_view_reset_presentation_state (view);
6240 page = ev_page_cache_get_current_page (view->page_cache);
6241 n_pages = ev_page_cache_get_n_pages (view->page_cache);
6243 if (view->dual_page && !view->presentation)
6248 if (page < n_pages) {
6249 ev_page_cache_set_current_page (view->page_cache, page);
6251 } else if (view->presentation && page == n_pages) {
6252 view->presentation_state = EV_PRESENTATION_END;
6253 gtk_widget_queue_draw (GTK_WIDGET (view));
6255 } else if (view->dual_page && page == n_pages) {
6256 ev_page_cache_set_current_page (view->page_cache, page - 1);
6264 ev_view_previous_page (EvView *view)
6268 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
6270 if (!view->page_cache)
6273 if (view->presentation &&
6274 view->presentation_state == EV_PRESENTATION_END) {
6275 ev_view_reset_presentation_state (view);
6279 if (view->presentation &&
6280 (view->presentation_state == EV_PRESENTATION_BLACK ||
6281 view->presentation_state == EV_PRESENTATION_WHITE)) {
6282 ev_view_reset_presentation_state (view);
6286 if (view->animation) {
6287 ev_view_transition_animation_cancel (view->animation, view);
6290 ev_view_reset_presentation_state (view);
6292 page = ev_page_cache_get_current_page (view->page_cache);
6294 if (view->dual_page && !view->presentation)
6300 ev_page_cache_set_current_page (view->page_cache, page);
6302 } else if (ev_view_get_dual_page (view) && page == -1) {
6303 ev_page_cache_set_current_page (view->page_cache, 0);