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
3 *
4 * Copyright (C) 2004 Red Hat, Inc
5 *
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.
10 *
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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <math.h>
25 #include <string.h>
26
27 #include <glib/gi18n-lib.h>
28 #include <gtk/gtk.h>
29 #include <gdk/gdkkeysyms.h>
30
31 #include "ev-mapping-list.h"
32 #include "ev-document-forms.h"
33 #include "ev-document-images.h"
34 #include "ev-document-links.h"
35 #include "ev-document-layers.h"
36 #include "ev-document-media.h"
37 #include "ev-document-misc.h"
38 #include "ev-form-field-private.h"
39 #include "ev-pixbuf-cache.h"
40 #include "ev-page-cache.h"
41 #include "ev-view-marshal.h"
42 #include "ev-document-annotations.h"
43 #include "ev-annotation-window.h"
44 #include "ev-view.h"
45 #include "ev-view-accessible.h"
46 #include "ev-view-private.h"
47 #include "ev-view-type-builtins.h"
48 #include "ev-debug.h"
49
50 #ifdef ENABLE_MULTIMEDIA
51 #include "ev-media-player.h"
52 #endif
53
54 enum {
55 SIGNAL_SCROLL,
56 SIGNAL_HANDLE_LINK,
57 SIGNAL_EXTERNAL_LINK,
58 SIGNAL_POPUP_MENU,
59 SIGNAL_SELECTION_CHANGED,
60 SIGNAL_SYNC_SOURCE,
61 SIGNAL_ANNOT_ADDED,
62 SIGNAL_ANNOT_CHANGED,
63 SIGNAL_ANNOT_REMOVED,
64 SIGNAL_LAYERS_CHANGED,
65 SIGNAL_MOVE_CURSOR,
66 SIGNAL_CURSOR_MOVED,
67 SIGNAL_ACTIVATE,
68 N_SIGNALS
69 };
70
71 enum {
72 TARGET_DND_URI,
73 TARGET_DND_TEXT,
74 TARGET_DND_IMAGE
75 };
76
77 enum {
78 PROP_0,
79 PROP_IS_LOADING,
80 PROP_HADJUSTMENT,
81 PROP_VADJUSTMENT,
82 PROP_HSCROLL_POLICY,
83 PROP_VSCROLL_POLICY,
84 PROP_CAN_ZOOM_IN,
85 PROP_CAN_ZOOM_OUT
86 };
87
88 static guint signals[N_SIGNALS];
89
90 typedef enum {
91 EV_VIEW_FIND_NEXT,
92 EV_VIEW_FIND_PREV
93 } EvViewFindDirection;
94
95 typedef struct {
96 GtkWidget *widget;
97
98 /* View coords */
99 gint x;
100 gint y;
101
102 /* Document */
103 guint page;
104 EvRectangle doc_rect;
105 } EvViewChild;
106
107 #define MIN_SCALE 0.05409 /* large documents (comics) need a small value, see #702 */
108 #define ZOOM_IN_FACTOR 1.2
109 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)
110
111 #define SCROLL_TIME 150
112 #define SCROLL_PAGE_THRESHOLD 0.7
113
114 #define DEFAULT_PIXBUF_CACHE_SIZE 52428800 /* 50MB */
115
116 #define EV_STYLE_CLASS_DOCUMENT_PAGE "document-page"
117 #define EV_STYLE_CLASS_INVERTED "inverted"
118 #define EV_STYLE_CLASS_FIND_RESULTS "find-results"
119
120 #define ANNOT_POPUP_WINDOW_DEFAULT_WIDTH 200
121 #define ANNOT_POPUP_WINDOW_DEFAULT_HEIGHT 150
122 #define ANNOTATION_ICON_SIZE 24
123
124 #define LINK_PREVIEW_PAGE_RATIO 1.0 / 3.0 /* Size of popover with respect to page size */
125 #define LINK_PREVIEW_HORIZONTAL_LINK_POS 0.5 /* as fraction of preview width */
126 #define LINK_PREVIEW_VERTICAL_LINK_POS 0.3 /* as fraction of preview height */
127 #define LINK_PREVIEW_DELAY_MS 300 /* Delay before showing preview in milliseconds */
128
129 /*** Scrolling ***/
130 static void view_update_range_and_current_page (EvView *view);
131
132 /*** Geometry computations ***/
133 static void compute_border (EvView *view,
134 GtkBorder *border);
135 static void get_page_y_offset (EvView *view,
136 int page,
137 int *y_offset,
138 GtkBorder *border);
139 static void find_page_at_location (EvView *view,
140 gdouble x,
141 gdouble y,
142 gint *page,
143 gint *x_offset,
144 gint *y_offset);
145 static gboolean real_ev_view_get_page_extents (EvView *view,
146 gint page,
147 GdkRectangle *page_area,
148 GtkBorder *border,
149 gboolean use_passed_border);
150 /*** Hyperrefs ***/
151 static EvLink * ev_view_get_link_at_location (EvView *view,
152 gdouble x,
153 gdouble y);
154 static char* tip_from_link (EvView *view,
155 EvLink *link);
156 static void ev_view_link_preview_popover_cleanup (EvView *view);
157 static void get_link_area (EvView *view,
158 gint x,
159 gint y,
160 EvLink *link,
161 GdkRectangle *area);
162 static void link_preview_show_thumbnail (cairo_surface_t *page_surface,
163 EvView *view);
164 static void link_preview_job_finished_cb (EvJobThumbnail *job,
165 EvView *view);
166 static gboolean link_preview_popover_motion_notify (EvView *view,
167 GdkEventMotion *event);
168 static gboolean link_preview_delayed_show (EvView *view);
169 /*** Forms ***/
170 static EvFormField *ev_view_get_form_field_at_location (EvView *view,
171 gdouble x,
172 gdouble y);
173 /*** Media ***/
174 static EvMedia *ev_view_get_media_at_location (EvView *view,
175 gdouble x,
176 gdouble y);
177 static gboolean ev_view_find_player_for_media (EvView *view,
178 EvMedia *media);
179 /*** Annotations ***/
180 static GtkWidget *get_window_for_annot (EvView *view,
181 EvAnnotation *annot);
182 static void map_annot_to_window (EvView *view,
183 EvAnnotation *annot,
184 GtkWidget *window);
185 static EvAnnotation *ev_view_get_annotation_at_location (EvView *view,
186 gdouble x,
187 gdouble y);
188 static void show_annotation_windows (EvView *view,
189 gint page);
190 static void hide_annotation_windows (EvView *view,
191 gint page);
192 static void ev_view_create_annotation_from_selection (EvView *view,
193 EvViewSelection *selection);
194 /*** GtkWidget implementation ***/
195 static void ev_view_size_request_continuous_dual_page (EvView *view,
196 GtkRequisition *requisition);
197 static void ev_view_size_request_continuous (EvView *view,
198 GtkRequisition *requisition);
199 static void ev_view_size_request_dual_page (EvView *view,
200 GtkRequisition *requisition);
201 static void ev_view_size_request_single_page (EvView *view,
202 GtkRequisition *requisition);
203 static void ev_view_size_request (GtkWidget *widget,
204 GtkRequisition *requisition);
205 static void ev_view_size_allocate (GtkWidget *widget,
206 GtkAllocation *allocation);
207 static gboolean ev_view_scroll_event (GtkWidget *widget,
208 GdkEventScroll *event);
209 static gboolean ev_view_draw (GtkWidget *widget,
210 cairo_t *cr);
211 static gboolean ev_view_popup_menu (GtkWidget *widget);
212 static gboolean ev_view_button_press_event (GtkWidget *widget,
213 GdkEventButton *event);
214 static gboolean ev_view_motion_notify_event (GtkWidget *widget,
215 GdkEventMotion *event);
216 static gboolean ev_view_button_release_event (GtkWidget *widget,
217 GdkEventButton *event);
218 static gboolean ev_view_enter_notify_event (GtkWidget *widget,
219 GdkEventCrossing *event);
220 static gboolean ev_view_leave_notify_event (GtkWidget *widget,
221 GdkEventCrossing *event);
222 static void ev_view_style_updated (GtkWidget *widget);
223 static void ev_view_remove_all (EvView *view);
224 static void ev_view_remove_all_form_fields (EvView *view);
225
226 static AtkObject *ev_view_get_accessible (GtkWidget *widget);
227
228 /*** Drawing ***/
229 static void highlight_find_results (EvView *view,
230 cairo_t *cr,
231 int page);
232 static void highlight_forward_search_results (EvView *view,
233 cairo_t *cr,
234 int page);
235 static void draw_one_page (EvView *view,
236 gint page,
237 cairo_t *cr,
238 GdkRectangle *page_area,
239 GtkBorder *border,
240 GdkRectangle *expose_area,
241 gboolean *page_ready);
242 static void ev_view_reload_page (EvView *view,
243 gint page,
244 cairo_region_t *region);
245 /*** Callbacks ***/
246 static void ev_view_change_page (EvView *view,
247 gint new_page);
248 static void job_finished_cb (EvPixbufCache *pixbuf_cache,
249 cairo_region_t *region,
250 EvView *view);
251 static void ev_view_page_changed_cb (EvDocumentModel *model,
252 gint old_page,
253 gint new_page,
254 EvView *view);
255 static void on_adjustment_value_changed (GtkAdjustment *adjustment,
256 EvView *view);
257 /*** GObject ***/
258 static void ev_view_finalize (GObject *object);
259 static void ev_view_dispose (GObject *object);
260 static void ev_view_class_init (EvViewClass *class);
261 static void ev_view_init (EvView *view);
262
263 /*** Zoom and sizing ***/
264 static double zoom_for_size_fit_width (gdouble doc_width,
265 gdouble doc_height,
266 int target_width,
267 int target_height);
268 static double zoom_for_size_fit_height (gdouble doc_width,
269 gdouble doc_height,
270 int target_width,
271 int target_height);
272 static double zoom_for_size_fit_page (gdouble doc_width,
273 gdouble doc_height,
274 int target_width,
275 int target_height);
276 static double zoom_for_size_automatic (GtkWidget *widget,
277 gdouble doc_width,
278 gdouble doc_height,
279 int target_width,
280 int target_height);
281 static gboolean ev_view_can_zoom (EvView *view,
282 gdouble factor);
283 static void ev_view_zoom (EvView *view,
284 gdouble factor);
285 static void ev_view_zoom_for_size (EvView *view,
286 int width,
287 int height);
288 static void ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
289 int width,
290 int height);
291 static void ev_view_zoom_for_size_continuous (EvView *view,
292 int width,
293 int height);
294 static void ev_view_zoom_for_size_dual_page (EvView *view,
295 int width,
296 int height);
297 static void ev_view_zoom_for_size_single_page (EvView *view,
298 int width,
299 int height);
300 static gboolean ev_view_page_fits (EvView *view,
301 GtkOrientation orientation);
302 /*** Cursors ***/
303 static void ev_view_set_cursor (EvView *view,
304 EvViewCursor new_cursor);
305 static void ev_view_handle_cursor_over_xy (EvView *view,
306 gint x,
307 gint y);
308
309 /*** Find ***/
310 static gint ev_view_find_get_n_results (EvView *view,
311 gint page);
312 static EvRectangle *ev_view_find_get_result (EvView *view,
313 gint page,
314 gint result);
315 static void jump_to_find_result (EvView *view);
316 static void jump_to_find_page (EvView *view,
317 EvViewFindDirection direction,
318 gint shift);
319 /*** Selection ***/
320 static void compute_selections (EvView *view,
321 EvSelectionStyle style,
322 GdkPoint *start,
323 GdkPoint *stop);
324 static void extend_selection (EvView *view,
325 GdkPoint *start,
326 GdkPoint *stop);
327 static void clear_selection (EvView *view);
328 static void clear_link_selected (EvView *view);
329 static void selection_free (EvViewSelection *selection);
330 static char* get_selected_text (EvView *ev_view);
331 static void ev_view_primary_get_cb (GtkClipboard *clipboard,
332 GtkSelectionData *selection_data,
333 guint info,
334 gpointer data);
335 static void ev_view_primary_clear_cb (GtkClipboard *clipboard,
336 gpointer data);
337 static void ev_view_update_primary_selection (EvView *ev_view);
338
339 /*** Caret navigation ***/
340 static void ev_view_check_cursor_blink (EvView *ev_view);
341
G_DEFINE_TYPE_WITH_CODE(EvView,ev_view,GTK_TYPE_CONTAINER,G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,NULL))342 G_DEFINE_TYPE_WITH_CODE (EvView, ev_view, GTK_TYPE_CONTAINER,
343 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
344
345 /* HeightToPage cache */
346 #define EV_HEIGHT_TO_PAGE_CACHE_KEY "ev-height-to-page-cache"
347
348 static void
349 ev_view_build_height_to_page_cache (EvView *view,
350 EvHeightToPageCache *cache)
351 {
352 gboolean swap, uniform;
353 int i;
354 double uniform_height, page_height, next_page_height;
355 double saved_height;
356 gdouble u_width, u_height;
357 gint n_pages;
358 EvDocument *document = view->document;
359
360 swap = (view->rotation == 90 || view->rotation == 270);
361
362 uniform = ev_document_is_page_size_uniform (document);
363 n_pages = ev_document_get_n_pages (document);
364
365 g_free (cache->height_to_page);
366 g_free (cache->dual_height_to_page);
367
368 cache->rotation = view->rotation;
369 cache->dual_even_left = view->dual_even_left;
370 cache->height_to_page = g_new0 (gdouble, n_pages + 1);
371 cache->dual_height_to_page = g_new0 (gdouble, n_pages + 2);
372
373 if (uniform)
374 ev_document_get_page_size (document, 0, &u_width, &u_height);
375
376 saved_height = 0;
377 for (i = 0; i <= n_pages; i++) {
378 if (uniform) {
379 uniform_height = swap ? u_width : u_height;
380 cache->height_to_page[i] = i * uniform_height;
381 } else {
382 if (i < n_pages) {
383 gdouble w, h;
384
385 ev_document_get_page_size (document, i, &w, &h);
386 page_height = swap ? w : h;
387 } else {
388 page_height = 0;
389 }
390 cache->height_to_page[i] = saved_height;
391 saved_height += page_height;
392 }
393 }
394
395 if (cache->dual_even_left && !uniform) {
396 gdouble w, h;
397
398 ev_document_get_page_size (document, 0, &w, &h);
399 saved_height = swap ? w : h;
400 } else {
401 saved_height = 0;
402 }
403
404 for (i = cache->dual_even_left; i < n_pages + 2; i += 2) {
405 if (uniform) {
406 uniform_height = swap ? u_width : u_height;
407 cache->dual_height_to_page[i] = ((i + cache->dual_even_left) / 2) * uniform_height;
408 if (i + 1 < n_pages + 2)
409 cache->dual_height_to_page[i + 1] = ((i + cache->dual_even_left) / 2) * uniform_height;
410 } else {
411 if (i + 1 < n_pages) {
412 gdouble w, h;
413
414 ev_document_get_page_size (document, i + 1, &w, &h);
415 next_page_height = swap ? w : h;
416 } else {
417 next_page_height = 0;
418 }
419
420 if (i < n_pages) {
421 gdouble w, h;
422
423 ev_document_get_page_size (document, i, &w, &h);
424 page_height = swap ? w : h;
425 } else {
426 page_height = 0;
427 }
428
429 if (i + 1 < n_pages + 2) {
430 cache->dual_height_to_page[i] = saved_height;
431 cache->dual_height_to_page[i + 1] = saved_height;
432 saved_height += MAX(page_height, next_page_height);
433 } else {
434 cache->dual_height_to_page[i] = saved_height;
435 }
436 }
437 }
438 }
439
440 static void
ev_height_to_page_cache_free(EvHeightToPageCache * cache)441 ev_height_to_page_cache_free (EvHeightToPageCache *cache)
442 {
443 if (cache->height_to_page) {
444 g_free (cache->height_to_page);
445 cache->height_to_page = NULL;
446 }
447
448 if (cache->dual_height_to_page) {
449 g_free (cache->dual_height_to_page);
450 cache->dual_height_to_page = NULL;
451 }
452 g_free (cache);
453 }
454
455 static EvHeightToPageCache *
ev_view_get_height_to_page_cache(EvView * view)456 ev_view_get_height_to_page_cache (EvView *view)
457 {
458 EvHeightToPageCache *cache;
459
460 if (!view->document)
461 return NULL;
462
463 cache = g_object_get_data (G_OBJECT (view->document), EV_HEIGHT_TO_PAGE_CACHE_KEY);
464 if (!cache) {
465 cache = g_new0 (EvHeightToPageCache, 1);
466 ev_view_build_height_to_page_cache (view, cache);
467 g_object_set_data_full (G_OBJECT (view->document),
468 EV_HEIGHT_TO_PAGE_CACHE_KEY,
469 cache,
470 (GDestroyNotify)ev_height_to_page_cache_free);
471 }
472
473 return cache;
474 }
475
476 static void
ev_view_get_height_to_page(EvView * view,gint page,gint * height,gint * dual_height)477 ev_view_get_height_to_page (EvView *view,
478 gint page,
479 gint *height,
480 gint *dual_height)
481 {
482 EvHeightToPageCache *cache = NULL;
483 gdouble h, dh;
484
485 if (!view->height_to_page_cache)
486 return;
487
488 cache = view->height_to_page_cache;
489 if (cache->rotation != view->rotation ||
490 cache->dual_even_left != view->dual_even_left) {
491 ev_view_build_height_to_page_cache (view, cache);
492 }
493
494 if (height) {
495 h = cache->height_to_page[page];
496 *height = (gint)(h * view->scale + 0.5);
497 }
498
499 if (dual_height) {
500 dh = cache->dual_height_to_page[page];
501 *dual_height = (gint)(dh * view->scale + 0.5);
502 }
503 }
504
505 static gint
ev_view_get_scrollbar_size(EvView * view,GtkOrientation orientation)506 ev_view_get_scrollbar_size (EvView *view,
507 GtkOrientation orientation)
508 {
509 GtkWidget *widget = GTK_WIDGET (view);
510 GtkWidget *sb;
511 GtkWidget *swindow = gtk_widget_get_parent (GTK_WIDGET (view));
512 GtkAllocation allocation;
513 GtkRequisition req;
514 gint spacing;
515
516 if (!GTK_IS_SCROLLED_WINDOW (swindow))
517 return 0;
518
519 gtk_widget_get_allocation (widget, &allocation);
520
521 if (orientation == GTK_ORIENTATION_VERTICAL) {
522 if (allocation.height >= view->requisition.height)
523 sb = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (swindow));
524 else
525 return 0;
526 } else {
527 if (allocation.width >= view->requisition.width)
528 sb = gtk_scrolled_window_get_hscrollbar (GTK_SCROLLED_WINDOW (swindow));
529 else
530 return 0;
531 }
532
533 gtk_widget_style_get (swindow, "scrollbar_spacing", &spacing, NULL);
534 gtk_widget_get_preferred_size (sb, &req, NULL);
535
536 return (orientation == GTK_ORIENTATION_VERTICAL ? req.width : req.height) + spacing;
537 }
538
539 static gboolean
is_dual_page(EvView * view,gboolean * odd_left_out)540 is_dual_page (EvView *view,
541 gboolean *odd_left_out)
542 {
543 gboolean dual = FALSE;
544 gboolean odd_left = FALSE;
545
546 switch (view->page_layout) {
547 case EV_PAGE_LAYOUT_AUTOMATIC: {
548 double scale;
549 double doc_width;
550 double doc_height;
551 GtkAllocation allocation;
552
553 scale = ev_document_misc_get_widget_dpi (GTK_WIDGET (view)) / 72.0;
554
555 ev_document_get_max_page_size (view->document, &doc_width, &doc_height);
556 gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
557
558 /* If the width is ok and the height is pretty close, try to fit it in */
559 if (ev_document_get_n_pages (view->document) > 1 &&
560 doc_width < doc_height &&
561 allocation.width > (2 * doc_width * scale) &&
562 allocation.height > (doc_height * scale * 0.9)) {
563 odd_left = !view->dual_even_left;
564 dual = TRUE;
565 }
566 }
567 break;
568 case EV_PAGE_LAYOUT_DUAL:
569 odd_left = !view->dual_even_left;
570 if (ev_document_get_n_pages (view->document) > 1)
571 dual = TRUE;
572 break;
573 case EV_PAGE_LAYOUT_SINGLE:
574 break;
575 default:
576 g_assert_not_reached ();
577 }
578
579 if (odd_left_out)
580 *odd_left_out = odd_left;
581
582 return dual;
583 }
584
585 static void
scroll_to_point(EvView * view,gdouble x,gdouble y,GtkOrientation orientation)586 scroll_to_point (EvView *view,
587 gdouble x,
588 gdouble y,
589 GtkOrientation orientation)
590 {
591 gdouble page_size;
592 gdouble upper, lower;
593
594 if (orientation == GTK_ORIENTATION_VERTICAL) {
595 page_size = gtk_adjustment_get_page_size (view->vadjustment);
596 upper = gtk_adjustment_get_upper (view->vadjustment);
597 lower = gtk_adjustment_get_lower (view->vadjustment);
598
599 if (view->continuous) {
600 gtk_adjustment_clamp_page (view->vadjustment,
601 y, y + page_size);
602 } else {
603 gtk_adjustment_set_value (view->vadjustment,
604 CLAMP (y, lower, upper - page_size));
605 }
606 } else {
607 page_size = gtk_adjustment_get_page_size (view->hadjustment);
608 upper = gtk_adjustment_get_upper (view->hadjustment);
609 lower = gtk_adjustment_get_lower (view->hadjustment);
610
611 if (is_dual_page (view, NULL)) {
612 gtk_adjustment_clamp_page (view->hadjustment, x,
613 x + page_size);
614 } else {
615 gtk_adjustment_set_value (view->hadjustment,
616 CLAMP (x, lower, upper - page_size));
617 }
618 }
619 }
620
621 static void
ev_view_scroll_to_page_position(EvView * view,GtkOrientation orientation)622 ev_view_scroll_to_page_position (EvView *view, GtkOrientation orientation)
623 {
624 gdouble x, y;
625
626 if (!view->document)
627 return;
628
629 if ((orientation == GTK_ORIENTATION_VERTICAL && view->pending_point.y == 0) ||
630 (orientation == GTK_ORIENTATION_HORIZONTAL && view->pending_point.x == 0)) {
631 GdkRectangle page_area;
632 GtkBorder border;
633
634 ev_view_get_page_extents (view, view->current_page, &page_area, &border);
635 x = page_area.x;
636 y = page_area.y;
637 } else {
638 GdkPoint view_point;
639
640 _ev_view_transform_doc_point_to_view_point (view, view->current_page,
641 &view->pending_point, &view_point);
642 x = view_point.x;
643 y = view_point.y;
644 }
645
646 scroll_to_point (view, x, y, orientation);
647 }
648
649 static void
ev_view_set_adjustment_values(EvView * view,GtkOrientation orientation)650 ev_view_set_adjustment_values (EvView *view,
651 GtkOrientation orientation)
652 {
653 GtkWidget *widget = GTK_WIDGET (view);
654 GtkAdjustment *adjustment;
655 GtkAllocation allocation;
656 int req_size;
657 int alloc_size;
658 gdouble page_size;
659 gdouble value;
660 gdouble upper;
661 double factor;
662 gint new_value;
663 gdouble zoom_center;
664
665 gtk_widget_get_allocation (widget, &allocation);
666
667 if (orientation == GTK_ORIENTATION_HORIZONTAL) {
668 req_size = view->requisition.width;
669 alloc_size = allocation.width;
670 adjustment = view->hadjustment;
671 zoom_center = view->zoom_center_x;
672 } else {
673 req_size = view->requisition.height;
674 alloc_size = allocation.height;
675 adjustment = view->vadjustment;
676 zoom_center = view->zoom_center_y;
677 }
678
679 if (!adjustment)
680 return;
681
682 factor = 1.0;
683 value = gtk_adjustment_get_value (adjustment);
684 upper = gtk_adjustment_get_upper (adjustment);
685 page_size = gtk_adjustment_get_page_size (adjustment);
686 if (zoom_center < 0)
687 zoom_center = page_size * 0.5;
688
689 if (upper != .0) {
690 switch (view->pending_scroll) {
691 case SCROLL_TO_KEEP_POSITION:
692 case SCROLL_TO_FIND_LOCATION:
693 factor = value / upper;
694 break;
695 case SCROLL_TO_PAGE_POSITION:
696 break;
697 case SCROLL_TO_CENTER:
698 factor = (value + zoom_center) / upper;
699 break;
700 }
701 }
702
703 upper = MAX (alloc_size, req_size);
704 page_size = alloc_size;
705
706 gtk_adjustment_set_page_size (adjustment, page_size);
707 gtk_adjustment_set_step_increment (adjustment, alloc_size * 0.1);
708 gtk_adjustment_set_page_increment (adjustment, alloc_size * 0.9);
709 gtk_adjustment_set_lower (adjustment, 0);
710 gtk_adjustment_set_upper (adjustment, upper);
711
712 /*
713 * We add 0.5 to the values before to average out our rounding errors.
714 */
715 switch (view->pending_scroll) {
716 case SCROLL_TO_KEEP_POSITION:
717 case SCROLL_TO_FIND_LOCATION:
718 new_value = CLAMP (upper * factor + 0.5, 0, upper - page_size);
719 gtk_adjustment_set_value (adjustment, (int)new_value);
720 break;
721 case SCROLL_TO_PAGE_POSITION:
722 ev_view_scroll_to_page_position (view, orientation);
723 break;
724 case SCROLL_TO_CENTER:
725 new_value = CLAMP (upper * factor - zoom_center + 0.5, 0, upper - page_size);
726 if (orientation == GTK_ORIENTATION_HORIZONTAL)
727 view->zoom_center_x = -1.0;
728 else
729 view->zoom_center_y = -1.0;
730 gtk_adjustment_set_value (adjustment, (int)new_value);
731 break;
732 }
733 }
734
735 static void
view_update_range_and_current_page(EvView * view)736 view_update_range_and_current_page (EvView *view)
737 {
738 gint start = view->start_page;
739 gint end = view->end_page;
740 gboolean odd_left;
741
742 if (ev_document_get_n_pages (view->document) <= 0 ||
743 !ev_document_check_dimensions (view->document))
744 return;
745
746 if (view->continuous) {
747 GdkRectangle current_area, unused, page_area;
748 GtkBorder border;
749 gboolean found = FALSE;
750 gint area_max = -1, area;
751 gint best_current_page = -1;
752 gint n_pages;
753 int i, j = 0;
754
755 if (!(view->vadjustment && view->hadjustment))
756 return;
757
758 current_area.x = gtk_adjustment_get_value (view->hadjustment);
759 current_area.width = gtk_adjustment_get_page_size (view->hadjustment);
760 current_area.y = gtk_adjustment_get_value (view->vadjustment);
761 current_area.height = gtk_adjustment_get_page_size (view->vadjustment);
762
763 n_pages = ev_document_get_n_pages (view->document);
764 compute_border (view, &border);
765 for (i = 0; i < n_pages; i++) {
766
767 ev_view_get_page_extents_for_border (view, i, &border, &page_area);
768
769 if (gdk_rectangle_intersect (¤t_area, &page_area, &unused)) {
770 area = unused.width * unused.height;
771
772 if (!found) {
773 area_max = area;
774 view->start_page = i;
775 found = TRUE;
776 best_current_page = i;
777 }
778 if (area > area_max) {
779 best_current_page = (area == area_max) ? MIN (i, best_current_page) : i;
780 area_max = area;
781 }
782
783 view->end_page = i;
784 j = 0;
785 } else if (found && view->current_page <= view->end_page) {
786 if (is_dual_page (view, NULL) && j < 1) {
787 /* In dual mode we stop searching
788 * after two consecutive non-visible pages.
789 */
790 j++;
791 continue;
792 }
793 break;
794 }
795 }
796
797 if (view->pending_scroll == SCROLL_TO_KEEP_POSITION ||
798 view->pending_scroll == SCROLL_TO_FIND_LOCATION) {
799 best_current_page = MAX (best_current_page, view->start_page);
800
801 if (best_current_page >= 0 && view->current_page != best_current_page) {
802 view->current_page = best_current_page;
803 ev_view_set_loading (view, FALSE);
804 ev_document_model_set_page (view->model, best_current_page);
805 }
806 }
807 } else if (is_dual_page (view, &odd_left)) {
808 if (view->current_page % 2 == !odd_left) {
809 view->start_page = view->current_page;
810 if (view->current_page + 1 < ev_document_get_n_pages (view->document))
811 view->end_page = view->start_page + 1;
812 else
813 view->end_page = view->start_page;
814 } else {
815 if (view->current_page < 1)
816 view->start_page = view->current_page;
817 else
818 view->start_page = view->current_page - 1;
819 view->end_page = view->current_page;
820 }
821 } else {
822 view->start_page = view->current_page;
823 view->end_page = view->current_page;
824 }
825
826 if (view->start_page == -1 || view->end_page == -1)
827 return;
828
829 if (start != view->start_page || end != view->end_page) {
830 gint i;
831
832 for (i = start; i < view->start_page && start != -1; i++) {
833 hide_annotation_windows (view, i);
834 }
835
836 for (i = end; i > view->end_page && end != -1; i--) {
837 hide_annotation_windows (view, i);
838 }
839
840 ev_view_check_cursor_blink (view);
841 }
842
843 ev_page_cache_set_page_range (view->page_cache,
844 view->start_page,
845 view->end_page);
846 ev_pixbuf_cache_set_page_range (view->pixbuf_cache,
847 view->start_page,
848 view->end_page,
849 view->selection_info.selections);
850 if (view->accessible)
851 ev_view_accessible_set_page_range (EV_VIEW_ACCESSIBLE (view->accessible),
852 view->start_page,
853 view->end_page);
854
855 if (ev_pixbuf_cache_get_surface (view->pixbuf_cache, view->current_page))
856 gtk_widget_queue_draw (GTK_WIDGET (view));
857 }
858
859 static void
ev_view_set_scroll_adjustment(EvView * view,GtkOrientation orientation,GtkAdjustment * adjustment)860 ev_view_set_scroll_adjustment (EvView *view,
861 GtkOrientation orientation,
862 GtkAdjustment *adjustment)
863 {
864 GtkAdjustment **to_set;
865 const gchar *prop_name;
866
867 if (orientation == GTK_ORIENTATION_HORIZONTAL) {
868 to_set = &view->hadjustment;
869 prop_name = "hadjustment";
870 } else {
871 to_set = &view->vadjustment;
872 prop_name = "vadjustment";
873 }
874
875 if (adjustment && adjustment == *to_set)
876 return;
877
878 if (*to_set) {
879 g_signal_handlers_disconnect_by_func (*to_set,
880 (gpointer) on_adjustment_value_changed,
881 view);
882 g_object_unref (*to_set);
883 }
884
885 if (!adjustment)
886 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
887 g_signal_connect (adjustment, "value_changed",
888 G_CALLBACK (on_adjustment_value_changed),
889 view);
890 *to_set = g_object_ref_sink (adjustment);
891 ev_view_set_adjustment_values (view, orientation);
892
893 g_object_notify (G_OBJECT (view), prop_name);
894 }
895
896 static void
add_scroll_binding_keypad(GtkBindingSet * binding_set,guint keyval,GdkModifierType modifiers,GtkScrollType scroll,GtkOrientation orientation)897 add_scroll_binding_keypad (GtkBindingSet *binding_set,
898 guint keyval,
899 GdkModifierType modifiers,
900 GtkScrollType scroll,
901 GtkOrientation orientation)
902 {
903 guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
904
905 gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
906 "scroll", 2,
907 GTK_TYPE_SCROLL_TYPE, scroll,
908 GTK_TYPE_ORIENTATION, orientation);
909 gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
910 "scroll", 2,
911 GTK_TYPE_SCROLL_TYPE, scroll,
912 GTK_TYPE_ORIENTATION, orientation);
913 }
914
915 static gdouble
compute_scroll_increment(EvView * view,GtkScrollType scroll)916 compute_scroll_increment (EvView *view,
917 GtkScrollType scroll)
918 {
919 GtkWidget *widget = GTK_WIDGET (view);
920 GtkAdjustment *adjustment = view->vadjustment;
921 cairo_region_t *text_region, *region;
922 GtkAllocation allocation;
923 gint page;
924 GdkRectangle rect;
925 EvRectangle doc_rect;
926 GdkRectangle page_area;
927 GtkBorder border;
928 gdouble fraction = 1.0;
929
930 if (scroll != GTK_SCROLL_PAGE_BACKWARD && scroll != GTK_SCROLL_PAGE_FORWARD)
931 return gtk_adjustment_get_page_size (adjustment);
932
933 page = scroll == GTK_SCROLL_PAGE_BACKWARD ? view->start_page : view->end_page;
934
935 text_region = ev_page_cache_get_text_mapping (view->page_cache, page);
936 if (!text_region || cairo_region_is_empty (text_region))
937 return gtk_adjustment_get_page_size (adjustment);
938
939 gtk_widget_get_allocation (widget, &allocation);
940 ev_view_get_page_extents (view, page, &page_area, &border);
941 rect.x = page_area.x + view->scroll_x;
942 rect.y = view->scroll_y + (scroll == GTK_SCROLL_PAGE_BACKWARD ? 5 : allocation.height - 5);
943 rect.width = page_area.width;
944 rect.height = 1;
945 _ev_view_transform_view_rect_to_doc_rect (view, &rect, &page_area, &border, &doc_rect);
946
947 /* Convert the doc rectangle into a GdkRectangle */
948 rect.x = doc_rect.x1;
949 rect.y = doc_rect.y1;
950 rect.width = doc_rect.x2 - doc_rect.x1;
951 rect.height = MAX (1, doc_rect.y2 - doc_rect.y1);
952 region = cairo_region_create_rectangle (&rect);
953
954 cairo_region_intersect (region, text_region);
955 if (cairo_region_num_rectangles (region)) {
956 EvRenderContext *rc;
957 EvPage *ev_page;
958 cairo_region_t *sel_region;
959
960 cairo_region_get_rectangle (region, 0, &rect);
961 ev_page = ev_document_get_page (view->document, page);
962 rc = ev_render_context_new (ev_page, view->rotation, 0.);
963 ev_render_context_set_target_size (rc,
964 page_area.width - (border.left + border.right),
965 page_area.height - (border.left + border.right));
966 g_object_unref (ev_page);
967 /* Get the selection region to know the height of the line */
968 doc_rect.x1 = doc_rect.x2 = rect.x + 0.5;
969 doc_rect.y1 = doc_rect.y2 = rect.y + 0.5;
970
971 ev_document_doc_mutex_lock ();
972 sel_region = ev_selection_get_selection_region (EV_SELECTION (view->document),
973 rc, EV_SELECTION_STYLE_LINE,
974 &doc_rect);
975 ev_document_doc_mutex_unlock ();
976
977 g_object_unref (rc);
978
979 if (cairo_region_num_rectangles (sel_region) > 0) {
980 cairo_region_get_rectangle (sel_region, 0, &rect);
981 fraction = 1 - (rect.height / gtk_adjustment_get_page_size (adjustment));
982 /* jump the full page height if the line is too large a
983 * fraction of the page */
984 if (fraction < SCROLL_PAGE_THRESHOLD)
985 fraction = 1.0;
986 }
987 cairo_region_destroy (sel_region);
988 }
989 cairo_region_destroy (region);
990
991 return gtk_adjustment_get_page_size (adjustment) * fraction;
992
993 }
994
995 static void
ev_view_first_page(EvView * view)996 ev_view_first_page (EvView *view)
997 {
998 ev_document_model_set_page (view->model, 0);
999 }
1000
1001 static void
ev_view_last_page(EvView * view)1002 ev_view_last_page (EvView *view)
1003 {
1004 gint n_pages;
1005
1006 if (!view->document)
1007 return;
1008
1009 n_pages = ev_document_get_n_pages (view->document);
1010 if (n_pages <= 1)
1011 return;
1012
1013 ev_document_model_set_page (view->model, n_pages - 1);
1014 }
1015
1016 /**
1017 * ev_view_scroll:
1018 * @view: a #EvView
1019 * @scroll:
1020 * @horizontal:
1021 *
1022 * Deprecated: 3.10
1023 */
1024 void
ev_view_scroll(EvView * view,GtkScrollType scroll,gboolean horizontal)1025 ev_view_scroll (EvView *view,
1026 GtkScrollType scroll,
1027 gboolean horizontal)
1028 {
1029 GtkAdjustment *adjustment;
1030 double value, increment;
1031 gdouble upper, lower;
1032 gdouble page_size;
1033 gdouble step_increment;
1034 gboolean first_page = FALSE;
1035 gboolean last_page = FALSE;
1036
1037 if (view->key_binding_handled)
1038 return;
1039
1040 view->jump_to_find_result = FALSE;
1041
1042 if ((!horizontal && ev_view_page_fits (view, GTK_ORIENTATION_VERTICAL)) ||
1043 (horizontal && ev_view_page_fits (view, GTK_ORIENTATION_HORIZONTAL))) {
1044 switch (scroll) {
1045 case GTK_SCROLL_PAGE_BACKWARD:
1046 case GTK_SCROLL_STEP_BACKWARD:
1047 ev_view_previous_page (view);
1048 break;
1049 case GTK_SCROLL_PAGE_FORWARD:
1050 case GTK_SCROLL_STEP_FORWARD:
1051 ev_view_next_page (view);
1052 break;
1053 case GTK_SCROLL_START:
1054 ev_view_first_page (view);
1055 break;
1056 case GTK_SCROLL_END:
1057 ev_view_last_page (view);
1058 break;
1059 default:
1060 break;
1061 }
1062 return;
1063 }
1064
1065 /* Assign values for increment and vertical adjustment */
1066 adjustment = horizontal ? view->hadjustment : view->vadjustment;
1067 value = gtk_adjustment_get_value (adjustment);
1068 upper = gtk_adjustment_get_upper (adjustment);
1069 lower = gtk_adjustment_get_lower (adjustment);
1070 page_size = gtk_adjustment_get_page_size (adjustment);
1071 step_increment = gtk_adjustment_get_step_increment (adjustment);
1072
1073 /* Assign boolean for first and last page */
1074 if (view->current_page == 0)
1075 first_page = TRUE;
1076 if (view->current_page == ev_document_get_n_pages (view->document) - 1)
1077 last_page = TRUE;
1078
1079 switch (scroll) {
1080 case GTK_SCROLL_PAGE_BACKWARD:
1081 /* Do not jump backwards if at the first page */
1082 if (value == lower && first_page) {
1083 /* Do nothing */
1084 /* At the top of a page, assign the upper bound limit of previous page */
1085 } else if (value == lower) {
1086 value = upper - page_size;
1087 ev_view_previous_page (view);
1088 /* Jump to the top */
1089 } else {
1090 increment = compute_scroll_increment (view, GTK_SCROLL_PAGE_BACKWARD);
1091 value = MAX (value - increment, lower);
1092 }
1093 break;
1094 case GTK_SCROLL_PAGE_FORWARD:
1095 /* Do not jump forward if at the last page */
1096 if (value == (upper - page_size) && last_page) {
1097 /* Do nothing */
1098 /* At the bottom of a page, assign the lower bound limit of next page */
1099 } else if (value == (upper - page_size)) {
1100 value = 0;
1101 ev_view_next_page (view);
1102 /* Jump to the bottom */
1103 } else {
1104 increment = compute_scroll_increment (view, GTK_SCROLL_PAGE_FORWARD);
1105 value = MIN (value + increment, upper - page_size);
1106 }
1107 break;
1108 case GTK_SCROLL_STEP_BACKWARD:
1109 value -= step_increment;
1110 break;
1111 case GTK_SCROLL_STEP_FORWARD:
1112 value += step_increment;
1113 break;
1114 case GTK_SCROLL_STEP_DOWN:
1115 value -= step_increment / 10;
1116 break;
1117 case GTK_SCROLL_STEP_UP:
1118 value += step_increment / 10;
1119 break;
1120 case GTK_SCROLL_START:
1121 value = lower;
1122 if (!first_page)
1123 ev_view_first_page (view);
1124 break;
1125 case GTK_SCROLL_END:
1126 value = upper - page_size;
1127 if (!last_page)
1128 ev_view_last_page (view);
1129 /* Changing pages causes the top to be shown. Here we want the bottom shown. */
1130 view->pending_point.y = value;
1131 break;
1132 default:
1133 break;
1134 }
1135
1136 value = CLAMP (value, lower, upper - page_size);
1137
1138 gtk_adjustment_set_value (adjustment, value);
1139 }
1140
1141 static void
ev_view_scroll_internal(EvView * view,GtkScrollType scroll,GtkOrientation orientation)1142 ev_view_scroll_internal (EvView *view,
1143 GtkScrollType scroll,
1144 GtkOrientation orientation)
1145 {
1146 ev_view_scroll (view, scroll, orientation == GTK_ORIENTATION_HORIZONTAL);
1147 }
1148
1149 #define MARGIN 5
1150
1151 void
_ev_view_ensure_rectangle_is_visible(EvView * view,GdkRectangle * rect)1152 _ev_view_ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect)
1153 {
1154 GtkWidget *widget = GTK_WIDGET (view);
1155 GtkAdjustment *adjustment;
1156 GtkAllocation allocation;
1157 gdouble adj_value;
1158 int value;
1159
1160 view->pending_scroll = SCROLL_TO_FIND_LOCATION;
1161
1162 gtk_widget_get_allocation (widget, &allocation);
1163
1164 adjustment = view->vadjustment;
1165 adj_value = gtk_adjustment_get_value (adjustment);
1166
1167 if (rect->y < adj_value) {
1168 value = MAX (gtk_adjustment_get_lower (adjustment), rect->y - MARGIN);
1169 gtk_adjustment_set_value (view->vadjustment, value);
1170 } else if (rect->y + rect->height > adj_value + allocation.height) {
1171 value = MIN (gtk_adjustment_get_upper (adjustment), rect->y + rect->height -
1172 allocation.height + MARGIN);
1173 gtk_adjustment_set_value (view->vadjustment, value);
1174 }
1175
1176 adjustment = view->hadjustment;
1177 adj_value = gtk_adjustment_get_value (adjustment);
1178
1179 if (rect->x < adj_value) {
1180 value = MAX (gtk_adjustment_get_lower (adjustment), rect->x - MARGIN);
1181 gtk_adjustment_set_value (view->hadjustment, value);
1182 } else if (rect->x + rect->height > adj_value + allocation.width) {
1183 value = MIN (gtk_adjustment_get_upper (adjustment), rect->x + rect->width -
1184 allocation.width + MARGIN);
1185 gtk_adjustment_set_value (view->hadjustment, value);
1186 }
1187 }
1188
1189 /*** Geometry computations ***/
1190
1191 static void
compute_border(EvView * view,GtkBorder * border)1192 compute_border (EvView *view, GtkBorder *border)
1193 {
1194 GtkWidget *widget = GTK_WIDGET (view);
1195 GtkStyleContext *context = gtk_widget_get_style_context (widget);
1196 GtkStateFlags state = gtk_widget_get_state_flags (widget);
1197
1198 gtk_style_context_save (context);
1199 gtk_style_context_add_class (context, EV_STYLE_CLASS_DOCUMENT_PAGE);
1200 gtk_style_context_get_border (context, state, border);
1201 gtk_style_context_restore (context);
1202 }
1203
1204 void
_get_page_size_for_scale_and_rotation(EvDocument * document,gint page,gdouble scale,gint rotation,gint * page_width,gint * page_height)1205 _get_page_size_for_scale_and_rotation (EvDocument *document,
1206 gint page,
1207 gdouble scale,
1208 gint rotation,
1209 gint *page_width,
1210 gint *page_height)
1211 {
1212 gdouble w, h;
1213 gint width, height;
1214
1215 ev_document_get_page_size (document, page, &w, &h);
1216
1217 width = (gint)(w * scale + 0.5);
1218 height = (gint)(h * scale + 0.5);
1219
1220 if (page_width)
1221 *page_width = (rotation == 0 || rotation == 180) ? width : height;
1222 if (page_height)
1223 *page_height = (rotation == 0 || rotation == 180) ? height : width;
1224 }
1225
1226 static void
ev_view_get_page_size(EvView * view,gint page,gint * page_width,gint * page_height)1227 ev_view_get_page_size (EvView *view,
1228 gint page,
1229 gint *page_width,
1230 gint *page_height)
1231 {
1232 _get_page_size_for_scale_and_rotation (view->document,
1233 page,
1234 view->scale,
1235 view->rotation,
1236 page_width,
1237 page_height);
1238 }
1239
1240 static void
ev_view_get_max_page_size(EvView * view,gint * max_width,gint * max_height)1241 ev_view_get_max_page_size (EvView *view,
1242 gint *max_width,
1243 gint *max_height)
1244 {
1245 double w, h;
1246 gint width, height;
1247
1248 ev_document_get_max_page_size (view->document, &w, &h);
1249
1250 width = (gint)(w * view->scale + 0.5);
1251 height = (gint)(h * view->scale + 0.5);
1252
1253 if (max_width)
1254 *max_width = (view->rotation == 0 || view->rotation == 180) ? width : height;
1255 if (max_height)
1256 *max_height = (view->rotation == 0 || view->rotation == 180) ? height : width;
1257 }
1258
1259 static void
get_page_y_offset(EvView * view,int page,int * y_offset,GtkBorder * border)1260 get_page_y_offset (EvView *view, int page, int *y_offset, GtkBorder *border)
1261 {
1262 int offset = 0;
1263 gboolean odd_left;
1264
1265 g_return_if_fail (y_offset != NULL);
1266
1267 if (is_dual_page (view, &odd_left)) {
1268 ev_view_get_height_to_page (view, page, NULL, &offset);
1269 offset += ((page + !odd_left) / 2 + 1) * view->spacing +
1270 ((page + !odd_left) / 2 ) * (border->top + border->bottom);
1271 } else {
1272 ev_view_get_height_to_page (view, page, &offset, NULL);
1273 offset += (page + 1) * view->spacing + page * (border->top + border->bottom);
1274 }
1275
1276 *y_offset = offset;
1277 return;
1278 }
1279
1280 gboolean
ev_view_get_page_extents_for_border(EvView * view,gint page,GtkBorder * border,GdkRectangle * page_area)1281 ev_view_get_page_extents_for_border (EvView *view,
1282 gint page,
1283 GtkBorder *border,
1284 GdkRectangle *page_area)
1285 {
1286 return real_ev_view_get_page_extents (view, page, page_area, border, TRUE);
1287 }
1288
1289 gboolean
ev_view_get_page_extents(EvView * view,gint page,GdkRectangle * page_area,GtkBorder * border)1290 ev_view_get_page_extents (EvView *view,
1291 gint page,
1292 GdkRectangle *page_area,
1293 GtkBorder *border)
1294 {
1295 return real_ev_view_get_page_extents (view, page, page_area, border, FALSE);
1296 }
1297
1298 static gboolean
real_ev_view_get_page_extents(EvView * view,gint page,GdkRectangle * page_area,GtkBorder * border,gboolean use_passed_border)1299 real_ev_view_get_page_extents (EvView *view,
1300 gint page,
1301 GdkRectangle *page_area,
1302 GtkBorder *border,
1303 gboolean use_passed_border)
1304 {
1305 GtkWidget *widget;
1306 int width, height;
1307 GtkAllocation allocation;
1308
1309 widget = GTK_WIDGET (view);
1310 gtk_widget_get_allocation (widget, &allocation);
1311
1312 /* Get the size of the page */
1313 ev_view_get_page_size (view, page, &width, &height);
1314 if (!use_passed_border)
1315 compute_border (view, border);
1316 page_area->width = width + border->left + border->right;
1317 page_area->height = height + border->top + border->bottom;
1318
1319 if (view->continuous) {
1320 gint max_width;
1321 gint x, y;
1322 gboolean odd_left;
1323
1324 ev_view_get_max_page_size (view, &max_width, NULL);
1325 max_width = max_width + border->left + border->right;
1326 /* Get the location of the bounding box */
1327 if (is_dual_page (view, &odd_left)) {
1328 gboolean right_page;
1329
1330 right_page = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR && page % 2 == !odd_left) ||
1331 (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL && page % 2 == odd_left);
1332
1333 x = view->spacing + (right_page ? 0 : 1) * (max_width + view->spacing);
1334 x = x + MAX (0, allocation.width - (max_width * 2 + view->spacing * 3)) / 2;
1335 if (right_page)
1336 x = x + (max_width - width - border->left - border->right);
1337 } else {
1338 x = view->spacing;
1339 x = x + MAX (0, allocation.width - (width + border->left + border->right + view->spacing * 2)) / 2;
1340 }
1341
1342 get_page_y_offset (view, page, &y, border);
1343
1344 page_area->x = x;
1345 page_area->y = y;
1346 } else {
1347 gint x, y;
1348 gboolean odd_left;
1349
1350 if (is_dual_page (view, &odd_left)) {
1351 gint width_2, height_2;
1352 gint max_width = width;
1353 gint max_height = height;
1354 GtkBorder overall_border;
1355 gint other_page;
1356
1357 other_page = (page % 2 == !odd_left) ? page + 1: page - 1;
1358
1359 /* First, we get the bounding box of the two pages */
1360 if (other_page < ev_document_get_n_pages (view->document)
1361 && (0 <= other_page)) {
1362 ev_view_get_page_size (view, other_page,
1363 &width_2, &height_2);
1364 if (width_2 > width)
1365 max_width = width_2;
1366 if (height_2 > height)
1367 max_height = height_2;
1368 }
1369 if (!use_passed_border)
1370 compute_border (view, &overall_border);
1371 else
1372 overall_border = *border;
1373
1374 /* Find the offsets */
1375 x = view->spacing;
1376 y = view->spacing;
1377
1378 /* Adjust for being the left or right page */
1379 if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR && page % 2 == !odd_left) ||
1380 (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL && page % 2 == odd_left))
1381 x = x + max_width - width;
1382 else
1383 x = x + (max_width + overall_border.left + overall_border.right) + view->spacing;
1384
1385 y = y + (max_height - height)/2;
1386
1387 /* Adjust for extra allocation */
1388 x = x + MAX (0, allocation.width -
1389 ((max_width + overall_border.left + overall_border.right) * 2 + view->spacing * 3))/2;
1390 y = y + MAX (0, allocation.height - (height + view->spacing * 2))/2;
1391 } else {
1392 x = view->spacing;
1393 y = view->spacing;
1394
1395 /* Adjust for extra allocation */
1396 x = x + MAX (0, allocation.width - (width + border->left + border->right + view->spacing * 2))/2;
1397 y = y + MAX (0, allocation.height - (height + border->top + border->bottom + view->spacing * 2))/2;
1398 }
1399
1400 page_area->x = x;
1401 page_area->y = y;
1402 }
1403
1404 return TRUE;
1405 }
1406
1407 static void
get_doc_page_size(EvView * view,gint page,gdouble * width,gdouble * height)1408 get_doc_page_size (EvView *view,
1409 gint page,
1410 gdouble *width,
1411 gdouble *height)
1412 {
1413 double w, h;
1414
1415 ev_document_get_page_size (view->document, page, &w, &h);
1416 if (view->rotation == 0 || view->rotation == 180) {
1417 if (width) *width = w;
1418 if (height) *height = h;
1419 } else {
1420 if (width) *width = h;
1421 if (height) *height = w;
1422 }
1423 }
1424
1425 void
_ev_view_transform_view_point_to_doc_point(EvView * view,GdkPoint * view_point,GdkRectangle * page_area,GtkBorder * border,double * doc_point_x,double * doc_point_y)1426 _ev_view_transform_view_point_to_doc_point (EvView *view,
1427 GdkPoint *view_point,
1428 GdkRectangle *page_area,
1429 GtkBorder *border,
1430 double *doc_point_x,
1431 double *doc_point_y)
1432 {
1433 *doc_point_x = MAX ((double) (view_point->x - page_area->x - border->left) / view->scale, 0);
1434 *doc_point_y = MAX ((double) (view_point->y - page_area->y - border->top) / view->scale, 0);
1435 }
1436
1437 void
_ev_view_transform_view_rect_to_doc_rect(EvView * view,GdkRectangle * view_rect,GdkRectangle * page_area,GtkBorder * border,EvRectangle * doc_rect)1438 _ev_view_transform_view_rect_to_doc_rect (EvView *view,
1439 GdkRectangle *view_rect,
1440 GdkRectangle *page_area,
1441 GtkBorder *border,
1442 EvRectangle *doc_rect)
1443 {
1444 doc_rect->x1 = MAX ((double) (view_rect->x - page_area->x - border->left) / view->scale, 0);
1445 doc_rect->y1 = MAX ((double) (view_rect->y - page_area->y - border->top) / view->scale, 0);
1446 doc_rect->x2 = doc_rect->x1 + (double) view_rect->width / view->scale;
1447 doc_rect->y2 = doc_rect->y1 + (double) view_rect->height / view->scale;
1448 }
1449
1450 void
_ev_view_transform_doc_point_by_rotation_scale(EvView * view,int page,EvPoint * doc_point,GdkPoint * view_point)1451 _ev_view_transform_doc_point_by_rotation_scale (EvView *view,
1452 int page,
1453 EvPoint *doc_point,
1454 GdkPoint *view_point)
1455 {
1456 GdkRectangle page_area;
1457 GtkBorder border;
1458 double x, y, view_x, view_y;
1459
1460 switch (view->rotation) {
1461 case 0:
1462 x = doc_point->x;
1463 y = doc_point->y;
1464
1465 break;
1466 case 90: {
1467 gdouble width;
1468
1469 get_doc_page_size (view, page, &width, NULL);
1470 x = width - doc_point->y;
1471 y = doc_point->x;
1472 }
1473 break;
1474 case 180: {
1475 gdouble width, height;
1476
1477 get_doc_page_size (view, page, &width, &height);
1478 x = width - doc_point->x;
1479 y = height - doc_point->y;
1480 }
1481 break;
1482 case 270: {
1483 gdouble height;
1484
1485 get_doc_page_size (view, page, NULL, &height);
1486 x = doc_point->y;
1487 y = height - doc_point->x;
1488 }
1489 break;
1490 default:
1491 g_assert_not_reached ();
1492 }
1493
1494 ev_view_get_page_extents (view, page, &page_area, &border);
1495
1496 view_x = CLAMP ((gint)(x * view->scale + 0.5), 0, page_area.width);
1497 view_y = CLAMP ((gint)(y * view->scale + 0.5), 0, page_area.height);
1498
1499 view_point->x = view_x;
1500 view_point->y = view_y;
1501 }
1502
1503 void
_ev_view_transform_doc_point_to_view_point(EvView * view,int page,EvPoint * doc_point,GdkPoint * view_point)1504 _ev_view_transform_doc_point_to_view_point (EvView *view,
1505 int page,
1506 EvPoint *doc_point,
1507 GdkPoint *view_point)
1508 {
1509 GdkRectangle page_area;
1510 GtkBorder border;
1511 _ev_view_transform_doc_point_by_rotation_scale (view, page, doc_point, view_point);
1512
1513 ev_view_get_page_extents (view, page, &page_area, &border);
1514
1515 view_point->x = view_point->x + page_area.x + border.left;
1516 view_point->y = view_point->y + page_area.y + border.top;
1517 }
1518
1519 void
_ev_view_transform_doc_rect_to_view_rect(EvView * view,int page,EvRectangle * doc_rect,GdkRectangle * view_rect)1520 _ev_view_transform_doc_rect_to_view_rect (EvView *view,
1521 int page,
1522 EvRectangle *doc_rect,
1523 GdkRectangle *view_rect)
1524 {
1525 GdkRectangle page_area;
1526 GtkBorder border;
1527 double x, y, w, h;
1528
1529 switch (view->rotation) {
1530 case 0:
1531 x = doc_rect->x1;
1532 y = doc_rect->y1;
1533 w = doc_rect->x2 - doc_rect->x1;
1534 h = doc_rect->y2 - doc_rect->y1;
1535
1536 break;
1537 case 90: {
1538 gdouble width;
1539
1540 get_doc_page_size (view, page, &width, NULL);
1541 x = width - doc_rect->y2;
1542 y = doc_rect->x1;
1543 w = doc_rect->y2 - doc_rect->y1;
1544 h = doc_rect->x2 - doc_rect->x1;
1545 }
1546 break;
1547 case 180: {
1548 gdouble width, height;
1549
1550 get_doc_page_size (view, page, &width, &height);
1551 x = width - doc_rect->x2;
1552 y = height - doc_rect->y2;
1553 w = doc_rect->x2 - doc_rect->x1;
1554 h = doc_rect->y2 - doc_rect->y1;
1555 }
1556 break;
1557 case 270: {
1558 gdouble height;
1559
1560 get_doc_page_size (view, page, NULL, &height);
1561 x = doc_rect->y1;
1562 y = height - doc_rect->x2;
1563 w = doc_rect->y2 - doc_rect->y1;
1564 h = doc_rect->x2 - doc_rect->x1;
1565 }
1566 break;
1567 default:
1568 g_assert_not_reached ();
1569 }
1570
1571 ev_view_get_page_extents (view, page, &page_area, &border);
1572
1573 view_rect->x = (gint)(x * view->scale + 0.5) + page_area.x + border.left;
1574 view_rect->y = (gint)(y * view->scale + 0.5) + page_area.y + border.top;
1575 view_rect->width = (gint)(w * view->scale + 0.5);
1576 view_rect->height = (gint)(h * view->scale + 0.5);
1577 }
1578
1579 static void
find_page_at_location(EvView * view,gdouble x,gdouble y,gint * page,gint * x_offset,gint * y_offset)1580 find_page_at_location (EvView *view,
1581 gdouble x,
1582 gdouble y,
1583 gint *page,
1584 gint *x_offset,
1585 gint *y_offset)
1586 {
1587 int i;
1588 GtkBorder border;
1589
1590 if (view->document == NULL)
1591 return;
1592
1593 g_assert (page);
1594 g_assert (x_offset);
1595 g_assert (y_offset);
1596
1597 compute_border (view, &border);
1598 for (i = view->start_page; i >= 0 && i <= view->end_page; i++) {
1599 GdkRectangle page_area;
1600
1601 if (! ev_view_get_page_extents_for_border (view, i, &border, &page_area))
1602 continue;
1603
1604 if ((x >= page_area.x + border.left) &&
1605 (x < page_area.x + page_area.width - border.right) &&
1606 (y >= page_area.y + border.top) &&
1607 (y < page_area.y + page_area.height - border.bottom)) {
1608 *page = i;
1609 *x_offset = x - (page_area.x + border.left);
1610 *y_offset = y - (page_area.y + border.top);
1611 return;
1612 }
1613 }
1614
1615 *page = -1;
1616 }
1617
1618 static gboolean
location_in_text(EvView * view,gdouble x,gdouble y)1619 location_in_text (EvView *view,
1620 gdouble x,
1621 gdouble y)
1622 {
1623 cairo_region_t *region;
1624 gint page = -1;
1625 gint x_offset = 0, y_offset = 0;
1626
1627 find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
1628
1629 if (page == -1)
1630 return FALSE;
1631
1632 region = ev_page_cache_get_text_mapping (view->page_cache, page);
1633
1634 if (region)
1635 return cairo_region_contains_point (region, x_offset / view->scale, y_offset / view->scale);
1636 else
1637 return FALSE;
1638 }
1639
1640 static gboolean
location_in_selected_text(EvView * view,gdouble x,gdouble y)1641 location_in_selected_text (EvView *view,
1642 gdouble x,
1643 gdouble y)
1644 {
1645 cairo_region_t *region;
1646 gint page = -1;
1647 gint x_offset = 0, y_offset = 0;
1648
1649 find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
1650
1651 if (page == -1)
1652 return FALSE;
1653
1654 region = ev_pixbuf_cache_get_selection_region (view->pixbuf_cache, page, view->scale);
1655
1656 if (region)
1657 return cairo_region_contains_point (region, x_offset, y_offset);
1658 else
1659 return FALSE;
1660 }
1661
1662 static gboolean
get_doc_point_from_offset(EvView * view,gint page,gint x_offset,gint y_offset,gint * x_new,gint * y_new)1663 get_doc_point_from_offset (EvView *view,
1664 gint page,
1665 gint x_offset,
1666 gint y_offset,
1667 gint *x_new,
1668 gint *y_new)
1669 {
1670 gdouble width, height;
1671 double x, y;
1672
1673 get_doc_page_size (view, page, &width, &height);
1674
1675 x_offset = x_offset / view->scale;
1676 y_offset = y_offset / view->scale;
1677
1678 if (view->rotation == 0) {
1679 x = x_offset;
1680 y = y_offset;
1681 } else if (view->rotation == 90) {
1682 x = y_offset;
1683 y = width - x_offset;
1684 } else if (view->rotation == 180) {
1685 x = width - x_offset;
1686 y = height - y_offset;
1687 } else if (view->rotation == 270) {
1688 x = height - y_offset;
1689 y = x_offset;
1690 } else {
1691 g_assert_not_reached ();
1692 }
1693
1694 *x_new = x;
1695 *y_new = y;
1696
1697 return TRUE;
1698 }
1699
1700 static gboolean
get_doc_point_from_location(EvView * view,gdouble x,gdouble y,gint * page,gint * x_new,gint * y_new)1701 get_doc_point_from_location (EvView *view,
1702 gdouble x,
1703 gdouble y,
1704 gint *page,
1705 gint *x_new,
1706 gint *y_new)
1707 {
1708 gint x_offset = 0, y_offset = 0;
1709
1710 x += view->scroll_x;
1711 y += view->scroll_y;
1712 find_page_at_location (view, x, y, page, &x_offset, &y_offset);
1713 if (*page == -1)
1714 return FALSE;
1715
1716 return get_doc_point_from_offset (view, *page, x_offset, y_offset, x_new, y_new);
1717 }
1718
1719 static void
ev_view_get_area_from_mapping(EvView * view,guint page,EvMappingList * mapping_list,gconstpointer data,GdkRectangle * area)1720 ev_view_get_area_from_mapping (EvView *view,
1721 guint page,
1722 EvMappingList *mapping_list,
1723 gconstpointer data,
1724 GdkRectangle *area)
1725 {
1726 EvMapping *mapping;
1727
1728 mapping = ev_mapping_list_find (mapping_list, data);
1729 _ev_view_transform_doc_rect_to_view_rect (view, page, &mapping->area, area);
1730 area->x -= view->scroll_x;
1731 area->y -= view->scroll_y;
1732 }
1733
1734 static void
ev_view_put(EvView * view,GtkWidget * child_widget,gint x,gint y,guint page,EvRectangle * doc_rect)1735 ev_view_put (EvView *view,
1736 GtkWidget *child_widget,
1737 gint x,
1738 gint y,
1739 guint page,
1740 EvRectangle *doc_rect)
1741 {
1742 EvViewChild *child;
1743
1744 child = g_slice_new (EvViewChild);
1745
1746 child->widget = child_widget;
1747 child->x = x;
1748 child->y = y;
1749 child->page = page;
1750 child->doc_rect = *doc_rect;
1751
1752 gtk_widget_set_parent (child_widget, GTK_WIDGET (view));
1753 view->children = g_list_append (view->children, child);
1754 }
1755
1756 static void
ev_view_put_to_doc_rect(EvView * view,GtkWidget * child_widget,guint page,EvRectangle * doc_rect)1757 ev_view_put_to_doc_rect (EvView *view,
1758 GtkWidget *child_widget,
1759 guint page,
1760 EvRectangle *doc_rect)
1761 {
1762 GdkRectangle area;
1763
1764 _ev_view_transform_doc_rect_to_view_rect (view, page, doc_rect, &area);
1765 area.x -= view->scroll_x;
1766 area.y -= view->scroll_y;
1767 ev_view_put (view, child_widget, area.x, area.y, page, doc_rect);
1768 }
1769
1770 /*** Hyperref ***/
1771 static EvMapping *
get_link_mapping_at_location(EvView * view,gdouble x,gdouble y,gint * page)1772 get_link_mapping_at_location (EvView *view,
1773 gdouble x,
1774 gdouble y,
1775 gint *page)
1776 {
1777 gint x_new = 0, y_new = 0;
1778 EvMappingList *link_mapping;
1779
1780 if (!EV_IS_DOCUMENT_LINKS (view->document))
1781 return NULL;
1782
1783 if (!get_doc_point_from_location (view, x, y, page, &x_new, &y_new))
1784 return NULL;
1785
1786 link_mapping = ev_page_cache_get_link_mapping (view->page_cache, *page);
1787 if (link_mapping)
1788 return ev_mapping_list_get (link_mapping, x_new, y_new);
1789
1790 return NULL;
1791 }
1792
1793 static EvLink *
ev_view_get_link_at_location(EvView * view,gdouble x,gdouble y)1794 ev_view_get_link_at_location (EvView *view,
1795 gdouble x,
1796 gdouble y)
1797 {
1798 EvMapping *mapping;
1799 gint page;
1800
1801 mapping = get_link_mapping_at_location (view, x, y, &page);
1802
1803 return mapping ? mapping->data : NULL;
1804 }
1805
1806 static void
goto_fitr_dest(EvView * view,EvLinkDest * dest)1807 goto_fitr_dest (EvView *view, EvLinkDest *dest)
1808 {
1809 EvPoint doc_point;
1810 gdouble left, top;
1811 gboolean change_left, change_top;
1812
1813 left = ev_link_dest_get_left (dest, &change_left);
1814 top = ev_link_dest_get_top (dest, &change_top);
1815
1816 if (view->allow_links_change_zoom) {
1817 gdouble doc_width, doc_height;
1818 gdouble zoom;
1819 GtkAllocation allocation;
1820
1821 gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
1822
1823 doc_width = ev_link_dest_get_right (dest) - left;
1824 doc_height = ev_link_dest_get_bottom (dest) - top;
1825
1826 zoom = zoom_for_size_fit_page (doc_width,
1827 doc_height,
1828 allocation.width,
1829 allocation.height);
1830
1831 ev_document_model_set_sizing_mode (view->model, EV_SIZING_FREE);
1832 ev_document_model_set_scale (view->model, zoom);
1833
1834 /* center the target box within the view */
1835 left -= (allocation.width / zoom - doc_width) / 2;
1836 top -= (allocation.height / zoom - doc_height) / 2;
1837 }
1838
1839 doc_point.x = change_left ? left : 0;
1840 doc_point.y = change_top ? top : 0;
1841 view->pending_point = doc_point;
1842
1843 ev_view_change_page (view, ev_link_dest_get_page (dest));
1844 }
1845
1846 static void
goto_fitv_dest(EvView * view,EvLinkDest * dest)1847 goto_fitv_dest (EvView *view, EvLinkDest *dest)
1848 {
1849 EvPoint doc_point;
1850 gint page;
1851 double left;
1852 gboolean change_left;
1853
1854 page = ev_link_dest_get_page (dest);
1855
1856 left = ev_link_dest_get_left (dest, &change_left);
1857 doc_point.x = change_left ? left : 0;
1858 doc_point.y = 0;
1859
1860 if (view->allow_links_change_zoom) {
1861 GtkAllocation allocation;
1862 gdouble doc_width, doc_height;
1863 double zoom;
1864
1865 gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
1866
1867 ev_document_get_page_size (view->document, page, &doc_width, &doc_height);
1868
1869 zoom = zoom_for_size_fit_height (doc_width - doc_point.x, doc_height,
1870 allocation.width,
1871 allocation.height);
1872
1873 ev_document_model_set_sizing_mode (view->model, EV_SIZING_FREE);
1874 ev_document_model_set_scale (view->model, zoom);
1875 }
1876
1877 view->pending_point = doc_point;
1878
1879 ev_view_change_page (view, page);
1880 }
1881
1882 static void
goto_fith_dest(EvView * view,EvLinkDest * dest)1883 goto_fith_dest (EvView *view, EvLinkDest *dest)
1884 {
1885 EvPoint doc_point;
1886 gint page;
1887 gdouble top;
1888 gboolean change_top;
1889
1890 page = ev_link_dest_get_page (dest);
1891
1892 top = ev_link_dest_get_top (dest, &change_top);
1893 doc_point.x = 0;
1894 doc_point.y = change_top ? top : 0;
1895
1896 if (view->allow_links_change_zoom) {
1897 GtkAllocation allocation;
1898 gdouble doc_width;
1899 gdouble zoom;
1900
1901 gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
1902
1903 ev_document_get_page_size (view->document, page, &doc_width, NULL);
1904
1905 zoom = zoom_for_size_fit_width (doc_width, top,
1906 allocation.width,
1907 allocation.height);
1908
1909 ev_document_model_set_sizing_mode (view->model, EV_SIZING_FIT_WIDTH);
1910 ev_document_model_set_scale (view->model, zoom);
1911 }
1912
1913 view->pending_point = doc_point;
1914
1915 ev_view_change_page (view, page);
1916 }
1917
1918 static void
goto_fit_dest(EvView * view,EvLinkDest * dest)1919 goto_fit_dest (EvView *view, EvLinkDest *dest)
1920 {
1921 int page;
1922
1923 page = ev_link_dest_get_page (dest);
1924
1925 if (view->allow_links_change_zoom) {
1926 double zoom;
1927 gdouble doc_width, doc_height;
1928 GtkAllocation allocation;
1929
1930 gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
1931
1932 ev_document_get_page_size (view->document, page, &doc_width, &doc_height);
1933
1934 zoom = zoom_for_size_fit_page (doc_width, doc_height,
1935 allocation.width,
1936 allocation.height);
1937
1938 ev_document_model_set_sizing_mode (view->model, EV_SIZING_FIT_PAGE);
1939 ev_document_model_set_scale (view->model, zoom);
1940 }
1941
1942 ev_view_change_page (view, page);
1943 }
1944
1945 static void
goto_xyz_dest(EvView * view,EvLinkDest * dest)1946 goto_xyz_dest (EvView *view, EvLinkDest *dest)
1947 {
1948 EvPoint doc_point;
1949 gint page;
1950 gdouble zoom, left, top;
1951 gboolean change_zoom, change_left, change_top;
1952
1953 zoom = ev_link_dest_get_zoom (dest, &change_zoom);
1954 page = ev_link_dest_get_page (dest);
1955
1956 if (view->allow_links_change_zoom && change_zoom && zoom > 1) {
1957 ev_document_model_set_sizing_mode (view->model, EV_SIZING_FREE);
1958 ev_document_model_set_scale (view->model, zoom);
1959 }
1960
1961 left = ev_link_dest_get_left (dest, &change_left);
1962 top = ev_link_dest_get_top (dest, &change_top);
1963
1964 doc_point.x = change_left ? left : 0;
1965 doc_point.y = change_top ? top : 0;
1966 view->pending_point = doc_point;
1967
1968 ev_view_change_page (view, page);
1969 }
1970
1971 static void
goto_dest(EvView * view,EvLinkDest * dest)1972 goto_dest (EvView *view, EvLinkDest *dest)
1973 {
1974 EvLinkDestType type;
1975 int page, n_pages, current_page;
1976
1977 page = ev_link_dest_get_page (dest);
1978 n_pages = ev_document_get_n_pages (view->document);
1979
1980 if (page < 0 || page >= n_pages)
1981 return;
1982
1983 current_page = view->current_page;
1984
1985 type = ev_link_dest_get_dest_type (dest);
1986
1987 switch (type) {
1988 case EV_LINK_DEST_TYPE_PAGE:
1989 ev_document_model_set_page (view->model, page);
1990 break;
1991 case EV_LINK_DEST_TYPE_FIT:
1992 goto_fit_dest (view, dest);
1993 break;
1994 case EV_LINK_DEST_TYPE_FITH:
1995 goto_fith_dest (view, dest);
1996 break;
1997 case EV_LINK_DEST_TYPE_FITV:
1998 goto_fitv_dest (view, dest);
1999 break;
2000 case EV_LINK_DEST_TYPE_FITR:
2001 goto_fitr_dest (view, dest);
2002 break;
2003 case EV_LINK_DEST_TYPE_XYZ:
2004 goto_xyz_dest (view, dest);
2005 break;
2006 case EV_LINK_DEST_TYPE_PAGE_LABEL:
2007 ev_document_model_set_page_by_label (view->model, ev_link_dest_get_page_label (dest));
2008 break;
2009 default:
2010 g_assert_not_reached ();
2011 }
2012
2013 if (current_page != view->current_page)
2014 ev_document_model_set_page (view->model, view->current_page);
2015 }
2016
2017 static void
ev_view_goto_dest(EvView * view,EvLinkDest * dest)2018 ev_view_goto_dest (EvView *view, EvLinkDest *dest)
2019 {
2020 EvLinkDestType type;
2021
2022 type = ev_link_dest_get_dest_type (dest);
2023
2024 if (type == EV_LINK_DEST_TYPE_NAMED) {
2025 EvLinkDest *dest2;
2026 const gchar *named_dest;
2027
2028 named_dest = ev_link_dest_get_named_dest (dest);
2029 dest2 = ev_document_links_find_link_dest (EV_DOCUMENT_LINKS (view->document),
2030 named_dest);
2031 if (dest2) {
2032 goto_dest (view, dest2);
2033 g_object_unref (dest2);
2034 }
2035
2036 return;
2037 }
2038
2039 goto_dest (view, dest);
2040 }
2041
2042 void
ev_view_handle_link(EvView * view,EvLink * link)2043 ev_view_handle_link (EvView *view, EvLink *link)
2044 {
2045 EvLinkAction *action = NULL;
2046 EvLinkActionType type;
2047
2048 action = ev_link_get_action (link);
2049 if (!action)
2050 return;
2051
2052 type = ev_link_action_get_action_type (action);
2053
2054 switch (type) {
2055 case EV_LINK_ACTION_TYPE_GOTO_DEST: {
2056 EvLinkDest *dest;
2057 gint old_page = ev_document_model_get_page (view->model);
2058 g_signal_emit (view, signals[SIGNAL_HANDLE_LINK], 0, old_page, link);
2059
2060 dest = ev_link_action_get_dest (action);
2061 ev_view_goto_dest (view, dest);
2062 }
2063 break;
2064 case EV_LINK_ACTION_TYPE_LAYERS_STATE: {
2065 GList *show, *hide, *toggle;
2066 GList *l;
2067 EvDocumentLayers *document_layers;
2068
2069 document_layers = EV_DOCUMENT_LAYERS (view->document);
2070
2071 show = ev_link_action_get_show_list (action);
2072 for (l = show; l; l = g_list_next (l)) {
2073 ev_document_layers_show_layer (document_layers, EV_LAYER (l->data));
2074 }
2075
2076 hide = ev_link_action_get_hide_list (action);
2077 for (l = hide; l; l = g_list_next (l)) {
2078 ev_document_layers_hide_layer (document_layers, EV_LAYER (l->data));
2079 }
2080
2081 toggle = ev_link_action_get_toggle_list (action);
2082 for (l = toggle; l; l = g_list_next (l)) {
2083 EvLayer *layer = EV_LAYER (l->data);
2084
2085 if (ev_document_layers_layer_is_visible (document_layers, layer)) {
2086 ev_document_layers_hide_layer (document_layers, layer);
2087 } else {
2088 ev_document_layers_show_layer (document_layers, layer);
2089 }
2090 }
2091
2092 g_signal_emit (view, signals[SIGNAL_LAYERS_CHANGED], 0);
2093 ev_view_reload (view);
2094 }
2095 break;
2096 case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
2097 case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
2098 case EV_LINK_ACTION_TYPE_LAUNCH:
2099 case EV_LINK_ACTION_TYPE_NAMED:
2100 case EV_LINK_ACTION_TYPE_RESET_FORM:
2101 g_signal_emit (view, signals[SIGNAL_EXTERNAL_LINK], 0, action);
2102 break;
2103 }
2104 }
2105
2106 static char *
tip_from_action_named(EvLinkAction * action)2107 tip_from_action_named (EvLinkAction *action)
2108 {
2109 const gchar *name = ev_link_action_get_name (action);
2110
2111 if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
2112 return g_strdup (_("Go to first page"));
2113 } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
2114 return g_strdup (_("Go to previous page"));
2115 } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
2116 return g_strdup (_("Go to next page"));
2117 } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
2118 return g_strdup (_("Go to last page"));
2119 } else if (g_ascii_strcasecmp (name, "GoToPage") == 0) {
2120 return g_strdup (_("Go to page"));
2121 } else if (g_ascii_strcasecmp (name, "Find") == 0) {
2122 return g_strdup (_("Find"));
2123 }
2124
2125 return NULL;
2126 }
2127
2128 static char *
tip_from_link(EvView * view,EvLink * link)2129 tip_from_link (EvView *view, EvLink *link)
2130 {
2131 EvLinkAction *action;
2132 EvLinkActionType type;
2133 char *msg = NULL;
2134 char *page_label;
2135 const char *title;
2136
2137 action = ev_link_get_action (link);
2138 title = ev_link_get_title (link);
2139
2140 if (!action)
2141 return title ? g_strdup (title) : NULL;
2142
2143 type = ev_link_action_get_action_type (action);
2144
2145 switch (type) {
2146 case EV_LINK_ACTION_TYPE_GOTO_DEST:
2147 page_label = ev_document_links_get_dest_page_label (EV_DOCUMENT_LINKS (view->document),
2148 ev_link_action_get_dest (action));
2149 if (page_label) {
2150 msg = g_strdup_printf (_("Go to page %s"), page_label);
2151 g_free (page_label);
2152 }
2153 break;
2154 case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
2155 if (title) {
2156 msg = g_strdup_printf (_("Go to %s on file “%s”"), title,
2157 ev_link_action_get_filename (action));
2158 } else {
2159 msg = g_strdup_printf (_("Go to file “%s”"),
2160 ev_link_action_get_filename (action));
2161 }
2162 break;
2163 case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
2164 msg = g_strdup (ev_link_action_get_uri (action));
2165 break;
2166 case EV_LINK_ACTION_TYPE_LAUNCH:
2167 msg = g_strdup_printf (_("Launch %s"),
2168 ev_link_action_get_filename (action));
2169 break;
2170 case EV_LINK_ACTION_TYPE_NAMED:
2171 msg = tip_from_action_named (action);
2172 break;
2173 case EV_LINK_ACTION_TYPE_RESET_FORM:
2174 msg = g_strdup_printf (_("Reset form"));
2175 break;
2176 default:
2177 if (title)
2178 msg = g_strdup (title);
2179 break;
2180 }
2181
2182 return msg;
2183 }
2184
2185 static void
ev_view_handle_cursor_over_xy(EvView * view,gint x,gint y)2186 ev_view_handle_cursor_over_xy (EvView *view, gint x, gint y)
2187 {
2188 EvLink *link;
2189 EvFormField *field;
2190 EvAnnotation *annot = NULL;
2191 EvMedia *media;
2192
2193 if (view->cursor == EV_VIEW_CURSOR_HIDDEN)
2194 return;
2195
2196 if (view->adding_annot_info.adding_annot) {
2197 if (view->adding_annot_info.type == EV_ANNOTATION_TYPE_TEXT_MARKUP) {
2198 ev_view_set_cursor (view, EV_VIEW_CURSOR_IBEAM);
2199 } else if (!view->adding_annot_info.annot) {
2200 ev_view_set_cursor (view, EV_VIEW_CURSOR_ADD);
2201 }
2202 return;
2203 }
2204
2205 if (view->drag_info.in_drag) {
2206 if (view->cursor != EV_VIEW_CURSOR_DRAG)
2207 ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG);
2208 return;
2209 }
2210
2211 if (view->scroll_info.autoscrolling) {
2212 if (view->cursor != EV_VIEW_CURSOR_AUTOSCROLL)
2213 ev_view_set_cursor (view, EV_VIEW_CURSOR_AUTOSCROLL);
2214 return;
2215 }
2216
2217 link = ev_view_get_link_at_location (view, x, y);
2218 if (link) {
2219 GdkRectangle link_area;
2220 EvLinkAction *action;
2221 EvLinkDest *dest;
2222 EvLinkDestType type;
2223 GtkWidget *popover, *spinner;
2224 cairo_surface_t *page_surface = NULL;
2225 guint link_dest_page;
2226 EvPoint link_dest_doc;
2227 GdkPoint link_dest_view;
2228 gint device_scale = 1;
2229
2230 ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
2231
2232 if (link == view->link_preview.link)
2233 return;
2234
2235 /* Display thumbnail, if applicable */
2236 action = ev_link_get_action (link);
2237 if (!action)
2238 return;
2239
2240 dest = ev_link_action_get_dest (action);
2241 if (!dest)
2242 return;
2243
2244 type = ev_link_dest_get_dest_type (dest);
2245 if (type == EV_LINK_DEST_TYPE_NAMED) {
2246 dest = ev_document_links_find_link_dest (EV_DOCUMENT_LINKS (view->document),
2247 ev_link_dest_get_named_dest (dest));
2248 }
2249
2250 ev_view_link_preview_popover_cleanup (view);
2251
2252 /* Init popover */
2253 view->link_preview.popover = popover = gtk_popover_new (GTK_WIDGET (view));
2254 get_link_area (view, x, y, link, &link_area);
2255 gtk_popover_set_pointing_to (GTK_POPOVER (popover), &link_area);
2256 gtk_popover_set_modal (GTK_POPOVER (popover), FALSE);
2257 g_signal_connect_swapped (popover, "motion-notify-event",
2258 G_CALLBACK (link_preview_popover_motion_notify),
2259 view);
2260
2261 spinner = gtk_spinner_new ();
2262 gtk_spinner_start (GTK_SPINNER (spinner));
2263 gtk_container_add (GTK_CONTAINER (popover) , spinner);
2264 gtk_widget_show (spinner);
2265
2266 /* Start thumbnailing job async */
2267 link_dest_page = ev_link_dest_get_page (dest);
2268 #ifdef HAVE_HIDPI_SUPPORT
2269 device_scale = gtk_widget_get_scale_factor (GTK_WIDGET (view));
2270 #endif
2271 view->link_preview.job = ev_job_thumbnail_new (view->document,
2272 link_dest_page,
2273 view->rotation,
2274 view->scale * device_scale);
2275 ev_job_thumbnail_set_output_format (EV_JOB_THUMBNAIL (view->link_preview.job),
2276 EV_JOB_THUMBNAIL_SURFACE);
2277
2278 link_dest_doc.x = ev_link_dest_get_left (dest, NULL);
2279 link_dest_doc.y = ev_link_dest_get_top (dest, NULL);
2280 _ev_view_transform_doc_point_by_rotation_scale (view, link_dest_page,
2281 &link_dest_doc, &link_dest_view);
2282 view->link_preview.left = link_dest_view.x;
2283 view->link_preview.top = link_dest_view.y;
2284 view->link_preview.link = link;
2285
2286 page_surface = ev_pixbuf_cache_get_surface (view->pixbuf_cache, link_dest_page);
2287
2288 if (page_surface)
2289 link_preview_show_thumbnail (page_surface, view);
2290 else {
2291 g_signal_connect (view->link_preview.job, "finished",
2292 G_CALLBACK (link_preview_job_finished_cb),
2293 view);
2294 ev_job_scheduler_push_job (view->link_preview.job, EV_JOB_PRIORITY_LOW);
2295 }
2296
2297 if (type == EV_LINK_DEST_TYPE_NAMED)
2298 g_object_unref (dest);
2299
2300 view->link_preview.delay_timeout_id = g_timeout_add (LINK_PREVIEW_DELAY_MS,
2301 (GSourceFunc)link_preview_delayed_show,
2302 view);
2303 g_source_set_name_by_id (view->link_preview.delay_timeout_id,
2304 "[evince] link_preview_timeout");
2305 } else {
2306 ev_view_link_preview_popover_cleanup (view);
2307 view->link_preview.link = NULL;
2308
2309 if ((field = ev_view_get_form_field_at_location (view, x, y))) {
2310 if (field->is_read_only) {
2311 if (view->cursor == EV_VIEW_CURSOR_LINK ||
2312 view->cursor == EV_VIEW_CURSOR_IBEAM ||
2313 view->cursor == EV_VIEW_CURSOR_DRAG)
2314 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
2315 } else if (EV_IS_FORM_FIELD_TEXT (field)) {
2316 ev_view_set_cursor (view, EV_VIEW_CURSOR_IBEAM);
2317 } else {
2318 ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
2319 }
2320 } else if ((media = ev_view_get_media_at_location (view, x, y))) {
2321 if (!ev_view_find_player_for_media (view, media))
2322 ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
2323 else
2324 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
2325 } else if ((annot = ev_view_get_annotation_at_location (view, x, y))) {
2326 ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
2327 } else if (location_in_text (view, x + view->scroll_x, y + view->scroll_y)) {
2328 ev_view_set_cursor (view, EV_VIEW_CURSOR_IBEAM);
2329 } else {
2330 if (view->cursor == EV_VIEW_CURSOR_LINK ||
2331 view->cursor == EV_VIEW_CURSOR_IBEAM ||
2332 view->cursor == EV_VIEW_CURSOR_DRAG ||
2333 view->cursor == EV_VIEW_CURSOR_AUTOSCROLL ||
2334 view->cursor == EV_VIEW_CURSOR_ADD)
2335 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
2336 }
2337 }
2338
2339 if (link || annot || (field && ev_form_field_get_alternate_name (field)))
2340 g_object_set (view, "has-tooltip", TRUE, NULL);
2341 }
2342
2343 /*** Images ***/
2344 static EvImage *
ev_view_get_image_at_location(EvView * view,gdouble x,gdouble y)2345 ev_view_get_image_at_location (EvView *view,
2346 gdouble x,
2347 gdouble y)
2348 {
2349 gint page = -1;
2350 gint x_new = 0, y_new = 0;
2351 EvMappingList *image_mapping;
2352
2353 if (!EV_IS_DOCUMENT_IMAGES (view->document))
2354 return NULL;
2355
2356 if (!get_doc_point_from_location (view, x, y, &page, &x_new, &y_new))
2357 return NULL;
2358
2359 image_mapping = ev_page_cache_get_image_mapping (view->page_cache, page);
2360
2361 if (image_mapping)
2362 return ev_mapping_list_get_data (image_mapping, x_new, y_new);
2363 else
2364 return NULL;
2365 }
2366
2367 /*** Focus ***/
2368 static gboolean
ev_view_get_focused_area(EvView * view,GdkRectangle * area)2369 ev_view_get_focused_area (EvView *view,
2370 GdkRectangle *area)
2371 {
2372 if (!view->focused_element)
2373 return FALSE;
2374
2375 _ev_view_transform_doc_rect_to_view_rect (view,
2376 view->focused_element_page,
2377 &view->focused_element->area,
2378 area);
2379 area->x -= view->scroll_x + 1;
2380 area->y -= view->scroll_y + 1;
2381 area->width += 1;
2382 area->height += 1;
2383
2384 return TRUE;
2385 }
2386
2387 void
_ev_view_set_focused_element(EvView * view,EvMapping * element_mapping,gint page)2388 _ev_view_set_focused_element (EvView *view,
2389 EvMapping *element_mapping,
2390 gint page)
2391 {
2392 GdkRectangle view_rect;
2393 cairo_region_t *region = NULL;
2394
2395 if (view->accessible)
2396 ev_view_accessible_set_focused_element (EV_VIEW_ACCESSIBLE (view->accessible), element_mapping, page);
2397
2398 if (ev_view_get_focused_area (view, &view_rect))
2399 region = cairo_region_create_rectangle (&view_rect);
2400
2401 view->focused_element = element_mapping;
2402 view->focused_element_page = page;
2403
2404 if (ev_view_get_focused_area (view, &view_rect)) {
2405 if (!region)
2406 region = cairo_region_create_rectangle (&view_rect);
2407 else
2408 cairo_region_union_rectangle (region, &view_rect);
2409
2410 ev_document_model_set_page (view->model, page);
2411 view_rect.x += view->scroll_x;
2412 view_rect.y += view->scroll_y;
2413 _ev_view_ensure_rectangle_is_visible (view, &view_rect);
2414 }
2415
2416 if (region) {
2417 gdk_window_invalidate_region (gtk_widget_get_window (GTK_WIDGET (view)),
2418 region, TRUE);
2419 cairo_region_destroy (region);
2420 }
2421 }
2422
2423 /*** Forms ***/
2424 static EvMapping *
get_form_field_mapping_at_location(EvView * view,gdouble x,gdouble y,gint * page)2425 get_form_field_mapping_at_location (EvView *view,
2426 gdouble x,
2427 gdouble y,
2428 gint *page)
2429 {
2430 gint x_new = 0, y_new = 0;
2431 EvMappingList *forms_mapping;
2432
2433 if (!EV_IS_DOCUMENT_FORMS (view->document))
2434 return NULL;
2435
2436 if (!get_doc_point_from_location (view, x, y, page, &x_new, &y_new))
2437 return NULL;
2438
2439 forms_mapping = ev_page_cache_get_form_field_mapping (view->page_cache, *page);
2440
2441 if (forms_mapping)
2442 return ev_mapping_list_get (forms_mapping, x_new, y_new);
2443
2444 return NULL;
2445 }
2446
2447 static EvFormField *
ev_view_get_form_field_at_location(EvView * view,gdouble x,gdouble y)2448 ev_view_get_form_field_at_location (EvView *view,
2449 gdouble x,
2450 gdouble y)
2451 {
2452 EvMapping *field_mapping;
2453 gint page;
2454
2455 field_mapping = get_form_field_mapping_at_location (view, x, y, &page);
2456
2457 return field_mapping ? field_mapping->data : NULL;
2458 }
2459
2460 static cairo_region_t *
ev_view_form_field_get_region(EvView * view,EvFormField * field)2461 ev_view_form_field_get_region (EvView *view,
2462 EvFormField *field)
2463 {
2464 GdkRectangle view_area;
2465 EvMappingList *forms_mapping;
2466
2467 forms_mapping = ev_page_cache_get_form_field_mapping (view->page_cache,
2468 field->page->index);
2469 ev_view_get_area_from_mapping (view, field->page->index,
2470 forms_mapping,
2471 field, &view_area);
2472
2473 return cairo_region_create_rectangle (&view_area);
2474 }
2475
2476 static gboolean
ev_view_forms_remove_widgets(EvView * view)2477 ev_view_forms_remove_widgets (EvView *view)
2478 {
2479 ev_view_remove_all_form_fields (view);
2480
2481 return FALSE;
2482 }
2483
2484 static void
ev_view_form_field_destroy(GtkWidget * widget,EvView * view)2485 ev_view_form_field_destroy (GtkWidget *widget,
2486 EvView *view)
2487 {
2488 g_idle_add ((GSourceFunc)ev_view_forms_remove_widgets, view);
2489 }
2490
2491 static void
ev_view_form_field_button_toggle(EvView * view,EvFormField * field)2492 ev_view_form_field_button_toggle (EvView *view,
2493 EvFormField *field)
2494 {
2495 EvMappingList *forms_mapping;
2496 cairo_region_t *region;
2497 gboolean state;
2498 GList *l;
2499 EvFormFieldButton *field_button = EV_FORM_FIELD_BUTTON (field);
2500
2501 if (field_button->type == EV_FORM_FIELD_BUTTON_PUSH)
2502 return;
2503
2504 state = ev_document_forms_form_field_button_get_state (EV_DOCUMENT_FORMS (view->document),
2505 field);
2506
2507 /* FIXME: it actually depends on NoToggleToOff flags */
2508 if (field_button->type == EV_FORM_FIELD_BUTTON_RADIO && state && field_button->state)
2509 return;
2510
2511 region = ev_view_form_field_get_region (view, field);
2512
2513 /* For radio buttons and checkbox buttons that are in a set
2514 * we need to update also the region for the current selected item
2515 */
2516 forms_mapping = ev_page_cache_get_form_field_mapping (view->page_cache,
2517 field->page->index);
2518
2519 for (l = ev_mapping_list_get_list (forms_mapping); l; l = g_list_next (l)) {
2520 EvFormField *button = ((EvMapping *)(l->data))->data;
2521 cairo_region_t *button_region;
2522
2523 if (button->id == field->id)
2524 continue;
2525
2526 /* FIXME: only buttons in the same group should be updated */
2527 if (!EV_IS_FORM_FIELD_BUTTON (button) ||
2528 EV_FORM_FIELD_BUTTON (button)->type != field_button->type ||
2529 EV_FORM_FIELD_BUTTON (button)->state != TRUE)
2530 continue;
2531
2532 button_region = ev_view_form_field_get_region (view, button);
2533 cairo_region_union (region, button_region);
2534 cairo_region_destroy (button_region);
2535 }
2536
2537 /* Update state */
2538 ev_document_forms_form_field_button_set_state (EV_DOCUMENT_FORMS (view->document),
2539 field,
2540 !state);
2541 field_button->state = !state;
2542
2543 if (view->accessible)
2544 ev_view_accessible_update_element_state (EV_VIEW_ACCESSIBLE (view->accessible),
2545 ev_mapping_list_find (forms_mapping, field),
2546 field->page->index);
2547
2548 ev_view_reload_page (view, field->page->index, region);
2549 cairo_region_destroy (region);
2550 }
2551
2552 static GtkWidget *
ev_view_form_field_button_create_widget(EvView * view,EvFormField * field)2553 ev_view_form_field_button_create_widget (EvView *view,
2554 EvFormField *field)
2555 {
2556 EvMappingList *form_mapping;
2557 EvMapping *mapping;
2558
2559 /* We need to do this focus grab prior to setting the focused element for accessibility */
2560 if (!gtk_widget_has_focus (GTK_WIDGET (view)))
2561 gtk_widget_grab_focus (GTK_WIDGET (view));
2562
2563 form_mapping = ev_page_cache_get_form_field_mapping (view->page_cache,
2564 field->page->index);
2565 mapping = ev_mapping_list_find (form_mapping, field);
2566 _ev_view_set_focused_element (view, mapping, field->page->index);
2567
2568 return NULL;
2569 }
2570
2571 static void
ev_view_form_field_text_save(EvView * view,GtkWidget * widget)2572 ev_view_form_field_text_save (EvView *view,
2573 GtkWidget *widget)
2574 {
2575 EvFormField *field;
2576
2577 if (!view->document)
2578 return;
2579
2580 field = g_object_get_data (G_OBJECT (widget), "form-field");
2581
2582 if (field->changed) {
2583 EvFormFieldText *field_text = EV_FORM_FIELD_TEXT (field);
2584 cairo_region_t *field_region;
2585
2586 field_region = ev_view_form_field_get_region (view, field);
2587
2588 ev_document_forms_form_field_text_set_text (EV_DOCUMENT_FORMS (view->document),
2589 field, field_text->text);
2590 field->changed = FALSE;
2591 ev_view_reload_page (view, field->page->index, field_region);
2592 cairo_region_destroy (field_region);
2593 }
2594 }
2595
2596 static void
ev_view_form_field_text_changed(GtkWidget * widget,EvFormField * field)2597 ev_view_form_field_text_changed (GtkWidget *widget,
2598 EvFormField *field)
2599 {
2600 EvFormFieldText *field_text = EV_FORM_FIELD_TEXT (field);
2601 gchar *text = NULL;
2602
2603 if (GTK_IS_ENTRY (widget)) {
2604 text = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
2605 } else if (GTK_IS_TEXT_BUFFER (widget)) {
2606 GtkTextIter start, end;
2607
2608 gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (widget), &start, &end);
2609 text = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (widget),
2610 &start, &end, FALSE);
2611 }
2612
2613 if (!field_text->text ||
2614 (field_text->text && g_ascii_strcasecmp (field_text->text, text) != 0)) {
2615 g_free (field_text->text);
2616 field_text->text = text;
2617 field->changed = TRUE;
2618 }
2619 }
2620
2621 static gboolean
ev_view_form_field_text_focus_out(GtkWidget * widget,GdkEventFocus * event,EvView * view)2622 ev_view_form_field_text_focus_out (GtkWidget *widget,
2623 GdkEventFocus *event,
2624 EvView *view)
2625 {
2626 ev_view_form_field_text_save (view, widget);
2627
2628 return FALSE;
2629 }
2630
2631 static gboolean
ev_view_form_field_text_button_pressed(GtkWidget * widget,GdkEventButton * event,gpointer data)2632 ev_view_form_field_text_button_pressed (GtkWidget *widget,
2633 GdkEventButton *event,
2634 gpointer data)
2635 {
2636 return GDK_EVENT_STOP;
2637 }
2638
2639 static GtkWidget *
ev_view_form_field_text_create_widget(EvView * view,EvFormField * field)2640 ev_view_form_field_text_create_widget (EvView *view,
2641 EvFormField *field)
2642 {
2643 EvFormFieldText *field_text = EV_FORM_FIELD_TEXT (field);
2644 GtkWidget *text = NULL;
2645 gchar *txt;
2646 GtkStyleContext *context;
2647
2648 txt = ev_document_forms_form_field_text_get_text (EV_DOCUMENT_FORMS (view->document),
2649 field);
2650
2651 switch (field_text->type) {
2652 case EV_FORM_FIELD_TEXT_FILE_SELECT:
2653 /* TODO */
2654 case EV_FORM_FIELD_TEXT_NORMAL:
2655 text = gtk_entry_new ();
2656 gtk_entry_set_has_frame (GTK_ENTRY (text), FALSE);
2657 /* Remove '.flat' style added by previous call
2658 * gtk_entry_set_has_frame(FALSE) which caused bug #687 */
2659 context = gtk_widget_get_style_context (text);
2660 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_FLAT);
2661 gtk_entry_set_max_length (GTK_ENTRY (text), field_text->max_len);
2662 gtk_entry_set_visibility (GTK_ENTRY (text), !field_text->is_password);
2663
2664 if (txt) {
2665 gtk_entry_set_text (GTK_ENTRY (text), txt);
2666 g_free (txt);
2667 }
2668
2669 g_signal_connect (text, "focus-out-event",
2670 G_CALLBACK (ev_view_form_field_text_focus_out),
2671 view);
2672 g_signal_connect (text, "changed",
2673 G_CALLBACK (ev_view_form_field_text_changed),
2674 field);
2675 g_signal_connect_after (text, "activate",
2676 G_CALLBACK (ev_view_form_field_destroy),
2677 view);
2678 g_signal_connect_after (text, "button-press-event",
2679 G_CALLBACK (ev_view_form_field_text_button_pressed),
2680 NULL);
2681 break;
2682 case EV_FORM_FIELD_TEXT_MULTILINE: {
2683 GtkTextBuffer *buffer;
2684
2685 text = gtk_text_view_new ();
2686 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text));
2687
2688 if (txt) {
2689 gtk_text_buffer_set_text (buffer, txt, -1);
2690 g_free (txt);
2691 }
2692
2693 g_signal_connect (text, "focus-out-event",
2694 G_CALLBACK (ev_view_form_field_text_focus_out),
2695 view);
2696 g_signal_connect (buffer, "changed",
2697 G_CALLBACK (ev_view_form_field_text_changed),
2698 field);
2699 g_signal_connect_after (text, "button-press-event",
2700 G_CALLBACK (ev_view_form_field_text_button_pressed),
2701 NULL);
2702 }
2703 break;
2704 }
2705
2706 g_object_weak_ref (G_OBJECT (text),
2707 (GWeakNotify)ev_view_form_field_text_save,
2708 view);
2709
2710 return text;
2711 }
2712
2713 static void
ev_view_form_field_choice_save(EvView * view,GtkWidget * widget)2714 ev_view_form_field_choice_save (EvView *view,
2715 GtkWidget *widget)
2716 {
2717 EvFormField *field;
2718
2719 if (!view->document)
2720 return;
2721
2722 field = g_object_get_data (G_OBJECT (widget), "form-field");
2723
2724 if (field->changed) {
2725 GList *l;
2726 EvFormFieldChoice *field_choice = EV_FORM_FIELD_CHOICE (field);
2727 cairo_region_t *field_region;
2728
2729 field_region = ev_view_form_field_get_region (view, field);
2730
2731 if (field_choice->is_editable) {
2732 ev_document_forms_form_field_choice_set_text (EV_DOCUMENT_FORMS (view->document),
2733 field, field_choice->text);
2734 } else {
2735 ev_document_forms_form_field_choice_unselect_all (EV_DOCUMENT_FORMS (view->document), field);
2736 for (l = field_choice->selected_items; l; l = g_list_next (l)) {
2737 ev_document_forms_form_field_choice_select_item (EV_DOCUMENT_FORMS (view->document),
2738 field,
2739 GPOINTER_TO_INT (l->data));
2740 }
2741 }
2742 field->changed = FALSE;
2743 ev_view_reload_page (view, field->page->index, field_region);
2744 cairo_region_destroy (field_region);
2745 }
2746 }
2747
2748 static void
ev_view_form_field_choice_changed(GtkWidget * widget,EvFormField * field)2749 ev_view_form_field_choice_changed (GtkWidget *widget,
2750 EvFormField *field)
2751 {
2752 EvFormFieldChoice *field_choice = EV_FORM_FIELD_CHOICE (field);
2753
2754 if (GTK_IS_COMBO_BOX (widget)) {
2755 gint item;
2756
2757 item = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
2758 if (item != -1 && (!field_choice->selected_items ||
2759 GPOINTER_TO_INT (field_choice->selected_items->data) != item)) {
2760 g_list_free (field_choice->selected_items);
2761 field_choice->selected_items = NULL;
2762 field_choice->selected_items = g_list_prepend (field_choice->selected_items,
2763 GINT_TO_POINTER (item));
2764 field->changed = TRUE;
2765 }
2766
2767 if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (widget))) {
2768 const gchar *text;
2769
2770 text = gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (widget))));
2771 if (!field_choice->text ||
2772 (field_choice->text && g_ascii_strcasecmp (field_choice->text, text) != 0)) {
2773 g_free (field_choice->text);
2774 field_choice->text = g_strdup (text);
2775 field->changed = TRUE;
2776 }
2777 }
2778 } else if (GTK_IS_TREE_SELECTION (widget)) {
2779 GtkTreeSelection *selection = GTK_TREE_SELECTION (widget);
2780 GtkTreeModel *model;
2781 GList *items, *l;
2782
2783 items = gtk_tree_selection_get_selected_rows (selection, &model);
2784 g_list_free (field_choice->selected_items);
2785 field_choice->selected_items = NULL;
2786
2787 for (l = items; l && l->data; l = g_list_next (l)) {
2788 GtkTreeIter iter;
2789 GtkTreePath *path = (GtkTreePath *)l->data;
2790 gint item;
2791
2792 gtk_tree_model_get_iter (model, &iter, path);
2793 gtk_tree_model_get (model, &iter, 1, &item, -1);
2794
2795 field_choice->selected_items = g_list_prepend (field_choice->selected_items,
2796 GINT_TO_POINTER (item));
2797
2798 gtk_tree_path_free (path);
2799 }
2800
2801 g_list_free (items);
2802
2803 field->changed = TRUE;
2804 }
2805 }
2806
2807 typedef struct _PopupShownData {
2808 GtkWidget *choice;
2809 EvFormField *field;
2810 EvView *view;
2811 } PopupShownData;
2812
2813 static gboolean
ev_view_form_field_choice_popup_shown_real(PopupShownData * data)2814 ev_view_form_field_choice_popup_shown_real (PopupShownData *data)
2815 {
2816 ev_view_form_field_choice_changed (data->choice, data->field);
2817 ev_view_form_field_destroy (data->choice, data->view);
2818
2819 g_object_unref (data->choice);
2820 g_object_unref (data->field);
2821 g_free (data);
2822
2823 return FALSE;
2824 }
2825
2826 static void
ev_view_form_field_choice_popup_shown_cb(GObject * self,GParamSpec * pspec,EvView * view)2827 ev_view_form_field_choice_popup_shown_cb (GObject *self,
2828 GParamSpec *pspec,
2829 EvView *view)
2830 {
2831 EvFormField *field;
2832 GtkWidget *choice;
2833 gboolean shown;
2834 PopupShownData *data;
2835
2836 g_object_get (self, "popup-shown", &shown, NULL);
2837 if (shown)
2838 return; /* popup is already opened */
2839
2840 /* Popup has been closed */
2841 field = g_object_get_data (self, "form-field");
2842 choice = GTK_WIDGET (self);
2843
2844 data = g_new0 (PopupShownData, 1);
2845 data->choice = g_object_ref (choice);
2846 data->field = g_object_ref (field);
2847 data->view = view;
2848 /* We need to use an idle here because combobox "active" item is not updated yet */
2849 g_idle_add ((GSourceFunc) ev_view_form_field_choice_popup_shown_real, (gpointer) data);
2850 }
2851
2852 static GtkWidget *
ev_view_form_field_choice_create_widget(EvView * view,EvFormField * field)2853 ev_view_form_field_choice_create_widget (EvView *view,
2854 EvFormField *field)
2855 {
2856 EvFormFieldChoice *field_choice = EV_FORM_FIELD_CHOICE (field);
2857 GtkWidget *choice;
2858 GtkTreeModel *model;
2859 gint n_items, i;
2860 gint selected_item = -1;
2861
2862 n_items = ev_document_forms_form_field_choice_get_n_items (EV_DOCUMENT_FORMS (view->document),
2863 field);
2864 model = GTK_TREE_MODEL (gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT));
2865 for (i = 0; i < n_items; i++) {
2866 GtkTreeIter iter;
2867 gchar *item;
2868
2869 item = ev_document_forms_form_field_choice_get_item (EV_DOCUMENT_FORMS (view->document),
2870 field, i);
2871 if (ev_document_forms_form_field_choice_is_item_selected (
2872 EV_DOCUMENT_FORMS (view->document), field, i)) {
2873 selected_item = i;
2874 /* FIXME: we need a get_selected_items function in poppler */
2875 field_choice->selected_items = g_list_prepend (field_choice->selected_items,
2876 GINT_TO_POINTER (i));
2877 }
2878
2879 if (item) {
2880 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
2881 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2882 0, item,
2883 1, i,
2884 -1);
2885 g_free (item);
2886 }
2887 }
2888
2889 if (field_choice->type == EV_FORM_FIELD_CHOICE_LIST) {
2890 GtkCellRenderer *renderer;
2891 GtkWidget *tree_view;
2892 GtkTreeSelection *selection;
2893
2894 tree_view = gtk_tree_view_new_with_model (model);
2895 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
2896
2897 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
2898 if (field_choice->multi_select) {
2899 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2900 }
2901
2902 /* TODO: set selected items */
2903
2904 renderer = gtk_cell_renderer_text_new ();
2905 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view),
2906 0,
2907 "choix", renderer,
2908 "text", 0,
2909 NULL);
2910
2911 choice = gtk_scrolled_window_new (NULL, NULL);
2912 gtk_container_add (GTK_CONTAINER (choice), tree_view);
2913 gtk_widget_show (tree_view);
2914
2915 g_signal_connect (selection, "changed",
2916 G_CALLBACK (ev_view_form_field_choice_changed),
2917 field);
2918 g_signal_connect_after (selection, "changed",
2919 G_CALLBACK (ev_view_form_field_destroy),
2920 view);
2921 } else if (field_choice->is_editable) { /* ComboBoxEntry */
2922 GtkEntry *combo_entry;
2923 gchar *text;
2924
2925 choice = gtk_combo_box_new_with_model_and_entry (model);
2926 combo_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (choice)));
2927 /* This sets GtkEntry's minimum-width to be 1 char long, short enough
2928 * to workaround gtk issue gtk#1422 . Evince issue #1002 */
2929 gtk_entry_set_width_chars (combo_entry, 1);
2930 gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (choice), 0);
2931
2932 text = ev_document_forms_form_field_choice_get_text (EV_DOCUMENT_FORMS (view->document), field);
2933 if (text) {
2934 gtk_entry_set_text (combo_entry, text);
2935 g_free (text);
2936 }
2937
2938 g_signal_connect (choice, "changed",
2939 G_CALLBACK (ev_view_form_field_choice_changed),
2940 field);
2941 g_signal_connect_after (gtk_bin_get_child (GTK_BIN (choice)),
2942 "activate",
2943 G_CALLBACK (ev_view_form_field_destroy),
2944 view);
2945 } else { /* ComboBoxText */
2946 GtkCellRenderer *renderer;
2947
2948 choice = gtk_combo_box_new_with_model (model);
2949 renderer = gtk_cell_renderer_text_new ();
2950 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (choice),
2951 renderer, TRUE);
2952 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (choice),
2953 renderer,
2954 "text", 0,
2955 NULL);
2956 gtk_combo_box_set_active (GTK_COMBO_BOX (choice), selected_item);
2957 gtk_combo_box_popup (GTK_COMBO_BOX (choice));
2958
2959 /* See issue #294 for why we use this instead of "changed" signal */
2960 g_signal_connect (choice, "notify::popup-shown",
2961 G_CALLBACK (ev_view_form_field_choice_popup_shown_cb),
2962 view);
2963 }
2964
2965 g_object_unref (model);
2966
2967 g_object_weak_ref (G_OBJECT (choice),
2968 (GWeakNotify)ev_view_form_field_choice_save,
2969 view);
2970
2971 return choice;
2972 }
2973
2974 void
_ev_view_focus_form_field(EvView * view,EvFormField * field)2975 _ev_view_focus_form_field (EvView *view,
2976 EvFormField *field)
2977 {
2978 GtkWidget *field_widget = NULL;
2979 EvMappingList *form_field_mapping;
2980 EvMapping *mapping;
2981
2982 _ev_view_set_focused_element (view, NULL, -1);
2983
2984 if (field->is_read_only)
2985 return;
2986
2987 if (EV_IS_FORM_FIELD_BUTTON (field)) {
2988 field_widget = ev_view_form_field_button_create_widget (view, field);
2989 } else if (EV_IS_FORM_FIELD_TEXT (field)) {
2990 field_widget = ev_view_form_field_text_create_widget (view, field);
2991 } else if (EV_IS_FORM_FIELD_CHOICE (field)) {
2992 field_widget = ev_view_form_field_choice_create_widget (view, field);
2993 } else if (EV_IS_FORM_FIELD_SIGNATURE (field)) {
2994 /* TODO */
2995 }
2996
2997 /* Form field doesn't require a widget */
2998 if (!field_widget) {
2999 if (!gtk_widget_has_focus (GTK_WIDGET (view)))
3000 gtk_widget_grab_focus (GTK_WIDGET (view));
3001 return;
3002 }
3003
3004 g_object_set_data_full (G_OBJECT (field_widget), "form-field",
3005 g_object_ref (field),
3006 (GDestroyNotify)g_object_unref);
3007
3008 form_field_mapping = ev_page_cache_get_form_field_mapping (view->page_cache,
3009 field->page->index);
3010 mapping = ev_mapping_list_find (form_field_mapping, field);
3011 _ev_view_set_focused_element (view, mapping, field->page->index);
3012 ev_view_put_to_doc_rect (view, field_widget, field->page->index, &mapping->area);
3013 gtk_widget_show (field_widget);
3014 gtk_widget_grab_focus (field_widget);
3015 }
3016
3017 static void
ev_view_handle_form_field(EvView * view,EvFormField * field)3018 ev_view_handle_form_field (EvView *view,
3019 EvFormField *field)
3020 {
3021 if (field->is_read_only)
3022 return;
3023
3024 _ev_view_focus_form_field (view, field);
3025
3026 if (field->activation_link)
3027 ev_view_handle_link (view, field->activation_link);
3028
3029 if (EV_IS_FORM_FIELD_BUTTON (field))
3030 ev_view_form_field_button_toggle (view, field);
3031
3032 }
3033
3034 /* Media */
3035 static EvMapping *
get_media_mapping_at_location(EvView * view,gdouble x,gdouble y,gint * page)3036 get_media_mapping_at_location (EvView *view,
3037 gdouble x,
3038 gdouble y,
3039 gint *page)
3040 {
3041 #ifdef ENABLE_MULTIMEDIA
3042 gint x_new = 0, y_new = 0;
3043 EvMappingList *media_mapping;
3044
3045 if (!EV_IS_DOCUMENT_MEDIA (view->document))
3046 return NULL;
3047
3048 if (!get_doc_point_from_location (view, x, y, page, &x_new, &y_new))
3049 return NULL;
3050
3051 media_mapping = ev_page_cache_get_media_mapping (view->page_cache, *page);
3052
3053 return media_mapping ? ev_mapping_list_get (media_mapping, x_new, y_new) : NULL;
3054 #else
3055 return NULL;
3056 #endif
3057 }
3058
3059 static EvMedia *
ev_view_get_media_at_location(EvView * view,gdouble x,gdouble y)3060 ev_view_get_media_at_location (EvView *view,
3061 gdouble x,
3062 gdouble y)
3063 {
3064 EvMapping *media_mapping;
3065 gint page;
3066
3067 media_mapping = get_media_mapping_at_location (view, x, y, &page);
3068
3069 return media_mapping ? media_mapping->data : NULL;
3070 }
3071
3072 static gboolean
ev_view_find_player_for_media(EvView * view,EvMedia * media)3073 ev_view_find_player_for_media (EvView *view,
3074 EvMedia *media)
3075 {
3076 #ifdef ENABLE_MULTIMEDIA
3077 GList *l;
3078
3079 for (l = view->children; l; l = g_list_next (l)) {
3080 EvViewChild *child = (EvViewChild *)l->data;
3081
3082 if (!EV_IS_MEDIA_PLAYER (child->widget))
3083 continue;
3084
3085 if (ev_media_player_get_media (EV_MEDIA_PLAYER (child->widget)) == media)
3086 return TRUE;
3087 }
3088 #endif
3089
3090 return FALSE;
3091 }
3092
3093 static void
ev_view_handle_media(EvView * view,EvMedia * media)3094 ev_view_handle_media (EvView *view,
3095 EvMedia *media)
3096 {
3097 #ifdef ENABLE_MULTIMEDIA
3098 GtkWidget *player;
3099 EvMappingList *media_mapping;
3100 EvMapping *mapping;
3101 GdkRectangle render_area;
3102 guint page;
3103
3104 page = ev_media_get_page_index (media);
3105 media_mapping = ev_page_cache_get_media_mapping (view->page_cache, page);
3106
3107 /* TODO: focus? */
3108
3109 if (ev_view_find_player_for_media (view, media))
3110 return;
3111
3112 player = ev_media_player_new (media);
3113
3114 mapping = ev_mapping_list_find (media_mapping, media);
3115 _ev_view_transform_doc_rect_to_view_rect (view, page, &mapping->area, &render_area);
3116 render_area.x -= view->scroll_x;
3117 render_area.y -= view->scroll_y;
3118
3119 ev_view_put (view, player, render_area.x, render_area.y, page, &mapping->area);
3120 gtk_widget_show (player);
3121 #endif /* ENABLE_MULTIMEDIA */
3122 }
3123
3124 /* Annotations */
3125 static GtkWidget *
get_window_for_annot(EvView * view,EvAnnotation * annot)3126 get_window_for_annot (EvView *view,
3127 EvAnnotation *annot)
3128 {
3129 if (view->annot_window_map == NULL)
3130 return NULL;
3131
3132 return g_hash_table_lookup (view->annot_window_map, annot);
3133 }
3134
3135 static void
map_annot_to_window(EvView * view,EvAnnotation * annot,GtkWidget * window)3136 map_annot_to_window (EvView *view,
3137 EvAnnotation *annot,
3138 GtkWidget *window)
3139 {
3140 if (view->annot_window_map == NULL)
3141 view->annot_window_map = g_hash_table_new (g_direct_hash, NULL);
3142
3143 g_hash_table_insert (view->annot_window_map, annot, window);
3144 }
3145
3146 static EvViewWindowChild *
ev_view_get_window_child(EvView * view,GtkWidget * window)3147 ev_view_get_window_child (EvView *view,
3148 GtkWidget *window)
3149 {
3150 GList *children = view->window_children;
3151
3152 while (children) {
3153 EvViewWindowChild *child;
3154
3155 child = (EvViewWindowChild *)children->data;
3156 children = children->next;
3157
3158 if (child->window == window)
3159 return child;
3160 }
3161
3162 return NULL;
3163 }
3164
3165 static void
ev_view_window_child_move(EvView * view,EvViewWindowChild * child,gint x,gint y)3166 ev_view_window_child_move (EvView *view,
3167 EvViewWindowChild *child,
3168 gint x,
3169 gint y)
3170 {
3171 GtkAllocation allocation;
3172 gint width, height;
3173
3174 gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
3175 gtk_window_get_size (GTK_WINDOW (child->window), &width, &height);
3176
3177 child->x = x;
3178 child->y = y;
3179 gtk_window_move (GTK_WINDOW (child->window),
3180 CLAMP (x, child->parent_x,
3181 child->parent_x + allocation.width - width),
3182 CLAMP (y, child->parent_y,
3183 child->parent_y + allocation.height - height));
3184 }
3185
3186 static void
ev_view_window_child_move_with_parent(EvView * view,GtkWidget * window)3187 ev_view_window_child_move_with_parent (EvView *view,
3188 GtkWidget *window)
3189 {
3190 EvViewWindowChild *child;
3191 gint root_x, root_y;
3192
3193 child = ev_view_get_window_child (view, window);
3194 gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (view)),
3195 &root_x, &root_y);
3196 if (root_x != child->parent_x || root_y != child->parent_y) {
3197 gint dest_x, dest_y;
3198
3199 dest_x = child->x + (root_x - child->parent_x);
3200 dest_y = child->y + (root_y - child->parent_y);
3201 child->parent_x = root_x;
3202 child->parent_y = root_y;
3203 ev_view_window_child_move (view, child, dest_x, dest_y);
3204 }
3205
3206 if (child->visible && !gtk_widget_get_visible (window))
3207 gtk_widget_show (window);
3208 }
3209
3210 static void
ev_view_window_child_put(EvView * view,GtkWidget * window,guint page,gint x,gint y,gdouble orig_x,gdouble orig_y)3211 ev_view_window_child_put (EvView *view,
3212 GtkWidget *window,
3213 guint page,
3214 gint x,
3215 gint y,
3216 gdouble orig_x,
3217 gdouble orig_y)
3218 {
3219 EvViewWindowChild *child;
3220 gint root_x, root_y;
3221
3222 gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (view)),
3223 &root_x, &root_y);
3224
3225 child = g_new0 (EvViewWindowChild, 1);
3226 child->window = window;
3227 child->page = page;
3228 child->orig_x = orig_x;
3229 child->orig_y = orig_y;
3230 child->parent_x = root_x;
3231 child->parent_y = root_y;
3232 child->visible = ev_annotation_window_is_open (EV_ANNOTATION_WINDOW (window));
3233 ev_view_window_child_move (view, child, x + root_x, y + root_y);
3234
3235 if (child->visible)
3236 gtk_widget_show (window);
3237 else
3238 gtk_widget_hide (window);
3239
3240 view->window_children = g_list_append (view->window_children, child);
3241 }
3242
3243 static void
ev_view_remove_window_child_for_annot(EvView * view,guint page,EvAnnotation * annot)3244 ev_view_remove_window_child_for_annot (EvView *view,
3245 guint page,
3246 EvAnnotation *annot)
3247 {
3248 GList *children = view->window_children;
3249
3250 while (children) {
3251 EvViewWindowChild *child;
3252 EvAnnotation *wannot;
3253
3254 child = (EvViewWindowChild *)children->data;
3255
3256 if (child->page != page) {
3257 children = children->next;
3258 continue;
3259 }
3260 wannot = ev_annotation_window_get_annotation (EV_ANNOTATION_WINDOW (child->window));
3261 if (ev_annotation_equal (wannot, annot)) {
3262 gtk_widget_destroy (child->window);
3263 view->window_children = g_list_delete_link (view->window_children, children);
3264 break;
3265 }
3266 children = children->next;
3267 }
3268 }
3269
3270 static void
ev_view_window_children_free(EvView * view)3271 ev_view_window_children_free (EvView *view)
3272 {
3273 GList *l;
3274
3275 if (!view->window_children)
3276 return;
3277
3278 for (l = view->window_children; l && l->data; l = g_list_next (l)) {
3279 EvViewWindowChild *child;
3280
3281 child = (EvViewWindowChild *)l->data;
3282 gtk_widget_destroy (GTK_WIDGET (child->window));
3283 g_free (child);
3284 }
3285 g_list_free (view->window_children);
3286 view->window_children = NULL;
3287 view->window_child_focus = NULL;
3288 }
3289
3290 static void
annotation_window_grab_focus(GtkWidget * widget,EvView * view)3291 annotation_window_grab_focus (GtkWidget *widget,
3292 EvView *view)
3293 {
3294 if (view->window_child_focus)
3295 ev_annotation_window_ungrab_focus (EV_ANNOTATION_WINDOW (view->window_child_focus->window));
3296 view->window_child_focus = ev_view_get_window_child (view, widget);
3297 }
3298
3299 static void
annotation_window_closed(EvAnnotationWindow * window,EvView * view)3300 annotation_window_closed (EvAnnotationWindow *window,
3301 EvView *view)
3302 {
3303 EvViewWindowChild *child;
3304
3305 child = ev_view_get_window_child (view, GTK_WIDGET (window));
3306 child->visible = FALSE;
3307 }
3308
3309 static void
annotation_window_moved(EvAnnotationWindow * window,gint x,gint y,EvView * view)3310 annotation_window_moved (EvAnnotationWindow *window,
3311 gint x,
3312 gint y,
3313 EvView *view)
3314 {
3315 EvViewWindowChild *child;
3316 GdkRectangle page_area;
3317 GtkBorder border;
3318 GdkRectangle view_rect;
3319 EvRectangle doc_rect;
3320 gint width, height;
3321
3322 child = ev_view_get_window_child (view, GTK_WIDGET (window));
3323 if (child->x == x && child->y == y)
3324 return;
3325
3326 child->moved = TRUE;
3327 child->x = x;
3328 child->y = y;
3329
3330 /* Window has been moved by the user,
3331 * we have to set a new origin in doc coords
3332 */
3333 gtk_window_get_size (GTK_WINDOW (window), &width, &height);
3334 view_rect.x = (x - child->parent_x) + view->scroll_x;
3335 view_rect.y = (y - child->parent_y) + view->scroll_y;
3336 view_rect.width = width;
3337 view_rect.height = height;
3338
3339 ev_view_get_page_extents (view, child->page, &page_area, &border);
3340 _ev_view_transform_view_rect_to_doc_rect (view, &view_rect, &page_area, &border, &doc_rect);
3341 child->orig_x = doc_rect.x1;
3342 child->orig_y = doc_rect.y1;
3343 }
3344
3345 static void
ev_view_annotation_save_contents(EvView * view,GParamSpec * pspec,EvAnnotation * annot)3346 ev_view_annotation_save_contents (EvView *view,
3347 GParamSpec *pspec,
3348 EvAnnotation *annot)
3349 {
3350 if (!view->document)
3351 return;
3352
3353 ev_document_doc_mutex_lock ();
3354 ev_document_annotations_save_annotation (EV_DOCUMENT_ANNOTATIONS (view->document),
3355 annot, EV_ANNOTATIONS_SAVE_CONTENTS);
3356 ev_document_doc_mutex_unlock ();
3357 g_signal_emit (view, signals[SIGNAL_ANNOT_CHANGED], 0, annot);
3358 }
3359
3360 static GtkWidget *
ev_view_create_annotation_window(EvView * view,EvAnnotation * annot,GtkWindow * parent)3361 ev_view_create_annotation_window (EvView *view,
3362 EvAnnotation *annot,
3363 GtkWindow *parent)
3364 {
3365 GtkWidget *window;
3366 EvRectangle doc_rect;
3367 GdkRectangle view_rect;
3368 guint page;
3369
3370 window = ev_annotation_window_new (annot, parent);
3371 g_signal_connect (window, "grab_focus",
3372 G_CALLBACK (annotation_window_grab_focus),
3373 view);
3374 g_signal_connect (window, "closed",
3375 G_CALLBACK (annotation_window_closed),
3376 view);
3377 g_signal_connect (window, "moved",
3378 G_CALLBACK (annotation_window_moved),
3379 view);
3380 g_signal_connect_swapped (annot, "notify::contents",
3381 G_CALLBACK (ev_view_annotation_save_contents),
3382 view);
3383 map_annot_to_window (view, annot, window);
3384
3385 page = ev_annotation_get_page_index (annot);
3386 ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (window), &doc_rect);
3387 _ev_view_transform_doc_rect_to_view_rect (view, page, &doc_rect, &view_rect);
3388 view_rect.x -= view->scroll_x;
3389 view_rect.y -= view->scroll_y;
3390
3391 ev_view_window_child_put (view, window, page,
3392 view_rect.x, view_rect.y,
3393 doc_rect.x1, doc_rect.y1);
3394 ev_annotation_window_set_enable_spellchecking (EV_ANNOTATION_WINDOW (window), ev_view_get_enable_spellchecking (view));
3395 return window;
3396 }
3397
3398 static void
show_annotation_windows(EvView * view,gint page)3399 show_annotation_windows (EvView *view,
3400 gint page)
3401 {
3402 EvMappingList *annots;
3403 GList *l;
3404
3405 annots = ev_page_cache_get_annot_mapping (view->page_cache, page);
3406
3407 for (l = ev_mapping_list_get_list (annots); l && l->data; l = g_list_next (l)) {
3408 EvAnnotation *annot;
3409 GtkWidget *window;
3410
3411 annot = ((EvMapping *)(l->data))->data;
3412
3413 if (!EV_IS_ANNOTATION_MARKUP (annot))
3414 continue;
3415
3416 if (!ev_annotation_markup_has_popup (EV_ANNOTATION_MARKUP (annot)))
3417 continue;
3418
3419 window = get_window_for_annot (view, annot);
3420 if (window) {
3421 ev_view_window_child_move_with_parent (view, window);
3422 }
3423 }
3424 }
3425
3426 static void
hide_annotation_windows(EvView * view,gint page)3427 hide_annotation_windows (EvView *view,
3428 gint page)
3429 {
3430 EvMappingList *annots;
3431 GList *l;
3432
3433 annots = ev_page_cache_get_annot_mapping (view->page_cache, page);
3434
3435 for (l = ev_mapping_list_get_list (annots); l && l->data; l = g_list_next (l)) {
3436 EvAnnotation *annot;
3437 GtkWidget *window;
3438
3439 annot = ((EvMapping *)(l->data))->data;
3440
3441 if (!EV_IS_ANNOTATION_MARKUP (annot))
3442 continue;
3443
3444 window = get_window_for_annot (view, annot);
3445 if (window)
3446 gtk_widget_hide (window);
3447 }
3448 }
3449
3450 static int
cmp_mapping_area_size(EvMapping * a,EvMapping * b)3451 cmp_mapping_area_size (EvMapping *a,
3452 EvMapping *b)
3453 {
3454 gdouble wa, ha, wb, hb;
3455
3456 wa = a->area.x2 - a->area.x1;
3457 ha = a->area.y2 - a->area.y1;
3458 wb = b->area.x2 - b->area.x1;
3459 hb = b->area.y2 - b->area.y1;
3460
3461 if (wa == wb) {
3462 if (ha == hb)
3463 return 0;
3464 return (ha < hb) ? -1 : 1;
3465 }
3466
3467 if (ha == hb) {
3468 return (wa < wb) ? -1 : 1;
3469 }
3470
3471 return (wa * ha < wb * hb) ? -1 : 1;
3472 }
3473
3474 static EvMapping *
get_annotation_mapping_at_location(EvView * view,gdouble x,gdouble y,gint * page)3475 get_annotation_mapping_at_location (EvView *view,
3476 gdouble x,
3477 gdouble y,
3478 gint *page)
3479 {
3480 gint x_new = 0, y_new = 0;
3481 EvMappingList *annotations_mapping;
3482 EvDocumentAnnotations *doc_annots;
3483 EvAnnotation *annot;
3484 EvMapping *best;
3485 GList *list;
3486
3487 if (!EV_IS_DOCUMENT_ANNOTATIONS (view->document))
3488 return NULL;
3489
3490 doc_annots = EV_DOCUMENT_ANNOTATIONS (view->document);
3491
3492 if (!doc_annots)
3493 return NULL;
3494
3495 if (!get_doc_point_from_location (view, x, y, page, &x_new, &y_new))
3496 return NULL;
3497
3498 annotations_mapping = ev_page_cache_get_annot_mapping (view->page_cache, *page);
3499
3500 if (!annotations_mapping)
3501 return NULL;
3502
3503 best = NULL;
3504 for (list = ev_mapping_list_get_list (annotations_mapping); list; list = list->next) {
3505 EvMapping *mapping = list->data;
3506
3507 if ((x_new >= mapping->area.x1) &&
3508 (y_new >= mapping->area.y1) &&
3509 (x_new <= mapping->area.x2) &&
3510 (y_new <= mapping->area.y2)) {
3511
3512 annot = EV_ANNOTATION (mapping->data);
3513
3514 if (ev_annotation_get_annotation_type (annot) == EV_ANNOTATION_TYPE_TEXT_MARKUP &&
3515 ev_document_annotations_over_markup (doc_annots, annot, (gdouble) x_new, (gdouble) y_new)
3516 == EV_ANNOTATION_OVER_MARKUP_NOT)
3517 continue; /* ignore markup annots clicked outside the markup text */
3518
3519 /* In case of only one match choose that. Otherwise
3520 * compare the area of the bounding boxes and return the
3521 * smallest element */
3522 if (best == NULL || cmp_mapping_area_size (mapping, best) < 0)
3523 best = mapping;
3524 }
3525 }
3526 return best;
3527 }
3528
3529 static EvAnnotation *
ev_view_get_annotation_at_location(EvView * view,gdouble x,gdouble y)3530 ev_view_get_annotation_at_location (EvView *view,
3531 gdouble x,
3532 gdouble y)
3533 {
3534 EvMapping *annotation_mapping;
3535 gint page;
3536
3537 annotation_mapping = get_annotation_mapping_at_location (view, x, y, &page);
3538
3539 return annotation_mapping ? annotation_mapping->data : NULL;
3540 }
3541
3542 static void
ev_view_annotation_show_popup_window(EvView * view,GtkWidget * window)3543 ev_view_annotation_show_popup_window (EvView *view,
3544 GtkWidget *window)
3545 {
3546 EvViewWindowChild *child;
3547
3548 if (!window)
3549 return;
3550
3551 child = ev_view_get_window_child (view, window);
3552 if (!child->visible) {
3553 child->visible = TRUE;
3554 ev_view_window_child_move (view, child, child->x, child->y);
3555 gtk_widget_show (window);
3556 }
3557 }
3558
3559 static void
ev_view_annotation_create_show_popup_window(EvView * view,EvAnnotation * annot)3560 ev_view_annotation_create_show_popup_window (EvView *view,
3561 EvAnnotation *annot)
3562 {
3563 GtkWindow *parent;
3564 /* the annotation window might already exist */
3565 GtkWidget *window = get_window_for_annot (view, annot);
3566
3567 if (!window) {
3568 parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view)));
3569 window = ev_view_create_annotation_window (view, annot, parent);
3570 }
3571
3572 ev_view_annotation_show_popup_window (view, window);
3573 }
3574
3575 static void
ev_view_handle_annotation(EvView * view,EvAnnotation * annot,gdouble x,gdouble y,guint32 timestamp)3576 ev_view_handle_annotation (EvView *view,
3577 EvAnnotation *annot,
3578 gdouble x,
3579 gdouble y,
3580 guint32 timestamp)
3581 {
3582 if (EV_IS_ANNOTATION_MARKUP (annot)) {
3583 GtkWidget *window;
3584
3585 window = get_window_for_annot (view, annot);
3586 if (!window && ev_annotation_markup_can_have_popup (EV_ANNOTATION_MARKUP (annot))) {
3587 EvRectangle popup_rect;
3588 GtkWindow *parent;
3589 EvMappingList *annots;
3590 EvMapping *mapping;
3591
3592 annots = ev_page_cache_get_annot_mapping (view->page_cache,
3593 ev_annotation_get_page_index (annot));
3594 mapping = ev_mapping_list_find (annots, annot);
3595
3596 popup_rect.x1 = mapping->area.x2;
3597 popup_rect.y1 = mapping->area.y2;
3598 popup_rect.x2 = popup_rect.x1 + ANNOT_POPUP_WINDOW_DEFAULT_WIDTH;
3599 popup_rect.y2 = popup_rect.y1 + ANNOT_POPUP_WINDOW_DEFAULT_HEIGHT;
3600 g_object_set (annot,
3601 "rectangle", &popup_rect,
3602 "has_popup", TRUE,
3603 "popup_is_open", TRUE,
3604 NULL);
3605
3606 parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view)));
3607 window = ev_view_create_annotation_window (view, annot, parent);
3608 } else if (window && ev_annotation_markup_has_popup (EV_ANNOTATION_MARKUP (annot))) {
3609 EvViewWindowChild *child;
3610 EvMappingList *annots;
3611 EvRectangle popup_rect;
3612 EvMapping *mapping;
3613 GdkPoint view_point;
3614 EvPoint annotation_corner;
3615
3616 child = ev_view_get_window_child (view, window);
3617 annots = ev_page_cache_get_annot_mapping (view->page_cache,
3618 ev_annotation_get_page_index (annot));
3619 mapping = ev_mapping_list_find (annots, annot);
3620 ev_annotation_markup_get_rectangle (EV_ANNOTATION_MARKUP (annot),
3621 &popup_rect);
3622
3623 popup_rect.x2 = mapping->area.x2 + popup_rect.x2 - popup_rect.x1;
3624 popup_rect.y2 = mapping->area.y2 + popup_rect.y2 - popup_rect.y1;
3625 popup_rect.x1 = mapping->area.x2;
3626 popup_rect.y1 = mapping->area.y2;
3627 g_object_set (annot,
3628 "rectangle", &popup_rect,
3629 "popup_is_open", TRUE,
3630 NULL);
3631
3632 annotation_corner.x = mapping->area.x2;
3633 annotation_corner.y = mapping->area.y2;
3634
3635 _ev_view_transform_doc_point_to_view_point (view,
3636 ev_annotation_get_page_index (annot),
3637 &annotation_corner,
3638 &view_point);
3639
3640 ev_view_window_child_move (view, child,
3641 child->parent_x + view_point.x - view->scroll_x,
3642 child->parent_y + view_point.y - view->scroll_y);
3643 }
3644 ev_view_annotation_show_popup_window (view, window);
3645 }
3646
3647 if (EV_IS_ANNOTATION_ATTACHMENT (annot)) {
3648 EvAttachment *attachment;
3649
3650 attachment = ev_annotation_attachment_get_attachment (EV_ANNOTATION_ATTACHMENT (annot));
3651 if (attachment) {
3652 GError *error = NULL;
3653
3654 ev_attachment_open (attachment,
3655 gtk_widget_get_screen (GTK_WIDGET (view)),
3656 timestamp,
3657 &error);
3658
3659 if (error) {
3660 g_warning ("%s", error->message);
3661 g_error_free (error);
3662 }
3663 }
3664 }
3665 }
3666
3667 static void
ev_view_create_annotation_real(EvView * view,gint annot_page,EvPoint start,EvPoint end)3668 ev_view_create_annotation_real (EvView *view,
3669 gint annot_page,
3670 EvPoint start,
3671 EvPoint end)
3672 {
3673 EvAnnotation *annot;
3674 EvRectangle doc_rect, popup_rect;
3675 EvPage *page;
3676 GdkColor color = { 0, 65535, 65535, 0 };
3677 GdkRectangle view_rect;
3678 cairo_region_t *region;
3679
3680 ev_document_doc_mutex_lock ();
3681 page = ev_document_get_page (view->document, annot_page);
3682 switch (view->adding_annot_info.type) {
3683 case EV_ANNOTATION_TYPE_TEXT:
3684 doc_rect.x1 = end.x;
3685 doc_rect.y1 = end.y;
3686 doc_rect.x2 = doc_rect.x1 + ANNOTATION_ICON_SIZE;
3687 doc_rect.y2 = doc_rect.y1 + ANNOTATION_ICON_SIZE;
3688 annot = ev_annotation_text_new (page);
3689 break;
3690 case EV_ANNOTATION_TYPE_TEXT_MARKUP:
3691 doc_rect.x1 = start.x;
3692 doc_rect.y1 = start.y;
3693 doc_rect.x2 = end.x;
3694 doc_rect.y2 = end.y;
3695 annot = ev_annotation_text_markup_highlight_new (page);
3696 break;
3697 case EV_ANNOTATION_TYPE_ATTACHMENT:
3698 /* TODO */
3699 g_object_unref (page);
3700 ev_document_doc_mutex_unlock ();
3701 return;
3702 default:
3703 g_assert_not_reached ();
3704 }
3705 g_object_unref (page);
3706
3707 ev_annotation_set_area (annot, &doc_rect);
3708 ev_annotation_set_color (annot, &color);
3709
3710 if (EV_IS_ANNOTATION_MARKUP (annot)) {
3711 popup_rect.x1 = doc_rect.x2;
3712 popup_rect.x2 = popup_rect.x1 + ANNOT_POPUP_WINDOW_DEFAULT_WIDTH;
3713 popup_rect.y1 = doc_rect.y2;
3714 popup_rect.y2 = popup_rect.y1 + ANNOT_POPUP_WINDOW_DEFAULT_HEIGHT;
3715 g_object_set (annot,
3716 "rectangle", &popup_rect,
3717 "can-have-popup", TRUE,
3718 "has_popup", TRUE,
3719 "popup_is_open", FALSE,
3720 "label", g_get_real_name (),
3721 "opacity", 1.0,
3722 NULL);
3723 }
3724 ev_document_annotations_add_annotation (EV_DOCUMENT_ANNOTATIONS (view->document),
3725 annot, &doc_rect);
3726 /* Re-fetch area as eg. adding Text Markup annots updates area for its bounding box */
3727 ev_annotation_get_area (annot, &doc_rect);
3728 ev_document_doc_mutex_unlock ();
3729
3730 /* If the page didn't have annots, mark the cache as dirty */
3731 if (!ev_page_cache_get_annot_mapping (view->page_cache, annot_page))
3732 ev_page_cache_mark_dirty (view->page_cache, annot_page, EV_PAGE_DATA_INCLUDE_ANNOTS);
3733
3734 _ev_view_transform_doc_rect_to_view_rect (view, annot_page, &doc_rect, &view_rect);
3735 view_rect.x -= view->scroll_x;
3736 view_rect.y -= view->scroll_y;
3737 region = cairo_region_create_rectangle (&view_rect);
3738 ev_view_reload_page (view, annot_page, region);
3739 cairo_region_destroy (region);
3740
3741 view->adding_annot_info.annot = annot;
3742 }
3743
3744 static void
ev_view_create_annotation(EvView * view)3745 ev_view_create_annotation (EvView *view)
3746 {
3747 EvPoint start;
3748 EvPoint end;
3749 gint annot_page;
3750 gint offset;
3751 GdkRectangle page_area;
3752 GtkBorder border;
3753
3754 find_page_at_location (view, view->adding_annot_info.start.x, view->adding_annot_info.start.y, &annot_page, &offset, &offset);
3755 if (annot_page == -1) {
3756 ev_view_cancel_add_annotation (view);
3757 return;
3758 }
3759
3760 ev_view_get_page_extents (view, annot_page, &page_area, &border);
3761 _ev_view_transform_view_point_to_doc_point (view, &view->adding_annot_info.start, &page_area, &border,
3762 &start.x, &start.y);
3763 _ev_view_transform_view_point_to_doc_point (view, &view->adding_annot_info.stop, &page_area, &border,
3764 &end.x, &end.y);
3765
3766 ev_view_create_annotation_real (view, annot_page, start, end);
3767 }
3768
3769 static gboolean
ev_view_get_doc_points_from_selection_region(EvView * view,gint page,EvPoint * begin,EvPoint * end)3770 ev_view_get_doc_points_from_selection_region (EvView *view,
3771 gint page,
3772 EvPoint *begin,
3773 EvPoint *end)
3774 {
3775 cairo_rectangle_int_t first, last;
3776 GdkPoint start, stop;
3777 cairo_region_t *region = NULL;
3778
3779 if (!view->pixbuf_cache)
3780 return FALSE;
3781
3782 region = ev_pixbuf_cache_get_selection_region (view->pixbuf_cache, page, view->scale);
3783
3784 if (!region)
3785 return FALSE;
3786
3787 cairo_region_get_rectangle (region, 0, &first);
3788 cairo_region_get_rectangle (region, cairo_region_num_rectangles(region) - 1, &last);
3789
3790 if (!get_doc_point_from_offset (view, page, first.x, first.y + (first.height / 2),
3791 &(start.x), &(start.y)))
3792 return FALSE;
3793
3794 if (!get_doc_point_from_offset (view, page, last.x + last.width, last.y + (last.height / 2),
3795 &(stop.x), &(stop.y)))
3796 return FALSE;
3797
3798 begin->x = start.x;
3799 begin->y = start.y;
3800 end->x = stop.x;
3801 end->y = stop.y;
3802
3803 return TRUE;
3804 }
3805
3806 static void
ev_view_create_annotation_from_selection(EvView * view,EvViewSelection * selection)3807 ev_view_create_annotation_from_selection (EvView *view,
3808 EvViewSelection *selection)
3809 {
3810 EvPoint doc_point_start;
3811 EvPoint doc_point_end;
3812
3813 /* Check if selection is of double/triple click type (STYLE_WORD and STYLE_LINE) and in that
3814 * case get the start/end points from the selection region of pixbuf cache. Issue #1119 */
3815 if (selection->style == EV_SELECTION_STYLE_WORD || selection->style == EV_SELECTION_STYLE_LINE) {
3816
3817 if (!ev_view_get_doc_points_from_selection_region (view, selection->page,
3818 &doc_point_start, &doc_point_end))
3819 return;
3820 } else {
3821 doc_point_start.x = selection->rect.x1;
3822 doc_point_start.y = selection->rect.y1;
3823 doc_point_end.x = selection->rect.x2;
3824 doc_point_end.y = selection->rect.y2;
3825 }
3826
3827 ev_view_create_annotation_real (view, selection->page, doc_point_start, doc_point_end);
3828 }
3829 void
ev_view_focus_annotation(EvView * view,EvMapping * annot_mapping)3830 ev_view_focus_annotation (EvView *view,
3831 EvMapping *annot_mapping)
3832 {
3833
3834 if (!EV_IS_DOCUMENT_ANNOTATIONS (view->document))
3835 return;
3836
3837 _ev_view_set_focused_element (view, annot_mapping,
3838 ev_annotation_get_page_index (EV_ANNOTATION (annot_mapping->data)));
3839 }
3840
3841 void
ev_view_begin_add_annotation(EvView * view,EvAnnotationType annot_type)3842 ev_view_begin_add_annotation (EvView *view,
3843 EvAnnotationType annot_type)
3844 {
3845 if (annot_type == EV_ANNOTATION_TYPE_UNKNOWN)
3846 return;
3847
3848 if (view->adding_annot_info.adding_annot)
3849 return;
3850
3851 view->adding_annot_info.adding_annot = TRUE;
3852 view->adding_annot_info.type = annot_type;
3853 ev_view_set_cursor (view, EV_VIEW_CURSOR_ADD);
3854 }
3855
3856 void
ev_view_cancel_add_annotation(EvView * view)3857 ev_view_cancel_add_annotation (EvView *view)
3858 {
3859 gint x, y;
3860
3861 if (!view->adding_annot_info.adding_annot)
3862 return;
3863
3864 view->adding_annot_info.adding_annot = FALSE;
3865 g_assert(!view->adding_annot_info.annot);
3866 ev_document_misc_get_pointer_position (GTK_WIDGET (view), &x, &y);
3867 ev_view_handle_cursor_over_xy (view, x, y);
3868 }
3869
3870 void
ev_view_remove_annotation(EvView * view,EvAnnotation * annot)3871 ev_view_remove_annotation (EvView *view,
3872 EvAnnotation *annot)
3873 {
3874 guint page;
3875
3876 g_return_if_fail (EV_IS_VIEW (view));
3877 g_return_if_fail (EV_IS_ANNOTATION (annot));
3878
3879 g_object_ref (annot);
3880
3881 page = ev_annotation_get_page_index (annot);
3882
3883 if (EV_IS_ANNOTATION_MARKUP (annot))
3884 ev_view_remove_window_child_for_annot (view, page, annot);
3885 if (view->annot_window_map != NULL)
3886 g_hash_table_remove (view->annot_window_map, annot);
3887
3888 _ev_view_set_focused_element (view, NULL, -1);
3889
3890 ev_document_doc_mutex_lock ();
3891 ev_document_annotations_remove_annotation (EV_DOCUMENT_ANNOTATIONS (view->document),
3892 annot);
3893 ev_document_doc_mutex_unlock ();
3894
3895 ev_page_cache_mark_dirty (view->page_cache, page, EV_PAGE_DATA_INCLUDE_ANNOTS);
3896
3897 /* FIXME: only redraw the annot area */
3898 ev_view_reload_page (view, page, NULL);
3899
3900 g_signal_emit (view, signals[SIGNAL_ANNOT_REMOVED], 0, annot);
3901 g_object_unref (annot);
3902 }
3903
3904 static gboolean
ev_view_synctex_backward_search(EvView * view,gdouble x,gdouble y)3905 ev_view_synctex_backward_search (EvView *view,
3906 gdouble x,
3907 gdouble y)
3908 {
3909 gint page = -1;
3910 gint x_new = 0, y_new = 0;
3911 EvSourceLink *link;
3912
3913 if (!ev_document_has_synctex (view->document))
3914 return FALSE;
3915
3916 if (!get_doc_point_from_location (view, x, y, &page, &x_new, &y_new))
3917 return FALSE;
3918
3919 link = ev_document_synctex_backward_search (view->document, page, x_new, y_new);
3920 if (link) {
3921 g_signal_emit (view, signals[SIGNAL_SYNC_SOURCE], 0, link);
3922 ev_source_link_free (link);
3923
3924 return TRUE;
3925 }
3926
3927 return FALSE;
3928 }
3929
3930 /* Caret navigation */
3931 #define CURSOR_ON_MULTIPLIER 2
3932 #define CURSOR_OFF_MULTIPLIER 1
3933 #define CURSOR_PEND_MULTIPLIER 3
3934 #define CURSOR_DIVIDER 3
3935
3936 static inline gboolean
cursor_is_in_visible_page(EvView * view)3937 cursor_is_in_visible_page (EvView *view)
3938 {
3939 return (view->cursor_page == view->current_page ||
3940 (view->cursor_page >= view->start_page &&
3941 view->cursor_page <= view->end_page));
3942 }
3943
3944 static gboolean
cursor_should_blink(EvView * view)3945 cursor_should_blink (EvView *view)
3946 {
3947 if (view->caret_enabled &&
3948 view->rotation == 0 &&
3949 cursor_is_in_visible_page (view) &&
3950 gtk_widget_has_focus (GTK_WIDGET (view)) &&
3951 view->pixbuf_cache &&
3952 !ev_pixbuf_cache_get_selection_region (view->pixbuf_cache, view->cursor_page, view->scale)) {
3953 GtkSettings *settings;
3954 gboolean blink;
3955
3956 settings = gtk_widget_get_settings (GTK_WIDGET (view));
3957 g_object_get (settings, "gtk-cursor-blink", &blink, NULL);
3958
3959 return blink;
3960 }
3961
3962 return FALSE;
3963 }
3964
3965 static gint
get_cursor_blink_time(EvView * view)3966 get_cursor_blink_time (EvView *view)
3967 {
3968 GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (view));
3969 gint time;
3970
3971 g_object_get (settings, "gtk-cursor-blink-time", &time, NULL);
3972
3973 return time;
3974 }
3975
3976 static gint
get_cursor_blink_timeout_id(EvView * view)3977 get_cursor_blink_timeout_id (EvView *view)
3978 {
3979 GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (view));
3980 gint timeout;
3981
3982 g_object_get (settings, "gtk-cursor-blink-timeout", &timeout, NULL);
3983
3984 return timeout;
3985 }
3986
3987 static gboolean
get_caret_cursor_area(EvView * view,gint page,gint offset,GdkRectangle * area)3988 get_caret_cursor_area (EvView *view,
3989 gint page,
3990 gint offset,
3991 GdkRectangle *area)
3992 {
3993 EvRectangle *areas = NULL;
3994 EvRectangle *doc_rect;
3995 guint n_areas = 0;
3996 gfloat cursor_aspect_ratio;
3997 gint stem_width;
3998
3999 if (!view->caret_enabled || view->rotation != 0)
4000 return FALSE;
4001
4002 if (!view->page_cache)
4003 return FALSE;
4004
4005 ev_page_cache_get_text_layout (view->page_cache, page, &areas, &n_areas);
4006 if (!areas)
4007 return FALSE;
4008
4009 if (offset > n_areas)
4010 return FALSE;
4011
4012 doc_rect = areas + offset;
4013 if (offset == n_areas ||
4014 ((doc_rect->x1 == doc_rect->x2 || doc_rect->y1 == doc_rect->y2) && offset > 0)) {
4015 EvRectangle *prev;
4016 EvRectangle last_rect;
4017
4018 /* Special characters like \n have an empty bounding box
4019 * and the end of a page doesn't have any bounding box,
4020 * use the size of the previous area.
4021 */
4022 prev = areas + offset - 1;
4023 last_rect.x1 = prev->x2;
4024 last_rect.y1 = prev->y1;
4025 last_rect.x2 = prev->x2 + (prev->x2 - prev->x1);
4026 last_rect.y2 = prev->y2;
4027
4028 _ev_view_transform_doc_rect_to_view_rect (view, page, &last_rect, area);
4029 } else {
4030 _ev_view_transform_doc_rect_to_view_rect (view, page, doc_rect, area);
4031 }
4032
4033 area->x -= view->scroll_x;
4034 area->y -= view->scroll_y;
4035
4036 gtk_style_context_get_style (gtk_widget_get_style_context (GTK_WIDGET (view)),
4037 "cursor-aspect-ratio", &cursor_aspect_ratio,
4038 NULL);
4039 stem_width = area->height * cursor_aspect_ratio + 1;
4040 area->x -= (stem_width / 2);
4041 area->width = stem_width;
4042
4043 return TRUE;
4044 }
4045
4046 static void
show_cursor(EvView * view)4047 show_cursor (EvView *view)
4048 {
4049 GtkWidget *widget;
4050 GdkRectangle view_rect;
4051
4052 if (view->cursor_visible)
4053 return;
4054
4055 widget = GTK_WIDGET (view);
4056 view->cursor_visible = TRUE;
4057 if (gtk_widget_has_focus (widget) &&
4058 get_caret_cursor_area (view, view->cursor_page, view->cursor_offset, &view_rect)) {
4059 gtk_widget_queue_draw_area (widget,
4060 view_rect.x, view_rect.y,
4061 view_rect.width, view_rect.height);
4062 }
4063 }
4064
4065 static void
hide_cursor(EvView * view)4066 hide_cursor (EvView *view)
4067 {
4068 GtkWidget *widget;
4069 GdkRectangle view_rect;
4070
4071 if (!view->cursor_visible)
4072 return;
4073
4074 widget = GTK_WIDGET (view);
4075 view->cursor_visible = FALSE;
4076 if (gtk_widget_has_focus (widget) &&
4077 get_caret_cursor_area (view, view->cursor_page, view->cursor_offset, &view_rect)) {
4078 gtk_widget_queue_draw_area (widget,
4079 view_rect.x, view_rect.y,
4080 view_rect.width, view_rect.height);
4081 }
4082 }
4083
4084 static gboolean
blink_cb(EvView * view)4085 blink_cb (EvView *view)
4086 {
4087 gint blink_timeout;
4088 guint blink_time;
4089
4090 blink_timeout = get_cursor_blink_timeout_id (view);
4091 if (view->cursor_blink_time > 1000 * blink_timeout && blink_timeout < G_MAXINT / 1000) {
4092 /* We've blinked enough without the user doing anything, stop blinking */
4093 show_cursor (view);
4094 view->cursor_blink_timeout_id = 0;
4095
4096 return FALSE;
4097 }
4098
4099 blink_time = get_cursor_blink_time (view);
4100 if (view->cursor_visible) {
4101 hide_cursor (view);
4102 blink_time *= CURSOR_OFF_MULTIPLIER;
4103 } else {
4104 show_cursor (view);
4105 view->cursor_blink_time += blink_time;
4106 blink_time *= CURSOR_ON_MULTIPLIER;
4107 }
4108
4109 view->cursor_blink_timeout_id = gdk_threads_add_timeout (blink_time / CURSOR_DIVIDER, (GSourceFunc)blink_cb, view);
4110
4111 return FALSE;
4112 }
4113
4114 static void
ev_view_check_cursor_blink(EvView * view)4115 ev_view_check_cursor_blink (EvView *view)
4116 {
4117 if (cursor_should_blink (view)) {
4118 if (view->cursor_blink_timeout_id == 0) {
4119 show_cursor (view);
4120 view->cursor_blink_timeout_id = gdk_threads_add_timeout (get_cursor_blink_time (view) * CURSOR_ON_MULTIPLIER / CURSOR_DIVIDER,
4121 (GSourceFunc)blink_cb, view);
4122 }
4123
4124 return;
4125 }
4126
4127 if (view->cursor_blink_timeout_id > 0) {
4128 g_source_remove (view->cursor_blink_timeout_id);
4129 view->cursor_blink_timeout_id = 0;
4130 }
4131
4132 view->cursor_visible = TRUE;
4133 view->cursor_blink_time = 0;
4134 }
4135
4136 static void
ev_view_pend_cursor_blink(EvView * view)4137 ev_view_pend_cursor_blink (EvView *view)
4138 {
4139 if (!cursor_should_blink (view))
4140 return;
4141
4142 if (view->cursor_blink_timeout_id > 0)
4143 g_source_remove (view->cursor_blink_timeout_id);
4144
4145 show_cursor (view);
4146 view->cursor_blink_timeout_id = gdk_threads_add_timeout (get_cursor_blink_time (view) * CURSOR_PEND_MULTIPLIER / CURSOR_DIVIDER,
4147 (GSourceFunc)blink_cb, view);
4148 }
4149
4150 static void
preload_pages_for_caret_navigation(EvView * view)4151 preload_pages_for_caret_navigation (EvView *view)
4152 {
4153 gint n_pages;
4154
4155 if (!view->document)
4156 return;
4157
4158 /* Upload to the cache the first and last pages,
4159 * this information is needed to position the cursor
4160 * in the beginning/end of the document, for example
4161 * when pressing <Ctr>Home/End
4162 */
4163 n_pages = ev_document_get_n_pages (view->document);
4164
4165 /* For documents with at least 3 pages, those are already cached anyway */
4166 if (n_pages > 0 && n_pages <= 3)
4167 return;
4168
4169 ev_page_cache_ensure_page (view->page_cache, 0);
4170 ev_page_cache_ensure_page (view->page_cache, n_pages - 1);
4171 }
4172
4173 /**
4174 * ev_view_supports_caret_navigation:
4175 * @view: a #EvView
4176 *
4177 * Returns: whether the document supports caret navigation
4178 *
4179 * Since: 3.10
4180 */
4181 gboolean
ev_view_supports_caret_navigation(EvView * view)4182 ev_view_supports_caret_navigation (EvView *view)
4183 {
4184 EvDocumentTextInterface *iface;
4185
4186 if (!view->document || !EV_IS_DOCUMENT_TEXT (view->document))
4187 return FALSE;
4188
4189 iface = EV_DOCUMENT_TEXT_GET_IFACE (view->document);
4190 if (!iface->get_text_layout || !iface->get_text)
4191 return FALSE;
4192
4193 return TRUE;
4194 }
4195
4196 /**
4197 * ev_view_set_caret_navigation_enabled:
4198 * @view: a #EvView
4199 * @enabled: whether to enable caret navigation mode
4200 *
4201 * Enables or disables caret navigation mode for the document.
4202 *
4203 * Since: 3.10
4204 */
4205 void
ev_view_set_caret_navigation_enabled(EvView * view,gboolean enabled)4206 ev_view_set_caret_navigation_enabled (EvView *view,
4207 gboolean enabled)
4208 {
4209 g_return_if_fail (EV_IS_VIEW (view));
4210
4211 if (view->caret_enabled != enabled) {
4212 view->caret_enabled = enabled;
4213 if (view->caret_enabled)
4214 preload_pages_for_caret_navigation (view);
4215
4216 ev_view_check_cursor_blink (view);
4217
4218 if (cursor_is_in_visible_page (view))
4219 gtk_widget_queue_draw (GTK_WIDGET (view));
4220 }
4221 }
4222
4223 /**
4224 * ev_view_get_caret_navigation_enabled:
4225 * @view: a #EvView
4226 *
4227 * Returns: whether caret navigation mode is enabled for the document
4228 *
4229 * Since: 3.10
4230 */
4231 gboolean
ev_view_is_caret_navigation_enabled(EvView * view)4232 ev_view_is_caret_navigation_enabled (EvView *view)
4233 {
4234 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
4235
4236 return view->caret_enabled;
4237 }
4238
4239 /**
4240 * ev_view_set_caret_cursor_position:
4241 * @view: a #EvView
4242 * @page:
4243 * @offset:
4244 *
4245 * Since: 3.10
4246 */
4247 void
ev_view_set_caret_cursor_position(EvView * view,guint page,guint offset)4248 ev_view_set_caret_cursor_position (EvView *view,
4249 guint page,
4250 guint offset)
4251 {
4252 g_return_if_fail (EV_IS_VIEW (view));
4253 g_return_if_fail (EV_IS_DOCUMENT (view->document));
4254 g_return_if_fail (page < ev_document_get_n_pages (view->document));
4255
4256 if (view->cursor_page != page || view->cursor_offset != offset) {
4257 view->cursor_page = page;
4258 view->cursor_offset = offset;
4259
4260 g_signal_emit (view, signals[SIGNAL_CURSOR_MOVED], 0,
4261 view->cursor_page, view->cursor_offset);
4262
4263 if (view->caret_enabled && cursor_is_in_visible_page (view))
4264 gtk_widget_queue_draw (GTK_WIDGET (view));
4265 }
4266 }
4267 /*** GtkWidget implementation ***/
4268
4269 static void
ev_view_size_request_continuous_dual_page(EvView * view,GtkRequisition * requisition)4270 ev_view_size_request_continuous_dual_page (EvView *view,
4271 GtkRequisition *requisition)
4272 {
4273 gint n_pages;
4274 GtkBorder border;
4275
4276 n_pages = ev_document_get_n_pages (view->document) + 1;
4277 compute_border (view, &border);
4278 get_page_y_offset (view, n_pages, &requisition->height, &border);
4279
4280 switch (view->sizing_mode) {
4281 case EV_SIZING_FIT_WIDTH:
4282 case EV_SIZING_FIT_PAGE:
4283 case EV_SIZING_AUTOMATIC:
4284 requisition->width = 1;
4285
4286 break;
4287 case EV_SIZING_FREE: {
4288 gint max_width;
4289
4290 ev_view_get_max_page_size (view, &max_width, NULL);
4291 requisition->width = (max_width + border.left + border.right) * 2 + (view->spacing * 3);
4292 }
4293 break;
4294 default:
4295 g_assert_not_reached ();
4296 }
4297 }
4298
4299 static void
ev_view_size_request_continuous(EvView * view,GtkRequisition * requisition)4300 ev_view_size_request_continuous (EvView *view,
4301 GtkRequisition *requisition)
4302 {
4303 gint n_pages;
4304 GtkBorder border;
4305
4306 n_pages = ev_document_get_n_pages (view->document);
4307 compute_border (view, &border);
4308 get_page_y_offset (view, n_pages, &requisition->height, &border);
4309
4310 switch (view->sizing_mode) {
4311 case EV_SIZING_FIT_WIDTH:
4312 case EV_SIZING_FIT_PAGE:
4313 case EV_SIZING_AUTOMATIC:
4314 requisition->width = 1;
4315
4316 break;
4317 case EV_SIZING_FREE: {
4318 gint max_width;
4319
4320 ev_view_get_max_page_size (view, &max_width, NULL);
4321 requisition->width = max_width + (view->spacing * 2) + border.left + border.right;
4322 }
4323 break;
4324 default:
4325 g_assert_not_reached ();
4326 }
4327 }
4328
4329 static void
ev_view_size_request_dual_page(EvView * view,GtkRequisition * requisition)4330 ev_view_size_request_dual_page (EvView *view,
4331 GtkRequisition *requisition)
4332 {
4333 GtkBorder border;
4334 gint width, height;
4335
4336 if (view->sizing_mode == EV_SIZING_FIT_PAGE) {
4337 requisition->width = 1;
4338 requisition->height = 1;
4339
4340 return;
4341 }
4342
4343 /* Find the largest of the two. */
4344 ev_view_get_page_size (view,
4345 view->current_page,
4346 &width, &height);
4347 if (view->current_page + 1 < ev_document_get_n_pages (view->document)) {
4348 gint width_2, height_2;
4349 ev_view_get_page_size (view,
4350 view->current_page + 1,
4351 &width_2, &height_2);
4352 if (width_2 > width) {
4353 width = width_2;
4354 height = height_2;
4355 }
4356 }
4357 compute_border (view, &border);
4358
4359 requisition->width = view->sizing_mode == EV_SIZING_FIT_WIDTH ? 1 :
4360 ((width + border.left + border.right) * 2) + (view->spacing * 3);
4361 requisition->height = (height + border.top + border.bottom) + (view->spacing * 2);
4362 }
4363
4364 static void
ev_view_size_request_single_page(EvView * view,GtkRequisition * requisition)4365 ev_view_size_request_single_page (EvView *view,
4366 GtkRequisition *requisition)
4367 {
4368 GtkBorder border;
4369 gint width, height;
4370
4371 if (view->sizing_mode == EV_SIZING_FIT_PAGE) {
4372 requisition->width = 1;
4373 requisition->height = 1;
4374
4375 return;
4376 }
4377
4378 ev_view_get_page_size (view, view->current_page, &width, &height);
4379 compute_border (view, &border);
4380
4381 requisition->width = view->sizing_mode == EV_SIZING_FIT_WIDTH ? 1 :
4382 width + border.left + border.right + (2 * view->spacing);
4383 requisition->height = height + border.top + border.bottom + (2 * view->spacing);
4384 }
4385
4386 static void
ev_view_size_request(GtkWidget * widget,GtkRequisition * requisition)4387 ev_view_size_request (GtkWidget *widget,
4388 GtkRequisition *requisition)
4389 {
4390 EvView *view = EV_VIEW (widget);
4391 gboolean dual_page;
4392
4393 if (view->document == NULL) {
4394 view->requisition.width = 1;
4395 view->requisition.height = 1;
4396
4397 *requisition = view->requisition;
4398
4399 return;
4400 }
4401
4402 /* Get zoom for size here when not called from
4403 * ev_view_size_allocate()
4404 */
4405 if (!view->internal_size_request &&
4406 (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
4407 view->sizing_mode == EV_SIZING_FIT_PAGE ||
4408 view->sizing_mode == EV_SIZING_AUTOMATIC)) {
4409 GtkAllocation allocation;
4410
4411 gtk_widget_get_allocation (widget, &allocation);
4412 ev_view_zoom_for_size (view,
4413 allocation.width,
4414 allocation.height);
4415 }
4416
4417 dual_page = is_dual_page (view, NULL);
4418 if (view->continuous && dual_page)
4419 ev_view_size_request_continuous_dual_page (view, &view->requisition);
4420 else if (view->continuous)
4421 ev_view_size_request_continuous (view, &view->requisition);
4422 else if (dual_page)
4423 ev_view_size_request_dual_page (view, &view->requisition);
4424 else
4425 ev_view_size_request_single_page (view, &view->requisition);
4426
4427 *requisition = view->requisition;
4428 }
4429
4430 static void
ev_view_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)4431 ev_view_get_preferred_width (GtkWidget *widget,
4432 gint *minimum,
4433 gint *natural)
4434 {
4435 GtkRequisition requisition;
4436
4437 ev_view_size_request (widget, &requisition);
4438
4439 *minimum = *natural = requisition.width;
4440 }
4441
4442 static void
ev_view_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)4443 ev_view_get_preferred_height (GtkWidget *widget,
4444 gint *minimum,
4445 gint *natural)
4446 {
4447 GtkRequisition requisition;
4448
4449 ev_view_size_request (widget, &requisition);
4450
4451 *minimum = *natural = requisition.height;
4452 }
4453
4454 static void
ev_view_size_allocate(GtkWidget * widget,GtkAllocation * allocation)4455 ev_view_size_allocate (GtkWidget *widget,
4456 GtkAllocation *allocation)
4457 {
4458 EvView *view = EV_VIEW (widget);
4459 GList *l;
4460 gint root_x, root_y;
4461
4462 gtk_widget_set_allocation (widget, allocation);
4463
4464 if (gtk_widget_get_realized (widget))
4465 gdk_window_move_resize (gtk_widget_get_window (widget),
4466 allocation->x,
4467 allocation->y,
4468 allocation->width,
4469 allocation->height);
4470
4471 if (!view->document)
4472 return;
4473
4474 if (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
4475 view->sizing_mode == EV_SIZING_FIT_PAGE ||
4476 view->sizing_mode == EV_SIZING_AUTOMATIC) {
4477 GtkRequisition req;
4478
4479 ev_view_zoom_for_size (view,
4480 allocation->width,
4481 allocation->height);
4482 view->internal_size_request = TRUE;
4483 ev_view_size_request (widget, &req);
4484 view->internal_size_request = FALSE;
4485 }
4486
4487 ev_view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
4488 ev_view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
4489
4490 if (view->document)
4491 view_update_range_and_current_page (view);
4492
4493 view->pending_scroll = SCROLL_TO_KEEP_POSITION;
4494 view->pending_resize = FALSE;
4495 view->pending_point.x = 0;
4496 view->pending_point.y = 0;
4497
4498 for (l = view->children; l && l->data; l = g_list_next (l)) {
4499 GdkRectangle view_area;
4500 EvViewChild *child = (EvViewChild *)l->data;
4501
4502 if (!gtk_widget_get_visible (child->widget))
4503 continue;
4504
4505 _ev_view_transform_doc_rect_to_view_rect (view, child->page, &child->doc_rect, &view_area);
4506 view_area.x -= view->scroll_x;
4507 view_area.y -= view->scroll_y;
4508
4509 gtk_widget_set_size_request (child->widget, view_area.width, view_area.height);
4510 gtk_widget_size_allocate (child->widget, &view_area);
4511 }
4512
4513 if (view->window_children)
4514 gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (view)),
4515 &root_x, &root_y);
4516
4517 for (l = view->window_children; l && l->data; l = g_list_next (l)) {
4518 EvViewWindowChild *child;
4519 EvRectangle doc_rect;
4520 GdkRectangle view_rect;
4521
4522 child = (EvViewWindowChild *)l->data;
4523
4524 ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (child->window), &doc_rect);
4525 if (child->moved) {
4526 doc_rect.x1 = child->orig_x;
4527 doc_rect.y1 = child->orig_y;
4528 }
4529 _ev_view_transform_doc_rect_to_view_rect (view, child->page, &doc_rect, &view_rect);
4530 view_rect.x -= view->scroll_x;
4531 view_rect.y -= view->scroll_y;
4532
4533 if (view_rect.x != child->orig_x || view_rect.y != child->orig_y) {
4534 child->parent_x = root_x;
4535 child->parent_y = root_y;
4536 ev_view_window_child_move (view, child, view_rect.x + root_x, view_rect.y + root_y);
4537 }
4538 }
4539 }
4540
4541 static gboolean
ev_view_scroll_event(GtkWidget * widget,GdkEventScroll * event)4542 ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event)
4543 {
4544 EvView *view = EV_VIEW (widget);
4545 guint state;
4546 gboolean fit_width, fit_height;
4547
4548 ev_view_link_preview_popover_cleanup (view);
4549
4550 state = event->state & gtk_accelerator_get_default_mod_mask ();
4551
4552 if (state == GDK_CONTROL_MASK) {
4553 ev_document_model_set_sizing_mode (view->model, EV_SIZING_FREE);
4554 view->zoom_center_x = event->x;
4555 view->zoom_center_y = event->y;
4556
4557 switch (event->direction) {
4558 case GDK_SCROLL_DOWN:
4559 case GDK_SCROLL_RIGHT:
4560 if (ev_view_can_zoom_out (view))
4561 ev_view_zoom_out (view);
4562 break;
4563 case GDK_SCROLL_UP:
4564 case GDK_SCROLL_LEFT:
4565 if (ev_view_can_zoom_in (view))
4566 ev_view_zoom_in (view);
4567 break;
4568 case GDK_SCROLL_SMOOTH: {
4569 gdouble delta = event->delta_x + event->delta_y;
4570 gdouble factor = pow (delta < 0 ? ZOOM_IN_FACTOR : ZOOM_OUT_FACTOR, fabs (delta));
4571
4572 if (ev_view_can_zoom (view, factor))
4573 ev_view_zoom (view, factor);
4574 }
4575 break;
4576 }
4577
4578 return TRUE;
4579 }
4580
4581 view->jump_to_find_result = FALSE;
4582
4583 /* Shift+Wheel scrolls the in the perpendicular direction */
4584 if (state & GDK_SHIFT_MASK) {
4585 if (event->direction == GDK_SCROLL_UP)
4586 event->direction = GDK_SCROLL_LEFT;
4587 else if (event->direction == GDK_SCROLL_LEFT)
4588 event->direction = GDK_SCROLL_UP;
4589 else if (event->direction == GDK_SCROLL_DOWN)
4590 event->direction = GDK_SCROLL_RIGHT;
4591 else if (event->direction == GDK_SCROLL_RIGHT)
4592 event->direction = GDK_SCROLL_DOWN;
4593 else if (event->direction == GDK_SCROLL_SMOOTH) {
4594 /* Swap the deltas for perpendicular direction */
4595 gdouble tmp_delta = event->delta_x;
4596
4597 event->delta_x = event->delta_y;
4598 event->delta_y = tmp_delta;
4599 }
4600
4601 event->state &= ~GDK_SHIFT_MASK;
4602 state &= ~GDK_SHIFT_MASK;
4603 }
4604
4605 fit_width = ev_view_page_fits (view, GTK_ORIENTATION_HORIZONTAL);
4606 fit_height = ev_view_page_fits (view, GTK_ORIENTATION_VERTICAL);
4607 if (state == 0 && !view->continuous && (fit_width || fit_height)) {
4608 switch (event->direction) {
4609 case GDK_SCROLL_DOWN:
4610 if (fit_height) {
4611 ev_view_next_page (view);
4612 return TRUE;
4613 }
4614 break;
4615 case GDK_SCROLL_RIGHT:
4616 if (fit_width) {
4617 ev_view_next_page (view);
4618 return TRUE;
4619 }
4620 break;
4621 case GDK_SCROLL_UP:
4622 if (fit_height) {
4623 ev_view_previous_page (view);
4624 return TRUE;
4625 }
4626 break;
4627 case GDK_SCROLL_LEFT:
4628 if (fit_width) {
4629 ev_view_previous_page (view);
4630 return TRUE;
4631 }
4632 break;
4633 case GDK_SCROLL_SMOOTH: {
4634 gdouble decrement;
4635 if ((fit_width && fit_height) ||
4636 ((fit_height && event->delta_x == 0.0) ||
4637 (fit_width && event->delta_y == 0.0))) {
4638 /* Emulate normal scrolling by summing the deltas */
4639 view->total_delta += event->delta_x + event->delta_y;
4640
4641 decrement = view->total_delta < 0 ? -1.0 : 1.0;
4642 for (; fabs (view->total_delta) >= 1.0; view->total_delta -= decrement) {
4643 if (decrement < 0)
4644 ev_view_previous_page (view);
4645 else
4646 ev_view_next_page (view);
4647 }
4648
4649 return TRUE;
4650 }
4651 }
4652 break;
4653 }
4654
4655 return FALSE;
4656 }
4657
4658 /* Do scroll only on one axis at a time. Issue #866 */
4659 if (event->direction == GDK_SCROLL_SMOOTH &&
4660 event->delta_x != 0.0 && event->delta_y != 0.0) {
4661 gdouble abs_x, abs_y;
4662 abs_x = fabs (event->delta_x);
4663 abs_y = fabs (event->delta_y);
4664
4665 if (abs_y > abs_x)
4666 event->delta_x = 0.0;
4667 else if (abs_x > abs_y)
4668 event->delta_y = 0.0;
4669 }
4670
4671 return FALSE;
4672 }
4673
4674 static EvViewSelection *
find_selection_for_page(EvView * view,gint page)4675 find_selection_for_page (EvView *view,
4676 gint page)
4677 {
4678 GList *list;
4679
4680 for (list = view->selection_info.selections; list != NULL; list = list->next) {
4681 EvViewSelection *selection;
4682
4683 selection = (EvViewSelection *) list->data;
4684
4685 if (selection->page == page)
4686 return selection;
4687 }
4688
4689 return NULL;
4690 }
4691
4692 static void
ev_view_realize(GtkWidget * widget)4693 ev_view_realize (GtkWidget *widget)
4694 {
4695 GtkAllocation allocation;
4696 GdkWindow *window;
4697 GdkWindowAttr attributes;
4698 gint attributes_mask;
4699
4700 gtk_widget_set_realized (widget, TRUE);
4701
4702 gtk_widget_get_allocation (widget, &allocation);
4703
4704 attributes.window_type = GDK_WINDOW_CHILD;
4705 attributes.x = allocation.x;
4706 attributes.y = allocation.y;
4707 attributes.width = allocation.width;
4708 attributes.height = allocation.height;
4709 attributes.wclass = GDK_INPUT_OUTPUT;
4710 attributes.visual = gtk_widget_get_visual (widget);
4711 attributes.event_mask = gtk_widget_get_events (widget);
4712
4713 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
4714
4715 window = gdk_window_new (gtk_widget_get_parent_window (widget),
4716 &attributes, attributes_mask);
4717 gtk_widget_set_window (widget, window);
4718 gdk_window_set_user_data (window, widget);
4719
4720 gtk_style_context_set_background (gtk_widget_get_style_context (widget),
4721 window);
4722 }
4723
4724 static void
get_cursor_color(GtkStyleContext * context,GdkRGBA * color)4725 get_cursor_color (GtkStyleContext *context,
4726 GdkRGBA *color)
4727 {
4728 GdkRGBA *caret_color;
4729
4730 gtk_style_context_get (context,
4731 gtk_style_context_get_state (context),
4732 "caret-color",
4733 &caret_color,
4734 NULL);
4735
4736 if (caret_color) {
4737 color->red = caret_color->red;
4738 color->green = caret_color->green;
4739 color->blue = caret_color->blue;
4740 color->alpha = caret_color->alpha;
4741
4742 gdk_rgba_free (caret_color);
4743 } else {
4744 gtk_style_context_save (context);
4745 gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, color);
4746 gtk_style_context_restore (context);
4747 }
4748 }
4749
4750 /* This is based on the deprecated function gtk_draw_insertion_cursor. */
4751 static void
draw_caret_cursor(EvView * view,cairo_t * cr)4752 draw_caret_cursor (EvView *view,
4753 cairo_t *cr)
4754 {
4755 GdkRectangle view_rect;
4756 GdkRGBA cursor_color;
4757
4758 if (!get_caret_cursor_area (view, view->cursor_page, view->cursor_offset, &view_rect))
4759 return;
4760
4761 get_cursor_color (gtk_widget_get_style_context (GTK_WIDGET (view)), &cursor_color);
4762
4763 cairo_save (cr);
4764 gdk_cairo_set_source_rgba (cr, &cursor_color);
4765 cairo_rectangle (cr, view_rect.x, view_rect.y, view_rect.width, view_rect.height);
4766 cairo_fill (cr);
4767 cairo_restore (cr);
4768 }
4769
4770 static gboolean
should_draw_caret_cursor(EvView * view,gint page)4771 should_draw_caret_cursor (EvView *view,
4772 gint page)
4773 {
4774 return (view->caret_enabled &&
4775 view->cursor_page == page &&
4776 view->cursor_visible &&
4777 gtk_widget_has_focus (GTK_WIDGET (view)) &&
4778 !ev_pixbuf_cache_get_selection_region (view->pixbuf_cache, page, view->scale));
4779 }
4780
4781 static void
draw_focus(EvView * view,cairo_t * cr,gint page,GdkRectangle * clip)4782 draw_focus (EvView *view,
4783 cairo_t *cr,
4784 gint page,
4785 GdkRectangle *clip)
4786 {
4787 GtkWidget *widget = GTK_WIDGET (view);
4788 GdkRectangle rect;
4789 GdkRectangle intersect;
4790
4791 if (view->focused_element_page != page)
4792 return;
4793
4794 if (!gtk_widget_has_focus (GTK_WIDGET (view)))
4795 return;
4796
4797 if (!ev_view_get_focused_area (view, &rect))
4798 return;
4799
4800 if (gdk_rectangle_intersect (&rect, clip, &intersect)) {
4801 gtk_render_focus (gtk_widget_get_style_context (widget),
4802 cr,
4803 intersect.x,
4804 intersect.y,
4805 intersect.width,
4806 intersect.height);
4807 }
4808 }
4809
4810 #ifdef EV_ENABLE_DEBUG
4811 static void
stroke_view_rect(cairo_t * cr,GdkRectangle * clip,GdkRectangle * view_rect)4812 stroke_view_rect (cairo_t *cr,
4813 GdkRectangle *clip,
4814 GdkRectangle *view_rect)
4815 {
4816 GdkRectangle intersect;
4817
4818 if (gdk_rectangle_intersect (view_rect, clip, &intersect)) {
4819 cairo_rectangle (cr,
4820 intersect.x, intersect.y,
4821 intersect.width, intersect.height);
4822 cairo_stroke (cr);
4823 }
4824 }
4825
4826 static void
stroke_doc_rect(EvView * view,cairo_t * cr,gint page,GdkRectangle * clip,EvRectangle * doc_rect)4827 stroke_doc_rect (EvView *view,
4828 cairo_t *cr,
4829 gint page,
4830 GdkRectangle *clip,
4831 EvRectangle *doc_rect)
4832 {
4833 GdkRectangle view_rect;
4834
4835 _ev_view_transform_doc_rect_to_view_rect (view, page, doc_rect, &view_rect);
4836 view_rect.x -= view->scroll_x;
4837 view_rect.y -= view->scroll_y;
4838 stroke_view_rect (cr, clip, &view_rect);
4839 }
4840
4841 static void
show_chars_border(EvView * view,cairo_t * cr,gint page,GdkRectangle * clip)4842 show_chars_border (EvView *view,
4843 cairo_t *cr,
4844 gint page,
4845 GdkRectangle *clip)
4846 {
4847 EvRectangle *areas = NULL;
4848 guint n_areas = 0;
4849 guint i;
4850
4851 ev_page_cache_get_text_layout (view->page_cache, page, &areas, &n_areas);
4852 if (!areas)
4853 return;
4854
4855 cairo_set_source_rgb (cr, 1., 0., 0.);
4856
4857 for (i = 0; i < n_areas; i++) {
4858 EvRectangle *doc_rect = areas + i;
4859
4860 stroke_doc_rect (view, cr, page, clip, doc_rect);
4861 }
4862 }
4863
4864 static void
show_mapping_list_border(EvView * view,cairo_t * cr,gint page,GdkRectangle * clip,EvMappingList * mapping_list)4865 show_mapping_list_border (EvView *view,
4866 cairo_t *cr,
4867 gint page,
4868 GdkRectangle *clip,
4869 EvMappingList *mapping_list)
4870 {
4871 GList *l;
4872
4873 for (l = ev_mapping_list_get_list (mapping_list); l; l = g_list_next (l)) {
4874 EvMapping *mapping = (EvMapping *)l->data;
4875
4876 stroke_doc_rect (view, cr, page, clip, &mapping->area);
4877 }
4878 }
4879
4880 static void
show_links_border(EvView * view,cairo_t * cr,gint page,GdkRectangle * clip)4881 show_links_border (EvView *view,
4882 cairo_t *cr,
4883 gint page,
4884 GdkRectangle *clip)
4885 {
4886 cairo_set_source_rgb (cr, 0., 0., 1.);
4887 show_mapping_list_border (view,cr, page, clip,
4888 ev_page_cache_get_link_mapping (view->page_cache, page));
4889 }
4890
4891 static void
show_forms_border(EvView * view,cairo_t * cr,gint page,GdkRectangle * clip)4892 show_forms_border (EvView *view,
4893 cairo_t *cr,
4894 gint page,
4895 GdkRectangle *clip)
4896 {
4897 cairo_set_source_rgb (cr, 0., 1., 0.);
4898 show_mapping_list_border (view, cr, page, clip,
4899 ev_page_cache_get_form_field_mapping (view->page_cache, page));
4900 }
4901
4902 static void
show_annots_border(EvView * view,cairo_t * cr,gint page,GdkRectangle * clip)4903 show_annots_border (EvView *view,
4904 cairo_t *cr,
4905 gint page,
4906 GdkRectangle *clip)
4907 {
4908 cairo_set_source_rgb (cr, 0., 1., 1.);
4909 show_mapping_list_border (view, cr, page, clip,
4910 ev_page_cache_get_annot_mapping (view->page_cache, page));
4911 }
4912
4913 static void
show_images_border(EvView * view,cairo_t * cr,gint page,GdkRectangle * clip)4914 show_images_border (EvView *view,
4915 cairo_t *cr,
4916 gint page,
4917 GdkRectangle *clip)
4918 {
4919 cairo_set_source_rgb (cr, 1., 0., 1.);
4920 show_mapping_list_border (view, cr, page, clip,
4921 ev_page_cache_get_image_mapping (view->page_cache, page));
4922 }
4923
4924 static void
show_media_border(EvView * view,cairo_t * cr,gint page,GdkRectangle * clip)4925 show_media_border (EvView *view,
4926 cairo_t *cr,
4927 gint page,
4928 GdkRectangle *clip)
4929 {
4930 cairo_set_source_rgb (cr, 1., 1., 0.);
4931 show_mapping_list_border (view, cr, page, clip,
4932 ev_page_cache_get_media_mapping (view->page_cache, page));
4933 }
4934
4935
4936 static void
show_selections_border(EvView * view,cairo_t * cr,gint page,GdkRectangle * clip)4937 show_selections_border (EvView *view,
4938 cairo_t *cr,
4939 gint page,
4940 GdkRectangle *clip)
4941 {
4942 cairo_region_t *region;
4943 guint i, n_rects;
4944
4945 region = ev_page_cache_get_text_mapping (view->page_cache, page);
4946 if (!region)
4947 return;
4948
4949 cairo_set_source_rgb (cr, 0.75, 0.50, 0.25);
4950
4951 region = cairo_region_copy (region);
4952 n_rects = cairo_region_num_rectangles (region);
4953 for (i = 0; i < n_rects; i++) {
4954 GdkRectangle doc_rect_int;
4955 EvRectangle doc_rect_float;
4956
4957 cairo_region_get_rectangle (region, i, &doc_rect_int);
4958
4959 /* Convert the doc rect to a EvRectangle */
4960 doc_rect_float.x1 = doc_rect_int.x;
4961 doc_rect_float.y1 = doc_rect_int.y;
4962 doc_rect_float.x2 = doc_rect_int.x + doc_rect_int.width;
4963 doc_rect_float.y2 = doc_rect_int.y + doc_rect_int.height;
4964
4965 stroke_doc_rect (view, cr, page, clip, &doc_rect_float);
4966 }
4967 cairo_region_destroy (region);
4968 }
4969
4970 static void
draw_debug_borders(EvView * view,cairo_t * cr,gint page,GdkRectangle * clip)4971 draw_debug_borders (EvView *view,
4972 cairo_t *cr,
4973 gint page,
4974 GdkRectangle *clip)
4975 {
4976 EvDebugBorders borders = ev_debug_get_debug_borders();
4977
4978 cairo_save (cr);
4979 cairo_set_line_width (cr, 0.5);
4980
4981 if (borders & EV_DEBUG_BORDER_CHARS)
4982 show_chars_border (view, cr, page, clip);
4983 if (borders & EV_DEBUG_BORDER_LINKS)
4984 show_links_border (view, cr, page, clip);
4985 if (borders & EV_DEBUG_BORDER_FORMS)
4986 show_forms_border (view, cr, page, clip);
4987 if (borders & EV_DEBUG_BORDER_ANNOTS)
4988 show_annots_border (view, cr, page, clip);
4989 if (borders & EV_DEBUG_BORDER_IMAGES)
4990 show_images_border (view, cr, page, clip);
4991 if (borders & EV_DEBUG_BORDER_MEDIA)
4992 show_media_border (view, cr, page, clip);
4993 if (borders & EV_DEBUG_BORDER_SELECTIONS)
4994 show_selections_border (view, cr, page, clip);
4995
4996 cairo_restore (cr);
4997 }
4998 #endif
4999
5000 static gboolean
ev_view_draw(GtkWidget * widget,cairo_t * cr)5001 ev_view_draw (GtkWidget *widget,
5002 cairo_t *cr)
5003 {
5004 EvView *view = EV_VIEW (widget);
5005 gint i;
5006 GdkRectangle clip_rect;
5007 GtkBorder border;
5008
5009 gtk_render_background (gtk_widget_get_style_context (widget),
5010 cr,
5011 0, 0,
5012 gtk_widget_get_allocated_width (widget),
5013 gtk_widget_get_allocated_height (widget));
5014
5015 if (view->document == NULL)
5016 return FALSE;
5017
5018 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
5019 return FALSE;
5020
5021 compute_border (view, &border);
5022 for (i = view->start_page; i >= 0 && i <= view->end_page; i++) {
5023 GdkRectangle page_area;
5024 gboolean page_ready = TRUE;
5025
5026 if (!ev_view_get_page_extents_for_border (view, i, &border, &page_area))
5027 continue;
5028
5029 page_area.x -= view->scroll_x;
5030 page_area.y -= view->scroll_y;
5031
5032 draw_one_page (view, i, cr, &page_area, &border, &clip_rect, &page_ready);
5033
5034 if (page_ready && should_draw_caret_cursor (view, i))
5035 draw_caret_cursor (view, cr);
5036 if (page_ready && view->find_pages && view->highlight_find_results)
5037 highlight_find_results (view, cr, i);
5038 if (page_ready && EV_IS_DOCUMENT_ANNOTATIONS (view->document))
5039 show_annotation_windows (view, i);
5040 if (page_ready && view->focused_element)
5041 draw_focus (view, cr, i, &clip_rect);
5042 if (page_ready && view->synctex_result)
5043 highlight_forward_search_results (view, cr, i);
5044 #ifdef EV_ENABLE_DEBUG
5045 if (page_ready)
5046 draw_debug_borders (view, cr, i, &clip_rect);
5047 #endif
5048 }
5049
5050 if (GTK_WIDGET_CLASS (ev_view_parent_class)->draw)
5051 GTK_WIDGET_CLASS (ev_view_parent_class)->draw (widget, cr);
5052
5053 return FALSE;
5054 }
5055
5056 static void
ev_view_set_focused_element_at_location(EvView * view,gdouble x,gdouble y)5057 ev_view_set_focused_element_at_location (EvView *view,
5058 gdouble x,
5059 gdouble y)
5060 {
5061 EvMapping *mapping;
5062 EvFormField *field;
5063 gint page;
5064
5065 mapping = get_annotation_mapping_at_location (view, x, y, &page);
5066 if (mapping) {
5067 _ev_view_set_focused_element (view, mapping, page);
5068 return;
5069 }
5070
5071 mapping = get_link_mapping_at_location (view, x, y, &page);
5072 if (mapping) {
5073 _ev_view_set_focused_element (view, mapping, page);
5074 return;
5075 }
5076
5077 if ((field = ev_view_get_form_field_at_location (view, x, y))) {
5078 ev_view_remove_all_form_fields (view);
5079 _ev_view_focus_form_field (view, field);
5080 return;
5081 }
5082
5083 _ev_view_set_focused_element (view, NULL, -1);
5084 }
5085
5086 static gboolean
ev_view_do_popup_menu(EvView * view,gdouble x,gdouble y)5087 ev_view_do_popup_menu (EvView *view,
5088 gdouble x,
5089 gdouble y)
5090 {
5091 GList *items = NULL;
5092 EvLink *link;
5093 EvImage *image;
5094 EvAnnotation *annot;
5095
5096 image = ev_view_get_image_at_location (view, x, y);
5097 if (image)
5098 items = g_list_prepend (items, image);
5099
5100 link = ev_view_get_link_at_location (view, x, y);
5101 if (link)
5102 items = g_list_prepend (items, link);
5103
5104 annot = ev_view_get_annotation_at_location (view, x, y);
5105 if (annot)
5106 items = g_list_prepend (items, annot);
5107
5108 g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, items);
5109
5110 g_list_free (items);
5111
5112 return TRUE;
5113 }
5114
5115 static gboolean
ev_view_popup_menu(GtkWidget * widget)5116 ev_view_popup_menu (GtkWidget *widget)
5117 {
5118 gint x, y;
5119
5120 ev_document_misc_get_pointer_position (widget, &x, &y);
5121 return ev_view_do_popup_menu (EV_VIEW (widget), x, y);
5122 }
5123
5124 static void
get_link_area(EvView * view,gint x,gint y,EvLink * link,GdkRectangle * area)5125 get_link_area (EvView *view,
5126 gint x,
5127 gint y,
5128 EvLink *link,
5129 GdkRectangle *area)
5130 {
5131 EvMappingList *link_mapping;
5132 gint page;
5133 gint x_offset = 0, y_offset = 0;
5134
5135 x += view->scroll_x;
5136 y += view->scroll_y;
5137
5138 find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
5139
5140 link_mapping = ev_page_cache_get_link_mapping (view->page_cache, page);
5141 ev_view_get_area_from_mapping (view, page,
5142 link_mapping,
5143 link, area);
5144 }
5145
5146 static void
get_annot_area(EvView * view,gint x,gint y,EvAnnotation * annot,GdkRectangle * area)5147 get_annot_area (EvView *view,
5148 gint x,
5149 gint y,
5150 EvAnnotation *annot,
5151 GdkRectangle *area)
5152 {
5153 EvMappingList *annot_mapping;
5154 gint page;
5155 gint x_offset = 0, y_offset = 0;
5156
5157 x += view->scroll_x;
5158 y += view->scroll_y;
5159
5160 find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
5161
5162 annot_mapping = ev_page_cache_get_annot_mapping (view->page_cache, page);
5163 ev_view_get_area_from_mapping (view, page,
5164 annot_mapping,
5165 annot, area);
5166 }
5167
5168 static void
get_field_area(EvView * view,gint x,gint y,EvFormField * field,GdkRectangle * area)5169 get_field_area (EvView *view,
5170 gint x,
5171 gint y,
5172 EvFormField *field,
5173 GdkRectangle *area)
5174 {
5175 EvMappingList *field_mapping;
5176 gint page;
5177 gint x_offset = 0, y_offset = 0;
5178
5179 x += view->scroll_x;
5180 y += view->scroll_y;
5181
5182 find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
5183
5184 field_mapping = ev_page_cache_get_form_field_mapping (view->page_cache, page);
5185 ev_view_get_area_from_mapping (view, page, field_mapping, field, area);
5186 }
5187
5188 static void
link_preview_show_thumbnail(cairo_surface_t * page_surface,EvView * view)5189 link_preview_show_thumbnail (cairo_surface_t *page_surface,
5190 EvView *view)
5191 {
5192 GtkWidget *popover = view->link_preview.popover;
5193 GtkWidget *image_view;
5194 gdouble x, y; /* position of the link on destination page */
5195 gint pwidth, pheight; /* dimensions of destination page */
5196 gint vwidth, vheight; /* dimensions of main view */
5197 gint width, height; /* dimensions of popup */
5198 gint left, top;
5199 gdouble device_scale_x = 1, device_scale_y = 1;
5200 cairo_surface_t *thumbnail_slice;
5201 cairo_t *cr;
5202
5203 x = view->link_preview.left;
5204 y = view->link_preview.top;
5205
5206 #ifdef HAVE_HIDPI_SUPPORT
5207 cairo_surface_get_device_scale (page_surface, &device_scale_x, &device_scale_y);
5208 #endif
5209 pwidth = cairo_image_surface_get_width (page_surface) / device_scale_x;
5210 pheight = cairo_image_surface_get_height (page_surface) / device_scale_y;
5211
5212 vwidth = gtk_widget_get_allocated_width (GTK_WIDGET (view));
5213 vheight = gtk_widget_get_allocated_height (GTK_WIDGET (view));
5214
5215 /* Horizontally, we try to display the full width of the destination
5216 * page. This is needed to make the popup useful for two-column papers.
5217 * Vertically, we limit the height to maximally LINK_PREVIEW_PAGE_RATIO
5218 * of the main view. The idea is avoid the popup dominte the main view,
5219 * and the reader can see context both in the popup and the main page.
5220 */
5221 width = MIN (pwidth, vwidth);
5222 height = MIN (pheight, (int)(vheight * LINK_PREVIEW_PAGE_RATIO));
5223
5224 /* Position on the destination page that will be in the top left
5225 * corner of the popup. We choose the link destination to be centered
5226 * horizontally, and slightly above the center vertically. This is a
5227 * compromise given that a link contains only (x,y) information for a
5228 * single point, and some links have their (x,y) point to the top left
5229 * of their main content (e.g. section headers, bibliographic
5230 * references, footnotes, and tables), while other links have their
5231 * (x,y) point to the center right of the main contents (e.g.
5232 * equations). Also, figures usually have their (x,y) point to the
5233 * caption below the figure, so seeing a little of the figure above is
5234 * often enough to remind the reader of the rest of the figure.
5235 */
5236 left = x - width * LINK_PREVIEW_HORIZONTAL_LINK_POS;
5237 top = y - height * LINK_PREVIEW_VERTICAL_LINK_POS;
5238
5239 /* link preview destination should stay within the destination page: */
5240 left = MIN (MAX (0, left), pwidth - width);
5241 top = MIN (MAX (0, top), pheight - height);
5242
5243 /* paint out the part of the page we want to a separate cairo_surface_t */
5244 thumbnail_slice = cairo_surface_create_similar (page_surface, CAIRO_CONTENT_COLOR, width, height);
5245 cr = cairo_create (thumbnail_slice);
5246 cairo_set_source_surface (cr, page_surface, -left, -top);
5247 cairo_rectangle (cr, 0, 0, width, height);
5248 cairo_fill (cr);
5249
5250 image_view = gtk_image_new_from_surface (thumbnail_slice);
5251
5252 gtk_widget_destroy (gtk_bin_get_child (GTK_BIN (popover)));
5253 gtk_container_add (GTK_CONTAINER (popover), image_view);
5254 gtk_widget_show (image_view);
5255
5256 cairo_destroy (cr);
5257 cairo_surface_destroy (thumbnail_slice);
5258 }
5259
5260 static gboolean
link_preview_popover_motion_notify(EvView * view,GdkEventMotion * event)5261 link_preview_popover_motion_notify (EvView *view,
5262 GdkEventMotion *event)
5263 {
5264 ev_view_link_preview_popover_cleanup (view);
5265 return TRUE;
5266 }
5267
5268 static gboolean
link_preview_delayed_show(EvView * view)5269 link_preview_delayed_show (EvView *view)
5270 {
5271 GtkWidget *popover = view->link_preview.popover;
5272 gtk_widget_show (popover);
5273
5274 view->link_preview.delay_timeout_id = 0;
5275 return FALSE;
5276 }
5277
5278 static void
link_preview_job_finished_cb(EvJobThumbnail * job,EvView * view)5279 link_preview_job_finished_cb (EvJobThumbnail *job,
5280 EvView *view)
5281 {
5282 GtkWidget *popover = view->link_preview.popover;
5283 gint device_scale = 1;
5284
5285 if (ev_job_is_failed (EV_JOB (job))) {
5286 gtk_widget_destroy (popover);
5287 view->link_preview.popover = NULL;
5288 g_object_unref (job);
5289 view->link_preview.job = NULL;
5290
5291 return;
5292 }
5293
5294 #ifdef HAVE_HIDPI_SUPPORT
5295 device_scale = gtk_widget_get_scale_factor (GTK_WIDGET (view));
5296 cairo_surface_set_device_scale (job->thumbnail_surface, device_scale, device_scale);
5297 #endif
5298
5299 if (ev_document_model_get_inverted_colors (view->model))
5300 ev_document_misc_invert_surface (job->thumbnail_surface);
5301
5302 link_preview_show_thumbnail (job->thumbnail_surface, view);
5303
5304 g_object_unref (job);
5305 view->link_preview.job = NULL;
5306 }
5307
5308 static void
ev_view_link_preview_popover_cleanup(EvView * view)5309 ev_view_link_preview_popover_cleanup (EvView *view) {
5310 if (view->link_preview.job) {
5311 ev_job_cancel (view->link_preview.job);
5312 g_object_unref (view->link_preview.job);
5313 view->link_preview.job = NULL;
5314 }
5315
5316 if (view->link_preview.popover) {
5317 gtk_widget_destroy (view->link_preview.popover);
5318 view->link_preview.popover = NULL;
5319 }
5320
5321 if (view->link_preview.delay_timeout_id) {
5322 g_source_remove (view->link_preview.delay_timeout_id);
5323 view->link_preview.delay_timeout_id = 0;
5324 }
5325 }
5326
5327 static gboolean
ev_view_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltip)5328 ev_view_query_tooltip (GtkWidget *widget,
5329 gint x,
5330 gint y,
5331 gboolean keyboard_tip,
5332 GtkTooltip *tooltip)
5333 {
5334 EvView *view = EV_VIEW (widget);
5335 EvFormField *field;
5336 EvLink *link;
5337 EvAnnotation *annot;
5338 gchar *text;
5339
5340 annot = ev_view_get_annotation_at_location (view, x, y);
5341 if (annot) {
5342 const gchar *contents;
5343
5344 contents = ev_annotation_get_contents (annot);
5345 if (contents && *contents != '\0') {
5346 GdkRectangle annot_area;
5347
5348 get_annot_area (view, x, y, annot, &annot_area);
5349 gtk_tooltip_set_text (tooltip, contents);
5350 gtk_tooltip_set_tip_area (tooltip, &annot_area);
5351
5352 return TRUE;
5353 }
5354 }
5355
5356 field = ev_view_get_form_field_at_location (view, x, y);
5357 if (field != NULL) {
5358 gchar *alt_ui_name = ev_form_field_get_alternate_name (field);
5359
5360 if (alt_ui_name && *(alt_ui_name) != '\0') {
5361 GdkRectangle field_area;
5362
5363 get_field_area (view, x, y, field, &field_area);
5364 gtk_tooltip_set_text (tooltip, alt_ui_name);
5365 gtk_tooltip_set_tip_area (tooltip, &field_area);
5366
5367 return TRUE;
5368 }
5369 }
5370
5371 link = ev_view_get_link_at_location (view, x, y);
5372 if (!link)
5373 return FALSE;
5374
5375 text = tip_from_link (view, link);
5376 if (text && g_utf8_validate (text, -1, NULL)) {
5377 GdkRectangle link_area;
5378
5379 get_link_area (view, x, y, link, &link_area);
5380 gtk_tooltip_set_text (tooltip, text);
5381 gtk_tooltip_set_tip_area (tooltip, &link_area);
5382 g_free (text);
5383
5384 return TRUE;
5385 }
5386 g_free (text);
5387
5388 return FALSE;
5389 }
5390
5391 static void
start_selection_for_event(EvView * view,GdkEventButton * event)5392 start_selection_for_event (EvView *view,
5393 GdkEventButton *event)
5394 {
5395 clear_selection (view);
5396
5397 view->selection_info.start.x = event->x + view->scroll_x;
5398 view->selection_info.start.y = event->y + view->scroll_y;
5399
5400 switch (event->type) {
5401 case GDK_2BUTTON_PRESS:
5402 view->selection_info.style = EV_SELECTION_STYLE_WORD;
5403 break;
5404 case GDK_3BUTTON_PRESS:
5405 view->selection_info.style = EV_SELECTION_STYLE_LINE;
5406 break;
5407 default:
5408 view->selection_info.style = EV_SELECTION_STYLE_GLYPH;
5409 return;
5410 }
5411
5412 /* In case of WORD or LINE, compute selections now */
5413 compute_selections (view,
5414 view->selection_info.style,
5415 &(view->selection_info.start),
5416 &(view->selection_info.start));
5417 }
5418
5419 gint
_ev_view_get_caret_cursor_offset_at_doc_point(EvView * view,gint page,gdouble doc_x,gdouble doc_y)5420 _ev_view_get_caret_cursor_offset_at_doc_point (EvView *view,
5421 gint page,
5422 gdouble doc_x,
5423 gdouble doc_y)
5424 {
5425 EvRectangle *areas = NULL;
5426 guint n_areas = 0;
5427 gint offset = -1;
5428 gint first_line_offset;
5429 gint last_line_offset = -1;
5430 EvRectangle *rect;
5431 guint i;
5432
5433 ev_page_cache_get_text_layout (view->page_cache, page, &areas, &n_areas);
5434 if (!areas)
5435 return -1;
5436
5437 i = 0;
5438 while (i < n_areas && offset == -1) {
5439 rect = areas + i;
5440
5441 first_line_offset = -1;
5442 while (doc_y >= rect->y1 && doc_y <= rect->y2) {
5443 if (first_line_offset == -1) {
5444 if (doc_x <= rect->x1) {
5445 /* Location is before the start of the line */
5446 if (last_line_offset != -1) {
5447 EvRectangle *last = areas + last_line_offset;
5448 gint dx1, dx2;
5449
5450 /* If there's a previous line, check distances */
5451
5452 dx1 = doc_x - last->x2;
5453 dx2 = rect->x1 - doc_x;
5454
5455 if (dx1 < dx2)
5456 offset = last_line_offset;
5457 else
5458 offset = i;
5459 } else {
5460 offset = i;
5461 }
5462
5463 last_line_offset = i + 1;
5464 break;
5465 }
5466 first_line_offset = i;
5467 }
5468 last_line_offset = i + 1;
5469
5470 if (doc_x >= rect->x1 && doc_x <= rect->x2) {
5471 /* Location is inside the line. Position the caret before
5472 * or after the character, depending on whether the point
5473 * falls within the left or right half of the bounding box.
5474 */
5475 if (doc_x <= rect->x1 + (rect->x2 - rect->x1) / 2)
5476 offset = i;
5477 else
5478 offset = i + 1;
5479 break;
5480 }
5481
5482 i++;
5483 rect = areas + i;
5484 }
5485
5486 if (first_line_offset == -1)
5487 i++;
5488 }
5489
5490 if (last_line_offset == -1)
5491 return -1;
5492
5493 if (offset == -1)
5494 offset = last_line_offset;
5495
5496 return offset;
5497 }
5498
5499 static gboolean
position_caret_cursor_at_doc_point(EvView * view,gint page,gdouble doc_x,gdouble doc_y)5500 position_caret_cursor_at_doc_point (EvView *view,
5501 gint page,
5502 gdouble doc_x,
5503 gdouble doc_y)
5504 {
5505 gint offset;
5506
5507 offset = _ev_view_get_caret_cursor_offset_at_doc_point (view, page, doc_x, doc_y);
5508 if (offset == -1)
5509 return FALSE;
5510
5511 if (view->cursor_offset != offset || view->cursor_page != page) {
5512 view->cursor_offset = offset;
5513 view->cursor_page = page;
5514
5515 return TRUE;
5516 }
5517
5518 return FALSE;
5519 }
5520
5521 static gboolean
position_caret_cursor_at_location(EvView * view,gdouble x,gdouble y)5522 position_caret_cursor_at_location (EvView *view,
5523 gdouble x,
5524 gdouble y)
5525 {
5526 gint page;
5527 gint doc_x, doc_y;
5528
5529 if (!view->caret_enabled || view->rotation != 0)
5530 return FALSE;
5531
5532 if (!view->page_cache)
5533 return FALSE;
5534
5535 /* Get the offset from the doc point */
5536 if (!get_doc_point_from_location (view, x, y, &page, &doc_x, &doc_y))
5537 return FALSE;
5538
5539 return position_caret_cursor_at_doc_point (view, page, doc_x, doc_y);
5540 }
5541
5542 static gboolean
position_caret_cursor_for_event(EvView * view,GdkEventButton * event,gboolean redraw)5543 position_caret_cursor_for_event (EvView *view,
5544 GdkEventButton *event,
5545 gboolean redraw)
5546 {
5547 GdkRectangle area;
5548 GdkRectangle prev_area = { 0, 0, 0, 0 };
5549
5550 if (redraw)
5551 get_caret_cursor_area (view, view->cursor_page, view->cursor_offset, &prev_area);
5552
5553 if (!position_caret_cursor_at_location (view, event->x, event->y))
5554 return FALSE;
5555
5556 if (!get_caret_cursor_area (view, view->cursor_page, view->cursor_offset, &area))
5557 return FALSE;
5558
5559 view->cursor_line_offset = area.x;
5560
5561 g_signal_emit (view, signals[SIGNAL_CURSOR_MOVED], 0, view->cursor_page, view->cursor_offset);
5562
5563 if (redraw) {
5564 cairo_region_t *damage_region;
5565
5566 damage_region = cairo_region_create_rectangle (&prev_area);
5567 cairo_region_union_rectangle (damage_region, &area);
5568 gdk_window_invalidate_region (gtk_widget_get_window (GTK_WIDGET (view)),
5569 damage_region, TRUE);
5570 cairo_region_destroy (damage_region);
5571 }
5572
5573 return TRUE;
5574 }
5575
5576 static gboolean
ev_view_button_press_event(GtkWidget * widget,GdkEventButton * event)5577 ev_view_button_press_event (GtkWidget *widget,
5578 GdkEventButton *event)
5579 {
5580 EvView *view = EV_VIEW (widget);
5581
5582 ev_view_link_preview_popover_cleanup (view);
5583
5584 if (!view->document || ev_document_get_n_pages (view->document) <= 0)
5585 return FALSE;
5586
5587 if (gtk_gesture_is_recognized (view->zoom_gesture))
5588 return TRUE;
5589
5590 if (!gtk_widget_has_focus (widget)) {
5591 gtk_widget_grab_focus (widget);
5592 }
5593
5594 if (view->window_child_focus) {
5595 EvAnnotationWindow *window;
5596
5597 window = EV_ANNOTATION_WINDOW (view->window_child_focus->window);
5598 ev_annotation_window_ungrab_focus (window);
5599 view->window_child_focus = NULL;
5600 }
5601
5602 view->pressed_button = event->button;
5603 view->selection_info.in_drag = FALSE;
5604
5605 if (view->scroll_info.autoscrolling)
5606 return TRUE;
5607
5608 if (view->adding_annot_info.adding_annot && !view->adding_annot_info.annot) {
5609 if (event->button != 1)
5610 return TRUE;
5611
5612 view->adding_annot_info.start.x = event->x + view->scroll_x;
5613 view->adding_annot_info.start.y = event->y + view->scroll_y;
5614 view->adding_annot_info.stop = view->adding_annot_info.start;
5615 ev_view_create_annotation (view);
5616
5617 return TRUE;
5618 }
5619
5620 switch (event->button) {
5621 case 1: {
5622 EvImage *image;
5623 EvAnnotation *annot;
5624 EvFormField *field;
5625 EvMapping *link;
5626 EvMedia *media;
5627 gint page;
5628
5629 if (event->state & GDK_CONTROL_MASK)
5630 return ev_view_synctex_backward_search (view, event->x , event->y);
5631
5632 if (EV_IS_SELECTION (view->document) && view->selection_info.selections) {
5633 if (event->type == GDK_3BUTTON_PRESS) {
5634 start_selection_for_event (view, event);
5635 } else if (event->state & GDK_SHIFT_MASK) {
5636 GdkPoint end_point;
5637
5638 end_point.x = event->x + view->scroll_x;
5639 end_point.y = event->y + view->scroll_y;
5640 extend_selection (view, &view->selection_info.start, &end_point);
5641 } else if (location_in_selected_text (view,
5642 event->x + view->scroll_x,
5643 event->y + view->scroll_y)) {
5644 view->selection_info.in_drag = TRUE;
5645 } else {
5646 start_selection_for_event (view, event);
5647 if (position_caret_cursor_for_event (view, event, TRUE)) {
5648 view->cursor_blink_time = 0;
5649 ev_view_pend_cursor_blink (view);
5650 }
5651 }
5652 } else if ((media = ev_view_get_media_at_location (view, event->x, event->y))) {
5653 ev_view_handle_media (view, media);
5654 } else if ((annot = ev_view_get_annotation_at_location (view, event->x, event->y))) {
5655 if (EV_IS_ANNOTATION_TEXT (annot)) {
5656 EvRectangle current_area;
5657 GdkPoint view_point;
5658 EvPoint doc_point;
5659 GdkRectangle page_area;
5660 GtkBorder border;
5661 guint annot_page;
5662
5663 /* annot_clicked remembers that we clicked
5664 * on an annotation. We need moving_annot
5665 * to distinguish moving an annotation from
5666 * showing its popup upon button release. */
5667 view->moving_annot_info.annot_clicked = TRUE;
5668 view->moving_annot_info.moving_annot = FALSE;
5669 view->moving_annot_info.annot = annot;
5670 ev_annotation_get_area (annot, ¤t_area);
5671
5672 view_point.x = event->x + view->scroll_x;
5673 view_point.y = event->y + view->scroll_y;
5674
5675 /* Remember the coordinates of the button press event
5676 * in order to implement a minimum threshold for moving
5677 * annotations. */
5678 view->moving_annot_info.start = view_point;
5679 annot_page = ev_annotation_get_page_index (annot);
5680 ev_view_get_page_extents (view, annot_page, &page_area, &border);
5681 _ev_view_transform_view_point_to_doc_point (view, &view_point,
5682 &page_area, &border,
5683 &doc_point.x, &doc_point.y);
5684
5685 /* Remember the offset of the cursor with respect to
5686 * the annotation area in order to prevent the annotation from
5687 * jumping under the cursor while moving it. */
5688 view->moving_annot_info.cursor_offset.x = doc_point.x - current_area.x1;
5689 view->moving_annot_info.cursor_offset.y = doc_point.y - current_area.y1;
5690 }
5691 } else if ((field = ev_view_get_form_field_at_location (view, event->x, event->y))) {
5692 ev_view_remove_all_form_fields (view);
5693 ev_view_handle_form_field (view, field);
5694 } else if ((link = get_link_mapping_at_location (view, event->x, event->y, &page))){
5695 _ev_view_set_focused_element (view, link, page);
5696 } else if (!location_in_text (view, event->x + view->scroll_x, event->y + view->scroll_y) &&
5697 (image = ev_view_get_image_at_location (view, event->x, event->y))) {
5698 if (view->image_dnd_info.image)
5699 g_object_unref (view->image_dnd_info.image);
5700 view->image_dnd_info.image = g_object_ref (image);
5701 view->image_dnd_info.in_drag = TRUE;
5702
5703 view->image_dnd_info.start.x = event->x + view->scroll_x;
5704 view->image_dnd_info.start.y = event->y + view->scroll_y;
5705 } else {
5706 ev_view_remove_all_form_fields (view);
5707 _ev_view_set_focused_element (view, NULL, -1);
5708
5709 if (view->synctex_result) {
5710 g_free (view->synctex_result);
5711 view->synctex_result = NULL;
5712 gtk_widget_queue_draw (widget);
5713 }
5714
5715 if (EV_IS_SELECTION (view->document))
5716 start_selection_for_event (view, event);
5717
5718 if (position_caret_cursor_for_event (view, event, TRUE)) {
5719 view->cursor_blink_time = 0;
5720 ev_view_pend_cursor_blink (view);
5721 }
5722 }
5723 }
5724 return TRUE;
5725 case 2:
5726 /* use root coordinates as reference point because
5727 * scrolling changes window relative coordinates */
5728 view->drag_info.start.x = event->x_root;
5729 view->drag_info.start.y = event->y_root;
5730 view->drag_info.hadj = gtk_adjustment_get_value (view->hadjustment);
5731 view->drag_info.vadj = gtk_adjustment_get_value (view->vadjustment);
5732
5733 ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG);
5734 ev_view_set_focused_element_at_location (view, event->x, event->y);
5735 return TRUE;
5736 case 3:
5737 view->scroll_info.start_y = event->y;
5738 ev_view_set_focused_element_at_location (view, event->x, event->y);
5739 return ev_view_do_popup_menu (view, event->x, event->y);
5740 }
5741
5742 return FALSE;
5743 }
5744
5745 static void
ev_view_remove_all(EvView * view)5746 ev_view_remove_all (EvView *view)
5747 {
5748 gtk_container_foreach (GTK_CONTAINER (view), (GtkCallback) gtk_widget_destroy, NULL);
5749 }
5750
5751 static void
destroy_child_if_form_widget(GtkWidget * widget)5752 destroy_child_if_form_widget (GtkWidget *widget)
5753 {
5754 if (g_object_get_data (G_OBJECT (widget), "form-field"))
5755 gtk_widget_destroy (widget);
5756 }
5757
5758 static void
ev_view_remove_all_form_fields(EvView * view)5759 ev_view_remove_all_form_fields (EvView *view)
5760 {
5761 gtk_container_foreach (GTK_CONTAINER (view), (GtkCallback)destroy_child_if_form_widget, NULL);
5762 }
5763
5764 /*** Drag and Drop ***/
5765 static void
ev_view_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)5766 ev_view_drag_data_get (GtkWidget *widget,
5767 GdkDragContext *context,
5768 GtkSelectionData *selection_data,
5769 guint info,
5770 guint time)
5771 {
5772 EvView *view = EV_VIEW (widget);
5773
5774 switch (info) {
5775 case TARGET_DND_TEXT:
5776 if (EV_IS_SELECTION (view->document) &&
5777 view->selection_info.selections) {
5778 gchar *text;
5779
5780 text = get_selected_text (view);
5781 gtk_selection_data_set_text (selection_data,
5782 text,
5783 strlen (text));
5784 g_free (text);
5785 }
5786 break;
5787 case TARGET_DND_IMAGE:
5788 if (view->image_dnd_info.image) {
5789 GdkPixbuf *pixbuf;
5790
5791 ev_document_doc_mutex_lock ();
5792 pixbuf = ev_document_images_get_image (EV_DOCUMENT_IMAGES (view->document),
5793 view->image_dnd_info.image);
5794 ev_document_doc_mutex_unlock ();
5795
5796 gtk_selection_data_set_pixbuf (selection_data, pixbuf);
5797 g_object_unref (pixbuf);
5798 }
5799 break;
5800 case TARGET_DND_URI:
5801 if (view->image_dnd_info.image) {
5802 GdkPixbuf *pixbuf;
5803 const gchar *tmp_uri;
5804 gchar *uris[2];
5805
5806 ev_document_doc_mutex_lock ();
5807 pixbuf = ev_document_images_get_image (EV_DOCUMENT_IMAGES (view->document),
5808 view->image_dnd_info.image);
5809 ev_document_doc_mutex_unlock ();
5810
5811 tmp_uri = ev_image_save_tmp (view->image_dnd_info.image, pixbuf);
5812 g_object_unref (pixbuf);
5813
5814 uris[0] = (gchar *)tmp_uri;
5815 uris[1] = NULL;
5816 gtk_selection_data_set_uris (selection_data, uris);
5817 }
5818 }
5819 }
5820
5821 static gboolean
ev_view_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)5822 ev_view_drag_motion (GtkWidget *widget,
5823 GdkDragContext *context,
5824 gint x,
5825 gint y,
5826 guint time)
5827 {
5828 if (gtk_drag_get_source_widget (context) == widget)
5829 gdk_drag_status (context, 0, time);
5830 else
5831 gdk_drag_status (context, gdk_drag_context_get_suggested_action (context), time);
5832
5833 return TRUE;
5834 }
5835
5836 static gboolean
selection_update_idle_cb(EvView * view)5837 selection_update_idle_cb (EvView *view)
5838 {
5839 compute_selections (view,
5840 view->selection_info.style,
5841 &view->selection_info.start,
5842 &view->motion);
5843 view->selection_update_id = 0;
5844 return FALSE;
5845 }
5846
5847 static gboolean
selection_scroll_timeout_cb(EvView * view)5848 selection_scroll_timeout_cb (EvView *view)
5849 {
5850 gint x, y, shift = 0;
5851 GtkWidget *widget = GTK_WIDGET (view);
5852 GtkAllocation allocation;
5853
5854 gtk_widget_get_allocation (widget, &allocation);
5855 ev_document_misc_get_pointer_position (widget, &x, &y);
5856
5857 if (y > allocation.height) {
5858 shift = (y - allocation.height) / 2;
5859 } else if (y < 0) {
5860 shift = y / 2;
5861 }
5862
5863 if (shift)
5864 gtk_adjustment_set_value (view->vadjustment,
5865 CLAMP (gtk_adjustment_get_value (view->vadjustment) + shift,
5866 gtk_adjustment_get_lower (view->vadjustment),
5867 gtk_adjustment_get_upper (view->vadjustment) -
5868 gtk_adjustment_get_page_size (view->vadjustment)));
5869
5870 if (x > allocation.width) {
5871 shift = (x - allocation.width) / 2;
5872 } else if (x < 0) {
5873 shift = x / 2;
5874 }
5875
5876 if (shift)
5877 gtk_adjustment_set_value (view->hadjustment,
5878 CLAMP (gtk_adjustment_get_value (view->hadjustment) + shift,
5879 gtk_adjustment_get_lower (view->hadjustment),
5880 gtk_adjustment_get_upper (view->hadjustment) -
5881 gtk_adjustment_get_page_size (view->hadjustment)));
5882
5883 return TRUE;
5884 }
5885
5886 static gboolean
ev_view_drag_update_momentum(EvView * view)5887 ev_view_drag_update_momentum (EvView *view)
5888 {
5889 int i;
5890 if (!view->drag_info.in_drag)
5891 return FALSE;
5892
5893 for (i = DRAG_HISTORY - 1; i > 0; i--) {
5894 view->drag_info.buffer[i].x = view->drag_info.buffer[i-1].x;
5895 view->drag_info.buffer[i].y = view->drag_info.buffer[i-1].y;
5896 }
5897
5898 /* Momentum is a moving average of 10ms granularity over
5899 * the last 100ms with each 10ms stored in buffer.
5900 */
5901
5902 view->drag_info.momentum.x = (view->drag_info.buffer[DRAG_HISTORY - 1].x - view->drag_info.buffer[0].x);
5903 view->drag_info.momentum.y = (view->drag_info.buffer[DRAG_HISTORY - 1].y - view->drag_info.buffer[0].y);
5904
5905 return TRUE;
5906 }
5907
5908 static gboolean
ev_view_scroll_drag_release(EvView * view)5909 ev_view_scroll_drag_release (EvView *view)
5910 {
5911 gdouble dhadj_value, dvadj_value;
5912 gdouble oldhadjustment, oldvadjustment;
5913 gdouble h_page_size, v_page_size;
5914 gdouble h_upper, v_upper;
5915 GtkAllocation allocation;
5916
5917 view->drag_info.momentum.x /= 1.2;
5918 view->drag_info.momentum.y /= 1.2; /* Alter these constants to change "friction" */
5919
5920 gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
5921
5922 h_page_size = gtk_adjustment_get_page_size (view->hadjustment);
5923 v_page_size = gtk_adjustment_get_page_size (view->vadjustment);
5924
5925 dhadj_value = h_page_size *
5926 (gdouble)view->drag_info.momentum.x / allocation.width;
5927 dvadj_value = v_page_size *
5928 (gdouble)view->drag_info.momentum.y / allocation.height;
5929
5930 oldhadjustment = gtk_adjustment_get_value (view->hadjustment);
5931 oldvadjustment = gtk_adjustment_get_value (view->vadjustment);
5932
5933 h_upper = gtk_adjustment_get_upper (view->hadjustment);
5934 v_upper = gtk_adjustment_get_upper (view->vadjustment);
5935
5936 /* When we reach the edges, we need either to absorb some momentum and bounce by
5937 * multiplying it on -0.5 or stop scrolling by setting momentum to 0. */
5938 if (((oldhadjustment + dhadj_value) > (h_upper - h_page_size)) ||
5939 ((oldhadjustment + dhadj_value) < 0))
5940 view->drag_info.momentum.x = 0;
5941 if (((oldvadjustment + dvadj_value) > (v_upper - v_page_size)) ||
5942 ((oldvadjustment + dvadj_value) < 0))
5943 view->drag_info.momentum.y = 0;
5944
5945 gtk_adjustment_set_value (view->hadjustment,
5946 MIN (oldhadjustment + dhadj_value,
5947 h_upper - h_page_size));
5948 gtk_adjustment_set_value (view->vadjustment,
5949 MIN (oldvadjustment + dvadj_value,
5950 v_upper - v_page_size));
5951
5952 if (((view->drag_info.momentum.x < 1) && (view->drag_info.momentum.x > -1)) &&
5953 ((view->drag_info.momentum.y < 1) && (view->drag_info.momentum.y > -1)))
5954 return FALSE;
5955 else
5956 return TRUE;
5957 }
5958
5959 static gboolean
ev_view_motion_notify_event(GtkWidget * widget,GdkEventMotion * event)5960 ev_view_motion_notify_event (GtkWidget *widget,
5961 GdkEventMotion *event)
5962 {
5963 EvView *view = EV_VIEW (widget);
5964 GdkWindow *window;
5965 gint x, y;
5966
5967 if (!view->document)
5968 return FALSE;
5969
5970 if (gtk_gesture_is_recognized (view->zoom_gesture))
5971 return TRUE;
5972
5973 window = gtk_widget_get_window (widget);
5974
5975 if (event->is_hint || event->window != window) {
5976 ev_document_misc_get_pointer_position (widget, &x, &y);
5977 } else {
5978 x = event->x;
5979 y = event->y;
5980 }
5981
5982 if (view->scroll_info.autoscrolling) {
5983 if (y >= 0)
5984 view->scroll_info.last_y = y;
5985 return TRUE;
5986 }
5987
5988 if (view->selection_info.in_drag) {
5989 if (gtk_drag_check_threshold (widget,
5990 view->selection_info.start.x,
5991 view->selection_info.start.y,
5992 x, y)) {
5993 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
5994
5995 gtk_target_list_add_text_targets (target_list, TARGET_DND_TEXT);
5996
5997 gtk_drag_begin_with_coordinates (widget, target_list,
5998 GDK_ACTION_COPY,
5999 1, (GdkEvent *)event,
6000 -1, -1);
6001
6002 view->selection_info.in_drag = FALSE;
6003 view->pressed_button = -1;
6004
6005 gtk_target_list_unref (target_list);
6006
6007 return TRUE;
6008 }
6009 } else if (view->image_dnd_info.in_drag) {
6010 if (gtk_drag_check_threshold (widget,
6011 view->selection_info.start.x,
6012 view->selection_info.start.y,
6013 x, y)) {
6014 GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
6015
6016 gtk_target_list_add_uri_targets (target_list, TARGET_DND_URI);
6017 gtk_target_list_add_image_targets (target_list, TARGET_DND_IMAGE, TRUE);
6018
6019 gtk_drag_begin_with_coordinates (widget, target_list,
6020 GDK_ACTION_COPY,
6021 1, (GdkEvent *)event,
6022 -1, -1);
6023
6024 view->image_dnd_info.in_drag = FALSE;
6025 view->pressed_button = -1;
6026
6027 gtk_target_list_unref (target_list);
6028
6029 return TRUE;
6030 }
6031 }
6032
6033 switch (view->pressed_button) {
6034 case 1:
6035 /* For the Evince 0.4.x release, we limit selection to un-rotated
6036 * documents only.
6037 */
6038 if (view->rotation != 0)
6039 return FALSE;
6040
6041 if (view->adding_annot_info.adding_annot) {
6042 EvRectangle rect;
6043 EvRectangle current_area;
6044 EvPoint start;
6045 EvPoint end;
6046 GdkRectangle page_area;
6047 GtkBorder border;
6048 guint annot_page;
6049
6050 if (!view->adding_annot_info.annot)
6051 return TRUE;
6052
6053 ev_annotation_get_area (view->adding_annot_info.annot, ¤t_area);
6054
6055 view->adding_annot_info.stop.x = event->x + view->scroll_x;
6056 view->adding_annot_info.stop.y = event->y + view->scroll_y;
6057 annot_page = ev_annotation_get_page_index (view->adding_annot_info.annot);
6058 ev_view_get_page_extents (view, annot_page, &page_area, &border);
6059 _ev_view_transform_view_point_to_doc_point (view, &view->adding_annot_info.start, &page_area, &border,
6060 &start.x, &start.y);
6061 _ev_view_transform_view_point_to_doc_point (view, &view->adding_annot_info.stop, &page_area, &border,
6062 &end.x, &end.y);
6063
6064 switch (view->adding_annot_info.type) {
6065 case EV_ANNOTATION_TYPE_TEXT:
6066 rect.x1 = end.x;
6067 rect.y1 = end.y;
6068 rect.x2 = rect.x1 + current_area.x2 - current_area.x1;
6069 rect.y2 = rect.y1 + current_area.y2 - current_area.y1;
6070 break;
6071 case EV_ANNOTATION_TYPE_TEXT_MARKUP:
6072 rect.x1 = start.x;
6073 rect.y1 = start.y;
6074 rect.x2 = end.x;
6075 rect.y2 = end.y;
6076 break;
6077 default:
6078 g_assert_not_reached ();
6079 }
6080
6081 /* Take the mutex before set_area, because the notify signal
6082 * updates the mappings in the backend */
6083 ev_document_doc_mutex_lock ();
6084 if (ev_annotation_set_area (view->adding_annot_info.annot, &rect)) {
6085 ev_document_annotations_save_annotation (EV_DOCUMENT_ANNOTATIONS (view->document),
6086 view->adding_annot_info.annot,
6087 EV_ANNOTATIONS_SAVE_AREA);
6088 }
6089 ev_document_doc_mutex_unlock ();
6090
6091
6092 /* FIXME: reload only annotation area */
6093 ev_view_reload_page (view, annot_page, NULL);
6094 } else if (view->moving_annot_info.annot_clicked) {
6095 EvRectangle rect;
6096 EvRectangle current_area;
6097 GdkPoint view_point;
6098 EvPoint doc_point;
6099 GdkRectangle page_area;
6100 GtkBorder border;
6101 guint annot_page;
6102 double page_width;
6103 double page_height;
6104
6105 if (!view->moving_annot_info.annot)
6106 return TRUE;
6107
6108 view_point.x = event->x + view->scroll_x;
6109 view_point.y = event->y + view->scroll_y;
6110
6111 if (!view->moving_annot_info.moving_annot) {
6112 /* Only move the annotation if the threshold is exceeded */
6113 if (!gtk_drag_check_threshold (widget,
6114 view->moving_annot_info.start.x,
6115 view->moving_annot_info.start.y,
6116 view_point.x,
6117 view_point.y))
6118 return TRUE;
6119 view->moving_annot_info.moving_annot = TRUE;
6120 }
6121
6122 ev_annotation_get_area (view->moving_annot_info.annot, ¤t_area);
6123 annot_page = ev_annotation_get_page_index (view->moving_annot_info.annot);
6124 ev_view_get_page_extents (view, annot_page, &page_area, &border);
6125 _ev_view_transform_view_point_to_doc_point (view, &view_point, &page_area, &border,
6126 &doc_point.x, &doc_point.y);
6127
6128 ev_document_get_page_size (view->document, annot_page, &page_width, &page_height);
6129
6130 rect.x1 = MAX (0, doc_point.x - view->moving_annot_info.cursor_offset.x);
6131 rect.y1 = MAX (0, doc_point.y - view->moving_annot_info.cursor_offset.y);
6132 rect.x2 = rect.x1 + current_area.x2 - current_area.x1;
6133 rect.y2 = rect.y1 + current_area.y2 - current_area.y1;
6134
6135 /* Prevent the annotation from being moved off the page */
6136 if (rect.x2 > page_width) {
6137 rect.x2 = page_width;
6138 rect.x1 = page_width - current_area.x2 + current_area.x1;
6139 }
6140 if (rect.y2 > page_height) {
6141 rect.y2 = page_height;
6142 rect.y1 = page_height - current_area.y2 + current_area.y1;
6143 }
6144
6145 /* Take the mutex before set_area, because the notify signal
6146 * updates the mappings in the backend */
6147 ev_document_doc_mutex_lock ();
6148 if (ev_annotation_set_area (view->moving_annot_info.annot, &rect)) {
6149 ev_document_annotations_save_annotation (EV_DOCUMENT_ANNOTATIONS (view->document),
6150 view->moving_annot_info.annot,
6151 EV_ANNOTATIONS_SAVE_AREA);
6152 }
6153 ev_document_doc_mutex_unlock ();
6154
6155 /* FIXME: reload only annotation area */
6156 ev_view_reload_page (view, annot_page, NULL);
6157 } else if (ev_document_has_synctex (view->document) && (event->state & GDK_CONTROL_MASK)) {
6158 /* Ignore spurious motion event triggered by slightly moving mouse
6159 * while clicking for launching synctex. Issue #951 */
6160 return TRUE;
6161 } else {
6162 /* Schedule timeout to scroll during selection and additionally
6163 * scroll once to allow arbitrary speed. */
6164 if (!view->selection_scroll_id)
6165 view->selection_scroll_id = g_timeout_add (SCROLL_TIME,
6166 (GSourceFunc)selection_scroll_timeout_cb,
6167 view);
6168 else
6169 selection_scroll_timeout_cb (view);
6170
6171 view->motion.x = x + view->scroll_x;
6172 view->motion.y = y + view->scroll_y;
6173
6174 /* Queue an idle to handle the motion. We do this because
6175 * handling any selection events in the motion could be slower
6176 * than new motion events reach us. We always put it in the
6177 * idle to make sure we catch up and don't visibly lag the
6178 * mouse. */
6179 if (!view->selection_update_id)
6180 view->selection_update_id = g_idle_add ((GSourceFunc)selection_update_idle_cb, view);
6181 }
6182
6183 return TRUE;
6184 case 2:
6185 if (!view->drag_info.in_drag) {
6186 gboolean start;
6187 int i;
6188
6189 start = gtk_drag_check_threshold (widget,
6190 view->drag_info.start.x,
6191 view->drag_info.start.y,
6192 event->x_root,
6193 event->y_root);
6194 view->drag_info.in_drag = start;
6195 view->drag_info.drag_timeout_id = g_timeout_add (10,
6196 (GSourceFunc)ev_view_drag_update_momentum, view);
6197 /* Set 100 to choose how long it takes to build up momentum */
6198 /* Clear out previous momentum info: */
6199 for (i = 0; i < DRAG_HISTORY; i++) {
6200 view->drag_info.buffer[i].x = event->x;
6201 view->drag_info.buffer[i].y = event->y;
6202 }
6203 view->drag_info.momentum.x = 0;
6204 view->drag_info.momentum.y = 0;
6205 }
6206
6207 if (view->drag_info.in_drag) {
6208 int dx, dy;
6209 gdouble dhadj_value, dvadj_value;
6210 GtkAllocation allocation;
6211
6212 view->drag_info.buffer[0].x = event->x;
6213 view->drag_info.buffer[0].y = event->y;
6214
6215 dx = event->x_root - view->drag_info.start.x;
6216 dy = event->y_root - view->drag_info.start.y;
6217
6218 gtk_widget_get_allocation (widget, &allocation);
6219
6220 dhadj_value = gtk_adjustment_get_page_size (view->hadjustment) *
6221 (gdouble)dx / allocation.width;
6222 dvadj_value = gtk_adjustment_get_page_size (view->vadjustment) *
6223 (gdouble)dy / allocation.height;
6224
6225 /* We will update the drag event's start position if
6226 * the adjustment value is changed, but only if the
6227 * change was not caused by this function. */
6228 view->drag_info.in_notify = TRUE;
6229
6230 /* clamp scrolling to visible area */
6231 gtk_adjustment_set_value (view->hadjustment,
6232 MIN (view->drag_info.hadj - dhadj_value,
6233 gtk_adjustment_get_upper (view->hadjustment) -
6234 gtk_adjustment_get_page_size (view->hadjustment)));
6235 gtk_adjustment_set_value (view->vadjustment,
6236 MIN (view->drag_info.vadj - dvadj_value,
6237 gtk_adjustment_get_upper (view->vadjustment) -
6238 gtk_adjustment_get_page_size (view->vadjustment)));
6239
6240 view->drag_info.in_notify = FALSE;
6241
6242 return TRUE;
6243 }
6244
6245 break;
6246 default:
6247 ev_view_handle_cursor_over_xy (view, x, y);
6248 }
6249
6250 return FALSE;
6251 }
6252 /**
6253 * ev_view_get_selected_text:
6254 * @view: #EvView instance
6255 *
6256 * Returns a pointer to a constant string containing the selected
6257 * text in the view.
6258 *
6259 * The value returned may be NULL if there is no selected text.
6260 *
6261 * Returns: The string representing selected text.
6262 *
6263 * Since: 3.30
6264 */
6265 char *
ev_view_get_selected_text(EvView * view)6266 ev_view_get_selected_text (EvView *view)
6267 {
6268 return get_selected_text (view);
6269 }
6270
6271 /**
6272 * ev_view_add_text_markup_annotation_for_selected_text:
6273 * @view: #EvView instance
6274 *
6275 * Adds a Text Markup annotation (defaulting to a 'highlight' one) to
6276 * the currently selected text on the document.
6277 *
6278 * When the selected text spans more than one page, it will add a
6279 * corresponding annotation for each page that contains selected text.
6280 *
6281 * Returns: %TRUE if annotations were added successfully, %FALSE otherwise.
6282 *
6283 * Since: 3.30
6284 */
6285 gboolean
ev_view_add_text_markup_annotation_for_selected_text(EvView * view)6286 ev_view_add_text_markup_annotation_for_selected_text (EvView *view)
6287 {
6288 GList *l;
6289
6290 if (view->adding_annot_info.annot || view->adding_annot_info.adding_annot ||
6291 view->selection_info.selections == NULL)
6292 return FALSE;
6293
6294 for (l = view->selection_info.selections; l != NULL; l = l->next) {
6295 EvViewSelection *selection = (EvViewSelection *)l->data;
6296
6297 view->adding_annot_info.adding_annot = TRUE;
6298 view->adding_annot_info.type = EV_ANNOTATION_TYPE_TEXT_MARKUP;
6299
6300 ev_view_create_annotation_from_selection (view, selection);
6301
6302 if (view->adding_annot_info.adding_annot)
6303 g_signal_emit (view, signals[SIGNAL_ANNOT_ADDED], 0, view->adding_annot_info.annot);
6304 }
6305
6306 clear_selection (view);
6307
6308 view->adding_annot_info.adding_annot = FALSE;
6309 view->adding_annot_info.annot = NULL;
6310
6311 return TRUE;
6312 }
6313
6314 void
ev_view_set_enable_spellchecking(EvView * view,gboolean enabled)6315 ev_view_set_enable_spellchecking (EvView *view,
6316 gboolean enabled)
6317 {
6318 EvMappingList *annots;
6319 GList *l;
6320 gint n_pages = 0;
6321 gint current_page;
6322
6323 g_return_if_fail (EV_IS_VIEW (view));
6324
6325 view->enable_spellchecking = enabled;
6326
6327 if (view->document)
6328 n_pages = ev_document_get_n_pages (view->document);
6329
6330 for (current_page = 0; current_page < n_pages; current_page++) {
6331 annots = ev_page_cache_get_annot_mapping (view->page_cache, current_page);
6332
6333 for (l = ev_mapping_list_get_list (annots); l && l->data; l = g_list_next (l)) {
6334 EvAnnotation *annot;
6335 GtkWidget *window;
6336
6337 annot = ((EvMapping *)(l->data))->data;
6338
6339 if (!EV_IS_ANNOTATION_MARKUP (annot))
6340 continue;
6341
6342 window = get_window_for_annot (view, annot);
6343
6344 if (window) {
6345 ev_annotation_window_set_enable_spellchecking (EV_ANNOTATION_WINDOW (window), view->enable_spellchecking);
6346 }
6347 }
6348 }
6349 }
6350
6351 gboolean
ev_view_get_enable_spellchecking(EvView * view)6352 ev_view_get_enable_spellchecking (EvView *view)
6353 {
6354 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
6355
6356 #ifdef WITH_GSPELL
6357 return view->enable_spellchecking;
6358 #else
6359 return FALSE;
6360 #endif
6361 }
6362
6363 static gboolean
ev_view_button_release_event(GtkWidget * widget,GdkEventButton * event)6364 ev_view_button_release_event (GtkWidget *widget,
6365 GdkEventButton *event)
6366 {
6367 EvView *view = EV_VIEW (widget);
6368 EvLink *link = NULL;
6369
6370 view->image_dnd_info.in_drag = FALSE;
6371
6372 if (gtk_gesture_is_recognized (view->zoom_gesture))
6373 return TRUE;
6374
6375 if (view->scroll_info.autoscrolling) {
6376 ev_view_autoscroll_stop (view);
6377 view->pressed_button = -1;
6378
6379 return TRUE;
6380 }
6381
6382 if (view->pressed_button == 1 && event->state & GDK_CONTROL_MASK) {
6383 view->pressed_button = -1;
6384 return TRUE;
6385 }
6386
6387 if (view->drag_info.in_drag) {
6388 view->drag_info.release_timeout_id =
6389 g_timeout_add (20,
6390 (GSourceFunc)ev_view_scroll_drag_release, view);
6391 }
6392
6393 if (view->document && !view->drag_info.in_drag &&
6394 (view->pressed_button == GDK_BUTTON_PRIMARY ||
6395 view->pressed_button == GDK_BUTTON_MIDDLE)) {
6396 link = ev_view_get_link_at_location (view, event->x, event->y);
6397 }
6398
6399 view->drag_info.in_drag = FALSE;
6400
6401 if (view->adding_annot_info.adding_annot) {
6402 gboolean annot_added = TRUE;
6403
6404 /* We ignore right-click buttons while in annotation add mode */
6405 if (view->pressed_button != 1)
6406 return FALSE;
6407 g_assert (view->adding_annot_info.annot);
6408
6409 if (EV_IS_ANNOTATION_MARKUP (view->adding_annot_info.annot)) {
6410 EvRectangle area;
6411 EvRectangle popup_rect;
6412
6413 ev_annotation_get_area (view->adding_annot_info.annot, &area);
6414
6415 if (area.x1 == 0 && area.y1 == 0 && area.x2 == 0 && area.y2 == 0) {
6416 /* Do not create empty annots */
6417 annot_added = FALSE;
6418
6419 ev_document_doc_mutex_lock ();
6420 ev_document_annotations_remove_annotation (EV_DOCUMENT_ANNOTATIONS (view->document),
6421 view->adding_annot_info.annot);
6422 ev_document_doc_mutex_unlock ();
6423
6424 ev_page_cache_mark_dirty (view->page_cache,
6425 ev_annotation_get_page_index (view->adding_annot_info.annot),
6426 EV_PAGE_DATA_INCLUDE_ANNOTS);
6427 } else {
6428 popup_rect.x1 = area.x2;
6429 popup_rect.x2 = popup_rect.x1 + ANNOT_POPUP_WINDOW_DEFAULT_WIDTH;
6430 popup_rect.y1 = area.y2;
6431 popup_rect.y2 = popup_rect.y1 + ANNOT_POPUP_WINDOW_DEFAULT_HEIGHT;
6432
6433 if (ev_annotation_markup_set_rectangle (EV_ANNOTATION_MARKUP (view->adding_annot_info.annot),
6434 &popup_rect)) {
6435 ev_document_doc_mutex_lock ();
6436 ev_document_annotations_save_annotation (EV_DOCUMENT_ANNOTATIONS (view->document),
6437 view->adding_annot_info.annot,
6438 EV_ANNOTATIONS_SAVE_POPUP_RECT);
6439 ev_document_doc_mutex_unlock ();
6440 }
6441 }
6442 }
6443
6444 if (view->adding_annot_info.type == EV_ANNOTATION_TYPE_TEXT)
6445 ev_view_annotation_create_show_popup_window (view, view->adding_annot_info.annot);
6446
6447 view->adding_annot_info.stop.x = event->x + view->scroll_x;
6448 view->adding_annot_info.stop.y = event->y + view->scroll_y;
6449 if (annot_added)
6450 g_signal_emit (view, signals[SIGNAL_ANNOT_ADDED], 0, view->adding_annot_info.annot);
6451
6452 view->adding_annot_info.adding_annot = FALSE;
6453 view->adding_annot_info.annot = NULL;
6454 ev_view_handle_cursor_over_xy (view, event->x, event->y);
6455 view->pressed_button = -1;
6456
6457 return FALSE;
6458 }
6459
6460 if (view->moving_annot_info.annot_clicked) {
6461 if (view->moving_annot_info.moving_annot)
6462 ev_view_handle_cursor_over_xy (view, event->x, event->y);
6463 else
6464 ev_view_handle_annotation (view, view->moving_annot_info.annot, event->x, event->y, event->time);
6465
6466 view->moving_annot_info.annot_clicked = FALSE;
6467 view->moving_annot_info.moving_annot = FALSE;
6468 view->moving_annot_info.annot = NULL;
6469 view->pressed_button = -1;
6470
6471 return FALSE;
6472 }
6473
6474 if (view->pressed_button == 1) {
6475 EvAnnotation *annot = ev_view_get_annotation_at_location (view, event->x, event->y);
6476
6477 if (annot)
6478 ev_view_handle_annotation (view, annot, event->x, event->y, event->time);
6479 }
6480
6481 if (view->pressed_button == 2) {
6482 ev_view_handle_cursor_over_xy (view, event->x, event->y);
6483 }
6484
6485 view->pressed_button = -1;
6486
6487 if (view->selection_scroll_id) {
6488 g_source_remove (view->selection_scroll_id);
6489 view->selection_scroll_id = 0;
6490 }
6491 if (view->selection_update_id) {
6492 g_source_remove (view->selection_update_id);
6493 view->selection_update_id = 0;
6494 }
6495
6496 if (view->selection_info.selections) {
6497 clear_link_selected (view);
6498 ev_view_update_primary_selection (view);
6499
6500 position_caret_cursor_for_event (view, event, FALSE);
6501
6502 if (view->selection_info.in_drag)
6503 clear_selection (view);
6504 view->selection_info.in_drag = FALSE;
6505 } else if (link) {
6506 if (event->button == 2) {
6507 EvLinkAction *action;
6508 EvLinkActionType type;
6509
6510 action = ev_link_get_action (link);
6511 if (!action)
6512 return FALSE;
6513
6514 type = ev_link_action_get_action_type (action);
6515 if (type == EV_LINK_ACTION_TYPE_GOTO_DEST) {
6516 g_signal_emit (view,
6517 signals[SIGNAL_EXTERNAL_LINK],
6518 0, action);
6519 }
6520 } else {
6521 ev_view_handle_link (view, link);
6522 }
6523 }
6524
6525 return FALSE;
6526 }
6527
6528 static gboolean
ev_view_forward_key_event_to_focused_child(EvView * view,GdkEventKey * event)6529 ev_view_forward_key_event_to_focused_child (EvView *view,
6530 GdkEventKey *event)
6531 {
6532 GtkWidget *child_widget = NULL;
6533 GdkEventKey *new_event;
6534 gboolean handled;
6535
6536 if (view->window_child_focus) {
6537 child_widget = view->window_child_focus->window;
6538 } else if (view->children) {
6539 EvViewChild *child = (EvViewChild *)view->children->data;
6540
6541 child_widget = child->widget;
6542 } else {
6543 return FALSE;
6544 }
6545
6546 new_event = (GdkEventKey *) gdk_event_copy ((GdkEvent *)event);
6547 g_object_unref (new_event->window);
6548 new_event->window = gtk_widget_get_window (child_widget);
6549 if (new_event->window)
6550 g_object_ref (new_event->window);
6551 gtk_widget_realize (child_widget);
6552 handled = gtk_widget_event (child_widget, (GdkEvent *)new_event);
6553 gdk_event_free ((GdkEvent *)new_event);
6554
6555 return handled;
6556 }
6557
6558 static gint
go_to_next_page(EvView * view,gint page)6559 go_to_next_page (EvView *view,
6560 gint page)
6561 {
6562 int n_pages;
6563 gboolean dual_page;
6564
6565 if (!view->document)
6566 return -1;
6567
6568 n_pages = ev_document_get_n_pages (view->document);
6569
6570 dual_page = is_dual_page (view, NULL);
6571 page += dual_page ? 2 : 1;
6572
6573 if (page < n_pages)
6574 return page;
6575
6576 if (dual_page && page == n_pages)
6577 return page - 1;
6578
6579 return -1;
6580 }
6581
6582 static gint
go_to_previous_page(EvView * view,gint page)6583 go_to_previous_page (EvView *view,
6584 gint page)
6585 {
6586 gboolean dual_page;
6587
6588 if (!view->document)
6589 return -1;
6590
6591 dual_page = is_dual_page (view, NULL);
6592 page -= dual_page ? 2 : 1;
6593
6594 if (page >= 0)
6595 return page;
6596
6597 if (dual_page && page == -1)
6598 return 0;
6599
6600 return -1;
6601 }
6602
6603 static gboolean
cursor_go_to_page_start(EvView * view)6604 cursor_go_to_page_start (EvView *view)
6605 {
6606 view->cursor_offset = 0;
6607
6608 return TRUE;
6609 }
6610
6611 static gboolean
cursor_go_to_page_end(EvView * view)6612 cursor_go_to_page_end (EvView *view)
6613 {
6614 PangoLogAttr *log_attrs = NULL;
6615 gulong n_attrs;
6616
6617 if (!view->page_cache)
6618 return FALSE;
6619
6620 ev_page_cache_get_text_log_attrs (view->page_cache, view->cursor_page, &log_attrs, &n_attrs);
6621 if (!log_attrs)
6622 return FALSE;
6623
6624 view->cursor_offset = n_attrs;
6625
6626 return TRUE;
6627 }
6628
6629 static gboolean
cursor_go_to_next_page(EvView * view)6630 cursor_go_to_next_page (EvView *view)
6631 {
6632 gint new_page;
6633
6634 new_page = go_to_next_page (view, view->cursor_page);
6635 if (new_page != -1) {
6636 view->cursor_page = new_page;
6637 return cursor_go_to_page_start (view);
6638 }
6639
6640 return FALSE;
6641 }
6642
6643 static gboolean
cursor_go_to_previous_page(EvView * view)6644 cursor_go_to_previous_page (EvView *view)
6645 {
6646 gint new_page;
6647
6648 new_page = go_to_previous_page (view, view->cursor_page);
6649 if (new_page != -1) {
6650 view->cursor_page = new_page;
6651 return cursor_go_to_page_end (view);
6652 }
6653 return FALSE;
6654 }
6655
6656 static gboolean
cursor_go_to_document_start(EvView * view)6657 cursor_go_to_document_start (EvView *view)
6658 {
6659 view->cursor_page = 0;
6660 return cursor_go_to_page_start (view);
6661 }
6662
6663 static gboolean
cursor_go_to_document_end(EvView * view)6664 cursor_go_to_document_end (EvView *view)
6665 {
6666 if (!view->document)
6667 return FALSE;
6668
6669 view->cursor_page = ev_document_get_n_pages (view->document) - 1;
6670 return cursor_go_to_page_end (view);
6671 }
6672
6673 static gboolean
cursor_backward_char(EvView * view)6674 cursor_backward_char (EvView *view)
6675 {
6676 PangoLogAttr *log_attrs = NULL;
6677 gulong n_attrs;
6678
6679 if (!view->page_cache)
6680 return FALSE;
6681
6682 ev_page_cache_get_text_log_attrs (view->page_cache, view->cursor_page, &log_attrs, &n_attrs);
6683 if (!log_attrs)
6684 return FALSE;
6685
6686 if (view->cursor_offset == 0)
6687 return cursor_go_to_previous_page (view);
6688
6689 do {
6690 view->cursor_offset--;
6691 } while (view->cursor_offset >= 0 && !log_attrs[view->cursor_offset].is_cursor_position);
6692
6693 return TRUE;
6694 }
6695
6696 static gboolean
cursor_forward_char(EvView * view)6697 cursor_forward_char (EvView *view)
6698 {
6699 PangoLogAttr *log_attrs = NULL;
6700 gulong n_attrs;
6701
6702 if (!view->page_cache)
6703 return FALSE;
6704
6705 ev_page_cache_get_text_log_attrs (view->page_cache, view->cursor_page, &log_attrs, &n_attrs);
6706 if (!log_attrs)
6707 return FALSE;
6708
6709 if (view->cursor_offset >= n_attrs)
6710 return cursor_go_to_next_page (view);
6711
6712 do {
6713 view->cursor_offset++;
6714 } while (view->cursor_offset <= n_attrs && !log_attrs[view->cursor_offset].is_cursor_position);
6715
6716 return TRUE;
6717 }
6718
6719 static gboolean
cursor_backward_word_start(EvView * view)6720 cursor_backward_word_start (EvView *view)
6721 {
6722 PangoLogAttr *log_attrs = NULL;
6723 gulong n_attrs;
6724 gint i, j;
6725
6726 if (!view->page_cache)
6727 return FALSE;
6728
6729 ev_page_cache_get_text_log_attrs (view->page_cache, view->cursor_page, &log_attrs, &n_attrs);
6730 if (!log_attrs)
6731 return FALSE;
6732
6733 /* Skip current word starts */
6734 for (i = view->cursor_offset; i >= 0 && log_attrs[i].is_word_start; i--);
6735 if (i <= 0) {
6736 if (cursor_go_to_previous_page (view))
6737 return cursor_backward_word_start (view);
6738 return FALSE;
6739 }
6740
6741 /* Move to the beginning of the word */
6742 for (j = i; j >= 0 && !log_attrs[j].is_word_start; j--);
6743 view->cursor_offset = MAX (0, j);
6744
6745 return TRUE;
6746 }
6747
6748 static gboolean
cursor_forward_word_end(EvView * view)6749 cursor_forward_word_end (EvView *view)
6750 {
6751 PangoLogAttr *log_attrs = NULL;
6752 gulong n_attrs;
6753 gint i, j;
6754
6755 if (!view->page_cache)
6756 return FALSE;
6757
6758 ev_page_cache_get_text_log_attrs (view->page_cache, view->cursor_page, &log_attrs, &n_attrs);
6759 if (!log_attrs)
6760 return FALSE;
6761
6762 /* Skip current word ends */
6763 for (i = view->cursor_offset; i < n_attrs && log_attrs[i].is_word_end; i++);
6764 if (i >= n_attrs) {
6765 if (cursor_go_to_next_page (view))
6766 return cursor_forward_word_end (view);
6767 return FALSE;
6768 }
6769
6770 /* Move to the end of the word. */
6771 for (j = i; j < n_attrs && !log_attrs[j].is_word_end; j++);
6772 view->cursor_offset = MIN (j, n_attrs);
6773
6774 return TRUE;
6775 }
6776
6777 static gboolean
cursor_go_to_line_start(EvView * view)6778 cursor_go_to_line_start (EvView *view)
6779 {
6780 PangoLogAttr *log_attrs = NULL;
6781 gulong n_attrs;
6782 gint i;
6783
6784 if (!view->page_cache)
6785 return FALSE;
6786
6787 ev_page_cache_get_text_log_attrs (view->page_cache, view->cursor_page, &log_attrs, &n_attrs);
6788 if (!log_attrs)
6789 return FALSE;
6790
6791 for (i = view->cursor_offset; i >= 0 && !log_attrs[i].is_mandatory_break; i--);
6792 view->cursor_offset = MAX (0, i);
6793
6794 return TRUE;
6795 }
6796
6797 static gboolean
cursor_backward_line(EvView * view)6798 cursor_backward_line (EvView *view)
6799 {
6800 PangoLogAttr *log_attrs = NULL;
6801 gulong n_attrs;
6802
6803 if (!cursor_go_to_line_start (view))
6804 return FALSE;
6805
6806 if (view->cursor_offset == 0)
6807 return cursor_go_to_previous_page (view);
6808
6809 ev_page_cache_get_text_log_attrs (view->page_cache, view->cursor_page, &log_attrs, &n_attrs);
6810
6811 do {
6812 view->cursor_offset--;
6813 } while (view->cursor_offset >= 0 && !log_attrs[view->cursor_offset].is_mandatory_break);
6814 view->cursor_offset = MAX (0, view->cursor_offset);
6815
6816 return TRUE;
6817 }
6818
6819 static gboolean
cursor_go_to_line_end(EvView * view)6820 cursor_go_to_line_end (EvView *view)
6821 {
6822 PangoLogAttr *log_attrs = NULL;
6823 gulong n_attrs;
6824 gint i;
6825
6826 if (!view->page_cache)
6827 return FALSE;
6828
6829 ev_page_cache_get_text_log_attrs (view->page_cache, view->cursor_page, &log_attrs, &n_attrs);
6830 if (!log_attrs)
6831 return FALSE;
6832
6833 for (i = view->cursor_offset + 1; i <= n_attrs && !log_attrs[i].is_mandatory_break; i++);
6834 view->cursor_offset = MIN (i, n_attrs);
6835
6836 if (view->cursor_offset == n_attrs)
6837 return TRUE;
6838
6839 do {
6840 view->cursor_offset--;
6841 } while (view->cursor_offset >= 0 && !log_attrs[view->cursor_offset].is_cursor_position);
6842
6843 return TRUE;
6844 }
6845
6846 static gboolean
cursor_forward_line(EvView * view)6847 cursor_forward_line (EvView *view)
6848 {
6849 PangoLogAttr *log_attrs = NULL;
6850 gulong n_attrs;
6851
6852 if (!cursor_go_to_line_end (view))
6853 return FALSE;
6854
6855 ev_page_cache_get_text_log_attrs (view->page_cache, view->cursor_page, &log_attrs, &n_attrs);
6856
6857 if (view->cursor_offset == n_attrs)
6858 return cursor_go_to_next_page (view);
6859
6860 do {
6861 view->cursor_offset++;
6862 } while (view->cursor_offset <= n_attrs && !log_attrs[view->cursor_offset].is_cursor_position);
6863
6864 return TRUE;
6865 }
6866
6867 static void
extend_selection(EvView * view,GdkPoint * start_point,GdkPoint * end_point)6868 extend_selection (EvView *view,
6869 GdkPoint *start_point,
6870 GdkPoint *end_point)
6871 {
6872 if (!view->selection_info.selections) {
6873 view->selection_info.start.x = start_point->x;
6874 view->selection_info.start.y = start_point->y;
6875 }
6876
6877 compute_selections (view,
6878 EV_SELECTION_STYLE_GLYPH,
6879 &(view->selection_info.start),
6880 end_point);
6881 }
6882
6883 static gboolean
cursor_clear_selection(EvView * view,gboolean forward)6884 cursor_clear_selection (EvView *view,
6885 gboolean forward)
6886 {
6887 GList *l;
6888 EvViewSelection *selection;
6889 cairo_rectangle_int_t rect;
6890 gint doc_x, doc_y;
6891
6892 /* When clearing the selection, move the cursor to
6893 * the limits of the selection region.
6894 */
6895 if (!view->selection_info.selections)
6896 return FALSE;
6897
6898 l = forward ? g_list_last (view->selection_info.selections) : view->selection_info.selections;
6899 selection = (EvViewSelection *)l->data;
6900 if (!selection->covered_region || cairo_region_is_empty (selection->covered_region))
6901 return FALSE;
6902
6903 cairo_region_get_rectangle (selection->covered_region,
6904 forward ? cairo_region_num_rectangles (selection->covered_region) - 1 : 0,
6905 &rect);
6906
6907 if (!get_doc_point_from_offset (view, selection->page,
6908 forward ? rect.x + rect.width : rect.x,
6909 rect.y + (rect.height / 2), &doc_x, &doc_y))
6910 return FALSE;
6911
6912 position_caret_cursor_at_doc_point (view, selection->page, doc_x, doc_y);
6913 return TRUE;
6914 }
6915
6916 static gboolean
ev_view_move_cursor(EvView * view,GtkMovementStep step,gint count,gboolean extend_selections)6917 ev_view_move_cursor (EvView *view,
6918 GtkMovementStep step,
6919 gint count,
6920 gboolean extend_selections)
6921 {
6922 GdkRectangle rect;
6923 GdkRectangle prev_rect;
6924 gint prev_offset;
6925 gint prev_page;
6926 cairo_region_t *damage_region;
6927 gboolean changed_page;
6928 gboolean clear_selections = FALSE;
6929 const gboolean forward = count >= 0;
6930
6931 if (!view->caret_enabled || view->rotation != 0)
6932 return FALSE;
6933
6934 view->key_binding_handled = TRUE;
6935 view->cursor_blink_time = 0;
6936
6937 prev_offset = view->cursor_offset;
6938 prev_page = view->cursor_page;
6939
6940 clear_selections = !extend_selections && view->selection_info.selections != NULL;
6941
6942 switch (step) {
6943 case GTK_MOVEMENT_VISUAL_POSITIONS:
6944 if (!clear_selections || !cursor_clear_selection (view, count > 0)) {
6945 while (count > 0) {
6946 cursor_forward_char (view);
6947 count--;
6948 }
6949 while (count < 0) {
6950 cursor_backward_char (view);
6951 count++;
6952 }
6953 }
6954 break;
6955 case GTK_MOVEMENT_WORDS:
6956 while (count > 0) {
6957 cursor_forward_word_end (view);
6958 count--;
6959 }
6960 while (count < 0) {
6961 cursor_backward_word_start (view);
6962 count++;
6963 }
6964 break;
6965 case GTK_MOVEMENT_DISPLAY_LINES:
6966 while (count > 0) {
6967 cursor_forward_line (view);
6968 count--;
6969 }
6970 while (count < 0) {
6971 cursor_backward_line (view);
6972 count++;
6973 }
6974 break;
6975 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
6976 if (count > 0)
6977 cursor_go_to_line_end (view);
6978 else if (count < 0)
6979 cursor_go_to_line_start (view);
6980 break;
6981 case GTK_MOVEMENT_BUFFER_ENDS:
6982 if (count > 0)
6983 cursor_go_to_document_end (view);
6984 else if (count < 0)
6985 cursor_go_to_document_start (view);
6986 break;
6987 default:
6988 g_assert_not_reached ();
6989 }
6990
6991 ev_view_pend_cursor_blink (view);
6992
6993 /* Notify the user that it was not possible to move the caret cursor */
6994 if (!clear_selections &&
6995 prev_offset == view->cursor_offset && prev_page == view->cursor_page) {
6996 gtk_widget_error_bell (GTK_WIDGET (view));
6997 return TRUE;
6998 }
6999
7000 /* Scroll to make the caret visible */
7001 if (!get_caret_cursor_area (view, view->cursor_page, view->cursor_offset, &rect))
7002 return TRUE;
7003
7004 if (!view->continuous) {
7005 changed_page = FALSE;
7006 if (prev_page < view->cursor_page) {
7007 ev_view_next_page (view);
7008 cursor_go_to_page_start (view);
7009 changed_page = TRUE;
7010 } else if (prev_page > view->cursor_page) {
7011 ev_view_previous_page (view);
7012 cursor_go_to_page_end (view);
7013 _ev_view_ensure_rectangle_is_visible (view, &rect);
7014 changed_page = TRUE;
7015 }
7016
7017 if (changed_page) {
7018 rect.x += view->scroll_x;
7019 rect.y += view->scroll_y;
7020 _ev_view_ensure_rectangle_is_visible (view, &rect);
7021 g_signal_emit (view, signals[SIGNAL_CURSOR_MOVED], 0, view->cursor_page, view->cursor_offset);
7022 clear_selection (view);
7023 return TRUE;
7024 }
7025 }
7026
7027 if (step == GTK_MOVEMENT_DISPLAY_LINES) {
7028 const gint prev_cursor_offset = view->cursor_offset;
7029
7030 position_caret_cursor_at_location (view,
7031 MAX (rect.x, view->cursor_line_offset),
7032 rect.y + (rect.height / 2));
7033 /* Make sure we didn't move the cursor in the wrong direction
7034 * in case the visual order isn't the same as the logical one,
7035 * in order to avoid cursor movement loops */
7036 if ((forward && prev_cursor_offset > view->cursor_offset) ||
7037 (!forward && prev_cursor_offset < view->cursor_offset)) {
7038 view->cursor_offset = prev_cursor_offset;
7039 }
7040 if (!clear_selections &&
7041 prev_offset == view->cursor_offset && prev_page == view->cursor_page) {
7042 gtk_widget_error_bell (GTK_WIDGET (view));
7043 return TRUE;
7044 }
7045
7046 if (!get_caret_cursor_area (view, view->cursor_page, view->cursor_offset, &rect))
7047 return TRUE;
7048 } else {
7049 view->cursor_line_offset = rect.x;
7050 }
7051
7052 damage_region = cairo_region_create_rectangle (&rect);
7053 if (get_caret_cursor_area (view, prev_page, prev_offset, &prev_rect))
7054 cairo_region_union_rectangle (damage_region, &prev_rect);
7055
7056 rect.x += view->scroll_x;
7057 rect.y += view->scroll_y;
7058
7059 ev_document_model_set_page (view->model, view->cursor_page);
7060 _ev_view_ensure_rectangle_is_visible (view, &rect);
7061
7062 g_signal_emit (view, signals[SIGNAL_CURSOR_MOVED], 0, view->cursor_page, view->cursor_offset);
7063
7064 gdk_window_invalidate_region (gtk_widget_get_window (GTK_WIDGET (view)),
7065 damage_region, TRUE);
7066 cairo_region_destroy (damage_region);
7067
7068 /* Select text */
7069 if (extend_selections && EV_IS_SELECTION (view->document)) {
7070 GdkPoint start_point, end_point;
7071
7072 start_point.x = prev_rect.x + view->scroll_x;
7073 start_point.y = prev_rect.y + (prev_rect.height / 2) + view->scroll_y;
7074
7075 end_point.x = rect.x;
7076 end_point.y = rect.y + rect.height / 2;
7077
7078 extend_selection (view, &start_point, &end_point);
7079 } else if (clear_selections)
7080 clear_selection (view);
7081
7082 return TRUE;
7083 }
7084
7085 static gboolean
ev_view_key_press_event(GtkWidget * widget,GdkEventKey * event)7086 ev_view_key_press_event (GtkWidget *widget,
7087 GdkEventKey *event)
7088 {
7089 EvView *view = EV_VIEW (widget);
7090 gboolean retval;
7091
7092 ev_view_link_preview_popover_cleanup (view);
7093
7094 if (!view->document)
7095 return FALSE;
7096
7097 if (!gtk_widget_has_focus (widget))
7098 return ev_view_forward_key_event_to_focused_child (view, event);
7099
7100 /* I expected GTK+ do this for me, but it doesn't cancel
7101 * the propagation of bindings handled for the same binding set
7102 */
7103 view->key_binding_handled = FALSE;
7104 retval = gtk_bindings_activate_event (G_OBJECT (widget), event);
7105 view->key_binding_handled = FALSE;
7106
7107 return retval;
7108 }
7109
7110 static gboolean
ev_view_activate_form_field(EvView * view,EvFormField * field)7111 ev_view_activate_form_field (EvView *view,
7112 EvFormField *field)
7113 {
7114 gboolean handled = FALSE;
7115
7116 if (field->is_read_only)
7117 return handled;
7118
7119 if (field->activation_link) {
7120 ev_view_handle_link (view, field->activation_link);
7121 handled = TRUE;
7122 }
7123
7124 if (EV_IS_FORM_FIELD_BUTTON (field)) {
7125 ev_view_form_field_button_toggle (view, field);
7126 handled = TRUE;
7127 }
7128
7129 return handled;
7130 }
7131
7132 static gboolean
current_event_is_space_key_press(void)7133 current_event_is_space_key_press (void)
7134 {
7135 GdkEvent *current_event;
7136 guint keyval;
7137 gboolean is_space_key_press;
7138
7139 current_event = gtk_get_current_event ();
7140 if (!current_event)
7141 return FALSE;
7142
7143 is_space_key_press = current_event->type == GDK_KEY_PRESS &&
7144 gdk_event_get_keyval (current_event, &keyval) &&
7145 (keyval == GDK_KEY_space || keyval == GDK_KEY_KP_Space);
7146 gdk_event_free (current_event);
7147
7148 return is_space_key_press;
7149 }
7150
7151 static gboolean
ev_view_activate_link(EvView * view,EvLink * link)7152 ev_view_activate_link (EvView *view,
7153 EvLink *link)
7154 {
7155 /* Most of the GtkWidgets emit activate on both Space and Return key press,
7156 * but we don't want to activate links on Space for consistency with the Web.
7157 */
7158 if (current_event_is_space_key_press ())
7159 return FALSE;
7160
7161 ev_view_handle_link (view, link);
7162
7163 return TRUE;
7164 }
7165
7166 static void
ev_view_activate(EvView * view)7167 ev_view_activate (EvView *view)
7168 {
7169 if (!view->focused_element)
7170 return;
7171
7172 if (EV_IS_DOCUMENT_FORMS (view->document) &&
7173 EV_IS_FORM_FIELD (view->focused_element->data)) {
7174 view->key_binding_handled = ev_view_activate_form_field (view, EV_FORM_FIELD (view->focused_element->data));
7175 return;
7176 }
7177
7178 if (EV_IS_DOCUMENT_LINKS (view->document) &&
7179 EV_IS_LINK (view->focused_element->data)) {
7180 view->key_binding_handled = ev_view_activate_link (view, EV_LINK (view->focused_element->data));
7181 return;
7182 }
7183 }
7184
7185 static gboolean
ev_view_autoscroll_cb(EvView * view)7186 ev_view_autoscroll_cb (EvView *view)
7187 {
7188 gdouble speed, value;
7189
7190 /* If the user stops autoscrolling, autoscrolling will be
7191 * set to false but the timeout will continue; stop the timeout: */
7192 if (!view->scroll_info.autoscrolling) {
7193 view->scroll_info.timeout_id = 0;
7194 return FALSE;
7195 }
7196
7197 /* Replace 100 with your speed of choice: The lower the faster.
7198 * Replace 3 with another speed of choice: The higher, the faster it accelerated
7199 * based on the distance of the starting point from the mouse
7200 * (All also effected by the timeout interval of this callback) */
7201
7202 if (view->scroll_info.start_y > view->scroll_info.last_y)
7203 speed = -pow ((((gdouble)view->scroll_info.start_y - view->scroll_info.last_y) / 100), 3);
7204 else
7205 speed = pow ((((gdouble)view->scroll_info.last_y - view->scroll_info.start_y) / 100), 3);
7206
7207 value = gtk_adjustment_get_value (view->vadjustment);
7208 value = CLAMP (value + speed, 0,
7209 gtk_adjustment_get_upper (view->vadjustment) -
7210 gtk_adjustment_get_page_size (view->vadjustment));
7211 gtk_adjustment_set_value (view->vadjustment, value);
7212
7213 return TRUE;
7214
7215 }
7216
7217 static void
ev_view_autoscroll_resume(EvView * view)7218 ev_view_autoscroll_resume (EvView *view)
7219 {
7220 if (!view->scroll_info.autoscrolling)
7221 return;
7222
7223 if (view->scroll_info.timeout_id > 0)
7224 return;
7225
7226 view->scroll_info.timeout_id =
7227 g_timeout_add (20, (GSourceFunc)ev_view_autoscroll_cb,
7228 view);
7229 }
7230
7231 static void
ev_view_autoscroll_pause(EvView * view)7232 ev_view_autoscroll_pause (EvView *view)
7233 {
7234 if (!view->scroll_info.autoscrolling)
7235 return;
7236
7237 if (view->scroll_info.timeout_id == 0)
7238 return;
7239
7240 g_source_remove (view->scroll_info.timeout_id);
7241 view->scroll_info.timeout_id = 0;
7242 }
7243
7244 static gint
ev_view_focus_in(GtkWidget * widget,GdkEventFocus * event)7245 ev_view_focus_in (GtkWidget *widget,
7246 GdkEventFocus *event)
7247 {
7248 EvView *view = EV_VIEW (widget);
7249
7250 if (view->pixbuf_cache)
7251 ev_pixbuf_cache_style_changed (view->pixbuf_cache);
7252
7253 ev_view_autoscroll_resume (view);
7254
7255 ev_view_check_cursor_blink (view);
7256 gtk_widget_queue_draw (widget);
7257
7258 return FALSE;
7259 }
7260
7261 static gint
ev_view_focus_out(GtkWidget * widget,GdkEventFocus * event)7262 ev_view_focus_out (GtkWidget *widget,
7263 GdkEventFocus *event)
7264 {
7265 EvView *view = EV_VIEW (widget);
7266
7267 if (view->pixbuf_cache)
7268 ev_pixbuf_cache_style_changed (view->pixbuf_cache);
7269
7270 ev_view_autoscroll_pause (view);
7271
7272 ev_view_check_cursor_blink (view);
7273 gtk_widget_queue_draw (widget);
7274
7275 return FALSE;
7276 }
7277
7278 static gboolean
ev_view_leave_notify_event(GtkWidget * widget,GdkEventCrossing * event)7279 ev_view_leave_notify_event (GtkWidget *widget, GdkEventCrossing *event)
7280 {
7281 EvView *view = EV_VIEW (widget);
7282
7283 if (view->cursor != EV_VIEW_CURSOR_NORMAL)
7284 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
7285
7286 return FALSE;
7287 }
7288
7289 static gboolean
ev_view_enter_notify_event(GtkWidget * widget,GdkEventCrossing * event)7290 ev_view_enter_notify_event (GtkWidget *widget, GdkEventCrossing *event)
7291 {
7292 EvView *view = EV_VIEW (widget);
7293
7294 ev_view_handle_cursor_over_xy (view, event->x, event->y);
7295
7296 return FALSE;
7297 }
7298
7299 static void
ev_view_style_updated(GtkWidget * widget)7300 ev_view_style_updated (GtkWidget *widget)
7301 {
7302 if (EV_VIEW (widget)->pixbuf_cache)
7303 ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
7304
7305 GTK_WIDGET_CLASS (ev_view_parent_class)->style_updated (widget);
7306 }
7307
7308 /*** Drawing ***/
7309
7310 static void
draw_rubberband(EvView * view,cairo_t * cr,const GdkRectangle * rect,gboolean active)7311 draw_rubberband (EvView *view,
7312 cairo_t *cr,
7313 const GdkRectangle *rect,
7314 gboolean active)
7315 {
7316 GtkStyleContext *context;
7317
7318 context = gtk_widget_get_style_context (GTK_WIDGET (view));
7319 gtk_style_context_save (context);
7320 gtk_style_context_add_class (context, EV_STYLE_CLASS_FIND_RESULTS);
7321 if (active)
7322 gtk_style_context_set_state (context, GTK_STATE_FLAG_ACTIVE);
7323 else
7324 gtk_style_context_set_state (context, GTK_STATE_FLAG_SELECTED);
7325
7326 gtk_render_background (context, cr,
7327 rect->x - view->scroll_x,
7328 rect->y - view->scroll_y,
7329 rect->width, rect->height);
7330 gtk_style_context_restore (context);
7331 }
7332
7333
7334 static void
highlight_find_results(EvView * view,cairo_t * cr,int page)7335 highlight_find_results (EvView *view,
7336 cairo_t *cr,
7337 int page)
7338 {
7339 gint i, n_results = 0;
7340
7341 n_results = ev_view_find_get_n_results (view, page);
7342
7343 for (i = 0; i < n_results; i++) {
7344 EvRectangle *rectangle;
7345 GdkRectangle view_rectangle;
7346 gboolean active;
7347
7348 active = i == view->find_result && page == view->find_page;
7349
7350 rectangle = ev_view_find_get_result (view, page, i);
7351 _ev_view_transform_doc_rect_to_view_rect (view, page, rectangle, &view_rectangle);
7352 draw_rubberband (view, cr, &view_rectangle, active);
7353 }
7354 }
7355
7356 static void
highlight_forward_search_results(EvView * view,cairo_t * cr,int page)7357 highlight_forward_search_results (EvView *view,
7358 cairo_t *cr,
7359 int page)
7360 {
7361 GdkRectangle rect;
7362 EvMapping *mapping = view->synctex_result;
7363
7364 if (GPOINTER_TO_INT (mapping->data) != page)
7365 return;
7366
7367 _ev_view_transform_doc_rect_to_view_rect (view, page, &mapping->area, &rect);
7368
7369 cairo_save (cr);
7370 cairo_set_source_rgb (cr, 1., 0., 0.);
7371 cairo_rectangle (cr,
7372 rect.x - view->scroll_x,
7373 rect.y - view->scroll_y,
7374 rect.width, rect.height);
7375 cairo_stroke (cr);
7376 cairo_restore (cr);
7377 }
7378
7379 static void
draw_surface(cairo_t * cr,cairo_surface_t * surface,gint x,gint y,gint offset_x,gint offset_y,gint target_width,gint target_height)7380 draw_surface (cairo_t *cr,
7381 cairo_surface_t *surface,
7382 gint x,
7383 gint y,
7384 gint offset_x,
7385 gint offset_y,
7386 gint target_width,
7387 gint target_height)
7388 {
7389 gdouble width, height;
7390 gdouble device_scale_x = 1, device_scale_y = 1;
7391
7392 #ifdef HAVE_HIDPI_SUPPORT
7393 cairo_surface_get_device_scale (surface, &device_scale_x, &device_scale_y);
7394 #endif
7395 width = cairo_image_surface_get_width (surface) / device_scale_x;
7396 height = cairo_image_surface_get_height (surface) / device_scale_y;
7397
7398 cairo_save (cr);
7399 cairo_translate (cr, x, y);
7400
7401 if (width != target_width || height != target_height) {
7402 gdouble scale_x, scale_y;
7403
7404 scale_x = (gdouble)target_width / width;
7405 scale_y = (gdouble)target_height / height;
7406 cairo_pattern_set_filter (cairo_get_source (cr),
7407 CAIRO_FILTER_NEAREST);
7408 cairo_scale (cr, scale_x, scale_y);
7409
7410 offset_x /= scale_x;
7411 offset_y /= scale_y;
7412 }
7413
7414 cairo_set_source_surface (cr, surface, -offset_x, -offset_y);
7415 cairo_paint (cr);
7416 cairo_restore (cr);
7417 }
7418
7419 void
_ev_view_get_selection_colors(EvView * view,GdkRGBA * bg_color,GdkRGBA * fg_color)7420 _ev_view_get_selection_colors (EvView *view,
7421 GdkRGBA *bg_color,
7422 GdkRGBA *fg_color)
7423 {
7424 GtkWidget *widget = GTK_WIDGET (view);
7425 GtkStateFlags state;
7426 GtkStyleContext *context;
7427
7428 context = gtk_widget_get_style_context (widget);
7429 gtk_style_context_save (context);
7430 gtk_style_context_add_class (context, EV_STYLE_CLASS_FIND_RESULTS);
7431 state = gtk_style_context_get_state (context) |
7432 (gtk_widget_has_focus (widget) ? GTK_STATE_FLAG_SELECTED : GTK_STATE_FLAG_ACTIVE);
7433 gtk_style_context_set_state (context, state);
7434
7435 if (bg_color) {
7436 g_autoptr (GdkRGBA) color = NULL;
7437 gtk_style_context_get (context, state,
7438 GTK_STYLE_PROPERTY_BACKGROUND_COLOR,
7439 &color, NULL);
7440 *bg_color = *color;
7441 }
7442
7443 if (fg_color)
7444 gtk_style_context_get_color (context, state, fg_color);
7445
7446 gtk_style_context_restore (context);
7447 }
7448
7449 static void
draw_selection_region(cairo_t * cr,cairo_region_t * region,GdkRGBA * color,gint x,gint y,gdouble scale_x,gdouble scale_y)7450 draw_selection_region (cairo_t *cr,
7451 cairo_region_t *region,
7452 GdkRGBA *color,
7453 gint x,
7454 gint y,
7455 gdouble scale_x,
7456 gdouble scale_y)
7457 {
7458 cairo_save (cr);
7459 cairo_translate (cr, x, y);
7460 cairo_scale (cr, scale_x, scale_y);
7461 gdk_cairo_region (cr, region);
7462 cairo_set_source_rgb (cr, color->red, color->green, color->blue);
7463 cairo_set_operator (cr, CAIRO_OPERATOR_MULTIPLY);
7464 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
7465 cairo_fill (cr);
7466 cairo_restore (cr);
7467 }
7468
7469 static void
draw_one_page(EvView * view,gint page,cairo_t * cr,GdkRectangle * page_area,GtkBorder * border,GdkRectangle * expose_area,gboolean * page_ready)7470 draw_one_page (EvView *view,
7471 gint page,
7472 cairo_t *cr,
7473 GdkRectangle *page_area,
7474 GtkBorder *border,
7475 GdkRectangle *expose_area,
7476 gboolean *page_ready)
7477 {
7478 GtkStyleContext *context;
7479 GdkRectangle overlap;
7480 GdkRectangle real_page_area;
7481 gint current_page;
7482
7483 g_assert (view->document);
7484
7485 if (! gdk_rectangle_intersect (page_area, expose_area, &overlap))
7486 return;
7487
7488 /* Render the document itself */
7489 real_page_area = *page_area;
7490
7491 real_page_area.x += border->left;
7492 real_page_area.y += border->top;
7493 real_page_area.width -= (border->left + border->right);
7494 real_page_area.height -= (border->top + border->bottom);
7495 *page_ready = TRUE;
7496
7497 context = gtk_widget_get_style_context (GTK_WIDGET (view));
7498 current_page = ev_document_model_get_page (view->model);
7499
7500 gtk_style_context_save (context);
7501 gtk_style_context_add_class (context, EV_STYLE_CLASS_DOCUMENT_PAGE);
7502 if (ev_document_model_get_inverted_colors (view->model))
7503 gtk_style_context_add_class (context, EV_STYLE_CLASS_INVERTED);
7504
7505 if (view->continuous && page == current_page)
7506 gtk_style_context_set_state (context, GTK_STATE_FLAG_ACTIVE);
7507
7508 gtk_render_background (context, cr, page_area->x, page_area->y, page_area->width, page_area->height);
7509 gtk_render_frame (context, cr, page_area->x, page_area->y, page_area->width, page_area->height);
7510 gtk_style_context_restore (context);
7511
7512 if (gdk_rectangle_intersect (&real_page_area, expose_area, &overlap)) {
7513 gint width, height;
7514 cairo_surface_t *page_surface = NULL;
7515 cairo_surface_t *selection_surface = NULL;
7516 gint offset_x, offset_y;
7517 cairo_region_t *region = NULL;
7518
7519 page_surface = ev_pixbuf_cache_get_surface (view->pixbuf_cache, page);
7520
7521 if (!page_surface) {
7522 if (page == current_page)
7523 ev_view_set_loading (view, TRUE);
7524
7525 *page_ready = FALSE;
7526
7527 return;
7528 }
7529
7530 if (page == current_page)
7531 ev_view_set_loading (view, FALSE);
7532
7533 ev_view_get_page_size (view, page, &width, &height);
7534 offset_x = overlap.x - real_page_area.x;
7535 offset_y = overlap.y - real_page_area.y;
7536
7537 draw_surface (cr, page_surface, overlap.x, overlap.y, offset_x, offset_y, width, height);
7538
7539 /* Get the selection pixbuf iff we have something to draw */
7540 if (!find_selection_for_page (view, page))
7541 return;
7542
7543 selection_surface = ev_pixbuf_cache_get_selection_surface (view->pixbuf_cache,
7544 page,
7545 view->scale);
7546 if (selection_surface) {
7547 draw_surface (cr, selection_surface, overlap.x, overlap.y, offset_x, offset_y,
7548 width, height);
7549 return;
7550 }
7551
7552 region = ev_pixbuf_cache_get_selection_region (view->pixbuf_cache,
7553 page,
7554 view->scale);
7555 if (region) {
7556 double scale_x, scale_y;
7557 GdkRGBA color;
7558 double device_scale_x = 1, device_scale_y = 1;
7559
7560 scale_x = (gdouble)width / cairo_image_surface_get_width (page_surface);
7561 scale_y = (gdouble)height / cairo_image_surface_get_height (page_surface);
7562
7563 #ifdef HAVE_HIDPI_SUPPORT
7564 cairo_surface_get_device_scale (page_surface, &device_scale_x, &device_scale_y);
7565 #endif
7566
7567 scale_x *= device_scale_x;
7568 scale_y *= device_scale_y;
7569
7570 _ev_view_get_selection_colors (view, &color, NULL);
7571 draw_selection_region (cr, region, &color, real_page_area.x, real_page_area.y,
7572 scale_x, scale_y);
7573 }
7574 }
7575 }
7576
7577 /*** GObject functions ***/
7578
7579 static void
ev_view_finalize(GObject * object)7580 ev_view_finalize (GObject *object)
7581 {
7582 EvView *view = EV_VIEW (object);
7583
7584 if (view->selection_info.selections) {
7585 g_list_free_full (view->selection_info.selections, (GDestroyNotify)selection_free);
7586 view->selection_info.selections = NULL;
7587 }
7588 clear_link_selected (view);
7589
7590 if (view->synctex_result) {
7591 g_free (view->synctex_result);
7592 view->synctex_result = NULL;
7593 }
7594
7595 if (view->image_dnd_info.image)
7596 g_object_unref (view->image_dnd_info.image);
7597 view->image_dnd_info.image = NULL;
7598 if (view->annot_window_map)
7599 g_hash_table_destroy (view->annot_window_map);
7600
7601 g_object_unref (view->zoom_gesture);
7602
7603 G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
7604 }
7605
7606 static void
ev_view_dispose(GObject * object)7607 ev_view_dispose (GObject *object)
7608 {
7609 EvView *view = EV_VIEW (object);
7610
7611 if (view->model) {
7612 g_signal_handlers_disconnect_by_data (view->model, view);
7613 g_object_unref (view->model);
7614 view->model = NULL;
7615 }
7616
7617 if (view->pixbuf_cache) {
7618 g_object_unref (view->pixbuf_cache);
7619 view->pixbuf_cache = NULL;
7620 }
7621
7622 if (view->document) {
7623 g_object_unref (view->document);
7624 view->document = NULL;
7625 }
7626
7627 if (view->page_cache) {
7628 g_object_unref (view->page_cache);
7629 view->page_cache = NULL;
7630 }
7631
7632 ev_view_find_cancel (view);
7633
7634 ev_view_window_children_free (view);
7635
7636 if (view->update_cursor_idle_id) {
7637 g_source_remove (view->update_cursor_idle_id);
7638 view->update_cursor_idle_id = 0;
7639 }
7640
7641 if (view->selection_scroll_id) {
7642 g_source_remove (view->selection_scroll_id);
7643 view->selection_scroll_id = 0;
7644 }
7645
7646 if (view->selection_update_id) {
7647 g_source_remove (view->selection_update_id);
7648 view->selection_update_id = 0;
7649 }
7650
7651 if (view->scroll_info.timeout_id) {
7652 g_source_remove (view->scroll_info.timeout_id);
7653 view->scroll_info.timeout_id = 0;
7654 }
7655
7656 if (view->drag_info.drag_timeout_id) {
7657 g_source_remove (view->drag_info.drag_timeout_id);
7658 view->drag_info.drag_timeout_id = 0;
7659 }
7660
7661 if (view->drag_info.release_timeout_id) {
7662 g_source_remove (view->drag_info.release_timeout_id);
7663 view->drag_info.release_timeout_id = 0;
7664 }
7665
7666 if (view->cursor_blink_timeout_id) {
7667 g_source_remove (view->cursor_blink_timeout_id);
7668 view->cursor_blink_timeout_id = 0;
7669 }
7670
7671 if (view->child_focus_idle_id) {
7672 g_source_remove (view->child_focus_idle_id);
7673 view->child_focus_idle_id = 0;
7674 }
7675
7676 if (view->link_preview.job) {
7677 ev_job_cancel (view->link_preview.job);
7678 g_object_unref (view->link_preview.job);
7679 view->link_preview.job = NULL;
7680 }
7681
7682 gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (view), NULL);
7683 gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (view), NULL);
7684
7685 g_clear_object(&view->accessible);
7686
7687 G_OBJECT_CLASS (ev_view_parent_class)->dispose (object);
7688 }
7689
7690 static void
ev_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)7691 ev_view_get_property (GObject *object,
7692 guint prop_id,
7693 GValue *value,
7694 GParamSpec *pspec)
7695 {
7696 EvView *view = EV_VIEW (object);
7697
7698 switch (prop_id) {
7699 case PROP_IS_LOADING:
7700 g_value_set_boolean (value, view->loading);
7701 break;
7702 case PROP_CAN_ZOOM_IN:
7703 g_value_set_boolean (value, view->can_zoom_in);
7704 break;
7705 case PROP_CAN_ZOOM_OUT:
7706 g_value_set_boolean (value, view->can_zoom_out);
7707 break;
7708 case PROP_HADJUSTMENT:
7709 g_value_set_object (value, view->hadjustment);
7710 break;
7711 case PROP_VADJUSTMENT:
7712 g_value_set_object (value, view->vadjustment);
7713 break;
7714 case PROP_HSCROLL_POLICY:
7715 g_value_set_enum (value, view->hscroll_policy);
7716 break;
7717 case PROP_VSCROLL_POLICY:
7718 g_value_set_enum (value, view->vscroll_policy);
7719 break;
7720 default:
7721 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
7722 break;
7723 }
7724 }
7725
7726 static void
ev_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)7727 ev_view_set_property (GObject *object,
7728 guint prop_id,
7729 const GValue *value,
7730 GParamSpec *pspec)
7731 {
7732 EvView *view = EV_VIEW (object);
7733
7734 switch (prop_id) {
7735 case PROP_IS_LOADING:
7736 ev_view_set_loading (view, g_value_get_boolean (value));
7737 break;
7738 case PROP_HADJUSTMENT:
7739 ev_view_set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL,
7740 (GtkAdjustment *) g_value_get_object (value));
7741 break;
7742 case PROP_VADJUSTMENT:
7743 ev_view_set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL,
7744 (GtkAdjustment *) g_value_get_object (value));
7745 break;
7746 case PROP_HSCROLL_POLICY:
7747 view->hscroll_policy = g_value_get_enum (value);
7748 gtk_widget_queue_resize (GTK_WIDGET (view));
7749 break;
7750 case PROP_VSCROLL_POLICY:
7751 view->vscroll_policy = g_value_get_enum (value);
7752 gtk_widget_queue_resize (GTK_WIDGET (view));
7753 break;
7754 default:
7755 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
7756 break;
7757 }
7758 }
7759
7760 /* Accessibility */
7761 static AtkObject *
ev_view_get_accessible(GtkWidget * widget)7762 ev_view_get_accessible (GtkWidget *widget)
7763 {
7764 EvView *view = EV_VIEW (widget);
7765
7766 if (!view->accessible)
7767 view->accessible = ev_view_accessible_new (widget);
7768 return view->accessible;
7769 }
7770
7771 /* GtkContainer */
7772 static void
ev_view_remove(GtkContainer * container,GtkWidget * widget)7773 ev_view_remove (GtkContainer *container,
7774 GtkWidget *widget)
7775 {
7776 EvView *view = EV_VIEW (container);
7777 GList *tmp_list = view->children;
7778 EvViewChild *child;
7779
7780 while (tmp_list) {
7781 child = tmp_list->data;
7782
7783 if (child->widget == widget) {
7784 gtk_widget_unparent (widget);
7785
7786 view->children = g_list_remove_link (view->children, tmp_list);
7787 g_list_free_1 (tmp_list);
7788 g_slice_free (EvViewChild, child);
7789
7790 return;
7791 }
7792
7793 tmp_list = tmp_list->next;
7794 }
7795 }
7796
7797 static void
ev_view_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)7798 ev_view_forall (GtkContainer *container,
7799 gboolean include_internals,
7800 GtkCallback callback,
7801 gpointer callback_data)
7802 {
7803 EvView *view = EV_VIEW (container);
7804 GList *tmp_list = view->children;
7805 EvViewChild *child;
7806
7807 while (tmp_list) {
7808 child = tmp_list->data;
7809 tmp_list = tmp_list->next;
7810
7811 (* callback) (child->widget, callback_data);
7812 }
7813 }
7814
7815 static void
view_update_scale_limits(EvView * view)7816 view_update_scale_limits (EvView *view)
7817 {
7818 gdouble min_width, min_height;
7819 gdouble width, height;
7820 gdouble max_scale;
7821 gdouble dpi;
7822 gint rotation;
7823
7824 if (!view->document)
7825 return;
7826
7827 rotation = ev_document_model_get_rotation (view->model);
7828
7829 dpi = ev_document_misc_get_widget_dpi (GTK_WIDGET (view)) / 72.0;
7830
7831 ev_document_get_min_page_size (view->document, &min_width, &min_height);
7832 width = (rotation == 0 || rotation == 180) ? min_width : min_height;
7833 height = (rotation == 0 || rotation == 180) ? min_height : min_width;
7834 max_scale = sqrt (view->pixbuf_cache_size / (width * dpi * 4 * height * dpi));
7835
7836 ev_document_model_set_min_scale (view->model, MIN_SCALE * dpi);
7837 ev_document_model_set_max_scale (view->model, max_scale * dpi);
7838 }
7839
7840 static void
ev_view_screen_changed(GtkWidget * widget,GdkScreen * old_screen)7841 ev_view_screen_changed (GtkWidget *widget,
7842 GdkScreen *old_screen)
7843 {
7844 EvView *view = EV_VIEW (widget);
7845 GdkScreen *screen;
7846
7847 screen = gtk_widget_get_screen (widget);
7848 if (screen == old_screen)
7849 return;
7850
7851 view_update_scale_limits (view);
7852
7853 if (GTK_WIDGET_CLASS (ev_view_parent_class)->screen_changed) {
7854 GTK_WIDGET_CLASS (ev_view_parent_class)->screen_changed (widget, old_screen);
7855 }
7856 }
7857
7858 static void
pan_gesture_pan_cb(GtkGesturePan * gesture,GtkPanDirection direction,gdouble offset,EvView * view)7859 pan_gesture_pan_cb (GtkGesturePan *gesture,
7860 GtkPanDirection direction,
7861 gdouble offset,
7862 EvView *view)
7863 {
7864 GtkAllocation allocation;
7865
7866 gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
7867
7868 if (view->continuous ||
7869 allocation.width < view->requisition.width) {
7870 gtk_gesture_set_state (GTK_GESTURE (gesture),
7871 GTK_EVENT_SEQUENCE_DENIED);
7872 return;
7873 }
7874
7875 #define PAN_ACTION_DISTANCE 200
7876
7877 view->pan_action = EV_PAN_ACTION_NONE;
7878 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
7879
7880 if (offset > PAN_ACTION_DISTANCE) {
7881 if (direction == GTK_PAN_DIRECTION_LEFT ||
7882 gtk_widget_get_direction (GTK_WIDGET (view)) == GTK_TEXT_DIR_RTL)
7883 view->pan_action = EV_PAN_ACTION_NEXT;
7884 else
7885 view->pan_action = EV_PAN_ACTION_PREV;
7886 }
7887 #undef PAN_ACTION_DISTANCE
7888 }
7889
7890 static void
pan_gesture_end_cb(GtkGesture * gesture,GdkEventSequence * sequence,EvView * view)7891 pan_gesture_end_cb (GtkGesture *gesture,
7892 GdkEventSequence *sequence,
7893 EvView *view)
7894 {
7895 if (!gtk_gesture_handles_sequence (gesture, sequence))
7896 return;
7897
7898 if (view->pan_action == EV_PAN_ACTION_PREV)
7899 ev_view_previous_page (view);
7900 else if (view->pan_action == EV_PAN_ACTION_NEXT)
7901 ev_view_next_page (view);
7902
7903 view->pan_action = EV_PAN_ACTION_NONE;
7904 }
7905
7906 static void
ev_view_hierarchy_changed(GtkWidget * widget,GtkWidget * previous_toplevel)7907 ev_view_hierarchy_changed (GtkWidget *widget,
7908 GtkWidget *previous_toplevel)
7909 {
7910 GtkWidget *parent = gtk_widget_get_parent (widget);
7911 EvView *view = EV_VIEW (widget);
7912
7913 if (parent && !view->pan_gesture) {
7914 view->pan_gesture =
7915 gtk_gesture_pan_new (parent, GTK_ORIENTATION_HORIZONTAL);
7916 g_signal_connect (view->pan_gesture, "pan",
7917 G_CALLBACK (pan_gesture_pan_cb), widget);
7918 g_signal_connect (view->pan_gesture, "end",
7919 G_CALLBACK (pan_gesture_end_cb), widget);
7920
7921 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (view->pan_gesture), TRUE);
7922 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (view->pan_gesture),
7923 GTK_PHASE_CAPTURE);
7924 } else if (!parent && view->pan_gesture) {
7925 g_clear_object (&view->pan_gesture);
7926 }
7927 }
7928
7929 static void
add_move_binding_keypad(GtkBindingSet * binding_set,guint keyval,GdkModifierType modifiers,GtkMovementStep step,gint count)7930 add_move_binding_keypad (GtkBindingSet *binding_set,
7931 guint keyval,
7932 GdkModifierType modifiers,
7933 GtkMovementStep step,
7934 gint count)
7935 {
7936 guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
7937
7938 gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
7939 "move-cursor", 3,
7940 GTK_TYPE_MOVEMENT_STEP, step,
7941 G_TYPE_INT, count,
7942 G_TYPE_BOOLEAN, FALSE);
7943 gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
7944 "move-cursor", 3,
7945 GTK_TYPE_MOVEMENT_STEP, step,
7946 G_TYPE_INT, count,
7947 G_TYPE_BOOLEAN, FALSE);
7948
7949 /* Selection-extending version */
7950 gtk_binding_entry_add_signal (binding_set, keyval, modifiers | GDK_SHIFT_MASK,
7951 "move-cursor", 3,
7952 GTK_TYPE_MOVEMENT_STEP, step,
7953 G_TYPE_INT, count,
7954 G_TYPE_BOOLEAN, TRUE);
7955 gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers | GDK_SHIFT_MASK,
7956 "move-cursor", 3,
7957 GTK_TYPE_MOVEMENT_STEP, step,
7958 G_TYPE_INT, count,
7959 G_TYPE_BOOLEAN, TRUE);
7960 }
7961
7962 static gint
ev_view_mapping_compare(const EvMapping * a,const EvMapping * b,gpointer user_data)7963 ev_view_mapping_compare (const EvMapping *a,
7964 const EvMapping *b,
7965 gpointer user_data)
7966 {
7967 GtkTextDirection text_direction = GPOINTER_TO_INT (user_data);
7968 gint y1 = a->area.y1 + (a->area.y2 - a->area.y1) / 2;
7969 gint y2 = b->area.y1 + (b->area.y2 - b->area.y1) / 2;
7970
7971 if (y1 == y2) {
7972 gint x1 = a->area.x1 + (a->area.x2 - a->area.x1) / 2;
7973 gint x2 = b->area.x1 + (b->area.x2 - b->area.x1) / 2;
7974
7975 if (text_direction == GTK_TEXT_DIR_RTL)
7976 return (x1 < x2) ? 1 : ((x1 == x2) ? 0 : -1);
7977
7978 return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
7979 }
7980
7981 return (y1 < y2) ? -1 : 1;
7982 }
7983
7984 static GList *
ev_view_get_sorted_mapping_list(EvView * view,GtkDirectionType direction,gint page)7985 ev_view_get_sorted_mapping_list (EvView *view,
7986 GtkDirectionType direction,
7987 gint page)
7988 {
7989 GList *mapping_list = NULL, *l;
7990 EvMappingList *forms_mapping;
7991
7992 forms_mapping = ev_page_cache_get_form_field_mapping (view->page_cache, page);
7993
7994 for (l = ev_mapping_list_get_list (forms_mapping); l; l = g_list_next (l)) {
7995 EvMapping *mapping = (EvMapping *)l->data;
7996 EvFormField *field = (EvFormField *)mapping->data;
7997
7998 if (field->is_read_only || EV_IS_FORM_FIELD_SIGNATURE (field))
7999 continue;
8000
8001 mapping_list = g_list_prepend (mapping_list, mapping);
8002 }
8003
8004 if (!mapping_list)
8005 return NULL;
8006
8007 mapping_list = g_list_sort_with_data (g_list_reverse (mapping_list),
8008 (GCompareDataFunc)ev_view_mapping_compare,
8009 GINT_TO_POINTER (gtk_widget_get_direction (GTK_WIDGET (view))));
8010
8011 if (direction == GTK_DIR_TAB_BACKWARD)
8012 mapping_list = g_list_reverse (mapping_list);
8013 return mapping_list;
8014 }
8015
8016 static gboolean
child_focus_forward_idle_cb(gpointer user_data)8017 child_focus_forward_idle_cb (gpointer user_data)
8018 {
8019 EvView *view = EV_VIEW (user_data);
8020
8021 view->child_focus_idle_id = 0;
8022 gtk_widget_child_focus (GTK_WIDGET (view), GTK_DIR_TAB_FORWARD);
8023
8024 return G_SOURCE_REMOVE;
8025 }
8026
8027 static gboolean
child_focus_backward_idle_cb(gpointer user_data)8028 child_focus_backward_idle_cb (gpointer user_data)
8029 {
8030 EvView *view = EV_VIEW (user_data);
8031
8032 view->child_focus_idle_id = 0;
8033 gtk_widget_child_focus (GTK_WIDGET (view), GTK_DIR_TAB_BACKWARD);
8034
8035 return G_SOURCE_REMOVE;
8036 }
8037
8038 static void
schedule_child_focus_in_idle(EvView * view,GtkDirectionType direction)8039 schedule_child_focus_in_idle (EvView *view,
8040 GtkDirectionType direction)
8041 {
8042 if (view->child_focus_idle_id)
8043 g_source_remove (view->child_focus_idle_id);
8044 view->child_focus_idle_id =
8045 g_idle_add (direction == GTK_DIR_TAB_FORWARD ? child_focus_forward_idle_cb : child_focus_backward_idle_cb,
8046 view);
8047 }
8048
8049 static gboolean
ev_view_focus_next(EvView * view,GtkDirectionType direction)8050 ev_view_focus_next (EvView *view,
8051 GtkDirectionType direction)
8052 {
8053 EvMapping *focus_element;
8054 GList *elements;
8055 gboolean had_focused_element;
8056
8057 if (view->focused_element) {
8058 GList *l;
8059
8060 elements = ev_view_get_sorted_mapping_list (view, direction, view->focused_element_page);
8061 l = g_list_find (elements, view->focused_element);
8062 l = g_list_next (l);
8063 focus_element = l ? l->data : NULL;
8064 had_focused_element = TRUE;
8065 } else {
8066 elements = ev_view_get_sorted_mapping_list (view, direction, view->current_page);
8067 focus_element = elements ? elements->data : NULL;
8068 had_focused_element = FALSE;
8069 }
8070
8071 g_list_free (elements);
8072
8073 if (focus_element) {
8074 ev_view_remove_all_form_fields (view);
8075 _ev_view_focus_form_field (view, EV_FORM_FIELD (focus_element->data));
8076
8077 return TRUE;
8078 }
8079
8080 ev_view_remove_all_form_fields (view);
8081 _ev_view_set_focused_element (view, NULL, -1);
8082
8083 /* Only try to move the focus to next/previous pages when the current page had
8084 * a focused element. This prevents the view from jumping to the first/last page
8085 * when there are not focusable elements.
8086 */
8087 if (!had_focused_element)
8088 return FALSE;
8089
8090 /* FIXME: this doesn't work if the next/previous page doesn't have form fields */
8091 if (direction == GTK_DIR_TAB_FORWARD) {
8092 if (ev_view_next_page (view)) {
8093 schedule_child_focus_in_idle (view, direction);
8094 return TRUE;
8095 }
8096 } else if (direction == GTK_DIR_TAB_BACKWARD) {
8097 if (ev_view_previous_page (view)) {
8098 schedule_child_focus_in_idle (view, direction);
8099 return TRUE;
8100 }
8101 }
8102
8103 return FALSE;
8104 }
8105
8106 static gboolean
ev_view_focus(GtkWidget * widget,GtkDirectionType direction)8107 ev_view_focus (GtkWidget *widget,
8108 GtkDirectionType direction)
8109 {
8110 EvView *view = EV_VIEW (widget);
8111
8112 if (view->document) {
8113 if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_TAB_BACKWARD)
8114 return ev_view_focus_next (view, direction);
8115 }
8116
8117 return GTK_WIDGET_CLASS (ev_view_parent_class)->focus (widget, direction);
8118 }
8119
8120 static void
ev_view_parent_set(GtkWidget * widget,GtkWidget * previous_parent)8121 ev_view_parent_set (GtkWidget *widget,
8122 GtkWidget *previous_parent)
8123 {
8124 GtkWidget *parent;
8125
8126 parent = gtk_widget_get_parent (widget);
8127 g_assert (!parent || GTK_IS_SCROLLED_WINDOW (parent));
8128 }
8129
8130 static void
ev_view_class_init(EvViewClass * class)8131 ev_view_class_init (EvViewClass *class)
8132 {
8133 GObjectClass *object_class = G_OBJECT_CLASS (class);
8134 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
8135 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
8136 GtkBindingSet *binding_set;
8137
8138 object_class->get_property = ev_view_get_property;
8139 object_class->set_property = ev_view_set_property;
8140 object_class->dispose = ev_view_dispose;
8141 object_class->finalize = ev_view_finalize;
8142
8143 widget_class->realize = ev_view_realize;
8144 widget_class->draw = ev_view_draw;
8145 widget_class->button_press_event = ev_view_button_press_event;
8146 widget_class->motion_notify_event = ev_view_motion_notify_event;
8147 widget_class->button_release_event = ev_view_button_release_event;
8148 widget_class->key_press_event = ev_view_key_press_event;
8149 widget_class->focus_in_event = ev_view_focus_in;
8150 widget_class->focus_out_event = ev_view_focus_out;
8151 widget_class->get_accessible = ev_view_get_accessible;
8152 widget_class->get_preferred_width = ev_view_get_preferred_width;
8153 widget_class->get_preferred_height = ev_view_get_preferred_height;
8154 widget_class->size_allocate = ev_view_size_allocate;
8155 widget_class->scroll_event = ev_view_scroll_event;
8156 widget_class->enter_notify_event = ev_view_enter_notify_event;
8157 widget_class->leave_notify_event = ev_view_leave_notify_event;
8158 widget_class->style_updated = ev_view_style_updated;
8159 widget_class->drag_data_get = ev_view_drag_data_get;
8160 widget_class->drag_motion = ev_view_drag_motion;
8161 widget_class->popup_menu = ev_view_popup_menu;
8162 widget_class->query_tooltip = ev_view_query_tooltip;
8163 widget_class->screen_changed = ev_view_screen_changed;
8164 widget_class->focus = ev_view_focus;
8165 widget_class->parent_set = ev_view_parent_set;
8166 widget_class->hierarchy_changed = ev_view_hierarchy_changed;
8167
8168 #if GTK_CHECK_VERSION(3, 20, 0)
8169 gtk_widget_class_set_css_name (widget_class, "evview");
8170 #endif
8171
8172 container_class->remove = ev_view_remove;
8173 container_class->forall = ev_view_forall;
8174
8175 class->scroll = ev_view_scroll_internal;
8176 class->move_cursor = ev_view_move_cursor;
8177 class->activate = ev_view_activate;
8178
8179 /**
8180 * EvView:is-loading:
8181 *
8182 * Allows to implement a custom notification system.
8183 *
8184 * Since: 3.8
8185 */
8186 g_object_class_install_property (object_class,
8187 PROP_IS_LOADING,
8188 g_param_spec_boolean ("is-loading",
8189 "Is Loading",
8190 "Whether the view is loading",
8191 FALSE,
8192 G_PARAM_READABLE |
8193 G_PARAM_STATIC_STRINGS));
8194 /**
8195 * EvView:can-zoom-in:
8196 *
8197 * Since: 3.8
8198 */
8199 g_object_class_install_property (object_class,
8200 PROP_CAN_ZOOM_IN,
8201 g_param_spec_boolean ("can-zoom-in",
8202 "Can Zoom In",
8203 "Whether the view can be zoomed in further",
8204 TRUE,
8205 G_PARAM_READABLE |
8206 G_PARAM_STATIC_STRINGS));
8207 /**
8208 * EvView:can-zoom-out:
8209 *
8210 * Since: 3.8
8211 */
8212 g_object_class_install_property (object_class,
8213 PROP_CAN_ZOOM_OUT,
8214 g_param_spec_boolean ("can-zoom-out",
8215 "Can Zoom Out",
8216 "Whether the view can be zoomed out further",
8217 TRUE,
8218 G_PARAM_READABLE |
8219 G_PARAM_STATIC_STRINGS));
8220
8221 /* Scrollable interface */
8222 g_object_class_override_property (object_class, PROP_HADJUSTMENT, "hadjustment");
8223 g_object_class_override_property (object_class, PROP_VADJUSTMENT, "vadjustment");
8224 g_object_class_override_property (object_class, PROP_HSCROLL_POLICY, "hscroll-policy");
8225 g_object_class_override_property (object_class, PROP_VSCROLL_POLICY, "vscroll-policy");
8226
8227 signals[SIGNAL_SCROLL] = g_signal_new ("scroll",
8228 G_TYPE_FROM_CLASS (object_class),
8229 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8230 G_STRUCT_OFFSET (EvViewClass, scroll),
8231 NULL, NULL,
8232 ev_view_marshal_VOID__ENUM_ENUM,
8233 G_TYPE_NONE, 2,
8234 GTK_TYPE_SCROLL_TYPE,
8235 GTK_TYPE_ORIENTATION);
8236 signals[SIGNAL_HANDLE_LINK] = g_signal_new ("handle-link",
8237 G_TYPE_FROM_CLASS (object_class),
8238 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8239 G_STRUCT_OFFSET (EvViewClass, handle_link),
8240 NULL, NULL,
8241 NULL,
8242 G_TYPE_NONE, 2,
8243 G_TYPE_INT, G_TYPE_OBJECT);
8244 signals[SIGNAL_EXTERNAL_LINK] = g_signal_new ("external-link",
8245 G_TYPE_FROM_CLASS (object_class),
8246 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8247 G_STRUCT_OFFSET (EvViewClass, external_link),
8248 NULL, NULL,
8249 g_cclosure_marshal_VOID__OBJECT,
8250 G_TYPE_NONE, 1,
8251 G_TYPE_OBJECT);
8252 signals[SIGNAL_POPUP_MENU] = g_signal_new ("popup",
8253 G_TYPE_FROM_CLASS (object_class),
8254 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8255 G_STRUCT_OFFSET (EvViewClass, popup_menu),
8256 NULL, NULL,
8257 g_cclosure_marshal_VOID__POINTER,
8258 G_TYPE_NONE, 1,
8259 G_TYPE_POINTER);
8260 signals[SIGNAL_SELECTION_CHANGED] = g_signal_new ("selection-changed",
8261 G_TYPE_FROM_CLASS (object_class),
8262 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8263 G_STRUCT_OFFSET (EvViewClass, selection_changed),
8264 NULL, NULL,
8265 g_cclosure_marshal_VOID__VOID,
8266 G_TYPE_NONE, 0,
8267 G_TYPE_NONE);
8268 signals[SIGNAL_SYNC_SOURCE] = g_signal_new ("sync-source",
8269 G_TYPE_FROM_CLASS (object_class),
8270 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8271 G_STRUCT_OFFSET (EvViewClass, sync_source),
8272 NULL, NULL,
8273 g_cclosure_marshal_VOID__BOXED,
8274 G_TYPE_NONE, 1,
8275 EV_TYPE_SOURCE_LINK | G_SIGNAL_TYPE_STATIC_SCOPE);
8276 signals[SIGNAL_ANNOT_ADDED] = g_signal_new ("annot-added",
8277 G_TYPE_FROM_CLASS (object_class),
8278 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8279 G_STRUCT_OFFSET (EvViewClass, annot_added),
8280 NULL, NULL,
8281 g_cclosure_marshal_VOID__OBJECT,
8282 G_TYPE_NONE, 1,
8283 EV_TYPE_ANNOTATION);
8284 signals[SIGNAL_ANNOT_CHANGED] = g_signal_new ("annot-changed",
8285 G_TYPE_FROM_CLASS (object_class),
8286 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8287 G_STRUCT_OFFSET (EvViewClass, annot_changed),
8288 NULL, NULL,
8289 g_cclosure_marshal_VOID__OBJECT,
8290 G_TYPE_NONE, 1,
8291 EV_TYPE_ANNOTATION);
8292 signals[SIGNAL_ANNOT_REMOVED] = g_signal_new ("annot-removed",
8293 G_TYPE_FROM_CLASS (object_class),
8294 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8295 G_STRUCT_OFFSET (EvViewClass, annot_removed),
8296 NULL, NULL,
8297 g_cclosure_marshal_VOID__OBJECT,
8298 G_TYPE_NONE, 1,
8299 EV_TYPE_ANNOTATION);
8300 signals[SIGNAL_LAYERS_CHANGED] = g_signal_new ("layers-changed",
8301 G_TYPE_FROM_CLASS (object_class),
8302 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8303 G_STRUCT_OFFSET (EvViewClass, layers_changed),
8304 NULL, NULL,
8305 g_cclosure_marshal_VOID__VOID,
8306 G_TYPE_NONE, 0,
8307 G_TYPE_NONE);
8308 signals[SIGNAL_MOVE_CURSOR] = g_signal_new ("move-cursor",
8309 G_TYPE_FROM_CLASS (object_class),
8310 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8311 G_STRUCT_OFFSET (EvViewClass, move_cursor),
8312 NULL, NULL,
8313 ev_view_marshal_BOOLEAN__ENUM_INT_BOOLEAN,
8314 G_TYPE_BOOLEAN, 3,
8315 GTK_TYPE_MOVEMENT_STEP,
8316 G_TYPE_INT,
8317 G_TYPE_BOOLEAN);
8318 signals[SIGNAL_CURSOR_MOVED] = g_signal_new ("cursor-moved",
8319 G_TYPE_FROM_CLASS (object_class),
8320 G_SIGNAL_RUN_LAST,
8321 0,
8322 NULL, NULL,
8323 ev_view_marshal_VOID__INT_INT,
8324 G_TYPE_NONE, 2,
8325 G_TYPE_INT,
8326 G_TYPE_INT);
8327 signals[SIGNAL_ACTIVATE] = g_signal_new ("activate",
8328 G_OBJECT_CLASS_TYPE (object_class),
8329 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
8330 G_STRUCT_OFFSET (EvViewClass, activate),
8331 NULL, NULL,
8332 g_cclosure_marshal_VOID__VOID,
8333 G_TYPE_NONE, 0,
8334 G_TYPE_NONE);
8335 widget_class->activate_signal = signals[SIGNAL_ACTIVATE];
8336
8337 binding_set = gtk_binding_set_by_class (class);
8338
8339 add_move_binding_keypad (binding_set, GDK_KEY_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1);
8340 add_move_binding_keypad (binding_set, GDK_KEY_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1);
8341 add_move_binding_keypad (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, -1);
8342 add_move_binding_keypad (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, 1);
8343 add_move_binding_keypad (binding_set, GDK_KEY_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1);
8344 add_move_binding_keypad (binding_set, GDK_KEY_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1);
8345 add_move_binding_keypad (binding_set, GDK_KEY_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
8346 add_move_binding_keypad (binding_set, GDK_KEY_End, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
8347 add_move_binding_keypad (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, -1);
8348 add_move_binding_keypad (binding_set, GDK_KEY_End, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, 1);
8349
8350 add_scroll_binding_keypad (binding_set, GDK_KEY_Left, 0, GTK_SCROLL_STEP_BACKWARD, GTK_ORIENTATION_HORIZONTAL);
8351 add_scroll_binding_keypad (binding_set, GDK_KEY_Right, 0, GTK_SCROLL_STEP_FORWARD, GTK_ORIENTATION_HORIZONTAL);
8352 add_scroll_binding_keypad (binding_set, GDK_KEY_Left, GDK_MOD1_MASK, GTK_SCROLL_STEP_DOWN, GTK_ORIENTATION_HORIZONTAL);
8353 add_scroll_binding_keypad (binding_set, GDK_KEY_Right, GDK_MOD1_MASK, GTK_SCROLL_STEP_UP, GTK_ORIENTATION_HORIZONTAL);
8354 add_scroll_binding_keypad (binding_set, GDK_KEY_Up, 0, GTK_SCROLL_STEP_BACKWARD, GTK_ORIENTATION_VERTICAL);
8355 add_scroll_binding_keypad (binding_set, GDK_KEY_Down, 0, GTK_SCROLL_STEP_FORWARD, GTK_ORIENTATION_VERTICAL);
8356 add_scroll_binding_keypad (binding_set, GDK_KEY_Up, GDK_MOD1_MASK, GTK_SCROLL_STEP_DOWN, GTK_ORIENTATION_VERTICAL);
8357 add_scroll_binding_keypad (binding_set, GDK_KEY_Down, GDK_MOD1_MASK, GTK_SCROLL_STEP_UP, GTK_ORIENTATION_VERTICAL);
8358 add_scroll_binding_keypad (binding_set, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_BACKWARD, GTK_ORIENTATION_VERTICAL);
8359 add_scroll_binding_keypad (binding_set, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_FORWARD, GTK_ORIENTATION_VERTICAL);
8360 add_scroll_binding_keypad (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_SCROLL_START, GTK_ORIENTATION_VERTICAL);
8361 add_scroll_binding_keypad (binding_set, GDK_KEY_End, GDK_CONTROL_MASK, GTK_SCROLL_END, GTK_ORIENTATION_VERTICAL);
8362
8363 /* We can't use the bindings defined in GtkWindow for Space and Return,
8364 * because we also have those bindings for scrolling.
8365 */
8366 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
8367 "activate", 0);
8368 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0,
8369 "activate", 0);
8370 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0,
8371 "activate", 0);
8372 gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0,
8373 "activate", 0);
8374 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0,
8375 "activate", 0);
8376
8377 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, "scroll", 2,
8378 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD,
8379 GTK_TYPE_ORIENTATION, GTK_ORIENTATION_VERTICAL);
8380 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, GDK_SHIFT_MASK, "scroll", 2,
8381 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD,
8382 GTK_TYPE_ORIENTATION, GTK_ORIENTATION_VERTICAL);
8383 gtk_binding_entry_add_signal (binding_set, GDK_KEY_H, 0, "scroll", 2,
8384 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
8385 GTK_TYPE_ORIENTATION, GTK_ORIENTATION_HORIZONTAL);
8386 gtk_binding_entry_add_signal (binding_set, GDK_KEY_J, 0, "scroll", 2,
8387 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
8388 GTK_TYPE_ORIENTATION, GTK_ORIENTATION_VERTICAL);
8389 gtk_binding_entry_add_signal (binding_set, GDK_KEY_K, 0, "scroll", 2,
8390 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
8391 GTK_TYPE_ORIENTATION, GTK_ORIENTATION_VERTICAL);
8392 gtk_binding_entry_add_signal (binding_set, GDK_KEY_L, 0, "scroll", 2,
8393 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
8394 GTK_TYPE_ORIENTATION, GTK_ORIENTATION_HORIZONTAL);
8395 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0, "scroll", 2,
8396 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD,
8397 GTK_TYPE_ORIENTATION, GTK_ORIENTATION_VERTICAL);
8398 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_SHIFT_MASK, "scroll", 2,
8399 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD,
8400 GTK_TYPE_ORIENTATION, GTK_ORIENTATION_VERTICAL);
8401 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0, "scroll", 2,
8402 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD,
8403 GTK_TYPE_ORIENTATION, GTK_ORIENTATION_VERTICAL);
8404 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_SHIFT_MASK, "scroll", 2,
8405 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD,
8406 GTK_TYPE_ORIENTATION, GTK_ORIENTATION_VERTICAL);
8407 }
8408
8409 static void
on_notify_scale_factor(EvView * view,GParamSpec * pspec)8410 on_notify_scale_factor (EvView *view,
8411 GParamSpec *pspec)
8412 {
8413 if (view->document)
8414 view_update_range_and_current_page (view);
8415 }
8416
8417 static void
zoom_gesture_begin_cb(GtkGesture * gesture,GdkEventSequence * sequence,EvView * view)8418 zoom_gesture_begin_cb (GtkGesture *gesture,
8419 GdkEventSequence *sequence,
8420 EvView *view)
8421 {
8422 view->prev_zoom_gesture_scale = 1;
8423 }
8424
8425 static void
zoom_gesture_scale_changed_cb(GtkGestureZoom * gesture,gdouble scale,EvView * view)8426 zoom_gesture_scale_changed_cb (GtkGestureZoom *gesture,
8427 gdouble scale,
8428 EvView *view)
8429 {
8430 gdouble factor;
8431
8432 view->drag_info.in_drag = FALSE;
8433 view->image_dnd_info.in_drag = FALSE;
8434
8435 factor = scale - view->prev_zoom_gesture_scale + 1;
8436 view->prev_zoom_gesture_scale = scale;
8437 ev_document_model_set_sizing_mode (view->model, EV_SIZING_FREE);
8438
8439 gtk_gesture_get_bounding_box_center (GTK_GESTURE (gesture), &view->zoom_center_x, &view->zoom_center_y);
8440
8441 if ((factor < 1.0 && ev_view_can_zoom_out (view)) ||
8442 (factor >= 1.0 && ev_view_can_zoom_in (view)))
8443 ev_view_zoom (view, factor);
8444 }
8445
8446 static void
ev_view_init(EvView * view)8447 ev_view_init (EvView *view)
8448 {
8449 GtkStyleContext *context;
8450 #if defined (ENABLE_MULTIMEDIA) && defined (GDK_WINDOWING_X11)
8451 GdkVisual *visual;
8452 #endif
8453
8454 gtk_widget_set_has_window (GTK_WIDGET (view), TRUE);
8455 gtk_widget_set_can_focus (GTK_WIDGET (view), TRUE);
8456 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (view), FALSE);
8457
8458 context = gtk_widget_get_style_context (GTK_WIDGET (view));
8459 gtk_style_context_add_class (context, "content-view");
8460 gtk_style_context_add_class (context, "view");
8461
8462 #if defined (ENABLE_MULTIMEDIA) && defined (GDK_WINDOWING_X11)
8463 /* Use always the system visual, instead of the one inherited from the
8464 * window, because gst xvimagesink doesn't work with RGBA visuals.
8465 * See https://bugzilla.gnome.org/show_bug.cgi?id=721148
8466 */
8467 visual = gdk_screen_get_system_visual (gdk_screen_get_default ());
8468 gtk_widget_set_visual (GTK_WIDGET (view), visual);
8469 #endif
8470
8471 gtk_widget_set_events (GTK_WIDGET (view),
8472 GDK_TOUCH_MASK |
8473 GDK_EXPOSURE_MASK |
8474 GDK_BUTTON_PRESS_MASK |
8475 GDK_BUTTON_RELEASE_MASK |
8476 GDK_SCROLL_MASK |
8477 GDK_SMOOTH_SCROLL_MASK |
8478 GDK_KEY_PRESS_MASK |
8479 GDK_POINTER_MOTION_MASK |
8480 GDK_POINTER_MOTION_HINT_MASK |
8481 GDK_ENTER_NOTIFY_MASK |
8482 GDK_LEAVE_NOTIFY_MASK);
8483
8484 view->start_page = -1;
8485 view->end_page = -1;
8486 view->spacing = 5;
8487 view->scale = 1.0;
8488 view->current_page = 0;
8489 view->pressed_button = -1;
8490 view->cursor = EV_VIEW_CURSOR_NORMAL;
8491 view->drag_info.in_drag = FALSE;
8492 view->scroll_info.autoscrolling = FALSE;
8493 view->selection_info.selections = NULL;
8494 view->selection_info.in_drag = FALSE;
8495 view->continuous = TRUE;
8496 view->dual_even_left = TRUE;
8497 view->fullscreen = FALSE;
8498 view->sizing_mode = EV_SIZING_FIT_WIDTH;
8499 view->page_layout = EV_PAGE_LAYOUT_SINGLE;
8500 view->pending_scroll = SCROLL_TO_KEEP_POSITION;
8501 view->find_page = -1;
8502 view->jump_to_find_result = TRUE;
8503 view->highlight_find_results = FALSE;
8504 view->pixbuf_cache_size = DEFAULT_PIXBUF_CACHE_SIZE;
8505 view->caret_enabled = FALSE;
8506 view->cursor_page = 0;
8507 view->allow_links_change_zoom = TRUE;
8508 view->zoom_center_x = -1;
8509 view->zoom_center_y = -1;
8510
8511 g_signal_connect (view, "notify::scale-factor",
8512 G_CALLBACK (on_notify_scale_factor), NULL);
8513
8514 view->zoom_gesture = gtk_gesture_zoom_new (GTK_WIDGET (view));
8515 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (view->zoom_gesture),
8516 GTK_PHASE_CAPTURE);
8517
8518 g_signal_connect (view->zoom_gesture, "begin",
8519 G_CALLBACK (zoom_gesture_begin_cb), view);
8520 g_signal_connect (view->zoom_gesture, "scale-changed",
8521 G_CALLBACK (zoom_gesture_scale_changed_cb), view);
8522 }
8523
8524 /*** Callbacks ***/
8525
8526 static void
ev_view_change_page(EvView * view,gint new_page)8527 ev_view_change_page (EvView *view,
8528 gint new_page)
8529 {
8530 gint x, y;
8531
8532 view->current_page = new_page;
8533 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
8534
8535 ev_view_set_loading (view, FALSE);
8536
8537 ev_document_misc_get_pointer_position (GTK_WIDGET (view), &x, &y);
8538 ev_view_handle_cursor_over_xy (view, x, y);
8539
8540 gtk_widget_queue_resize (GTK_WIDGET (view));
8541 }
8542
8543 static void
job_finished_cb(EvPixbufCache * pixbuf_cache,cairo_region_t * region,EvView * view)8544 job_finished_cb (EvPixbufCache *pixbuf_cache,
8545 cairo_region_t *region,
8546 EvView *view)
8547 {
8548 if (region) {
8549 gdk_window_invalidate_region (gtk_widget_get_window (GTK_WIDGET (view)), region, TRUE);
8550 } else {
8551 gtk_widget_queue_draw (GTK_WIDGET (view));
8552 }
8553 }
8554
8555 static void
ev_view_page_changed_cb(EvDocumentModel * model,gint old_page,gint new_page,EvView * view)8556 ev_view_page_changed_cb (EvDocumentModel *model,
8557 gint old_page,
8558 gint new_page,
8559 EvView *view)
8560 {
8561 if (!view->document)
8562 return;
8563
8564 if (view->current_page != new_page) {
8565 ev_view_change_page (view, new_page);
8566 } else {
8567 gtk_widget_queue_draw (GTK_WIDGET (view));
8568 }
8569 }
8570
8571 static gboolean
cursor_scroll_update(gpointer data)8572 cursor_scroll_update (gpointer data)
8573 {
8574 EvView *view = data;
8575 gint x, y;
8576
8577 view->update_cursor_idle_id = 0;
8578 ev_document_misc_get_pointer_position (GTK_WIDGET (view), &x, &y);
8579 ev_view_handle_cursor_over_xy (view, x, y);
8580
8581 return FALSE;
8582 }
8583
8584 static void
schedule_scroll_cursor_update(EvView * view)8585 schedule_scroll_cursor_update (EvView *view)
8586 {
8587 if (view->update_cursor_idle_id)
8588 return;
8589
8590 view->update_cursor_idle_id =
8591 g_idle_add (cursor_scroll_update, view);
8592 }
8593
8594 static void
on_adjustment_value_changed(GtkAdjustment * adjustment,EvView * view)8595 on_adjustment_value_changed (GtkAdjustment *adjustment,
8596 EvView *view)
8597 {
8598 GtkWidget *widget = GTK_WIDGET (view);
8599 int dx = 0, dy = 0;
8600 gdouble x, y;
8601 gint value;
8602 GList *l;
8603 GdkEvent *event;
8604 gboolean cursor_updated;
8605
8606 if (!gtk_widget_get_realized (widget))
8607 return;
8608
8609 /* If the adjustment value is set during a drag event, update the drag
8610 * start position so it can continue from the new location. */
8611 if (view->drag_info.in_drag && !view->drag_info.in_notify) {
8612 view->drag_info.hadj += gtk_adjustment_get_value (view->hadjustment) - view->scroll_x;
8613 view->drag_info.vadj += gtk_adjustment_get_value (view->vadjustment) - view->scroll_y;
8614 }
8615
8616 if (view->hadjustment) {
8617 value = (gint) gtk_adjustment_get_value (view->hadjustment);
8618 dx = view->scroll_x - value;
8619 view->scroll_x = value;
8620 } else {
8621 view->scroll_x = 0;
8622 }
8623
8624 if (view->vadjustment) {
8625 value = (gint) gtk_adjustment_get_value (view->vadjustment);
8626 dy = view->scroll_y - value;
8627 view->scroll_y = value;
8628 } else {
8629 view->scroll_y = 0;
8630 }
8631
8632 for (l = view->children; l && l->data; l = g_list_next (l)) {
8633 EvViewChild *child = (EvViewChild *)l->data;
8634
8635 child->x += dx;
8636 child->y += dy;
8637 if (gtk_widget_get_visible (child->widget) && gtk_widget_get_visible (widget))
8638 gtk_widget_queue_resize (widget);
8639 }
8640
8641 for (l = view->window_children; l && l->data; l = g_list_next (l)) {
8642 EvViewWindowChild *child;
8643
8644 child = (EvViewWindowChild *)l->data;
8645
8646 ev_view_window_child_move (view, child, child->x + dx, child->y + dy);
8647 }
8648
8649 if (view->pending_resize) {
8650 gtk_widget_queue_draw (widget);
8651 } else {
8652 gdk_window_scroll (gtk_widget_get_window (widget), dx, dy);
8653 }
8654
8655 cursor_updated = FALSE;
8656 event = gtk_get_current_event ();
8657 if (event) {
8658 if (event->type == GDK_SCROLL &&
8659 gdk_event_get_window (event) == gtk_widget_get_window (widget)) {
8660 gdk_event_get_coords (event, &x, &y);
8661 ev_view_handle_cursor_over_xy (view, (gint) x, (gint) y);
8662 cursor_updated = TRUE;
8663 }
8664 gdk_event_free (event);
8665 }
8666
8667 if (!cursor_updated)
8668 schedule_scroll_cursor_update (view);
8669
8670 if (view->document)
8671 view_update_range_and_current_page (view);
8672 }
8673
8674 GtkWidget*
ev_view_new(void)8675 ev_view_new (void)
8676 {
8677 GtkWidget *view;
8678
8679 view = g_object_new (EV_TYPE_VIEW, NULL);
8680
8681 return view;
8682 }
8683
8684 static void
setup_caches(EvView * view)8685 setup_caches (EvView *view)
8686 {
8687 gboolean inverted_colors;
8688
8689 view->height_to_page_cache = ev_view_get_height_to_page_cache (view);
8690 view->pixbuf_cache = ev_pixbuf_cache_new (GTK_WIDGET (view), view->model, view->pixbuf_cache_size);
8691 view->page_cache = ev_page_cache_new (view->document);
8692
8693 ev_page_cache_set_flags (view->page_cache,
8694 ev_page_cache_get_flags (view->page_cache) |
8695 EV_PAGE_DATA_INCLUDE_TEXT_LAYOUT |
8696 EV_PAGE_DATA_INCLUDE_TEXT |
8697 EV_PAGE_DATA_INCLUDE_TEXT_ATTRS |
8698 EV_PAGE_DATA_INCLUDE_TEXT_LOG_ATTRS);
8699
8700 inverted_colors = ev_document_model_get_inverted_colors (view->model);
8701 ev_pixbuf_cache_set_inverted_colors (view->pixbuf_cache, inverted_colors);
8702 g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
8703 }
8704
8705 static void
clear_caches(EvView * view)8706 clear_caches (EvView *view)
8707 {
8708 if (view->pixbuf_cache) {
8709 g_object_unref (view->pixbuf_cache);
8710 view->pixbuf_cache = NULL;
8711 }
8712
8713 if (view->page_cache) {
8714 g_object_unref (view->page_cache);
8715 view->page_cache = NULL;
8716 }
8717 }
8718
8719 /**
8720 * ev_view_set_page_cache_size:
8721 * @view: #EvView instance
8722 * @cache_size: size in bytes
8723 *
8724 * Sets the maximum size in bytes that will be used to cache
8725 * rendered pages. Use 0 to disable caching rendered pages.
8726 *
8727 * Note that this limit doesn't affect the current visible page range,
8728 * which will always be rendered. In order to limit the total memory used
8729 * you have to use ev_document_model_set_max_scale() too.
8730 *
8731 */
8732 void
ev_view_set_page_cache_size(EvView * view,gsize cache_size)8733 ev_view_set_page_cache_size (EvView *view,
8734 gsize cache_size)
8735 {
8736 if (view->pixbuf_cache_size == cache_size)
8737 return;
8738
8739 view->pixbuf_cache_size = cache_size;
8740 if (view->pixbuf_cache)
8741 ev_pixbuf_cache_set_max_size (view->pixbuf_cache, cache_size);
8742
8743 view_update_scale_limits (view);
8744 }
8745
8746 /**
8747 * ev_view_set_loading:
8748 * @view:
8749 * @loading:
8750 *
8751 * Deprecated: 3.8
8752 */
8753 void
ev_view_set_loading(EvView * view,gboolean loading)8754 ev_view_set_loading (EvView *view,
8755 gboolean loading)
8756 {
8757 if (view->loading == loading)
8758 return;
8759
8760 view->loading = loading;
8761 g_object_notify (G_OBJECT (view), "is-loading");
8762 }
8763
8764 /**
8765 * ev_view_is_loading:
8766 * @view:
8767 *
8768 * Returns: %TRUE iff the view is currently loading a document
8769 *
8770 * Since: 3.8
8771 */
8772 gboolean
ev_view_is_loading(EvView * view)8773 ev_view_is_loading (EvView *view)
8774 {
8775 return view->loading;
8776 }
8777
8778 void
ev_view_autoscroll_start(EvView * view)8779 ev_view_autoscroll_start (EvView *view)
8780 {
8781 gint x, y;
8782
8783 g_return_if_fail (EV_IS_VIEW (view));
8784
8785 if (view->scroll_info.autoscrolling)
8786 return;
8787
8788 view->scroll_info.autoscrolling = TRUE;
8789 ev_view_autoscroll_resume (view);
8790
8791 ev_document_misc_get_pointer_position (GTK_WIDGET (view), &x, &y);
8792 ev_view_handle_cursor_over_xy (view, x, y);
8793 }
8794
8795 void
ev_view_autoscroll_stop(EvView * view)8796 ev_view_autoscroll_stop (EvView *view)
8797 {
8798 gint x, y;
8799
8800 g_return_if_fail (EV_IS_VIEW (view));
8801
8802 if (!view->scroll_info.autoscrolling)
8803 return;
8804
8805 view->scroll_info.autoscrolling = FALSE;
8806 ev_view_autoscroll_pause (view);
8807
8808 ev_document_misc_get_pointer_position (GTK_WIDGET (view), &x, &y);
8809 ev_view_handle_cursor_over_xy (view, x, y);
8810 }
8811
8812 static void
ev_view_document_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)8813 ev_view_document_changed_cb (EvDocumentModel *model,
8814 GParamSpec *pspec,
8815 EvView *view)
8816 {
8817 EvDocument *document = ev_document_model_get_document (model);
8818
8819 if (document != view->document) {
8820 gint current_page;
8821
8822 ev_view_remove_all (view);
8823 clear_caches (view);
8824
8825 if (view->document) {
8826 g_object_unref (view->document);
8827 }
8828
8829 view->document = document ? g_object_ref (document) : NULL;
8830 view->find_page = -1;
8831 view->find_result = 0;
8832
8833 if (view->document) {
8834 if (ev_document_get_n_pages (view->document) <= 0 ||
8835 !ev_document_check_dimensions (view->document))
8836 return;
8837
8838 ev_view_set_loading (view, FALSE);
8839 setup_caches (view);
8840
8841 if (view->caret_enabled)
8842 preload_pages_for_caret_navigation (view);
8843 }
8844
8845 current_page = ev_document_model_get_page (model);
8846 if (view->current_page != current_page) {
8847 ev_view_change_page (view, current_page);
8848 } else {
8849 view->pending_scroll = SCROLL_TO_KEEP_POSITION;
8850 gtk_widget_queue_resize (GTK_WIDGET (view));
8851 }
8852 view_update_scale_limits (view);
8853 }
8854 }
8855
8856 static void
ev_view_rotation_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)8857 ev_view_rotation_changed_cb (EvDocumentModel *model,
8858 GParamSpec *pspec,
8859 EvView *view)
8860 {
8861 gint rotation = ev_document_model_get_rotation (model);
8862
8863 view->rotation = rotation;
8864
8865 if (view->pixbuf_cache) {
8866 ev_pixbuf_cache_clear (view->pixbuf_cache);
8867 if (!ev_document_is_page_size_uniform (view->document))
8868 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
8869 gtk_widget_queue_resize (GTK_WIDGET (view));
8870 }
8871
8872 ev_view_remove_all (view);
8873 view_update_scale_limits (view);
8874
8875 if (rotation != 0)
8876 clear_selection (view);
8877 }
8878
8879 static void
ev_view_inverted_colors_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)8880 ev_view_inverted_colors_changed_cb (EvDocumentModel *model,
8881 GParamSpec *pspec,
8882 EvView *view)
8883 {
8884 if (view->pixbuf_cache) {
8885 gboolean inverted_colors;
8886
8887 inverted_colors = ev_document_model_get_inverted_colors (model);
8888 ev_pixbuf_cache_set_inverted_colors (view->pixbuf_cache, inverted_colors);
8889 gtk_widget_queue_draw (GTK_WIDGET (view));
8890 }
8891 }
8892
8893 static void
ev_view_sizing_mode_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)8894 ev_view_sizing_mode_changed_cb (EvDocumentModel *model,
8895 GParamSpec *pspec,
8896 EvView *view)
8897 {
8898 EvSizingMode mode = ev_document_model_get_sizing_mode (model);
8899
8900 view->sizing_mode = mode;
8901 if (mode != EV_SIZING_FREE)
8902 gtk_widget_queue_resize (GTK_WIDGET (view));
8903 }
8904
8905 static void
update_can_zoom(EvView * view)8906 update_can_zoom (EvView *view)
8907 {
8908 gdouble min_scale;
8909 gdouble max_scale;
8910 gboolean can_zoom_in;
8911 gboolean can_zoom_out;
8912
8913 min_scale = ev_document_model_get_min_scale (view->model);
8914 max_scale = ev_document_model_get_max_scale (view->model);
8915
8916 can_zoom_in = view->scale <= max_scale;
8917 can_zoom_out = view->scale > min_scale;
8918
8919 if (can_zoom_in != view->can_zoom_in) {
8920 view->can_zoom_in = can_zoom_in;
8921 g_object_notify (G_OBJECT (view), "can-zoom-in");
8922 }
8923
8924 if (can_zoom_out != view->can_zoom_out) {
8925 view->can_zoom_out = can_zoom_out;
8926 g_object_notify (G_OBJECT (view), "can-zoom-out");
8927 }
8928 }
8929
8930 static void
ev_view_page_layout_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)8931 ev_view_page_layout_changed_cb (EvDocumentModel *model,
8932 GParamSpec *pspec,
8933 EvView *view)
8934 {
8935 EvPageLayout layout = ev_document_model_get_page_layout (model);
8936
8937 view->page_layout = layout;
8938
8939 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
8940 gtk_widget_queue_resize (GTK_WIDGET (view));
8941
8942 /* FIXME: if we're keeping the pixbuf cache around, we should extend the
8943 * preload_cache_size to be 2 if dual_page is set.
8944 */
8945 }
8946
8947 #define EPSILON 0.0000001
8948 static void
ev_view_scale_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)8949 ev_view_scale_changed_cb (EvDocumentModel *model,
8950 GParamSpec *pspec,
8951 EvView *view)
8952 {
8953 gdouble scale = ev_document_model_get_scale (model);
8954
8955 if (ABS (view->scale - scale) < EPSILON)
8956 return;
8957
8958 view->scale = scale;
8959
8960 view->pending_resize = TRUE;
8961 if (view->sizing_mode == EV_SIZING_FREE)
8962 gtk_widget_queue_resize (GTK_WIDGET (view));
8963
8964 update_can_zoom (view);
8965 }
8966
8967 static void
ev_view_min_scale_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)8968 ev_view_min_scale_changed_cb (EvDocumentModel *model,
8969 GParamSpec *pspec,
8970 EvView *view)
8971 {
8972 update_can_zoom (view);
8973 }
8974
8975 static void
ev_view_max_scale_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)8976 ev_view_max_scale_changed_cb (EvDocumentModel *model,
8977 GParamSpec *pspec,
8978 EvView *view)
8979 {
8980 update_can_zoom (view);
8981 }
8982
8983 static void
ev_view_continuous_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)8984 ev_view_continuous_changed_cb (EvDocumentModel *model,
8985 GParamSpec *pspec,
8986 EvView *view)
8987 {
8988 gboolean continuous = ev_document_model_get_continuous (model);
8989
8990 if (view->document) {
8991 GdkPoint view_point;
8992 GdkRectangle page_area;
8993 GtkBorder border;
8994
8995 view_point.x = view->scroll_x;
8996 view_point.y = view->scroll_y;
8997 ev_view_get_page_extents (view, view->start_page, &page_area, &border);
8998 _ev_view_transform_view_point_to_doc_point (view, &view_point,
8999 &page_area, &border,
9000 &view->pending_point.x,
9001 &view->pending_point.y);
9002 }
9003 view->continuous = continuous;
9004 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
9005 gtk_widget_queue_resize (GTK_WIDGET (view));
9006 }
9007
9008 static void
ev_view_dual_odd_left_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)9009 ev_view_dual_odd_left_changed_cb (EvDocumentModel *model,
9010 GParamSpec *pspec,
9011 EvView *view)
9012 {
9013 view->dual_even_left = !ev_document_model_get_dual_page_odd_pages_left (model);
9014 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
9015 if (ev_document_model_get_dual_page (model))
9016 /* odd_left may be set when not in dual mode,
9017 queue_resize is not needed in that case */
9018 gtk_widget_queue_resize (GTK_WIDGET (view));
9019 }
9020
9021 static void
ev_view_direction_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)9022 ev_view_direction_changed_cb (EvDocumentModel *model,
9023 GParamSpec *pspec,
9024 EvView *view)
9025 {
9026 gboolean rtl = ev_document_model_get_rtl (model);
9027 gtk_widget_set_direction (GTK_WIDGET (view), rtl ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
9028 view->pending_scroll = SCROLL_TO_PAGE_POSITION;
9029 gtk_widget_queue_resize (GTK_WIDGET (view));
9030 }
9031
9032 static void
ev_view_fullscreen_changed_cb(EvDocumentModel * model,GParamSpec * pspec,EvView * view)9033 ev_view_fullscreen_changed_cb (EvDocumentModel *model,
9034 GParamSpec *pspec,
9035 EvView *view)
9036 {
9037 gboolean fullscreen = ev_document_model_get_fullscreen (model);
9038
9039 view->fullscreen = fullscreen;
9040 gtk_widget_queue_resize (GTK_WIDGET (view));
9041 }
9042
9043 void
ev_view_set_model(EvView * view,EvDocumentModel * model)9044 ev_view_set_model (EvView *view,
9045 EvDocumentModel *model)
9046 {
9047 g_return_if_fail (EV_IS_VIEW (view));
9048 g_return_if_fail (EV_IS_DOCUMENT_MODEL (model));
9049
9050 if (model == view->model)
9051 return;
9052
9053 if (view->model) {
9054 g_signal_handlers_disconnect_by_data (view->model, view);
9055 g_object_unref (view->model);
9056 }
9057 view->model = g_object_ref (model);
9058
9059 /* Initialize view from model */
9060 view->rotation = ev_document_model_get_rotation (view->model);
9061 view->sizing_mode = ev_document_model_get_sizing_mode (view->model);
9062 view->scale = ev_document_model_get_scale (view->model);
9063 view->continuous = ev_document_model_get_continuous (view->model);
9064 view->page_layout = ev_document_model_get_page_layout (view->model);
9065 gtk_widget_set_direction (GTK_WIDGET(view), ev_document_model_get_rtl (view->model));
9066 view->fullscreen = ev_document_model_get_fullscreen (view->model);
9067 ev_view_document_changed_cb (view->model, NULL, view);
9068
9069 g_signal_connect (view->model, "notify::document",
9070 G_CALLBACK (ev_view_document_changed_cb),
9071 view);
9072 g_signal_connect (view->model, "notify::rotation",
9073 G_CALLBACK (ev_view_rotation_changed_cb),
9074 view);
9075 g_signal_connect (view->model, "notify::inverted-colors",
9076 G_CALLBACK (ev_view_inverted_colors_changed_cb),
9077 view);
9078 g_signal_connect (view->model, "notify::sizing-mode",
9079 G_CALLBACK (ev_view_sizing_mode_changed_cb),
9080 view);
9081 g_signal_connect (view->model, "notify::page-layout",
9082 G_CALLBACK (ev_view_page_layout_changed_cb),
9083 view);
9084 g_signal_connect (view->model, "notify::scale",
9085 G_CALLBACK (ev_view_scale_changed_cb),
9086 view);
9087 g_signal_connect (view->model, "notify::min-scale",
9088 G_CALLBACK (ev_view_min_scale_changed_cb),
9089 view);
9090 g_signal_connect (view->model, "notify::max-scale",
9091 G_CALLBACK (ev_view_max_scale_changed_cb),
9092 view);
9093 g_signal_connect (view->model, "notify::continuous",
9094 G_CALLBACK (ev_view_continuous_changed_cb),
9095 view);
9096 g_signal_connect (view->model, "notify::dual-odd-left",
9097 G_CALLBACK (ev_view_dual_odd_left_changed_cb),
9098 view);
9099 g_signal_connect (view->model, "notify::rtl",
9100 G_CALLBACK (ev_view_direction_changed_cb),
9101 view);
9102 g_signal_connect (view->model, "notify::fullscreen",
9103 G_CALLBACK (ev_view_fullscreen_changed_cb),
9104 view);
9105 g_signal_connect (view->model, "page-changed",
9106 G_CALLBACK (ev_view_page_changed_cb),
9107 view);
9108
9109 if (view->accessible)
9110 ev_view_accessible_set_model (EV_VIEW_ACCESSIBLE (view->accessible),
9111 view->model);
9112 }
9113
9114 static void
ev_view_reload_page(EvView * view,gint page,cairo_region_t * region)9115 ev_view_reload_page (EvView *view,
9116 gint page,
9117 cairo_region_t *region)
9118 {
9119 ev_pixbuf_cache_reload_page (view->pixbuf_cache,
9120 region,
9121 page,
9122 view->rotation,
9123 view->scale);
9124 }
9125
9126 void
ev_view_reload(EvView * view)9127 ev_view_reload (EvView *view)
9128 {
9129 ev_pixbuf_cache_clear (view->pixbuf_cache);
9130 view_update_range_and_current_page (view);
9131 }
9132
9133 /*** Zoom and sizing mode ***/
9134
9135 static gboolean
ev_view_can_zoom(EvView * view,gdouble factor)9136 ev_view_can_zoom (EvView *view, gdouble factor)
9137 {
9138 if (factor == 1.0)
9139 return TRUE;
9140
9141 else if (factor < 1.0) {
9142 return ev_view_can_zoom_out (view);
9143 } else {
9144 return ev_view_can_zoom_in (view);
9145 }
9146 }
9147
9148 gboolean
ev_view_can_zoom_in(EvView * view)9149 ev_view_can_zoom_in (EvView *view)
9150 {
9151 return view->can_zoom_in;
9152 }
9153
9154 gboolean
ev_view_can_zoom_out(EvView * view)9155 ev_view_can_zoom_out (EvView *view)
9156 {
9157 return view->can_zoom_out;
9158 }
9159
9160 static void
ev_view_zoom(EvView * view,gdouble factor)9161 ev_view_zoom (EvView *view, gdouble factor)
9162 {
9163 gdouble scale;
9164
9165 g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
9166
9167 view->pending_scroll = SCROLL_TO_CENTER;
9168 scale = ev_document_model_get_scale (view->model) * factor;
9169 ev_document_model_set_scale (view->model, scale);
9170 }
9171
9172 void
ev_view_zoom_in(EvView * view)9173 ev_view_zoom_in (EvView *view)
9174 {
9175 ev_view_zoom (view, ZOOM_IN_FACTOR);
9176 }
9177
9178 void
ev_view_zoom_out(EvView * view)9179 ev_view_zoom_out (EvView *view)
9180 {
9181 ev_view_zoom (view, ZOOM_OUT_FACTOR);
9182 }
9183
9184 static double
zoom_for_size_fit_width(gdouble doc_width,gdouble doc_height,int target_width,int target_height)9185 zoom_for_size_fit_width (gdouble doc_width,
9186 gdouble doc_height,
9187 int target_width,
9188 int target_height)
9189 {
9190 return (double)target_width / doc_width;
9191 }
9192
9193 static double
zoom_for_size_fit_height(gdouble doc_width,gdouble doc_height,int target_width,int target_height)9194 zoom_for_size_fit_height (gdouble doc_width,
9195 gdouble doc_height,
9196 int target_width,
9197 int target_height)
9198 {
9199 return (double)target_height / doc_height;
9200 }
9201
9202 static double
zoom_for_size_fit_page(gdouble doc_width,gdouble doc_height,int target_width,int target_height)9203 zoom_for_size_fit_page (gdouble doc_width,
9204 gdouble doc_height,
9205 int target_width,
9206 int target_height)
9207 {
9208 double w_scale;
9209 double h_scale;
9210
9211 w_scale = (double)target_width / doc_width;
9212 h_scale = (double)target_height / doc_height;
9213
9214 return MIN (w_scale, h_scale);
9215 }
9216
9217 static double
zoom_for_size_automatic(GtkWidget * widget,gdouble doc_width,gdouble doc_height,int target_width,int target_height)9218 zoom_for_size_automatic (GtkWidget *widget,
9219 gdouble doc_width,
9220 gdouble doc_height,
9221 int target_width,
9222 int target_height)
9223 {
9224 double fit_width_scale;
9225 double scale;
9226
9227 fit_width_scale = zoom_for_size_fit_width (doc_width, doc_height, target_width, target_height);
9228
9229 if (doc_height < doc_width) {
9230 double fit_height_scale;
9231
9232 fit_height_scale = zoom_for_size_fit_height (doc_width, doc_height, target_width, target_height);
9233 scale = MIN (fit_width_scale, fit_height_scale);
9234 } else {
9235 double actual_scale;
9236
9237 actual_scale = ev_document_misc_get_widget_dpi (widget) / 72.0;
9238 scale = MIN (fit_width_scale, actual_scale);
9239 }
9240
9241 return scale;
9242 }
9243
9244 static void
ev_view_zoom_for_size_continuous_and_dual_page(EvView * view,int width,int height)9245 ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
9246 int width,
9247 int height)
9248 {
9249 gdouble doc_width, doc_height;
9250 GtkBorder border;
9251 gdouble scale;
9252 gint sb_size;
9253
9254 ev_document_get_max_page_size (view->document, &doc_width, &doc_height);
9255 if (view->rotation == 90 || view->rotation == 270) {
9256 gdouble tmp;
9257
9258 tmp = doc_width;
9259 doc_width = doc_height;
9260 doc_height = tmp;
9261 }
9262
9263 compute_border (view, &border);
9264
9265 doc_width *= 2;
9266 width -= (2 * (border.left + border.right) + 3 * view->spacing);
9267 height -= (border.top + border.bottom + 2 * view->spacing);
9268
9269 sb_size = ev_view_get_scrollbar_size (view, GTK_ORIENTATION_VERTICAL);
9270
9271 switch (view->sizing_mode) {
9272 case EV_SIZING_FIT_WIDTH:
9273 scale = zoom_for_size_fit_width (doc_width, doc_height, width - sb_size, height);
9274 break;
9275 case EV_SIZING_FIT_PAGE:
9276 scale = zoom_for_size_fit_page (doc_width, doc_height, width - sb_size, height);
9277 break;
9278 case EV_SIZING_AUTOMATIC:
9279 scale = zoom_for_size_automatic (GTK_WIDGET (view),
9280 doc_width, doc_height, width - sb_size, height);
9281 break;
9282 default:
9283 g_assert_not_reached ();
9284 }
9285
9286 ev_document_model_set_scale (view->model, scale);
9287 }
9288
9289 static void
ev_view_zoom_for_size_continuous(EvView * view,int width,int height)9290 ev_view_zoom_for_size_continuous (EvView *view,
9291 int width,
9292 int height)
9293 {
9294 gdouble doc_width, doc_height;
9295 GtkBorder border;
9296 gdouble scale;
9297 gint sb_size;
9298
9299 ev_document_get_max_page_size (view->document, &doc_width, &doc_height);
9300 if (view->rotation == 90 || view->rotation == 270) {
9301 gdouble tmp;
9302
9303 tmp = doc_width;
9304 doc_width = doc_height;
9305 doc_height = tmp;
9306 }
9307
9308 compute_border (view, &border);
9309
9310 width -= (border.left + border.right + 2 * view->spacing);
9311 height -= (border.top + border.bottom + 2 * view->spacing);
9312
9313 sb_size = ev_view_get_scrollbar_size (view, GTK_ORIENTATION_VERTICAL);
9314
9315 switch (view->sizing_mode) {
9316 case EV_SIZING_FIT_WIDTH:
9317 scale = zoom_for_size_fit_width (doc_width, doc_height, width - sb_size, height);
9318 break;
9319 case EV_SIZING_FIT_PAGE:
9320 scale = zoom_for_size_fit_page (doc_width, doc_height, width - sb_size, height);
9321 break;
9322 case EV_SIZING_AUTOMATIC:
9323 scale = zoom_for_size_automatic (GTK_WIDGET (view),
9324 doc_width, doc_height, width - sb_size, height);
9325 break;
9326 default:
9327 g_assert_not_reached ();
9328 }
9329
9330 ev_document_model_set_scale (view->model, scale);
9331 }
9332
9333 static void
ev_view_zoom_for_size_dual_page(EvView * view,int width,int height)9334 ev_view_zoom_for_size_dual_page (EvView *view,
9335 int width,
9336 int height)
9337 {
9338 GtkBorder border;
9339 gdouble doc_width, doc_height;
9340 gdouble scale;
9341 gint other_page;
9342 gint sb_size;
9343
9344 other_page = view->current_page ^ 1;
9345
9346 /* Find the largest of the two. */
9347 get_doc_page_size (view, view->current_page, &doc_width, &doc_height);
9348 if (other_page < ev_document_get_n_pages (view->document)) {
9349 gdouble width_2, height_2;
9350
9351 get_doc_page_size (view, other_page, &width_2, &height_2);
9352 if (width_2 > doc_width)
9353 doc_width = width_2;
9354 if (height_2 > doc_height)
9355 doc_height = height_2;
9356 }
9357 compute_border (view, &border);
9358
9359 doc_width = doc_width * 2;
9360 width -= ((border.left + border.right)* 2 + 3 * view->spacing);
9361 height -= (border.top + border.bottom + 2 * view->spacing);
9362
9363 switch (view->sizing_mode) {
9364 case EV_SIZING_FIT_WIDTH:
9365 sb_size = ev_view_get_scrollbar_size (view, GTK_ORIENTATION_VERTICAL);
9366 scale = zoom_for_size_fit_width (doc_width, doc_height, width - sb_size, height);
9367 break;
9368 case EV_SIZING_FIT_PAGE:
9369 scale = zoom_for_size_fit_page (doc_width, doc_height, width, height);
9370 break;
9371 case EV_SIZING_AUTOMATIC:
9372 sb_size = ev_view_get_scrollbar_size (view, GTK_ORIENTATION_VERTICAL);
9373 scale = zoom_for_size_automatic (GTK_WIDGET (view),
9374 doc_width, doc_height, width - sb_size, height);
9375 break;
9376 default:
9377 g_assert_not_reached ();
9378 }
9379
9380 ev_document_model_set_scale (view->model, scale);
9381 }
9382
9383 static void
ev_view_zoom_for_size_single_page(EvView * view,int width,int height)9384 ev_view_zoom_for_size_single_page (EvView *view,
9385 int width,
9386 int height)
9387 {
9388 gdouble doc_width, doc_height;
9389 GtkBorder border;
9390 gdouble scale;
9391 gint sb_size;
9392
9393 get_doc_page_size (view, view->current_page, &doc_width, &doc_height);
9394
9395 /* Get an approximate border */
9396 compute_border (view, &border);
9397
9398 width -= (border.left + border.right + 2 * view->spacing);
9399 height -= (border.top + border.bottom + 2 * view->spacing);
9400
9401 switch (view->sizing_mode) {
9402 case EV_SIZING_FIT_WIDTH:
9403 sb_size = ev_view_get_scrollbar_size (view, GTK_ORIENTATION_VERTICAL);
9404 scale = zoom_for_size_fit_width (doc_width, doc_height, width - sb_size, height);
9405 break;
9406 case EV_SIZING_FIT_PAGE:
9407 scale = zoom_for_size_fit_page (doc_width, doc_height, width, height);
9408 break;
9409 case EV_SIZING_AUTOMATIC:
9410 sb_size = ev_view_get_scrollbar_size (view, GTK_ORIENTATION_VERTICAL);
9411 scale = zoom_for_size_automatic (GTK_WIDGET (view),
9412 doc_width, doc_height, width - sb_size, height);
9413 break;
9414 default:
9415 g_assert_not_reached ();
9416 }
9417
9418 ev_document_model_set_scale (view->model, scale);
9419 }
9420
9421 static void
ev_view_zoom_for_size(EvView * view,int width,int height)9422 ev_view_zoom_for_size (EvView *view,
9423 int width,
9424 int height)
9425 {
9426 gboolean dual_page;
9427
9428 g_return_if_fail (EV_IS_VIEW (view));
9429 g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
9430 view->sizing_mode == EV_SIZING_FIT_PAGE ||
9431 view->sizing_mode == EV_SIZING_AUTOMATIC);
9432 g_return_if_fail (width >= 0);
9433 g_return_if_fail (height >= 0);
9434
9435 if (view->document == NULL)
9436 return;
9437
9438 dual_page = is_dual_page (view, NULL);
9439 if (view->continuous && dual_page)
9440 ev_view_zoom_for_size_continuous_and_dual_page (view, width, height);
9441 else if (view->continuous)
9442 ev_view_zoom_for_size_continuous (view, width, height);
9443 else if (dual_page)
9444 ev_view_zoom_for_size_dual_page (view, width, height);
9445 else
9446 ev_view_zoom_for_size_single_page (view, width, height);
9447 }
9448
9449 static gboolean
ev_view_page_fits(EvView * view,GtkOrientation orientation)9450 ev_view_page_fits (EvView *view,
9451 GtkOrientation orientation)
9452 {
9453 GtkRequisition requisition;
9454 GtkAllocation allocation;
9455 double size;
9456
9457 if (view->sizing_mode == EV_SIZING_FIT_PAGE)
9458 return TRUE;
9459
9460 if (orientation == GTK_ORIENTATION_HORIZONTAL &&
9461 (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
9462 view->sizing_mode == EV_SIZING_AUTOMATIC))
9463 return TRUE;
9464
9465 gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
9466 ev_view_size_request (GTK_WIDGET (view), &requisition);
9467
9468 if (orientation == GTK_ORIENTATION_HORIZONTAL) {
9469 if (requisition.width == 1) {
9470 size = 1.0;
9471 } else {
9472 if (allocation.width > 0.0)
9473 size = (double) requisition.width / allocation.width;
9474 else
9475 size = 1.0;
9476 }
9477 } else {
9478 if (requisition.height == 1) {
9479 size = 1.0;
9480 } else {
9481 if (allocation.height > 0.0)
9482 size = (double) requisition.height / allocation.height;
9483 else
9484 size = 1.0;
9485 }
9486 }
9487
9488 return size <= 1.0;
9489 }
9490
9491 /*** Find ***/
9492 static gint
ev_view_find_get_n_results(EvView * view,gint page)9493 ev_view_find_get_n_results (EvView *view, gint page)
9494 {
9495 return view->find_pages ? g_list_length (view->find_pages[page]) : 0;
9496 }
9497
9498 static EvRectangle *
ev_view_find_get_result(EvView * view,gint page,gint result)9499 ev_view_find_get_result (EvView *view, gint page, gint result)
9500 {
9501 return view->find_pages ? (EvRectangle *) g_list_nth_data (view->find_pages[page], result) : NULL;
9502 }
9503
9504 static void
jump_to_find_result(EvView * view)9505 jump_to_find_result (EvView *view)
9506 {
9507 gint n_results;
9508 gint page = view->find_page;
9509
9510 n_results = ev_view_find_get_n_results (view, page);
9511
9512 if (n_results > 0 && view->find_result < n_results) {
9513 EvRectangle *rect;
9514 GdkRectangle view_rect;
9515
9516 rect = ev_view_find_get_result (view, page, view->find_result);
9517 _ev_view_transform_doc_rect_to_view_rect (view, page, rect, &view_rect);
9518 _ev_view_ensure_rectangle_is_visible (view, &view_rect);
9519 if (view->caret_enabled && view->rotation == 0)
9520 position_caret_cursor_at_doc_point (view, page, rect->x1, rect->y1);
9521
9522 view->jump_to_find_result = FALSE;
9523 }
9524 }
9525
9526 /**
9527 * jump_to_find_page:
9528 * @view: #EvView instance
9529 * @direction: Direction to look
9530 * @shift: Shift from current page
9531 *
9532 * Jumps to the first page that has occurrences of searched word.
9533 * Uses a direction where to look and a shift from current page.
9534 *
9535 */
9536 static void
jump_to_find_page(EvView * view,EvViewFindDirection direction,gint shift)9537 jump_to_find_page (EvView *view, EvViewFindDirection direction, gint shift)
9538 {
9539 int n_pages, i;
9540
9541 n_pages = ev_document_get_n_pages (view->document);
9542
9543 for (i = 0; i < n_pages; i++) {
9544 int page;
9545
9546 if (direction == EV_VIEW_FIND_NEXT)
9547 page = view->find_page + i;
9548 else
9549 page = view->find_page - i;
9550 page += shift;
9551
9552 if (page >= n_pages)
9553 page = page - n_pages;
9554 else if (page < 0)
9555 page = page + n_pages;
9556
9557 if (view->find_pages && view->find_pages[page]) {
9558 view->find_page = page;
9559 break;
9560 }
9561 }
9562
9563 if (!view->continuous)
9564 ev_document_model_set_page (view->model, view->find_page);
9565 }
9566
9567 static void
find_job_updated_cb(EvJobFind * job,gint page,EvView * view)9568 find_job_updated_cb (EvJobFind *job, gint page, EvView *view)
9569 {
9570 ev_view_find_changed (view, ev_job_find_get_results (job), page);
9571 }
9572
9573 /**
9574 * ev_view_find_started:
9575 * @view:
9576 * @job:
9577 *
9578 * Since: 3.6
9579 */
9580 void
ev_view_find_started(EvView * view,EvJobFind * job)9581 ev_view_find_started (EvView *view, EvJobFind *job)
9582 {
9583 if (view->find_job == job)
9584 return;
9585
9586 ev_view_find_cancel (view);
9587 view->find_job = g_object_ref (job);
9588 view->find_page = view->current_page;
9589 view->find_result = 0;
9590
9591 g_signal_connect (job, "updated", G_CALLBACK (find_job_updated_cb), view);
9592 }
9593
9594 /**
9595 * ev_view_find_changed: (skip)
9596 * @view: an #EvView
9597 * @results: the results as returned by ev_job_find_get_results()
9598 * @page: page index
9599 *
9600 * Deprecated: 3.6: Use ev_view_find_started() instead
9601 */
9602 void
ev_view_find_changed(EvView * view,GList ** results,gint page)9603 ev_view_find_changed (EvView *view, GList **results, gint page)
9604 {
9605 g_return_if_fail (view->current_page >= 0);
9606
9607 view->find_pages = results;
9608 if (view->find_page == -1)
9609 view->find_page = view->current_page;
9610
9611 if (view->jump_to_find_result == TRUE) {
9612 jump_to_find_page (view, EV_VIEW_FIND_NEXT, 0);
9613 jump_to_find_result (view);
9614 }
9615
9616 if (view->find_page == page)
9617 gtk_widget_queue_draw (GTK_WIDGET (view));
9618 }
9619
9620 /**
9621 * ev_view_find_restart:
9622 * @view: an #EvView
9623 * @page: a page index
9624 *
9625 * Restart the current search operation from the given @page.
9626 *
9627 * Since: 3.12
9628 */
9629 void
ev_view_find_restart(EvView * view,gint page)9630 ev_view_find_restart (EvView *view,
9631 gint page)
9632 {
9633 if (!view->find_job)
9634 return;
9635
9636 view->find_page = page;
9637 view->find_result = 0;
9638 jump_to_find_page (view, EV_VIEW_FIND_NEXT, 0);
9639 jump_to_find_result (view);
9640 gtk_widget_queue_draw (GTK_WIDGET (view));
9641 }
9642
9643 void
ev_view_find_next(EvView * view)9644 ev_view_find_next (EvView *view)
9645 {
9646 gint n_results;
9647
9648 n_results = ev_view_find_get_n_results (view, view->find_page);
9649 view->find_result++;
9650
9651 if (view->find_result >= n_results) {
9652 view->find_result = 0;
9653 jump_to_find_page (view, EV_VIEW_FIND_NEXT, 1);
9654 } else if (view->find_page != view->current_page) {
9655 jump_to_find_page (view, EV_VIEW_FIND_NEXT, 0);
9656 }
9657
9658 jump_to_find_result (view);
9659 gtk_widget_queue_draw (GTK_WIDGET (view));
9660 }
9661
9662 void
ev_view_find_previous(EvView * view)9663 ev_view_find_previous (EvView *view)
9664 {
9665 view->find_result--;
9666
9667 if (view->find_result < 0) {
9668 jump_to_find_page (view, EV_VIEW_FIND_PREV, -1);
9669 view->find_result = MAX (0, ev_view_find_get_n_results (view, view->find_page) - 1);
9670 } else if (view->find_page != view->current_page) {
9671 jump_to_find_page (view, EV_VIEW_FIND_PREV, 0);
9672 }
9673
9674 jump_to_find_result (view);
9675 gtk_widget_queue_draw (GTK_WIDGET (view));
9676 }
9677
9678 /**
9679 * ev_view_find_set_result:
9680 * @view: a #EvView
9681 * @page:
9682 * @result:
9683 *
9684 * FIXME
9685 *
9686 * Since: 3.10
9687 */
9688 void
ev_view_find_set_result(EvView * view,gint page,gint result)9689 ev_view_find_set_result (EvView *view, gint page, gint result)
9690 {
9691 view->find_page = page;
9692 view->find_result = result;
9693 jump_to_find_page (view, EV_VIEW_FIND_NEXT, 0);
9694 jump_to_find_result (view);
9695 gtk_widget_queue_draw (GTK_WIDGET (view));
9696 }
9697
9698 void
ev_view_find_search_changed(EvView * view)9699 ev_view_find_search_changed (EvView *view)
9700 {
9701 /* search string has changed, focus on new search result */
9702 view->jump_to_find_result = TRUE;
9703 ev_view_find_cancel (view);
9704 }
9705
9706 void
ev_view_find_set_highlight_search(EvView * view,gboolean value)9707 ev_view_find_set_highlight_search (EvView *view, gboolean value)
9708 {
9709 view->highlight_find_results = value;
9710 gtk_widget_queue_draw (GTK_WIDGET (view));
9711 }
9712
9713 void
ev_view_find_cancel(EvView * view)9714 ev_view_find_cancel (EvView *view)
9715 {
9716 view->find_pages = NULL;
9717 view->find_page = -1;
9718 view->find_result = 0;
9719
9720 if (!view->find_job)
9721 return;
9722
9723 g_signal_handlers_disconnect_by_func (view->find_job, find_job_updated_cb, view);
9724 g_object_unref (view->find_job);
9725 view->find_job = NULL;
9726 }
9727
9728 /*** Synctex ***/
9729 void
ev_view_highlight_forward_search(EvView * view,EvSourceLink * link)9730 ev_view_highlight_forward_search (EvView *view,
9731 EvSourceLink *link)
9732 {
9733 EvMapping *mapping;
9734 gint page;
9735 GdkRectangle view_rect;
9736
9737 if (!ev_document_has_synctex (view->document))
9738 return;
9739
9740 mapping = ev_document_synctex_forward_search (view->document, link);
9741 if (!mapping)
9742 return;
9743
9744 if (view->synctex_result)
9745 g_free (view->synctex_result);
9746 view->synctex_result = mapping;
9747
9748 page = GPOINTER_TO_INT (mapping->data);
9749 ev_document_model_set_page (view->model, page);
9750
9751 _ev_view_transform_doc_rect_to_view_rect (view, page, &mapping->area, &view_rect);
9752 _ev_view_ensure_rectangle_is_visible (view, &view_rect);
9753 gtk_widget_queue_draw (GTK_WIDGET (view));
9754 }
9755
9756 /*** Selections ***/
9757 static gboolean
gdk_rectangle_point_in(GdkRectangle * rectangle,GdkPoint * point)9758 gdk_rectangle_point_in (GdkRectangle *rectangle,
9759 GdkPoint *point)
9760 {
9761 return rectangle->x <= point->x &&
9762 rectangle->y <= point->y &&
9763 point->x < rectangle->x + rectangle->width &&
9764 point->y < rectangle->y + rectangle->height;
9765 }
9766
9767 static inline gboolean
gdk_point_equal(GdkPoint * a,GdkPoint * b)9768 gdk_point_equal (GdkPoint *a,
9769 GdkPoint *b)
9770 {
9771 return a->x == b->x && a->y == b->y;
9772 }
9773
9774 static gboolean
get_selection_page_range(EvView * view,EvSelectionStyle style,GdkPoint * start,GdkPoint * stop,gint * first_page,gint * last_page)9775 get_selection_page_range (EvView *view,
9776 EvSelectionStyle style,
9777 GdkPoint *start,
9778 GdkPoint *stop,
9779 gint *first_page,
9780 gint *last_page)
9781 {
9782 gint start_page, end_page;
9783 gint first, last;
9784 gint i, n_pages;
9785 GtkBorder border;
9786
9787 n_pages = ev_document_get_n_pages (view->document);
9788
9789 if (gdk_point_equal (start, stop)) {
9790 start_page = view->start_page;
9791 end_page = view->end_page;
9792 } else if (view->continuous) {
9793 start_page = 0;
9794 end_page = n_pages - 1;
9795 } else if (is_dual_page (view, NULL)) {
9796 start_page = view->start_page;
9797 end_page = view->end_page;
9798 } else {
9799 start_page = view->current_page;
9800 end_page = view->current_page;
9801 }
9802
9803 first = -1;
9804 last = -1;
9805 compute_border (view, &border);
9806 for (i = start_page; i <= end_page; i++) {
9807 GdkRectangle page_area;
9808
9809 ev_view_get_page_extents_for_border (view, i, &border, &page_area);
9810 page_area.x -= border.left;
9811 page_area.y -= border.top;
9812 page_area.width += border.left + border.right;
9813 page_area.height += border.top + border.bottom;
9814 if (gdk_rectangle_point_in (&page_area, start) ||
9815 gdk_rectangle_point_in (&page_area, stop)) {
9816 if (first == -1)
9817 first = i;
9818 last = i;
9819 }
9820 }
9821
9822 if (first != -1 && last != -1) {
9823 *first_page = first;
9824 *last_page = last;
9825
9826 return TRUE;
9827 }
9828
9829 return FALSE;
9830 }
9831
9832 static GList *
compute_new_selection(EvView * view,EvSelectionStyle style,GdkPoint * start,GdkPoint * stop)9833 compute_new_selection (EvView *view,
9834 EvSelectionStyle style,
9835 GdkPoint *start,
9836 GdkPoint *stop)
9837 {
9838 int i, first, last;
9839 GtkBorder border;
9840 GList *list = NULL;
9841
9842 /* First figure out the range of pages the selection affects. */
9843 if (!get_selection_page_range (view, style, start, stop, &first, &last))
9844 return list;
9845
9846 /* Now create a list of EvViewSelection's for the affected
9847 * pages. This could be an empty list, a list of just one
9848 * page or a number of pages.*/
9849 compute_border (view, &border);
9850 for (i = first; i <= last; i++) {
9851 EvViewSelection *selection;
9852 GdkRectangle page_area;
9853 GdkPoint *point;
9854 gdouble width, height;
9855
9856 get_doc_page_size (view, i, &width, &height);
9857
9858 selection = g_slice_new0 (EvViewSelection);
9859 selection->page = i;
9860 selection->style = style;
9861 selection->rect.x1 = selection->rect.y1 = 0;
9862 selection->rect.x2 = width;
9863 selection->rect.y2 = height;
9864
9865 ev_view_get_page_extents_for_border (view, i, &border, &page_area);
9866 if (gdk_rectangle_point_in (&page_area, start))
9867 point = start;
9868 else
9869 point = stop;
9870
9871 if (i == first) {
9872 _ev_view_transform_view_point_to_doc_point (view, point,
9873 &page_area, &border,
9874 &selection->rect.x1,
9875 &selection->rect.y1);
9876 }
9877
9878 /* If the selection is contained within just one page,
9879 * make sure we don't write 'start' into both points
9880 * in selection->rect. */
9881 if (first == last)
9882 point = stop;
9883
9884 if (i == last) {
9885 _ev_view_transform_view_point_to_doc_point (view, point,
9886 &page_area, &border,
9887 &selection->rect.x2,
9888 &selection->rect.y2);
9889 }
9890
9891 list = g_list_prepend (list, selection);
9892 }
9893
9894 return g_list_reverse (list);
9895 }
9896
9897 /* This function takes the newly calculated list, and figures out which regions
9898 * have changed. It then queues a redraw appropriately.
9899 */
9900 static void
merge_selection_region(EvView * view,GList * new_list)9901 merge_selection_region (EvView *view,
9902 GList *new_list)
9903 {
9904 GList *old_list;
9905 GList *new_list_ptr, *old_list_ptr;
9906 GtkBorder border;
9907
9908 /* Update the selection */
9909 old_list = ev_pixbuf_cache_get_selection_list (view->pixbuf_cache);
9910 g_list_free_full (view->selection_info.selections, (GDestroyNotify)selection_free);
9911 view->selection_info.selections = new_list;
9912 ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, new_list);
9913 g_signal_emit (view, signals[SIGNAL_SELECTION_CHANGED], 0, NULL);
9914
9915 new_list_ptr = new_list;
9916 old_list_ptr = old_list;
9917
9918 compute_border (view, &border);
9919 while (new_list_ptr || old_list_ptr) {
9920 EvViewSelection *old_sel, *new_sel;
9921 int cur_page;
9922 cairo_region_t *region = NULL;
9923
9924 new_sel = (new_list_ptr) ? (new_list_ptr->data) : NULL;
9925 old_sel = (old_list_ptr) ? (old_list_ptr->data) : NULL;
9926
9927 /* Assume that the lists are in order, and we run through them
9928 * comparing them, one page at a time. We come out with the
9929 * first page we see. */
9930 if (new_sel && old_sel) {
9931 if (new_sel->page < old_sel->page) {
9932 new_list_ptr = new_list_ptr->next;
9933 old_sel = NULL;
9934 } else if (new_sel->page > old_sel->page) {
9935 old_list_ptr = old_list_ptr->next;
9936 new_sel = NULL;
9937 } else {
9938 new_list_ptr = new_list_ptr->next;
9939 old_list_ptr = old_list_ptr->next;
9940 }
9941 } else if (new_sel) {
9942 new_list_ptr = new_list_ptr->next;
9943 } else if (old_sel) {
9944 old_list_ptr = old_list_ptr->next;
9945 }
9946
9947 g_assert (new_sel || old_sel);
9948
9949 /* is the page we're looking at on the screen?*/
9950 cur_page = new_sel ? new_sel->page : old_sel->page;
9951 if (cur_page < view->start_page || cur_page > view->end_page)
9952 continue;
9953
9954 /* seed the cache with a new page. We are going to need the new
9955 * region too. */
9956 if (new_sel) {
9957 cairo_region_t *tmp_region;
9958
9959 tmp_region = ev_pixbuf_cache_get_selection_region (view->pixbuf_cache,
9960 cur_page,
9961 view->scale);
9962 if (tmp_region)
9963 new_sel->covered_region = cairo_region_reference (tmp_region);
9964 }
9965
9966 /* Now we figure out what needs redrawing */
9967 if (old_sel && new_sel) {
9968 if (old_sel->covered_region && new_sel->covered_region) {
9969 if (!cairo_region_equal (old_sel->covered_region, new_sel->covered_region)) {
9970 /* Anything that was previously or currently selected may
9971 * have changed */
9972 region = cairo_region_copy (old_sel->covered_region);
9973 cairo_region_union (region, new_sel->covered_region);
9974 }
9975 } else if (old_sel->covered_region) {
9976 region = cairo_region_reference (old_sel->covered_region);
9977 } else if (new_sel->covered_region) {
9978 region = cairo_region_reference (new_sel->covered_region);
9979 }
9980 } else if (old_sel && !new_sel) {
9981 if (old_sel->covered_region && !cairo_region_is_empty (old_sel->covered_region)) {
9982 region = cairo_region_reference (old_sel->covered_region);
9983 }
9984 } else if (!old_sel && new_sel) {
9985 if (new_sel->covered_region && !cairo_region_is_empty (new_sel->covered_region)) {
9986 region = cairo_region_reference (new_sel->covered_region);
9987 }
9988 } else {
9989 g_assert_not_reached ();
9990 }
9991
9992 /* Redraw the damaged region! */
9993 if (region) {
9994 GdkRectangle page_area;
9995 cairo_region_t *damage_region;
9996 gint i, n_rects;
9997
9998 ev_view_get_page_extents_for_border (view, cur_page, &border, &page_area);
9999
10000 damage_region = cairo_region_create ();
10001 /* Translate the region and grow it 2 pixels because for some zoom levels
10002 * the area actually drawn by cairo is larger than the selected region, due
10003 * to rounding errors or pixel alignment.
10004 */
10005 n_rects = cairo_region_num_rectangles (region);
10006 for (i = 0; i < n_rects; i++) {
10007 cairo_rectangle_int_t rect;
10008
10009 cairo_region_get_rectangle (region, i, &rect);
10010 rect.x += page_area.x + border.left - view->scroll_x - 2;
10011 rect.y += page_area.y + border.top - view->scroll_y - 2;
10012 rect.width += 4;
10013 rect.height += 4;
10014 cairo_region_union_rectangle (damage_region, &rect);
10015 }
10016 cairo_region_destroy (region);
10017
10018 gdk_window_invalidate_region (gtk_widget_get_window (GTK_WIDGET (view)),
10019 damage_region, TRUE);
10020 cairo_region_destroy (damage_region);
10021 }
10022 }
10023
10024 ev_view_check_cursor_blink (view);
10025
10026 /* Free the old list, now that we're done with it. */
10027 g_list_free_full (old_list, (GDestroyNotify)selection_free);
10028 }
10029
10030 static void
compute_selections(EvView * view,EvSelectionStyle style,GdkPoint * start,GdkPoint * stop)10031 compute_selections (EvView *view,
10032 EvSelectionStyle style,
10033 GdkPoint *start,
10034 GdkPoint *stop)
10035 {
10036 merge_selection_region (view, compute_new_selection (view, style, start, stop));
10037 }
10038
10039 /* Free's the selection. It's up to the caller to queue redraws if needed.
10040 */
10041 static void
selection_free(EvViewSelection * selection)10042 selection_free (EvViewSelection *selection)
10043 {
10044 if (selection->covered_region)
10045 cairo_region_destroy (selection->covered_region);
10046 g_slice_free (EvViewSelection, selection);
10047 }
10048
10049 static void
clear_selection(EvView * view)10050 clear_selection (EvView *view)
10051 {
10052 merge_selection_region (view, NULL);
10053 }
10054
10055 void
ev_view_select_all(EvView * view)10056 ev_view_select_all (EvView *view)
10057 {
10058 GList *selections = NULL;
10059 int n_pages, i;
10060
10061 /* Disable selection on rotated pages for the 0.4.0 series */
10062 if (view->rotation != 0)
10063 return;
10064
10065 n_pages = ev_document_get_n_pages (view->document);
10066 for (i = 0; i < n_pages; i++) {
10067 gdouble width, height;
10068 EvViewSelection *selection;
10069
10070 get_doc_page_size (view, i, &width, &height);
10071
10072 selection = g_slice_new0 (EvViewSelection);
10073 selection->page = i;
10074 selection->style = EV_SELECTION_STYLE_GLYPH;
10075 selection->rect.x1 = selection->rect.y1 = 0;
10076 selection->rect.x2 = width;
10077 selection->rect.y2 = height;
10078
10079 selections = g_list_prepend (selections, selection);
10080 }
10081
10082 merge_selection_region (view, g_list_reverse (selections));
10083 }
10084
10085 gboolean
ev_view_get_has_selection(EvView * view)10086 ev_view_get_has_selection (EvView *view)
10087 {
10088 return view->selection_info.selections != NULL;
10089 }
10090
10091 void
_ev_view_clear_selection(EvView * view)10092 _ev_view_clear_selection (EvView *view)
10093 {
10094 clear_selection (view);
10095 }
10096
10097 void
_ev_view_set_selection(EvView * view,GdkPoint * start_point,GdkPoint * end_point)10098 _ev_view_set_selection (EvView *view,
10099 GdkPoint *start_point,
10100 GdkPoint *end_point)
10101 {
10102 compute_selections (view, EV_SELECTION_STYLE_GLYPH, start_point, end_point);
10103 }
10104
10105 static char *
get_selected_text(EvView * view)10106 get_selected_text (EvView *view)
10107 {
10108 GString *text;
10109 GList *l;
10110 gchar *normalized_text;
10111
10112 text = g_string_new (NULL);
10113
10114 ev_document_doc_mutex_lock ();
10115
10116 for (l = view->selection_info.selections; l != NULL; l = l->next) {
10117 EvViewSelection *selection = (EvViewSelection *)l->data;
10118 EvPage *page;
10119 gchar *tmp;
10120
10121 page = ev_document_get_page (view->document, selection->page);
10122 tmp = ev_selection_get_selected_text (EV_SELECTION (view->document),
10123 page, selection->style,
10124 &(selection->rect));
10125 g_object_unref (page);
10126 g_string_append (text, tmp);
10127 g_free (tmp);
10128 }
10129
10130 ev_document_doc_mutex_unlock ();
10131
10132 /* For copying text from the document to the clipboard, we want a normalization
10133 * that preserves 'canonical equivalence' i.e. that text after normalization
10134 * is not visually different than the original text. Issue #1085 */
10135 normalized_text = g_utf8_normalize (text->str, text->len, G_NORMALIZE_NFC);
10136 g_string_free (text, TRUE);
10137 return normalized_text;
10138 }
10139
10140 static void
ev_view_clipboard_copy(EvView * view,const gchar * text)10141 ev_view_clipboard_copy (EvView *view,
10142 const gchar *text)
10143 {
10144 GtkClipboard *clipboard;
10145
10146 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view),
10147 GDK_SELECTION_CLIPBOARD);
10148 gtk_clipboard_set_text (clipboard, text, -1);
10149 }
10150
10151 void
ev_view_copy(EvView * ev_view)10152 ev_view_copy (EvView *ev_view)
10153 {
10154 char *text;
10155
10156 if (!EV_IS_SELECTION (ev_view->document))
10157 return;
10158
10159 text = get_selected_text (ev_view);
10160 ev_view_clipboard_copy (ev_view, text);
10161 g_free (text);
10162 }
10163
10164 static void
ev_view_primary_get_cb(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer data)10165 ev_view_primary_get_cb (GtkClipboard *clipboard,
10166 GtkSelectionData *selection_data,
10167 guint info,
10168 gpointer data)
10169 {
10170 EvView *ev_view = EV_VIEW (data);
10171
10172 if (ev_view->link_selected) {
10173 gtk_selection_data_set_text (selection_data,
10174 ev_link_action_get_uri (ev_view->link_selected),
10175 -1);
10176 } else if (EV_IS_SELECTION (ev_view->document) &&
10177 ev_view->selection_info.selections) {
10178 gchar *text;
10179
10180 text = get_selected_text (ev_view);
10181 if (text) {
10182 gtk_selection_data_set_text (selection_data, text, -1);
10183 g_free (text);
10184 }
10185 }
10186 }
10187
10188 static void
ev_view_primary_clear_cb(GtkClipboard * clipboard,gpointer data)10189 ev_view_primary_clear_cb (GtkClipboard *clipboard,
10190 gpointer data)
10191 {
10192 EvView *view = EV_VIEW (data);
10193
10194 clear_selection (view);
10195 clear_link_selected (view);
10196 }
10197
10198 static void
ev_view_update_primary_selection(EvView * ev_view)10199 ev_view_update_primary_selection (EvView *ev_view)
10200 {
10201 GtkClipboard *clipboard;
10202
10203 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
10204 GDK_SELECTION_PRIMARY);
10205
10206 if (ev_view->selection_info.selections || ev_view->link_selected) {
10207 GtkTargetList *target_list;
10208 GtkTargetEntry *targets;
10209 int n_targets;
10210
10211 target_list = gtk_target_list_new (NULL, 0);
10212 gtk_target_list_add_text_targets (target_list, 0);
10213 targets = gtk_target_table_new_from_list (target_list, &n_targets);
10214 gtk_target_list_unref (target_list);
10215
10216 gtk_clipboard_set_with_owner (clipboard,
10217 targets, n_targets,
10218 ev_view_primary_get_cb,
10219 ev_view_primary_clear_cb,
10220 G_OBJECT (ev_view));
10221
10222 gtk_target_table_free (targets, n_targets);
10223 } else {
10224 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
10225 gtk_clipboard_clear (clipboard);
10226 }
10227 }
10228
10229 static void
clear_link_selected(EvView * view)10230 clear_link_selected (EvView *view)
10231 {
10232 if (view->link_selected) {
10233 g_object_unref (view->link_selected);
10234 view->link_selected = NULL;
10235 }
10236 }
10237
10238 void
ev_view_copy_link_address(EvView * view,EvLinkAction * action)10239 ev_view_copy_link_address (EvView *view,
10240 EvLinkAction *action)
10241 {
10242 clear_link_selected (view);
10243
10244 ev_view_clipboard_copy (view, ev_link_action_get_uri (action));
10245
10246 view->link_selected = g_object_ref (action);
10247 ev_view_update_primary_selection (view);
10248 }
10249
10250 /*** Cursor operations ***/
10251 static void
ev_view_set_cursor(EvView * view,EvViewCursor new_cursor)10252 ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
10253 {
10254 GdkCursor *cursor = NULL;
10255 GtkWidget *widget;
10256 GdkWindow *window;
10257
10258 if (view->cursor == new_cursor) {
10259 return;
10260 }
10261
10262 view->cursor = new_cursor;
10263
10264 window = gtk_widget_get_window (GTK_WIDGET (view));
10265 widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
10266 cursor = ev_view_cursor_new (gtk_widget_get_display (widget), new_cursor);
10267 gdk_window_set_cursor (window, cursor);
10268 gdk_display_flush (gtk_widget_get_display (widget));
10269 if (cursor)
10270 g_object_unref (cursor);
10271 }
10272
10273 void
ev_view_hide_cursor(EvView * view)10274 ev_view_hide_cursor (EvView *view)
10275 {
10276 ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN);
10277 }
10278
10279 void
ev_view_show_cursor(EvView * view)10280 ev_view_show_cursor (EvView *view)
10281 {
10282 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
10283 }
10284
10285 gboolean
ev_view_next_page(EvView * view)10286 ev_view_next_page (EvView *view)
10287 {
10288 gint next_page;
10289
10290 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
10291
10292 next_page = go_to_next_page (view, view->current_page);
10293 if (next_page == -1)
10294 return FALSE;
10295
10296 ev_document_model_set_page (view->model, next_page);
10297
10298 return TRUE;
10299 }
10300
10301 gboolean
ev_view_previous_page(EvView * view)10302 ev_view_previous_page (EvView *view)
10303 {
10304 gint prev_page;
10305
10306 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
10307
10308 prev_page = go_to_previous_page (view, view->current_page);
10309 if (prev_page == -1)
10310 return FALSE;
10311
10312 ev_document_model_set_page (view->model, prev_page);
10313
10314 return TRUE;
10315 }
10316
10317 void
ev_view_set_allow_links_change_zoom(EvView * view,gboolean allowed)10318 ev_view_set_allow_links_change_zoom (EvView *view, gboolean allowed)
10319 {
10320 g_return_if_fail (EV_IS_VIEW (view));
10321
10322 view->allow_links_change_zoom = allowed;
10323 }
10324
10325 gboolean
ev_view_get_allow_links_change_zoom(EvView * view)10326 ev_view_get_allow_links_change_zoom (EvView *view)
10327 {
10328 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
10329
10330 return view->allow_links_change_zoom;
10331 }
10332