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