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