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