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