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