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