]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
Allow drag and drop of text. Fixes #316772
[evince.git] / shell / ev-view.c
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include <math.h>
22 #include <string.h>
23 #include <gtk/gtkalignment.h>
24 #include <glib/gi18n.h>
25 #include <gtk/gtkbindings.h>
26 #include <gtk/gtkselection.h>
27 #include <gtk/gtkclipboard.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <libgnomevfs/gnome-vfs-utils.h>
30
31 #include "ev-marshal.h"
32 #include "ev-view.h"
33 #include "ev-utils.h"
34 #include "ev-selection.h"
35 #include "ev-document-find.h"
36 #include "ev-document-misc.h"
37 #include "ev-debug.h"
38 #include "ev-job-queue.h"
39 #include "ev-page-cache.h"
40 #include "ev-pixbuf-cache.h"
41 #include "ev-tooltip.h"
42
43 #define EV_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass))
44 #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW))
45 #define EV_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_VIEW, EvViewClass))
46
47
48 enum {
49         PROP_0,
50         PROP_STATUS,
51         PROP_FIND_STATUS,
52         PROP_CONTINUOUS,
53         PROP_DUAL_PAGE,
54         PROP_FULLSCREEN,
55         PROP_PRESENTATION,
56         PROP_SIZING_MODE,
57         PROP_ZOOM,
58         PROP_ROTATION,
59 };
60
61 enum {
62         SIGNAL_BINDING_ACTIVATED,
63         SIGNAL_ZOOM_INVALID,
64         SIGNAL_EXTERNAL_LINK,
65         N_SIGNALS,
66 };
67
68 enum {
69         TARGET_STRING,
70         TARGET_TEXT,
71         TARGET_COMPOUND_TEXT,
72         TARGET_UTF8_STRING,
73         TARGET_TEXT_BUFFER_CONTENTS
74 };
75
76 static const GtkTargetEntry targets[] = {
77         { "STRING", 0, TARGET_STRING },
78         { "TEXT",   0, TARGET_TEXT },
79         { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
80         { "UTF8_STRING", 0, TARGET_UTF8_STRING },
81 };
82
83 static guint signals[N_SIGNALS];
84
85 typedef enum {
86         EV_VIEW_CURSOR_NORMAL,
87         EV_VIEW_CURSOR_IBEAM,
88         EV_VIEW_CURSOR_LINK,
89         EV_VIEW_CURSOR_WAIT,
90         EV_VIEW_CURSOR_HIDDEN,
91         EV_VIEW_CURSOR_DRAG
92 } EvViewCursor;
93
94 typedef enum {
95         EV_VIEW_FIND_NEXT,
96         EV_VIEW_FIND_PREV
97 } EvViewFindDirection;
98
99 #define ZOOM_IN_FACTOR  1.2
100 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)
101
102 #define MIN_SCALE 0.05409
103 #define MAX_SCALE 4.0
104
105 /* Information for middle clicking and moving around the doc */
106 typedef struct {
107         gboolean in_drag;
108         GdkPoint start;
109         gdouble hadj;
110         gdouble vadj;
111 } DragInfo;
112
113 /* Information for handling selection */
114 typedef struct {
115         gboolean in_selection;
116         gboolean in_drag;
117         GdkPoint start;
118         GList *selections;
119 } SelectionInfo;
120
121 typedef enum {
122         SCROLL_TO_KEEP_POSITION,
123         SCROLL_TO_CURRENT_PAGE,
124         SCROLL_TO_CENTER
125 } PendingScroll;
126
127 struct _EvView {
128         GtkWidget parent_instance;
129
130         EvDocument *document;
131
132         char *status;
133         char *find_status;
134
135         /* Scrolling */
136         GtkAdjustment *hadjustment;
137         GtkAdjustment *vadjustment;
138
139         gint scroll_x;
140         gint scroll_y;
141
142         /* Information for middle clicking and dragging around. */
143         DragInfo drag_info;
144
145         /* Selection */
146         gint motion_x;
147         gint motion_y;
148         guint selection_update_id;
149
150         EvViewSelectionMode selection_mode;
151         SelectionInfo selection_info;
152
153         int pressed_button;
154         EvViewCursor cursor;
155         GtkWidget *link_tooltip;
156         EvLink *hovered_link;
157
158         EvPageCache *page_cache;
159         EvPixbufCache *pixbuf_cache;
160
161         gint start_page;
162         gint end_page;
163         gint current_page;
164
165         EvJobRender *current_job;
166
167         int find_page;
168         int find_result;
169         int spacing;
170
171         int rotation;
172         double scale;
173
174         gboolean continuous;
175         gboolean dual_page;
176         gboolean fullscreen;
177         gboolean presentation;
178         EvSizingMode sizing_mode;
179
180         PendingScroll pending_scroll;
181         gboolean pending_resize;
182 };
183
184 struct _EvViewClass {
185         GtkWidgetClass parent_class;
186
187         void    (*set_scroll_adjustments) (EvView         *view,
188                                            GtkAdjustment  *hadjustment,
189                                            GtkAdjustment  *vadjustment);
190         void    (*binding_activated)      (EvView         *view,
191                                            GtkScrollType   scroll,
192                                            gboolean        horizontal);
193         void    (*zoom_invalid)           (EvView         *view);
194         void    (*external_link)          (EvView         *view,
195                                            EvLink         *link);
196 };
197
198 /*** Scrolling ***/
199 static void       scroll_to_current_page                     (EvView *view,
200                                                               GtkOrientation orientation);
201 static void       ev_view_set_scroll_adjustments             (EvView             *view,
202                                                               GtkAdjustment      *hadjustment,
203                                                               GtkAdjustment      *vadjustment);
204 static void       view_update_range_and_current_page         (EvView             *view);
205 static void       set_scroll_adjustment                      (EvView             *view,
206                                                               GtkOrientation      orientation,
207                                                               GtkAdjustment      *adjustment);
208 static void       ev_view_set_scroll_adjustments             (EvView             *view,
209                                                               GtkAdjustment      *hadjustment,
210                                                               GtkAdjustment      *vadjustment);
211 static void       add_scroll_binding_keypad                  (GtkBindingSet      *binding_set,
212                                                               guint               keyval,
213                                                               GtkScrollType       scroll,
214                                                               gboolean            horizontal);
215 static void       ev_view_binding_activated                  (EvView             *view,
216                                                               GtkScrollType       scroll,
217                                                               gboolean            horizontal);
218 static void       ensure_rectangle_is_visible                (EvView             *view,
219                                                               GdkRectangle       *rect);
220
221 /*** Geometry computations ***/
222 static void       compute_border                             (EvView             *view,
223                                                               int                 width,
224                                                               int                 height,
225                                                               GtkBorder          *border);
226 static void       get_page_y_offset                          (EvView *view,
227                                                               int page,
228                                                               double zoom,
229                                                               int *y_offset);
230 static gboolean   get_page_extents                           (EvView             *view,
231                                                               gint                page,
232                                                               GdkRectangle       *page_area,
233                                                               GtkBorder          *border);
234 static void       view_rect_to_doc_rect                      (EvView             *view,
235                                                               GdkRectangle       *view_rect,
236                                                               GdkRectangle       *page_area,
237                                                               EvRectangle        *doc_rect);
238 static void       doc_rect_to_view_rect                      (EvView             *view,
239                                                               int                 page,
240                                                               EvRectangle        *doc_rect,
241                                                               GdkRectangle       *view_rect);
242 static void       find_page_at_location                      (EvView             *view,
243                                                               gdouble             x,
244                                                               gdouble             y,
245                                                               gint               *page,
246                                                               gint               *x_offset,
247                                                               gint               *y_offset);
248
249 /*** Hyperrefs ***/
250 static EvLink*    get_link_at_location                       (EvView             *view,
251                                                               gdouble             x,
252                                                               gdouble             y);
253 static char*      tip_from_link                              (EvView             *view,
254                                                               EvLink             *link);
255 static void       handle_link_over_xy                        (EvView *view, 
256                                                               gint x, 
257                                                               gint y);
258 /*** GtkWidget implementation ***/
259 static void       ev_view_size_request_continuous_dual_page  (EvView             *view,
260                                                               GtkRequisition     *requisition);
261 static void       ev_view_size_request_continuous            (EvView             *view,
262                                                               GtkRequisition     *requisition);
263 static void       ev_view_size_request_dual_page             (EvView             *view,
264                                                               GtkRequisition     *requisition);
265 static void       ev_view_size_request_single_page           (EvView             *view,
266                                                               GtkRequisition     *requisition);
267 static void       ev_view_size_request                       (GtkWidget          *widget,
268                                                               GtkRequisition     *requisition);
269 static void       ev_view_size_allocate                      (GtkWidget          *widget,
270                                                               GtkAllocation      *allocation);
271 static void       ev_view_realize                            (GtkWidget          *widget);
272 static gboolean   ev_view_scroll_event                       (GtkWidget          *widget,
273                                                               GdkEventScroll     *event);
274 static gboolean   ev_view_expose_event                       (GtkWidget          *widget,
275                                                               GdkEventExpose     *event);
276 static gboolean   ev_view_button_press_event                 (GtkWidget          *widget,
277                                                               GdkEventButton     *event);
278 static gboolean   ev_view_motion_notify_event                (GtkWidget          *widget,
279                                                               GdkEventMotion     *event);
280 static gboolean   ev_view_button_release_event               (GtkWidget          *widget,
281                                                               GdkEventButton     *event);
282 static gboolean   ev_view_enter_notify_event                 (GtkWidget          *widget,
283                                                               GdkEventCrossing   *event);
284 static gboolean   ev_view_leave_notify_event                 (GtkWidget          *widget,
285                                                               GdkEventCrossing   *event);
286 static void       ev_view_style_set                          (GtkWidget          *widget,
287                                                               GtkStyle           *old_style);
288
289 /*** Drawing ***/
290 static guint32    ev_gdk_color_to_rgb                        (const GdkColor     *color);
291 static void       draw_rubberband                            (GtkWidget          *widget,
292                                                               GdkWindow          *window,
293                                                               const GdkRectangle *rect,
294                                                               guchar              alpha);
295 static void       highlight_find_results                     (EvView             *view,
296                                                               int                 page);
297 static void       draw_one_page                              (EvView             *view,
298                                                               gint                page,
299                                                               GdkRectangle       *page_area,
300                                                               GtkBorder          *border,
301                                                               GdkRectangle       *expose_area);
302
303 /*** Callbacks ***/
304 static void       find_changed_cb                            (EvDocument         *document,
305                                                               int                 page,
306                                                               EvView             *view);
307 static void       job_finished_cb                            (EvPixbufCache      *pixbuf_cache,
308                                                               EvView             *view);
309 static void       page_changed_cb                            (EvPageCache        *page_cache,
310                                                               int                 new_page,
311                                                               EvView             *view);
312 static void       on_adjustment_value_changed                (GtkAdjustment      *adjustment,
313                                                               EvView             *view);
314
315 /*** GObject ***/
316 static void       ev_view_finalize                           (GObject            *object);
317 static void       ev_view_destroy                            (GtkObject          *object);
318 static void       ev_view_set_property                       (GObject            *object,
319                                                               guint               prop_id,
320                                                               const GValue       *value,
321                                                               GParamSpec         *pspec);
322 static void       ev_view_get_property                       (GObject            *object,
323                                                               guint               prop_id,
324                                                               GValue             *value,
325                                                               GParamSpec         *pspec);
326 static void       ev_view_class_init                         (EvViewClass        *class);
327 static void       ev_view_init                               (EvView             *view);
328
329 /*** Zoom and sizing ***/
330 static double   zoom_for_size_fit_width                      (int doc_width,
331                                                               int doc_height,
332                                                               int target_width,
333                                                               int target_height,
334                                                               int vsb_width);
335 static double   zoom_for_size_fit_height                     (int doc_width,
336                                                               int doc_height,
337                                                               int target_width,
338                                                               int target_height,
339                                                               int vsb_height);
340 static double   zoom_for_size_best_fit                       (int doc_width,
341                                                               int doc_height,
342                                                               int target_width,
343                                                               int target_height,
344                                                               int vsb_width,
345                                                               int hsb_width);
346 static void     ev_view_zoom_for_size_presentation           (EvView *view,
347                                                               int     width,
348                                                               int     height);
349 static void     ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
350                                                                 int     width,
351                                                                 int     height,
352                                                                 int     vsb_width,
353                                                                 int     hsb_height);
354 static void     ev_view_zoom_for_size_continuous               (EvView *view,
355                                                                 int     width,
356                                                                 int     height,
357                                                                 int     vsb_width,
358                                                                 int     hsb_height);
359 static void     ev_view_zoom_for_size_dual_page                (EvView *view,
360                                                                 int     width,
361                                                                 int     height,
362                                                                 int     vsb_width,
363                                                                 int     hsb_height);
364 static void     ev_view_zoom_for_size_single_page              (EvView *view,
365                                                                 int     width,
366                                                                 int     height,
367                                                                 int     vsb_width,
368                                                                 int     hsb_height);
369 /*** Cursors ***/
370 static GdkCursor* ev_view_create_invisible_cursor            (void);
371 static void       ev_view_set_cursor                         (EvView             *view,
372                                                               EvViewCursor        new_cursor);
373
374 /*** Status messages ***/
375 static void       ev_view_set_status                         (EvView             *view,
376                                                               const char         *message);
377 static void       update_find_status_message                 (EvView             *view);
378 static void       ev_view_set_find_status                    (EvView             *view,
379                                                               const char         *message);
380 /*** Find ***/
381 static void       jump_to_find_result                        (EvView             *view);
382 static void       jump_to_find_page                          (EvView             *view, 
383                                                               EvViewFindDirection direction);
384
385 /*** Selection ***/
386 static void       compute_selections                         (EvView             *view,
387                                                               GdkPoint           *start,
388                                                               GdkPoint           *stop);
389 static void       clear_selection                            (EvView             *view);
390 static void       selection_free                             (EvViewSelection    *selection);
391 static char*      get_selected_text                          (EvView             *ev_view);
392 static void       ev_view_primary_get_cb                     (GtkClipboard       *clipboard,
393                                                               GtkSelectionData   *selection_data,
394                                                               guint               info,
395                                                               gpointer            data);
396 static void       ev_view_primary_clear_cb                   (GtkClipboard       *clipboard,
397                                                               gpointer            data);
398 static void       ev_view_update_primary_selection           (EvView             *ev_view);
399
400
401 G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET)
402
403 static void
404 scroll_to_current_page (EvView *view, GtkOrientation orientation)
405 {
406         GdkRectangle page_area;
407         GtkBorder border;
408
409         if (view->document == NULL) {
410                 return;
411         }
412
413         get_page_extents (view, view->current_page, &page_area, &border);
414
415         if (orientation == GTK_ORIENTATION_VERTICAL) {
416                 if (view->continuous) {
417                         gtk_adjustment_clamp_page (view->vadjustment,
418                                                    page_area.y - view->spacing,
419                                                    page_area.y + view->vadjustment->page_size);
420                 } else {
421                         gtk_adjustment_set_value (view->vadjustment,
422                                                   view->vadjustment->lower);
423                 }
424         } else {
425                 if (view->dual_page) {
426                         gtk_adjustment_clamp_page (view->hadjustment,
427                                                    page_area.x,
428                                                    page_area.x + view->hadjustment->page_size);
429                 } else {
430                         gtk_adjustment_set_value (view->hadjustment,
431                                                   CLAMP (view->hadjustment->value,
432                                                   view->hadjustment->lower,
433                                                   view->hadjustment->upper -
434                                                   view->hadjustment->page_size));
435                 }
436         }
437 }
438
439 static void
440 view_set_adjustment_values (EvView         *view,
441                             GtkOrientation  orientation)
442 {
443         GtkWidget *widget = GTK_WIDGET (view);
444         GtkAdjustment *adjustment;
445         int requisition;
446         int allocation;
447
448         double factor;
449         gint new_value;
450
451         if (orientation == GTK_ORIENTATION_HORIZONTAL)  {
452                 requisition = widget->requisition.width;
453                 allocation = widget->allocation.width;
454                 adjustment = view->hadjustment;
455         } else {
456                 requisition = widget->requisition.height;
457                 allocation = widget->allocation.height;
458                 adjustment = view->vadjustment;
459         }
460
461         if (!adjustment)
462                 return;
463
464         factor = 1.0;
465         switch (view->pending_scroll) {
466                 case SCROLL_TO_KEEP_POSITION:
467                         factor = (adjustment->value) / adjustment->upper;
468                         break;
469                 case SCROLL_TO_CURRENT_PAGE:
470                         break;
471                 case SCROLL_TO_CENTER:
472                         factor = (adjustment->value + adjustment->page_size * 0.5) / adjustment->upper;
473                         break;
474         }
475
476         adjustment->page_size = allocation;
477         adjustment->step_increment = allocation * 0.1;
478         adjustment->page_increment = allocation * 0.9;
479         adjustment->lower = 0;
480         adjustment->upper = MAX (allocation, requisition);
481
482         /*
483          * We add 0.5 to the values before to average out our rounding errors.
484          */
485         switch (view->pending_scroll) {
486                 case SCROLL_TO_KEEP_POSITION:
487                         new_value = CLAMP (adjustment->upper * factor + 0.5, 0, adjustment->upper - adjustment->page_size);
488                         gtk_adjustment_set_value (adjustment, (int)new_value);
489                         break;
490                 case SCROLL_TO_CURRENT_PAGE:
491                         scroll_to_current_page (view, orientation);
492                         break;
493                 case SCROLL_TO_CENTER:
494                         new_value = CLAMP (adjustment->upper * factor - adjustment->page_size * 0.5 + 0.5,
495                                            0, adjustment->upper - adjustment->page_size);
496                         gtk_adjustment_set_value (adjustment, (int)new_value);
497                         break;
498         }
499
500         gtk_adjustment_changed (adjustment);
501 }
502
503 static void
504 view_update_range_and_current_page (EvView *view)
505 {
506         if (view->pending_scroll != SCROLL_TO_KEEP_POSITION)
507                 return;
508
509         /* Presentation trumps all other modes */
510         if (view->presentation) {
511                 view->start_page = view->current_page;
512                 view->end_page = view->current_page;
513         } else if (view->continuous) {
514                 GdkRectangle current_area, unused, page_area;
515                 GtkBorder border;
516                 gint current_page;
517                 gboolean found = FALSE;
518                 int i;
519
520                 if (!(view->vadjustment && view->hadjustment))
521                         return;
522
523                 current_area.x = view->hadjustment->value;
524                 current_area.width = view->hadjustment->upper;
525                 current_area.y = view->vadjustment->value;
526                 current_area.height = view->vadjustment->page_size;
527
528                 for (i = 0; i < ev_page_cache_get_n_pages (view->page_cache); i++) {
529
530                         get_page_extents (view, i, &page_area, &border);
531
532                         if (gdk_rectangle_intersect (&current_area, &page_area, &unused)) {
533                                 if (! found) {
534                                         view->start_page = i;
535                                         found = TRUE;
536
537                                 }
538                                 view->end_page = i;
539                         } else if (found) {
540                                 break;
541                         }
542                 }
543
544                 current_page = ev_page_cache_get_current_page (view->page_cache);
545
546                 if (current_page < view->start_page || current_page > view->end_page) {
547                         view->current_page = view->start_page;
548                         ev_page_cache_set_current_page (view->page_cache, view->start_page);
549                 }
550         } else {
551                 if (view->dual_page) {
552                         if (view->current_page % 2 == 0) {
553                                 view->start_page = view->current_page;
554                                 if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache))
555                                         view->end_page = view->start_page + 1;
556                                 else 
557                                         view->end_page = view->start_page;
558                         } else {
559                                 view->start_page = view->current_page - 1;
560                                 view->end_page = view->current_page;
561                         }
562                 } else {
563                         view->start_page = view->current_page;
564                         view->end_page = view->current_page;
565                 }
566         }
567
568         ev_pixbuf_cache_set_page_range (view->pixbuf_cache,
569                                         view->start_page,
570                                         view->end_page,
571                                         view->rotation,
572                                         view->scale,
573                                         view->selection_info.selections);
574 }
575
576 static void
577 set_scroll_adjustment (EvView *view,
578                        GtkOrientation  orientation,
579                        GtkAdjustment  *adjustment)
580 {
581         GtkAdjustment **to_set;
582
583         if (orientation == GTK_ORIENTATION_HORIZONTAL)
584                 to_set = &view->hadjustment;
585         else
586                 to_set = &view->vadjustment;
587
588         if (*to_set != adjustment) {
589                 if (*to_set) {
590                         g_signal_handlers_disconnect_by_func (*to_set,
591                                                               (gpointer) on_adjustment_value_changed,
592                                                               view);
593                         g_object_unref (*to_set);
594                 }
595
596                 *to_set = adjustment;
597                 view_set_adjustment_values (view, orientation);
598
599                 if (*to_set) {
600                         g_object_ref (*to_set);
601                         g_signal_connect (*to_set, "value_changed",
602                                           G_CALLBACK (on_adjustment_value_changed), view);
603                 }
604         }
605 }
606
607 static void
608 ev_view_set_scroll_adjustments (EvView *view,
609                                 GtkAdjustment  *hadjustment,
610                                 GtkAdjustment  *vadjustment)
611 {
612         set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
613         set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
614
615         on_adjustment_value_changed (NULL, view);
616 }
617
618 static void
619 add_scroll_binding_keypad (GtkBindingSet  *binding_set,
620                            guint           keyval,
621                            GtkScrollType   scroll,
622                            gboolean        horizontal)
623 {
624   guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
625
626   gtk_binding_entry_add_signal (binding_set, keyval, 0,
627                                 "binding_activated", 2,
628                                 GTK_TYPE_SCROLL_TYPE, scroll,
629                                 G_TYPE_BOOLEAN, horizontal);
630   gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
631                                 "binding_activated", 2,
632                                 GTK_TYPE_SCROLL_TYPE, scroll,
633                                 G_TYPE_BOOLEAN, horizontal);
634 }
635
636 void
637 ev_view_scroll (EvView        *view,
638                 EvScrollType   scroll)
639 {
640         GtkAdjustment *adjustment;
641         double value, increment;
642         gboolean first_page = FALSE;
643         gboolean last_page = FALSE;
644
645         /* Assign values for increment and vertical adjustment */
646         adjustment = view->vadjustment;
647         increment = adjustment->page_size * 0.75;
648         value = adjustment->value;
649
650         /* Assign boolean for first and last page */
651         if (view->current_page == 0)
652                 first_page = TRUE;
653         if (view->current_page == ev_page_cache_get_n_pages (view->page_cache) - 1)
654                 last_page = TRUE;
655
656         switch (scroll) {
657                 case EV_SCROLL_PAGE_BACKWARD:
658                         /* Do not jump backwards if at the first page */
659                         if (value == (adjustment->lower) && first_page) {
660                                 /* Do nothing */
661                                 /* At the top of a page, assign the upper bound limit of previous page */
662                         } else if (value == (adjustment->lower)) {
663                                 value = adjustment->upper - adjustment->page_size;
664                                 ev_page_cache_set_current_page (view->page_cache, view->current_page - 1);
665                                 /* Jump to the top */
666                         } else {
667                                 value = MAX (value - increment, adjustment->lower);
668                         }
669                         break;
670                 case EV_SCROLL_PAGE_FORWARD:
671                         /* Do not jump forward if at the last page */
672                         if (value == (adjustment->upper - adjustment->page_size) && last_page) {
673                                 /* Do nothing */
674                         /* At the bottom of a page, assign the lower bound limit of next page */
675                         } else if (value == (adjustment->upper - adjustment->page_size)) {
676                                 value = 0;
677                                 ev_page_cache_set_current_page (view->page_cache, view->current_page + 1);
678                         /* Jump to the bottom */
679                         } else {
680                                 value = MIN (value + increment, adjustment->upper - adjustment->page_size);
681                         }
682                         break;
683                 default:
684                         break;
685         }
686
687         gtk_adjustment_set_value (adjustment, value);
688 }
689
690 static void
691 ev_view_binding_activated (EvView *view,
692                            GtkScrollType scroll,
693                            gboolean horizontal)
694 {
695         GtkAdjustment *adjustment;
696         double value;
697
698         if (view->presentation) {
699                 switch (scroll) {
700                         case GTK_SCROLL_STEP_BACKWARD:
701                                 ev_view_previous_page (view);
702                                 break;
703                         case GTK_SCROLL_STEP_FORWARD:
704                                 ev_view_next_page (view);
705                                 break;
706                         default:
707                                 break;
708                 }
709                 return;
710         }
711
712         if (horizontal) {
713                 adjustment = view->hadjustment;
714         } else {
715                 adjustment = view->vadjustment;
716         }
717
718         value = adjustment->value;
719
720         switch (scroll) {
721                 case GTK_SCROLL_STEP_BACKWARD:
722                         value -= adjustment->step_increment;
723                         break;
724                 case GTK_SCROLL_STEP_FORWARD:
725                         value += adjustment->step_increment;
726                         break;
727                 default:
728                         break;
729         }
730
731         value = CLAMP (value, adjustment->lower,
732                        adjustment->upper - adjustment->page_size);
733
734         gtk_adjustment_set_value (adjustment, value);
735 }
736
737 #define MARGIN 5
738
739 static void
740 ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect)
741 {
742         GtkWidget *widget = GTK_WIDGET (view);
743         GtkAdjustment *adjustment;
744         int value;
745
746         view->pending_scroll = SCROLL_TO_KEEP_POSITION;
747
748         adjustment = view->vadjustment;
749
750         if (rect->y < adjustment->value) {
751                 value = MAX (adjustment->lower, rect->y - MARGIN);
752                 gtk_adjustment_set_value (view->vadjustment, value);
753         } else if (rect->y + rect->height >
754                    adjustment->value + widget->allocation.height) {
755                 value = MIN (adjustment->upper, rect->y + rect->height -
756                              widget->allocation.height + MARGIN);
757                 gtk_adjustment_set_value (view->vadjustment, value);
758         }
759
760         adjustment = view->hadjustment;
761
762         if (rect->x < adjustment->value) {
763                 value = MAX (adjustment->lower, rect->x - MARGIN);
764                 gtk_adjustment_set_value (view->hadjustment, value);
765         } else if (rect->x + rect->height >
766                    adjustment->value + widget->allocation.width) {
767                 value = MIN (adjustment->upper, rect->x + rect->width -
768                              widget->allocation.width + MARGIN);
769                 gtk_adjustment_set_value (view->hadjustment, value);
770         }
771 }
772
773 /*** Geometry computations ***/
774
775 static void
776 compute_border (EvView *view, int width, int height, GtkBorder *border)
777 {
778         if (view->presentation) {
779                 border->left = 0;
780                 border->right = 0;
781                 border->top = 0;
782                 border->bottom = 0;
783         } else {
784                 ev_document_misc_get_page_border_size (width, height, border);
785         }
786 }
787
788 static void
789 get_page_y_offset (EvView *view, int page, double zoom, int *y_offset)
790 {
791         int max_width, offset;
792         GtkBorder border;
793
794         g_return_if_fail (y_offset != NULL);
795
796         ev_page_cache_get_max_width (view->page_cache, view->rotation, zoom, &max_width);
797
798         compute_border (view, max_width, max_width, &border);
799
800         if (view->dual_page) {
801                 ev_page_cache_get_height_to_page (view->page_cache, page,
802                                                   view->rotation, zoom, NULL, &offset);
803                 offset += (page / 2 + 1) * view->spacing + (page / 2) * (border.top + border.bottom);
804         } else {
805                 ev_page_cache_get_height_to_page (view->page_cache, page,
806                                                   view->rotation, zoom, &offset, NULL);
807                 offset += (page + 1) * view->spacing + page * (border.top + border.bottom);
808         }
809
810         *y_offset = offset;
811         return;
812 }
813
814 static gboolean
815 get_page_extents (EvView       *view,
816                   gint          page,
817                   GdkRectangle *page_area,
818                   GtkBorder    *border)
819 {
820         GtkWidget *widget;
821         int width, height;
822
823         widget = GTK_WIDGET (view);
824
825         /* Get the size of the page */
826         ev_page_cache_get_size (view->page_cache, page,
827                                 view->rotation,
828                                 view->scale,
829                                 &width, &height);
830         compute_border (view, width, height, border);
831         page_area->width = width + border->left + border->right;
832         page_area->height = height + border->top + border->bottom;
833
834         if (view->presentation) {
835                 page_area->x = (MAX (0, widget->allocation.width - width))/2;
836                 page_area->y = (MAX (0, widget->allocation.height - height))/2;
837         } else if (view->continuous) {
838                 gint max_width;
839                 gint x, y;
840
841                 ev_page_cache_get_max_width (view->page_cache, view->rotation,
842                                              view->scale, &max_width);
843                 max_width = max_width + border->left + border->right;
844                 /* Get the location of the bounding box */
845                 if (view->dual_page) {
846                         x = view->spacing + (page % 2) * (max_width + view->spacing);
847                         x = x + MAX (0, widget->allocation.width - (max_width * 2 + view->spacing * 3)) / 2;
848                         if (page % 2 == 0)
849                                 x = x + (max_width - width - border->left - border->right);
850                 } else {
851                         x = view->spacing;
852                         x = x + MAX (0, widget->allocation.width - (width + view->spacing * 2)) / 2;
853                 }
854
855                 get_page_y_offset (view, page, view->scale, &y);
856
857                 page_area->x = x;
858                 page_area->y = y;
859         } else {
860                 gint x, y;
861                 if (view->dual_page) {
862                         gint width_2, height_2;
863                         gint max_width = width;
864                         gint max_height = height;
865                         GtkBorder overall_border;
866                         gint other_page;
867
868                         other_page = page ^ 1;
869
870                         /* First, we get the bounding box of the two pages */
871                         if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
872                                 ev_page_cache_get_size (view->page_cache,
873                                                         other_page,
874                                                         view->rotation,
875                                                         view->scale,
876                                                         &width_2, &height_2);
877                                 if (width_2 > width)
878                                         max_width = width_2;
879                                 if (height_2 > height)
880                                         max_height = height_2;
881                         }
882                         compute_border (view, max_width, max_height, &overall_border);
883
884                         /* Find the offsets */
885                         x = view->spacing;
886                         y = view->spacing;
887
888                         /* Adjust for being the left or right page */
889                         if (page % 2 == 0)
890                                 x = x + max_width - width;
891                         else
892                                 x = x + (max_width + overall_border.left + overall_border.right) + view->spacing;
893
894                         y = y + (max_height - height)/2;
895
896                         /* Adjust for extra allocation */
897                         x = x + MAX (0, widget->allocation.width -
898                                      ((max_width + overall_border.left + overall_border.right) * 2 + view->spacing * 3))/2;
899                         y = y + MAX (0, widget->allocation.height - (height + view->spacing * 2))/2;
900                 } else {
901                         x = view->spacing;
902                         y = view->spacing;
903
904                         /* Adjust for extra allocation */
905                         x = x + MAX (0, widget->allocation.width - (width + border->left + border->right + view->spacing * 2))/2;
906                         y = y + MAX (0, widget->allocation.height - (height + border->top + border->bottom +  view->spacing * 2))/2;
907                 }
908
909                 page_area->x = x;
910                 page_area->y = y;
911         }
912
913         return TRUE;
914 }
915
916 static void
917 view_point_to_doc_point (EvView *view,
918                          GdkPoint *view_point,
919                          GdkRectangle *page_area,
920                          double  *doc_point_x,
921                          double  *doc_point_y)
922 {
923         *doc_point_x = (double) (view_point->x - page_area->x) / view->scale;
924         *doc_point_y = (double) (view_point->y - page_area->y) / view->scale;
925 }
926
927 static void
928 view_rect_to_doc_rect (EvView *view,
929                        GdkRectangle *view_rect,
930                        GdkRectangle *page_area,
931                        EvRectangle  *doc_rect)
932 {
933         doc_rect->x1 = (double) (view_rect->x - page_area->x) / view->scale;
934         doc_rect->y1 = (double) (view_rect->y - page_area->y) / view->scale;
935         doc_rect->x2 = doc_rect->x1 + (double) view_rect->width / view->scale;
936         doc_rect->y2 = doc_rect->y1 + (double) view_rect->height / view->scale;
937 }
938
939 static gboolean
940 doc_point_to_view_point (EvView       *view,
941                          int           page,
942                          EvPoint      *doc_point,
943                          GdkPoint     *view_point)
944 {
945         GdkRectangle page_area;
946         GtkBorder border;
947         double x, y, view_x, view_y;
948         int width, height;
949
950         ev_page_cache_get_size (view->page_cache, page,
951                                 view->rotation,
952                                 1.0,
953                                 &width, &height);
954
955         if (view->rotation == 0) {
956                 x = doc_point->x;
957                 y = doc_point->y;
958         } else if (view->rotation == 90) {
959                 x = width - doc_point->y;
960                 y = doc_point->x;
961         } else if (view->rotation == 180) {
962                 x = width - doc_point->x;
963                 y = height - doc_point->y;
964         } else if (view->rotation == 270) {
965                 x = doc_point->y;
966                 y = height - doc_point->x;
967         } else {
968                 g_assert_not_reached ();
969         }
970
971         get_page_extents (view, page, &page_area, &border);
972
973         view_x = x * view->scale;
974         view_y = y * view->scale;
975         view_point->x = view_x + page_area.x;
976         view_point->y = view_y + page_area.y;
977
978         return (view_x > 0 && view_x <= page_area.width &&
979                 view_y > 0 && view_y <= page_area.height);
980 }
981
982 static void
983 doc_rect_to_view_rect (EvView       *view,
984                        int           page,
985                        EvRectangle  *doc_rect,
986                        GdkRectangle *view_rect)
987 {
988         GdkRectangle page_area;
989         GtkBorder border;
990         double x, y, w, h;
991         int width, height;
992
993         ev_page_cache_get_size (view->page_cache, page,
994                                 view->rotation,
995                                 1.0,
996                                 &width, &height);
997
998         if (view->rotation == 0) {
999                 x = doc_rect->x1;
1000                 y = doc_rect->y1;
1001                 w = doc_rect->x2 - doc_rect->x1;
1002                 h = doc_rect->y2 - doc_rect->y1;
1003         } else if (view->rotation == 90) {
1004                 x = width - doc_rect->y2;
1005                 y = doc_rect->x1;
1006                 w = doc_rect->y2 - doc_rect->y1;
1007                 h = doc_rect->x2 - doc_rect->x1;
1008         } else if (view->rotation == 180) {
1009                 x = width - doc_rect->x2;
1010                 y = height - doc_rect->y2;
1011                 w = doc_rect->x2 - doc_rect->x1;
1012                 h = doc_rect->y2 - doc_rect->y1;
1013         } else if (view->rotation == 270) {
1014                 x = doc_rect->y1;
1015                 y = height - doc_rect->x2;
1016                 w = doc_rect->y2 - doc_rect->y1;
1017                 h = doc_rect->x2 - doc_rect->x1;
1018         } else {
1019                 g_assert_not_reached ();
1020         }
1021
1022         get_page_extents (view, page, &page_area, &border);
1023
1024         view_rect->x = x * view->scale + page_area.x;
1025         view_rect->y = y * view->scale + page_area.y;
1026         view_rect->width = w * view->scale;
1027         view_rect->height = h * view->scale;
1028 }
1029
1030 static void
1031 find_page_at_location (EvView  *view,
1032                        gdouble  x,
1033                        gdouble  y,
1034                        gint    *page,
1035                        gint    *x_offset,
1036                        gint    *y_offset)
1037 {
1038         int i;
1039
1040         if (view->document == NULL)
1041                 return;
1042
1043         g_assert (page);
1044         g_assert (x_offset);
1045         g_assert (y_offset);
1046
1047         for (i = view->start_page; i <= view->end_page; i++) {
1048                 GdkRectangle page_area;
1049                 GtkBorder border;
1050
1051                 if (! get_page_extents (view, i, &page_area, &border))
1052                         continue;
1053
1054                 if ((x >= page_area.x + border.left) &&
1055                     (x < page_area.x + page_area.width - border.right) &&
1056                     (y >= page_area.y + border.top) &&
1057                     (y < page_area.y + page_area.height - border.bottom)) {
1058                         *page = i;
1059                         *x_offset = x - (page_area.x + border.left);
1060                         *y_offset = y - (page_area.y + border.top);
1061                         return;
1062                 }
1063         }
1064
1065         *page = -1;
1066 }
1067
1068 static gboolean
1069 location_in_text (EvView  *view,
1070                   gdouble  x,
1071                   gdouble  y)
1072 {
1073         GdkRegion *region;
1074         gint page = -1;
1075         gint x_offset = 0, y_offset = 0;
1076
1077         find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
1078
1079         if (page == -1)
1080                 return FALSE;
1081         
1082         region = ev_pixbuf_cache_get_text_mapping (view->pixbuf_cache, page);
1083
1084         if (region)
1085                 return gdk_region_point_in (region, x_offset / view->scale, y_offset / view->scale);
1086         else
1087                 return FALSE;
1088 }
1089
1090 static int
1091 ev_view_get_width (EvView *view)
1092 {
1093         return GTK_WIDGET (view)->allocation.width;
1094 }
1095
1096 static int
1097 ev_view_get_height (EvView *view)
1098 {
1099         return GTK_WIDGET (view)->allocation.height;
1100 }
1101
1102 static gboolean
1103 location_in_selected_text (EvView  *view,
1104                            gdouble  x,
1105                            gdouble  y)
1106 {
1107         gint page = -1;
1108         gint x_offset = 0, y_offset = 0;
1109         EvViewSelection *selection;
1110         GList *l = NULL;
1111
1112         for (l = view->selection_info.selections; l != NULL; l = l->next) {
1113                 selection = (EvViewSelection *)l->data;
1114                 
1115                 find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
1116                 
1117                 if (page != selection->page)
1118                         continue;
1119                 
1120                 if (gdk_region_point_in (selection->covered_region, x_offset, y_offset))
1121                         return TRUE;
1122         }
1123
1124         return FALSE;
1125 }
1126
1127 /*** Hyperref ***/
1128 static EvLink *
1129 get_link_at_location (EvView  *view,
1130                       gdouble  x,
1131                       gdouble  y)
1132 {
1133         gint page = -1;
1134         gint x_offset = 0, y_offset = 0;
1135         GList *link_mapping;
1136
1137         find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
1138
1139         if (page == -1)
1140                 return NULL;
1141
1142         link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page);
1143
1144         if (link_mapping)
1145                 return ev_link_mapping_find (link_mapping, x_offset / view->scale, y_offset / view->scale);
1146         else
1147                 return NULL;
1148 }
1149
1150 static void
1151 goto_fitr_link (EvView *view, EvLink *link)
1152 {
1153         GdkPoint view_point;
1154         EvPoint doc_point;
1155         int doc_width, doc_height, page;
1156         double zoom;
1157
1158         page = ev_link_get_page (link);
1159         ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height);
1160
1161         doc_point.x = ev_link_get_left (link);
1162         doc_point.y = ev_link_get_top (link);
1163
1164         zoom = zoom_for_size_best_fit (ev_link_get_right (link) - ev_link_get_left (link),
1165                                        ev_link_get_top (link) - ev_link_get_bottom (link),
1166                                        ev_view_get_width (view),
1167                                        ev_view_get_height (view), 0, 0);
1168
1169         ev_view_set_sizing_mode (view, EV_SIZING_FREE);
1170         ev_view_set_zoom (view, zoom, FALSE);
1171         ev_page_cache_set_current_page (view->page_cache, page);
1172
1173         if (doc_point_to_view_point (view, page, &doc_point, &view_point)) {
1174                 gtk_adjustment_set_value (view->hadjustment, view_point.x);
1175                 gtk_adjustment_set_value (view->vadjustment, view_point.y);
1176         }
1177 }
1178
1179 static void
1180 goto_fitv_link (EvView *view, EvLink *link)
1181 {
1182         GdkPoint view_point;
1183         EvPoint doc_point;
1184         int doc_width, doc_height, page;
1185         double zoom;
1186
1187         page = ev_link_get_page (link);
1188         ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height);
1189
1190         doc_point.x = ev_link_get_left (link);
1191         doc_point.y = 0;
1192
1193         zoom = zoom_for_size_fit_height (doc_width - doc_point.x , doc_height,
1194                                          ev_view_get_width (view),
1195                                          ev_view_get_height (view), 0);
1196
1197         ev_view_set_sizing_mode (view, EV_SIZING_FREE);
1198         ev_view_set_zoom (view, zoom, FALSE);
1199         ev_page_cache_set_current_page (view->page_cache, page);
1200
1201         if (doc_point_to_view_point (view, page, &doc_point, &view_point)) {
1202                 gtk_adjustment_set_value (view->hadjustment, view_point.x);
1203         }
1204 }
1205
1206 static void
1207 goto_fith_link (EvView *view, EvLink *link)
1208 {
1209         GdkPoint view_point;
1210         EvPoint doc_point;
1211         int doc_width, doc_height, page;
1212         double zoom;
1213
1214         page = ev_link_get_page (link);
1215         ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height);
1216
1217         doc_point.x = 0;
1218         doc_point.y = doc_height - ev_link_get_top (link);
1219
1220         zoom = zoom_for_size_fit_width (doc_width, ev_link_get_top (link),
1221                                         ev_view_get_width (view),
1222                                         ev_view_get_height (view), 0);
1223
1224         ev_view_set_sizing_mode (view, EV_SIZING_FREE);
1225         ev_view_set_zoom (view, zoom, FALSE);
1226
1227         if (doc_point_to_view_point (view, page, &doc_point, &view_point)) {
1228                 gtk_adjustment_set_value (view->vadjustment, view_point.y);
1229         } else {
1230                 ev_page_cache_set_current_page (view->page_cache, page);
1231         }
1232 }
1233
1234 static void
1235 goto_fit_link (EvView *view, EvLink *link)
1236 {
1237         double zoom;
1238         int doc_width, doc_height;
1239         int page;
1240
1241         page = ev_link_get_page (link);
1242         ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height);
1243
1244         zoom = zoom_for_size_best_fit (doc_width, doc_height, ev_view_get_width (view),
1245                                        ev_view_get_height (view), 0, 0);
1246
1247         ev_view_set_sizing_mode (view, EV_SIZING_FREE);
1248         ev_view_set_zoom (view, zoom, FALSE);
1249         ev_page_cache_set_current_page (view->page_cache, page);
1250 }
1251
1252 static void
1253 goto_xyz_link (EvView *view, EvLink *link)
1254 {
1255         GdkPoint view_point;
1256         EvPoint doc_point;
1257         int height, page;
1258         double zoom;
1259
1260         zoom = ev_link_get_zoom (link);
1261         page = ev_link_get_page (link);
1262         ev_page_cache_get_size (view->page_cache, page, 0, 1.0, NULL, &height);
1263
1264         ev_view_set_sizing_mode (view, EV_SIZING_FREE);
1265         if (zoom != 0) {
1266                 ev_view_set_zoom (view, zoom, FALSE);
1267         }
1268
1269         doc_point.x = ev_link_get_left (link);
1270         doc_point.y = height - ev_link_get_top (link);
1271
1272         if (doc_point_to_view_point (view, page, &doc_point, &view_point)) {
1273                 gtk_adjustment_set_value (view->hadjustment, view_point.x);
1274                 gtk_adjustment_set_value (view->vadjustment, view_point.y);
1275         } else {
1276                 ev_page_cache_set_current_page (view->page_cache, page);
1277         }
1278 }
1279
1280 void
1281 ev_view_goto_link (EvView *view, EvLink *link)
1282 {
1283         EvLinkType type;
1284         int page;
1285
1286         type = ev_link_get_link_type (link);
1287
1288         switch (type) {
1289                 case EV_LINK_TYPE_TITLE:
1290                         break;
1291                 case EV_LINK_TYPE_PAGE:
1292                         page = ev_link_get_page (link);
1293                         ev_page_cache_set_current_page (view->page_cache, page);
1294                         break;
1295                 case EV_LINK_TYPE_PAGE_FIT:
1296                         goto_fit_link (view, link);
1297                         break;
1298                 case EV_LINK_TYPE_PAGE_FITH:
1299                         goto_fith_link (view, link);
1300                         break;
1301                 case EV_LINK_TYPE_PAGE_FITV:
1302                         goto_fitv_link (view, link);
1303                         break;
1304                 case EV_LINK_TYPE_PAGE_FITR:
1305                         goto_fitr_link (view, link);
1306                         break;
1307                 case EV_LINK_TYPE_PAGE_XYZ:
1308                         goto_xyz_link (view, link);
1309                         break;
1310                 case EV_LINK_TYPE_EXTERNAL_URI:
1311                 case EV_LINK_TYPE_LAUNCH:
1312                         g_signal_emit (view, signals[SIGNAL_EXTERNAL_LINK], 0, link);
1313                         break;
1314         }
1315 }
1316
1317 static char *
1318 tip_from_link (EvView *view, EvLink *link)
1319 {
1320         EvLinkType type;
1321         char *msg = NULL;
1322         char *page_label;
1323
1324         type = ev_link_get_link_type (link);
1325
1326         switch (type) {
1327                 case EV_LINK_TYPE_TITLE:
1328                         if (ev_link_get_title (link))
1329                                 msg = g_strdup (ev_link_get_title (link));
1330                         break;
1331                 case EV_LINK_TYPE_PAGE:
1332                 case EV_LINK_TYPE_PAGE_XYZ:
1333                         page_label = ev_page_cache_get_page_label (view->page_cache, ev_link_get_page (link));
1334                         msg = g_strdup_printf (_("Go to page %s"), page_label);
1335                         g_free (page_label);
1336                         break;
1337                 case EV_LINK_TYPE_EXTERNAL_URI:
1338                         msg = g_strdup (ev_link_get_uri (link));
1339                         break;
1340                 default:
1341                         break;
1342         }
1343
1344         return msg;
1345 }
1346
1347 static void
1348 handle_link_over_xy (EvView *view, gint x, gint y)
1349 {
1350         EvLink *link;
1351
1352         link = get_link_at_location (view, x + view->scroll_x, y + view->scroll_y);
1353
1354         if (view->link_tooltip == NULL) {
1355                 view->link_tooltip = ev_tooltip_new (GTK_WIDGET (view));
1356         }
1357
1358         if (view->hovered_link != link) {
1359                 view->hovered_link = link;
1360                 ev_tooltip_deactivate (EV_TOOLTIP (view->link_tooltip));
1361         }
1362
1363         if (link) {
1364                 char *msg = tip_from_link (view, link);
1365
1366                 ev_tooltip_set_position (EV_TOOLTIP (view->link_tooltip), x, y);
1367                 ev_tooltip_set_text (EV_TOOLTIP (view->link_tooltip), msg);
1368                 ev_tooltip_activate (EV_TOOLTIP (view->link_tooltip));
1369                 g_free (msg);
1370
1371                 ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
1372         } else if (location_in_text (view, x + view->scroll_x, y + view->scroll_y)) {
1373                 ev_view_set_cursor (view, EV_VIEW_CURSOR_IBEAM);
1374         } else {
1375                 ev_view_set_status (view, NULL);
1376                 if (view->cursor == EV_VIEW_CURSOR_LINK ||
1377                     view->cursor == EV_VIEW_CURSOR_IBEAM)
1378                         ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1379         }
1380         return;
1381 }
1382
1383 /*** GtkWidget implementation ***/
1384
1385 static void
1386 ev_view_size_request_continuous_dual_page (EvView         *view,
1387                                            GtkRequisition *requisition)
1388 {
1389         int max_width;
1390         gint n_pages;
1391         GtkBorder border;
1392
1393         ev_page_cache_get_max_width (view->page_cache, view->rotation,
1394                                      view->scale, &max_width);
1395         compute_border (view, max_width, max_width, &border);
1396
1397         n_pages = ev_page_cache_get_n_pages (view->page_cache) + 1;
1398
1399         requisition->width = (max_width + border.left + border.right) * 2 + (view->spacing * 3);
1400         get_page_y_offset (view, n_pages, view->scale, &requisition->height);
1401
1402         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1403                 requisition->width = 1;
1404         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1405                 requisition->width = 1;
1406                 /* FIXME: This could actually be set on one page docs or docs
1407                  * with a strange aspect ratio. */
1408                 /* requisition->height = 1;*/
1409         }
1410 }
1411
1412 static void
1413 ev_view_size_request_continuous (EvView         *view,
1414                                  GtkRequisition *requisition)
1415 {
1416         int max_width;
1417         int n_pages;
1418         GtkBorder border;
1419
1420
1421         ev_page_cache_get_max_width (view->page_cache, view->rotation,
1422                                      view->scale, &max_width);
1423         n_pages = ev_page_cache_get_n_pages (view->page_cache);
1424         compute_border (view, max_width, max_width, &border);
1425
1426         requisition->width = max_width + (view->spacing * 2) + border.left + border.right;
1427         get_page_y_offset (view, n_pages, view->scale, &requisition->height);
1428
1429         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1430                 requisition->width = 1;
1431         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1432                 requisition->width = 1;
1433                 /* FIXME: This could actually be set on one page docs or docs
1434                  * with a strange aspect ratio. */
1435                 /* requisition->height = 1;*/
1436         }
1437 }
1438
1439 static void
1440 ev_view_size_request_dual_page (EvView         *view,
1441                                 GtkRequisition *requisition)
1442 {
1443         GtkBorder border;
1444         gint width, height;
1445
1446         /* Find the largest of the two. */
1447         ev_page_cache_get_size (view->page_cache,
1448                                 view->current_page,
1449                                 view->rotation,
1450                                 view->scale,
1451                                 &width, &height);
1452         if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache)) {
1453                 gint width_2, height_2;
1454                 ev_page_cache_get_size (view->page_cache,
1455                                         view->current_page + 1,
1456                                         view->rotation,
1457                                         view->scale,
1458                                         &width_2, &height_2);
1459                 if (width_2 > width) {
1460                         width = width_2;
1461                         height = height_2;
1462                 }
1463         }
1464         compute_border (view, width, height, &border);
1465
1466         requisition->width = ((width + border.left + border.right) * 2) +
1467                 (view->spacing * 3);
1468         requisition->height = (height + border.top + border.bottom) +
1469                 (view->spacing * 2);
1470
1471         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1472                 requisition->width = 1;
1473         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1474                 requisition->width = 1;
1475                 requisition->height = 1;
1476         }
1477 }
1478
1479 static void
1480 ev_view_size_request_single_page (EvView         *view,
1481                                   GtkRequisition *requisition)
1482 {
1483         GtkBorder border;
1484         gint width, height;
1485
1486         ev_page_cache_get_size (view->page_cache,
1487                                 view->current_page,
1488                                 view->rotation,
1489                                 view->scale,
1490                                 &width, &height);
1491         compute_border (view, width, height, &border);
1492
1493         requisition->width = width + border.left + border.right + (2 * view->spacing);
1494         requisition->height = height + border.top + border.bottom + (2 * view->spacing);
1495
1496         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1497                 requisition->width = 1;
1498                 requisition->height = height + border.top + border.bottom + (2 * view->spacing);
1499         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1500                 requisition->width = 1;
1501                 requisition->height = 1;
1502         }
1503 }
1504
1505 static void
1506 ev_view_size_request (GtkWidget      *widget,
1507                       GtkRequisition *requisition)
1508 {
1509         EvView *view = EV_VIEW (widget);
1510
1511         if (view->document == NULL) {
1512                 requisition->width = 1;
1513                 requisition->height = 1;
1514                 return;
1515         }
1516
1517         if (view->presentation) {
1518                 requisition->width = 1;
1519                 requisition->height = 1;
1520                 return;
1521         }
1522
1523         if (view->continuous && view->dual_page)
1524                 ev_view_size_request_continuous_dual_page (view, requisition);
1525         else if (view->continuous)
1526                 ev_view_size_request_continuous (view, requisition);
1527         else if (view->dual_page)
1528                 ev_view_size_request_dual_page (view, requisition);
1529         else
1530                 ev_view_size_request_single_page (view, requisition);
1531 }
1532
1533 static void
1534 ev_view_size_allocate (GtkWidget      *widget,
1535                        GtkAllocation  *allocation)
1536 {
1537         EvView *view = EV_VIEW (widget);
1538
1539         if (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
1540             view->sizing_mode == EV_SIZING_BEST_FIT) {
1541
1542                 g_signal_emit (view, signals[SIGNAL_ZOOM_INVALID], 0);
1543
1544                 ev_view_size_request (widget, &widget->requisition);
1545         }
1546
1547         view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
1548         view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
1549
1550         view->pending_scroll = SCROLL_TO_KEEP_POSITION;
1551         view->pending_resize = FALSE;
1552
1553         if (view->document)
1554                 view_update_range_and_current_page (view);
1555
1556         GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
1557 }
1558
1559 static void
1560 ev_view_realize (GtkWidget *widget)
1561 {
1562         EvView *view = EV_VIEW (widget);
1563         GdkWindowAttr attributes;
1564
1565         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1566
1567
1568         attributes.window_type = GDK_WINDOW_CHILD;
1569         attributes.wclass = GDK_INPUT_OUTPUT;
1570         attributes.visual = gtk_widget_get_visual (widget);
1571         attributes.colormap = gtk_widget_get_colormap (widget);
1572
1573         attributes.x = widget->allocation.x;
1574         attributes.y = widget->allocation.y;
1575         attributes.width = widget->allocation.width;
1576         attributes.height = widget->allocation.height;
1577         attributes.event_mask = GDK_EXPOSURE_MASK |
1578                                 GDK_BUTTON_PRESS_MASK |
1579                                 GDK_BUTTON_RELEASE_MASK |
1580                                 GDK_SCROLL_MASK |
1581                                 GDK_KEY_PRESS_MASK |
1582                                 GDK_POINTER_MOTION_MASK |
1583                                 GDK_ENTER_NOTIFY_MASK |
1584                                 GDK_LEAVE_NOTIFY_MASK;
1585
1586         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1587                                          &attributes,
1588                                          GDK_WA_X | GDK_WA_Y |
1589                                          GDK_WA_COLORMAP |
1590                                          GDK_WA_VISUAL);
1591         gdk_window_set_user_data (widget->window, widget);
1592         widget->style = gtk_style_attach (widget->style, widget->window);
1593
1594         if (view->presentation)
1595                 gdk_window_set_background (widget->window, &widget->style->black);
1596         else
1597                 gdk_window_set_background (widget->window, &widget->style->mid [GTK_STATE_NORMAL]);
1598 }
1599
1600 static gboolean
1601 ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event)
1602 {
1603         EvView *view = EV_VIEW (widget);
1604         guint state;
1605
1606         state = event->state & gtk_accelerator_get_default_mod_mask ();
1607
1608         if (state == GDK_CONTROL_MASK && view->presentation == FALSE) {
1609                 ev_view_set_sizing_mode (view, EV_SIZING_FREE);
1610
1611                 if (event->direction == GDK_SCROLL_UP ||
1612                     event->direction == GDK_SCROLL_LEFT) {
1613                         if (ev_view_can_zoom_in (view)) {
1614                                 ev_view_zoom_in (view);
1615                         }
1616                 } else {
1617                         if (ev_view_can_zoom_out (view)) {
1618                                 ev_view_zoom_out (view);
1619                         }
1620                 }
1621
1622                 return TRUE;
1623         }
1624
1625         /* Shift+Wheel scrolls the in the perpendicular direction */
1626         if (state & GDK_SHIFT_MASK) {
1627                 if (event->direction == GDK_SCROLL_UP)
1628                         event->direction = GDK_SCROLL_LEFT;
1629                 if (event->direction == GDK_SCROLL_LEFT)
1630                         event->direction = GDK_SCROLL_UP;
1631                 if (event->direction == GDK_SCROLL_DOWN)
1632                         event->direction = GDK_SCROLL_RIGHT;
1633                 if (event->direction == GDK_SCROLL_RIGHT)
1634                         event->direction = GDK_SCROLL_DOWN;
1635
1636                 event->state &= ~GDK_SHIFT_MASK;
1637                 state &= ~GDK_SHIFT_MASK;
1638         }
1639
1640         if (state == 0 && view->presentation) {
1641                 switch (event->direction) {
1642                 case GDK_SCROLL_DOWN:
1643                 case GDK_SCROLL_RIGHT:
1644                         ev_view_next_page (view);       
1645                         break;
1646                 case GDK_SCROLL_UP:
1647                 case GDK_SCROLL_LEFT:
1648                         ev_view_previous_page (view);
1649                         break;
1650                 }
1651
1652                 return TRUE;
1653         }
1654
1655         return FALSE;
1656 }
1657
1658 static EvViewSelection *
1659 find_selection_for_page (EvView *view,
1660                          gint    page)
1661 {
1662         GList *list;
1663
1664         for (list = view->selection_info.selections; list != NULL; list = list->next) {
1665                 EvViewSelection *selection;
1666
1667                 selection = (EvViewSelection *) list->data;
1668
1669                 if (selection->page == page)
1670                         return selection;
1671         }
1672
1673         return NULL;
1674 }
1675
1676 static gboolean
1677 ev_view_expose_event (GtkWidget      *widget,
1678                       GdkEventExpose *event)
1679 {
1680         EvView *view = EV_VIEW (widget);
1681         int i;
1682
1683         if (view->document == NULL)
1684                 return FALSE;
1685
1686         for (i = view->start_page; i <= view->end_page; i++) {
1687                 GdkRectangle page_area;
1688                 GtkBorder border;
1689
1690                 if (!get_page_extents (view, i, &page_area, &border))
1691                         continue;
1692
1693                 page_area.x -= view->scroll_x;
1694                 page_area.y -= view->scroll_y;
1695
1696                 draw_one_page (view, i, &page_area, &border, &(event->area));
1697
1698                 if (EV_IS_DOCUMENT_FIND (view->document))
1699                         highlight_find_results (view, i);
1700         }
1701
1702         return FALSE;
1703 }
1704
1705 static gboolean
1706 ev_view_button_press_event (GtkWidget      *widget,
1707                             GdkEventButton *event)
1708 {
1709         EvView *view = EV_VIEW (widget);
1710         
1711         if (!GTK_WIDGET_HAS_FOCUS (widget)) {
1712                 gtk_widget_grab_focus (widget);
1713         }
1714         
1715         view->pressed_button = event->button;
1716         view->selection_info.in_drag = FALSE;
1717         
1718         switch (event->button) {
1719                 case 1:
1720                         if (view->selection_info.selections) {
1721                                 if (location_in_selected_text (view,
1722                                                                event->x + view->scroll_x,
1723                                                                event->y + view->scroll_y)) {
1724                                         view->selection_info.in_drag = TRUE;
1725                                 } else {
1726                                         clear_selection (view);
1727                                 }
1728                                 
1729                                 gtk_widget_queue_draw (widget);
1730                         }
1731
1732                         view->selection_info.start.x = event->x + view->scroll_x;
1733                         view->selection_info.start.y = event->y + view->scroll_y;
1734                         
1735                         return TRUE;
1736                 case 2:
1737                         /* use root coordinates as reference point because
1738                          * scrolling changes window relative coordinates */
1739                         view->drag_info.start.x = event->x_root;
1740                         view->drag_info.start.y = event->y_root;
1741                         view->drag_info.hadj = gtk_adjustment_get_value (view->hadjustment);
1742                         view->drag_info.vadj = gtk_adjustment_get_value (view->vadjustment);
1743
1744                         ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG);
1745
1746                         return TRUE;
1747         }
1748         
1749         return FALSE;
1750 }
1751
1752 static void
1753 ev_view_drag_data_get (GtkWidget        *widget,
1754                        GdkDragContext   *context,
1755                        GtkSelectionData *selection_data,
1756                        guint             info,
1757                        guint             time)
1758 {
1759         EvView *view = EV_VIEW (widget);
1760
1761         if (view->selection_info.selections &&
1762             ev_document_can_get_text (view->document)) {
1763                 gchar *text;
1764
1765                 text = get_selected_text (view);
1766
1767                 gtk_selection_data_set_text (selection_data, text, strlen (text));
1768
1769                 g_free (text);
1770         }
1771 }
1772
1773 static gboolean
1774 selection_update_idle_cb (EvView *view)
1775 {
1776         GdkPoint point;
1777         point.x = view->motion_x;
1778         point.y = view->motion_y;
1779         compute_selections (view, &view->selection_info.start, &point);
1780
1781         view->selection_update_id = 0;
1782         return FALSE;
1783 }
1784
1785 static gboolean
1786 ev_view_motion_notify_event (GtkWidget      *widget,
1787                              GdkEventMotion *event)
1788 {
1789         EvView *view = EV_VIEW (widget);
1790
1791         if (!view->document)
1792                 return FALSE;
1793
1794         if (view->selection_info.in_drag) {
1795                 if (gtk_drag_check_threshold (widget,
1796                                               view->selection_info.start.x,
1797                                               view->selection_info.start.y,
1798                                               event->x, event->y)) {
1799                         GdkDragContext *context;
1800                         GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
1801
1802                         gtk_target_list_add_text_targets (target_list, 0);
1803
1804                         context = gtk_drag_begin (widget, target_list,
1805                                                   GDK_ACTION_COPY,
1806                                                   1, (GdkEvent *)event);
1807
1808                         view->selection_info.in_drag = FALSE;
1809
1810                         gtk_target_list_unref (target_list);
1811
1812                         return TRUE;
1813                 }
1814         }
1815         
1816         /* For the Evince 0.4.x release, we limit selection to un-rotated
1817          * documents only.
1818          */
1819         if (view->pressed_button == 1 && view->rotation == 0) {
1820                 view->selection_info.in_selection = TRUE;
1821                 view->motion_x = event->x + view->scroll_x;
1822                 view->motion_y = event->y + view->scroll_y;
1823
1824                 /* Queue an idle to handle the motion.  We do this because      
1825                  * handling any selection events in the motion could be slower  
1826                  * than new motion events reach us.  We always put it in the    
1827                  * idle to make sure we catch up and don't visibly lag the      
1828                  * mouse. */
1829                 if (! view->selection_update_id)
1830                         view->selection_update_id = g_idle_add ((GSourceFunc)selection_update_idle_cb, view);
1831
1832                 return TRUE;
1833         } else if (view->pressed_button == 2) {
1834                 if (!view->drag_info.in_drag) {
1835                         gboolean start;
1836
1837                         start = gtk_drag_check_threshold (widget,
1838                                                           view->drag_info.start.x,
1839                                                           view->drag_info.start.y,
1840                                                           event->x_root,
1841                                                           event->y_root);
1842                         view->drag_info.in_drag = start;
1843                 }
1844
1845                 if (view->drag_info.in_drag) {
1846                         int dx, dy;
1847                         gdouble dhadj_value, dvadj_value;
1848
1849                         dx = event->x_root - view->drag_info.start.x;
1850                         dy = event->y_root - view->drag_info.start.y;
1851
1852                         dhadj_value = view->hadjustment->page_size *
1853                                       (gdouble)dx / widget->allocation.width;
1854                         dvadj_value = view->vadjustment->page_size *
1855                                       (gdouble)dy / widget->allocation.height;
1856
1857                         /* clamp scrolling to visible area */
1858                         gtk_adjustment_set_value (view->hadjustment,
1859                                                   MIN(view->drag_info.hadj - dhadj_value,
1860                                                       view->hadjustment->upper -
1861                                                       view->hadjustment->page_size));
1862                         gtk_adjustment_set_value (view->vadjustment,
1863                                                   MIN(view->drag_info.vadj - dvadj_value,
1864                                                       view->vadjustment->upper -
1865                                                       view->vadjustment->page_size));
1866
1867                         return TRUE;
1868                 }
1869         /* For the Evince 0.4.x release, we limit links to un-rotated documents
1870          * only.
1871          */
1872         } else if (view->pressed_button <= 0 &&
1873                    view->rotation == 0) {
1874                 handle_link_over_xy (view, event->x, event->y);
1875                 return TRUE;
1876         }
1877
1878         return FALSE;
1879 }
1880
1881 static gboolean
1882 ev_view_button_release_event (GtkWidget      *widget,
1883                               GdkEventButton *event)
1884 {
1885         EvView *view = EV_VIEW (widget);
1886         EvLink *link;
1887
1888         if (view->pressed_button == 2) {
1889                 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1890         }
1891
1892         if (view->document) {
1893                 link = get_link_at_location (view, event->x + view->scroll_x,
1894                                              event->y + view->scroll_y);
1895         } else {
1896                 link = NULL;
1897         }
1898
1899         view->pressed_button = -1;
1900         view->drag_info.in_drag = FALSE;
1901
1902         if (view->selection_info.selections) {
1903                 ev_view_update_primary_selection (view);
1904                 
1905                 if (view->selection_info.in_drag) {
1906                         clear_selection (view);
1907                         gtk_widget_queue_draw (widget);
1908                 }
1909                 
1910                 view->selection_info.in_drag = FALSE;
1911         } else if (link) {
1912                 ev_view_goto_link (view, link);
1913         } else if (view->presentation) {
1914                 switch (event->button) {
1915                 case 1:
1916                         ev_view_next_page (view);       
1917                         return TRUE;
1918                 case 3:
1919                         ev_view_previous_page (view);   
1920                         return TRUE;
1921                 }
1922         }
1923  
1924         return FALSE;
1925 }
1926
1927 static gint
1928 ev_view_focus_in (GtkWidget     *widget,
1929                   GdkEventFocus *event)
1930 {
1931         if (EV_VIEW (widget)->pixbuf_cache)
1932                 ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
1933         gtk_widget_queue_draw (widget);
1934
1935         return FALSE;
1936 }
1937
1938 static gint
1939 ev_view_focus_out (GtkWidget     *widget,
1940                      GdkEventFocus *event)
1941 {
1942         if (EV_VIEW (widget)->pixbuf_cache)
1943                 ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
1944         gtk_widget_queue_draw (widget);
1945
1946         return FALSE;
1947 }
1948
1949 static gboolean
1950 ev_view_leave_notify_event (GtkWidget *widget, GdkEventCrossing   *event)
1951 {
1952         EvView *view = EV_VIEW (widget);
1953     
1954         ev_view_set_status (view, NULL);
1955
1956         if (view->cursor == EV_VIEW_CURSOR_LINK ||
1957             view->cursor == EV_VIEW_CURSOR_IBEAM)
1958                 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1959
1960         if (view->link_tooltip) {
1961                 view->hovered_link = NULL;
1962                 ev_tooltip_deactivate (EV_TOOLTIP (view->link_tooltip));
1963         }
1964
1965         return FALSE;
1966 }
1967
1968 static gboolean
1969 ev_view_enter_notify_event (GtkWidget *widget, GdkEventCrossing   *event)
1970 {
1971         EvView *view = EV_VIEW (widget);
1972
1973         handle_link_over_xy (view, event->x, event->y);
1974     
1975         return FALSE;
1976 }
1977
1978 static void
1979 ev_view_style_set (GtkWidget *widget,
1980                    GtkStyle  *old_style)
1981 {
1982         if (EV_VIEW (widget)->pixbuf_cache)
1983                 ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
1984
1985         GTK_WIDGET_CLASS (ev_view_parent_class)->style_set (widget, old_style);
1986 }
1987
1988
1989 /*** Drawing ***/
1990
1991 static guint32
1992 ev_gdk_color_to_rgb (const GdkColor *color)
1993 {
1994   guint32 result;
1995   result = (0xff0000 | (color->red & 0xff00));
1996   result <<= 8;
1997   result |= ((color->green & 0xff00) | (color->blue >> 8));
1998   return result;
1999 }
2000
2001 static void
2002 draw_rubberband (GtkWidget *widget, GdkWindow *window,
2003                  const GdkRectangle *rect, guchar alpha)
2004 {
2005         GdkGC *gc;
2006         GdkPixbuf *pixbuf;
2007         GdkColor *fill_color_gdk;
2008         guint fill_color;
2009
2010         fill_color_gdk = gdk_color_copy (&GTK_WIDGET (widget)->style->base[GTK_STATE_SELECTED]);
2011         fill_color = ev_gdk_color_to_rgb (fill_color_gdk) << 8 | alpha;
2012
2013         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
2014                                  rect->width, rect->height);
2015         gdk_pixbuf_fill (pixbuf, fill_color);
2016
2017         gdk_draw_pixbuf (window, NULL, pixbuf,
2018                          0, 0,
2019                          rect->x - EV_VIEW (widget)->scroll_x, rect->y - EV_VIEW (widget)->scroll_y,
2020                          rect->width, rect->height,
2021                          GDK_RGB_DITHER_NONE,
2022                          0, 0);
2023
2024         g_object_unref (pixbuf);
2025
2026         gc = gdk_gc_new (window);
2027         gdk_gc_set_rgb_fg_color (gc, fill_color_gdk);
2028         gdk_draw_rectangle (window, gc, FALSE,
2029                             rect->x - EV_VIEW (widget)->scroll_x, rect->y - EV_VIEW (widget)->scroll_y,
2030                             rect->width - 1,
2031                             rect->height - 1);
2032         g_object_unref (gc);
2033
2034         gdk_color_free (fill_color_gdk);
2035 }
2036
2037
2038 static void
2039 highlight_find_results (EvView *view, int page)
2040 {
2041         EvDocumentFind *find;
2042         int i, results = 0;
2043
2044         g_return_if_fail (EV_IS_DOCUMENT_FIND (view->document));
2045
2046         find = EV_DOCUMENT_FIND (view->document);
2047
2048         results = ev_document_find_get_n_results (find, page);
2049
2050         for (i = 0; i < results; i++) {
2051                 EvRectangle rectangle;
2052                 GdkRectangle view_rectangle;
2053                 guchar alpha;
2054
2055                 if (i == view->find_result && page == view->find_page) {
2056                         alpha = 0x90;
2057                 } else {
2058                         alpha = 0x20;
2059                 }
2060
2061                 ev_document_find_get_result (find, page, i, &rectangle);
2062                 doc_rect_to_view_rect (view, page, &rectangle, &view_rectangle);
2063                 draw_rubberband (GTK_WIDGET (view), GTK_WIDGET(view)->window,
2064                                  &view_rectangle, alpha);
2065         }
2066 }
2067
2068 static void
2069 draw_loading_text (EvView       *view,
2070                    GdkRectangle *page_area,
2071                    GdkRectangle *expose_area)
2072 {
2073         PangoLayout *layout;
2074         PangoFontDescription *font_desc;
2075         PangoRectangle logical_rect;
2076         double real_scale;
2077         int target_width;
2078
2079         const char *loading_text = _("Loading...");     
2080
2081         layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), loading_text);
2082
2083         font_desc = pango_font_description_new ();
2084
2085
2086         /* We set the font to be 10 points, get the size, and scale appropriately */
2087         pango_font_description_set_size (font_desc, 10 * PANGO_SCALE);
2088         pango_layout_set_font_description (layout, font_desc);
2089         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2090
2091         /* Make sure we fit the middle of the page */
2092         target_width = MAX (page_area->width / 2, 1);
2093         real_scale = ((double)target_width / (double) logical_rect.width) * (PANGO_SCALE * 10);
2094         pango_font_description_set_size (font_desc, (int)real_scale);
2095         pango_layout_set_font_description (layout, font_desc);
2096         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2097
2098         gtk_paint_layout (GTK_WIDGET (view)->style,
2099                           GTK_WIDGET (view)->window,
2100                           GTK_WIDGET_STATE (view),
2101                           FALSE,
2102                           page_area,
2103                           GTK_WIDGET (view),
2104                           NULL,
2105                           page_area->x + (target_width/2),
2106                           page_area->y + (page_area->height - logical_rect.height) / 2,
2107                           layout);
2108
2109         pango_font_description_free (font_desc);
2110         g_object_unref (layout);
2111 }
2112
2113 static void
2114 draw_one_page (EvView          *view,
2115                gint             page,
2116                GdkRectangle    *page_area,
2117                GtkBorder       *border,
2118                GdkRectangle    *expose_area)
2119 {
2120         gint width, height;
2121         GdkPixbuf *current_pixbuf;
2122         GdkRectangle overlap;
2123         GdkRectangle real_page_area;
2124         EvViewSelection *selection;
2125
2126         g_assert (view->document);
2127         if (! gdk_rectangle_intersect (page_area, expose_area, &overlap))
2128                 return;
2129
2130         selection = find_selection_for_page (view, page);
2131         ev_page_cache_get_size (view->page_cache,
2132                                 page, view->rotation,
2133                                 view->scale,
2134                                 &width, &height);
2135         /* Render the document itself */
2136         real_page_area = *page_area;
2137
2138         real_page_area.x += border->left;
2139         real_page_area.y += border->top;
2140         real_page_area.width -= (border->left + border->right);
2141         real_page_area.height -= (border->top + border->bottom);
2142
2143         ev_document_misc_paint_one_page (GTK_WIDGET(view)->window,
2144                                          GTK_WIDGET (view),
2145                                          page_area, border);
2146
2147         if (gdk_rectangle_intersect (&real_page_area, expose_area, &overlap)) {
2148                 GdkPixbuf *selection_pixbuf = NULL;
2149                 GdkPixbuf *scaled_image;
2150                 GdkPixbuf *scaled_selection;
2151
2152                 current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, page);
2153
2154                 /* Get the selection pixbuf iff we have something to draw */
2155                 if (current_pixbuf && view->selection_mode == EV_VIEW_SELECTION_TEXT && selection)
2156                         selection_pixbuf = ev_pixbuf_cache_get_selection_pixbuf (view->pixbuf_cache,
2157                                                                                  page,
2158                                                                                  view->scale,
2159                                                                                  NULL);
2160
2161                 if (current_pixbuf == NULL)
2162                         scaled_image = NULL;
2163                 else if (width == gdk_pixbuf_get_width (current_pixbuf) &&
2164                          height == gdk_pixbuf_get_height (current_pixbuf))
2165                         scaled_image = g_object_ref (current_pixbuf);
2166                 else
2167                         /* FIXME: We don't want to scale the whole area, just the right
2168                          * area of it */
2169                         scaled_image = gdk_pixbuf_scale_simple (current_pixbuf,
2170                                                                 width, height,
2171                                                                 GDK_INTERP_NEAREST);
2172
2173                 if (selection_pixbuf == NULL)
2174                         scaled_selection = NULL;
2175                 else if (width == gdk_pixbuf_get_width (selection_pixbuf) &&
2176                          height == gdk_pixbuf_get_height (selection_pixbuf))
2177                         scaled_selection = g_object_ref (selection_pixbuf);
2178                 else
2179                         /* FIXME: We don't want to scale the whole area, just the right
2180                          * area of it */
2181                         scaled_selection = gdk_pixbuf_scale_simple (selection_pixbuf,
2182                                                                     width, height,
2183                                                                     GDK_INTERP_NEAREST);
2184
2185                 if (scaled_image) {
2186                         gdk_draw_pixbuf (GTK_WIDGET(view)->window,
2187                                          GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
2188                                          scaled_image,
2189                                          overlap.x - real_page_area.x,
2190                                          overlap.y - real_page_area.y,
2191                                          overlap.x, overlap.y,
2192                                          overlap.width, overlap.height,
2193                                          GDK_RGB_DITHER_NORMAL,
2194                                          0, 0);
2195                         g_object_unref (scaled_image);
2196                 } else {
2197                         draw_loading_text (view,
2198                                            &real_page_area,
2199                                            expose_area);
2200                 }
2201
2202                 if (scaled_selection) {
2203                         gdk_draw_pixbuf (GTK_WIDGET(view)->window,
2204                                          GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
2205                                          scaled_selection,
2206                                          overlap.x - real_page_area.x,
2207                                          overlap.y - real_page_area.y,
2208                                          overlap.x, overlap.y,
2209                                          overlap.width, overlap.height,
2210                                          GDK_RGB_DITHER_NORMAL,
2211                                          0, 0);
2212                         g_object_unref (scaled_selection);
2213                 }
2214         }
2215 }
2216
2217 /*** GObject functions ***/
2218
2219 static void
2220 ev_view_finalize (GObject *object)
2221 {
2222         EvView *view = EV_VIEW (object);
2223
2224         LOG ("Finalize");
2225
2226         g_free (view->status);
2227         g_free (view->find_status);
2228
2229         clear_selection (view);
2230
2231         G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
2232 }
2233
2234 static void
2235 ev_view_destroy (GtkObject *object)
2236 {
2237         EvView *view = EV_VIEW (object);
2238
2239         if (view->document) {
2240                 g_object_unref (view->document);
2241                 view->document = NULL;
2242         }
2243
2244         if (view->pixbuf_cache) {
2245                 g_object_unref (view->pixbuf_cache);
2246                 view->pixbuf_cache = NULL;
2247         }
2248
2249         if (view->link_tooltip) {
2250                 gtk_widget_destroy (view->link_tooltip);
2251         }
2252
2253         ev_view_set_scroll_adjustments (view, NULL, NULL);
2254
2255         GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
2256 }
2257
2258 static void
2259 ev_view_set_property (GObject      *object,
2260                       guint         prop_id,
2261                       const GValue *value,
2262                       GParamSpec   *pspec)
2263 {
2264         EvView *view = EV_VIEW (object);
2265
2266         switch (prop_id)
2267         {
2268         case PROP_CONTINUOUS:
2269                 ev_view_set_continuous (view, g_value_get_boolean (value));
2270                 break;
2271         case PROP_DUAL_PAGE:
2272                 ev_view_set_dual_page (view, g_value_get_boolean (value));
2273                 break;
2274         case PROP_FULLSCREEN:
2275                 ev_view_set_fullscreen (view, g_value_get_boolean (value));
2276                 break;
2277         case PROP_PRESENTATION:
2278                 ev_view_set_presentation (view, g_value_get_boolean (value));
2279                 break;
2280         case PROP_SIZING_MODE:
2281                 ev_view_set_sizing_mode (view, g_value_get_enum (value));
2282                 break;
2283         case PROP_ZOOM:
2284                 ev_view_set_zoom (view, g_value_get_double (value), FALSE);
2285                 break;
2286         case PROP_ROTATION:
2287                 ev_view_set_rotation (view, g_value_get_int (value));
2288                 break;
2289         default:
2290                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2291         }
2292 }
2293
2294 static void
2295 ev_view_get_property (GObject *object,
2296                       guint prop_id,
2297                       GValue *value,
2298                       GParamSpec *pspec)
2299 {
2300         EvView *view = EV_VIEW (object);
2301
2302         switch (prop_id)
2303         {
2304         case PROP_STATUS:
2305                 g_value_set_string (value, view->status);
2306                 break;
2307         case PROP_FIND_STATUS:
2308                 g_value_set_string (value, view->status);
2309                 break;
2310         case PROP_CONTINUOUS:
2311                 g_value_set_boolean (value, view->continuous);
2312                 break;
2313         case PROP_DUAL_PAGE:
2314                 g_value_set_boolean (value, view->dual_page);
2315                 break;
2316         case PROP_FULLSCREEN:
2317                 g_value_set_boolean (value, view->fullscreen);
2318                 break;
2319         case PROP_PRESENTATION:
2320                 g_value_set_boolean (value, view->presentation);
2321                 break;
2322         case PROP_SIZING_MODE:
2323                 g_value_set_enum (value, view->sizing_mode);
2324                 break;
2325         case PROP_ZOOM:
2326                 g_value_set_double (value, view->scale);
2327                 break;
2328         case PROP_ROTATION:
2329                 g_value_set_int (value, view->rotation);
2330                 break;
2331         default:
2332                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2333         }
2334 }
2335
2336 static void
2337 ev_view_class_init (EvViewClass *class)
2338 {
2339         GObjectClass *object_class = G_OBJECT_CLASS (class);
2340         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
2341         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
2342         GtkBindingSet *binding_set;
2343
2344         object_class->finalize = ev_view_finalize;
2345         object_class->set_property = ev_view_set_property;
2346         object_class->get_property = ev_view_get_property;
2347
2348         widget_class->expose_event = ev_view_expose_event;
2349         widget_class->button_press_event = ev_view_button_press_event;
2350         widget_class->motion_notify_event = ev_view_motion_notify_event;
2351         widget_class->button_release_event = ev_view_button_release_event;
2352         widget_class->focus_in_event = ev_view_focus_in;
2353         widget_class->focus_out_event = ev_view_focus_out;
2354         widget_class->size_request = ev_view_size_request;
2355         widget_class->size_allocate = ev_view_size_allocate;
2356         widget_class->realize = ev_view_realize;
2357         widget_class->scroll_event = ev_view_scroll_event;
2358         widget_class->enter_notify_event = ev_view_enter_notify_event;
2359         widget_class->leave_notify_event = ev_view_leave_notify_event;
2360         widget_class->style_set = ev_view_style_set;
2361         widget_class->drag_data_get = ev_view_drag_data_get;
2362         gtk_object_class->destroy = ev_view_destroy;
2363
2364         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
2365         class->binding_activated = ev_view_binding_activated;
2366
2367         widget_class->set_scroll_adjustments_signal =
2368             g_signal_new ("set-scroll-adjustments",
2369                           G_OBJECT_CLASS_TYPE (object_class),
2370                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2371                           G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
2372                           NULL, NULL,
2373                           ev_marshal_VOID__OBJECT_OBJECT,
2374                           G_TYPE_NONE, 2,
2375                           GTK_TYPE_ADJUSTMENT,
2376                           GTK_TYPE_ADJUSTMENT);
2377
2378         signals[SIGNAL_BINDING_ACTIVATED] = g_signal_new ("binding_activated",
2379                          G_TYPE_FROM_CLASS (object_class),
2380                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2381                          G_STRUCT_OFFSET (EvViewClass, binding_activated),
2382                          NULL, NULL,
2383                          ev_marshal_VOID__ENUM_BOOLEAN,
2384                          G_TYPE_NONE, 2,
2385                          GTK_TYPE_SCROLL_TYPE,
2386                          G_TYPE_BOOLEAN);
2387
2388         signals[SIGNAL_ZOOM_INVALID] = g_signal_new ("zoom-invalid",
2389                          G_TYPE_FROM_CLASS (object_class),
2390                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2391                          G_STRUCT_OFFSET (EvViewClass, zoom_invalid),
2392                          NULL, NULL,
2393                          ev_marshal_VOID__VOID,
2394                          G_TYPE_NONE, 0, G_TYPE_NONE);
2395         signals[SIGNAL_EXTERNAL_LINK] = g_signal_new ("external-link",
2396                          G_TYPE_FROM_CLASS (object_class),
2397                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2398                          G_STRUCT_OFFSET (EvViewClass, external_link),
2399                          NULL, NULL,
2400                          g_cclosure_marshal_VOID__OBJECT,
2401                          G_TYPE_NONE, 1,
2402                          G_TYPE_OBJECT);
2403
2404         g_object_class_install_property (object_class,
2405                                          PROP_STATUS,
2406                                          g_param_spec_string ("status",
2407                                                               "Status Message",
2408                                                               "The status message",
2409                                                               NULL,
2410                                                               G_PARAM_READABLE));
2411
2412         g_object_class_install_property (object_class,
2413                                          PROP_FIND_STATUS,
2414                                          g_param_spec_string ("find-status",
2415                                                               "Find Status Message",
2416                                                               "The find status message",
2417                                                               NULL,
2418                                                               G_PARAM_READABLE));
2419
2420         g_object_class_install_property (object_class,
2421                                          PROP_CONTINUOUS,
2422                                          g_param_spec_boolean ("continuous",
2423                                                                "Continuous",
2424                                                                "Continuous scrolling mode",
2425                                                                TRUE,
2426                                                                G_PARAM_READWRITE));
2427
2428         g_object_class_install_property (object_class,
2429                                          PROP_DUAL_PAGE,
2430                                          g_param_spec_boolean ("dual-page",
2431                                                                "Dual Page",
2432                                                                "Two pages visible at once",
2433                                                                FALSE,
2434                                                                G_PARAM_READWRITE));
2435         g_object_class_install_property (object_class,
2436                                          PROP_FULLSCREEN,
2437                                          g_param_spec_boolean ("fullscreen",
2438                                                                "Full Screen",
2439                                                                "Draw page in a fullscreen fashion",
2440                                                                FALSE,
2441                                                                G_PARAM_READWRITE));
2442         g_object_class_install_property (object_class,
2443                                          PROP_PRESENTATION,
2444                                          g_param_spec_boolean ("presentation",
2445                                                                "Presentation",
2446                                                                "Draw page in presentation mode",
2447                                                                TRUE,
2448                                                                G_PARAM_READWRITE));
2449
2450         g_object_class_install_property (object_class,
2451                                          PROP_SIZING_MODE,
2452                                          g_param_spec_enum ("sizing-mode",
2453                                                             "Sizing Mode",
2454                                                             "Sizing Mode",
2455                                                             EV_TYPE_SIZING_MODE,
2456                                                             EV_SIZING_FIT_WIDTH,
2457                                                             G_PARAM_READWRITE));
2458
2459         g_object_class_install_property (object_class,
2460                                          PROP_ZOOM,
2461                                          g_param_spec_double ("zoom",
2462                                                               "Zoom factor",
2463                                                                "Zoom factor",
2464                                                                MIN_SCALE,
2465                                                                MAX_SCALE,
2466                                                                1.0,
2467                                                                G_PARAM_READWRITE));
2468         g_object_class_install_property (object_class,
2469                                          PROP_ROTATION,
2470                                          g_param_spec_double ("rotation",
2471                                                               "Rotation",
2472                                                                "Rotation",
2473                                                                0,
2474                                                                360,
2475                                                                0,
2476                                                                G_PARAM_READWRITE));
2477
2478         binding_set = gtk_binding_set_by_class (class);
2479
2480         add_scroll_binding_keypad (binding_set, GDK_Left,  GTK_SCROLL_STEP_BACKWARD, TRUE);
2481         add_scroll_binding_keypad (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD,  TRUE);
2482         add_scroll_binding_keypad (binding_set, GDK_Up,    GTK_SCROLL_STEP_BACKWARD, FALSE);
2483         add_scroll_binding_keypad (binding_set, GDK_Down,  GTK_SCROLL_STEP_FORWARD,  FALSE);
2484 }
2485
2486 static void
2487 ev_view_init (EvView *view)
2488 {
2489         GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
2490
2491         view->spacing = 5;
2492         view->scale = 1.0;
2493         view->current_page = 0;
2494         view->pressed_button = -1;
2495         view->cursor = EV_VIEW_CURSOR_NORMAL;
2496         view->drag_info.in_drag = FALSE;
2497         view->selection_info.in_selection = FALSE;
2498         view->selection_info.in_drag = FALSE;
2499         view->selection_mode = EV_VIEW_SELECTION_TEXT;
2500         view->continuous = TRUE;
2501         view->dual_page = FALSE;
2502         view->presentation = FALSE;
2503         view->fullscreen = FALSE;
2504         view->sizing_mode = EV_SIZING_FIT_WIDTH;
2505         view->pending_scroll = SCROLL_TO_KEEP_POSITION;
2506 }
2507
2508 /*** Callbacks ***/
2509
2510 static void
2511 find_changed_cb (EvDocument *document, int page, EvView *view)
2512 {
2513         jump_to_find_page (view, EV_VIEW_FIND_NEXT);
2514         jump_to_find_result (view);
2515         update_find_status_message (view);
2516
2517         if (view->current_page == page)
2518                 gtk_widget_queue_draw (GTK_WIDGET (view));
2519 }
2520
2521 static void
2522 job_finished_cb (EvPixbufCache *pixbuf_cache,
2523                  EvView        *view)
2524 {
2525         gtk_widget_queue_draw (GTK_WIDGET (view));
2526 }
2527
2528 static void
2529 page_changed_cb (EvPageCache *page_cache,
2530                  int          new_page,
2531                  EvView      *view)
2532 {
2533         if (view->current_page != new_page) {
2534
2535                 view->current_page = new_page;
2536                 view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2537                 gtk_widget_queue_resize (GTK_WIDGET (view));
2538
2539                 if (EV_IS_DOCUMENT_FIND (view->document)) {
2540                         view->find_page = new_page;
2541                         view->find_result = 0;
2542                         update_find_status_message (view);
2543                 }
2544         }
2545 }
2546
2547 static void on_adjustment_value_changed (GtkAdjustment  *adjustment,
2548                                          EvView *view)
2549 {
2550         int dx = 0, dy = 0;
2551
2552         if (! GTK_WIDGET_REALIZED (view))
2553                 return;
2554
2555         if (view->hadjustment) {
2556                 dx = view->scroll_x - (int) view->hadjustment->value;
2557                 view->scroll_x = (int) view->hadjustment->value;
2558         } else {
2559                 view->scroll_x = 0;
2560         }
2561
2562         if (view->vadjustment) {
2563                 dy = view->scroll_y - (int) view->vadjustment->value;
2564                 view->scroll_y = (int) view->vadjustment->value;
2565         } else {
2566                 view->scroll_y = 0;
2567         }
2568
2569
2570         if (view->pending_resize)
2571                 gtk_widget_queue_draw (GTK_WIDGET (view));
2572         else
2573                 gdk_window_scroll (GTK_WIDGET (view)->window, dx, dy);
2574
2575
2576         if (view->document)
2577                 view_update_range_and_current_page (view);
2578 }
2579
2580 GtkWidget*
2581 ev_view_new (void)
2582 {
2583         GtkWidget *view;
2584
2585         view = g_object_new (EV_TYPE_VIEW, NULL);
2586
2587         return view;
2588 }
2589
2590 static void
2591 setup_caches (EvView *view)
2592 {
2593         view->page_cache = ev_page_cache_get (view->document);
2594         g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
2595         view->pixbuf_cache = ev_pixbuf_cache_new (GTK_WIDGET (view), view->document);
2596         g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
2597 }
2598
2599 static void
2600 clear_caches (EvView *view)
2601 {
2602         if (view->pixbuf_cache) {
2603                 g_object_unref (view->pixbuf_cache);
2604                 view->pixbuf_cache = NULL;
2605         }
2606
2607         if (view->page_cache) {
2608                 view->page_cache = NULL;
2609         }
2610 }
2611
2612 void
2613 ev_view_set_document (EvView     *view,
2614                       EvDocument *document)
2615 {
2616         g_return_if_fail (EV_IS_VIEW (view));
2617
2618         if (document != view->document) {
2619                 clear_caches (view);
2620
2621                 if (view->document) {
2622                         g_signal_handlers_disconnect_by_func (view->document,
2623                                                               find_changed_cb,
2624                                                               view);
2625                         g_object_unref (view->document);
2626                         view->page_cache = NULL;
2627
2628                 }
2629
2630                 view->document = document;
2631                 view->find_page = 0;
2632                 view->find_result = 0;
2633
2634                 if (view->document) {
2635                         g_object_ref (view->document);
2636                         if (EV_IS_DOCUMENT_FIND (view->document)) {
2637                                 g_signal_connect (view->document,
2638                                                   "find_changed",
2639                                                   G_CALLBACK (find_changed_cb),
2640                                                   view);
2641                         }
2642
2643                         setup_caches (view);
2644                 }
2645
2646                 gtk_widget_queue_resize (GTK_WIDGET (view));
2647         }
2648 }
2649
2650 /*** Zoom and sizing mode ***/
2651
2652 #define EPSILON 0.0000001
2653 void
2654 ev_view_set_zoom (EvView   *view,
2655                   double    factor,
2656                   gboolean  relative)
2657 {
2658         double scale;
2659
2660         if (relative)
2661                 scale = view->scale * factor;
2662         else
2663                 scale = factor;
2664
2665         scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
2666
2667         if (ABS (view->scale - scale) < EPSILON)
2668                 return;
2669
2670         view->scale = scale;
2671         view->pending_resize = TRUE;
2672
2673         gtk_widget_queue_resize (GTK_WIDGET (view));
2674
2675         g_object_notify (G_OBJECT (view), "zoom");
2676 }
2677
2678 double
2679 ev_view_get_zoom (EvView *view)
2680 {
2681         return view->scale;
2682 }
2683
2684 gboolean
2685 ev_view_get_continuous (EvView *view)
2686 {
2687         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2688
2689         return view->continuous;
2690 }
2691
2692 void
2693 ev_view_set_continuous (EvView   *view,
2694                         gboolean  continuous)
2695 {
2696         g_return_if_fail (EV_IS_VIEW (view));
2697
2698         continuous = continuous != FALSE;
2699
2700         if (view->continuous != continuous) {
2701                 view->continuous = continuous;
2702                 view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2703                 gtk_widget_queue_resize (GTK_WIDGET (view));
2704         }
2705
2706         g_object_notify (G_OBJECT (view), "continuous");
2707 }
2708
2709 gboolean
2710 ev_view_get_dual_page (EvView *view)
2711 {
2712         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2713
2714         return view->dual_page;
2715 }
2716
2717 void
2718 ev_view_set_dual_page (EvView   *view,
2719                        gboolean  dual_page)
2720 {
2721         g_return_if_fail (EV_IS_VIEW (view));
2722
2723         dual_page = dual_page != FALSE;
2724
2725         if (view->dual_page == dual_page)
2726                 return;
2727
2728         view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2729         view->dual_page = dual_page;
2730         /* FIXME: if we're keeping the pixbuf cache around, we should extend the
2731          * preload_cache_size to be 2 if dual_page is set.
2732          */
2733         gtk_widget_queue_resize (GTK_WIDGET (view));
2734
2735         g_object_notify (G_OBJECT (view), "dual-page");
2736 }
2737
2738 void
2739 ev_view_set_fullscreen (EvView   *view,
2740                          gboolean  fullscreen)
2741 {
2742         g_return_if_fail (EV_IS_VIEW (view));
2743
2744         fullscreen = fullscreen != FALSE;
2745
2746         if (view->fullscreen == fullscreen) 
2747                 return;
2748                 
2749         view->fullscreen = fullscreen;
2750         gtk_widget_queue_resize (GTK_WIDGET (view));
2751         
2752         g_object_notify (G_OBJECT (view), "fullscreen");
2753 }
2754
2755 gboolean
2756 ev_view_get_fullscreen (EvView *view)
2757 {
2758         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2759
2760         return view->fullscreen;
2761 }
2762
2763 void
2764 ev_view_set_presentation (EvView   *view,
2765                           gboolean  presentation)
2766 {
2767         g_return_if_fail (EV_IS_VIEW (view));
2768
2769         presentation = presentation != FALSE;
2770
2771         if (view->presentation == presentation)
2772                 return;
2773
2774         view->presentation = presentation;
2775         view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2776         gtk_widget_queue_resize (GTK_WIDGET (view));
2777
2778         if (GTK_WIDGET_REALIZED (view)) {
2779                 if (view->presentation)
2780                         gdk_window_set_background (GTK_WIDGET(view)->window,
2781                                                    &GTK_WIDGET (view)->style->black);
2782                 else
2783                         gdk_window_set_background (GTK_WIDGET(view)->window,
2784                                                    &GTK_WIDGET (view)->style->mid [GTK_STATE_NORMAL]);
2785         }
2786
2787
2788         g_object_notify (G_OBJECT (view), "presentation");
2789 }
2790
2791 gboolean
2792 ev_view_get_presentation (EvView *view)
2793 {
2794         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2795
2796         return view->presentation;
2797 }
2798
2799 void
2800 ev_view_set_sizing_mode (EvView       *view,
2801                          EvSizingMode  sizing_mode)
2802 {
2803         g_return_if_fail (EV_IS_VIEW (view));
2804
2805         if (view->sizing_mode == sizing_mode)
2806                 return;
2807
2808         view->sizing_mode = sizing_mode;
2809         gtk_widget_queue_resize (GTK_WIDGET (view));
2810
2811         g_object_notify (G_OBJECT (view), "sizing-mode");
2812 }
2813
2814 EvSizingMode
2815 ev_view_get_sizing_mode (EvView *view)
2816 {
2817         g_return_val_if_fail (EV_IS_VIEW (view), EV_SIZING_FREE);
2818
2819         return view->sizing_mode;
2820 }
2821
2822 gboolean
2823 ev_view_can_zoom_in (EvView *view)
2824 {
2825         return view->scale * ZOOM_IN_FACTOR <= MAX_SCALE;
2826 }
2827
2828 gboolean
2829 ev_view_can_zoom_out (EvView *view)
2830 {
2831         return view->scale * ZOOM_OUT_FACTOR >= MIN_SCALE;
2832 }
2833
2834 void
2835 ev_view_zoom_in (EvView *view)
2836 {
2837         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2838
2839         view->pending_scroll = SCROLL_TO_CENTER;
2840         ev_view_set_zoom (view, ZOOM_IN_FACTOR, TRUE);
2841 }
2842
2843 void
2844 ev_view_zoom_out (EvView *view)
2845 {
2846         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2847
2848         view->pending_scroll = SCROLL_TO_CENTER;
2849         ev_view_set_zoom (view, ZOOM_OUT_FACTOR, TRUE);
2850 }
2851
2852 void
2853 ev_view_rotate_right (EvView *view)
2854 {
2855         int rotation = view->rotation + 90;
2856
2857         if (rotation >= 360) {
2858                 rotation -= 360;
2859         }
2860
2861         ev_view_set_rotation (view, rotation);
2862 }
2863
2864 void
2865 ev_view_rotate_left (EvView *view)
2866 {
2867         int rotation = view->rotation - 90;
2868
2869         if (rotation < 0) {
2870                 rotation += 360;
2871         }
2872
2873         ev_view_set_rotation (view, rotation);
2874 }
2875
2876 void
2877 ev_view_set_rotation (EvView *view, int rotation)
2878 {
2879         view->rotation = rotation;
2880
2881         if (view->pixbuf_cache) {
2882                 ev_pixbuf_cache_clear (view->pixbuf_cache);
2883                 gtk_widget_queue_resize (GTK_WIDGET (view));
2884         }
2885
2886         if (rotation != 0)
2887                 clear_selection (view);
2888
2889         g_object_notify (G_OBJECT (view), "rotation");
2890 }
2891
2892 int
2893 ev_view_get_rotation (EvView *view)
2894 {
2895         return view->rotation;
2896 }
2897
2898 static double
2899 zoom_for_size_fit_width (int doc_width,
2900                          int doc_height,
2901                          int target_width,
2902                          int target_height,
2903                          int vsb_width)
2904 {
2905         double scale;
2906
2907         scale = (double)target_width / doc_width;
2908
2909         if (doc_height * scale > target_height)
2910                 scale = (double) (target_width - vsb_width) / doc_width;
2911
2912         return scale;
2913 }
2914
2915 static double
2916 zoom_for_size_fit_height (int doc_width,
2917                           int doc_height,
2918                           int target_width,
2919                           int target_height,
2920                           int vsb_height)
2921 {
2922         double scale;
2923
2924         scale = (double)target_height / doc_height;
2925
2926         if (doc_width * scale > target_width)
2927                 scale = (double) (target_height - vsb_height) / doc_height;
2928
2929         return scale;
2930 }
2931
2932 static double
2933 zoom_for_size_best_fit (int doc_width,
2934                         int doc_height,
2935                         int target_width,
2936                         int target_height,
2937                         int vsb_width,
2938                         int hsb_width)
2939 {
2940         double w_scale;
2941         double h_scale;
2942
2943         w_scale = (double)target_width / doc_width;
2944         h_scale = (double)target_height / doc_height;
2945
2946         if (doc_height * w_scale > target_height)
2947                 w_scale = (double) (target_width - vsb_width) / doc_width;
2948         if (doc_width * h_scale > target_width)
2949                 h_scale = (double) (target_height - hsb_width) / doc_height;
2950
2951         return MIN (w_scale, h_scale);
2952 }
2953
2954
2955 static void
2956 ev_view_zoom_for_size_presentation (EvView *view,
2957                                     int     width,
2958                                     int     height)
2959 {
2960         int doc_width, doc_height;
2961         gdouble scale;
2962
2963         ev_page_cache_get_size (view->page_cache,
2964                                 view->current_page,
2965                                 view->rotation,
2966                                 1.0,
2967                                 &doc_width,
2968                                 &doc_height);
2969         scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, 0, 0);
2970         ev_view_set_zoom (view, scale, FALSE);
2971 }
2972
2973 static void
2974 ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
2975                            int     width,
2976                            int     height,
2977                            int     vsb_width,
2978                            int     hsb_height)
2979 {
2980         int doc_width, doc_height;
2981         GtkBorder border;
2982         gdouble scale;
2983
2984         ev_page_cache_get_max_width (view->page_cache,
2985                                      view->rotation,
2986                                      1.0,
2987                                      &doc_width);
2988         ev_page_cache_get_max_height (view->page_cache,
2989                                       view->rotation,
2990                                       1.0,
2991                                       &doc_height);
2992         compute_border (view, doc_width, doc_height, &border);
2993
2994         doc_width = doc_width * 2;
2995         width -= (2 * (border.left + border.right) + 3 * view->spacing);
2996         height -= (border.top + border.bottom + 2 * view->spacing - 1);
2997
2998         /* FIXME: We really need to calculate the overall height here, not the
2999          * page height.  We assume there's always a vertical scrollbar for
3000          * now.  We need to fix this. */
3001         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
3002                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
3003         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
3004                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
3005         else
3006                 g_assert_not_reached ();
3007
3008         ev_view_set_zoom (view, scale, FALSE);
3009 }
3010
3011 static void
3012 ev_view_zoom_for_size_continuous (EvView *view,
3013                                   int     width,
3014                                   int     height,
3015                                   int     vsb_width,
3016                                   int     hsb_height)
3017 {
3018         int doc_width, doc_height;
3019         GtkBorder border;
3020         gdouble scale;
3021
3022         ev_page_cache_get_max_width (view->page_cache,
3023                                      view->rotation,
3024                                      1.0,
3025                                      &doc_width);
3026         ev_page_cache_get_max_height (view->page_cache,
3027                                       view->rotation,
3028                                       1.0,
3029                                       &doc_height);
3030         compute_border (view, doc_width, doc_height, &border);
3031
3032         width -= (border.left + border.right + 2 * view->spacing);
3033         height -= (border.top + border.bottom + 2 * view->spacing - 1);
3034
3035         /* FIXME: We really need to calculate the overall height here, not the
3036          * page height.  We assume there's always a vertical scrollbar for
3037          * now.  We need to fix this. */
3038         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
3039                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
3040         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
3041                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
3042         else
3043                 g_assert_not_reached ();
3044
3045         ev_view_set_zoom (view, scale, FALSE);
3046 }
3047
3048 static void
3049 ev_view_zoom_for_size_dual_page (EvView *view,
3050                                  int     width,
3051                                  int     height,
3052                                  int     vsb_width,
3053                                  int     hsb_height)
3054 {
3055         GtkBorder border;
3056         gint doc_width, doc_height;
3057         gdouble scale;
3058         gint other_page;
3059
3060         other_page = view->current_page ^ 1;
3061
3062         /* Find the largest of the two. */
3063         ev_page_cache_get_size (view->page_cache,
3064                                 view->current_page,
3065                                 view->rotation,
3066                                 1.0,
3067                                 &doc_width, &doc_height);
3068
3069         if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
3070                 gint width_2, height_2;
3071                 ev_page_cache_get_size (view->page_cache,
3072                                         other_page,
3073                                         view->rotation,
3074                                         1.0,
3075                                         &width_2, &height_2);
3076                 if (width_2 > doc_width)
3077                         doc_width = width_2;
3078                 if (height_2 > doc_height)
3079                         doc_height = height_2;
3080         }
3081         compute_border (view, doc_width, doc_height, &border);
3082
3083         doc_width = doc_width * 2;
3084         width -= ((border.left + border.right)* 2 + 3 * view->spacing);
3085         height -= (border.top + border.bottom + 2 * view->spacing);
3086
3087         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
3088                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
3089         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
3090                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
3091         else
3092                 g_assert_not_reached ();
3093
3094         ev_view_set_zoom (view, scale, FALSE);
3095 }
3096
3097 static void
3098 ev_view_zoom_for_size_single_page (EvView *view,
3099                                    int     width,
3100                                    int     height,
3101                                    int     vsb_width,
3102                                    int     hsb_height)
3103 {
3104         int doc_width, doc_height;
3105         GtkBorder border;
3106         gdouble scale;
3107
3108         ev_page_cache_get_size (view->page_cache,
3109                                 view->current_page,
3110                                 view->rotation,
3111                                 1.0,
3112                                 &doc_width,
3113                                 &doc_height);
3114         /* Get an approximate border */
3115         compute_border (view, width, height, &border);
3116
3117         width -= (border.left + border.right + 2 * view->spacing);
3118         height -= (border.top + border.bottom + 2 * view->spacing);
3119
3120         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
3121                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
3122         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
3123                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
3124         else
3125                 g_assert_not_reached ();
3126
3127         ev_view_set_zoom (view, scale, FALSE);
3128 }
3129
3130 void
3131 ev_view_set_zoom_for_size (EvView *view,
3132                            int     width,
3133                            int     height,
3134                            int     vsb_width,
3135                            int     hsb_height)
3136 {
3137         g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
3138                           view->sizing_mode == EV_SIZING_BEST_FIT);
3139         g_return_if_fail (width >= 0);
3140         g_return_if_fail (height >= 0);
3141
3142         if (view->document == NULL)
3143                 return;
3144
3145         if (view->presentation)
3146                 ev_view_zoom_for_size_presentation (view, width, height);
3147         else if (view->continuous && view->dual_page)
3148                 ev_view_zoom_for_size_continuous_and_dual_page (view, width, height, vsb_width, hsb_height);
3149         else if (view->continuous)
3150                 ev_view_zoom_for_size_continuous (view, width, height, vsb_width, hsb_height);
3151         else if (view->dual_page)
3152                 ev_view_zoom_for_size_dual_page (view, width, height, vsb_width, hsb_height);
3153         else
3154                 ev_view_zoom_for_size_single_page (view, width, height, vsb_width, hsb_height);
3155 }
3156
3157 /*** Status text messages ***/
3158
3159 const char *
3160 ev_view_get_status (EvView *view)
3161 {
3162         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
3163
3164         return view->status;
3165 }
3166
3167 static void
3168 ev_view_set_status (EvView *view, const char *message)
3169 {
3170         g_return_if_fail (EV_IS_VIEW (view));
3171
3172         if (message != view->status) {
3173                 g_free (view->status);
3174                 view->status = g_strdup (message);
3175                 g_object_notify (G_OBJECT (view), "status");
3176         }
3177 }
3178
3179 static void
3180 update_find_status_message (EvView *view)
3181 {
3182         char *message;
3183
3184         if (view->current_page == view->find_page) {
3185                 int results;
3186
3187                 results = ev_document_find_get_n_results
3188                                 (EV_DOCUMENT_FIND (view->document),
3189                                  view->current_page);
3190                 /* TRANS: Sometimes this could be better translated as
3191                    "%d hit(s) on this page".  Therefore this string
3192                    contains plural cases. */
3193                 message = g_strdup_printf (ngettext ("%d found on this page",
3194                                                      "%d found on this page",
3195                                                      results),
3196                                            results);
3197         } else {
3198                 double percent;
3199
3200                 percent = ev_document_find_get_progress
3201                                 (EV_DOCUMENT_FIND (view->document));
3202                 if (percent >= (1.0 - 1e-10)) {
3203                         message = g_strdup (_("Not found"));
3204                 } else {
3205                         message = g_strdup_printf (_("%3d%% remaining to search"),
3206                                                    (int) ((1.0 - percent) * 100));
3207                 }
3208
3209         }
3210         ev_view_set_find_status (view, message);
3211         g_free (message);
3212 }
3213
3214 const char *
3215 ev_view_get_find_status (EvView *view)
3216 {
3217         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
3218
3219         return view->find_status;
3220 }
3221
3222 static void
3223 ev_view_set_find_status (EvView *view, const char *message)
3224 {
3225         g_return_if_fail (EV_IS_VIEW (view));
3226
3227         g_free (view->find_status);
3228         view->find_status = g_strdup (message);
3229         g_object_notify (G_OBJECT (view), "find-status");
3230 }
3231
3232 /*** Find ***/
3233
3234 static void
3235 jump_to_find_result (EvView *view)
3236 {
3237         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
3238         EvRectangle rect;
3239         GdkRectangle view_rect;
3240         int n_results;
3241         int page = view->find_page;
3242
3243         n_results = ev_document_find_get_n_results (find, page);
3244
3245         if (n_results > 0  && view->find_result < n_results) {
3246                 ev_document_find_get_result
3247                         (find, page, view->find_result, &rect);
3248
3249                 doc_rect_to_view_rect (view, page, &rect, &view_rect);
3250                 ensure_rectangle_is_visible (view, &view_rect);
3251         }
3252 }
3253
3254 static void
3255 jump_to_find_page (EvView *view, EvViewFindDirection direction)
3256 {
3257         int n_pages, i;
3258
3259         n_pages = ev_page_cache_get_n_pages (view->page_cache);
3260
3261         for (i = 0; i < n_pages; i++) {
3262                 int has_results;
3263                 int page;
3264                 
3265                 if (direction == EV_VIEW_FIND_NEXT)
3266                         page = view->find_page + i;
3267                 else
3268                         page = view->find_page - i;
3269
3270
3271                 if (page >= n_pages) {
3272                         page = page - n_pages;
3273                 }
3274                 if (page < 0) 
3275                         page = page + n_pages;
3276                 
3277                 has_results = ev_document_find_page_has_results
3278                                 (EV_DOCUMENT_FIND (view->document), page);
3279                 if (has_results == -1) {
3280                         view->find_page = page;
3281                         break;
3282                 } else if (has_results == 1) {
3283                         ev_page_cache_set_current_page (view->page_cache, page);
3284                         break;
3285                 }
3286         }
3287 }
3288
3289 gboolean
3290 ev_view_can_find_next (EvView *view)
3291 {
3292         if (EV_IS_DOCUMENT_FIND (view->document)) {
3293                 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
3294                 int i, n_pages;
3295
3296                 n_pages = ev_page_cache_get_n_pages (view->page_cache);
3297                 for (i = 0; i < n_pages; i++) {
3298                         if (ev_document_find_get_n_results (find, i) > 0) {
3299                                 return TRUE;
3300                         }
3301                 }
3302         }
3303
3304         return FALSE;
3305 }
3306
3307 void
3308 ev_view_find_next (EvView *view)
3309 {
3310         int n_results, n_pages;
3311         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
3312
3313         n_results = ev_document_find_get_n_results (find, view->current_page);
3314
3315         n_pages = ev_page_cache_get_n_pages (view->page_cache);
3316
3317         view->find_result++;
3318
3319         if (view->find_result >= n_results) {
3320
3321                 view->find_result = 0;
3322                 view->find_page++;
3323                 if (view->find_page >= n_pages) {
3324                         view->find_page = 0;
3325                 }
3326
3327                 jump_to_find_page (view, EV_VIEW_FIND_NEXT);
3328                 jump_to_find_result (view);
3329         } else {
3330                 jump_to_find_result (view);
3331                 gtk_widget_queue_draw (GTK_WIDGET (view));
3332         }
3333 }
3334
3335 void
3336 ev_view_find_previous (EvView *view)
3337 {
3338         int n_results, n_pages;
3339         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
3340         EvPageCache *page_cache;
3341
3342         page_cache = ev_page_cache_get (view->document);
3343
3344         n_results = ev_document_find_get_n_results (find, view->current_page);
3345
3346         n_pages = ev_page_cache_get_n_pages (page_cache);
3347
3348         view->find_result--;
3349
3350         if (view->find_result < 0) {
3351
3352                 view->find_page--;
3353                 if (view->find_page < 0) {
3354                         view->find_page = n_pages - 1;
3355                 }
3356
3357                 jump_to_find_page (view, EV_VIEW_FIND_PREV);
3358                 view->find_result = ev_document_find_get_n_results (find, view->current_page) - 1;
3359                 jump_to_find_result (view);
3360         } else {
3361                 jump_to_find_result (view);
3362                 gtk_widget_queue_draw (GTK_WIDGET (view));
3363         }
3364 }
3365
3366 /*** Selections ***/
3367
3368 /* compute_new_selection_rect/text calculates the area currently selected by
3369  * view_rect.  each handles a different mode;
3370  */
3371 static GList *
3372 compute_new_selection_rect (EvView       *view,
3373                             GdkPoint     *start,
3374                             GdkPoint     *stop)
3375 {
3376         GdkRectangle view_rect;
3377         int n_pages, i;
3378         GList *list = NULL;
3379
3380         g_assert (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE);
3381         
3382         view_rect.x = MIN (start->x, stop->x);
3383         view_rect.y = MIN (start->y, stop->y);
3384         view_rect.width = MAX (start->x, stop->x) - view_rect.x;
3385         view_rect.width = MAX (start->y, stop->y) - view_rect.y;
3386
3387         n_pages = ev_page_cache_get_n_pages (view->page_cache);
3388
3389         for (i = 0; i < n_pages; i++) {
3390                 GdkRectangle page_area;
3391                 GtkBorder border;
3392                 
3393                 if (get_page_extents (view, i, &page_area, &border)) {
3394                         GdkRectangle overlap;
3395
3396                         if (gdk_rectangle_intersect (&page_area, &view_rect, &overlap)) {
3397                                 EvViewSelection *selection;
3398
3399                                 selection = g_new0 (EvViewSelection, 1);
3400                                 selection->page = i;
3401                                 view_rect_to_doc_rect (view, &overlap, &page_area,
3402                                                        &(selection->rect));
3403
3404                                 list = g_list_append (list, selection);
3405                         }
3406                 }
3407         }
3408
3409         return list;
3410 }
3411
3412 static gboolean
3413 gdk_rectangle_point_in (GdkRectangle *rectangle,
3414                         GdkPoint     *point)
3415 {
3416         return rectangle->x <= point->x &&
3417                 rectangle->y <= point->y &&
3418                 point->x < rectangle->x + rectangle->width &&
3419                 point->y < rectangle->y + rectangle->height;
3420 }
3421
3422 static GList *
3423 compute_new_selection_text (EvView   *view,
3424                             GdkPoint *start,
3425                             GdkPoint *stop)
3426 {
3427         int n_pages, i, first, last;
3428         GList *list = NULL;
3429         EvViewSelection *selection;
3430         gint width, height;
3431         int start_page, end_page;
3432
3433         g_assert (view->selection_mode == EV_VIEW_SELECTION_TEXT);
3434
3435         n_pages = ev_page_cache_get_n_pages (view->page_cache);
3436
3437         /* First figure out the range of pages the selection
3438          * affects. */
3439         first = n_pages;
3440         last = 0;
3441         if (view->continuous) {
3442                 start_page = 0;
3443                 end_page = n_pages;
3444         } else if (view->dual_page) {
3445                 start_page = view->start_page;
3446                 end_page = view->end_page + 1;
3447         } else {
3448                 start_page = view->current_page;
3449                 end_page = view->current_page + 1;
3450         }
3451
3452         for (i = start_page; i < end_page; i++) {
3453                 GdkRectangle page_area;
3454                 GtkBorder border;
3455                 
3456                 get_page_extents (view, i, &page_area, &border);
3457                 if (gdk_rectangle_point_in (&page_area, start) || 
3458                     gdk_rectangle_point_in (&page_area, stop)) {
3459                         if (first == n_pages)
3460                                 first = i;
3461                         last = i;
3462                 }
3463
3464         }
3465
3466
3467
3468         /* Now create a list of EvViewSelection's for the affected
3469          * pages.  This could be an empty list, a list of just one
3470          * page or a number of pages.*/
3471         for (i = first; i < last + 1; i++) {
3472                 GdkRectangle page_area;
3473                 GtkBorder border;
3474                 GdkPoint *point;
3475
3476                 ev_page_cache_get_size (view->page_cache, i,
3477                                         view->rotation,
3478                                         1.0, &width, &height);
3479
3480                 selection = g_new0 (EvViewSelection, 1);
3481                 selection->page = i;
3482                 selection->rect.x1 = selection->rect.y1 = 0;
3483                 selection->rect.x2 = width;
3484                 selection->rect.y2 = height;
3485
3486                 get_page_extents (view, i, &page_area, &border);
3487
3488                 if (gdk_rectangle_point_in (&page_area, start))
3489                         point = start;
3490                 else
3491                         point = stop;
3492
3493                 if (i == first)
3494                         view_point_to_doc_point (view, point, &page_area,
3495                                                  &selection->rect.x1,
3496                                                  &selection->rect.y1);
3497
3498                 /* If the selection is contained within just one page,
3499                  * make sure we don't write 'start' into both points
3500                  * in selection->rect. */
3501                 if (first == last)
3502                         point = stop;
3503
3504                 if (i == last)
3505                         view_point_to_doc_point (view, point, &page_area,
3506                                                  &selection->rect.x2,
3507                                                  &selection->rect.y2);
3508
3509                 list = g_list_append (list, selection);
3510         }
3511
3512         return list;
3513 }
3514
3515 /* This function takes the newly calculated list, and figures out which regions
3516  * have changed.  It then queues a redraw approporiately.
3517  */
3518 static void
3519 merge_selection_region (EvView *view,
3520                         GList  *new_list)
3521 {
3522         GList *old_list;
3523         GList *new_list_ptr, *old_list_ptr;
3524
3525         /* Update the selection */
3526         old_list = ev_pixbuf_cache_get_selection_list (view->pixbuf_cache);
3527         g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL);
3528         view->selection_info.selections = new_list;
3529         ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, new_list);
3530
3531         new_list_ptr = new_list;
3532         old_list_ptr = old_list;
3533
3534         while (new_list_ptr || old_list_ptr) {
3535                 EvViewSelection *old_sel, *new_sel;
3536                 int cur_page;
3537                 GdkRegion *region = NULL;
3538
3539                 new_sel = (new_list_ptr) ? (new_list_ptr->data) : NULL;
3540                 old_sel = (old_list_ptr) ? (old_list_ptr->data) : NULL;
3541
3542                 /* Assume that the lists are in order, and we run through them
3543                  * comparing them, one page at a time.  We come out with the
3544                  * first page we see. */
3545                 if (new_sel && old_sel) {
3546                         if (new_sel->page < old_sel->page) {
3547                                 new_list_ptr = new_list_ptr->next;
3548                                 old_sel = NULL;
3549                         } else if (new_sel->page > old_sel->page) {
3550                                 old_list_ptr = old_list_ptr->next;
3551                                 new_sel = NULL;
3552                         } else {
3553                                 new_list_ptr = new_list_ptr->next;
3554                                 old_list_ptr = old_list_ptr->next;
3555                         }
3556                 } else if (new_sel) {
3557                         new_list_ptr = new_list_ptr->next;
3558                 } else if (old_sel) {
3559                         old_list_ptr = old_list_ptr->next;
3560                 }
3561
3562                 g_assert (new_sel || old_sel);
3563
3564                 /* is the page we're looking at on the screen?*/
3565                 cur_page = new_sel ? new_sel->page : old_sel->page;
3566                 if (cur_page < view->start_page || cur_page > view->end_page)
3567                         continue;
3568
3569                 /* seed the cache with a new page.  We are going to need the new
3570                  * region too. */
3571                 if (new_sel) {
3572                         GdkRegion *tmp_region = NULL;
3573                         ev_pixbuf_cache_get_selection_pixbuf (view->pixbuf_cache,
3574                                                               cur_page,
3575                                                               view->scale,
3576                                                               &tmp_region);
3577                         if (tmp_region) {
3578                                 new_sel->covered_region = gdk_region_copy (tmp_region);
3579                         }
3580                 }
3581
3582                 /* Now we figure out what needs redrawing */
3583                 if (old_sel && new_sel) {
3584                         if (old_sel->covered_region &&
3585                             new_sel->covered_region) {
3586                                 /* We only want to redraw the areas that have
3587                                  * changed, so we xor the old and new regions
3588                                  * and redraw if it's different */
3589                                 region = gdk_region_copy (old_sel->covered_region);
3590                                 gdk_region_xor (region, new_sel->covered_region);
3591                                 if (gdk_region_empty (region)) {
3592                                         gdk_region_destroy (region);
3593                                         region = NULL;
3594                                 }
3595                         } else if (old_sel->covered_region) {
3596                                 region = gdk_region_copy (old_sel->covered_region);
3597                         } else if (new_sel->covered_region) {
3598                                 region = gdk_region_copy (new_sel->covered_region);
3599                         }
3600                 } else if (old_sel && !new_sel) {
3601                         if (old_sel->covered_region && !gdk_region_empty (old_sel->covered_region)) {
3602                                 region = gdk_region_copy (old_sel->covered_region);
3603                         }
3604                 } else if (!old_sel && new_sel) {
3605                         if (new_sel->covered_region && !gdk_region_empty (new_sel->covered_region)) {
3606                                 region = gdk_region_copy (new_sel->covered_region);
3607                         }
3608                 } else {
3609                         g_assert_not_reached ();
3610                 }
3611
3612                 /* Redraw the damaged region! */
3613                 if (region) {
3614                         GdkRectangle page_area;
3615                         GtkBorder border;
3616
3617                         get_page_extents (view, cur_page, &page_area, &border);
3618                         gdk_region_offset (region,
3619                                            page_area.x + border.left - view->scroll_x,
3620                                            page_area.y + border.top - view->scroll_y);
3621                         gdk_window_invalidate_region (GTK_WIDGET (view)->window, region, TRUE);
3622                         gdk_region_destroy (region);
3623                 }
3624         }
3625
3626         /* Free the old list, now that we're done with it. */
3627         g_list_foreach (old_list, (GFunc) selection_free, NULL);
3628 }
3629
3630 static void
3631 compute_selections (EvView   *view,
3632                     GdkPoint *start,
3633                     GdkPoint *stop)
3634 {
3635         GList *list;
3636
3637         if (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE)
3638                 list = compute_new_selection_rect (view, start, stop);
3639         else
3640                 list = compute_new_selection_text (view, start, stop);
3641         merge_selection_region (view, list);
3642 }
3643
3644 /* Free's the selection.  It's up to the caller to queue redraws if needed.
3645  */
3646 static void
3647 selection_free (EvViewSelection *selection)
3648 {
3649         if (selection->covered_region)
3650                 gdk_region_destroy (selection->covered_region);
3651         g_free (selection);
3652 }
3653
3654 static void
3655 clear_selection (EvView *view)
3656 {
3657         g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL);
3658         view->selection_info.selections = NULL;
3659         view->selection_info.in_selection = FALSE;
3660 }
3661
3662
3663 void
3664 ev_view_select_all (EvView *view)
3665 {
3666         int n_pages, i;
3667
3668         /* Disable selection on rotated pages for the 0.4.0 series */
3669         if (view->rotation != 0)
3670                 return;
3671
3672         clear_selection (view);
3673
3674         n_pages = ev_page_cache_get_n_pages (view->page_cache);
3675         for (i = 0; i < n_pages; i++) {
3676                 int width, height;
3677                 EvViewSelection *selection;
3678
3679                 ev_page_cache_get_size (view->page_cache,
3680                                         i,
3681                                         view->rotation,
3682                                         1.0, &width, &height);
3683
3684                 selection = g_new0 (EvViewSelection, 1);
3685                 selection->page = i;
3686                 selection->rect.x1 = selection->rect.y1 = 0;
3687                 selection->rect.x2 = width;
3688                 selection->rect.y2 = height;
3689
3690                 view->selection_info.selections = g_list_append (view->selection_info.selections, selection);
3691         }
3692
3693         ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, view->selection_info.selections);
3694         gtk_widget_queue_draw (GTK_WIDGET (view));
3695 }
3696
3697 static char *
3698 get_selected_text (EvView *ev_view)
3699 {
3700         GString *text;
3701         GList *l;
3702
3703         text = g_string_new (NULL);
3704
3705         ev_document_doc_mutex_lock ();
3706
3707         for (l = ev_view->selection_info.selections; l != NULL; l = l->next) {
3708                 EvViewSelection *selection = (EvViewSelection *)l->data;
3709                 char *tmp;
3710
3711                 tmp = ev_document_get_text (ev_view->document,
3712                                             selection->page,
3713                                             &selection->rect);
3714                 g_string_append (text, tmp);
3715                 g_free (tmp);
3716         }
3717
3718         ev_document_doc_mutex_unlock ();
3719
3720         return g_string_free (text, FALSE);
3721 }
3722
3723 void
3724 ev_view_copy (EvView *ev_view)
3725 {
3726         GtkClipboard *clipboard;
3727         char *text;
3728
3729         if (!ev_document_can_get_text (ev_view->document)) {
3730                 return;
3731         }
3732
3733         text = get_selected_text (ev_view);
3734         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
3735                                               GDK_SELECTION_CLIPBOARD);
3736         gtk_clipboard_set_text (clipboard, text, -1);
3737         g_free (text);
3738 }
3739
3740 static void
3741 ev_view_primary_get_cb (GtkClipboard     *clipboard,
3742                         GtkSelectionData *selection_data,
3743                         guint             info,
3744                         gpointer          data)
3745 {
3746         EvView *ev_view = EV_VIEW (data);
3747         char *text;
3748
3749         if (!ev_document_can_get_text (ev_view->document)) {
3750                 return;
3751         }
3752
3753         text = get_selected_text (ev_view);
3754         gtk_selection_data_set_text (selection_data, text, -1);
3755         g_free (text);
3756 }
3757
3758 static void
3759 ev_view_primary_clear_cb (GtkClipboard *clipboard,
3760                           gpointer      data)
3761 {
3762         EvView *view = EV_VIEW (data);
3763
3764         clear_selection (view);
3765 }
3766
3767 static void
3768 ev_view_update_primary_selection (EvView *ev_view)
3769 {
3770         GtkClipboard *clipboard;
3771
3772         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
3773                                               GDK_SELECTION_PRIMARY);
3774
3775         if (ev_view->selection_info.selections) {
3776                 if (!gtk_clipboard_set_with_owner (clipboard,
3777                                                    targets,
3778                                                    G_N_ELEMENTS (targets),
3779                                                    ev_view_primary_get_cb,
3780                                                    ev_view_primary_clear_cb,
3781                                                    G_OBJECT (ev_view)))
3782                         ev_view_primary_clear_cb (clipboard, ev_view);
3783         } else {
3784                 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
3785                         gtk_clipboard_clear (clipboard);
3786         }
3787 }
3788
3789 /*** Cursor operations ***/
3790
3791 static GdkCursor *
3792 ev_view_create_invisible_cursor(void)
3793 {
3794        GdkBitmap *empty;
3795        GdkColor black = { 0, 0, 0, 0 };
3796        static char bits[] = { 0x00 };
3797
3798        empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
3799
3800        return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
3801 }
3802
3803 static void
3804 ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
3805 {
3806         GdkCursor *cursor = NULL;
3807         GdkDisplay *display;
3808         GtkWidget *widget;
3809
3810         if (view->cursor == new_cursor) {
3811                 return;
3812         }
3813
3814         widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
3815         display = gtk_widget_get_display (widget);
3816         view->cursor = new_cursor;
3817
3818         switch (new_cursor) {
3819                 case EV_VIEW_CURSOR_NORMAL:
3820                         gdk_window_set_cursor (widget->window, NULL);
3821                         break;
3822                 case EV_VIEW_CURSOR_IBEAM:
3823                         cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
3824                         break;
3825                 case EV_VIEW_CURSOR_LINK:
3826                         cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
3827                         break;
3828                 case EV_VIEW_CURSOR_WAIT:
3829                         cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
3830                         break;
3831                 case EV_VIEW_CURSOR_HIDDEN:
3832                         cursor = ev_view_create_invisible_cursor ();
3833                         break;
3834                 case EV_VIEW_CURSOR_DRAG:
3835                         cursor = gdk_cursor_new_for_display (display, GDK_FLEUR);
3836                         break;
3837         }
3838
3839         if (cursor) {
3840                 gdk_window_set_cursor (widget->window, cursor);
3841                 gdk_cursor_unref (cursor);
3842                 gdk_flush();
3843         }
3844 }
3845
3846 void
3847 ev_view_hide_cursor (EvView *view)
3848 {
3849        ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN);
3850 }
3851
3852 void
3853 ev_view_show_cursor (EvView *view)
3854 {
3855        ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
3856 }
3857
3858 gboolean
3859 ev_view_next_page (EvView *view)
3860 {
3861         int page;
3862
3863         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
3864
3865         page = ev_page_cache_get_current_page (view->page_cache);
3866         page = ev_view_get_dual_page (view) ? page + 2 : page + 1;
3867
3868         if (page < ev_page_cache_get_n_pages (view->page_cache)) {
3869                 ev_page_cache_set_current_page (view->page_cache, page);
3870                 return TRUE;
3871         } else {
3872                 return FALSE;
3873         }
3874 }
3875
3876 gboolean
3877 ev_view_previous_page (EvView *view)
3878 {
3879         int page;
3880
3881         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
3882
3883         page = ev_page_cache_get_current_page (view->page_cache);
3884         page = ev_view_get_dual_page (view) ? page - 2 : page - 1;
3885
3886         if (page >= 0) {
3887                 ev_page_cache_set_current_page (view->page_cache, page);
3888                 return TRUE;
3889         } else {
3890                 return FALSE;
3891         }
3892 }
3893
3894 /*** Enum description for usage in signal ***/
3895
3896 GType
3897 ev_sizing_mode_get_type (void)
3898 {
3899   static GType etype = 0;
3900   if (etype == 0) {
3901     static const GEnumValue values[] = {
3902       { EV_SIZING_FIT_WIDTH, "EV_SIZING_FIT_WIDTH", "fit-width" },
3903       { EV_SIZING_BEST_FIT, "EV_SIZING_BEST_FIT", "best-fit" },
3904       { EV_SIZING_FREE, "EV_SIZING_FREE", "free" },
3905       { 0, NULL, NULL }
3906     };
3907     etype = g_enum_register_static ("EvSizingMode", values);
3908   }
3909   return etype;
3910 }