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