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