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