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