]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
b8723ccc8bc0a721500514d1191c8ae9650b10d8
[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 - view->hadjustment->value;
369                 view->scroll_x = view->hadjustment->value;
370         } else {
371                 view->scroll_x = 0;
372         }
373
374         if (view->vadjustment) {
375                 dy = view->scroll_y - view->vadjustment->value;
376                 view->scroll_y = 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, 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, 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         if (!gdk_rectangle_intersect (page_area, expose_area, &overlap))
1609                 return;
1610
1611         ev_page_cache_get_size (view->page_cache,
1612                                 page, view->scale,
1613                                 &width, &height);
1614
1615         /* Render the document itself */
1616         real_page_area = *page_area;
1617
1618         real_page_area.x += border->left;
1619         real_page_area.y += border->top;
1620         real_page_area.width -= (border->left + border->right);
1621         real_page_area.height -= (border->top + border->bottom);
1622
1623         if (! gdk_rectangle_intersect (&real_page_area, expose_area, &overlap))
1624                 return;
1625
1626         ev_document_misc_paint_one_page (GTK_WIDGET(view)->window,
1627                                          GTK_WIDGET (view),
1628                                          page_area, border);
1629
1630         current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, page);
1631
1632         if (current_pixbuf == NULL)
1633                 scaled_image = NULL;
1634         else if (width == gdk_pixbuf_get_width (current_pixbuf) &&
1635                  height == gdk_pixbuf_get_height (current_pixbuf))
1636                 scaled_image = g_object_ref (current_pixbuf);
1637         else
1638                 scaled_image = gdk_pixbuf_scale_simple (current_pixbuf,
1639                                                         width, height,
1640                                                         GDK_INTERP_NEAREST);
1641
1642         if (scaled_image) {
1643                 gdk_draw_pixbuf (GTK_WIDGET(view)->window,
1644                                  GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
1645                                  scaled_image,
1646                                  overlap.x - real_page_area.x,
1647                                  overlap.y - real_page_area.y,
1648                                  overlap.x, overlap.y,
1649                                  overlap.width, overlap.height,
1650                                  GDK_RGB_DITHER_NORMAL,
1651                                  0, 0);
1652                 g_object_unref (scaled_image);
1653         }
1654 }
1655
1656 /*** GObject functions ***/
1657
1658 static void
1659 ev_view_finalize (GObject *object)
1660 {
1661         EvView *view = EV_VIEW (object);
1662
1663         LOG ("Finalize");
1664
1665         g_free (view->status);
1666         g_free (view->find_status);
1667
1668         clear_selection (view);
1669
1670         G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
1671 }
1672
1673 static void
1674 ev_view_destroy (GtkObject *object)
1675 {
1676         EvView *view = EV_VIEW (object);
1677
1678         if (view->document) {
1679                 g_object_unref (view->document);
1680                 view->document = NULL;
1681         }
1682         if (view->pixbuf_cache) {
1683                 g_object_unref (view->pixbuf_cache);
1684                 view->pixbuf_cache = NULL;
1685         }
1686         ev_view_set_scroll_adjustments (view, NULL, NULL);
1687
1688         GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
1689 }
1690
1691 static void
1692 ev_view_set_property (GObject      *object,
1693                       guint         prop_id,
1694                       const GValue *value,
1695                       GParamSpec   *pspec)
1696 {
1697         EvView *view = EV_VIEW (object);
1698
1699         switch (prop_id)
1700         {
1701         case PROP_CONTINUOUS:
1702                 ev_view_set_continuous (view, g_value_get_boolean (value));
1703                 break;
1704         case PROP_DUAL_PAGE:
1705                 ev_view_set_dual_page (view, g_value_get_boolean (value));
1706                 break;
1707         case PROP_FULLSCREEN:
1708                 ev_view_set_fullscreen (view, g_value_get_boolean (value));
1709                 break;
1710         case PROP_PRESENTATION:
1711                 ev_view_set_presentation (view, g_value_get_boolean (value));
1712                 break;
1713         case PROP_SIZING_MODE:
1714                 ev_view_set_sizing_mode (view, g_value_get_enum (value));
1715                 break;
1716         default:
1717                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1718         }
1719 }
1720
1721 static void
1722 ev_view_get_property (GObject *object,
1723                       guint prop_id,
1724                       GValue *value,
1725                       GParamSpec *pspec)
1726 {
1727         EvView *view = EV_VIEW (object);
1728
1729         switch (prop_id)
1730         {
1731         case PROP_STATUS:
1732                 g_value_set_string (value, view->status);
1733                 break;
1734         case PROP_FIND_STATUS:
1735                 g_value_set_string (value, view->status);
1736                 break;
1737         case PROP_CONTINUOUS:
1738                 g_value_set_boolean (value, view->continuous);
1739                 break;
1740         case PROP_DUAL_PAGE:
1741                 g_value_set_boolean (value, view->dual_page);
1742                 break;
1743         case PROP_FULLSCREEN:
1744                 g_value_set_boolean (value, view->fullscreen);
1745                 break;
1746         case PROP_PRESENTATION:
1747                 g_value_set_boolean (value, view->presentation);
1748                 break;
1749         case PROP_SIZING_MODE:
1750                 g_value_set_enum (value, view->sizing_mode);
1751                 break;
1752         default:
1753                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1754         }
1755 }
1756
1757 static void
1758 ev_view_class_init (EvViewClass *class)
1759 {
1760         GObjectClass *object_class = G_OBJECT_CLASS (class);
1761         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
1762         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1763         GtkBindingSet *binding_set;
1764
1765         object_class->finalize = ev_view_finalize;
1766         object_class->set_property = ev_view_set_property;
1767         object_class->get_property = ev_view_get_property;
1768
1769         widget_class->expose_event = ev_view_expose_event;
1770         widget_class->button_press_event = ev_view_button_press_event;
1771         widget_class->motion_notify_event = ev_view_motion_notify_event;
1772         widget_class->button_release_event = ev_view_button_release_event;
1773         widget_class->size_request = ev_view_size_request;
1774         widget_class->size_allocate = ev_view_size_allocate;
1775         widget_class->realize = ev_view_realize;
1776         widget_class->unrealize = ev_view_unrealize;
1777         widget_class->scroll_event = ev_view_scroll_event;
1778         gtk_object_class->destroy = ev_view_destroy;
1779
1780         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
1781         class->scroll_view = ev_view_scroll_view;
1782
1783         widget_class->set_scroll_adjustments_signal =  
1784             g_signal_new ("set-scroll-adjustments",
1785                           G_OBJECT_CLASS_TYPE (object_class),
1786                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1787                           G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
1788                           NULL, NULL,
1789                           ev_marshal_VOID__OBJECT_OBJECT,
1790                           G_TYPE_NONE, 2,
1791                           GTK_TYPE_ADJUSTMENT,
1792                           GTK_TYPE_ADJUSTMENT);
1793
1794         signals[SIGNAL_SCROLL_VIEW] = g_signal_new ("scroll-view",
1795                          G_TYPE_FROM_CLASS (object_class),
1796                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1797                          G_STRUCT_OFFSET (EvViewClass, scroll_view),
1798                          NULL, NULL,
1799                          ev_marshal_VOID__ENUM_BOOLEAN,
1800                          G_TYPE_NONE, 2,
1801                          GTK_TYPE_SCROLL_TYPE,
1802                          G_TYPE_BOOLEAN);
1803
1804         signals[SIGNAL_ZOOM_INVALID] = g_signal_new ("zoom-invalid",
1805                          G_TYPE_FROM_CLASS (object_class),
1806                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1807                          G_STRUCT_OFFSET (EvViewClass, zoom_invalid),
1808                          NULL, NULL,
1809                          ev_marshal_VOID__VOID,
1810                          G_TYPE_NONE, 0, G_TYPE_NONE);
1811
1812         g_object_class_install_property (object_class,
1813                                          PROP_STATUS,
1814                                          g_param_spec_string ("status",
1815                                                               "Status Message",
1816                                                               "The status message",
1817                                                               NULL,
1818                                                               G_PARAM_READABLE));
1819
1820         g_object_class_install_property (object_class,
1821                                          PROP_FIND_STATUS,
1822                                          g_param_spec_string ("find-status",
1823                                                               "Find Status Message",
1824                                                               "The find status message",
1825                                                               NULL,
1826                                                               G_PARAM_READABLE));
1827
1828         g_object_class_install_property (object_class,
1829                                          PROP_CONTINUOUS,
1830                                          g_param_spec_boolean ("continuous",
1831                                                                "Continuous",
1832                                                                "Continuous scrolling mode",
1833                                                                TRUE,
1834                                                                G_PARAM_READWRITE));
1835
1836         g_object_class_install_property (object_class,
1837                                          PROP_DUAL_PAGE,
1838                                          g_param_spec_boolean ("dual-page",
1839                                                                "Dual Page",
1840                                                                "Two pages visible at once",
1841                                                                FALSE,
1842                                                                G_PARAM_READWRITE));
1843         g_object_class_install_property (object_class,
1844                                          PROP_FULLSCREEN,
1845                                          g_param_spec_boolean ("fullscreen",
1846                                                                "Full Screen",
1847                                                                "Draw page in a fullscreen fashion",
1848                                                                FALSE,
1849                                                                G_PARAM_READWRITE));
1850         g_object_class_install_property (object_class,
1851                                          PROP_PRESENTATION,
1852                                          g_param_spec_boolean ("presentation",
1853                                                                "Presentation",
1854                                                                "Draw page in presentation mode",
1855                                                                TRUE,
1856                                                                G_PARAM_READWRITE));
1857
1858         g_object_class_install_property (object_class,
1859                                          PROP_SIZING_MODE,
1860                                          g_param_spec_enum ("sizing-mode",
1861                                                             "Sizing Mode",
1862                                                             "Sizing Mode",
1863                                                             EV_TYPE_SIZING_MODE,
1864                                                             EV_SIZING_FIT_WIDTH,
1865                                                             G_PARAM_READWRITE));
1866
1867         binding_set = gtk_binding_set_by_class (class);
1868
1869         add_scroll_binding_keypad (binding_set, GDK_Left,  GTK_SCROLL_STEP_BACKWARD, TRUE);
1870         add_scroll_binding_keypad (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD,  TRUE);
1871         add_scroll_binding_keypad (binding_set, GDK_Up,    GTK_SCROLL_STEP_BACKWARD, FALSE);
1872         add_scroll_binding_keypad (binding_set, GDK_Down,  GTK_SCROLL_STEP_FORWARD,  FALSE);
1873 }
1874
1875 static void
1876 ev_view_init (EvView *view)
1877 {
1878         GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
1879
1880         view->spacing = 10;
1881         view->scale = 1.0;
1882         view->current_page = 0;
1883         view->pressed_button = -1;
1884         view->cursor = EV_VIEW_CURSOR_NORMAL;
1885         view->drag_info.dragging = FALSE;
1886
1887         view->continuous = TRUE;
1888         view->dual_page = FALSE;
1889         view->presentation = FALSE;
1890         view->fullscreen = FALSE;
1891         view->sizing_mode = EV_SIZING_FIT_WIDTH;
1892         view->pending_scroll = SCROLL_TO_KEEP_POSITION;
1893 }
1894
1895 /*** Callbacks ***/
1896
1897 static void
1898 find_changed_cb (EvDocument *document, int page, EvView *view)
1899 {
1900         jump_to_find_page (view);
1901         jump_to_find_result (view);
1902         update_find_status_message (view);
1903
1904         if (view->current_page == page)
1905                 gtk_widget_queue_draw (GTK_WIDGET (view));
1906 }
1907
1908 static void
1909 job_finished_cb (EvPixbufCache *pixbuf_cache,
1910                  EvView        *view)
1911 {
1912         gtk_widget_queue_draw (GTK_WIDGET (view));
1913 }
1914
1915 static void
1916 page_changed_cb (EvPageCache *page_cache,
1917                  int          new_page,
1918                  EvView      *view)
1919 {
1920         if (view->current_page != new_page) {
1921
1922                 if (view->pending_scroll != SCROLL_TO_CURRENT_PAGE) {
1923                         /* Should scroll right now */
1924                         view_scroll_to_page (view, new_page);
1925                 } else {        
1926                         /* We'll scroll to new page on allocate */
1927                         view->current_page = new_page;
1928                 }
1929
1930                 if (EV_IS_DOCUMENT_FIND (view->document)) {
1931                         view->find_page = new_page;
1932                         view->find_result = 0;
1933                         update_find_status_message (view);
1934                 }
1935         }
1936 }
1937
1938 static void on_adjustment_value_changed (GtkAdjustment  *adjustment,
1939                                          EvView *view)
1940 {
1941         view_update_adjustments (view);
1942 }
1943
1944 GtkWidget*
1945 ev_view_new (void)
1946 {
1947         GtkWidget *view;
1948
1949         view = g_object_new (EV_TYPE_VIEW, NULL);
1950
1951         return view;
1952 }
1953
1954 void
1955 ev_view_set_document (EvView     *view,
1956                       EvDocument *document)
1957 {
1958         g_return_if_fail (EV_IS_VIEW (view));
1959
1960         if (document != view->document) {
1961                 if (view->document) {
1962                         g_signal_handlers_disconnect_by_func (view->document,
1963                                                               find_changed_cb,
1964                                                               view);
1965                         g_object_unref (view->document);
1966                         view->page_cache = NULL;
1967
1968                 }
1969
1970                 view->document = document;
1971                 view->find_page = 0;
1972                 view->find_result = 0;
1973
1974                 if (view->document) {
1975                         g_object_ref (view->document);
1976                         if (EV_IS_DOCUMENT_FIND (view->document)) {
1977                                 g_signal_connect (view->document,
1978                                                   "find_changed",
1979                                                   G_CALLBACK (find_changed_cb),
1980                                                   view);
1981                         }
1982                         view->page_cache = ev_document_get_page_cache (view->document);
1983                         view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
1984                         g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
1985                         view->pixbuf_cache = ev_pixbuf_cache_new (view->document);
1986                         g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
1987                 }
1988
1989                 gtk_widget_queue_resize (GTK_WIDGET (view));
1990         }
1991 }
1992
1993 /*** Zoom and sizing mode ***/
1994
1995 #define EPSILON 0.0000001
1996 void
1997 ev_view_set_zoom (EvView   *view,
1998                   double    factor,
1999                   gboolean  relative)
2000 {
2001         double scale;
2002
2003         if (relative)
2004                 scale = view->scale * factor;
2005         else
2006                 scale = factor;
2007
2008         scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
2009
2010         if (ABS (view->scale - scale) < EPSILON)
2011                 return;
2012         view->scale = scale;
2013         view->pending_resize = TRUE;
2014
2015         gtk_widget_queue_resize (GTK_WIDGET (view));
2016 }
2017
2018 double
2019 ev_view_get_zoom (EvView *view)
2020 {
2021         return view->scale;
2022 }
2023
2024 void
2025 ev_view_set_continuous (EvView   *view,
2026                         gboolean  continuous)
2027 {
2028         g_return_if_fail (EV_IS_VIEW (view));
2029
2030         continuous = continuous != FALSE;
2031
2032         if (view->continuous != continuous) {
2033                 view->continuous = continuous;
2034                 view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2035                 gtk_widget_queue_resize (GTK_WIDGET (view));
2036         }
2037
2038         g_object_notify (G_OBJECT (view), "continuous");
2039 }
2040
2041 void
2042 ev_view_set_dual_page (EvView   *view,
2043                        gboolean  dual_page)
2044 {
2045         g_return_if_fail (EV_IS_VIEW (view));
2046
2047         dual_page = dual_page != FALSE;
2048
2049         if (view->dual_page == dual_page)
2050                 return;
2051     
2052         view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2053         view->dual_page = dual_page;
2054         /* FIXME: if we're keeping the pixbuf cache around, we should extend the
2055          * preload_cache_size to be 2 if dual_page is set.
2056          */
2057         gtk_widget_queue_resize (GTK_WIDGET (view));
2058
2059         g_object_notify (G_OBJECT (view), "dual-page");
2060 }
2061
2062 void
2063 ev_view_set_fullscreen (EvView   *view,
2064                          gboolean  fullscreen)
2065 {
2066         g_return_if_fail (EV_IS_VIEW (view));
2067
2068         fullscreen = fullscreen != FALSE;
2069
2070         if (view->fullscreen != fullscreen) {
2071                 view->fullscreen = fullscreen;
2072                 gtk_widget_queue_resize (GTK_WIDGET (view));
2073         }
2074
2075         g_object_notify (G_OBJECT (view), "fullscreen");
2076 }
2077
2078 gboolean
2079 ev_view_get_fullscreen (EvView *view)
2080 {
2081         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2082
2083         return view->fullscreen;
2084 }
2085
2086 void
2087 ev_view_set_presentation (EvView   *view,
2088                           gboolean  presentation)
2089 {
2090         g_return_if_fail (EV_IS_VIEW (view));
2091
2092         presentation = presentation != FALSE;
2093
2094         if (view->presentation == presentation)
2095                 return;
2096
2097         view->presentation = presentation;
2098         gtk_widget_queue_resize (GTK_WIDGET (view));
2099         if (GTK_WIDGET_REALIZED (view)) {
2100                 if (view->presentation)
2101                         gdk_window_set_background (GTK_WIDGET(view)->window,
2102                                                    &GTK_WIDGET (view)->style->black);
2103                 else
2104                         gdk_window_set_background (GTK_WIDGET(view)->window,
2105                                                    &GTK_WIDGET (view)->style->mid [GTK_STATE_NORMAL]);
2106         }
2107
2108
2109         g_object_notify (G_OBJECT (view), "presentation");
2110 }
2111
2112 gboolean
2113 ev_view_get_presentation (EvView *view)
2114 {
2115         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2116
2117         return view->presentation;
2118 }
2119
2120 void
2121 ev_view_set_sizing_mode (EvView       *view,
2122                          EvSizingMode  sizing_mode)
2123 {
2124         g_return_if_fail (EV_IS_VIEW (view));
2125
2126         if (view->sizing_mode == sizing_mode)
2127                 return;
2128
2129         view->sizing_mode = sizing_mode;
2130         gtk_widget_queue_resize (GTK_WIDGET (view));
2131         
2132         g_object_notify (G_OBJECT (view), "sizing-mode");
2133 }
2134
2135 EvSizingMode
2136 ev_view_get_sizing_mode (EvView *view)
2137 {
2138         g_return_val_if_fail (EV_IS_VIEW (view), EV_SIZING_FREE);
2139
2140         return view->sizing_mode;
2141 }
2142
2143 gboolean
2144 ev_view_can_zoom_in (EvView *view)
2145 {
2146         return view->scale * ZOOM_IN_FACTOR <= MAX_SCALE;
2147 }
2148
2149 gboolean
2150 ev_view_can_zoom_out (EvView *view)
2151 {
2152         return view->scale * ZOOM_OUT_FACTOR >= MIN_SCALE;
2153 }
2154
2155 void
2156 ev_view_zoom_in (EvView *view)
2157 {
2158         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2159         
2160         view->pending_scroll = SCROLL_TO_CENTER;
2161         ev_view_set_zoom (view, ZOOM_IN_FACTOR, TRUE);
2162 }
2163
2164 void
2165 ev_view_zoom_out (EvView *view)
2166 {
2167         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2168
2169         view->pending_scroll = SCROLL_TO_CENTER;
2170         ev_view_set_zoom (view, ZOOM_OUT_FACTOR, TRUE);
2171 }
2172
2173 static double
2174 zoom_for_size_fit_width (int doc_width,
2175                          int doc_height,
2176                          int target_width,
2177                          int target_height,
2178                          int vsb_width)
2179 {
2180         double scale;
2181
2182         scale = (double)target_width / doc_width;
2183
2184         if (doc_height * scale > target_height)
2185                 scale = (double) (target_width - vsb_width) / doc_width;
2186
2187         return scale;
2188 }
2189
2190 static double
2191 zoom_for_size_best_fit (int doc_width,
2192                         int doc_height,
2193                         int target_width,
2194                         int target_height,
2195                         int vsb_width,
2196                         int hsb_width)
2197 {
2198         double w_scale;
2199         double h_scale;
2200
2201         w_scale = (double)target_width / doc_width;
2202         h_scale = (double)target_height / doc_height;
2203
2204         if (doc_height * w_scale > target_height)
2205                 w_scale = (double) (target_width - vsb_width) / doc_width;
2206         if (doc_width * h_scale > target_width)
2207                 h_scale = (double) (target_height - hsb_width) / doc_height;
2208
2209         return MIN (w_scale, h_scale);
2210 }
2211
2212
2213 static void
2214 ev_view_zoom_for_size_presentation (EvView *view,
2215                                     int     width,
2216                                     int     height)
2217 {
2218         int doc_width, doc_height;
2219         gdouble scale;
2220
2221         ev_page_cache_get_size (view->page_cache,
2222                                 view->current_page,
2223                                 1.0,
2224                                 &doc_width,
2225                                 &doc_height);
2226         scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, 0, 0);
2227         ev_view_set_zoom (view, scale, FALSE);
2228 }
2229
2230 static void
2231 ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
2232                            int     width,
2233                            int     height,
2234                            int     vsb_width,
2235                            int     hsb_height)
2236 {
2237         int doc_width, doc_height;
2238         GtkBorder border;
2239         gdouble scale;
2240
2241         ev_page_cache_get_max_width_size (view->page_cache,
2242                                           1.0,
2243                                           &doc_width, NULL);
2244         ev_page_cache_get_max_height_size (view->page_cache,
2245                                            1.0,
2246                                            NULL, &doc_height);
2247         compute_border (view, doc_width, doc_height, &border);
2248
2249         doc_width = doc_width * 2;
2250         width -= (2 * (border.left + border.right) + 3 * view->spacing);
2251         height -= (border.top + border.bottom + 2 * view->spacing - 1);
2252
2253         /* FIXME: We really need to calculate the overall height here, not the
2254          * page height.  We assume there's always a vertical scrollbar for
2255          * now.  We need to fix this. */
2256         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2257                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2258         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2259                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2260         else
2261                 g_assert_not_reached ();
2262
2263         ev_view_set_zoom (view, scale, FALSE);
2264 }
2265
2266 static void
2267 ev_view_zoom_for_size_continuous (EvView *view,
2268                                   int     width,
2269                                   int     height,
2270                                   int     vsb_width,
2271                                   int     hsb_height)
2272 {
2273         int doc_width, doc_height;
2274         GtkBorder border;
2275         gdouble scale;
2276
2277         ev_page_cache_get_max_width_size (view->page_cache,
2278                                           1.0,
2279                                           &doc_width, NULL);
2280         ev_page_cache_get_max_height_size (view->page_cache,
2281                                            1.0,
2282                                            NULL, &doc_height);
2283         compute_border (view, doc_width, doc_height, &border);
2284
2285         width -= (border.left + border.right + 2 * view->spacing);
2286         height -= (border.top + border.bottom + 2 * view->spacing - 1);
2287
2288         /* FIXME: We really need to calculate the overall height here, not the
2289          * page height.  We assume there's always a vertical scrollbar for
2290          * now.  We need to fix this. */
2291         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2292                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2293         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2294                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2295         else
2296                 g_assert_not_reached ();
2297
2298         ev_view_set_zoom (view, scale, FALSE);
2299 }
2300
2301 static void
2302 ev_view_zoom_for_size_dual_page (EvView *view,
2303                                  int     width,
2304                                  int     height,
2305                                  int     vsb_width,
2306                                  int     hsb_height)
2307 {
2308         GtkBorder border;
2309         gint doc_width, doc_height;
2310         gdouble scale;
2311         gint other_page;
2312
2313         other_page = view->current_page ^ 1;
2314
2315         /* Find the largest of the two. */
2316         ev_page_cache_get_size (view->page_cache,
2317                                 view->current_page,
2318                                 1.0,
2319                                 &doc_width, &doc_height);
2320
2321         if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
2322                 gint width_2, height_2;
2323                 ev_page_cache_get_size (view->page_cache,
2324                                         other_page,
2325                                         1.0,
2326                                         &width_2, &height_2);
2327                 if (width_2 > doc_width)
2328                         doc_width = width_2;
2329                 if (height_2 > doc_height)
2330                         doc_height = height_2;
2331         }
2332         compute_border (view, doc_width, doc_height, &border);
2333
2334         doc_width = doc_width * 2;
2335         width -= ((border.left + border.right)* 2 + 3 * view->spacing);
2336         height -= (border.top + border.bottom + 2 * view->spacing);
2337
2338         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2339                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2340         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2341                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2342         else
2343                 g_assert_not_reached ();
2344
2345         ev_view_set_zoom (view, scale, FALSE);
2346 }
2347
2348 static void
2349 ev_view_zoom_for_size_single_page (EvView *view,
2350                                    int     width,
2351                                    int     height,
2352                                    int     vsb_width,
2353                                    int     hsb_height)
2354 {
2355         int doc_width, doc_height;
2356         GtkBorder border;
2357         gdouble scale;
2358
2359         ev_page_cache_get_size (view->page_cache,
2360                                 view->current_page,
2361                                 1.0,
2362                                 &doc_width,
2363                                 &doc_height);
2364         /* Get an approximate border */
2365         compute_border (view, width, height, &border);
2366
2367         width -= (border.left + border.right + 2 * view->spacing);
2368         height -= (border.top + border.bottom + 2 * view->spacing);
2369
2370         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2371                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2372         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2373                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2374         else
2375                 g_assert_not_reached ();
2376
2377         ev_view_set_zoom (view, scale, FALSE);
2378 }
2379
2380 void
2381 ev_view_set_zoom_for_size (EvView *view,
2382                            int     width,
2383                            int     height,
2384                            int     vsb_width,
2385                            int     hsb_height)
2386 {       
2387         g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
2388                           view->sizing_mode == EV_SIZING_BEST_FIT);
2389         g_return_if_fail (width >= 0);
2390         g_return_if_fail (height >= 0);
2391
2392         if (view->document == NULL)
2393                 return;
2394
2395         if (view->presentation)
2396                 ev_view_zoom_for_size_presentation (view, width, height);
2397         else if (view->continuous && view->dual_page)
2398                 ev_view_zoom_for_size_continuous_and_dual_page (view, width, height, vsb_width, hsb_height);
2399         else if (view->continuous)
2400                 ev_view_zoom_for_size_continuous (view, width, height, vsb_width, hsb_height);
2401         else if (view->dual_page)
2402                 ev_view_zoom_for_size_dual_page (view, width, height, vsb_width, hsb_height);
2403         else
2404                 ev_view_zoom_for_size_single_page (view, width, height, vsb_width, hsb_height);
2405 }
2406
2407 /*** Status text messages ***/
2408
2409 const char *
2410 ev_view_get_status (EvView *view)
2411 {
2412         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2413
2414         return view->status;
2415 }
2416
2417 static void
2418 ev_view_set_status (EvView *view, const char *message)
2419 {
2420         g_return_if_fail (EV_IS_VIEW (view));
2421
2422         if (message != view->status) {
2423                 g_free (view->status);
2424                 view->status = g_strdup (message);
2425                 g_object_notify (G_OBJECT (view), "status");
2426         }
2427 }
2428
2429 static void
2430 update_find_status_message (EvView *view)
2431 {
2432         char *message;
2433
2434         if (view->current_page == view->find_page) {
2435                 int results;
2436
2437                 results = ev_document_find_get_n_results
2438                                 (EV_DOCUMENT_FIND (view->document),
2439                                  view->current_page);
2440                 /* TRANS: Sometimes this could be better translated as
2441                    "%d hit(s) on this page".  Therefore this string
2442                    contains plural cases. */
2443                 message = g_strdup_printf (ngettext ("%d found on this page",
2444                                                      "%d found on this page",
2445                                                      results),
2446                                            results);
2447         } else {
2448                 double percent;
2449
2450                 ev_document_doc_mutex_lock ();
2451                 percent = ev_document_find_get_progress
2452                                 (EV_DOCUMENT_FIND (view->document));
2453                 ev_document_doc_mutex_unlock ();
2454                 if (percent >= (1.0 - 1e-10)) {
2455                         message = g_strdup (_("Not found"));
2456                 } else {
2457                         message = g_strdup_printf (_("%3d%% remaining to search"),
2458                                                    (int) ((1.0 - percent) * 100));
2459                 }
2460
2461         }
2462         ev_view_set_find_status (view, message);
2463 }
2464
2465 const char *
2466 ev_view_get_find_status (EvView *view)
2467 {
2468         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2469
2470         return view->find_status;
2471 }
2472
2473 static void
2474 ev_view_set_find_status (EvView *view, const char *message)
2475 {
2476         g_return_if_fail (EV_IS_VIEW (view));
2477
2478         g_free (view->find_status);
2479         view->find_status = g_strdup (message);
2480         g_object_notify (G_OBJECT (view), "find-status");
2481 }
2482
2483 /*** Find ***/
2484
2485 static void
2486 jump_to_find_result (EvView *view)
2487 {
2488         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2489         EvRectangle rect;
2490         GdkRectangle view_rect;
2491         int n_results;
2492         int page = view->find_page;
2493
2494         ev_document_doc_mutex_lock ();
2495         n_results = ev_document_find_get_n_results (find, page);
2496         ev_document_doc_mutex_unlock ();
2497
2498         if (n_results > view->find_result) {
2499                 ev_document_doc_mutex_lock ();
2500                 ev_document_find_get_result
2501                         (find, page, view->find_result, &rect);
2502                 ev_document_doc_mutex_unlock ();
2503
2504                 doc_rect_to_view_rect (view, page, &rect, &view_rect);
2505                 ensure_rectangle_is_visible (view, &view_rect);
2506         }
2507 }
2508
2509 static void
2510 jump_to_find_page (EvView *view)
2511 {
2512         int n_pages, i;
2513
2514         n_pages = ev_page_cache_get_n_pages (view->page_cache);
2515
2516         for (i = 0; i < n_pages; i++) {
2517                 int has_results;
2518                 int page;
2519
2520                 page = i + view->find_page;
2521                 if (page >= n_pages) {
2522                         page = page - n_pages;
2523                 }
2524
2525                 has_results = ev_document_find_page_has_results
2526                                 (EV_DOCUMENT_FIND (view->document), page);
2527                 if (has_results == -1) {
2528                         view->find_page = page;
2529                         break;
2530                 } else if (has_results == 1) {
2531                         ev_page_cache_set_current_page (view->page_cache, page);
2532                         jump_to_find_result (view);
2533                         break;
2534                 }
2535         }
2536 }
2537
2538 gboolean
2539 ev_view_can_find_next (EvView *view)
2540 {
2541         int n_results = 0;
2542
2543         if (EV_IS_DOCUMENT_FIND (view->document)) {
2544                 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2545
2546                 ev_document_doc_mutex_lock ();
2547                 n_results = ev_document_find_get_n_results (find, view->current_page);
2548                 ev_document_doc_mutex_unlock ();
2549         }
2550
2551         return n_results > 0;
2552 }
2553
2554 void
2555 ev_view_find_next (EvView *view)
2556 {
2557         EvPageCache *page_cache;
2558         int n_results, n_pages;
2559         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2560
2561         page_cache = ev_document_get_page_cache (view->document);
2562         ev_document_doc_mutex_lock ();
2563         n_results = ev_document_find_get_n_results (find, view->current_page);
2564         ev_document_doc_mutex_unlock ();
2565
2566         n_pages = ev_page_cache_get_n_pages (page_cache);
2567
2568         view->find_result++;
2569
2570         if (view->find_result >= n_results) {
2571                 view->find_result = 0;
2572                 view->find_page++;
2573
2574                 if (view->find_page >= n_pages) {
2575                         view->find_page = 0;
2576                 }
2577
2578                 jump_to_find_page (view);
2579         } else {
2580                 jump_to_find_result (view);
2581                 gtk_widget_queue_draw (GTK_WIDGET (view));
2582         }
2583 }
2584
2585 void
2586 ev_view_find_previous (EvView *view)
2587 {
2588         int n_results, n_pages;
2589         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2590         EvPageCache *page_cache;
2591
2592         page_cache = ev_document_get_page_cache (view->document);
2593
2594         ev_document_doc_mutex_lock ();
2595         n_results = ev_document_find_get_n_results (find, view->current_page);
2596         ev_document_doc_mutex_unlock ();
2597
2598         n_pages = ev_page_cache_get_n_pages (page_cache);
2599
2600         view->find_result--;
2601
2602         if (view->find_result < 0) {
2603                 view->find_result = 0;
2604                 view->find_page--;
2605
2606                 if (view->find_page < 0) {
2607                         view->find_page = n_pages - 1;
2608                 }
2609
2610                 jump_to_find_page (view);
2611         } else {
2612                 jump_to_find_result (view);
2613                 gtk_widget_queue_draw (GTK_WIDGET (view));
2614         }
2615 }
2616
2617 /*** Selections ***/
2618
2619 static void
2620 compute_selections (EvView *view, GdkRectangle *view_rect)
2621 {
2622         int n_pages, i;
2623
2624         clear_selection (view);
2625
2626         n_pages = ev_page_cache_get_n_pages (view->page_cache);
2627         for (i = 0; i < n_pages; i++) {
2628                 GdkRectangle page_area;
2629                 GtkBorder border;
2630
2631                 if (get_page_extents (view, i, &page_area, &border)) {
2632                         GdkRectangle overlap;
2633
2634                         if (gdk_rectangle_intersect (&page_area, view_rect, &overlap)) {
2635                                 EvViewSelection *selection;
2636
2637                                 selection = g_new0 (EvViewSelection, 1);
2638                                 selection->page = i;
2639                                 view_rect_to_doc_rect (view, &overlap, &page_area,
2640                                                        &(selection->rect));
2641
2642                                 view->selections = g_list_append
2643                                                 (view->selections, selection);
2644                         }
2645                 }
2646         }
2647 }
2648
2649 static void
2650 clear_selection (EvView *view)
2651 {
2652         g_list_foreach (view->selections, (GFunc)g_free, NULL);
2653         view->selections = NULL;
2654 }
2655
2656
2657 void
2658 ev_view_select_all (EvView *view)
2659 {
2660         int n_pages, i;
2661
2662         clear_selection (view);
2663
2664         n_pages = ev_page_cache_get_n_pages (view->page_cache);
2665         for (i = 0; i < n_pages; i++) {
2666                 int width, height;
2667                 EvViewSelection *selection;
2668
2669                 ev_page_cache_get_size (view->page_cache,
2670                                         i, 1.0, &width, &height);
2671
2672                 selection = g_new0 (EvViewSelection, 1);
2673                 selection->page = i;
2674                 selection->rect.x1 = selection->rect.y1 = 0;
2675                 selection->rect.x2 = width;
2676                 selection->rect.y2 = height;
2677
2678                 view->selections = g_list_append (view->selections, selection);
2679         }
2680
2681         gtk_widget_queue_draw (GTK_WIDGET (view));
2682 }
2683
2684 static char *
2685 get_selected_text (EvView *ev_view)
2686 {
2687         GString *text;
2688         GList *l;
2689
2690         text = g_string_new (NULL);
2691
2692         ev_document_doc_mutex_lock ();
2693
2694         for (l = ev_view->selections; l != NULL; l = l->next) {
2695                 EvViewSelection *selection = (EvViewSelection *)l->data;
2696                 char *tmp;
2697
2698                 tmp = ev_document_get_text (ev_view->document,
2699                                             selection->page,
2700                                             &selection->rect);
2701                 g_string_append (text, tmp);
2702                 g_free (tmp);
2703         }
2704
2705         ev_document_doc_mutex_unlock ();
2706
2707         return g_string_free (text, FALSE);
2708 }
2709
2710 void
2711 ev_view_copy (EvView *ev_view)
2712 {
2713         GtkClipboard *clipboard;
2714         char *text;
2715
2716         if (!ev_document_can_get_text (ev_view->document)) {
2717                 return;
2718         }
2719
2720         text = get_selected_text (ev_view);
2721         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
2722                                               GDK_SELECTION_CLIPBOARD);
2723         gtk_clipboard_set_text (clipboard, text, -1);
2724         g_free (text);
2725 }
2726
2727 static void
2728 ev_view_primary_get_cb (GtkClipboard     *clipboard,
2729                         GtkSelectionData *selection_data,
2730                         guint             info,
2731                         gpointer          data)
2732 {
2733         EvView *ev_view = EV_VIEW (data);
2734         char *text;
2735
2736         if (!ev_document_can_get_text (ev_view->document)) {
2737                 return;
2738         }
2739
2740         text = get_selected_text (ev_view);
2741         gtk_selection_data_set_text (selection_data, text, -1);
2742         g_free (text);
2743 }
2744
2745 static void
2746 ev_view_primary_clear_cb (GtkClipboard *clipboard,
2747                           gpointer      data)
2748 {
2749         EvView *view = EV_VIEW (data);
2750
2751         clear_selection (view);
2752 }
2753
2754 static void
2755 ev_view_update_primary_selection (EvView *ev_view)
2756 {
2757         GtkClipboard *clipboard;
2758
2759         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
2760                                               GDK_SELECTION_PRIMARY);
2761
2762         if (ev_view->selections) {
2763                 if (!gtk_clipboard_set_with_owner (clipboard,
2764                                                    targets,
2765                                                    G_N_ELEMENTS (targets),
2766                                                    ev_view_primary_get_cb,
2767                                                    ev_view_primary_clear_cb,
2768                                                    G_OBJECT (ev_view)))
2769                         ev_view_primary_clear_cb (clipboard, ev_view);
2770         } else {
2771                 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
2772                         gtk_clipboard_clear (clipboard);
2773         }
2774 }
2775
2776 /*** Cursor operations ***/
2777
2778 static GdkCursor *
2779 ev_view_create_invisible_cursor(void)
2780 {
2781        GdkBitmap *empty;
2782        GdkColor black = { 0, 0, 0, 0 };
2783        static char bits[] = { 0x00 };
2784
2785        empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
2786
2787        return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
2788 }
2789
2790 static void
2791 ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
2792 {
2793         GdkCursor *cursor = NULL;
2794         GdkDisplay *display;
2795         GtkWidget *widget;
2796
2797         if (view->cursor == new_cursor) {
2798                 return;
2799         }
2800
2801         widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
2802         display = gtk_widget_get_display (widget);
2803         view->cursor = new_cursor;
2804
2805         switch (new_cursor) {
2806                 case EV_VIEW_CURSOR_NORMAL:
2807                         gdk_window_set_cursor (widget->window, NULL);
2808                         break;
2809                 case EV_VIEW_CURSOR_LINK:
2810                         cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
2811                         break;
2812                 case EV_VIEW_CURSOR_WAIT:
2813                         cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2814                         break;
2815                 case EV_VIEW_CURSOR_HIDDEN:
2816                         cursor = ev_view_create_invisible_cursor ();
2817                         break;
2818                 case EV_VIEW_CURSOR_DRAG:
2819                         cursor = gdk_cursor_new_for_display (display, GDK_FLEUR);
2820                         break;
2821         }
2822
2823         if (cursor) {
2824                 gdk_window_set_cursor (widget->window, cursor);
2825                 gdk_cursor_unref (cursor);
2826                 gdk_flush();
2827         }
2828 }
2829
2830 void
2831 ev_view_hide_cursor (EvView *view)
2832 {
2833        ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN);
2834 }
2835
2836 void
2837 ev_view_show_cursor (EvView *view)
2838 {
2839        ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
2840 }
2841
2842 /*** Enum description for usage in signal ***/
2843
2844 GType
2845 ev_sizing_mode_get_type (void)
2846 {
2847   static GType etype = 0;
2848   if (etype == 0) {
2849     static const GEnumValue values[] = {
2850       { EV_SIZING_FIT_WIDTH, "EV_SIZING_FIT_WIDTH", "fit-width" },
2851       { EV_SIZING_BEST_FIT, "EV_SIZING_BEST_FIT", "best-fit" },
2852       { EV_SIZING_FREE, "EV_SIZING_FREE", "free" },
2853       { 0, NULL, NULL }
2854     };
2855     etype = g_enum_register_static ("EvSizingMode", values);
2856   }
2857   return etype;
2858 }
2859