]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
5cc21ab1eedddd2631052b8784d6fa18cf46ae0a
[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         EvLink *link;
1677
1678         if (view->pressed_button == 2) {
1679                 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1680         }
1681
1682         if (view->document) {
1683                 link = get_link_at_location (view, event->x + view->scroll_x,
1684                                              event->y + view->scroll_y);
1685         } else {
1686                 link = NULL;
1687         }
1688
1689         view->pressed_button = -1;
1690         view->drag_info.in_drag = FALSE;
1691
1692         if (view->selection_info.selections) {
1693                 ev_view_update_primary_selection (view);
1694         } else if (link) {
1695                 ev_view_goto_link (view, link);
1696         } else if (view->presentation) {
1697                 switch (event->button) {
1698                 case 1:
1699                         ev_view_next_page (view);       
1700                         return TRUE;
1701                 case 3:
1702                         ev_view_previous_page (view);   
1703                         return TRUE;
1704                 }
1705         }
1706  
1707         return FALSE;
1708 }
1709
1710 static gint
1711 ev_view_focus_in (GtkWidget     *widget,
1712                   GdkEventFocus *event)
1713 {
1714         if (EV_VIEW (widget)->pixbuf_cache)
1715                 ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
1716         gtk_widget_queue_draw (widget);
1717
1718         return FALSE;
1719 }
1720
1721 static gint
1722 ev_view_focus_out (GtkWidget     *widget,
1723                      GdkEventFocus *event)
1724 {
1725         if (EV_VIEW (widget)->pixbuf_cache)
1726                 ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
1727         gtk_widget_queue_draw (widget);
1728
1729         return FALSE;
1730 }
1731
1732 static gboolean
1733 ev_view_leave_notify_event (GtkWidget *widget, GdkEventCrossing   *event)
1734 {
1735         EvView *view = EV_VIEW (widget);
1736     
1737         ev_view_set_status (view, NULL);
1738
1739         if (view->cursor == EV_VIEW_CURSOR_LINK ||
1740             view->cursor == EV_VIEW_CURSOR_IBEAM)
1741                 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1742
1743         if (view->link_tooltip) {
1744                 view->hovered_link = NULL;
1745                 ev_tooltip_deactivate (EV_TOOLTIP (view->link_tooltip));
1746         }
1747
1748         return FALSE;
1749 }
1750
1751 static gboolean
1752 ev_view_enter_notify_event (GtkWidget *widget, GdkEventCrossing   *event)
1753 {
1754         EvView *view = EV_VIEW (widget);
1755
1756         handle_link_over_xy (view, event->x, event->y);
1757     
1758         return FALSE;
1759 }
1760
1761 static void
1762 ev_view_style_set (GtkWidget *widget,
1763                    GtkStyle  *old_style)
1764 {
1765         if (EV_VIEW (widget)->pixbuf_cache)
1766                 ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
1767
1768         GTK_WIDGET_CLASS (ev_view_parent_class)->style_set (widget, old_style);
1769 }
1770
1771
1772 /*** Drawing ***/
1773
1774 static guint32
1775 ev_gdk_color_to_rgb (const GdkColor *color)
1776 {
1777   guint32 result;
1778   result = (0xff0000 | (color->red & 0xff00));
1779   result <<= 8;
1780   result |= ((color->green & 0xff00) | (color->blue >> 8));
1781   return result;
1782 }
1783
1784 static void
1785 draw_rubberband (GtkWidget *widget, GdkWindow *window,
1786                  const GdkRectangle *rect, guchar alpha)
1787 {
1788         GdkGC *gc;
1789         GdkPixbuf *pixbuf;
1790         GdkColor *fill_color_gdk;
1791         guint fill_color;
1792
1793         fill_color_gdk = gdk_color_copy (&GTK_WIDGET (widget)->style->base[GTK_STATE_SELECTED]);
1794         fill_color = ev_gdk_color_to_rgb (fill_color_gdk) << 8 | alpha;
1795
1796         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
1797                                  rect->width, rect->height);
1798         gdk_pixbuf_fill (pixbuf, fill_color);
1799
1800         gdk_draw_pixbuf (window, NULL, pixbuf,
1801                          0, 0,
1802                          rect->x - EV_VIEW (widget)->scroll_x, rect->y - EV_VIEW (widget)->scroll_y,
1803                          rect->width, rect->height,
1804                          GDK_RGB_DITHER_NONE,
1805                          0, 0);
1806
1807         g_object_unref (pixbuf);
1808
1809         gc = gdk_gc_new (window);
1810         gdk_gc_set_rgb_fg_color (gc, fill_color_gdk);
1811         gdk_draw_rectangle (window, gc, FALSE,
1812                             rect->x - EV_VIEW (widget)->scroll_x, rect->y - EV_VIEW (widget)->scroll_y,
1813                             rect->width - 1,
1814                             rect->height - 1);
1815         g_object_unref (gc);
1816
1817         gdk_color_free (fill_color_gdk);
1818 }
1819
1820
1821 static void
1822 highlight_find_results (EvView *view, int page)
1823 {
1824         EvDocumentFind *find;
1825         int i, results = 0;
1826
1827         g_return_if_fail (EV_IS_DOCUMENT_FIND (view->document));
1828
1829         find = EV_DOCUMENT_FIND (view->document);
1830
1831         results = ev_document_find_get_n_results (find, page);
1832
1833         for (i = 0; i < results; i++) {
1834                 EvRectangle rectangle;
1835                 GdkRectangle view_rectangle;
1836                 guchar alpha;
1837
1838                 if (i == view->find_result && page == view->find_page) {
1839                         alpha = 0x90;
1840                 } else {
1841                         alpha = 0x20;
1842                 }
1843
1844                 ev_document_find_get_result (find, page, i, &rectangle);
1845                 doc_rect_to_view_rect (view, page, &rectangle, &view_rectangle);
1846                 draw_rubberband (GTK_WIDGET (view), GTK_WIDGET(view)->window,
1847                                  &view_rectangle, alpha);
1848         }
1849 }
1850
1851 static void
1852 draw_loading_text (EvView       *view,
1853                    GdkRectangle *page_area,
1854                    GdkRectangle *expose_area)
1855 {
1856         PangoLayout *layout;
1857         PangoFontDescription *font_desc;
1858         PangoRectangle logical_rect;
1859         double real_scale;
1860         int target_width;
1861
1862         const char *loading_text = _("Loading...");     
1863
1864         layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), loading_text);
1865
1866         font_desc = pango_font_description_new ();
1867
1868
1869         /* We set the font to be 10 points, get the size, and scale appropriately */
1870         pango_font_description_set_size (font_desc, 10 * PANGO_SCALE);
1871         pango_layout_set_font_description (layout, font_desc);
1872         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1873
1874         /* Make sure we fit the middle of the page */
1875         target_width = MAX (page_area->width / 2, 1);
1876         real_scale = ((double)target_width / (double) logical_rect.width) * (PANGO_SCALE * 10);
1877         pango_font_description_set_size (font_desc, (int)real_scale);
1878         pango_layout_set_font_description (layout, font_desc);
1879         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1880
1881         gtk_paint_layout (GTK_WIDGET (view)->style,
1882                           GTK_WIDGET (view)->window,
1883                           GTK_WIDGET_STATE (view),
1884                           FALSE,
1885                           page_area,
1886                           GTK_WIDGET (view),
1887                           NULL,
1888                           page_area->x + (target_width/2),
1889                           page_area->y + (page_area->height - logical_rect.height) / 2,
1890                           layout);
1891
1892         pango_font_description_free (font_desc);
1893         g_object_unref (layout);
1894 }
1895
1896 static void
1897 draw_one_page (EvView          *view,
1898                gint             page,
1899                GdkRectangle    *page_area,
1900                GtkBorder       *border,
1901                GdkRectangle    *expose_area)
1902 {
1903         gint width, height;
1904         GdkPixbuf *current_pixbuf;
1905         GdkRectangle overlap;
1906         GdkRectangle real_page_area;
1907         EvViewSelection *selection;
1908
1909         g_assert (view->document);
1910         if (! gdk_rectangle_intersect (page_area, expose_area, &overlap))
1911                 return;
1912
1913         selection = find_selection_for_page (view, page);
1914         ev_page_cache_get_size (view->page_cache,
1915                                 page, view->rotation,
1916                                 view->scale,
1917                                 &width, &height);
1918         /* Render the document itself */
1919         real_page_area = *page_area;
1920
1921         real_page_area.x += border->left;
1922         real_page_area.y += border->top;
1923         real_page_area.width -= (border->left + border->right);
1924         real_page_area.height -= (border->top + border->bottom);
1925
1926         ev_document_misc_paint_one_page (GTK_WIDGET(view)->window,
1927                                          GTK_WIDGET (view),
1928                                          page_area, border);
1929
1930         if (gdk_rectangle_intersect (&real_page_area, expose_area, &overlap)) {
1931                 GdkPixbuf *selection_pixbuf = NULL;
1932                 GdkPixbuf *scaled_image;
1933                 GdkPixbuf *scaled_selection;
1934
1935                 current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, page);
1936
1937                 /* Get the selection pixbuf iff we have something to draw */
1938                 if (current_pixbuf && view->selection_mode == EV_VIEW_SELECTION_TEXT && selection)
1939                         selection_pixbuf = ev_pixbuf_cache_get_selection_pixbuf (view->pixbuf_cache,
1940                                                                                  page,
1941                                                                                  view->scale,
1942                                                                                  NULL);
1943
1944                 if (current_pixbuf == NULL)
1945                         scaled_image = NULL;
1946                 else if (width == gdk_pixbuf_get_width (current_pixbuf) &&
1947                          height == gdk_pixbuf_get_height (current_pixbuf))
1948                         scaled_image = g_object_ref (current_pixbuf);
1949                 else
1950                         /* FIXME: We don't want to scale the whole area, just the right
1951                          * area of it */
1952                         scaled_image = gdk_pixbuf_scale_simple (current_pixbuf,
1953                                                                 width, height,
1954                                                                 GDK_INTERP_NEAREST);
1955
1956                 if (selection_pixbuf == NULL)
1957                         scaled_selection = NULL;
1958                 else if (width == gdk_pixbuf_get_width (selection_pixbuf) &&
1959                          height == gdk_pixbuf_get_height (selection_pixbuf))
1960                         scaled_selection = g_object_ref (selection_pixbuf);
1961                 else
1962                         /* FIXME: We don't want to scale the whole area, just the right
1963                          * area of it */
1964                         scaled_selection = gdk_pixbuf_scale_simple (selection_pixbuf,
1965                                                                     width, height,
1966                                                                     GDK_INTERP_NEAREST);
1967
1968                 if (scaled_image) {
1969                         gdk_draw_pixbuf (GTK_WIDGET(view)->window,
1970                                          GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
1971                                          scaled_image,
1972                                          overlap.x - real_page_area.x,
1973                                          overlap.y - real_page_area.y,
1974                                          overlap.x, overlap.y,
1975                                          overlap.width, overlap.height,
1976                                          GDK_RGB_DITHER_NORMAL,
1977                                          0, 0);
1978                         g_object_unref (scaled_image);
1979                 } else {
1980                         draw_loading_text (view,
1981                                            &real_page_area,
1982                                            expose_area);
1983                 }
1984
1985                 if (scaled_selection) {
1986                         gdk_draw_pixbuf (GTK_WIDGET(view)->window,
1987                                          GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
1988                                          scaled_selection,
1989                                          overlap.x - real_page_area.x,
1990                                          overlap.y - real_page_area.y,
1991                                          overlap.x, overlap.y,
1992                                          overlap.width, overlap.height,
1993                                          GDK_RGB_DITHER_NORMAL,
1994                                          0, 0);
1995                         g_object_unref (scaled_selection);
1996                 }
1997         }
1998 }
1999
2000 /*** GObject functions ***/
2001
2002 static void
2003 ev_view_finalize (GObject *object)
2004 {
2005         EvView *view = EV_VIEW (object);
2006
2007         LOG ("Finalize");
2008
2009         g_free (view->status);
2010         g_free (view->find_status);
2011
2012         clear_selection (view);
2013
2014         G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
2015 }
2016
2017 static void
2018 ev_view_destroy (GtkObject *object)
2019 {
2020         EvView *view = EV_VIEW (object);
2021
2022         if (view->document) {
2023                 g_object_unref (view->document);
2024                 view->document = NULL;
2025         }
2026
2027         if (view->pixbuf_cache) {
2028                 g_object_unref (view->pixbuf_cache);
2029                 view->pixbuf_cache = NULL;
2030         }
2031
2032         if (view->link_tooltip) {
2033                 gtk_widget_destroy (view->link_tooltip);
2034         }
2035
2036         ev_view_set_scroll_adjustments (view, NULL, NULL);
2037
2038         GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
2039 }
2040
2041 static void
2042 ev_view_set_property (GObject      *object,
2043                       guint         prop_id,
2044                       const GValue *value,
2045                       GParamSpec   *pspec)
2046 {
2047         EvView *view = EV_VIEW (object);
2048
2049         switch (prop_id)
2050         {
2051         case PROP_CONTINUOUS:
2052                 ev_view_set_continuous (view, g_value_get_boolean (value));
2053                 break;
2054         case PROP_DUAL_PAGE:
2055                 ev_view_set_dual_page (view, g_value_get_boolean (value));
2056                 break;
2057         case PROP_FULLSCREEN:
2058                 ev_view_set_fullscreen (view, g_value_get_boolean (value));
2059                 break;
2060         case PROP_PRESENTATION:
2061                 ev_view_set_presentation (view, g_value_get_boolean (value));
2062                 break;
2063         case PROP_SIZING_MODE:
2064                 ev_view_set_sizing_mode (view, g_value_get_enum (value));
2065                 break;
2066         case PROP_ZOOM:
2067                 ev_view_set_zoom (view, g_value_get_double (value), FALSE);
2068                 break;
2069         case PROP_ROTATION:
2070                 ev_view_set_rotation (view, g_value_get_int (value));
2071                 break;
2072         default:
2073                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2074         }
2075 }
2076
2077 static void
2078 ev_view_get_property (GObject *object,
2079                       guint prop_id,
2080                       GValue *value,
2081                       GParamSpec *pspec)
2082 {
2083         EvView *view = EV_VIEW (object);
2084
2085         switch (prop_id)
2086         {
2087         case PROP_STATUS:
2088                 g_value_set_string (value, view->status);
2089                 break;
2090         case PROP_FIND_STATUS:
2091                 g_value_set_string (value, view->status);
2092                 break;
2093         case PROP_CONTINUOUS:
2094                 g_value_set_boolean (value, view->continuous);
2095                 break;
2096         case PROP_DUAL_PAGE:
2097                 g_value_set_boolean (value, view->dual_page);
2098                 break;
2099         case PROP_FULLSCREEN:
2100                 g_value_set_boolean (value, view->fullscreen);
2101                 break;
2102         case PROP_PRESENTATION:
2103                 g_value_set_boolean (value, view->presentation);
2104                 break;
2105         case PROP_SIZING_MODE:
2106                 g_value_set_enum (value, view->sizing_mode);
2107                 break;
2108         case PROP_ZOOM:
2109                 g_value_set_double (value, view->scale);
2110                 break;
2111         case PROP_ROTATION:
2112                 g_value_set_int (value, view->rotation);
2113                 break;
2114         default:
2115                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2116         }
2117 }
2118
2119 static void
2120 ev_view_class_init (EvViewClass *class)
2121 {
2122         GObjectClass *object_class = G_OBJECT_CLASS (class);
2123         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
2124         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
2125         GtkBindingSet *binding_set;
2126
2127         object_class->finalize = ev_view_finalize;
2128         object_class->set_property = ev_view_set_property;
2129         object_class->get_property = ev_view_get_property;
2130
2131         widget_class->expose_event = ev_view_expose_event;
2132         widget_class->button_press_event = ev_view_button_press_event;
2133         widget_class->motion_notify_event = ev_view_motion_notify_event;
2134         widget_class->button_release_event = ev_view_button_release_event;
2135         widget_class->focus_in_event = ev_view_focus_in;
2136         widget_class->focus_out_event = ev_view_focus_out;
2137         widget_class->size_request = ev_view_size_request;
2138         widget_class->size_allocate = ev_view_size_allocate;
2139         widget_class->realize = ev_view_realize;
2140         widget_class->unrealize = ev_view_unrealize;
2141         widget_class->scroll_event = ev_view_scroll_event;
2142         widget_class->enter_notify_event = ev_view_enter_notify_event;
2143         widget_class->leave_notify_event = ev_view_leave_notify_event;
2144         widget_class->style_set = ev_view_style_set;
2145         gtk_object_class->destroy = ev_view_destroy;
2146
2147         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
2148         class->binding_activated = ev_view_binding_activated;
2149
2150         widget_class->set_scroll_adjustments_signal =
2151             g_signal_new ("set-scroll-adjustments",
2152                           G_OBJECT_CLASS_TYPE (object_class),
2153                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2154                           G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
2155                           NULL, NULL,
2156                           ev_marshal_VOID__OBJECT_OBJECT,
2157                           G_TYPE_NONE, 2,
2158                           GTK_TYPE_ADJUSTMENT,
2159                           GTK_TYPE_ADJUSTMENT);
2160
2161         signals[SIGNAL_BINDING_ACTIVATED] = g_signal_new ("binding_activated",
2162                          G_TYPE_FROM_CLASS (object_class),
2163                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2164                          G_STRUCT_OFFSET (EvViewClass, binding_activated),
2165                          NULL, NULL,
2166                          ev_marshal_VOID__ENUM_BOOLEAN,
2167                          G_TYPE_NONE, 2,
2168                          GTK_TYPE_SCROLL_TYPE,
2169                          G_TYPE_BOOLEAN);
2170
2171         signals[SIGNAL_ZOOM_INVALID] = g_signal_new ("zoom-invalid",
2172                          G_TYPE_FROM_CLASS (object_class),
2173                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2174                          G_STRUCT_OFFSET (EvViewClass, zoom_invalid),
2175                          NULL, NULL,
2176                          ev_marshal_VOID__VOID,
2177                          G_TYPE_NONE, 0, G_TYPE_NONE);
2178
2179         g_object_class_install_property (object_class,
2180                                          PROP_STATUS,
2181                                          g_param_spec_string ("status",
2182                                                               "Status Message",
2183                                                               "The status message",
2184                                                               NULL,
2185                                                               G_PARAM_READABLE));
2186
2187         g_object_class_install_property (object_class,
2188                                          PROP_FIND_STATUS,
2189                                          g_param_spec_string ("find-status",
2190                                                               "Find Status Message",
2191                                                               "The find status message",
2192                                                               NULL,
2193                                                               G_PARAM_READABLE));
2194
2195         g_object_class_install_property (object_class,
2196                                          PROP_CONTINUOUS,
2197                                          g_param_spec_boolean ("continuous",
2198                                                                "Continuous",
2199                                                                "Continuous scrolling mode",
2200                                                                TRUE,
2201                                                                G_PARAM_READWRITE));
2202
2203         g_object_class_install_property (object_class,
2204                                          PROP_DUAL_PAGE,
2205                                          g_param_spec_boolean ("dual-page",
2206                                                                "Dual Page",
2207                                                                "Two pages visible at once",
2208                                                                FALSE,
2209                                                                G_PARAM_READWRITE));
2210         g_object_class_install_property (object_class,
2211                                          PROP_FULLSCREEN,
2212                                          g_param_spec_boolean ("fullscreen",
2213                                                                "Full Screen",
2214                                                                "Draw page in a fullscreen fashion",
2215                                                                FALSE,
2216                                                                G_PARAM_READWRITE));
2217         g_object_class_install_property (object_class,
2218                                          PROP_PRESENTATION,
2219                                          g_param_spec_boolean ("presentation",
2220                                                                "Presentation",
2221                                                                "Draw page in presentation mode",
2222                                                                TRUE,
2223                                                                G_PARAM_READWRITE));
2224
2225         g_object_class_install_property (object_class,
2226                                          PROP_SIZING_MODE,
2227                                          g_param_spec_enum ("sizing-mode",
2228                                                             "Sizing Mode",
2229                                                             "Sizing Mode",
2230                                                             EV_TYPE_SIZING_MODE,
2231                                                             EV_SIZING_FIT_WIDTH,
2232                                                             G_PARAM_READWRITE));
2233
2234         g_object_class_install_property (object_class,
2235                                          PROP_ZOOM,
2236                                          g_param_spec_double ("zoom",
2237                                                               "Zoom factor",
2238                                                                "Zoom factor",
2239                                                                MIN_SCALE,
2240                                                                MAX_SCALE,
2241                                                                1.0,
2242                                                                G_PARAM_READWRITE));
2243         g_object_class_install_property (object_class,
2244                                          PROP_ROTATION,
2245                                          g_param_spec_double ("rotation",
2246                                                               "Rotation",
2247                                                                "Rotation",
2248                                                                0,
2249                                                                360,
2250                                                                0,
2251                                                                G_PARAM_READWRITE));
2252
2253         binding_set = gtk_binding_set_by_class (class);
2254
2255         add_scroll_binding_keypad (binding_set, GDK_Left,  GTK_SCROLL_STEP_BACKWARD, TRUE);
2256         add_scroll_binding_keypad (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD,  TRUE);
2257         add_scroll_binding_keypad (binding_set, GDK_Up,    GTK_SCROLL_STEP_BACKWARD, FALSE);
2258         add_scroll_binding_keypad (binding_set, GDK_Down,  GTK_SCROLL_STEP_FORWARD,  FALSE);
2259 }
2260
2261 static void
2262 ev_view_init (EvView *view)
2263 {
2264         GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
2265
2266         view->spacing = 5;
2267         view->scale = 1.0;
2268         view->current_page = 0;
2269         view->pressed_button = -1;
2270         view->cursor = EV_VIEW_CURSOR_NORMAL;
2271         view->drag_info.in_drag = FALSE;
2272         view->selection_info.in_selection = FALSE;
2273         view->selection_mode = EV_VIEW_SELECTION_TEXT;
2274         view->continuous = TRUE;
2275         view->dual_page = FALSE;
2276         view->presentation = FALSE;
2277         view->fullscreen = FALSE;
2278         view->sizing_mode = EV_SIZING_FIT_WIDTH;
2279         view->pending_scroll = SCROLL_TO_KEEP_POSITION;
2280 }
2281
2282 /*** Callbacks ***/
2283
2284 static void
2285 find_changed_cb (EvDocument *document, int page, EvView *view)
2286 {
2287         jump_to_find_page (view, EV_VIEW_FIND_NEXT);
2288         jump_to_find_result (view);
2289         update_find_status_message (view);
2290
2291         if (view->current_page == page)
2292                 gtk_widget_queue_draw (GTK_WIDGET (view));
2293 }
2294
2295 static void
2296 job_finished_cb (EvPixbufCache *pixbuf_cache,
2297                  EvView        *view)
2298 {
2299         gtk_widget_queue_draw (GTK_WIDGET (view));
2300 }
2301
2302 static void
2303 page_changed_cb (EvPageCache *page_cache,
2304                  int          new_page,
2305                  EvView      *view)
2306 {
2307         if (view->current_page != new_page) {
2308
2309                 view->current_page = new_page;
2310                 view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2311                 gtk_widget_queue_resize (GTK_WIDGET (view));
2312
2313                 if (EV_IS_DOCUMENT_FIND (view->document)) {
2314                         view->find_page = new_page;
2315                         view->find_result = 0;
2316                         update_find_status_message (view);
2317                 }
2318         }
2319 }
2320
2321 static void on_adjustment_value_changed (GtkAdjustment  *adjustment,
2322                                          EvView *view)
2323 {
2324         int dx = 0, dy = 0;
2325
2326         if (! GTK_WIDGET_REALIZED (view))
2327                 return;
2328
2329         if (view->hadjustment) {
2330                 dx = view->scroll_x - (int) view->hadjustment->value;
2331                 view->scroll_x = (int) view->hadjustment->value;
2332         } else {
2333                 view->scroll_x = 0;
2334         }
2335
2336         if (view->vadjustment) {
2337                 dy = view->scroll_y - (int) view->vadjustment->value;
2338                 view->scroll_y = (int) view->vadjustment->value;
2339         } else {
2340                 view->scroll_y = 0;
2341         }
2342
2343
2344         if (view->pending_resize)
2345                 gtk_widget_queue_draw (GTK_WIDGET (view));
2346         else
2347                 gdk_window_scroll (GTK_WIDGET (view)->window, dx, dy);
2348
2349
2350         if (view->document)
2351                 view_update_range_and_current_page (view);
2352 }
2353
2354 GtkWidget*
2355 ev_view_new (void)
2356 {
2357         GtkWidget *view;
2358
2359         view = g_object_new (EV_TYPE_VIEW, NULL);
2360
2361         return view;
2362 }
2363
2364 static void
2365 setup_caches (EvView *view)
2366 {
2367         view->page_cache = ev_page_cache_get (view->document);
2368         g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
2369         view->pixbuf_cache = ev_pixbuf_cache_new (GTK_WIDGET (view), view->document);
2370         g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
2371 }
2372
2373 static void
2374 clear_caches (EvView *view)
2375 {
2376         if (view->pixbuf_cache) {
2377                 g_object_unref (view->pixbuf_cache);
2378                 view->pixbuf_cache = NULL;
2379         }
2380
2381         if (view->page_cache) {
2382                 view->page_cache = NULL;
2383         }
2384 }
2385
2386 void
2387 ev_view_set_document (EvView     *view,
2388                       EvDocument *document)
2389 {
2390         g_return_if_fail (EV_IS_VIEW (view));
2391
2392         if (document != view->document) {
2393                 clear_caches (view);
2394
2395                 if (view->document) {
2396                         g_signal_handlers_disconnect_by_func (view->document,
2397                                                               find_changed_cb,
2398                                                               view);
2399                         g_object_unref (view->document);
2400                         view->page_cache = NULL;
2401
2402                 }
2403
2404                 view->document = document;
2405                 view->find_page = 0;
2406                 view->find_result = 0;
2407
2408                 if (view->document) {
2409                         g_object_ref (view->document);
2410                         if (EV_IS_DOCUMENT_FIND (view->document)) {
2411                                 g_signal_connect (view->document,
2412                                                   "find_changed",
2413                                                   G_CALLBACK (find_changed_cb),
2414                                                   view);
2415                         }
2416
2417                         setup_caches (view);
2418                 }
2419
2420                 gtk_widget_queue_resize (GTK_WIDGET (view));
2421         }
2422 }
2423
2424 /*** Zoom and sizing mode ***/
2425
2426 #define EPSILON 0.0000001
2427 void
2428 ev_view_set_zoom (EvView   *view,
2429                   double    factor,
2430                   gboolean  relative)
2431 {
2432         double scale;
2433
2434         if (relative)
2435                 scale = view->scale * factor;
2436         else
2437                 scale = factor;
2438
2439         scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
2440
2441         if (ABS (view->scale - scale) < EPSILON)
2442                 return;
2443
2444         view->scale = scale;
2445         view->pending_resize = TRUE;
2446
2447         gtk_widget_queue_resize (GTK_WIDGET (view));
2448
2449         g_object_notify (G_OBJECT (view), "zoom");
2450 }
2451
2452 double
2453 ev_view_get_zoom (EvView *view)
2454 {
2455         return view->scale;
2456 }
2457
2458 gboolean
2459 ev_view_get_continuous (EvView *view)
2460 {
2461         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2462
2463         return view->continuous;
2464 }
2465
2466 void
2467 ev_view_set_continuous (EvView   *view,
2468                         gboolean  continuous)
2469 {
2470         g_return_if_fail (EV_IS_VIEW (view));
2471
2472         continuous = continuous != FALSE;
2473
2474         if (view->continuous != continuous) {
2475                 view->continuous = continuous;
2476                 view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2477                 gtk_widget_queue_resize (GTK_WIDGET (view));
2478         }
2479
2480         g_object_notify (G_OBJECT (view), "continuous");
2481 }
2482
2483 gboolean
2484 ev_view_get_dual_page (EvView *view)
2485 {
2486         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2487
2488         return view->dual_page;
2489 }
2490
2491 void
2492 ev_view_set_dual_page (EvView   *view,
2493                        gboolean  dual_page)
2494 {
2495         g_return_if_fail (EV_IS_VIEW (view));
2496
2497         dual_page = dual_page != FALSE;
2498
2499         if (view->dual_page == dual_page)
2500                 return;
2501
2502         view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2503         view->dual_page = dual_page;
2504         /* FIXME: if we're keeping the pixbuf cache around, we should extend the
2505          * preload_cache_size to be 2 if dual_page is set.
2506          */
2507         gtk_widget_queue_resize (GTK_WIDGET (view));
2508
2509         g_object_notify (G_OBJECT (view), "dual-page");
2510 }
2511
2512 void
2513 ev_view_set_fullscreen (EvView   *view,
2514                          gboolean  fullscreen)
2515 {
2516         g_return_if_fail (EV_IS_VIEW (view));
2517
2518         fullscreen = fullscreen != FALSE;
2519
2520         if (view->fullscreen == fullscreen) 
2521                 return;
2522                 
2523         view->fullscreen = fullscreen;
2524         gtk_widget_queue_resize (GTK_WIDGET (view));
2525         
2526         g_object_notify (G_OBJECT (view), "fullscreen");
2527 }
2528
2529 gboolean
2530 ev_view_get_fullscreen (EvView *view)
2531 {
2532         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2533
2534         return view->fullscreen;
2535 }
2536
2537 void
2538 ev_view_set_presentation (EvView   *view,
2539                           gboolean  presentation)
2540 {
2541         g_return_if_fail (EV_IS_VIEW (view));
2542
2543         presentation = presentation != FALSE;
2544
2545         if (view->presentation == presentation)
2546                 return;
2547
2548         view->presentation = presentation;
2549         view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2550         gtk_widget_queue_resize (GTK_WIDGET (view));
2551
2552         if (GTK_WIDGET_REALIZED (view)) {
2553                 if (view->presentation)
2554                         gdk_window_set_background (GTK_WIDGET(view)->window,
2555                                                    &GTK_WIDGET (view)->style->black);
2556                 else
2557                         gdk_window_set_background (GTK_WIDGET(view)->window,
2558                                                    &GTK_WIDGET (view)->style->mid [GTK_STATE_NORMAL]);
2559         }
2560
2561
2562         g_object_notify (G_OBJECT (view), "presentation");
2563 }
2564
2565 gboolean
2566 ev_view_get_presentation (EvView *view)
2567 {
2568         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2569
2570         return view->presentation;
2571 }
2572
2573 void
2574 ev_view_set_sizing_mode (EvView       *view,
2575                          EvSizingMode  sizing_mode)
2576 {
2577         g_return_if_fail (EV_IS_VIEW (view));
2578
2579         if (view->sizing_mode == sizing_mode)
2580                 return;
2581
2582         view->sizing_mode = sizing_mode;
2583         gtk_widget_queue_resize (GTK_WIDGET (view));
2584
2585         g_object_notify (G_OBJECT (view), "sizing-mode");
2586 }
2587
2588 EvSizingMode
2589 ev_view_get_sizing_mode (EvView *view)
2590 {
2591         g_return_val_if_fail (EV_IS_VIEW (view), EV_SIZING_FREE);
2592
2593         return view->sizing_mode;
2594 }
2595
2596 gboolean
2597 ev_view_can_zoom_in (EvView *view)
2598 {
2599         return view->scale * ZOOM_IN_FACTOR <= MAX_SCALE;
2600 }
2601
2602 gboolean
2603 ev_view_can_zoom_out (EvView *view)
2604 {
2605         return view->scale * ZOOM_OUT_FACTOR >= MIN_SCALE;
2606 }
2607
2608 void
2609 ev_view_zoom_in (EvView *view)
2610 {
2611         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2612
2613         view->pending_scroll = SCROLL_TO_CENTER;
2614         ev_view_set_zoom (view, ZOOM_IN_FACTOR, TRUE);
2615 }
2616
2617 void
2618 ev_view_zoom_out (EvView *view)
2619 {
2620         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2621
2622         view->pending_scroll = SCROLL_TO_CENTER;
2623         ev_view_set_zoom (view, ZOOM_OUT_FACTOR, TRUE);
2624 }
2625
2626 void
2627 ev_view_rotate_right (EvView *view)
2628 {
2629         int rotation = view->rotation + 90;
2630
2631         if (rotation >= 360) {
2632                 rotation -= 360;
2633         }
2634
2635         ev_view_set_rotation (view, rotation);
2636 }
2637
2638 void
2639 ev_view_rotate_left (EvView *view)
2640 {
2641         int rotation = view->rotation - 90;
2642
2643         if (rotation < 0) {
2644                 rotation += 360;
2645         }
2646
2647         ev_view_set_rotation (view, rotation);
2648 }
2649
2650 void
2651 ev_view_set_rotation (EvView *view, int rotation)
2652 {
2653         view->rotation = rotation;
2654
2655         if (view->pixbuf_cache) {
2656                 ev_pixbuf_cache_clear (view->pixbuf_cache);
2657                 gtk_widget_queue_resize (GTK_WIDGET (view));
2658         }
2659
2660         if (rotation != 0)
2661                 clear_selection (view);
2662
2663         g_object_notify (G_OBJECT (view), "rotation");
2664 }
2665
2666 int
2667 ev_view_get_rotation (EvView *view)
2668 {
2669         return view->rotation;
2670 }
2671
2672 static double
2673 zoom_for_size_fit_width (int doc_width,
2674                          int doc_height,
2675                          int target_width,
2676                          int target_height,
2677                          int vsb_width)
2678 {
2679         double scale;
2680
2681         scale = (double)target_width / doc_width;
2682
2683         if (doc_height * scale > target_height)
2684                 scale = (double) (target_width - vsb_width) / doc_width;
2685
2686         return scale;
2687 }
2688
2689 static double
2690 zoom_for_size_best_fit (int doc_width,
2691                         int doc_height,
2692                         int target_width,
2693                         int target_height,
2694                         int vsb_width,
2695                         int hsb_width)
2696 {
2697         double w_scale;
2698         double h_scale;
2699
2700         w_scale = (double)target_width / doc_width;
2701         h_scale = (double)target_height / doc_height;
2702
2703         if (doc_height * w_scale > target_height)
2704                 w_scale = (double) (target_width - vsb_width) / doc_width;
2705         if (doc_width * h_scale > target_width)
2706                 h_scale = (double) (target_height - hsb_width) / doc_height;
2707
2708         return MIN (w_scale, h_scale);
2709 }
2710
2711
2712 static void
2713 ev_view_zoom_for_size_presentation (EvView *view,
2714                                     int     width,
2715                                     int     height)
2716 {
2717         int doc_width, doc_height;
2718         gdouble scale;
2719
2720         ev_page_cache_get_size (view->page_cache,
2721                                 view->current_page,
2722                                 view->rotation,
2723                                 1.0,
2724                                 &doc_width,
2725                                 &doc_height);
2726         scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, 0, 0);
2727         ev_view_set_zoom (view, scale, FALSE);
2728 }
2729
2730 static void
2731 ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
2732                            int     width,
2733                            int     height,
2734                            int     vsb_width,
2735                            int     hsb_height)
2736 {
2737         int doc_width, doc_height;
2738         GtkBorder border;
2739         gdouble scale;
2740
2741         ev_page_cache_get_max_width (view->page_cache,
2742                                      view->rotation,
2743                                      1.0,
2744                                      &doc_width);
2745         ev_page_cache_get_max_height (view->page_cache,
2746                                       view->rotation,
2747                                       1.0,
2748                                       &doc_height);
2749         compute_border (view, doc_width, doc_height, &border);
2750
2751         doc_width = doc_width * 2;
2752         width -= (2 * (border.left + border.right) + 3 * view->spacing);
2753         height -= (border.top + border.bottom + 2 * view->spacing - 1);
2754
2755         /* FIXME: We really need to calculate the overall height here, not the
2756          * page height.  We assume there's always a vertical scrollbar for
2757          * now.  We need to fix this. */
2758         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2759                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2760         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2761                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2762         else
2763                 g_assert_not_reached ();
2764
2765         ev_view_set_zoom (view, scale, FALSE);
2766 }
2767
2768 static void
2769 ev_view_zoom_for_size_continuous (EvView *view,
2770                                   int     width,
2771                                   int     height,
2772                                   int     vsb_width,
2773                                   int     hsb_height)
2774 {
2775         int doc_width, doc_height;
2776         GtkBorder border;
2777         gdouble scale;
2778
2779         ev_page_cache_get_max_width (view->page_cache,
2780                                      view->rotation,
2781                                      1.0,
2782                                      &doc_width);
2783         ev_page_cache_get_max_height (view->page_cache,
2784                                       view->rotation,
2785                                       1.0,
2786                                       &doc_height);
2787         compute_border (view, doc_width, doc_height, &border);
2788
2789         width -= (border.left + border.right + 2 * view->spacing);
2790         height -= (border.top + border.bottom + 2 * view->spacing - 1);
2791
2792         /* FIXME: We really need to calculate the overall height here, not the
2793          * page height.  We assume there's always a vertical scrollbar for
2794          * now.  We need to fix this. */
2795         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2796                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2797         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2798                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2799         else
2800                 g_assert_not_reached ();
2801
2802         ev_view_set_zoom (view, scale, FALSE);
2803 }
2804
2805 static void
2806 ev_view_zoom_for_size_dual_page (EvView *view,
2807                                  int     width,
2808                                  int     height,
2809                                  int     vsb_width,
2810                                  int     hsb_height)
2811 {
2812         GtkBorder border;
2813         gint doc_width, doc_height;
2814         gdouble scale;
2815         gint other_page;
2816
2817         other_page = view->current_page ^ 1;
2818
2819         /* Find the largest of the two. */
2820         ev_page_cache_get_size (view->page_cache,
2821                                 view->current_page,
2822                                 view->rotation,
2823                                 1.0,
2824                                 &doc_width, &doc_height);
2825
2826         if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
2827                 gint width_2, height_2;
2828                 ev_page_cache_get_size (view->page_cache,
2829                                         other_page,
2830                                         view->rotation,
2831                                         1.0,
2832                                         &width_2, &height_2);
2833                 if (width_2 > doc_width)
2834                         doc_width = width_2;
2835                 if (height_2 > doc_height)
2836                         doc_height = height_2;
2837         }
2838         compute_border (view, doc_width, doc_height, &border);
2839
2840         doc_width = doc_width * 2;
2841         width -= ((border.left + border.right)* 2 + 3 * view->spacing);
2842         height -= (border.top + border.bottom + 2 * view->spacing);
2843
2844         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2845                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2846         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2847                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2848         else
2849                 g_assert_not_reached ();
2850
2851         ev_view_set_zoom (view, scale, FALSE);
2852 }
2853
2854 static void
2855 ev_view_zoom_for_size_single_page (EvView *view,
2856                                    int     width,
2857                                    int     height,
2858                                    int     vsb_width,
2859                                    int     hsb_height)
2860 {
2861         int doc_width, doc_height;
2862         GtkBorder border;
2863         gdouble scale;
2864
2865         ev_page_cache_get_size (view->page_cache,
2866                                 view->current_page,
2867                                 view->rotation,
2868                                 1.0,
2869                                 &doc_width,
2870                                 &doc_height);
2871         /* Get an approximate border */
2872         compute_border (view, width, height, &border);
2873
2874         width -= (border.left + border.right + 2 * view->spacing);
2875         height -= (border.top + border.bottom + 2 * view->spacing);
2876
2877         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2878                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2879         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2880                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2881         else
2882                 g_assert_not_reached ();
2883
2884         ev_view_set_zoom (view, scale, FALSE);
2885 }
2886
2887 void
2888 ev_view_set_zoom_for_size (EvView *view,
2889                            int     width,
2890                            int     height,
2891                            int     vsb_width,
2892                            int     hsb_height)
2893 {
2894         g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
2895                           view->sizing_mode == EV_SIZING_BEST_FIT);
2896         g_return_if_fail (width >= 0);
2897         g_return_if_fail (height >= 0);
2898
2899         if (view->document == NULL)
2900                 return;
2901
2902         if (view->presentation)
2903                 ev_view_zoom_for_size_presentation (view, width, height);
2904         else if (view->continuous && view->dual_page)
2905                 ev_view_zoom_for_size_continuous_and_dual_page (view, width, height, vsb_width, hsb_height);
2906         else if (view->continuous)
2907                 ev_view_zoom_for_size_continuous (view, width, height, vsb_width, hsb_height);
2908         else if (view->dual_page)
2909                 ev_view_zoom_for_size_dual_page (view, width, height, vsb_width, hsb_height);
2910         else
2911                 ev_view_zoom_for_size_single_page (view, width, height, vsb_width, hsb_height);
2912 }
2913
2914 /*** Status text messages ***/
2915
2916 const char *
2917 ev_view_get_status (EvView *view)
2918 {
2919         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2920
2921         return view->status;
2922 }
2923
2924 static void
2925 ev_view_set_status (EvView *view, const char *message)
2926 {
2927         g_return_if_fail (EV_IS_VIEW (view));
2928
2929         if (message != view->status) {
2930                 g_free (view->status);
2931                 view->status = g_strdup (message);
2932                 g_object_notify (G_OBJECT (view), "status");
2933         }
2934 }
2935
2936 static void
2937 update_find_status_message (EvView *view)
2938 {
2939         char *message;
2940
2941         if (view->current_page == view->find_page) {
2942                 int results;
2943
2944                 results = ev_document_find_get_n_results
2945                                 (EV_DOCUMENT_FIND (view->document),
2946                                  view->current_page);
2947                 /* TRANS: Sometimes this could be better translated as
2948                    "%d hit(s) on this page".  Therefore this string
2949                    contains plural cases. */
2950                 message = g_strdup_printf (ngettext ("%d found on this page",
2951                                                      "%d found on this page",
2952                                                      results),
2953                                            results);
2954         } else {
2955                 double percent;
2956
2957                 percent = ev_document_find_get_progress
2958                                 (EV_DOCUMENT_FIND (view->document));
2959                 if (percent >= (1.0 - 1e-10)) {
2960                         message = g_strdup (_("Not found"));
2961                 } else {
2962                         message = g_strdup_printf (_("%3d%% remaining to search"),
2963                                                    (int) ((1.0 - percent) * 100));
2964                 }
2965
2966         }
2967         ev_view_set_find_status (view, message);
2968         g_free (message);
2969 }
2970
2971 const char *
2972 ev_view_get_find_status (EvView *view)
2973 {
2974         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2975
2976         return view->find_status;
2977 }
2978
2979 static void
2980 ev_view_set_find_status (EvView *view, const char *message)
2981 {
2982         g_return_if_fail (EV_IS_VIEW (view));
2983
2984         g_free (view->find_status);
2985         view->find_status = g_strdup (message);
2986         g_object_notify (G_OBJECT (view), "find-status");
2987 }
2988
2989 /*** Find ***/
2990
2991 static void
2992 jump_to_find_result (EvView *view)
2993 {
2994         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2995         EvRectangle rect;
2996         GdkRectangle view_rect;
2997         int n_results;
2998         int page = view->find_page;
2999
3000         n_results = ev_document_find_get_n_results (find, page);
3001
3002         if (n_results > 0  && view->find_result < n_results) {
3003                 ev_document_find_get_result
3004                         (find, page, view->find_result, &rect);
3005
3006                 doc_rect_to_view_rect (view, page, &rect, &view_rect);
3007                 ensure_rectangle_is_visible (view, &view_rect);
3008         }
3009 }
3010
3011 static void
3012 jump_to_find_page (EvView *view, EvViewFindDirection direction)
3013 {
3014         int n_pages, i;
3015
3016         n_pages = ev_page_cache_get_n_pages (view->page_cache);
3017
3018         for (i = 0; i < n_pages; i++) {
3019                 int has_results;
3020                 int page;
3021                 
3022                 if (direction == EV_VIEW_FIND_NEXT)
3023                         page = view->find_page + i;
3024                 else
3025                         page = view->find_page - i;
3026
3027
3028                 if (page >= n_pages) {
3029                         page = page - n_pages;
3030                 }
3031                 if (page < 0) 
3032                         page = page + n_pages;
3033                 
3034                 has_results = ev_document_find_page_has_results
3035                                 (EV_DOCUMENT_FIND (view->document), page);
3036                 if (has_results == -1) {
3037                         view->find_page = page;
3038                         break;
3039                 } else if (has_results == 1) {
3040                         ev_page_cache_set_current_page (view->page_cache, page);
3041                         break;
3042                 }
3043         }
3044 }
3045
3046 gboolean
3047 ev_view_can_find_next (EvView *view)
3048 {
3049         if (EV_IS_DOCUMENT_FIND (view->document)) {
3050                 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
3051                 int i, n_pages;
3052
3053                 n_pages = ev_page_cache_get_n_pages (view->page_cache);
3054                 for (i = 0; i < n_pages; i++) {
3055                         if (ev_document_find_get_n_results (find, i) > 0) {
3056                                 return TRUE;
3057                         }
3058                 }
3059         }
3060
3061         return FALSE;
3062 }
3063
3064 void
3065 ev_view_find_next (EvView *view)
3066 {
3067         int n_results, n_pages;
3068         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
3069
3070         n_results = ev_document_find_get_n_results (find, view->current_page);
3071
3072         n_pages = ev_page_cache_get_n_pages (view->page_cache);
3073
3074         view->find_result++;
3075
3076         if (view->find_result >= n_results) {
3077
3078                 view->find_result = 0;
3079                 view->find_page++;
3080                 if (view->find_page >= n_pages) {
3081                         view->find_page = 0;
3082                 }
3083
3084                 jump_to_find_page (view, EV_VIEW_FIND_NEXT);
3085                 jump_to_find_result (view);
3086         } else {
3087                 jump_to_find_result (view);
3088                 gtk_widget_queue_draw (GTK_WIDGET (view));
3089         }
3090 }
3091
3092 void
3093 ev_view_find_previous (EvView *view)
3094 {
3095         int n_results, n_pages;
3096         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
3097         EvPageCache *page_cache;
3098
3099         page_cache = ev_page_cache_get (view->document);
3100
3101         n_results = ev_document_find_get_n_results (find, view->current_page);
3102
3103         n_pages = ev_page_cache_get_n_pages (page_cache);
3104
3105         view->find_result--;
3106
3107         if (view->find_result < 0) {
3108
3109                 view->find_page--;
3110                 if (view->find_page < 0) {
3111                         view->find_page = n_pages - 1;
3112                 }
3113
3114                 jump_to_find_page (view, EV_VIEW_FIND_PREV);
3115                 view->find_result = ev_document_find_get_n_results (find, view->current_page) - 1;
3116                 jump_to_find_result (view);
3117         } else {
3118                 jump_to_find_result (view);
3119                 gtk_widget_queue_draw (GTK_WIDGET (view));
3120         }
3121 }
3122
3123 /*** Selections ***/
3124
3125 /* compute_new_selection_rect/text calculates the area currently selected by
3126  * view_rect.  each handles a different mode;
3127  */
3128 static GList *
3129 compute_new_selection_rect (EvView       *view,
3130                             GdkPoint     *start,
3131                             GdkPoint     *stop)
3132 {
3133         GdkRectangle view_rect;
3134         int n_pages, i;
3135         GList *list = NULL;
3136
3137         g_assert (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE);
3138         
3139         view_rect.x = MIN (start->x, stop->x);
3140         view_rect.y = MIN (start->y, stop->y);
3141         view_rect.width = MAX (start->x, stop->x) - view_rect.x;
3142         view_rect.width = MAX (start->y, stop->y) - view_rect.y;
3143
3144         n_pages = ev_page_cache_get_n_pages (view->page_cache);
3145
3146         for (i = 0; i < n_pages; i++) {
3147                 GdkRectangle page_area;
3148                 GtkBorder border;
3149                 
3150                 if (get_page_extents (view, i, &page_area, &border)) {
3151                         GdkRectangle overlap;
3152
3153                         if (gdk_rectangle_intersect (&page_area, &view_rect, &overlap)) {
3154                                 EvViewSelection *selection;
3155
3156                                 selection = g_new0 (EvViewSelection, 1);
3157                                 selection->page = i;
3158                                 view_rect_to_doc_rect (view, &overlap, &page_area,
3159                                                        &(selection->rect));
3160
3161                                 list = g_list_append (list, selection);
3162                         }
3163                 }
3164         }
3165
3166         return list;
3167 }
3168
3169 static gboolean
3170 gdk_rectangle_point_in (GdkRectangle *rectangle,
3171                         GdkPoint     *point)
3172 {
3173         return rectangle->x <= point->x &&
3174                 rectangle->y <= point->y &&
3175                 point->x < rectangle->x + rectangle->width &&
3176                 point->y < rectangle->y + rectangle->height;
3177 }
3178
3179 static GList *
3180 compute_new_selection_text (EvView   *view,
3181                             GdkPoint *start,
3182                             GdkPoint *stop)
3183 {
3184         int n_pages, i, first, last;
3185         GList *list = NULL;
3186         EvViewSelection *selection;
3187         gint width, height;
3188         int start_page, end_page;
3189
3190         g_assert (view->selection_mode == EV_VIEW_SELECTION_TEXT);
3191
3192         n_pages = ev_page_cache_get_n_pages (view->page_cache);
3193
3194         /* First figure out the range of pages the selection
3195          * affects. */
3196         first = n_pages;
3197         last = 0;
3198         if (view->continuous) {
3199                 start_page = 0;
3200                 end_page = n_pages;
3201         } else if (view->dual_page) {
3202                 start_page = view->start_page;
3203                 end_page = view->end_page + 1;
3204         } else {
3205                 start_page = view->current_page;
3206                 end_page = view->current_page + 1;
3207         }
3208
3209         for (i = start_page; i < end_page; i++) {
3210                 GdkRectangle page_area;
3211                 GtkBorder border;
3212                 
3213                 get_page_extents (view, i, &page_area, &border);
3214                 if (gdk_rectangle_point_in (&page_area, start) || 
3215                     gdk_rectangle_point_in (&page_area, stop)) {
3216                         if (first == n_pages)
3217                                 first = i;
3218                         last = i;
3219                 }
3220
3221         }
3222
3223
3224
3225         /* Now create a list of EvViewSelection's for the affected
3226          * pages.  This could be an empty list, a list of just one
3227          * page or a number of pages.*/
3228         for (i = first; i < last + 1; i++) {
3229                 GdkRectangle page_area;
3230                 GtkBorder border;
3231                 GdkPoint *point;
3232
3233                 ev_page_cache_get_size (view->page_cache, i,
3234                                         view->rotation,
3235                                         1.0, &width, &height);
3236
3237                 selection = g_new0 (EvViewSelection, 1);
3238                 selection->page = i;
3239                 selection->rect.x1 = selection->rect.y1 = 0;
3240                 selection->rect.x2 = width;
3241                 selection->rect.y2 = height;
3242
3243                 get_page_extents (view, i, &page_area, &border);
3244
3245                 if (gdk_rectangle_point_in (&page_area, start))
3246                         point = start;
3247                 else
3248                         point = stop;
3249
3250                 if (i == first)
3251                         view_point_to_doc_point (view, point, &page_area,
3252                                                  &selection->rect.x1,
3253                                                  &selection->rect.y1);
3254
3255                 /* If the selection is contained within just one page,
3256                  * make sure we don't write 'start' into both points
3257                  * in selection->rect. */
3258                 if (first == last)
3259                         point = stop;
3260
3261                 if (i == last)
3262                         view_point_to_doc_point (view, point, &page_area,
3263                                                  &selection->rect.x2,
3264                                                  &selection->rect.y2);
3265
3266                 list = g_list_append (list, selection);
3267         }
3268
3269         return list;
3270 }
3271
3272 /* This function takes the newly calculated list, and figures out which regions
3273  * have changed.  It then queues a redraw approporiately.
3274  */
3275 static void
3276 merge_selection_region (EvView *view,
3277                         GList  *new_list)
3278 {
3279         GList *old_list;
3280         GList *new_list_ptr, *old_list_ptr;
3281
3282         /* Update the selection */
3283         old_list = ev_pixbuf_cache_get_selection_list (view->pixbuf_cache);
3284         g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL);
3285         view->selection_info.selections = new_list;
3286         ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, new_list);
3287
3288         new_list_ptr = new_list;
3289         old_list_ptr = old_list;
3290
3291         while (new_list_ptr || old_list_ptr) {
3292                 EvViewSelection *old_sel, *new_sel;
3293                 int cur_page;
3294                 GdkRegion *region = NULL;
3295
3296                 new_sel = (new_list_ptr) ? (new_list_ptr->data) : NULL;
3297                 old_sel = (old_list_ptr) ? (old_list_ptr->data) : NULL;
3298
3299                 /* Assume that the lists are in order, and we run through them
3300                  * comparing them, one page at a time.  We come out with the
3301                  * first page we see. */
3302                 if (new_sel && old_sel) {
3303                         if (new_sel->page < old_sel->page) {
3304                                 new_list_ptr = new_list_ptr->next;
3305                                 old_sel = NULL;
3306                         } else if (new_sel->page > old_sel->page) {
3307                                 old_list_ptr = old_list_ptr->next;
3308                                 new_sel = NULL;
3309                         } else {
3310                                 new_list_ptr = new_list_ptr->next;
3311                                 old_list_ptr = old_list_ptr->next;
3312                         }
3313                 } else if (new_sel) {
3314                         new_list_ptr = new_list_ptr->next;
3315                 } else if (old_sel) {
3316                         old_list_ptr = old_list_ptr->next;
3317                 }
3318
3319                 g_assert (new_sel || old_sel);
3320
3321                 /* is the page we're looking at on the screen?*/
3322                 cur_page = new_sel ? new_sel->page : old_sel->page;
3323                 if (cur_page < view->start_page || cur_page > view->end_page)
3324                         continue;
3325
3326                 /* seed the cache with a new page.  We are going to need the new
3327                  * region too. */
3328                 if (new_sel) {
3329                         GdkRegion *tmp_region = NULL;
3330                         ev_pixbuf_cache_get_selection_pixbuf (view->pixbuf_cache,
3331                                                               cur_page,
3332                                                               view->scale,
3333                                                               &tmp_region);
3334                         if (tmp_region) {
3335                                 new_sel->covered_region = gdk_region_copy (tmp_region);
3336                         }
3337                 }
3338
3339                 /* Now we figure out what needs redrawing */
3340                 if (old_sel && new_sel) {
3341                         if (old_sel->covered_region &&
3342                             new_sel->covered_region) {
3343                                 /* We only want to redraw the areas that have
3344                                  * changed, so we xor the old and new regions
3345                                  * and redraw if it's different */
3346                                 region = gdk_region_copy (old_sel->covered_region);
3347                                 gdk_region_xor (region, new_sel->covered_region);
3348                                 if (gdk_region_empty (region)) {
3349                                         gdk_region_destroy (region);
3350                                         region = NULL;
3351                                 }
3352                         } else if (old_sel->covered_region) {
3353                                 region = gdk_region_copy (old_sel->covered_region);
3354                         } else if (new_sel->covered_region) {
3355                                 region = gdk_region_copy (new_sel->covered_region);
3356                         }
3357                 } else if (old_sel && !new_sel) {
3358                         if (old_sel->covered_region && !gdk_region_empty (old_sel->covered_region)) {
3359                                 region = gdk_region_copy (old_sel->covered_region);
3360                         }
3361                 } else if (!old_sel && new_sel) {
3362                         if (new_sel->covered_region && !gdk_region_empty (new_sel->covered_region)) {
3363                                 region = gdk_region_copy (new_sel->covered_region);
3364                         }
3365                 } else {
3366                         g_assert_not_reached ();
3367                 }
3368
3369                 /* Redraw the damaged region! */
3370                 if (region) {
3371                         GdkRectangle page_area;
3372                         GtkBorder border;
3373
3374                         get_page_extents (view, cur_page, &page_area, &border);
3375                         gdk_region_offset (region,
3376                                            page_area.x + border.left - view->scroll_x,
3377                                            page_area.y + border.top - view->scroll_y);
3378                         gdk_window_invalidate_region (GTK_WIDGET (view)->window, region, TRUE);
3379                         gdk_region_destroy (region);
3380                 }
3381         }
3382
3383         /* Free the old list, now that we're done with it. */
3384         g_list_foreach (old_list, (GFunc) selection_free, NULL);
3385 }
3386
3387 static void
3388 compute_selections (EvView   *view,
3389                     GdkPoint *start,
3390                     GdkPoint *stop)
3391 {
3392         GList *list;
3393
3394         if (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE)
3395                 list = compute_new_selection_rect (view, start, stop);
3396         else
3397                 list = compute_new_selection_text (view, start, stop);
3398         merge_selection_region (view, list);
3399 }
3400
3401 /* Free's the selection.  It's up to the caller to queue redraws if needed.
3402  */
3403 static void
3404 selection_free (EvViewSelection *selection)
3405 {
3406         if (selection->covered_region)
3407                 gdk_region_destroy (selection->covered_region);
3408         g_free (selection);
3409 }
3410
3411 static void
3412 clear_selection (EvView *view)
3413 {
3414         g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL);
3415         view->selection_info.selections = NULL;
3416         view->selection_info.in_selection = FALSE;
3417 }
3418
3419
3420 void
3421 ev_view_select_all (EvView *view)
3422 {
3423         int n_pages, i;
3424
3425         /* Disable selection on rotated pages for the 0.4.0 series */
3426         if (view->rotation != 0)
3427                 return;
3428
3429         clear_selection (view);
3430
3431         n_pages = ev_page_cache_get_n_pages (view->page_cache);
3432         for (i = 0; i < n_pages; i++) {
3433                 int width, height;
3434                 EvViewSelection *selection;
3435
3436                 ev_page_cache_get_size (view->page_cache,
3437                                         i,
3438                                         view->rotation,
3439                                         1.0, &width, &height);
3440
3441                 selection = g_new0 (EvViewSelection, 1);
3442                 selection->page = i;
3443                 selection->rect.x1 = selection->rect.y1 = 0;
3444                 selection->rect.x2 = width;
3445                 selection->rect.y2 = height;
3446
3447                 view->selection_info.selections = g_list_append (view->selection_info.selections, selection);
3448         }
3449
3450         ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, view->selection_info.selections);
3451         gtk_widget_queue_draw (GTK_WIDGET (view));
3452 }
3453
3454 static char *
3455 get_selected_text (EvView *ev_view)
3456 {
3457         GString *text;
3458         GList *l;
3459
3460         text = g_string_new (NULL);
3461
3462         ev_document_doc_mutex_lock ();
3463
3464         for (l = ev_view->selection_info.selections; l != NULL; l = l->next) {
3465                 EvViewSelection *selection = (EvViewSelection *)l->data;
3466                 char *tmp;
3467
3468                 tmp = ev_document_get_text (ev_view->document,
3469                                             selection->page,
3470                                             &selection->rect);
3471                 g_string_append (text, tmp);
3472                 g_free (tmp);
3473         }
3474
3475         ev_document_doc_mutex_unlock ();
3476
3477         return g_string_free (text, FALSE);
3478 }
3479
3480 void
3481 ev_view_copy (EvView *ev_view)
3482 {
3483         GtkClipboard *clipboard;
3484         char *text;
3485
3486         if (!ev_document_can_get_text (ev_view->document)) {
3487                 return;
3488         }
3489
3490         text = get_selected_text (ev_view);
3491         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
3492                                               GDK_SELECTION_CLIPBOARD);
3493         gtk_clipboard_set_text (clipboard, text, -1);
3494         g_free (text);
3495 }
3496
3497 static void
3498 ev_view_primary_get_cb (GtkClipboard     *clipboard,
3499                         GtkSelectionData *selection_data,
3500                         guint             info,
3501                         gpointer          data)
3502 {
3503         EvView *ev_view = EV_VIEW (data);
3504         char *text;
3505
3506         if (!ev_document_can_get_text (ev_view->document)) {
3507                 return;
3508         }
3509
3510         text = get_selected_text (ev_view);
3511         gtk_selection_data_set_text (selection_data, text, -1);
3512         g_free (text);
3513 }
3514
3515 static void
3516 ev_view_primary_clear_cb (GtkClipboard *clipboard,
3517                           gpointer      data)
3518 {
3519         EvView *view = EV_VIEW (data);
3520
3521         clear_selection (view);
3522 }
3523
3524 static void
3525 ev_view_update_primary_selection (EvView *ev_view)
3526 {
3527         GtkClipboard *clipboard;
3528
3529         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
3530                                               GDK_SELECTION_PRIMARY);
3531
3532         if (ev_view->selection_info.selections) {
3533                 if (!gtk_clipboard_set_with_owner (clipboard,
3534                                                    targets,
3535                                                    G_N_ELEMENTS (targets),
3536                                                    ev_view_primary_get_cb,
3537                                                    ev_view_primary_clear_cb,
3538                                                    G_OBJECT (ev_view)))
3539                         ev_view_primary_clear_cb (clipboard, ev_view);
3540         } else {
3541                 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
3542                         gtk_clipboard_clear (clipboard);
3543         }
3544 }
3545
3546 /*** Cursor operations ***/
3547
3548 static GdkCursor *
3549 ev_view_create_invisible_cursor(void)
3550 {
3551        GdkBitmap *empty;
3552        GdkColor black = { 0, 0, 0, 0 };
3553        static char bits[] = { 0x00 };
3554
3555        empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
3556
3557        return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
3558 }
3559
3560 static void
3561 ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
3562 {
3563         GdkCursor *cursor = NULL;
3564         GdkDisplay *display;
3565         GtkWidget *widget;
3566
3567         if (view->cursor == new_cursor) {
3568                 return;
3569         }
3570
3571         widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
3572         display = gtk_widget_get_display (widget);
3573         view->cursor = new_cursor;
3574
3575         switch (new_cursor) {
3576                 case EV_VIEW_CURSOR_NORMAL:
3577                         gdk_window_set_cursor (widget->window, NULL);
3578                         break;
3579                 case EV_VIEW_CURSOR_IBEAM:
3580                         cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
3581                         break;
3582                 case EV_VIEW_CURSOR_LINK:
3583                         cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
3584                         break;
3585                 case EV_VIEW_CURSOR_WAIT:
3586                         cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
3587                         break;
3588                 case EV_VIEW_CURSOR_HIDDEN:
3589                         cursor = ev_view_create_invisible_cursor ();
3590                         break;
3591                 case EV_VIEW_CURSOR_DRAG:
3592                         cursor = gdk_cursor_new_for_display (display, GDK_FLEUR);
3593                         break;
3594         }
3595
3596         if (cursor) {
3597                 gdk_window_set_cursor (widget->window, cursor);
3598                 gdk_cursor_unref (cursor);
3599                 gdk_flush();
3600         }
3601 }
3602
3603 void
3604 ev_view_hide_cursor (EvView *view)
3605 {
3606        ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN);
3607 }
3608
3609 void
3610 ev_view_show_cursor (EvView *view)
3611 {
3612        ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
3613 }
3614
3615 gboolean
3616 ev_view_next_page (EvView *view)
3617 {
3618         int page;
3619
3620         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
3621
3622         page = ev_page_cache_get_current_page (view->page_cache);
3623         page = ev_view_get_dual_page (view) ? page + 2 : page + 1;
3624
3625         if (page < ev_page_cache_get_n_pages (view->page_cache)) {
3626                 ev_page_cache_set_current_page (view->page_cache, page);
3627                 return TRUE;
3628         } else {
3629                 return FALSE;
3630         }
3631 }
3632
3633 gboolean
3634 ev_view_previous_page (EvView *view)
3635 {
3636         int page;
3637
3638         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
3639
3640         page = ev_page_cache_get_current_page (view->page_cache);
3641         page = ev_view_get_dual_page (view) ? page - 2 : page - 1;
3642
3643         if (page >= 0) {
3644                 ev_page_cache_set_current_page (view->page_cache, page);
3645                 return TRUE;
3646         } else {
3647                 return FALSE;
3648         }
3649 }
3650
3651 /*** Enum description for usage in signal ***/
3652
3653 GType
3654 ev_sizing_mode_get_type (void)
3655 {
3656   static GType etype = 0;
3657   if (etype == 0) {
3658     static const GEnumValue values[] = {
3659       { EV_SIZING_FIT_WIDTH, "EV_SIZING_FIT_WIDTH", "fit-width" },
3660       { EV_SIZING_BEST_FIT, "EV_SIZING_BEST_FIT", "best-fit" },
3661       { EV_SIZING_FREE, "EV_SIZING_FREE", "free" },
3662       { 0, NULL, NULL }
3663     };
3664     etype = g_enum_register_static ("EvSizingMode", values);
3665   }
3666   return etype;
3667 }