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