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