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