ev-view.c 236 KB
Newer Older
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* this file is part of evince, a gnome document viewer
 *
 *  Copyright (C) 2004 Red Hat, Inc
 *
 * Evince is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Evince is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * 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 22
#include "config.h"

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 34
#include "ev-document-images.h"
#include "ev-document-links.h"
35
#include "ev-document-layers.h"
36
#include "ev-document-misc.h"
37
#include "ev-pixbuf-cache.h"
38
#include "ev-page-cache.h"
39
#include "ev-view-marshal.h"
40 41
#include "ev-document-annotations.h"
#include "ev-annotation-window.h"
42
#include "ev-view.h"
43
#include "ev-view-accessible.h"
44
#include "ev-view-private.h"
45
#include "ev-view-type-builtins.h"
46
#include "ev-debug.h"
47

48
enum {
49
	SIGNAL_SCROLL,
50
	SIGNAL_HANDLE_LINK,
51
	SIGNAL_EXTERNAL_LINK,
52
	SIGNAL_POPUP_MENU,
53
	SIGNAL_SELECTION_CHANGED,
54
	SIGNAL_SYNC_SOURCE,
55
	SIGNAL_ANNOT_ADDED,
56
	SIGNAL_ANNOT_REMOVED,
57
	SIGNAL_LAYERS_CHANGED,
58
	SIGNAL_MOVE_CURSOR,
59
	SIGNAL_CURSOR_MOVED,
60
	SIGNAL_ACTIVATE,
61
	N_SIGNALS
62 63
};

64 65 66 67 68 69
enum {
	TARGET_DND_URI,
	TARGET_DND_TEXT,
	TARGET_DND_IMAGE
};

70 71
enum {
	PROP_0,
72
	PROP_IS_LOADING,
73 74 75
	PROP_HADJUSTMENT,
	PROP_VADJUSTMENT,
	PROP_HSCROLL_POLICY,
76 77 78
	PROP_VSCROLL_POLICY,
	PROP_CAN_ZOOM_IN,
	PROP_CAN_ZOOM_OUT
79 80
};

81 82
static guint signals[N_SIGNALS];

83 84 85 86 87
typedef enum {
	EV_VIEW_FIND_NEXT,
	EV_VIEW_FIND_PREV
} EvViewFindDirection;

88 89 90 91 92 93 94 95 96 97 98 99
typedef struct {
	GtkWidget  *widget;

	/* View coords */
	gint        x;
	gint        y;

	/* Document */
	guint       page;
	EvRectangle doc_rect;
} EvViewChild;

100
#define MIN_SCALE 0.2
101 102 103
#define ZOOM_IN_FACTOR  1.2
#define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)

104 105
#define SCROLL_TIME 150

106 107
#define DEFAULT_PIXBUF_CACHE_SIZE 52428800 /* 50MB */

108
#define EV_STYLE_CLASS_DOCUMENT_PAGE "document-page"
109
#define EV_STYLE_CLASS_INVERTED      "inverted"
110

111 112 113 114 115 116 117 118
/*** Scrolling ***/
static void       view_update_range_and_current_page         (EvView             *view);
static void       ensure_rectangle_is_visible                (EvView             *view,
							      GdkRectangle       *rect);

/*** Geometry computations ***/
static void       compute_border                             (EvView             *view,
							      GtkBorder          *border);
119 120 121
static void       get_page_y_offset                          (EvView             *view,
							      int                 page,
							      int                *y_offset);
122 123 124 125 126 127 128
static void       find_page_at_location                      (EvView             *view,
							      gdouble             x,
							      gdouble             y,
							      gint               *page,
							      gint               *x_offset,
							      gint               *y_offset);
/*** Hyperrefs ***/
129 130 131
static EvLink *   ev_view_get_link_at_location 		     (EvView             *view,
				  	         	      gdouble             x,
		            				      gdouble             y);
132
static char*      tip_from_link                              (EvView             *view,
133
							      EvLink             *link);
134 135 136 137 138
/*** Forms ***/
static EvFormField *ev_view_get_form_field_at_location       (EvView             *view,
							       gdouble            x,
							       gdouble            y);

139 140 141 142 143 144 145 146
/*** Annotations ***/
static EvAnnotation *ev_view_get_annotation_at_location      (EvView             *view,
							      gdouble             x,
							      gdouble             y);
static void          show_annotation_windows                 (EvView             *view,
							      gint                page);
static void          hide_annotation_windows                 (EvView             *view,
							      gint                page);
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
/*** GtkWidget implementation ***/
static void       ev_view_size_request_continuous_dual_page  (EvView             *view,
							      GtkRequisition     *requisition);
static void       ev_view_size_request_continuous            (EvView             *view,
							      GtkRequisition     *requisition);
static void       ev_view_size_request_dual_page             (EvView             *view,
							      GtkRequisition     *requisition);
static void       ev_view_size_request_single_page           (EvView             *view,
							      GtkRequisition     *requisition);
static void       ev_view_size_request                       (GtkWidget          *widget,
							      GtkRequisition     *requisition);
static void       ev_view_size_allocate                      (GtkWidget          *widget,
							      GtkAllocation      *allocation);
static gboolean   ev_view_scroll_event                       (GtkWidget          *widget,
							      GdkEventScroll     *event);
162
static gboolean   ev_view_draw                               (GtkWidget          *widget,
163
                                                              cairo_t            *cr);
164
static gboolean   ev_view_popup_menu                         (GtkWidget 	 *widget);
165 166 167 168 169 170
static gboolean   ev_view_button_press_event                 (GtkWidget          *widget,
							      GdkEventButton     *event);
static gboolean   ev_view_motion_notify_event                (GtkWidget          *widget,
							      GdkEventMotion     *event);
static gboolean   ev_view_button_release_event               (GtkWidget          *widget,
							      GdkEventButton     *event);
171 172
static gboolean   ev_view_enter_notify_event                 (GtkWidget          *widget,
							      GdkEventCrossing   *event);
173 174
static gboolean   ev_view_leave_notify_event                 (GtkWidget          *widget,
							      GdkEventCrossing   *event);
175
static void       ev_view_style_updated                      (GtkWidget          *widget);
176
static void       ev_view_remove_all                         (EvView             *view);
177

178 179
static AtkObject *ev_view_get_accessible                     (GtkWidget *widget);

180 181
/*** Drawing ***/
static void       highlight_find_results                     (EvView             *view,
182
                                                              cairo_t            *cr,
183
							      int                 page);
184
static void       highlight_forward_search_results           (EvView             *view,
185
                                                              cairo_t            *cr,
186
							      int                 page);
187 188
static void       draw_one_page                              (EvView             *view,
							      gint                page,
189
							      cairo_t            *cr,
190 191
							      GdkRectangle       *page_area,
							      GtkBorder          *border,
192 193
							      GdkRectangle       *expose_area,
							      gboolean		 *page_ready);
194 195
static void       ev_view_reload_page                        (EvView             *view,
							      gint                page,
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
196
							      cairo_region_t     *region);
197
/*** Callbacks ***/
198
static void       ev_view_change_page                        (EvView             *view,
199
							      gint                new_page);
200
static void       job_finished_cb                            (EvPixbufCache      *pixbuf_cache,
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
201
							      cairo_region_t     *region,
202
							      EvView             *view);
203 204 205
static void       ev_view_page_changed_cb                    (EvDocumentModel    *model,
							      gint                old_page,
							      gint                new_page,
206 207 208 209 210
							      EvView             *view);
static void       on_adjustment_value_changed                (GtkAdjustment      *adjustment,
							      EvView             *view);
/*** GObject ***/
static void       ev_view_finalize                           (GObject            *object);
211
static void       ev_view_dispose                            (GObject            *object);
212 213 214 215
static void       ev_view_class_init                         (EvViewClass        *class);
static void       ev_view_init                               (EvView             *view);

/*** Zoom and sizing ***/
216 217 218
static double   zoom_for_size_fit_width	 		     (gdouble doc_width,
							      gdouble doc_height,
	    						      int     target_width,
219
							      int     target_height);
220 221 222
static double   zoom_for_size_fit_height		     (gdouble doc_width,
			  				      gdouble doc_height,
							      int     target_width,
223
							      int     target_height);
224
static double	zoom_for_size_fit_page 			     (gdouble doc_width,
225 226
							      gdouble doc_height,
							      int     target_width,
227
							      int     target_height);
228 229 230 231 232
static double   zoom_for_size_automatic                      (GdkScreen *screen,
							      gdouble    doc_width,
							      gdouble    doc_height,
							      int        target_width,
							      int        target_height);
233 234
static void     ev_view_zoom                                 (EvView *view,
                                                              gdouble factor);
235 236 237
static void     ev_view_zoom_for_size                        (EvView *view,
							      int     width,
							      int     height);
238 239
static void	ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
							        int     width,
240
						     	        int     height);
241 242
static void	ev_view_zoom_for_size_continuous	       (EvView *view,
					    		        int     width,
243
								int     height);
244 245
static void 	ev_view_zoom_for_size_dual_page 	       (EvView *view,
						    		int     width,
246
								int     height);
247 248
static void	ev_view_zoom_for_size_single_page 	       (EvView *view,
				    			        int     width,
249
					    			int     height);
250 251 252
/*** Cursors ***/
static void       ev_view_set_cursor                         (EvView             *view,
							      EvViewCursor        new_cursor);
253 254 255
static void       ev_view_handle_cursor_over_xy              (EvView *view,
							      gint x,
							      gint y);
256

257
/*** Find ***/
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
258 259 260 261 262
static gint         ev_view_find_get_n_results               (EvView             *view,
							      gint                page);
static EvRectangle *ev_view_find_get_result                  (EvView             *view,
							      gint                page,
							      gint                result);
263
static void       jump_to_find_result                        (EvView             *view);
264
static void       jump_to_find_page                          (EvView             *view, 
265 266
							      EvViewFindDirection direction,
							      gint                shift);
267 268
/*** Selection ***/
static void       compute_selections                         (EvView             *view,
269
							      EvSelectionStyle    style,
270 271
							      GdkPoint           *start,
							      GdkPoint           *stop);
272 273 274
static void       extend_selection                           (EvView             *view,
							      GdkPoint           *start,
							      GdkPoint           *stop);
275
static void       clear_selection                            (EvView             *view);
276
static void       clear_link_selected                        (EvView             *view);
277
static void       selection_free                             (EvViewSelection    *selection);
278 279 280 281 282 283 284 285 286
static char*      get_selected_text                          (EvView             *ev_view);
static void       ev_view_primary_get_cb                     (GtkClipboard       *clipboard,
							      GtkSelectionData   *selection_data,
							      guint               info,
							      gpointer            data);
static void       ev_view_primary_clear_cb                   (GtkClipboard       *clipboard,
							      gpointer            data);
static void       ev_view_update_primary_selection           (EvView             *ev_view);

287 288 289
/*** Caret navigation ***/
static void       ev_view_check_cursor_blink                 (EvView             *ev_view);

290
G_DEFINE_TYPE_WITH_CODE (EvView, ev_view, GTK_TYPE_CONTAINER,
291
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
292

293 294 295 296
/* HeightToPage cache */
#define EV_HEIGHT_TO_PAGE_CACHE_KEY "ev-height-to-page-cache"

static void
297 298
ev_view_build_height_to_page_cache (EvView		*view,
                                    EvHeightToPageCache *cache)
299
{
300
	gboolean swap, uniform;
301 302 303 304 305
	int i;
	double uniform_height, page_height, next_page_height;
	double saved_height;
	gdouble u_width, u_height;
	gint n_pages;
306
	EvDocument *document = view->document;
307

308
	swap = (view->rotation == 90 || view->rotation == 270);
309 310 311 312 313 314 315

	uniform = ev_document_is_page_size_uniform (document);
	n_pages = ev_document_get_n_pages (document);

	g_free (cache->height_to_page);
	g_free (cache->dual_height_to_page);

316
	cache->rotation = view->rotation;
317
	cache->dual_even_left = view->dual_even_left;
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
	cache->height_to_page = g_new0 (gdouble, n_pages + 1);
	cache->dual_height_to_page = g_new0 (gdouble, n_pages + 2);

	if (uniform)
		ev_document_get_page_size (document, 0, &u_width, &u_height);

	saved_height = 0;
	for (i = 0; i <= n_pages; i++) {
		if (uniform) {
			uniform_height = swap ? u_width : u_height;
			cache->height_to_page[i] = i * uniform_height;
		} else {
			if (i < n_pages) {
				gdouble w, h;

				ev_document_get_page_size (document, i, &w, &h);
				page_height = swap ? w : h;
			} else {
				page_height = 0;
			}
			cache->height_to_page[i] = saved_height;
			saved_height += page_height;
		}
	}

343
	if (cache->dual_even_left && !uniform) {
344 345 346 347 348 349 350 351
		gdouble w, h;

		ev_document_get_page_size (document, 0, &w, &h);
		saved_height = swap ? w : h;
	} else {
		saved_height = 0;
	}

352
	for (i = cache->dual_even_left; i < n_pages + 2; i += 2) {
353 354
    		if (uniform) {
			uniform_height = swap ? u_width : u_height;
355
			cache->dual_height_to_page[i] = ((i + cache->dual_even_left) / 2) * uniform_height;
356
			if (i + 1 < n_pages + 2)
357
				cache->dual_height_to_page[i + 1] = ((i + cache->dual_even_left) / 2) * uniform_height;
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
		} else {
			if (i + 1 < n_pages) {
				gdouble w, h;

				ev_document_get_page_size (document, i + 1, &w, &h);
				next_page_height = swap ? w : h;
			} else {
				next_page_height = 0;
			}

			if (i < n_pages) {
				gdouble w, h;

				ev_document_get_page_size (document, i, &w, &h);
				page_height = swap ? w : h;
			} else {
				page_height = 0;
			}

			if (i + 1 < n_pages + 2) {
				cache->dual_height_to_page[i] = saved_height;
				cache->dual_height_to_page[i + 1] = saved_height;
				saved_height += MAX(page_height, next_page_height);
			} else {
				cache->dual_height_to_page[i] = saved_height;
			}
		}
	}
}

static void
ev_height_to_page_cache_free (EvHeightToPageCache *cache)
{
	if (cache->height_to_page) {
		g_free (cache->height_to_page);
		cache->height_to_page = NULL;
	}

	if (cache->dual_height_to_page) {
		g_free (cache->dual_height_to_page);
		cache->dual_height_to_page = NULL;
	}
	g_free (cache);
}

static EvHeightToPageCache *
ev_view_get_height_to_page_cache (EvView *view)
{
	EvHeightToPageCache *cache;

	if (!view->document)
		return NULL;

	cache = g_object_get_data (G_OBJECT (view->document), EV_HEIGHT_TO_PAGE_CACHE_KEY);
	if (!cache) {
		cache = g_new0 (EvHeightToPageCache, 1);
414
		ev_view_build_height_to_page_cache (view, cache);
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
		g_object_set_data_full (G_OBJECT (view->document),
					EV_HEIGHT_TO_PAGE_CACHE_KEY,
					cache,
					(GDestroyNotify)ev_height_to_page_cache_free);
	}

	return cache;
}

static void
ev_view_get_height_to_page (EvView *view,
			    gint    page,
			    gint   *height,
			    gint   *dual_height)
{
430
	EvHeightToPageCache *cache = NULL;
431 432 433 434 435
	gdouble h, dh;

	if (!view->height_to_page_cache)
		return;

436
	cache = view->height_to_page_cache;
437 438
	if (cache->rotation != view->rotation ||
	    cache->dual_even_left != view->dual_even_left) {
439
		ev_view_build_height_to_page_cache (view, cache);
440
	}
441 442 443
	h = cache->height_to_page[page];
	dh = cache->dual_height_to_page[page];

444 445 446 447 448 449 450
	if (height)
		*height = (gint)(h * view->scale + 0.5);

	if (dual_height)
		*dual_height = (gint)(dh * view->scale + 0.5);
}

451 452 453 454 455 456 457
static gint
ev_view_get_scrollbar_size (EvView        *view,
			    GtkOrientation orientation)
{
	GtkWidget *widget = GTK_WIDGET (view);
	GtkWidget *sb;
	GtkWidget *swindow = gtk_widget_get_parent (GTK_WIDGET (view));
458
	GtkAllocation allocation;
459 460 461 462 463 464
	GtkRequisition req;
	gint spacing;

	if (!GTK_IS_SCROLLED_WINDOW (swindow))
		return 0;

465 466
	gtk_widget_get_allocation (widget, &allocation);

467
	if (orientation == GTK_ORIENTATION_VERTICAL) {
468
		if (allocation.height >= view->requisition.height)
469 470 471 472
			sb = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (swindow));
		else
			return 0;
	} else {
473
		if (allocation.width >= view->requisition.width)
474 475 476 477 478 479
			sb = gtk_scrolled_window_get_hscrollbar (GTK_SCROLLED_WINDOW (swindow));
		else
			return 0;
	}

	gtk_widget_style_get (swindow, "scrollbar_spacing", &spacing, NULL);
480
	gtk_widget_get_preferred_size (sb, &req, NULL);
481 482 483 484

	return (orientation == GTK_ORIENTATION_VERTICAL ? req.width : req.height) + spacing;
}

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
static gboolean
is_dual_page (EvView   *view,
	      gboolean *odd_left_out)
{
	gboolean dual = FALSE;
	gboolean odd_left = FALSE;

	switch (view->page_layout) {
	case EV_PAGE_LAYOUT_AUTOMATIC: {
		GdkScreen    *screen;
		double        scale;
		double        doc_width;
		double        doc_height;
		GtkAllocation allocation;

		screen = gtk_widget_get_screen (GTK_WIDGET (view));
		scale = ev_document_misc_get_screen_dpi (screen) / 72.0;

		ev_document_get_max_page_size (view->document, &doc_width, &doc_height);
		gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);

		/* If the width is ok and the height is pretty close, try to fit it in */
		if (ev_document_get_n_pages (view->document) > 1 &&
		    doc_width < doc_height &&
		    allocation.width > (2 * doc_width * scale) &&
		    allocation.height > (doc_height * scale * 0.9)) {
			odd_left = !view->dual_even_left;
			dual = TRUE;
		}
	}
		break;
	case EV_PAGE_LAYOUT_DUAL:
		odd_left = !view->dual_even_left;
		dual = TRUE;
		break;
	case EV_PAGE_LAYOUT_SINGLE:
		break;
	default:
		g_assert_not_reached ();
	}

	if (odd_left_out)
		*odd_left_out = odd_left;

	return dual;
}

532
static void
533 534 535 536
scroll_to_point (EvView        *view,
		 gdouble        x,
		 gdouble        y,
		 GtkOrientation orientation)
537
{
538 539 540
	gdouble page_size;
	gdouble upper, lower;

541
	if (orientation == GTK_ORIENTATION_VERTICAL) {
542 543 544 545
		page_size = gtk_adjustment_get_page_size (view->vadjustment);
		upper = gtk_adjustment_get_upper (view->vadjustment);
		lower = gtk_adjustment_get_lower (view->vadjustment);

546 547
		if (view->continuous) {
    			gtk_adjustment_clamp_page (view->vadjustment,
548
						   y, y + page_size);
549 550
		} else {
			gtk_adjustment_set_value (view->vadjustment,
551
						  CLAMP (y, lower, upper - page_size));
552 553
		}
	} else {
554 555 556 557
		page_size = gtk_adjustment_get_page_size (view->hadjustment);
		upper = gtk_adjustment_get_upper (view->hadjustment);
		lower = gtk_adjustment_get_lower (view->hadjustment);

558
		if (is_dual_page (view, NULL)) {
559
			gtk_adjustment_clamp_page (view->hadjustment, x,
560
						   x + page_size);
561 562
		} else {
			gtk_adjustment_set_value (view->hadjustment,
563
						  CLAMP (x, lower, upper - page_size));
564 565
		}
	}
566 567
}

568 569 570 571 572 573 574 575 576 577 578 579 580
static void
ev_view_scroll_to_page_position (EvView *view, GtkOrientation orientation)
{
	gdouble x, y;

	if (!view->document)
		return;

	if ((orientation == GTK_ORIENTATION_VERTICAL && view->pending_point.y == 0) ||
	    (orientation == GTK_ORIENTATION_HORIZONTAL && view->pending_point.x == 0)) {
		GdkRectangle page_area;
		GtkBorder    border;

581
		ev_view_get_page_extents (view, view->current_page, &page_area, &border);
582 583 584 585 586
		x = page_area.x;
		y = page_area.y;
	} else {
		GdkPoint view_point;

587 588
		_ev_view_transform_doc_point_to_view_point (view, view->current_page,
							    &view->pending_point, &view_point);
589 590 591 592 593 594 595
		x = view_point.x;
		y = view_point.y;
	}

	scroll_to_point (view, x, y, orientation);
}

596
static void
597 598
ev_view_set_adjustment_values (EvView         *view,
			       GtkOrientation  orientation)
599 600 601
{
	GtkWidget *widget = GTK_WIDGET (view);
	GtkAdjustment *adjustment;
602 603 604 605 606 607
	GtkAllocation allocation;
	int req_size;
	int alloc_size;
	gdouble page_size;
	gdouble value;
	gdouble upper;
608 609 610
	double factor;
	gint new_value;

611 612
	gtk_widget_get_allocation (widget, &allocation);

613
	if (orientation == GTK_ORIENTATION_HORIZONTAL)  {
614 615
		req_size = view->requisition.width;
		alloc_size = allocation.width;
616 617
		adjustment = view->hadjustment;
	} else {
618 619
		req_size = view->requisition.height;
		alloc_size = allocation.height;
620 621 622 623 624
		adjustment = view->vadjustment;
	}

	if (!adjustment)
		return;
625

626
	factor = 1.0;
627 628 629 630
	value = gtk_adjustment_get_value (adjustment);
	upper = gtk_adjustment_get_upper (adjustment);
	page_size = gtk_adjustment_get_page_size (adjustment);

631 632
	if (upper != .0) {
		switch (view->pending_scroll) {
633
    	        case SCROLL_TO_KEEP_POSITION:
634
    	        case SCROLL_TO_FIND_LOCATION:
635
			factor = value / upper;
636
			break;
637
    	        case SCROLL_TO_PAGE_POSITION:
638
			break;
639
    	        case SCROLL_TO_CENTER:
640
			factor = (value + page_size * 0.5) / upper;
641
			break;
642
		}
643
	}
644

645 646 647 648 649 650 651 652
	upper = MAX (alloc_size, req_size);
	page_size = alloc_size;

	gtk_adjustment_set_page_size (adjustment, page_size);
	gtk_adjustment_set_step_increment (adjustment, alloc_size * 0.1);
	gtk_adjustment_set_page_increment (adjustment, alloc_size * 0.9);
	gtk_adjustment_set_lower (adjustment, 0);
	gtk_adjustment_set_upper (adjustment, upper);
653

654
	/*
655 656
	 * We add 0.5 to the values before to average out our rounding errors.
	 */
657
	switch (view->pending_scroll) {
658
    	        case SCROLL_TO_KEEP_POSITION:
659
    	        case SCROLL_TO_FIND_LOCATION:
660
			new_value = CLAMP (upper * factor + 0.5, 0, upper - page_size);
661
			gtk_adjustment_set_value (adjustment, (int)new_value);
662
			break;
663
    	        case SCROLL_TO_PAGE_POSITION:
664
			ev_view_scroll_to_page_position (view, orientation);
665
			break;
666
    	        case SCROLL_TO_CENTER:
667 668
			new_value = CLAMP (upper * factor - page_size * 0.5 + 0.5,
					   0, upper - page_size);
669
			gtk_adjustment_set_value (adjustment, (int)new_value);
670
			break;
671 672 673 674 675
	}

	gtk_adjustment_changed (adjustment);
}

676 677 678
static void
view_update_range_and_current_page (EvView *view)
{
679 680
	gint start = view->start_page;
	gint end = view->end_page;
681
	gboolean odd_left;
682 683 684 685 686

	if (ev_document_get_n_pages (view->document) <= 0 ||
	    !ev_document_check_dimensions (view->document))
		return;

687
	if (view->continuous) {
688
		GdkRectangle current_area, unused, page_area;
689
		GtkBorder border;
690
		gboolean found = FALSE;
691
		gint area_max = -1, area;
692
		gint best_current_page = -1;
693
		int i, j = 0;
694

695 696
		if (!(view->vadjustment && view->hadjustment))
			return;
697

698 699 700 701
		current_area.x = gtk_adjustment_get_value (view->hadjustment);
		current_area.width = gtk_adjustment_get_page_size (view->hadjustment);
		current_area.y = gtk_adjustment_get_value (view->vadjustment);
		current_area.height = gtk_adjustment_get_page_size (view->vadjustment);
702

703
		for (i = 0; i < ev_document_get_n_pages (view->document); i++) {
704

705
			ev_view_get_page_extents (view, i, &page_area, &border);
706

707
			if (gdk_rectangle_intersect (&current_area, &page_area, &unused)) {
708 709 710 711
				area = unused.width * unused.height;

				if (!found) {
					area_max = area;
712 713
					view->start_page = i;
					found = TRUE;
714 715 716 717 718
					best_current_page = i;
				}
				if (area > area_max) {
					best_current_page = (area == area_max) ? MIN (i, best_current_page) : i;
					area_max = area;
719
				}
720

721
				view->end_page = i;
722
				j = 0;
723
			} else if (found && view->current_page <= view->end_page) {
724
				if (is_dual_page (view, NULL) && j < 1) {
725 726 727 728 729 730
					/* In dual mode  we stop searching
					 * after two consecutive non-visible pages.
					 */
					j++;
					continue;
				}
731 732 733
				break;
			}
		}
734

735 736
		if (view->pending_scroll == SCROLL_TO_KEEP_POSITION ||
		    view->pending_scroll == SCROLL_TO_FIND_LOCATION) {
737 738
			best_current_page = MAX (best_current_page, view->start_page);

739
			if (best_current_page >= 0 && view->current_page != best_current_page) {
740
				view->current_page = best_current_page;
741
				ev_view_set_loading (view, FALSE);
742 743 744
				ev_document_model_set_page (view->model, best_current_page);
			}
		}
745 746
	} else if (is_dual_page (view, &odd_left)) {
		if (view->current_page % 2 == !odd_left) {
747
			view->start_page = view->current_page;
748
			if (view->current_page + 1 < ev_document_get_n_pages (view->document))
749
				view->end_page = view->start_page + 1;
750
			else
751 752 753 754 755 756
				view->end_page = view->start_page;
		} else {
			if (view->current_page < 1)
				view->start_page = view->current_page;
			else
				view->start_page = view->current_page - 1;
757 758
			view->end_page = view->current_page;
		}
759 760 761
	} else {
		view->start_page = view->current_page;
		view->end_page = view->current_page;
762 763
	}

764 765 766
	if (view->start_page == -1 || view->end_page == -1)
		return;

767 768 769
	if (start != view->start_page || end != view->end_page) {
		gint i;

770
		for (i = start; i < view->start_page && start != -1; i++) {
771 772 773
			hide_annotation_windows (view, i);
		}

774
		for (i = end; i > view->end_page && end != -1; i--) {
775 776
			hide_annotation_windows (view, i);
		}
777 778

		ev_view_check_cursor_blink (view);
779
	}
780

781 782 783
	ev_page_cache_set_page_range (view->page_cache,
				      view->start_page,
				      view->end_page);
784 785 786 787
	ev_pixbuf_cache_set_page_range (view->pixbuf_cache,
					view->start_page,
					view->end_page,
					view->selection_info.selections);
788 789 790 791
	if (view->accessible)
		ev_view_accessible_set_page_range (EV_VIEW_ACCESSIBLE (view->accessible),
						   view->start_page,
						   view->end_page);
792 793 794

	if (ev_pixbuf_cache_get_surface (view->pixbuf_cache, view->current_page))
	    gtk_widget_queue_draw (GTK_WIDGET (view));
795 796
}

797
static void
798 799 800
ev_view_set_scroll_adjustment (EvView         *view,
			       GtkOrientation  orientation,
			       GtkAdjustment  *adjustment)
801 802
{
	GtkAdjustment **to_set;
803
	const gchar    *prop_name;
804

805
	if (orientation == GTK_ORIENTATION_HORIZONTAL) {
806
		to_set = &view->hadjustment;
807 808
		prop_name = "hadjustment";
	} else {
809
		to_set = &view->vadjustment;
810 811
		prop_name = "vadjustment";
	}
812

813 814
	if (adjustment && adjustment == *to_set)
		return;
815

816 817 818 819 820
	if (*to_set) {
		g_signal_handlers_disconnect_by_func (*to_set,
						      (gpointer) on_adjustment_value_changed,
						      view);
		g_object_unref (*to_set);
821
	}
822

823 824 825 826 827 828 829
	if (!adjustment)
		adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
	g_signal_connect (adjustment, "value_changed",
			  G_CALLBACK (on_adjustment_value_changed),
			  view);
	*to_set = g_object_ref_sink (adjustment);
	ev_view_set_adjustment_values (view, orientation);
830

831
	g_object_notify (G_OBJECT (view), prop_name);
832
}
833

834 835 836
static void
add_scroll_binding_keypad (GtkBindingSet  *binding_set,
    			   guint           keyval,
837
    			   GdkModifierType modifiers,
838
    			   GtkScrollType   scroll,
839
			   GtkOrientation  orientation)
840
{
841
	guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
842

843 844 845 846 847 848 849 850
	gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
				      "scroll", 2,
				      GTK_TYPE_SCROLL_TYPE, scroll,
				      GTK_TYPE_ORIENTATION, orientation);
	gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
				      "scroll", 2,
				      GTK_TYPE_SCROLL_TYPE, scroll,
				      GTK_TYPE_ORIENTATION, orientation);
851
}
852

853 854 855 856 857 858
static gdouble
compute_scroll_increment (EvView        *view,
			  GtkScrollType  scroll)
{
	GtkWidget *widget = GTK_WIDGET (view);
	GtkAdjustment *adjustment = view->vadjustment;
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
859
	cairo_region_t *text_region, *region;
860
	GtkAllocation allocation;
861 862 863 864 865 866 867 868
	gint page;
	GdkRectangle rect;
	EvRectangle doc_rect;
	GdkRectangle page_area;
	GtkBorder border;
	gdouble fraction = 1.0;

	if (scroll != GTK_SCROLL_PAGE_BACKWARD && scroll != GTK_SCROLL_PAGE_FORWARD)
869
		return gtk_adjustment_get_page_size (adjustment);
870 871 872

	page = scroll == GTK_SCROLL_PAGE_BACKWARD ? view->start_page : view->end_page;

873
	text_region = ev_page_cache_get_text_mapping (view->page_cache, page);
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
874
	if (!text_region || cairo_region_is_empty (text_region))
875
		return gtk_adjustment_get_page_size (adjustment);
876

877
	gtk_widget_get_allocation (widget, &allocation);
878
	ev_view_get_page_extents (view, page, &page_area, &border);
879
	rect.x = page_area.x + view->scroll_x;
880
	rect.y = view->scroll_y + (scroll == GTK_SCROLL_PAGE_BACKWARD ? 5 : allocation.height - 5);
881 882
	rect.width = page_area.width;
	rect.height = 1;
883
	_ev_view_transform_view_rect_to_doc_rect (view, &rect, &page_area, &border, &doc_rect);
884 885 886 887 888 889

	/* Convert the doc rectangle into a GdkRectangle */
	rect.x = doc_rect.x1;
	rect.y = doc_rect.y1;
	rect.width = doc_rect.x2 - doc_rect.x1;
	rect.height = MAX (1, doc_rect.y2 - doc_rect.y1);
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
890
	region = cairo_region_create_rectangle (&rect);
891

Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
892 893
	cairo_region_intersect (region, text_region);
	if (cairo_region_num_rectangles (region)) {
894 895
		EvRenderContext *rc;
		EvPage  *ev_page;
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
896
		cairo_region_t *sel_region;
897

Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
898
		cairo_region_get_rectangle (region, 0, &rect);
899
		ev_page = ev_document_get_page (view->document, page);
900 901 902 903
		rc = ev_render_context_new (ev_page, view->rotation, 0.);
		ev_render_context_set_target_size (rc,
						   page_area.width - (border.left + border.right),
						   page_area.height - (border.left + border.right));
904 905
		g_object_unref (ev_page);
		/* Get the selection region to know the height of the line */
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
906 907
		doc_rect.x1 = doc_rect.x2 = rect.x + 0.5;
		doc_rect.y1 = doc_rect.y2 = rect.y + 0.5;
908 909

		ev_document_doc_mutex_lock ();
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
910 911 912
		sel_region = ev_selection_get_selection_region (EV_SELECTION (view->document),
								rc, EV_SELECTION_STYLE_LINE,
								&doc_rect);
913 914 915
		ev_document_doc_mutex_unlock ();

		g_object_unref (rc);
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
916 917 918 919

		if (cairo_region_num_rectangles (sel_region) > 0) {
			cairo_region_get_rectangle (sel_region, 0, &rect);
			fraction = 1 - (rect.height / gtk_adjustment_get_page_size (adjustment));
920
		}
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
921
		cairo_region_destroy (sel_region);
922
	}
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
923
	cairo_region_destroy (region);
924

925
	return gtk_adjustment_get_page_size (adjustment) * fraction;
926 927 928

}

929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949
static void
ev_view_first_page (EvView *view)
{
	ev_document_model_set_page (view->model, 0);
}

static void
ev_view_last_page (EvView *view)
{
	gint n_pages;

	if (!view->document)
		return;

	n_pages = ev_document_get_n_pages (view->document);
	if (n_pages <= 1)
		return;

	ev_document_model_set_page (view->model, n_pages - 1);
}

950 951 952 953 954 955 956 957
/**
 * ev_view_scroll:
 * @view: a #EvView
 * @scroll:
 * @horizontal:
 *
 * Deprecated: 3.10
 */
958 959
void
ev_view_scroll (EvView        *view,
960 961
	        GtkScrollType  scroll,
		gboolean       horizontal)
962 963 964
{
	GtkAdjustment *adjustment;
	double value, increment;
965 966 967
	gdouble upper, lower;
	gdouble page_size;
	gdouble step_increment;
968 969 970
	gboolean first_page = FALSE;
	gboolean last_page = FALSE;

971 972 973
	if (view->key_binding_handled)
		return;

974 975
	view->jump_to_find_result = FALSE;

976
	if (view->sizing_mode == EV_SIZING_FIT_PAGE) {
977
		switch (scroll) {
978 979
			case GTK_SCROLL_PAGE_BACKWARD:
			case GTK_SCROLL_STEP_BACKWARD:
980 981
				ev_view_previous_page (view);
				break;
982 983
			case GTK_SCROLL_PAGE_FORWARD:
			case GTK_SCROLL_STEP_FORWARD:
984 985 986 987 988 989 990 991
				ev_view_next_page (view);
				break;
			default:
				break;
		}
		return;
	}

992
	/* Assign values for increment and vertical adjustment */
993
	adjustment = horizontal ? view->hadjustment : view->vadjustment;
994 995 996 997 998
	value = gtk_adjustment_get_value (adjustment);
	upper = gtk_adjustment_get_upper (adjustment);
	lower = gtk_adjustment_get_lower (adjustment);
	page_size = gtk_adjustment_get_page_size (adjustment);
	step_increment = gtk_adjustment_get_step_increment (adjustment);
999 1000 1001 1002

	/* Assign boolean for first and last page */
	if (view->current_page == 0)
		first_page = TRUE;
1003
	if (view->current_page == ev_document_get_n_pages (view->document) - 1)
1004 1005 1006
		last_page = TRUE;

	switch (scroll) {
1007
		case GTK_SCROLL_PAGE_BACKWARD:
1008
			/* Do not jump backwards if at the first page */
1009
			if (value == lower && first_page) {
1010 1011
				/* Do nothing */
				/* At the top of a page, assign the upper bound limit of previous page */
1012 1013
			} else if (value == lower) {
				value = upper - page_size;
1014
				ev_view_previous_page (view);
1015 1016
				/* Jump to the top */
			} else {
1017
				increment = compute_scroll_increment (view, GTK_SCROLL_PAGE_BACKWARD);
1018
				value = MAX (value - increment, lower);
1019 1020
			}
			break;
1021
		case GTK_SCROLL_PAGE_FORWARD:
1022
			/* Do not jump forward if at the last page */
1023
			if (value == (upper - page_size) && last_page) {
1024 1025
				/* Do nothing */
			/* At the bottom of a page, assign the lower bound limit of next page */
1026
			} else if (value == (upper - page_size)) {
1027
				value = 0;
1028
				ev_view_next_page (view);
1029 1030
			/* Jump to the bottom */
			} else {
1031
				increment = compute_scroll_increment (view, GTK_SCROLL_PAGE_FORWARD);
1032
				value = MIN (value + increment, upper - page_size);
1033 1034
			}
			break;
1035
	        case GTK_SCROLL_STEP_BACKWARD:
1036
			value -= step_increment;
1037
			break;
1038
	        case GTK_SCROLL_STEP_FORWARD:
1039
			value += step_increment;
1040
			break;
1041
        	case GTK_SCROLL_STEP_DOWN:
1042
			value -= step_increment / 10;
1043
			break;
1044
        	case GTK_SCROLL_STEP_UP:
1045
			value += step_increment / 10;
1046
			break;
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
	        case GTK_SCROLL_START:
			value = lower;
			if (!first_page)
				ev_view_first_page (view);
			break;
	        case GTK_SCROLL_END:
			value = upper - page_size;
			if (!last_page)
				ev_view_last_page (view);
			/* Changing pages causes the top to be shown. Here we want the bottom shown. */
			view->pending_point.y = value;
			break;
1059
        	default:
1060 1061
			break;
	}
1062

1063
	value = CLAMP (value, lower, upper - page_size);
1064

1065
	gtk_adjustment_set_value (adjustment, value);
1066 1067
}

1068 1069 1070 1071 1072 1073 1074 1075
static void
ev_view_scroll_internal (EvView        *view,
			 GtkScrollType  scroll,
			 GtkOrientation orientation)
{
	ev_view_scroll (view, scroll, orientation == GTK_ORIENTATION_HORIZONTAL);
}

1076 1077 1078 1079 1080 1081 1082
#define MARGIN 5

static void
ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect)
{
	GtkWidget *widget = GTK_WIDGET (view);
	GtkAdjustment *adjustment;
1083 1084
	GtkAllocation allocation;
	gdouble adj_value;
1085 1086
	int value;

1087
	view->pending_scroll = SCROLL_TO_FIND_LOCATION;
1088

1089 1090
	gtk_widget_get_allocation (widget, &allocation);

1091
	adjustment = view->vadjustment;
1092
	adj_value = gtk_adjustment_get_value (adjustment);
1093

1094 1095
	if (rect->y < adj_value) {
		value = MAX (gtk_adjustment_get_lower (adjustment), rect->y - MARGIN);
1096
		gtk_adjustment_set_value (view->vadjustment, value);
1097 1098 1099
	} else if (rect->y + rect->height > adj_value + allocation.height) {
		value = MIN (gtk_adjustment_get_upper (adjustment), rect->y + rect->height -
			     allocation.height + MARGIN);
1100
		gtk_adjustment_set_value (view->vadjustment, value);
1101
	}
1102

1103
	adjustment = view->hadjustment;
1104
	adj_value = gtk_adjustment_get_value (adjustment);
1105

1106 1107
	if (rect->x < adj_value) {
		value = MAX (gtk_adjustment_get_lower (adjustment), rect->x - MARGIN);
1108
		gtk_adjustment_set_value (view->hadjustment, value);
1109 1110 1111
	} else if (rect->x + rect->height > adj_value + allocation.width) {
		value = MIN (gtk_adjustment_get_upper (adjustment), rect->x + rect->width -
			     allocation.width + MARGIN);
1112 1113
		gtk_adjustment_set_value (view->hadjustment, value);
	}
1114 1115
}

1116 1117
/*** Geometry computations ***/

1118
static void
1119
compute_border (EvView *view, GtkBorder *border)
1120
{
1121 1122 1123 1124 1125 1126 1127 1128
	GtkWidget       *widget = GTK_WIDGET (view);
	GtkStyleContext *context = gtk_widget_get_style_context (widget);
	GtkStateFlags    state = gtk_widget_get_state_flags (widget);

	gtk_style_context_save (context);
	gtk_style_context_add_class (context, EV_STYLE_CLASS_DOCUMENT_PAGE);
	gtk_style_context_get_border (context, state, border);
	gtk_style_context_restore (context);
1129
}
1130

1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
void
_get_page_size_for_scale_and_rotation (EvDocument *document,
				       gint        page,
				       gdouble     scale,
				       gint        rotation,
				       gint       *page_width,
				       gint       *page_height)
{
	gdouble w, h;
	gint    width, height;

	ev_document_get_page_size (document, page, &w, &h);

	width = (gint)(w * scale + 0.5);
	height = (gint)(h * scale + 0.5);

	if (page_width)
		*page_width = (rotation == 0 || rotation == 180) ? width : height;
	if (page_height)
		*page_height = (rotation == 0 || rotation == 180) ? height : width;
}

static void
ev_view_get_page_size (EvView *view,
		       gint    page,
		       gint   *page_width,
		       gint   *page_height)
{
	_get_page_size_for_scale_and_rotation (view->document,
					       page,
					       view->scale,
					       view->rotation,
					       page_width,
					       page_height);
}

1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
static void
ev_view_get_max_page_size (EvView *view,
			   gint   *max_width,
			   gint   *max_height)
{
	double w, h;
	gint   width, height;

	ev_document_get_max_page_size (view->document, &w, &h);

	width = (gint)(w * view->scale + 0.5);
	height = (gint)(h * view->scale + 0.5);

	if (max_width)
		*max_width = (view->rotation == 0 || view->rotation == 180) ? width : height;
	if (max_height)
		*max_height = (view->rotation == 0 || view->rotation == 180) ? height : width;
}

1186
static void
1187
get_page_y_offset (EvView *view, int page, int *y_offset)
1188
{
1189
	int offset = 0;
1190
	GtkBorder border;
1191
	gboolean odd_left;
1192

1193
	g_return_if_fail (y_offset != NULL);
1194

1195
	compute_border (view, &border);
1196

1197
	if (is_dual_page (view, &odd_left)) {
1198
		ev_view_get_height_to_page (view, page, NULL, &offset);
1199 1200
		offset += ((page + !odd_left) / 2 + 1) * view->spacing +
			((page + !odd_left) / 2 ) * (border.top + border.bottom);
1201
	} else {
1202
		ev_view_get_height_to_page (view, page, &offset, NULL);
1203 1204
		offset += (page + 1) * view->spacing + page * (border.top + border.bottom);
	}
1205

1206 1207 1208 1209
	*y_offset = offset;
	return;
}

1210 1211 1212 1213 1214
gboolean
ev_view_get_page_extents (EvView       *view,
			  gint          page,
			  GdkRectangle *page_area,
			  GtkBorder    *border)
1215 1216 1217
{
	GtkWidget *widget;
	int width, height;
1218
	GtkAllocation allocation;
1219

1220
	widget = GTK_WIDGET (view);
1221
	gtk_widget_get_allocation (widget, &allocation);
1222 1223

	/* Get the size of the page */
1224
	ev_view_get_page_size (view, page, &width, &height);
1225
	compute_border (view, border);
1226 1227 1228
	page_area->width = width + border->left + border->right;
	page_area->height = height + border->top + border->bottom;

1229
	if (view->continuous) {
1230
		gint max_width;
1231
		gint x, y;
1232
		gboolean odd_left;
1233

1234
		ev_view_get_max_page_size (view, &max_width, NULL);
1235
		max_width = max_width + border->left + border->right;
1236
		/* Get the location of the bounding box */
1237 1238
		if (is_dual_page (view, &odd_left)) {
			x = view->spacing + ((page % 2 == !odd_left) ? 0 : 1) * (max_width + view->spacing);
1239
			x = x + MAX (0, allocation.width - (max_width * 2 + view->spacing * 3)) / 2;
1240
			if (page % 2 == !odd_left)
1241
				x = x + (max_width - width - border->left - border->right);
1242 1243
		} else {
			x = view->spacing;
Carlos Garcia Campos's avatar<