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