]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
Massive changes. We now support text selection of pdfs, and not just
[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 <gtk/gtkalignment.h>
23 #include <glib/gi18n.h>
24 #include <gtk/gtkbindings.h>
25 #include <gtk/gtkselection.h>
26 #include <gtk/gtkclipboard.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <libgnomevfs/gnome-vfs-utils.h>
29
30 #include "ev-marshal.h"
31 #include "ev-view.h"
32 #include "ev-selection.h"
33 #include "ev-document-find.h"
34 #include "ev-document-misc.h"
35 #include "ev-debug.h"
36 #include "ev-job-queue.h"
37 #include "ev-page-cache.h"
38 #include "ev-pixbuf-cache.h"
39
40 #define EV_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass))
41 #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW))
42 #define EV_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_VIEW, EvViewClass))
43
44 enum {
45         PROP_0,
46         PROP_STATUS,
47         PROP_FIND_STATUS,
48         PROP_CONTINUOUS,
49         PROP_DUAL_PAGE,
50         PROP_FULLSCREEN,
51         PROP_PRESENTATION,
52         PROP_SIZING_MODE,
53         PROP_ZOOM,
54 };
55
56 enum {
57         SIGNAL_BINDING_ACTIVATED,
58         SIGNAL_ZOOM_INVALID,
59         N_SIGNALS,
60 };
61
62 enum {
63         TARGET_STRING,
64         TARGET_TEXT,
65         TARGET_COMPOUND_TEXT,
66         TARGET_UTF8_STRING,
67         TARGET_TEXT_BUFFER_CONTENTS
68 };
69
70 static const GtkTargetEntry targets[] = {
71         { "STRING", 0, TARGET_STRING },
72         { "TEXT",   0, TARGET_TEXT },
73         { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
74         { "UTF8_STRING", 0, TARGET_UTF8_STRING },
75 };
76
77 static guint signals[N_SIGNALS];
78
79 typedef enum {
80         EV_VIEW_CURSOR_NORMAL,
81         EV_VIEW_CURSOR_LINK,
82         EV_VIEW_CURSOR_WAIT,
83         EV_VIEW_CURSOR_HIDDEN,
84         EV_VIEW_CURSOR_DRAG
85 } EvViewCursor;
86
87 #define ZOOM_IN_FACTOR  1.2
88 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)
89
90 #define MIN_SCALE 0.05409
91 #define MAX_SCALE 4.0
92
93 /* Information for middle clicking and moving around the doc */
94 typedef struct {
95         gboolean in_drag;
96         GdkPoint start;
97         gdouble hadj;
98         gdouble vadj;
99 } DragInfo;
100
101 /* Information for handling selection */
102 typedef struct {
103         gboolean in_selection;
104         GdkPoint start;
105         GList *selections;
106 } SelectionInfo;
107
108 typedef enum {
109         SCROLL_TO_KEEP_POSITION,
110         SCROLL_TO_CURRENT_PAGE,
111         SCROLL_TO_CENTER
112 } PendingScroll;
113
114 struct _EvView {
115         GtkWidget parent_instance;
116
117         EvDocument *document;
118
119         char *status;
120         char *find_status;
121
122         gint scroll_x;
123         gint scroll_y;
124
125         /* Information for middle clicking and dragging around. */
126         DragInfo drag_info;
127
128         /* Selection */
129         EvViewSelectionMode selection_mode;
130         SelectionInfo selection_info;
131
132         gboolean pressed_button;
133         EvViewCursor cursor;
134
135         GtkAdjustment *hadjustment;
136         GtkAdjustment *vadjustment;
137
138         EvPageCache *page_cache;
139         EvPixbufCache *pixbuf_cache;
140
141         gint start_page;
142         gint end_page;
143         gint current_page;
144
145         EvJobRender *current_job;
146
147         int find_page;
148         int find_result;
149         int spacing;
150
151         double scale;
152
153         gboolean continuous;
154         gboolean dual_page;
155         gboolean fullscreen;
156         gboolean presentation;
157         EvSizingMode sizing_mode;
158
159         PendingScroll pending_scroll;
160         gboolean pending_resize;
161 };
162
163 struct _EvViewClass {
164         GtkWidgetClass parent_class;
165
166         void    (*set_scroll_adjustments) (EvView         *view,
167                                            GtkAdjustment  *hadjustment,
168                                            GtkAdjustment  *vadjustment);
169         void    (*binding_activated)      (EvView         *view,
170                                            GtkScrollType   scroll,
171                                            gboolean        horizontal);
172         void    (*zoom_invalid)           (EvView         *view);
173 };
174
175 /*** Scrolling ***/
176 static void       scroll_to_current_page                     (EvView *view,
177                                                               GtkOrientation orientation);
178 static void       ev_view_set_scroll_adjustments             (EvView             *view,
179                                                               GtkAdjustment      *hadjustment,
180                                                               GtkAdjustment      *vadjustment);
181 static void       view_update_range_and_current_page         (EvView             *view);
182 static void       set_scroll_adjustment                      (EvView             *view,
183                                                               GtkOrientation      orientation,
184                                                               GtkAdjustment      *adjustment);
185 static void       ev_view_set_scroll_adjustments             (EvView             *view,
186                                                               GtkAdjustment      *hadjustment,
187                                                               GtkAdjustment      *vadjustment);
188 static void       add_scroll_binding_keypad                  (GtkBindingSet      *binding_set,
189                                                               guint               keyval,
190                                                               GtkScrollType       scroll,
191                                                               gboolean            horizontal);
192 static void       ev_view_binding_activated                  (EvView             *view,
193                                                               GtkScrollType       scroll,
194                                                               gboolean            horizontal);
195 static void       ensure_rectangle_is_visible                (EvView             *view,
196                                                               GdkRectangle       *rect);
197
198 /*** Geometry computations ***/
199 static void       compute_border                             (EvView             *view,
200                                                               int                 width,
201                                                               int                 height,
202                                                               GtkBorder          *border);
203 static void       get_page_y_offset                          (EvView *view,
204                                                               int page,
205                                                               double zoom,
206                                                               int *y_offset);
207 static gboolean   get_page_extents                           (EvView             *view,
208                                                               gint                page,
209                                                               GdkRectangle       *page_area,
210                                                               GtkBorder          *border);
211 static void       view_rect_to_doc_rect                      (EvView             *view,
212                                                               GdkRectangle       *view_rect,
213                                                               GdkRectangle       *page_area,
214                                                               EvRectangle        *doc_rect);
215 static void       doc_rect_to_view_rect                      (EvView             *view,
216                                                               int                 page,
217                                                               EvRectangle        *doc_rect,
218                                                               GdkRectangle       *view_rect);
219 static void       find_page_at_location                      (EvView             *view,
220                                                               gdouble             x,
221                                                               gdouble             y,
222                                                               gint               *page,
223                                                               gint               *x_offset,
224                                                               gint               *y_offset);
225
226 /*** Hyperrefs ***/
227 static EvLink*    get_link_at_location                       (EvView             *view,
228                                                               gdouble             x,
229                                                               gdouble             y);
230 static void       go_to_link                                 (EvView             *view,
231                                                               EvLink             *link);
232 static char*      status_message_from_link                   (EvView             *view,
233                                                               EvLink             *link);
234
235 /*** GtkWidget implementation ***/
236 static void       ev_view_size_request_continuous_dual_page  (EvView             *view,
237                                                               GtkRequisition     *requisition);
238 static void       ev_view_size_request_continuous            (EvView             *view,
239                                                               GtkRequisition     *requisition);
240 static void       ev_view_size_request_dual_page             (EvView             *view,
241                                                               GtkRequisition     *requisition);
242 static void       ev_view_size_request_single_page           (EvView             *view,
243                                                               GtkRequisition     *requisition);
244 static void       ev_view_size_request                       (GtkWidget          *widget,
245                                                               GtkRequisition     *requisition);
246 static void       ev_view_size_allocate                      (GtkWidget          *widget,
247                                                               GtkAllocation      *allocation);
248 static void       ev_view_realize                            (GtkWidget          *widget);
249 static void       ev_view_unrealize                          (GtkWidget          *widget);
250 static gboolean   ev_view_scroll_event                       (GtkWidget          *widget,
251                                                               GdkEventScroll     *event);
252 static gboolean   ev_view_expose_event                       (GtkWidget          *widget,
253                                                               GdkEventExpose     *event);
254 static gboolean   ev_view_button_press_event                 (GtkWidget          *widget,
255                                                               GdkEventButton     *event);
256 static gboolean   ev_view_motion_notify_event                (GtkWidget          *widget,
257                                                               GdkEventMotion     *event);
258 static gboolean   ev_view_button_release_event               (GtkWidget          *widget,
259                                                               GdkEventButton     *event);
260
261 /*** Drawing ***/
262 static guint32    ev_gdk_color_to_rgb                        (const GdkColor     *color);
263 static void       draw_rubberband                            (GtkWidget          *widget,
264                                                               GdkWindow          *window,
265                                                               const GdkRectangle *rect,
266                                                               guchar              alpha);
267 static void       highlight_find_results                     (EvView             *view,
268                                                               int                 page);
269 static void       draw_one_page                              (EvView             *view,
270                                                               gint                page,
271                                                               GdkRectangle       *page_area,
272                                                               GtkBorder          *border,
273                                                               GdkRectangle       *expose_area);
274
275 /*** Callbacks ***/
276 static void       find_changed_cb                            (EvDocument         *document,
277                                                               int                 page,
278                                                               EvView             *view);
279 static void       job_finished_cb                            (EvPixbufCache      *pixbuf_cache,
280                                                               EvView             *view);
281 static void       page_changed_cb                            (EvPageCache        *page_cache,
282                                                               int                 new_page,
283                                                               EvView             *view);
284 static void       on_adjustment_value_changed                (GtkAdjustment      *adjustment,
285                                                               EvView             *view);
286
287 /*** GObject ***/
288 static void       ev_view_finalize                           (GObject            *object);
289 static void       ev_view_destroy                            (GtkObject          *object);
290 static void       ev_view_set_property                       (GObject            *object,
291                                                               guint               prop_id,
292                                                               const GValue       *value,
293                                                               GParamSpec         *pspec);
294 static void       ev_view_get_property                       (GObject            *object,
295                                                               guint               prop_id,
296                                                               GValue             *value,
297                                                               GParamSpec         *pspec);
298 static void       ev_view_class_init                         (EvViewClass        *class);
299 static void       ev_view_init                               (EvView             *view);
300
301 /*** Zoom and sizing ***/
302 static double   zoom_for_size_fit_width                      (int doc_width,
303                                                               int doc_height,
304                                                               int target_width,
305                                                               int target_height,
306                                                               int vsb_width);
307 static double   zoom_for_size_best_fit                       (int doc_width,
308                                                               int doc_height,
309                                                               int target_width,
310                                                               int target_height,
311                                                               int vsb_width,
312                                                               int hsb_width);
313 static void     ev_view_zoom_for_size_presentation           (EvView *view,
314                                                               int     width,
315                                                               int     height);
316 static void     ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
317                                                                 int     width,
318                                                                 int     height,
319                                                                 int     vsb_width,
320                                                                 int     hsb_height);
321 static void     ev_view_zoom_for_size_continuous               (EvView *view,
322                                                                 int     width,
323                                                                 int     height,
324                                                                 int     vsb_width,
325                                                                 int     hsb_height);
326 static void     ev_view_zoom_for_size_dual_page                (EvView *view,
327                                                                 int     width,
328                                                                 int     height,
329                                                                 int     vsb_width,
330                                                                 int     hsb_height);
331 static void     ev_view_zoom_for_size_single_page              (EvView *view,
332                                                                 int     width,
333                                                                 int     height,
334                                                                 int     vsb_width,
335                                                                 int     hsb_height);
336 /*** Cursors ***/
337 static GdkCursor* ev_view_create_invisible_cursor            (void);
338 static void       ev_view_set_cursor                         (EvView             *view,
339                                                               EvViewCursor        new_cursor);
340
341 /*** Status messages ***/
342 static void       ev_view_set_status                         (EvView             *view,
343                                                               const char         *message);
344 static void       update_find_status_message                 (EvView             *view);
345 static void       ev_view_set_find_status                    (EvView             *view,
346                                                               const char         *message);
347 /*** Find ***/
348 static void       jump_to_find_result                        (EvView             *view);
349 static void       jump_to_find_page                          (EvView             *view);
350
351 /*** Selection ***/
352 static void       compute_selections                         (EvView             *view,
353                                                               GdkRectangle       *view_rect);
354 static void       clear_selection                            (EvView             *view);
355 static char*      get_selected_text                          (EvView             *ev_view);
356 static void       ev_view_primary_get_cb                     (GtkClipboard       *clipboard,
357                                                               GtkSelectionData   *selection_data,
358                                                               guint               info,
359                                                               gpointer            data);
360 static void       ev_view_primary_clear_cb                   (GtkClipboard       *clipboard,
361                                                               gpointer            data);
362 static void       ev_view_update_primary_selection           (EvView             *ev_view);
363
364
365 G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET)
366
367 static void
368 scroll_to_current_page (EvView *view, GtkOrientation orientation)
369 {
370         GdkRectangle page_area;
371         GtkBorder border;
372
373         get_page_extents (view, view->current_page, &page_area, &border);
374
375         if (orientation == GTK_ORIENTATION_VERTICAL) {
376                 if (view->continuous) {
377                         gtk_adjustment_clamp_page (view->vadjustment,
378                                                    page_area.y - view->spacing,
379                                                    page_area.y + view->vadjustment->page_size);
380                 } else {
381                         gtk_adjustment_set_value (view->vadjustment,
382                                                   view->vadjustment->lower);
383                 }
384         } else {
385                 if (view->dual_page) {
386                         gtk_adjustment_clamp_page (view->hadjustment,
387                                                    page_area.x,
388                                                    page_area.x + view->hadjustment->page_size);
389                 } else {
390                         gtk_adjustment_set_value (view->hadjustment,
391                                                   CLAMP (view->hadjustment->value,
392                                                   view->hadjustment->lower,
393                                                   view->hadjustment->upper -
394                                                   view->hadjustment->page_size));
395                 }
396         }
397 }
398
399 static void
400 view_set_adjustment_values (EvView         *view,
401                             GtkOrientation  orientation)
402 {
403         GtkWidget *widget = GTK_WIDGET (view);
404         GtkAdjustment *adjustment;
405         int requisition;
406         int allocation;
407
408         double factor;
409         gint new_value;
410
411         if (orientation == GTK_ORIENTATION_HORIZONTAL)  {
412                 requisition = widget->requisition.width;
413                 allocation = widget->allocation.width;
414                 adjustment = view->hadjustment;
415         } else {
416                 requisition = widget->requisition.height;
417                 allocation = widget->allocation.height;
418                 adjustment = view->vadjustment;
419         }
420
421         if (!adjustment)
422                 return;
423
424         factor = 1.0;
425         switch (view->pending_scroll) {
426                 case SCROLL_TO_KEEP_POSITION:
427                         factor = (adjustment->value) / adjustment->upper;
428                         break;
429                 case SCROLL_TO_CURRENT_PAGE:
430                         break;
431                 case SCROLL_TO_CENTER:
432                         factor = (adjustment->value + adjustment->page_size * 0.5) / adjustment->upper;
433                         break;
434         }
435
436         adjustment->page_size = allocation;
437         adjustment->step_increment = allocation * 0.1;
438         adjustment->page_increment = allocation * 0.9;
439         adjustment->lower = 0;
440         adjustment->upper = MAX (allocation, requisition);
441
442         /*
443          * We add 0.5 to the values before to average out our rounding errors.
444          */
445         switch (view->pending_scroll) {
446                 case SCROLL_TO_KEEP_POSITION:
447                         new_value = CLAMP (adjustment->upper * factor + 0.5, 0, adjustment->upper - adjustment->page_size);
448                         gtk_adjustment_set_value (adjustment, (int)new_value);
449                         break;
450                 case SCROLL_TO_CURRENT_PAGE:
451                         scroll_to_current_page (view, orientation);
452                         break;
453                 case SCROLL_TO_CENTER:
454                         new_value = CLAMP (adjustment->upper * factor - adjustment->page_size * 0.5 + 0.5,
455                                            0, adjustment->upper - adjustment->page_size);
456                         gtk_adjustment_set_value (adjustment, (int)new_value);
457                         break;
458         }
459
460         gtk_adjustment_changed (adjustment);
461 }
462
463 static void
464 view_update_range_and_current_page (EvView *view)
465 {
466         /* Presentation trumps all other modes */
467         if (view->presentation) {
468                 view->start_page = view->current_page;
469                 view->end_page = view->current_page;
470         } else if (view->continuous) {
471                 GdkRectangle current_area, unused, page_area;
472                 GtkBorder border;
473                 gint current_page;
474                 gboolean found = FALSE;
475                 int i;
476
477                 if (!(view->vadjustment && view->hadjustment))
478                         return;
479
480                 current_area.x = view->hadjustment->value;
481                 current_area.width = view->hadjustment->upper;
482                 current_area.y = view->vadjustment->value;
483                 current_area.height = view->vadjustment->page_size;
484
485                 for (i = 0; i < ev_page_cache_get_n_pages (view->page_cache); i++) {
486
487                         get_page_extents (view, i, &page_area, &border);
488
489                         if (gdk_rectangle_intersect (&current_area, &page_area, &unused)) {
490                                 if (! found) {
491                                         view->start_page = i;
492                                         found = TRUE;
493
494                                 }
495                                 view->end_page = i;
496                         } else if (found) {
497                                 break;
498                         }
499                 }
500
501                 current_page = ev_page_cache_get_current_page (view->page_cache);
502
503                 if (current_page < view->start_page || current_page > view->end_page) {
504                         view->current_page = view->start_page;
505                         ev_page_cache_set_current_page (view->page_cache, view->start_page);
506                 }
507         } else {
508                 if (view->dual_page) {
509                         if (view->current_page % 2 == 0) {
510                                 view->start_page = view->current_page;
511                                 if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache))
512                                         view->end_page = view->start_page + 1;
513                         } else {
514                                 view->start_page = view->current_page - 1;
515                                 view->end_page = view->current_page;
516                         }
517                 } else {
518                         view->start_page = view->current_page;
519                         view->end_page = view->current_page;
520                 }
521         }
522
523         ev_pixbuf_cache_set_page_range (view->pixbuf_cache,
524                                         view->start_page,
525                                         view->end_page,
526                                         view->scale,
527                                         view->selection_info.selections);
528 }
529
530 static void
531 set_scroll_adjustment (EvView *view,
532                        GtkOrientation  orientation,
533                        GtkAdjustment  *adjustment)
534 {
535         GtkAdjustment **to_set;
536
537         if (orientation == GTK_ORIENTATION_HORIZONTAL)
538                 to_set = &view->hadjustment;
539         else
540                 to_set = &view->vadjustment;
541
542         if (*to_set != adjustment) {
543                 if (*to_set) {
544                         g_signal_handlers_disconnect_by_func (*to_set,
545                                                               (gpointer) on_adjustment_value_changed,
546                                                               view);
547                         g_object_unref (*to_set);
548                 }
549
550                 *to_set = adjustment;
551                 view_set_adjustment_values (view, orientation);
552
553                 if (*to_set) {
554                         g_object_ref (*to_set);
555                         g_signal_connect (*to_set, "value_changed",
556                                           G_CALLBACK (on_adjustment_value_changed), view);
557                 }
558         }
559 }
560
561 static void
562 ev_view_set_scroll_adjustments (EvView *view,
563                                 GtkAdjustment  *hadjustment,
564                                 GtkAdjustment  *vadjustment)
565 {
566         set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
567         set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
568
569         on_adjustment_value_changed (NULL, view);
570 }
571
572 static void
573 add_scroll_binding_keypad (GtkBindingSet  *binding_set,
574                            guint           keyval,
575                            GtkScrollType   scroll,
576                            gboolean        horizontal)
577 {
578   guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
579
580   gtk_binding_entry_add_signal (binding_set, keyval, 0,
581                                 "binding_activated", 2,
582                                 GTK_TYPE_SCROLL_TYPE, scroll,
583                                 G_TYPE_BOOLEAN, horizontal);
584   gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
585                                 "binding_activated", 2,
586                                 GTK_TYPE_SCROLL_TYPE, scroll,
587                                 G_TYPE_BOOLEAN, horizontal);
588 }
589
590 void
591 ev_view_scroll (EvView        *view,
592                 EvScrollType   scroll)
593 {
594         GtkAdjustment *adjustment;
595         double value, increment;
596         gboolean first_page = FALSE;
597         gboolean last_page = FALSE;
598
599         /* Assign values for increment and vertical adjustment */
600         adjustment = view->vadjustment;
601         increment = adjustment->page_size * 0.75;
602         value = adjustment->value;
603
604         /* Assign boolean for first and last page */
605         if (view->current_page == 0)
606                 first_page = TRUE;
607         if (view->current_page == ev_page_cache_get_n_pages (view->page_cache) - 1)
608                 last_page = TRUE;
609
610         switch (scroll) {
611                 case EV_SCROLL_PAGE_BACKWARD:
612                         /* Do not jump backwards if at the first page */
613                         if (value == (adjustment->lower) && first_page) {
614                                 /* Do nothing */
615                                 /* At the top of a page, assign the upper bound limit of previous page */
616                         } else if (value == (adjustment->lower)) {
617                                 value = adjustment->upper - adjustment->page_size;
618                                 ev_page_cache_set_current_page (view->page_cache, view->current_page - 1);
619                                 /* Jump to the top */
620                         } else {
621                                 value = MAX (value - increment, adjustment->lower);
622                         }
623                         break;
624                 case EV_SCROLL_PAGE_FORWARD:
625                         /* Do not jump forward if at the last page */
626                         if (value == (adjustment->upper - adjustment->page_size) && last_page) {
627                                 /* Do nothing */
628                         /* At the bottom of a page, assign the lower bound limit of next page */
629                         } else if (value == (adjustment->upper - adjustment->page_size)) {
630                                 value = 0;
631                                 ev_page_cache_set_current_page (view->page_cache, view->current_page + 1);
632                         /* Jump to the bottom */
633                         } else {
634                                 value = MIN (value + increment, adjustment->upper - adjustment->page_size);
635                         }
636                         break;
637                 default:
638                         break;
639         }
640
641         gtk_adjustment_set_value (adjustment, value);
642 }
643
644 static void
645 ev_view_binding_activated (EvView *view,
646                            GtkScrollType scroll,
647                            gboolean horizontal)
648 {
649         GtkAdjustment *adjustment;
650         double value;
651
652         if (view->presentation) {
653                 switch (scroll) {
654                         case GTK_SCROLL_STEP_BACKWARD:
655                                 ev_page_cache_prev_page (view->page_cache);
656                                 break;
657                         case GTK_SCROLL_STEP_FORWARD:
658                                 ev_page_cache_next_page (view->page_cache);
659                                 break;
660                         default:
661                                 break;
662                 }
663                 return;
664         }
665
666         if (horizontal) {
667                 adjustment = view->hadjustment;
668         } else {
669                 adjustment = view->vadjustment;
670         }
671
672         value = adjustment->value;
673
674         switch (scroll) {
675                 case GTK_SCROLL_STEP_BACKWARD:
676                         value -= adjustment->step_increment;
677                         break;
678                 case GTK_SCROLL_STEP_FORWARD:
679                         value += adjustment->step_increment;
680                         break;
681                 default:
682                         break;
683         }
684
685         value = CLAMP (value, adjustment->lower,
686                        adjustment->upper - adjustment->page_size);
687
688         gtk_adjustment_set_value (adjustment, value);
689 }
690
691 #define MARGIN 5
692
693 static void
694 ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect)
695 {
696         GtkWidget *widget = GTK_WIDGET (view);
697         GtkAdjustment *adjustment;
698         int value;
699
700         adjustment = view->vadjustment;
701
702         if (rect->y < adjustment->value) {
703                 value = MAX (adjustment->lower, rect->y - MARGIN);
704                 gtk_adjustment_set_value (view->vadjustment, value);
705         } else if (rect->y + rect->height >
706                    adjustment->value + widget->allocation.height) {
707                 value = MIN (adjustment->upper, rect->y + rect->height -
708                              widget->allocation.height + MARGIN);
709                 gtk_adjustment_set_value (view->vadjustment, value);
710         }
711
712         adjustment = view->hadjustment;
713
714         if (rect->x < adjustment->value) {
715                 value = MAX (adjustment->lower, rect->x - MARGIN);
716                 gtk_adjustment_set_value (view->hadjustment, value);
717         } else if (rect->x + rect->height >
718                    adjustment->value + widget->allocation.width) {
719                 value = MIN (adjustment->upper, rect->x + rect->width -
720                              widget->allocation.width + MARGIN);
721                 gtk_adjustment_set_value (view->hadjustment, value);
722         }
723 }
724
725 /*** Geometry computations ***/
726
727 static void
728 compute_border (EvView *view, int width, int height, GtkBorder *border)
729 {
730         if (view->presentation) {
731                 border->left = 0;
732                 border->right = 0;
733                 border->top = 0;
734                 border->bottom = 0;
735         } else {
736                 ev_document_misc_get_page_border_size (width, height, border);
737         }
738 }
739
740 static void       get_page_y_offset                          (EvView *view,
741                                                               int page,
742                                                               double zoom,
743                                                               int *y_offset)
744 {
745         int max_width, offset;
746         GtkBorder border;
747
748         g_return_if_fail (y_offset != NULL);
749
750         ev_page_cache_get_max_width (view->page_cache, zoom, &max_width);
751
752         compute_border (view, max_width, max_width, &border);
753
754         if (view->dual_page) {
755                 ev_page_cache_get_height_to_page (view->page_cache, page, zoom, NULL, &offset);
756                 offset += (page / 2 + 1) * view->spacing + (page / 2) * (border.top + border.bottom);
757         } else {
758                 ev_page_cache_get_height_to_page (view->page_cache, page, zoom, &offset, NULL);
759                 offset += (page + 1) * view->spacing + page * (border.top + border.bottom);
760         }
761
762         *y_offset = offset;
763         return;
764 }
765
766 static gboolean
767 get_page_extents (EvView       *view,
768                   gint          page,
769                   GdkRectangle *page_area,
770                   GtkBorder    *border)
771 {
772         GtkWidget *widget;
773         int width, height;
774
775         widget = GTK_WIDGET (view);
776
777         /* Get the size of the page */
778         ev_page_cache_get_size (view->page_cache, page,
779                                 view->scale,
780                                 &width, &height);
781         compute_border (view, width, height, border);
782         page_area->width = width + border->left + border->right;
783         page_area->height = height + border->top + border->bottom;
784
785         if (view->presentation) {
786                 page_area->x = (MAX (0, widget->allocation.width - width))/2;
787                 page_area->y = (MAX (0, widget->allocation.height - height))/2;
788         } else if (view->continuous) {
789                 gint max_width;
790                 gint x, y;
791
792                 ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width);
793                 max_width = max_width + border->left + border->right;
794                 /* Get the location of the bounding box */
795                 if (view->dual_page) {
796                         x = view->spacing + (page % 2) * (max_width + view->spacing);
797                         x = x + MAX (0, widget->allocation.width - (max_width * 2 + view->spacing * 3)) / 2;
798                         if (page % 2 == 0)
799                                 x = x + (max_width - width - border->left - border->right);
800                 } else {
801                         x = view->spacing;
802                         x = x + MAX (0, widget->allocation.width - (width + view->spacing * 2)) / 2;
803                 }
804
805                 get_page_y_offset (view, page, view->scale, &y);
806
807                 page_area->x = x;
808                 page_area->y = y;
809         } else {
810                 gint x, y;
811                 if (view->dual_page) {
812                         gint width_2, height_2;
813                         gint max_width = width;
814                         gint max_height = height;
815                         GtkBorder overall_border;
816                         gint other_page;
817
818                         other_page = page ^ 1;
819
820                         /* First, we get the bounding box of the two pages */
821                         if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
822                                 ev_page_cache_get_size (view->page_cache,
823                                                         other_page,
824                                                         view->scale,
825                                                         &width_2, &height_2);
826                                 if (width_2 > width)
827                                         max_width = width_2;
828                                 if (height_2 > height)
829                                         max_height = height_2;
830                         }
831                         compute_border (view, max_width, max_height, &overall_border);
832
833                         /* Find the offsets */
834                         x = view->spacing;
835                         y = view->spacing;
836
837                         /* Adjust for being the left or right page */
838                         if (page % 2 == 0)
839                                 x = x + max_width - width;
840                         else
841                                 x = x + (max_width + overall_border.left + overall_border.right) + view->spacing;
842
843                         y = y + (max_height - height)/2;
844
845                         /* Adjust for extra allocation */
846                         x = x + MAX (0, widget->allocation.width -
847                                      ((max_width + overall_border.left + overall_border.right) * 2 + view->spacing * 3))/2;
848                         y = y + MAX (0, widget->allocation.height - (height + view->spacing * 2))/2;
849                 } else {
850                         x = view->spacing;
851                         y = view->spacing;
852
853                         /* Adjust for extra allocation */
854                         x = x + MAX (0, widget->allocation.width - (width + border->left + border->right + view->spacing * 2))/2;
855                         y = y + MAX (0, widget->allocation.height - (height + border->top + border->bottom +  view->spacing * 2))/2;
856                 }
857
858                 page_area->x = x;
859                 page_area->y = y;
860         }
861
862         return TRUE;
863 }
864
865 static void
866 view_rect_to_doc_rect (EvView *view,
867                        GdkRectangle *view_rect,
868                        GdkRectangle *page_area,
869                        EvRectangle  *doc_rect)
870 {
871         doc_rect->x1 = floor ((view_rect->x - page_area->x) / view->scale);
872         doc_rect->y1 = floor ((view_rect->y - page_area->y) / view->scale);
873         doc_rect->x2 = doc_rect->x1 + ceil (view_rect->width / view->scale);
874         doc_rect->y2 = doc_rect->y1 + ceil (view_rect->height / view->scale);
875 }
876
877 static void
878 doc_rect_to_view_rect (EvView       *view,
879                        int           page,
880                        EvRectangle  *doc_rect,
881                        GdkRectangle *view_rect)
882 {
883         GdkRectangle page_area;
884         GtkBorder border;
885         int width, height;
886
887         get_page_extents (view, page, &page_area, &border);
888
889         width = doc_rect->x2 - doc_rect->x1;
890         height = doc_rect->y2 - doc_rect->y1;
891         view_rect->x = floor (doc_rect->x1 * view->scale) + page_area.x;
892         view_rect->y = floor (doc_rect->y1 * view->scale) + page_area.y;
893         view_rect->width = ceil (width * view->scale);
894         view_rect->height = ceil (height * view->scale);
895 }
896
897 static void
898 find_page_at_location (EvView  *view,
899                        gdouble  x,
900                        gdouble  y,
901                        gint    *page,
902                        gint    *x_offset,
903                        gint    *y_offset)
904 {
905         int i;
906
907         if (view->document == NULL)
908                 return;
909
910         g_assert (page);
911         g_assert (x_offset);
912         g_assert (y_offset);
913
914         for (i = view->start_page; i <= view->end_page; i++) {
915                 GdkRectangle page_area;
916                 GtkBorder border;
917
918                 if (! get_page_extents (view, i, &page_area, &border))
919                         continue;
920
921                 if ((x >= page_area.x + border.left) &&
922                     (x < page_area.x + page_area.width - border.right) &&
923                     (y >= page_area.y + border.top) &&
924                     (y < page_area.y + page_area.height - border.bottom)) {
925                         *page = i;
926                         *x_offset = x - (page_area.x + border.left);
927                         *y_offset = y - (page_area.y + border.top);
928                         return;
929                 }
930         }
931
932         *page = -1;
933 }
934
935 /*** Hyperref ***/
936 static EvLink *
937 get_link_at_location (EvView  *view,
938                       gdouble  x,
939                       gdouble  y)
940 {
941         gint page = -1;
942         gint x_offset = 0, y_offset = 0;
943         GList *link_mapping;
944
945         find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
946
947         if (page == -1)
948                 return NULL;
949
950         link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page);
951
952         if (link_mapping)
953                 return ev_link_mapping_find (link_mapping, x_offset / view->scale, y_offset / view->scale);
954         else
955                 return NULL;
956 }
957
958 /* FIXME: standardize this sometime */
959 static void
960 go_to_link (EvView *view, EvLink *link)
961 {
962         EvLinkType type;
963         const char *uri;
964         int page;
965
966         type = ev_link_get_link_type (link);
967
968         switch (type) {
969                 case EV_LINK_TYPE_TITLE:
970                         break;
971                 case EV_LINK_TYPE_PAGE:
972                         page = ev_link_get_page (link);
973                         ev_page_cache_set_current_page (view->page_cache, page);
974                         break;
975                 case EV_LINK_TYPE_EXTERNAL_URI:
976                         uri = ev_link_get_uri (link);
977                         gnome_vfs_url_show (uri);
978                         break;
979         }
980 }
981
982 static char *
983 status_message_from_link (EvView *view, EvLink *link)
984 {
985         EvLinkType type;
986         char *msg = NULL;
987         char *page_label;
988
989         type = ev_link_get_link_type (link);
990
991         switch (type) {
992                 case EV_LINK_TYPE_TITLE:
993                         if (ev_link_get_title (link))
994                                 msg = g_strdup (ev_link_get_title (link));
995                         break;
996                 case EV_LINK_TYPE_PAGE:
997                         page_label = ev_page_cache_get_page_label (view->page_cache, ev_link_get_page (link));
998                         msg = g_strdup_printf (_("Go to page %s"), page_label);
999                         g_free (page_label);
1000                         break;
1001                 case EV_LINK_TYPE_EXTERNAL_URI:
1002                         msg = g_strdup (ev_link_get_uri (link));
1003                         break;
1004                 default:
1005                         break;
1006         }
1007
1008         return msg;
1009 }
1010
1011
1012 /*** GtkWidget implementation ***/
1013
1014 static void
1015 ev_view_size_request_continuous_dual_page (EvView         *view,
1016                                            GtkRequisition *requisition)
1017 {
1018         int max_width;
1019         gint n_pages;
1020         GtkBorder border;
1021
1022         ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width);
1023         compute_border (view, max_width, max_width, &border);
1024
1025         n_pages = ev_page_cache_get_n_pages (view->page_cache) + 1;
1026
1027         requisition->width = (max_width + border.left + border.right) * 2 + (view->spacing * 3);
1028         get_page_y_offset (view, n_pages, view->scale, &requisition->height);
1029
1030         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1031                 requisition->width = 1;
1032         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1033                 requisition->width = 1;
1034                 /* FIXME: This could actually be set on one page docs or docs
1035                  * with a strange aspect ratio. */
1036                 /* requisition->height = 1;*/
1037         }
1038 }
1039
1040 static void
1041 ev_view_size_request_continuous (EvView         *view,
1042                                  GtkRequisition *requisition)
1043 {
1044         int max_width;
1045         int n_pages;
1046         GtkBorder border;
1047
1048
1049         ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width);
1050         n_pages = ev_page_cache_get_n_pages (view->page_cache);
1051         compute_border (view, max_width, max_width, &border);
1052
1053         requisition->width = max_width + (view->spacing * 2) + border.left + border.right;
1054         get_page_y_offset (view, n_pages, view->scale, &requisition->height);
1055
1056         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1057                 requisition->width = 1;
1058         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1059                 requisition->width = 1;
1060                 /* FIXME: This could actually be set on one page docs or docs
1061                  * with a strange aspect ratio. */
1062                 /* requisition->height = 1;*/
1063         }
1064 }
1065
1066 static void
1067 ev_view_size_request_dual_page (EvView         *view,
1068                                 GtkRequisition *requisition)
1069 {
1070         GtkBorder border;
1071         gint width, height;
1072
1073         /* Find the largest of the two. */
1074         ev_page_cache_get_size (view->page_cache,
1075                                 view->current_page,
1076                                 view->scale,
1077                                 &width, &height);
1078         if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache)) {
1079                 gint width_2, height_2;
1080                 ev_page_cache_get_size (view->page_cache,
1081                                         view->current_page + 1,
1082                                         view->scale,
1083                                         &width_2, &height_2);
1084                 if (width_2 > width) {
1085                         width = width_2;
1086                         height = height_2;
1087                 }
1088         }
1089         compute_border (view, width, height, &border);
1090
1091         requisition->width = ((width + border.left + border.right) * 2) +
1092                 (view->spacing * 3);
1093         requisition->height = (height + border.top + border.bottom) +
1094                 (view->spacing * 2);
1095
1096         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1097                 requisition->width = 1;
1098         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1099                 requisition->width = 1;
1100                 requisition->height = 1;
1101         }
1102 }
1103
1104 static void
1105 ev_view_size_request_single_page (EvView         *view,
1106                                   GtkRequisition *requisition)
1107 {
1108         GtkBorder border;
1109         gint width, height;
1110
1111         ev_page_cache_get_size (view->page_cache,
1112                                 view->current_page,
1113                                 view->scale,
1114                                 &width, &height);
1115         compute_border (view, width, height, &border);
1116
1117         requisition->width = width + border.left + border.right + (2 * view->spacing);
1118         requisition->height = height + border.top + border.bottom + (2 * view->spacing);
1119
1120         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1121                 requisition->width = 1;
1122                 requisition->height = height + border.top + border.bottom + (2 * view->spacing);
1123         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1124                 requisition->width = 1;
1125                 requisition->height = 1;
1126         }
1127 }
1128
1129 static void
1130 ev_view_size_request (GtkWidget      *widget,
1131                       GtkRequisition *requisition)
1132 {
1133         EvView *view = EV_VIEW (widget);
1134
1135         if (view->document == NULL) {
1136                 requisition->width = 1;
1137                 requisition->height = 1;
1138                 return;
1139         }
1140
1141         if (view->presentation) {
1142                 requisition->width = 1;
1143                 requisition->height = 1;
1144                 return;
1145         }
1146
1147         if (view->continuous && view->dual_page)
1148                 ev_view_size_request_continuous_dual_page (view, requisition);
1149         else if (view->continuous)
1150                 ev_view_size_request_continuous (view, requisition);
1151         else if (view->dual_page)
1152                 ev_view_size_request_dual_page (view, requisition);
1153         else
1154                 ev_view_size_request_single_page (view, requisition);
1155 }
1156
1157 static void
1158 ev_view_size_allocate (GtkWidget      *widget,
1159                        GtkAllocation  *allocation)
1160 {
1161         EvView *view = EV_VIEW (widget);
1162
1163         if (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
1164                   view->sizing_mode == EV_SIZING_BEST_FIT) {
1165
1166                 g_signal_emit (view, signals[SIGNAL_ZOOM_INVALID], 0);
1167
1168                 ev_view_size_request (widget, &widget->requisition);
1169         }
1170
1171         view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
1172         view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
1173
1174         view->pending_scroll = SCROLL_TO_KEEP_POSITION;
1175         view->pending_resize = FALSE;
1176
1177         if (view->document)
1178                 view_update_range_and_current_page (view);
1179
1180         GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
1181 }
1182
1183 static void
1184 ev_view_realize (GtkWidget *widget)
1185 {
1186         EvView *view = EV_VIEW (widget);
1187         GdkWindowAttr attributes;
1188
1189         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1190
1191
1192         attributes.window_type = GDK_WINDOW_CHILD;
1193         attributes.wclass = GDK_INPUT_OUTPUT;
1194         attributes.visual = gtk_widget_get_visual (widget);
1195         attributes.colormap = gtk_widget_get_colormap (widget);
1196
1197         attributes.x = widget->allocation.x;
1198         attributes.y = widget->allocation.y;
1199         attributes.width = widget->allocation.width;
1200         attributes.height = widget->allocation.height;
1201         attributes.event_mask = GDK_EXPOSURE_MASK |
1202                                 GDK_BUTTON_PRESS_MASK |
1203                                 GDK_BUTTON_RELEASE_MASK |
1204                                 GDK_SCROLL_MASK |
1205                                 GDK_KEY_PRESS_MASK |
1206                                 GDK_POINTER_MOTION_MASK |
1207                                 GDK_LEAVE_NOTIFY_MASK;
1208
1209         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1210                                          &attributes,
1211                                          GDK_WA_X | GDK_WA_Y |
1212                                          GDK_WA_COLORMAP |
1213                                          GDK_WA_VISUAL);
1214         gdk_window_set_user_data (widget->window, widget);
1215         widget->style = gtk_style_attach (widget->style, widget->window);
1216
1217         if (view->presentation)
1218                 gdk_window_set_background (widget->window, &widget->style->black);
1219         else
1220                 gdk_window_set_background (widget->window, &widget->style->mid [GTK_STATE_NORMAL]);
1221 }
1222
1223 static void
1224 ev_view_unrealize (GtkWidget *widget)
1225 {
1226         GTK_WIDGET_CLASS (ev_view_parent_class)->unrealize (widget);
1227 }
1228
1229 static gboolean
1230 ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event)
1231 {
1232         EvView *view = EV_VIEW (widget);
1233
1234         if ((event->state & GDK_CONTROL_MASK) != 0) {
1235
1236                  ev_view_set_sizing_mode (view, EV_SIZING_FREE);
1237
1238                  if (event->direction == GDK_SCROLL_UP ||
1239                         event->direction == GDK_SCROLL_LEFT) {
1240                             if (ev_view_can_zoom_in (view)) {
1241                                     ev_view_zoom_in (view);
1242                             }
1243                  } else {
1244                             if (ev_view_can_zoom_out (view)) {
1245                                     ev_view_zoom_out (view);
1246                             }
1247                  }
1248                  return TRUE;
1249         }
1250
1251         if ((event->state & GDK_SHIFT_MASK) != 0) {
1252                 if (event->direction == GDK_SCROLL_UP)
1253                         event->direction = GDK_SCROLL_LEFT;
1254                 if (event->direction == GDK_SCROLL_DOWN)
1255                         event->direction = GDK_SCROLL_RIGHT;
1256         }
1257
1258         return FALSE;
1259 }
1260
1261 static EvViewSelection *
1262 find_selection_for_page (EvView *view,
1263                          gint    page)
1264 {
1265         GList *list;
1266
1267         for (list = view->selection_info.selections; list != NULL; list = list->next) {
1268                 EvViewSelection *selection;
1269
1270                 selection = (EvViewSelection *) list->data;
1271
1272                 if (selection->page == page)
1273                         return selection;
1274         }
1275
1276         return NULL;
1277 }
1278
1279 static gboolean
1280 ev_view_expose_event (GtkWidget      *widget,
1281                       GdkEventExpose *event)
1282 {
1283         EvView *view = EV_VIEW (widget);
1284         int i;
1285
1286         if (view->document == NULL)
1287                 return FALSE;
1288
1289         for (i = view->start_page; i <= view->end_page; i++) {
1290                 GdkRectangle page_area;
1291                 GtkBorder border;
1292
1293                 if (!get_page_extents (view, i, &page_area, &border))
1294                         continue;
1295
1296                 page_area.x -= view->scroll_x;
1297                 page_area.y -= view->scroll_y;
1298
1299                 draw_one_page (view, i, &page_area, &border, &(event->area));
1300
1301                 if (EV_IS_DOCUMENT_FIND (view->document))
1302                         highlight_find_results (view, i);
1303         }
1304
1305         return FALSE;
1306 }
1307
1308 static gboolean
1309 ev_view_button_press_event (GtkWidget      *widget,
1310                             GdkEventButton *event)
1311 {
1312         EvView *view = EV_VIEW (widget);
1313
1314         if (!GTK_WIDGET_HAS_FOCUS (widget)) {
1315                 gtk_widget_grab_focus (widget);
1316         }
1317
1318         view->pressed_button = event->button;
1319
1320         switch (event->button) {
1321                 case 1:
1322                         if (view->selection_info.selections) {
1323                                 clear_selection (view);
1324                                 gtk_widget_queue_draw (widget);
1325                         }
1326
1327                         view->selection_info.start.x = event->x;
1328                         view->selection_info.start.y = event->y;
1329                         return TRUE;
1330                 case 2:
1331                         /* use root coordinates as reference point because
1332                          * scrolling changes window relative coordinates */
1333                         view->drag_info.start.x = event->x_root;
1334                         view->drag_info.start.y = event->y_root;
1335                         view->drag_info.hadj = gtk_adjustment_get_value (view->hadjustment);
1336                         view->drag_info.vadj = gtk_adjustment_get_value (view->vadjustment);
1337
1338                         ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG);
1339
1340                         return TRUE;
1341         }
1342
1343         return FALSE;
1344 }
1345
1346 static gboolean
1347 ev_view_motion_notify_event (GtkWidget      *widget,
1348                              GdkEventMotion *event)
1349 {
1350         EvView *view = EV_VIEW (widget);
1351
1352         if (!view->document)
1353                 return FALSE;
1354
1355         if (view->pressed_button == 1) {
1356                 GdkRectangle selection;
1357                 view->selection_info.in_selection = TRUE;
1358
1359                 selection.x = MIN (view->selection_info.start.x, event->x) + view->scroll_x;
1360                 selection.y = MIN (view->selection_info.start.y, event->y) + view->scroll_y;
1361                 selection.width = ABS (view->selection_info.start.x - event->x) + 1;
1362                 selection.height = ABS (view->selection_info.start.y - event->y) + 1;
1363
1364                 compute_selections (view, &selection);
1365
1366                 return TRUE;
1367         } else if (view->pressed_button == 2) {
1368                 if (!view->drag_info.in_drag) {
1369                         gboolean start;
1370
1371                         start = gtk_drag_check_threshold (widget,
1372                                                           view->drag_info.start.x,
1373                                                           view->drag_info.start.y,
1374                                                           event->x_root,
1375                                                           event->y_root);
1376                         view->drag_info.in_drag = start;
1377                 }
1378
1379                 if (view->drag_info.in_drag) {
1380                         int dx, dy;
1381                         gdouble dhadj_value, dvadj_value;
1382
1383                         dx = event->x_root - view->drag_info.start.x;
1384                         dy = event->y_root - view->drag_info.start.y;
1385
1386                         dhadj_value = view->hadjustment->page_size *
1387                                       (gdouble)dx / widget->allocation.width;
1388                         dvadj_value = view->vadjustment->page_size *
1389                                       (gdouble)dy / widget->allocation.height;
1390
1391                         /* clamp scrolling to visible area */
1392                         gtk_adjustment_set_value (view->hadjustment,
1393                                                   MIN(view->drag_info.hadj - dhadj_value,
1394                                                       view->hadjustment->upper -
1395                                                       view->hadjustment->page_size));
1396                         gtk_adjustment_set_value (view->vadjustment,
1397                                                   MIN(view->drag_info.vadj - dvadj_value,
1398                                                       view->vadjustment->upper -
1399                                                       view->vadjustment->page_size));
1400
1401                         return TRUE;
1402                 }
1403         } else if (view->pressed_button <= 0) {
1404                 EvLink *link;
1405
1406                 link = get_link_at_location (view, event->x + view->scroll_x, event->y + view->scroll_y);
1407                 if (link) {
1408                         char *msg;
1409
1410                         msg = status_message_from_link (view, link);
1411                         ev_view_set_status (view, msg);
1412                         ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
1413                         g_free (msg);
1414                 } else {
1415                         ev_view_set_status (view, NULL);
1416                         if (view->cursor == EV_VIEW_CURSOR_LINK) {
1417                                 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1418                         }
1419                 }
1420                 return TRUE;
1421         }
1422
1423         return FALSE;
1424 }
1425
1426 static gboolean
1427 ev_view_button_release_event (GtkWidget      *widget,
1428                               GdkEventButton *event)
1429 {
1430         EvView *view = EV_VIEW (widget);
1431
1432         if (view->pressed_button == 2) {
1433                 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1434         }
1435
1436         view->pressed_button = -1;
1437         view->drag_info.in_drag = FALSE;
1438
1439         if (view->selection_info.selections) {
1440                 ev_view_update_primary_selection (view);
1441         } else if (view->document) {
1442                 EvLink *link;
1443
1444                 link = get_link_at_location (view, event->x + view->scroll_x, event->y + view->scroll_y);
1445                 if (link) {
1446                         go_to_link (view, link);
1447                 }
1448         }
1449
1450         return FALSE;
1451 }
1452
1453 /*** Drawing ***/
1454
1455 static guint32
1456 ev_gdk_color_to_rgb (const GdkColor *color)
1457 {
1458   guint32 result;
1459   result = (0xff0000 | (color->red & 0xff00));
1460   result <<= 8;
1461   result |= ((color->green & 0xff00) | (color->blue >> 8));
1462   return result;
1463 }
1464
1465 static void
1466 draw_rubberband (GtkWidget *widget, GdkWindow *window,
1467                  const GdkRectangle *rect, guchar alpha)
1468 {
1469         GdkGC *gc;
1470         GdkPixbuf *pixbuf;
1471         GdkColor *fill_color_gdk;
1472         guint fill_color;
1473
1474         fill_color_gdk = gdk_color_copy (&GTK_WIDGET (widget)->style->base[GTK_STATE_SELECTED]);
1475         fill_color = ev_gdk_color_to_rgb (fill_color_gdk) << 8 | alpha;
1476
1477         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
1478                                  rect->width, rect->height);
1479         gdk_pixbuf_fill (pixbuf, fill_color);
1480
1481         gdk_draw_pixbuf (window, NULL, pixbuf,
1482                          0, 0,
1483                          rect->x - EV_VIEW (widget)->scroll_x, rect->y - EV_VIEW (widget)->scroll_y,
1484                          rect->width, rect->height,
1485                          GDK_RGB_DITHER_NONE,
1486                          0, 0);
1487
1488         g_object_unref (pixbuf);
1489
1490         gc = gdk_gc_new (window);
1491         gdk_gc_set_rgb_fg_color (gc, fill_color_gdk);
1492         gdk_draw_rectangle (window, gc, FALSE,
1493                             rect->x - EV_VIEW (widget)->scroll_x, rect->y - EV_VIEW (widget)->scroll_y,
1494                             rect->width - 1,
1495                             rect->height - 1);
1496         g_object_unref (gc);
1497
1498         gdk_color_free (fill_color_gdk);
1499 }
1500
1501
1502 static void
1503 highlight_find_results (EvView *view, int page)
1504 {
1505         EvDocumentFind *find;
1506         int i, results = 0;
1507
1508         g_return_if_fail (EV_IS_DOCUMENT_FIND (view->document));
1509
1510         find = EV_DOCUMENT_FIND (view->document);
1511
1512         results = ev_document_find_get_n_results (find, page);
1513
1514         for (i = 0; i < results; i++) {
1515                 EvRectangle rectangle;
1516                 GdkRectangle view_rectangle;
1517                 guchar alpha;
1518
1519                 if (i == view->find_result && page == view->find_page) {
1520                         alpha = 0x90;
1521                 } else {
1522                         alpha = 0x20;
1523                 }
1524
1525                 ev_document_find_get_result (find, page,
1526                                              i, &rectangle);
1527                 doc_rect_to_view_rect (view, page, &rectangle, &view_rectangle);
1528                 draw_rubberband (GTK_WIDGET (view), GTK_WIDGET(view)->window,
1529                                  &view_rectangle, alpha);
1530         }
1531 }
1532
1533 static void
1534 draw_one_page (EvView          *view,
1535                gint             page,
1536                GdkRectangle    *page_area,
1537                GtkBorder       *border,
1538                GdkRectangle    *expose_area)
1539 {
1540         gint width, height;
1541         GdkPixbuf *current_pixbuf;
1542         GdkRectangle overlap;
1543         GdkRectangle real_page_area;
1544         EvViewSelection *selection;
1545
1546         g_assert (view->document);
1547         if (! gdk_rectangle_intersect (page_area, expose_area, &overlap))
1548                 return;
1549
1550         selection = find_selection_for_page (view, page);
1551         ev_page_cache_get_size (view->page_cache,
1552                                 page, view->scale,
1553                                 &width, &height);
1554         /* Render the document itself */
1555         real_page_area = *page_area;
1556
1557         real_page_area.x += border->left;
1558         real_page_area.y += border->top;
1559         real_page_area.width -= (border->left + border->right);
1560         real_page_area.height -= (border->top + border->bottom);
1561
1562         ev_document_misc_paint_one_page (GTK_WIDGET(view)->window,
1563                                          GTK_WIDGET (view),
1564                                          page_area, border);
1565
1566         if (gdk_rectangle_intersect (&real_page_area, expose_area, &overlap)) {
1567                 GdkPixbuf *selection_pixbuf = NULL;
1568                 GdkPixbuf *scaled_image;
1569                 GdkPixbuf *scaled_selection;
1570
1571                 current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, page);
1572
1573                 /* Get the selection pixbuf iff we have something to draw */
1574                 if (current_pixbuf && view->selection_mode == EV_VIEW_SELECTION_TEXT && selection)
1575                         selection_pixbuf = ev_pixbuf_cache_get_selection_pixbuf (view->pixbuf_cache,
1576                                                                                  page,
1577                                                                                  view->scale);
1578
1579                 if (current_pixbuf == NULL)
1580                         scaled_image = NULL;
1581                 else if (width == gdk_pixbuf_get_width (current_pixbuf) &&
1582                          height == gdk_pixbuf_get_height (current_pixbuf))
1583                         scaled_image = g_object_ref (current_pixbuf);
1584                 else
1585                         /* FIXME: We don't want to scale the whole area, just the right
1586                          * area of it */
1587                         scaled_image = gdk_pixbuf_scale_simple (current_pixbuf,
1588                                                                 width, height,
1589                                                                 GDK_INTERP_NEAREST);
1590
1591                 if (selection_pixbuf == NULL)
1592                         scaled_selection = NULL;
1593                 else if (width == gdk_pixbuf_get_width (selection_pixbuf) &&
1594                          height == gdk_pixbuf_get_height (selection_pixbuf))
1595                         scaled_selection = g_object_ref (selection_pixbuf);
1596                 else
1597                         /* FIXME: We don't want to scale the whole area, just the right
1598                          * area of it */
1599                         scaled_selection = gdk_pixbuf_scale_simple (selection_pixbuf,
1600                                                                     width, height,
1601                                                                     GDK_INTERP_NEAREST);
1602
1603                 if (scaled_image) {
1604                         gdk_draw_pixbuf (GTK_WIDGET(view)->window,
1605                                          GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
1606                                          scaled_image,
1607                                          overlap.x - real_page_area.x,
1608                                          overlap.y - real_page_area.y,
1609                                          overlap.x, overlap.y,
1610                                          overlap.width, overlap.height,
1611                                          GDK_RGB_DITHER_NORMAL,
1612                                          0, 0);
1613                         g_object_unref (scaled_image);
1614                 }
1615
1616                 if (scaled_selection) {
1617                         gdk_draw_pixbuf (GTK_WIDGET(view)->window,
1618                                          GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
1619                                          scaled_selection,
1620                                          overlap.x - real_page_area.x,
1621                                          overlap.y - real_page_area.y,
1622                                          overlap.x, overlap.y,
1623                                          overlap.width, overlap.height,
1624                                          GDK_RGB_DITHER_NORMAL,
1625                                          0, 0);
1626                         g_object_unref (scaled_selection);
1627                 }
1628         }
1629 }
1630
1631 /*** GObject functions ***/
1632
1633 static void
1634 ev_view_finalize (GObject *object)
1635 {
1636         EvView *view = EV_VIEW (object);
1637
1638         LOG ("Finalize");
1639
1640         g_free (view->status);
1641         g_free (view->find_status);
1642
1643         clear_selection (view);
1644
1645         G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
1646 }
1647
1648 static void
1649 ev_view_destroy (GtkObject *object)
1650 {
1651         EvView *view = EV_VIEW (object);
1652
1653         if (view->document) {
1654                 g_object_unref (view->document);
1655                 view->document = NULL;
1656         }
1657         if (view->pixbuf_cache) {
1658                 g_object_unref (view->pixbuf_cache);
1659                 view->pixbuf_cache = NULL;
1660         }
1661         ev_view_set_scroll_adjustments (view, NULL, NULL);
1662
1663         GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
1664 }
1665
1666 static void
1667 ev_view_set_property (GObject      *object,
1668                       guint         prop_id,
1669                       const GValue *value,
1670                       GParamSpec   *pspec)
1671 {
1672         EvView *view = EV_VIEW (object);
1673
1674         switch (prop_id)
1675         {
1676         case PROP_CONTINUOUS:
1677                 ev_view_set_continuous (view, g_value_get_boolean (value));
1678                 break;
1679         case PROP_DUAL_PAGE:
1680                 ev_view_set_dual_page (view, g_value_get_boolean (value));
1681                 break;
1682         case PROP_FULLSCREEN:
1683                 ev_view_set_fullscreen (view, g_value_get_boolean (value));
1684                 break;
1685         case PROP_PRESENTATION:
1686                 ev_view_set_presentation (view, g_value_get_boolean (value));
1687                 break;
1688         case PROP_SIZING_MODE:
1689                 ev_view_set_sizing_mode (view, g_value_get_enum (value));
1690                 break;
1691         case PROP_ZOOM:
1692                 ev_view_set_zoom (view, g_value_get_double (value), FALSE);
1693                 break;
1694         default:
1695                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1696         }
1697 }
1698
1699 static void
1700 ev_view_get_property (GObject *object,
1701                       guint prop_id,
1702                       GValue *value,
1703                       GParamSpec *pspec)
1704 {
1705         EvView *view = EV_VIEW (object);
1706
1707         switch (prop_id)
1708         {
1709         case PROP_STATUS:
1710                 g_value_set_string (value, view->status);
1711                 break;
1712         case PROP_FIND_STATUS:
1713                 g_value_set_string (value, view->status);
1714                 break;
1715         case PROP_CONTINUOUS:
1716                 g_value_set_boolean (value, view->continuous);
1717                 break;
1718         case PROP_DUAL_PAGE:
1719                 g_value_set_boolean (value, view->dual_page);
1720                 break;
1721         case PROP_FULLSCREEN:
1722                 g_value_set_boolean (value, view->fullscreen);
1723                 break;
1724         case PROP_PRESENTATION:
1725                 g_value_set_boolean (value, view->presentation);
1726                 break;
1727         case PROP_SIZING_MODE:
1728                 g_value_set_enum (value, view->sizing_mode);
1729                 break;
1730         case PROP_ZOOM:
1731                 g_value_set_double (value, view->scale);
1732                 break;
1733         default:
1734                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1735         }
1736 }
1737
1738 static void
1739 ev_view_class_init (EvViewClass *class)
1740 {
1741         GObjectClass *object_class = G_OBJECT_CLASS (class);
1742         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
1743         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1744         GtkBindingSet *binding_set;
1745
1746         object_class->finalize = ev_view_finalize;
1747         object_class->set_property = ev_view_set_property;
1748         object_class->get_property = ev_view_get_property;
1749
1750         widget_class->expose_event = ev_view_expose_event;
1751         widget_class->button_press_event = ev_view_button_press_event;
1752         widget_class->motion_notify_event = ev_view_motion_notify_event;
1753         widget_class->button_release_event = ev_view_button_release_event;
1754         widget_class->size_request = ev_view_size_request;
1755         widget_class->size_allocate = ev_view_size_allocate;
1756         widget_class->realize = ev_view_realize;
1757         widget_class->unrealize = ev_view_unrealize;
1758         widget_class->scroll_event = ev_view_scroll_event;
1759         gtk_object_class->destroy = ev_view_destroy;
1760
1761         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
1762         class->binding_activated = ev_view_binding_activated;
1763
1764         widget_class->set_scroll_adjustments_signal =
1765             g_signal_new ("set-scroll-adjustments",
1766                           G_OBJECT_CLASS_TYPE (object_class),
1767                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1768                           G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
1769                           NULL, NULL,
1770                           ev_marshal_VOID__OBJECT_OBJECT,
1771                           G_TYPE_NONE, 2,
1772                           GTK_TYPE_ADJUSTMENT,
1773                           GTK_TYPE_ADJUSTMENT);
1774
1775         signals[SIGNAL_BINDING_ACTIVATED] = g_signal_new ("binding_activated",
1776                          G_TYPE_FROM_CLASS (object_class),
1777                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1778                          G_STRUCT_OFFSET (EvViewClass, binding_activated),
1779                          NULL, NULL,
1780                          ev_marshal_VOID__ENUM_BOOLEAN,
1781                          G_TYPE_NONE, 2,
1782                          GTK_TYPE_SCROLL_TYPE,
1783                          G_TYPE_BOOLEAN);
1784
1785         signals[SIGNAL_ZOOM_INVALID] = g_signal_new ("zoom-invalid",
1786                          G_TYPE_FROM_CLASS (object_class),
1787                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1788                          G_STRUCT_OFFSET (EvViewClass, zoom_invalid),
1789                          NULL, NULL,
1790                          ev_marshal_VOID__VOID,
1791                          G_TYPE_NONE, 0, G_TYPE_NONE);
1792
1793         g_object_class_install_property (object_class,
1794                                          PROP_STATUS,
1795                                          g_param_spec_string ("status",
1796                                                               "Status Message",
1797                                                               "The status message",
1798                                                               NULL,
1799                                                               G_PARAM_READABLE));
1800
1801         g_object_class_install_property (object_class,
1802                                          PROP_FIND_STATUS,
1803                                          g_param_spec_string ("find-status",
1804                                                               "Find Status Message",
1805                                                               "The find status message",
1806                                                               NULL,
1807                                                               G_PARAM_READABLE));
1808
1809         g_object_class_install_property (object_class,
1810                                          PROP_CONTINUOUS,
1811                                          g_param_spec_boolean ("continuous",
1812                                                                "Continuous",
1813                                                                "Continuous scrolling mode",
1814                                                                TRUE,
1815                                                                G_PARAM_READWRITE));
1816
1817         g_object_class_install_property (object_class,
1818                                          PROP_DUAL_PAGE,
1819                                          g_param_spec_boolean ("dual-page",
1820                                                                "Dual Page",
1821                                                                "Two pages visible at once",
1822                                                                FALSE,
1823                                                                G_PARAM_READWRITE));
1824         g_object_class_install_property (object_class,
1825                                          PROP_FULLSCREEN,
1826                                          g_param_spec_boolean ("fullscreen",
1827                                                                "Full Screen",
1828                                                                "Draw page in a fullscreen fashion",
1829                                                                FALSE,
1830                                                                G_PARAM_READWRITE));
1831         g_object_class_install_property (object_class,
1832                                          PROP_PRESENTATION,
1833                                          g_param_spec_boolean ("presentation",
1834                                                                "Presentation",
1835                                                                "Draw page in presentation mode",
1836                                                                TRUE,
1837                                                                G_PARAM_READWRITE));
1838
1839         g_object_class_install_property (object_class,
1840                                          PROP_SIZING_MODE,
1841                                          g_param_spec_enum ("sizing-mode",
1842                                                             "Sizing Mode",
1843                                                             "Sizing Mode",
1844                                                             EV_TYPE_SIZING_MODE,
1845                                                             EV_SIZING_FIT_WIDTH,
1846                                                             G_PARAM_READWRITE));
1847
1848         g_object_class_install_property (object_class,
1849                                          PROP_ZOOM,
1850                                          g_param_spec_double ("zoom",
1851                                                               "Zoom factor",
1852                                                                "Zoom factor",
1853                                                                MIN_SCALE,
1854                                                                MAX_SCALE,
1855                                                                1.0,
1856                                                                G_PARAM_READWRITE));
1857
1858         binding_set = gtk_binding_set_by_class (class);
1859
1860         add_scroll_binding_keypad (binding_set, GDK_Left,  GTK_SCROLL_STEP_BACKWARD, TRUE);
1861         add_scroll_binding_keypad (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD,  TRUE);
1862         add_scroll_binding_keypad (binding_set, GDK_Up,    GTK_SCROLL_STEP_BACKWARD, FALSE);
1863         add_scroll_binding_keypad (binding_set, GDK_Down,  GTK_SCROLL_STEP_FORWARD,  FALSE);
1864 }
1865
1866 static void
1867 ev_view_init (EvView *view)
1868 {
1869         GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
1870
1871         view->spacing = 5;
1872         view->scale = 1.0;
1873         view->current_page = 0;
1874         view->pressed_button = -1;
1875         view->cursor = EV_VIEW_CURSOR_NORMAL;
1876         view->drag_info.in_drag = FALSE;
1877         view->selection_info.in_selection = FALSE;
1878
1879         view->selection_mode = EV_VIEW_SELECTION_TEXT;
1880         view->continuous = TRUE;
1881         view->dual_page = FALSE;
1882         view->presentation = FALSE;
1883         view->fullscreen = FALSE;
1884         view->sizing_mode = EV_SIZING_FIT_WIDTH;
1885         view->pending_scroll = SCROLL_TO_KEEP_POSITION;
1886 }
1887
1888 /*** Callbacks ***/
1889
1890 static void
1891 find_changed_cb (EvDocument *document, int page, EvView *view)
1892 {
1893         jump_to_find_page (view);
1894         jump_to_find_result (view);
1895         update_find_status_message (view);
1896
1897         if (view->current_page == page)
1898                 gtk_widget_queue_draw (GTK_WIDGET (view));
1899 }
1900
1901 static void
1902 job_finished_cb (EvPixbufCache *pixbuf_cache,
1903                  EvView        *view)
1904 {
1905         gtk_widget_queue_draw (GTK_WIDGET (view));
1906 }
1907
1908 static void
1909 page_changed_cb (EvPageCache *page_cache,
1910                  int          new_page,
1911                  EvView      *view)
1912 {
1913         if (view->current_page != new_page) {
1914
1915                 view->current_page = new_page;
1916                 view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
1917                 gtk_widget_queue_resize (GTK_WIDGET (view));
1918
1919                 if (EV_IS_DOCUMENT_FIND (view->document)) {
1920                         view->find_page = new_page;
1921                         view->find_result = 0;
1922                         update_find_status_message (view);
1923                 }
1924         }
1925 }
1926
1927 static void on_adjustment_value_changed (GtkAdjustment  *adjustment,
1928                                          EvView *view)
1929 {
1930         int dx = 0, dy = 0;
1931
1932         if (! GTK_WIDGET_REALIZED (view))
1933                 return;
1934
1935         if (view->hadjustment) {
1936                 dx = view->scroll_x - (int) view->hadjustment->value;
1937                 view->scroll_x = (int) view->hadjustment->value;
1938         } else {
1939                 view->scroll_x = 0;
1940         }
1941
1942         if (view->vadjustment) {
1943                 dy = view->scroll_y - (int) view->vadjustment->value;
1944                 view->scroll_y = (int) view->vadjustment->value;
1945         } else {
1946                 view->scroll_y = 0;
1947         }
1948
1949
1950         if (view->pending_resize)
1951                 gtk_widget_queue_draw (GTK_WIDGET (view));
1952         else
1953                 gdk_window_scroll (GTK_WIDGET (view)->window, dx, dy);
1954
1955
1956         if (view->document)
1957                 view_update_range_and_current_page (view);
1958 }
1959
1960 GtkWidget*
1961 ev_view_new (void)
1962 {
1963         GtkWidget *view;
1964
1965         view = g_object_new (EV_TYPE_VIEW, NULL);
1966
1967         return view;
1968 }
1969
1970 static void
1971 setup_caches (EvView *view)
1972 {
1973         view->page_cache = ev_page_cache_get (view->document);
1974         g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
1975         view->pixbuf_cache = ev_pixbuf_cache_new (view->document);
1976         g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
1977 }
1978
1979 static void
1980 clear_caches (EvView *view)
1981 {
1982         if (view->pixbuf_cache) {
1983                 g_object_unref (view->pixbuf_cache);
1984                 view->pixbuf_cache = NULL;
1985         }
1986
1987         if (view->document) {
1988                 ev_page_cache_clear (view->document);
1989         }
1990 }
1991
1992 void
1993 ev_view_set_document (EvView     *view,
1994                       EvDocument *document)
1995 {
1996         g_return_if_fail (EV_IS_VIEW (view));
1997
1998         if (document != view->document) {
1999                 clear_caches (view);
2000
2001                 if (view->document) {
2002                         g_signal_handlers_disconnect_by_func (view->document,
2003                                                               find_changed_cb,
2004                                                               view);
2005                         g_object_unref (view->document);
2006                         view->page_cache = NULL;
2007
2008                 }
2009
2010                 view->document = document;
2011                 view->find_page = 0;
2012                 view->find_result = 0;
2013
2014                 if (view->document) {
2015                         g_object_ref (view->document);
2016                         if (EV_IS_DOCUMENT_FIND (view->document)) {
2017                                 g_signal_connect (view->document,
2018                                                   "find_changed",
2019                                                   G_CALLBACK (find_changed_cb),
2020                                                   view);
2021                         }
2022
2023                         setup_caches (view);
2024                 }
2025
2026                 gtk_widget_queue_resize (GTK_WIDGET (view));
2027         }
2028 }
2029
2030 /*** Zoom and sizing mode ***/
2031
2032 #define EPSILON 0.0000001
2033 void
2034 ev_view_set_zoom (EvView   *view,
2035                   double    factor,
2036                   gboolean  relative)
2037 {
2038         double scale;
2039
2040         if (relative)
2041                 scale = view->scale * factor;
2042         else
2043                 scale = factor;
2044
2045         scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
2046
2047         if (ABS (view->scale - scale) < EPSILON)
2048                 return;
2049
2050         view->scale = scale;
2051         view->pending_resize = TRUE;
2052
2053         gtk_widget_queue_resize (GTK_WIDGET (view));
2054
2055         g_object_notify (G_OBJECT (view), "zoom");
2056 }
2057
2058 double
2059 ev_view_get_zoom (EvView *view)
2060 {
2061         return view->scale;
2062 }
2063
2064 void
2065 ev_view_set_continuous (EvView   *view,
2066                         gboolean  continuous)
2067 {
2068         g_return_if_fail (EV_IS_VIEW (view));
2069
2070         continuous = continuous != FALSE;
2071
2072         if (view->continuous != continuous) {
2073                 view->continuous = continuous;
2074                 view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2075                 gtk_widget_queue_resize (GTK_WIDGET (view));
2076         }
2077
2078         g_object_notify (G_OBJECT (view), "continuous");
2079 }
2080
2081 void
2082 ev_view_set_dual_page (EvView   *view,
2083                        gboolean  dual_page)
2084 {
2085         g_return_if_fail (EV_IS_VIEW (view));
2086
2087         dual_page = dual_page != FALSE;
2088
2089         if (view->dual_page == dual_page)
2090                 return;
2091
2092         view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2093         view->dual_page = dual_page;
2094         /* FIXME: if we're keeping the pixbuf cache around, we should extend the
2095          * preload_cache_size to be 2 if dual_page is set.
2096          */
2097         gtk_widget_queue_resize (GTK_WIDGET (view));
2098
2099         g_object_notify (G_OBJECT (view), "dual-page");
2100 }
2101
2102 void
2103 ev_view_set_fullscreen (EvView   *view,
2104                          gboolean  fullscreen)
2105 {
2106         g_return_if_fail (EV_IS_VIEW (view));
2107
2108         fullscreen = fullscreen != FALSE;
2109
2110         if (view->fullscreen != fullscreen) {
2111                 view->fullscreen = fullscreen;
2112                 gtk_widget_queue_resize (GTK_WIDGET (view));
2113         }
2114
2115         g_object_notify (G_OBJECT (view), "fullscreen");
2116 }
2117
2118 gboolean
2119 ev_view_get_fullscreen (EvView *view)
2120 {
2121         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2122
2123         return view->fullscreen;
2124 }
2125
2126 void
2127 ev_view_set_presentation (EvView   *view,
2128                           gboolean  presentation)
2129 {
2130         g_return_if_fail (EV_IS_VIEW (view));
2131
2132         presentation = presentation != FALSE;
2133
2134         if (view->presentation == presentation)
2135                 return;
2136
2137         view->presentation = presentation;
2138         gtk_widget_queue_resize (GTK_WIDGET (view));
2139         if (GTK_WIDGET_REALIZED (view)) {
2140                 if (view->presentation)
2141                         gdk_window_set_background (GTK_WIDGET(view)->window,
2142                                                    &GTK_WIDGET (view)->style->black);
2143                 else
2144                         gdk_window_set_background (GTK_WIDGET(view)->window,
2145                                                    &GTK_WIDGET (view)->style->mid [GTK_STATE_NORMAL]);
2146         }
2147
2148
2149         g_object_notify (G_OBJECT (view), "presentation");
2150 }
2151
2152 gboolean
2153 ev_view_get_presentation (EvView *view)
2154 {
2155         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2156
2157         return view->presentation;
2158 }
2159
2160 void
2161 ev_view_set_sizing_mode (EvView       *view,
2162                          EvSizingMode  sizing_mode)
2163 {
2164         g_return_if_fail (EV_IS_VIEW (view));
2165
2166         if (view->sizing_mode == sizing_mode)
2167                 return;
2168
2169         view->sizing_mode = sizing_mode;
2170         gtk_widget_queue_resize (GTK_WIDGET (view));
2171
2172         g_object_notify (G_OBJECT (view), "sizing-mode");
2173 }
2174
2175 EvSizingMode
2176 ev_view_get_sizing_mode (EvView *view)
2177 {
2178         g_return_val_if_fail (EV_IS_VIEW (view), EV_SIZING_FREE);
2179
2180         return view->sizing_mode;
2181 }
2182
2183 gboolean
2184 ev_view_can_zoom_in (EvView *view)
2185 {
2186         return view->scale * ZOOM_IN_FACTOR <= MAX_SCALE;
2187 }
2188
2189 gboolean
2190 ev_view_can_zoom_out (EvView *view)
2191 {
2192         return view->scale * ZOOM_OUT_FACTOR >= MIN_SCALE;
2193 }
2194
2195 void
2196 ev_view_zoom_in (EvView *view)
2197 {
2198         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2199
2200         view->pending_scroll = SCROLL_TO_CENTER;
2201         ev_view_set_zoom (view, ZOOM_IN_FACTOR, TRUE);
2202 }
2203
2204 void
2205 ev_view_zoom_out (EvView *view)
2206 {
2207         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2208
2209         view->pending_scroll = SCROLL_TO_CENTER;
2210         ev_view_set_zoom (view, ZOOM_OUT_FACTOR, TRUE);
2211 }
2212
2213 static void
2214 ev_view_set_orientation (EvView         *view,
2215                          EvOrientation   orientation)
2216 {
2217         ev_document_set_orientation (view->document, orientation);
2218
2219         clear_caches (view);
2220         setup_caches (view);
2221
2222         gtk_widget_queue_resize (GTK_WIDGET (view));
2223 }
2224
2225 void
2226 ev_view_rotate_right (EvView *view)
2227 {
2228         EvOrientation orientation, new_orientation;
2229
2230         orientation = ev_document_get_orientation (view->document);
2231         if (orientation == EV_ORIENTATION_PORTRAIT) {
2232                 new_orientation = EV_ORIENTATION_LANDSCAPE;
2233         } else if (orientation == EV_ORIENTATION_LANDSCAPE) {
2234                 new_orientation = EV_ORIENTATION_UPSIDEDOWN;
2235         } else if (orientation == EV_ORIENTATION_UPSIDEDOWN) {
2236                 new_orientation = EV_ORIENTATION_SEASCAPE;
2237         } else {
2238                 new_orientation = EV_ORIENTATION_PORTRAIT;
2239         }
2240         ev_view_set_orientation (view, new_orientation);
2241 }
2242
2243 void
2244 ev_view_rotate_left (EvView *view)
2245 {
2246         EvOrientation orientation, new_orientation;
2247
2248         orientation = ev_document_get_orientation (view->document);
2249         if (orientation == EV_ORIENTATION_PORTRAIT) {
2250                 new_orientation = EV_ORIENTATION_SEASCAPE;
2251         } else if (orientation == EV_ORIENTATION_SEASCAPE) {
2252                 new_orientation = EV_ORIENTATION_UPSIDEDOWN;
2253         } else if (orientation == EV_ORIENTATION_UPSIDEDOWN) {
2254                 new_orientation = EV_ORIENTATION_LANDSCAPE;
2255         } else {
2256                 new_orientation = EV_ORIENTATION_PORTRAIT;
2257         }
2258         ev_view_set_orientation (view, new_orientation);
2259 }
2260
2261 static double
2262 zoom_for_size_fit_width (int doc_width,
2263                          int doc_height,
2264                          int target_width,
2265                          int target_height,
2266                          int vsb_width)
2267 {
2268         double scale;
2269
2270         scale = (double)target_width / doc_width;
2271
2272         if (doc_height * scale > target_height)
2273                 scale = (double) (target_width - vsb_width) / doc_width;
2274
2275         return scale;
2276 }
2277
2278 static double
2279 zoom_for_size_best_fit (int doc_width,
2280                         int doc_height,
2281                         int target_width,
2282                         int target_height,
2283                         int vsb_width,
2284                         int hsb_width)
2285 {
2286         double w_scale;
2287         double h_scale;
2288
2289         w_scale = (double)target_width / doc_width;
2290         h_scale = (double)target_height / doc_height;
2291
2292         if (doc_height * w_scale > target_height)
2293                 w_scale = (double) (target_width - vsb_width) / doc_width;
2294         if (doc_width * h_scale > target_width)
2295                 h_scale = (double) (target_height - hsb_width) / doc_height;
2296
2297         return MIN (w_scale, h_scale);
2298 }
2299
2300
2301 static void
2302 ev_view_zoom_for_size_presentation (EvView *view,
2303                                     int     width,
2304                                     int     height)
2305 {
2306         int doc_width, doc_height;
2307         gdouble scale;
2308
2309         ev_page_cache_get_size (view->page_cache,
2310                                 view->current_page,
2311                                 1.0,
2312                                 &doc_width,
2313                                 &doc_height);
2314         scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, 0, 0);
2315         ev_view_set_zoom (view, scale, FALSE);
2316 }
2317
2318 static void
2319 ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
2320                            int     width,
2321                            int     height,
2322                            int     vsb_width,
2323                            int     hsb_height)
2324 {
2325         int doc_width, doc_height;
2326         GtkBorder border;
2327         gdouble scale;
2328
2329         ev_page_cache_get_max_width (view->page_cache,
2330                                      1.0,
2331                                      &doc_width);
2332         ev_page_cache_get_max_height (view->page_cache,
2333                                       1.0,
2334                                       &doc_height);
2335         compute_border (view, doc_width, doc_height, &border);
2336
2337         doc_width = doc_width * 2;
2338         width -= (2 * (border.left + border.right) + 3 * view->spacing);
2339         height -= (border.top + border.bottom + 2 * view->spacing - 1);
2340
2341         /* FIXME: We really need to calculate the overall height here, not the
2342          * page height.  We assume there's always a vertical scrollbar for
2343          * now.  We need to fix this. */
2344         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2345                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2346         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2347                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2348         else
2349                 g_assert_not_reached ();
2350
2351         ev_view_set_zoom (view, scale, FALSE);
2352 }
2353
2354 static void
2355 ev_view_zoom_for_size_continuous (EvView *view,
2356                                   int     width,
2357                                   int     height,
2358                                   int     vsb_width,
2359                                   int     hsb_height)
2360 {
2361         int doc_width, doc_height;
2362         GtkBorder border;
2363         gdouble scale;
2364
2365         ev_page_cache_get_max_width (view->page_cache,
2366                                      1.0,
2367                                      &doc_width);
2368         ev_page_cache_get_max_height (view->page_cache,
2369                                       1.0,
2370                                       &doc_height);
2371         compute_border (view, doc_width, doc_height, &border);
2372
2373         width -= (border.left + border.right + 2 * view->spacing);
2374         height -= (border.top + border.bottom + 2 * view->spacing - 1);
2375
2376         /* FIXME: We really need to calculate the overall height here, not the
2377          * page height.  We assume there's always a vertical scrollbar for
2378          * now.  We need to fix this. */
2379         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2380                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2381         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2382                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2383         else
2384                 g_assert_not_reached ();
2385
2386         ev_view_set_zoom (view, scale, FALSE);
2387 }
2388
2389 static void
2390 ev_view_zoom_for_size_dual_page (EvView *view,
2391                                  int     width,
2392                                  int     height,
2393                                  int     vsb_width,
2394                                  int     hsb_height)
2395 {
2396         GtkBorder border;
2397         gint doc_width, doc_height;
2398         gdouble scale;
2399         gint other_page;
2400
2401         other_page = view->current_page ^ 1;
2402
2403         /* Find the largest of the two. */
2404         ev_page_cache_get_size (view->page_cache,
2405                                 view->current_page,
2406                                 1.0,
2407                                 &doc_width, &doc_height);
2408
2409         if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
2410                 gint width_2, height_2;
2411                 ev_page_cache_get_size (view->page_cache,
2412                                         other_page,
2413                                         1.0,
2414                                         &width_2, &height_2);
2415                 if (width_2 > doc_width)
2416                         doc_width = width_2;
2417                 if (height_2 > doc_height)
2418                         doc_height = height_2;
2419         }
2420         compute_border (view, doc_width, doc_height, &border);
2421
2422         doc_width = doc_width * 2;
2423         width -= ((border.left + border.right)* 2 + 3 * view->spacing);
2424         height -= (border.top + border.bottom + 2 * view->spacing);
2425
2426         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2427                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2428         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2429                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2430         else
2431                 g_assert_not_reached ();
2432
2433         ev_view_set_zoom (view, scale, FALSE);
2434 }
2435
2436 static void
2437 ev_view_zoom_for_size_single_page (EvView *view,
2438                                    int     width,
2439                                    int     height,
2440                                    int     vsb_width,
2441                                    int     hsb_height)
2442 {
2443         int doc_width, doc_height;
2444         GtkBorder border;
2445         gdouble scale;
2446
2447         ev_page_cache_get_size (view->page_cache,
2448                                 view->current_page,
2449                                 1.0,
2450                                 &doc_width,
2451                                 &doc_height);
2452         /* Get an approximate border */
2453         compute_border (view, width, height, &border);
2454
2455         width -= (border.left + border.right + 2 * view->spacing);
2456         height -= (border.top + border.bottom + 2 * view->spacing);
2457
2458         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2459                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2460         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2461                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2462         else
2463                 g_assert_not_reached ();
2464
2465         ev_view_set_zoom (view, scale, FALSE);
2466 }
2467
2468 void
2469 ev_view_set_zoom_for_size (EvView *view,
2470                            int     width,
2471                            int     height,
2472                            int     vsb_width,
2473                            int     hsb_height)
2474 {
2475         g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
2476                           view->sizing_mode == EV_SIZING_BEST_FIT);
2477         g_return_if_fail (width >= 0);
2478         g_return_if_fail (height >= 0);
2479
2480         if (view->document == NULL)
2481                 return;
2482
2483         if (view->presentation)
2484                 ev_view_zoom_for_size_presentation (view, width, height);
2485         else if (view->continuous && view->dual_page)
2486                 ev_view_zoom_for_size_continuous_and_dual_page (view, width, height, vsb_width, hsb_height);
2487         else if (view->continuous)
2488                 ev_view_zoom_for_size_continuous (view, width, height, vsb_width, hsb_height);
2489         else if (view->dual_page)
2490                 ev_view_zoom_for_size_dual_page (view, width, height, vsb_width, hsb_height);
2491         else
2492                 ev_view_zoom_for_size_single_page (view, width, height, vsb_width, hsb_height);
2493 }
2494
2495 /*** Status text messages ***/
2496
2497 const char *
2498 ev_view_get_status (EvView *view)
2499 {
2500         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2501
2502         return view->status;
2503 }
2504
2505 static void
2506 ev_view_set_status (EvView *view, const char *message)
2507 {
2508         g_return_if_fail (EV_IS_VIEW (view));
2509
2510         if (message != view->status) {
2511                 g_free (view->status);
2512                 view->status = g_strdup (message);
2513                 g_object_notify (G_OBJECT (view), "status");
2514         }
2515 }
2516
2517 static void
2518 update_find_status_message (EvView *view)
2519 {
2520         char *message;
2521
2522         if (view->current_page == view->find_page) {
2523                 int results;
2524
2525                 results = ev_document_find_get_n_results
2526                                 (EV_DOCUMENT_FIND (view->document),
2527                                  view->current_page);
2528                 /* TRANS: Sometimes this could be better translated as
2529                    "%d hit(s) on this page".  Therefore this string
2530                    contains plural cases. */
2531                 message = g_strdup_printf (ngettext ("%d found on this page",
2532                                                      "%d found on this page",
2533                                                      results),
2534                                            results);
2535         } else {
2536                 double percent;
2537
2538                 percent = ev_document_find_get_progress
2539                                 (EV_DOCUMENT_FIND (view->document));
2540                 if (percent >= (1.0 - 1e-10)) {
2541                         message = g_strdup (_("Not found"));
2542                 } else {
2543                         message = g_strdup_printf (_("%3d%% remaining to search"),
2544                                                    (int) ((1.0 - percent) * 100));
2545                 }
2546
2547         }
2548         ev_view_set_find_status (view, message);
2549         g_free (message);
2550 }
2551
2552 const char *
2553 ev_view_get_find_status (EvView *view)
2554 {
2555         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2556
2557         return view->find_status;
2558 }
2559
2560 static void
2561 ev_view_set_find_status (EvView *view, const char *message)
2562 {
2563         g_return_if_fail (EV_IS_VIEW (view));
2564
2565         g_free (view->find_status);
2566         view->find_status = g_strdup (message);
2567         g_object_notify (G_OBJECT (view), "find-status");
2568 }
2569
2570 /*** Find ***/
2571
2572 static void
2573 jump_to_find_result (EvView *view)
2574 {
2575         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2576         EvRectangle rect;
2577         GdkRectangle view_rect;
2578         int n_results;
2579         int page = view->find_page;
2580
2581         n_results = ev_document_find_get_n_results (find, page);
2582
2583         if (n_results > view->find_result) {
2584                 ev_document_find_get_result
2585                         (find, page, view->find_result, &rect);
2586
2587                 doc_rect_to_view_rect (view, page, &rect, &view_rect);
2588                 ensure_rectangle_is_visible (view, &view_rect);
2589         }
2590 }
2591
2592 static void
2593 jump_to_find_page (EvView *view)
2594 {
2595         int n_pages, i;
2596
2597         n_pages = ev_page_cache_get_n_pages (view->page_cache);
2598
2599         for (i = 0; i < n_pages; i++) {
2600                 int has_results;
2601                 int page;
2602
2603                 page = i + view->find_page;
2604                 if (page >= n_pages) {
2605                         page = page - n_pages;
2606                 }
2607
2608                 has_results = ev_document_find_page_has_results
2609                                 (EV_DOCUMENT_FIND (view->document), page);
2610                 if (has_results == -1) {
2611                         view->find_page = page;
2612                         break;
2613                 } else if (has_results == 1) {
2614                         ev_page_cache_set_current_page (view->page_cache, page);
2615                         jump_to_find_result (view);
2616                         break;
2617                 }
2618         }
2619 }
2620
2621 gboolean
2622 ev_view_can_find_next (EvView *view)
2623 {
2624         int n_results = 0;
2625
2626         if (EV_IS_DOCUMENT_FIND (view->document)) {
2627                 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2628
2629                 n_results = ev_document_find_get_n_results (find, view->current_page);
2630         }
2631
2632         return n_results > 0;
2633 }
2634
2635 void
2636 ev_view_find_next (EvView *view)
2637 {
2638         EvPageCache *page_cache;
2639         int n_results, n_pages;
2640         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2641
2642         page_cache = ev_page_cache_get (view->document);
2643         n_results = ev_document_find_get_n_results (find, view->current_page);
2644
2645         n_pages = ev_page_cache_get_n_pages (page_cache);
2646
2647         view->find_result++;
2648
2649         if (view->find_result >= n_results) {
2650                 view->find_result = 0;
2651                 view->find_page++;
2652
2653                 if (view->find_page >= n_pages) {
2654                         view->find_page = 0;
2655                 }
2656
2657                 jump_to_find_page (view);
2658         } else {
2659                 jump_to_find_result (view);
2660                 gtk_widget_queue_draw (GTK_WIDGET (view));
2661         }
2662 }
2663
2664 void
2665 ev_view_find_previous (EvView *view)
2666 {
2667         int n_results, n_pages;
2668         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2669         EvPageCache *page_cache;
2670
2671         page_cache = ev_page_cache_get (view->document);
2672
2673         n_results = ev_document_find_get_n_results (find, view->current_page);
2674
2675         n_pages = ev_page_cache_get_n_pages (page_cache);
2676
2677         view->find_result--;
2678
2679         if (view->find_result < 0) {
2680                 view->find_result = 0;
2681                 view->find_page--;
2682
2683                 if (view->find_page < 0) {
2684                         view->find_page = n_pages - 1;
2685                 }
2686
2687                 jump_to_find_page (view);
2688         } else {
2689                 jump_to_find_result (view);
2690                 gtk_widget_queue_draw (GTK_WIDGET (view));
2691         }
2692 }
2693
2694 /*** Selections ***/
2695
2696 /* compute_new_selection_rect/text calculates the area currently selected by
2697  * view_rect.  each handles a different mode;
2698  */
2699 static GList *
2700 compute_new_selection_rect (EvView       *view,
2701                             GdkRectangle *view_rect)
2702 {
2703         int n_pages, i;
2704         GList *list = NULL;
2705
2706         g_assert (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE);
2707
2708         n_pages = ev_page_cache_get_n_pages (view->page_cache);
2709
2710         for (i = 0; i < n_pages; i++) {
2711                 GdkRectangle page_area;
2712                 GtkBorder border;
2713
2714                 if (get_page_extents (view, i, &page_area, &border)) {
2715                         GdkRectangle overlap;
2716
2717                         if (gdk_rectangle_intersect (&page_area, view_rect, &overlap)) {
2718                                 EvViewSelection *selection;
2719
2720                                 selection = g_new0 (EvViewSelection, 1);
2721                                 selection->page = i;
2722                                 view_rect_to_doc_rect (view, &overlap, &page_area,
2723                                                        &(selection->rect));
2724
2725                                 list = g_list_append (list, selection);
2726                         }
2727                 }
2728         }
2729
2730         return list;
2731 }
2732
2733 static GList *
2734 compute_new_selection_text (EvView       *view,
2735                             GdkRectangle *view_rect)
2736 {
2737         int n_pages, i;
2738         GList *list = NULL;
2739         EvViewSelection *first_selection = NULL;
2740         EvViewSelection *last_selection = NULL;
2741         gint width, height;
2742
2743         g_assert (view->selection_mode == EV_VIEW_SELECTION_TEXT);
2744
2745         n_pages = ev_page_cache_get_n_pages (view->page_cache);
2746
2747         /* We get the two edge pages */
2748         for (i = 0; i < n_pages; i++) {
2749                 GdkRectangle page_area;
2750                 GtkBorder border;
2751
2752                 if (get_page_extents (view, i, &page_area, &border)) {
2753                         GdkRectangle overlap;
2754
2755                         if (gdk_rectangle_intersect (&page_area, view_rect, &overlap)) {
2756                                 EvViewSelection *selection;
2757
2758                                 if (first_selection == NULL) {
2759                                         first_selection = g_new0 (EvViewSelection, 1);
2760                                         selection = first_selection;
2761                                 } else if (last_selection == NULL) {
2762                                         last_selection = g_new0 (EvViewSelection, 1);
2763                                         selection = last_selection;
2764                                 } else {
2765                                         selection = last_selection;
2766                                 }
2767
2768                                 selection->page = i;
2769                                 view_rect_to_doc_rect (view, &overlap, &page_area,
2770                                                        &(selection->rect));
2771                         }
2772                 }
2773         }
2774
2775         /* No overlap */
2776         if (first_selection == NULL)
2777                 return NULL;
2778
2779         /* only one selection.  Return a page of it */
2780         if (last_selection == NULL)
2781                 return g_list_append (NULL, first_selection);
2782
2783         /*clean up the selections;
2784          */
2785         ev_page_cache_get_size (view->page_cache, first_selection->page,
2786                                 1.0, &width, &height);
2787         first_selection->rect.x2 = width;
2788         first_selection->rect.y2 = height;
2789         list = g_list_append (NULL, first_selection);
2790
2791         /* Add all the intervening pages */
2792         for (i = first_selection->page + 1; i < last_selection->page; i++) {
2793                 EvViewSelection *selection;
2794
2795                 selection = g_new0 (EvViewSelection, 1);
2796                 selection->page = i;
2797                 ev_page_cache_get_size (view->page_cache, i,
2798                                         1.0, &width, &height);
2799                 selection->rect.x1 = selection->rect.y1 = 0;
2800                 selection->rect.x2 = width;
2801                 selection->rect.y2 = height;
2802                 g_list_append (list, selection);
2803         }
2804
2805         /* Clean up the last page */
2806         last_selection->rect.x1 = 0;
2807         last_selection->rect.y1 = 0;
2808         list = g_list_append (list, last_selection);
2809
2810         return list;
2811 }
2812
2813 /* This function takes the newly calculated list, and figures out which regions
2814  * have changed.  It then queues a redraw approporiately.
2815  */
2816 static void
2817 merge_selection_region (EvView *view,
2818                         GList  *list)
2819 {
2820
2821         /* FIXME: actually write... */
2822         clear_selection (view);
2823         gtk_widget_queue_draw (GTK_WIDGET (view));
2824
2825         view->selection_info.selections = list;
2826         ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, list);
2827 }
2828
2829 static void
2830 compute_selections (EvView       *view,
2831                     GdkRectangle *view_rect)
2832 {
2833         GList *list;
2834
2835         if (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE)
2836                 list = compute_new_selection_rect (view, view_rect);
2837         else
2838                 list = compute_new_selection_text (view, view_rect);
2839         merge_selection_region (view, list);
2840 }
2841
2842 /* Free's the selection.  It's up to the caller to queue redraws if needed.
2843  */
2844 static void
2845 selection_free (EvViewSelection *selection)
2846 {
2847         g_free (selection);
2848 }
2849
2850 static void
2851 clear_selection (EvView *view)
2852 {
2853         g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL);
2854         view->selection_info.selections = NULL;
2855         view->selection_info.in_selection = FALSE;
2856 }
2857
2858
2859 void
2860 ev_view_select_all (EvView *view)
2861 {
2862         int n_pages, i;
2863
2864         clear_selection (view);
2865
2866         n_pages = ev_page_cache_get_n_pages (view->page_cache);
2867         for (i = 0; i < n_pages; i++) {
2868                 int width, height;
2869                 EvViewSelection *selection;
2870
2871                 ev_page_cache_get_size (view->page_cache,
2872                                         i, 1.0, &width, &height);
2873
2874                 selection = g_new0 (EvViewSelection, 1);
2875                 selection->page = i;
2876                 selection->rect.x1 = selection->rect.y1 = 0;
2877                 selection->rect.x2 = width;
2878                 selection->rect.y2 = height;
2879
2880                 view->selection_info.selections = g_list_append (view->selection_info.selections, selection);
2881         }
2882
2883         ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, view->selection_info.selections);
2884         gtk_widget_queue_draw (GTK_WIDGET (view));
2885 }
2886
2887 static char *
2888 get_selected_text (EvView *ev_view)
2889 {
2890         GString *text;
2891         GList *l;
2892
2893         text = g_string_new (NULL);
2894
2895         ev_document_doc_mutex_lock ();
2896
2897         for (l = ev_view->selection_info.selections; l != NULL; l = l->next) {
2898                 EvViewSelection *selection = (EvViewSelection *)l->data;
2899                 char *tmp;
2900
2901                 tmp = ev_document_get_text (ev_view->document,
2902                                             selection->page,
2903                                             &selection->rect);
2904                 g_string_append (text, tmp);
2905                 g_free (tmp);
2906         }
2907
2908         ev_document_doc_mutex_unlock ();
2909
2910         return g_string_free (text, FALSE);
2911 }
2912
2913 void
2914 ev_view_copy (EvView *ev_view)
2915 {
2916         GtkClipboard *clipboard;
2917         char *text;
2918
2919         if (!ev_document_can_get_text (ev_view->document)) {
2920                 return;
2921         }
2922
2923         text = get_selected_text (ev_view);
2924         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
2925                                               GDK_SELECTION_CLIPBOARD);
2926         gtk_clipboard_set_text (clipboard, text, -1);
2927         g_free (text);
2928 }
2929
2930 static void
2931 ev_view_primary_get_cb (GtkClipboard     *clipboard,
2932                         GtkSelectionData *selection_data,
2933                         guint             info,
2934                         gpointer          data)
2935 {
2936         EvView *ev_view = EV_VIEW (data);
2937         char *text;
2938
2939         if (!ev_document_can_get_text (ev_view->document)) {
2940                 return;
2941         }
2942
2943         text = get_selected_text (ev_view);
2944         gtk_selection_data_set_text (selection_data, text, -1);
2945         g_free (text);
2946 }
2947
2948 static void
2949 ev_view_primary_clear_cb (GtkClipboard *clipboard,
2950                           gpointer      data)
2951 {
2952         EvView *view = EV_VIEW (data);
2953
2954         clear_selection (view);
2955 }
2956
2957 static void
2958 ev_view_update_primary_selection (EvView *ev_view)
2959 {
2960         GtkClipboard *clipboard;
2961
2962         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
2963                                               GDK_SELECTION_PRIMARY);
2964
2965         if (ev_view->selection_info.selections) {
2966                 if (!gtk_clipboard_set_with_owner (clipboard,
2967                                                    targets,
2968                                                    G_N_ELEMENTS (targets),
2969                                                    ev_view_primary_get_cb,
2970                                                    ev_view_primary_clear_cb,
2971                                                    G_OBJECT (ev_view)))
2972                         ev_view_primary_clear_cb (clipboard, ev_view);
2973         } else {
2974                 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
2975                         gtk_clipboard_clear (clipboard);
2976         }
2977 }
2978
2979 /*** Cursor operations ***/
2980
2981 static GdkCursor *
2982 ev_view_create_invisible_cursor(void)
2983 {
2984        GdkBitmap *empty;
2985        GdkColor black = { 0, 0, 0, 0 };
2986        static char bits[] = { 0x00 };
2987
2988        empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
2989
2990        return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
2991 }
2992
2993 static void
2994 ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
2995 {
2996         GdkCursor *cursor = NULL;
2997         GdkDisplay *display;
2998         GtkWidget *widget;
2999
3000         if (view->cursor == new_cursor) {
3001                 return;
3002         }
3003
3004         widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
3005         display = gtk_widget_get_display (widget);
3006         view->cursor = new_cursor;
3007
3008         switch (new_cursor) {
3009                 case EV_VIEW_CURSOR_NORMAL:
3010                         gdk_window_set_cursor (widget->window, NULL);
3011                         break;
3012                 case EV_VIEW_CURSOR_LINK:
3013                         cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
3014                         break;
3015                 case EV_VIEW_CURSOR_WAIT:
3016                         cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
3017                         break;
3018                 case EV_VIEW_CURSOR_HIDDEN:
3019                         cursor = ev_view_create_invisible_cursor ();
3020                         break;
3021                 case EV_VIEW_CURSOR_DRAG:
3022                         cursor = gdk_cursor_new_for_display (display, GDK_FLEUR);
3023                         break;
3024         }
3025
3026         if (cursor) {
3027                 gdk_window_set_cursor (widget->window, cursor);
3028                 gdk_cursor_unref (cursor);
3029                 gdk_flush();
3030         }
3031 }
3032
3033 void
3034 ev_view_hide_cursor (EvView *view)
3035 {
3036        ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN);
3037 }
3038
3039 void
3040 ev_view_show_cursor (EvView *view)
3041 {
3042        ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
3043 }
3044
3045 /*** Enum description for usage in signal ***/
3046
3047 GType
3048 ev_sizing_mode_get_type (void)
3049 {
3050   static GType etype = 0;
3051   if (etype == 0) {
3052     static const GEnumValue values[] = {
3053       { EV_SIZING_FIT_WIDTH, "EV_SIZING_FIT_WIDTH", "fit-width" },
3054       { EV_SIZING_BEST_FIT, "EV_SIZING_BEST_FIT", "best-fit" },
3055       { EV_SIZING_FREE, "EV_SIZING_FREE", "free" },
3056       { 0, NULL, NULL }
3057     };
3058     etype = g_enum_register_static ("EvSizingMode", values);
3059   }
3060   return etype;
3061 }
3062