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