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