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