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