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