]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
9ff59dbbe6c22b7ec7b0889b10bc4ac3bad39b95
[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 <gtk/gtkalignment.h>
23 #include <glib/gi18n.h>
24 #include <gtk/gtkbindings.h>
25 #include <gtk/gtkselection.h>
26 #include <gtk/gtkclipboard.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <libgnomevfs/gnome-vfs-utils.h>
29
30 #include "ev-marshal.h"
31 #include "ev-view.h"
32 #include "ev-document-find.h"
33 #include "ev-document-misc.h"
34 #include "ev-debug.h"
35 #include "ev-job-queue.h"
36 #include "ev-page-cache.h"
37 #include "ev-pixbuf-cache.h"
38
39 #define EV_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass))
40 #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW))
41 #define EV_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_VIEW, EvViewClass))
42
43 enum {
44         PROP_0,
45         PROP_STATUS,
46         PROP_FIND_STATUS,
47         PROP_CONTINUOUS,
48         PROP_DUAL_PAGE,
49         PROP_FULLSCREEN,
50         PROP_PRESENTATION,
51         PROP_SIZING_MODE,
52         PROP_ZOOM,
53 };
54
55 enum {
56         SIGNAL_BINDING_ACTIVATED,
57         SIGNAL_ZOOM_INVALID,
58         N_SIGNALS,
59 };
60
61 enum {
62         TARGET_STRING,
63         TARGET_TEXT,
64         TARGET_COMPOUND_TEXT,
65         TARGET_UTF8_STRING,
66         TARGET_TEXT_BUFFER_CONTENTS
67 };
68
69 static const GtkTargetEntry targets[] = {
70         { "STRING", 0, TARGET_STRING },
71         { "TEXT",   0, TARGET_TEXT },
72         { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
73         { "UTF8_STRING", 0, TARGET_UTF8_STRING },
74 };
75
76 static guint signals[N_SIGNALS];
77
78 typedef enum {
79         EV_VIEW_CURSOR_NORMAL,
80         EV_VIEW_CURSOR_LINK,
81         EV_VIEW_CURSOR_WAIT,
82         EV_VIEW_CURSOR_HIDDEN,
83         EV_VIEW_CURSOR_DRAG
84 } EvViewCursor;
85
86 #define ZOOM_IN_FACTOR  1.2
87 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)
88
89 #define MIN_SCALE 0.05409
90 #define MAX_SCALE 4.0
91
92 typedef struct {
93         EvRectangle rect;
94         int page;
95 } EvViewSelection;
96
97 typedef struct {
98         gboolean dragging;
99         GdkPoint start;
100         gdouble hadj;
101         gdouble vadj;
102 } DragInfo;
103
104 typedef enum {
105         SCROLL_TO_KEEP_POSITION,
106         SCROLL_TO_CURRENT_PAGE,
107         SCROLL_TO_CENTER
108 } PendingScroll;
109
110 struct _EvView {
111         GtkWidget parent_instance;
112
113         EvDocument *document;
114
115         char *status;
116         char *find_status;
117
118         gint scroll_x;
119         gint scroll_y;
120
121         DragInfo drag_info;
122         gboolean pressed_button;
123         GdkPoint selection_start;
124         GList *selections;
125         EvViewCursor cursor;
126
127         GtkAdjustment *hadjustment;
128         GtkAdjustment *vadjustment;
129
130         EvPageCache *page_cache;
131         EvPixbufCache *pixbuf_cache;
132
133         gint start_page;
134         gint end_page;
135         gint current_page;
136
137         EvJobRender *current_job;
138
139         int find_page;
140         int find_result;
141         int spacing;
142
143         double scale;
144
145         gboolean continuous;
146         gboolean dual_page;
147         gboolean fullscreen;
148         gboolean presentation;
149         EvSizingMode sizing_mode;
150         
151         PendingScroll pending_scroll;
152         gboolean pending_resize;
153 };
154
155 struct _EvViewClass {
156         GtkWidgetClass parent_class;
157
158         void    (*set_scroll_adjustments) (EvView         *view,
159                                            GtkAdjustment  *hadjustment,
160                                            GtkAdjustment  *vadjustment);
161         void    (*binding_activated)      (EvView         *view,
162                                            GtkScrollType   scroll,
163                                            gboolean        horizontal);
164         void    (*zoom_invalid)           (EvView         *view);
165 };
166
167 /*** Scrolling ***/
168 static void       scroll_to_current_page                     (EvView *view, 
169                                                               GtkOrientation orientation);
170 static void       ev_view_set_scroll_adjustments             (EvView             *view,
171                                                               GtkAdjustment      *hadjustment,
172                                                               GtkAdjustment      *vadjustment);
173 static void       view_update_range_and_current_page         (EvView             *view);
174 static void       set_scroll_adjustment                      (EvView             *view,
175                                                               GtkOrientation      orientation,
176                                                               GtkAdjustment      *adjustment);
177 static void       ev_view_set_scroll_adjustments             (EvView             *view,
178                                                               GtkAdjustment      *hadjustment,
179                                                               GtkAdjustment      *vadjustment);
180 static void       add_scroll_binding_keypad                  (GtkBindingSet      *binding_set,
181                                                               guint               keyval,
182                                                               GtkScrollType       scroll,
183                                                               gboolean            horizontal);
184 static void       ev_view_binding_activated                  (EvView             *view,
185                                                               GtkScrollType       scroll,
186                                                               gboolean            horizontal);
187 static void       ensure_rectangle_is_visible                (EvView             *view,
188                                                               GdkRectangle       *rect);
189
190 /*** Geometry computations ***/
191 static void       compute_border                             (EvView             *view,
192                                                               int                 width,
193                                                               int                 height,
194                                                               GtkBorder          *border);
195 static void       get_page_y_offset                          (EvView *view,
196                                                               int page,
197                                                               double zoom,
198                                                               int *y_offset);
199 static gboolean   get_page_extents                           (EvView             *view,
200                                                               gint                page,
201                                                               GdkRectangle       *page_area,
202                                                               GtkBorder          *border);
203 static void       view_rect_to_doc_rect                      (EvView             *view,
204                                                               GdkRectangle       *view_rect,
205                                                               GdkRectangle       *page_area,
206                                                               EvRectangle        *doc_rect);
207 static void       doc_rect_to_view_rect                      (EvView             *view,
208                                                               int                 page,
209                                                               EvRectangle        *doc_rect,
210                                                               GdkRectangle       *view_rect);
211 static void       find_page_at_location                      (EvView             *view,
212                                                               gdouble             x,
213                                                               gdouble             y,
214                                                               gint               *page,
215                                                               gint               *x_offset,
216                                                               gint               *y_offset);
217
218 /*** Hyperrefs ***/
219 static EvLink*    get_link_at_location                       (EvView             *view,
220                                                               gdouble             x,
221                                                               gdouble             y);
222 static void       go_to_link                                 (EvView             *view,
223                                                               EvLink             *link);
224 static char*      status_message_from_link                   (EvView             *view,
225                                                               EvLink             *link);
226
227 /*** GtkWidget implementation ***/
228 static void       ev_view_size_request_continuous_dual_page  (EvView             *view,
229                                                               GtkRequisition     *requisition);
230 static void       ev_view_size_request_continuous            (EvView             *view,
231                                                               GtkRequisition     *requisition);
232 static void       ev_view_size_request_dual_page             (EvView             *view,
233                                                               GtkRequisition     *requisition);
234 static void       ev_view_size_request_single_page           (EvView             *view,
235                                                               GtkRequisition     *requisition);
236 static void       ev_view_size_request                       (GtkWidget          *widget,
237                                                               GtkRequisition     *requisition);
238 static void       ev_view_size_allocate                      (GtkWidget          *widget,
239                                                               GtkAllocation      *allocation);
240 static void       ev_view_realize                            (GtkWidget          *widget);
241 static void       ev_view_unrealize                          (GtkWidget          *widget);
242 static gboolean   ev_view_scroll_event                       (GtkWidget          *widget,
243                                                               GdkEventScroll     *event);
244 static gboolean   ev_view_expose_event                       (GtkWidget          *widget,
245                                                               GdkEventExpose     *event);
246 static gboolean   ev_view_button_press_event                 (GtkWidget          *widget,
247                                                               GdkEventButton     *event);
248 static gboolean   ev_view_motion_notify_event                (GtkWidget          *widget,
249                                                               GdkEventMotion     *event);
250 static gboolean   ev_view_button_release_event               (GtkWidget          *widget,
251                                                               GdkEventButton     *event);
252
253 /*** Drawing ***/
254 static guint32    ev_gdk_color_to_rgb                        (const GdkColor     *color);
255 static void       draw_rubberband                            (GtkWidget          *widget,
256                                                               GdkWindow          *window,
257                                                               const GdkRectangle *rect,
258                                                               guchar              alpha);
259 static void       highlight_find_results                     (EvView             *view,
260                                                               int                 page);
261 static void       draw_one_page                              (EvView             *view,
262                                                               gint                page,
263                                                               GdkRectangle       *page_area,
264                                                               GtkBorder          *border,
265                                                               GdkRectangle       *expose_area);
266
267 /*** Callbacks ***/
268 static void       find_changed_cb                            (EvDocument         *document,
269                                                               int                 page,
270                                                               EvView             *view);
271 static void       job_finished_cb                            (EvPixbufCache      *pixbuf_cache,
272                                                               EvView             *view);
273 static void       page_changed_cb                            (EvPageCache        *page_cache,
274                                                               int                 new_page,
275                                                               EvView             *view);
276 static void       on_adjustment_value_changed                (GtkAdjustment      *adjustment,
277                                                               EvView             *view);
278
279 /*** GObject ***/
280 static void       ev_view_finalize                           (GObject            *object);
281 static void       ev_view_destroy                            (GtkObject          *object);
282 static void       ev_view_set_property                       (GObject            *object,
283                                                               guint               prop_id,
284                                                               const GValue       *value,
285                                                               GParamSpec         *pspec);
286 static void       ev_view_get_property                       (GObject            *object,
287                                                               guint               prop_id,
288                                                               GValue             *value,
289                                                               GParamSpec         *pspec);
290 static void       ev_view_class_init                         (EvViewClass        *class);
291 static void       ev_view_init                               (EvView             *view);
292
293 /*** Zoom and sizing ***/
294 static double   zoom_for_size_fit_width                      (int doc_width,
295                                                               int doc_height,
296                                                               int target_width,
297                                                               int target_height,
298                                                               int vsb_width);
299 static double   zoom_for_size_best_fit                       (int doc_width,
300                                                               int doc_height,
301                                                               int target_width,
302                                                               int target_height,
303                                                               int vsb_width,
304                                                               int hsb_width);
305 static void     ev_view_zoom_for_size_presentation           (EvView *view,
306                                                               int     width,
307                                                               int     height);
308 static void     ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
309                                                                 int     width,
310                                                                 int     height,
311                                                                 int     vsb_width,
312                                                                 int     hsb_height);
313 static void     ev_view_zoom_for_size_continuous               (EvView *view,
314                                                                 int     width,
315                                                                 int     height,
316                                                                 int     vsb_width,
317                                                                 int     hsb_height);
318 static void     ev_view_zoom_for_size_dual_page                (EvView *view,
319                                                                 int     width,
320                                                                 int     height,
321                                                                 int     vsb_width,
322                                                                 int     hsb_height);
323 static void     ev_view_zoom_for_size_single_page              (EvView *view,
324                                                                 int     width,
325                                                                 int     height,
326                                                                 int     vsb_width,
327                                                                 int     hsb_height);
328 /*** Cursors ***/
329 static GdkCursor* ev_view_create_invisible_cursor            (void);
330 static void       ev_view_set_cursor                         (EvView             *view,
331                                                               EvViewCursor        new_cursor);
332
333 /*** Status messages ***/
334 static void       ev_view_set_status                         (EvView             *view,
335                                                               const char         *message);
336 static void       update_find_status_message                 (EvView             *view);
337 static void       ev_view_set_find_status                    (EvView             *view,
338                                                               const char         *message);
339 /*** Find ***/
340 static void       jump_to_find_result                        (EvView             *view);
341 static void       jump_to_find_page                          (EvView             *view);
342
343 /*** Selection ***/
344 static void       compute_selections                         (EvView             *view,
345                                                               GdkRectangle       *view_rect);
346 static void       clear_selection                            (EvView             *view);
347 static char*      get_selected_text                          (EvView             *ev_view);
348 static void       ev_view_primary_get_cb                     (GtkClipboard       *clipboard,
349                                                               GtkSelectionData   *selection_data,
350                                                               guint               info,
351                                                               gpointer            data);
352 static void       ev_view_primary_clear_cb                   (GtkClipboard       *clipboard,
353                                                               gpointer            data);
354 static void       ev_view_update_primary_selection           (EvView             *ev_view);
355
356                                                 
357 G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET)
358
359 static void
360 scroll_to_current_page (EvView *view, GtkOrientation orientation)
361 {
362         GdkRectangle page_area;
363         GtkBorder border;
364
365         get_page_extents (view, view->current_page, &page_area, &border);
366
367         if (orientation == GTK_ORIENTATION_VERTICAL) {  
368                 if (view->continuous) {
369                         gtk_adjustment_clamp_page (view->vadjustment,
370                                                    page_area.y - view->spacing,
371                                                    page_area.y + view->vadjustment->page_size);
372                 } else {
373                         gtk_adjustment_set_value (view->vadjustment,
374                                                   view->vadjustment->lower);
375                 }
376         } else {
377                 if (view->dual_page) {
378                         gtk_adjustment_clamp_page (view->hadjustment,
379                                                    page_area.x, 
380                                                    page_area.x + view->hadjustment->page_size);
381                 } else {
382                         gtk_adjustment_set_value (view->hadjustment,
383                                                   CLAMP (view->hadjustment->value, 
384                                                   view->hadjustment->lower,
385                                                   view->hadjustment->upper - 
386                                                   view->hadjustment->page_size));
387                 }
388         }
389 }
390
391 static void
392 view_set_adjustment_values (EvView         *view,
393                             GtkOrientation  orientation)
394 {
395         GtkWidget *widget = GTK_WIDGET (view);
396         GtkAdjustment *adjustment;
397         int requisition;
398         int allocation;
399
400         double factor;
401         gint new_value;
402
403         if (orientation == GTK_ORIENTATION_HORIZONTAL)  {
404                 requisition = widget->requisition.width;
405                 allocation = widget->allocation.width;
406                 adjustment = view->hadjustment;
407         } else {
408                 requisition = widget->requisition.height;
409                 allocation = widget->allocation.height;
410                 adjustment = view->vadjustment;
411         }
412
413         if (!adjustment)
414                 return;
415         
416         factor = 1.0;
417         switch (view->pending_scroll) {
418                 case SCROLL_TO_KEEP_POSITION: 
419                         factor = (adjustment->value) / adjustment->upper;
420                         break;
421                 case SCROLL_TO_CURRENT_PAGE: 
422                         break;
423                 case SCROLL_TO_CENTER: 
424                         factor = (adjustment->value + adjustment->page_size * 0.5) / adjustment->upper;
425                         break;
426         }
427
428         adjustment->page_size = allocation;
429         adjustment->step_increment = allocation * 0.1;
430         adjustment->page_increment = allocation * 0.9;
431         adjustment->lower = 0;
432         adjustment->upper = MAX (allocation, requisition);
433
434         /* 
435          * We add 0.5 to the values before to average out our rounding errors.
436          */
437         switch (view->pending_scroll) {
438                 case SCROLL_TO_KEEP_POSITION: 
439                         new_value = CLAMP (adjustment->upper * factor + 0.5, 0, adjustment->upper - adjustment->page_size);
440                         gtk_adjustment_set_value (adjustment, (int)new_value);
441                         break;
442                 case SCROLL_TO_CURRENT_PAGE: 
443                         scroll_to_current_page (view, orientation);
444                         break;
445                 case SCROLL_TO_CENTER: 
446                         new_value = CLAMP (adjustment->upper * factor - adjustment->page_size * 0.5 + 0.5,
447                                            0, adjustment->upper - adjustment->page_size);
448                         gtk_adjustment_set_value (adjustment, (int)new_value);
449                         break;
450         }
451
452         gtk_adjustment_changed (adjustment);
453 }
454
455 static void
456 view_update_range_and_current_page (EvView *view)
457 {
458         /* Presentation trumps all other modes */
459         if (view->presentation) {
460                 view->start_page = view->current_page;
461                 view->end_page = view->current_page;
462         } else if (view->continuous) {
463                 GdkRectangle current_area, unused, page_area;
464                 GtkBorder border;
465                 gint current_page;
466                 gboolean found = FALSE;
467                 int i;
468                 
469                 if (!(view->vadjustment && view->hadjustment))
470                         return;
471                 
472                 current_area.x = view->hadjustment->value;
473                 current_area.width = view->hadjustment->upper;
474                 current_area.y = view->vadjustment->value;
475                 current_area.height = view->vadjustment->page_size;
476
477                 for (i = 0; i < ev_page_cache_get_n_pages (view->page_cache); i++) {
478
479                         get_page_extents (view, i, &page_area, &border);
480                         
481                         if (gdk_rectangle_intersect (&current_area, &page_area, &unused)) {
482                                 if (! found) {
483                                         view->start_page = i;
484                                         found = TRUE;
485                                         
486                                 }
487                                 view->end_page = i;
488                         } else if (found) {
489                                 break;
490                         }
491                 }
492                 
493                 current_page = ev_page_cache_get_current_page (view->page_cache);
494
495                 if (current_page < view->start_page || current_page > view->end_page) {
496                         view->current_page = view->start_page;
497                         ev_page_cache_set_current_page (view->page_cache, view->start_page);
498                 }                       
499         } else {
500                 if (view->dual_page) {
501                         if (view->current_page % 2 == 0) {
502                                 view->start_page = view->current_page;
503                                 if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache))
504                                         view->end_page = view->start_page + 1;
505                         } else {
506                                 view->start_page = view->current_page - 1;
507                                 view->end_page = view->current_page;
508                         }
509                 } else {
510                         view->start_page = view->current_page;
511                         view->end_page = view->current_page;
512                 }
513         }
514
515         ev_pixbuf_cache_set_page_range (view->pixbuf_cache,
516                                         view->start_page,
517                                         view->end_page,
518                                         view->scale);   
519 }
520
521 static void
522 set_scroll_adjustment (EvView *view,
523                        GtkOrientation  orientation,
524                        GtkAdjustment  *adjustment)
525 {
526         GtkAdjustment **to_set;
527
528         if (orientation == GTK_ORIENTATION_HORIZONTAL)
529                 to_set = &view->hadjustment;
530         else
531                 to_set = &view->vadjustment;
532
533         if (*to_set != adjustment) {
534                 if (*to_set) {
535                         g_signal_handlers_disconnect_by_func (*to_set,
536                                                               (gpointer) on_adjustment_value_changed,
537                                                               view);
538                         g_object_unref (*to_set);
539                 }
540
541                 *to_set = adjustment;
542                 view_set_adjustment_values (view, orientation);
543
544                 if (*to_set) {
545                         g_object_ref (*to_set);
546                         g_signal_connect (*to_set, "value_changed",
547                                           G_CALLBACK (on_adjustment_value_changed), view);
548                 }
549         }
550 }
551
552 static void
553 ev_view_set_scroll_adjustments (EvView *view,
554                                 GtkAdjustment  *hadjustment,
555                                 GtkAdjustment  *vadjustment)
556 {
557         set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
558         set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
559
560         on_adjustment_value_changed (NULL, view);
561 }
562
563 static void
564 add_scroll_binding_keypad (GtkBindingSet  *binding_set,
565                            guint           keyval,
566                            GtkScrollType   scroll,
567                            gboolean        horizontal)
568 {
569   guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
570
571   gtk_binding_entry_add_signal (binding_set, keyval, 0,
572                                 "binding_activated", 2,
573                                 GTK_TYPE_SCROLL_TYPE, scroll,
574                                 G_TYPE_BOOLEAN, horizontal);
575   gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
576                                 "binding_activated", 2,
577                                 GTK_TYPE_SCROLL_TYPE, scroll,
578                                 G_TYPE_BOOLEAN, horizontal);
579 }
580
581 void
582 ev_view_scroll (EvView        *view,
583                 EvScrollType   scroll)
584 {
585         GtkAdjustment *adjustment;
586         double value, increment;
587         gboolean first_page = FALSE;
588         gboolean last_page = FALSE;
589
590         /* Assign values for increment and vertical adjustment */
591         adjustment = view->vadjustment;
592         increment = adjustment->page_size * 0.75;
593         value = adjustment->value;
594
595         /* Assign boolean for first and last page */
596         if (view->current_page == 0)
597                 first_page = TRUE;
598         if (view->current_page == ev_page_cache_get_n_pages (view->page_cache) - 1)
599                 last_page = TRUE;
600
601         switch (scroll) {
602                 case EV_SCROLL_PAGE_BACKWARD:
603                         /* Do not jump backwards if at the first page */
604                         if (value == (adjustment->lower) && first_page) {
605                                 /* Do nothing */
606                                 /* At the top of a page, assign the upper bound limit of previous page */
607                         } else if (value == (adjustment->lower)) {
608                                 value = adjustment->upper - adjustment->page_size;
609                                 ev_page_cache_set_current_page (view->page_cache, view->current_page - 1);
610                                 /* Jump to the top */
611                         } else {
612                                 value = MAX (value - increment, adjustment->lower);
613                         }
614                         break;
615                 case EV_SCROLL_PAGE_FORWARD:
616                         /* Do not jump forward if at the last page */
617                         if (value == (adjustment->upper - adjustment->page_size) && last_page) {
618                                 /* Do nothing */
619                         /* At the bottom of a page, assign the lower bound limit of next page */
620                         } else if (value == (adjustment->upper - adjustment->page_size)) {
621                                 value = 0;
622                                 ev_page_cache_set_current_page (view->page_cache, view->current_page + 1);
623                         /* Jump to the bottom */
624                         } else {
625                                 value = MIN (value + increment, adjustment->upper - adjustment->page_size);
626                         }
627                         break;
628                 default:
629                         break;
630         }
631
632         gtk_adjustment_set_value (adjustment, value);
633 }
634
635 static void
636 ev_view_binding_activated (EvView *view,
637                            GtkScrollType scroll,
638                            gboolean horizontal)
639 {
640         GtkAdjustment *adjustment;
641         double value;
642         
643         if (view->presentation) {
644                 switch (scroll) {
645                         case GTK_SCROLL_STEP_BACKWARD:
646                                 ev_page_cache_prev_page (view->page_cache);
647                                 break;
648                         case GTK_SCROLL_STEP_FORWARD:
649                                 ev_page_cache_next_page (view->page_cache);
650                                 break;
651                         default:
652                                 break;
653                 }
654                 return;
655         }
656
657         if (horizontal) {
658                 adjustment = view->hadjustment;
659         } else {
660                 adjustment = view->vadjustment;
661         }
662
663         value = adjustment->value;
664
665         switch (scroll) {
666                 case GTK_SCROLL_STEP_BACKWARD:
667                         value -= adjustment->step_increment;
668                         break;
669                 case GTK_SCROLL_STEP_FORWARD:
670                         value += adjustment->step_increment;
671                         break;
672                 default:
673                         break;
674         }
675
676         value = CLAMP (value, adjustment->lower,
677                        adjustment->upper - adjustment->page_size);
678
679         gtk_adjustment_set_value (adjustment, value);
680 }
681
682 #define MARGIN 5
683
684 static void
685 ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect)
686 {
687         GtkWidget *widget = GTK_WIDGET (view);
688         GtkAdjustment *adjustment;
689         int value;
690
691         adjustment = view->vadjustment;
692
693         if (rect->y < adjustment->value) {
694                 value = MAX (adjustment->lower, rect->y - MARGIN);
695                 gtk_adjustment_set_value (view->vadjustment, value);
696         } else if (rect->y + rect->height >
697                    adjustment->value + widget->allocation.height) {
698                 value = MIN (adjustment->upper, rect->y + rect->height -
699                              widget->allocation.height + MARGIN);
700                 gtk_adjustment_set_value (view->vadjustment, value);
701         }
702
703         adjustment = view->hadjustment;
704
705         if (rect->x < adjustment->value) {
706                 value = MAX (adjustment->lower, rect->x - MARGIN);
707                 gtk_adjustment_set_value (view->hadjustment, value);
708         } else if (rect->x + rect->height >
709                    adjustment->value + widget->allocation.width) {
710                 value = MIN (adjustment->upper, rect->x + rect->width -
711                              widget->allocation.width + MARGIN);
712                 gtk_adjustment_set_value (view->hadjustment, value);
713         }
714 }
715
716 /*** Geometry computations ***/
717
718 static void
719 compute_border (EvView *view, int width, int height, GtkBorder *border)
720 {
721         if (view->presentation) {
722                 border->left = 0;
723                 border->right = 0;
724                 border->top = 0;
725                 border->bottom = 0;
726         } else {
727                 ev_document_misc_get_page_border_size (width, height, border);
728         }
729 }
730
731 static void       get_page_y_offset                          (EvView *view,
732                                                               int page,
733                                                               double zoom,
734                                                               int *y_offset)
735 {
736         int max_width, offset;
737         GtkBorder border;
738         
739         g_return_if_fail (y_offset != NULL);
740                 
741         ev_page_cache_get_max_width (view->page_cache, zoom, &max_width);
742
743         compute_border (view, max_width, max_width, &border);
744         
745         if (view->dual_page) {
746                 ev_page_cache_get_height_to_page (view->page_cache, page, zoom, NULL, &offset);
747                 offset += (page / 2 + 1) * view->spacing + (page / 2) * (border.top + border.bottom);
748         } else {
749                 ev_page_cache_get_height_to_page (view->page_cache, page, zoom, &offset, NULL);
750                 offset += (page + 1) * view->spacing + page * (border.top + border.bottom);
751         }
752         
753         *y_offset = offset;
754         return;
755 }
756
757 static gboolean
758 get_page_extents (EvView       *view,
759                   gint          page,
760                   GdkRectangle *page_area,
761                   GtkBorder    *border)
762 {
763         GtkWidget *widget;
764         int width, height;
765
766         widget = GTK_WIDGET (view);
767
768         /* Get the size of the page */
769         ev_page_cache_get_size (view->page_cache, page,
770                                 view->scale,
771                                 &width, &height);
772         compute_border (view, width, height, border);
773         page_area->width = width + border->left + border->right;
774         page_area->height = height + border->top + border->bottom;
775
776         if (view->presentation) {
777                 page_area->x = (MAX (0, widget->allocation.width - width))/2;
778                 page_area->y = (MAX (0, widget->allocation.height - height))/2;
779         } else if (view->continuous) {
780                 gint max_width;
781                 gint x, y;
782
783                 ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width);
784                 max_width = max_width + border->left + border->right;
785                 /* Get the location of the bounding box */
786                 if (view->dual_page) {
787                         x = view->spacing + (page % 2) * (max_width + view->spacing);
788                         x = x + MAX (0, widget->allocation.width - (max_width * 2 + view->spacing * 3)) / 2;
789                         if (page % 2 == 0)
790                                 x = x + (max_width - width - border->left - border->right);
791                 } else {
792                         x = view->spacing;
793                         x = x + MAX (0, widget->allocation.width - (width + view->spacing * 2)) / 2;
794                 }
795
796                 get_page_y_offset (view, page, view->scale, &y);
797
798                 page_area->x = x;
799                 page_area->y = y;
800         } else {
801                 gint x, y;
802                 if (view->dual_page) {
803                         gint width_2, height_2;
804                         gint max_width = width;
805                         gint max_height = height;
806                         GtkBorder overall_border;
807                         gint other_page;
808
809                         other_page = page ^ 1;
810
811                         /* First, we get the bounding box of the two pages */
812                         if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
813                                 ev_page_cache_get_size (view->page_cache,
814                                                         other_page,
815                                                         view->scale,
816                                                         &width_2, &height_2);
817                                 if (width_2 > width)
818                                         max_width = width_2;
819                                 if (height_2 > height)
820                                         max_height = height_2;
821                         }
822                         compute_border (view, max_width, max_height, &overall_border);
823
824                         /* Find the offsets */
825                         x = view->spacing;
826                         y = view->spacing;
827
828                         /* Adjust for being the left or right page */
829                         if (page % 2 == 0)
830                                 x = x + max_width - width;
831                         else
832                                 x = x + (max_width + overall_border.left + overall_border.right) + view->spacing;
833
834                         y = y + (max_height - height)/2;
835
836                         /* Adjust for extra allocation */
837                         x = x + MAX (0, widget->allocation.width -
838                                      ((max_width + overall_border.left + overall_border.right) * 2 + view->spacing * 3))/2;
839                         y = y + MAX (0, widget->allocation.height - (height + view->spacing * 2))/2;
840                 } else {
841                         x = view->spacing;
842                         y = view->spacing;
843
844                         /* Adjust for extra allocation */
845                         x = x + MAX (0, widget->allocation.width - (width + border->left + border->right + view->spacing * 2))/2;
846                         y = y + MAX (0, widget->allocation.height - (height + border->top + border->bottom +  view->spacing * 2))/2;
847                 }
848
849                 page_area->x = x;
850                 page_area->y = y;
851         }
852
853         return TRUE;
854 }
855
856 static void
857 view_rect_to_doc_rect (EvView *view,
858                        GdkRectangle *view_rect,
859                        GdkRectangle *page_area,
860                        EvRectangle  *doc_rect)
861 {
862         doc_rect->x1 = floor ((view_rect->x - page_area->x) / view->scale);
863         doc_rect->y1 = floor ((view_rect->y - page_area->y) / view->scale);
864         doc_rect->x2 = doc_rect->x1 + ceil (view_rect->width / view->scale);
865         doc_rect->y2 = doc_rect->y1 + ceil (view_rect->height / view->scale);
866 }
867
868 static void
869 doc_rect_to_view_rect (EvView       *view,
870                        int           page,
871                        EvRectangle  *doc_rect,
872                        GdkRectangle *view_rect)
873 {
874         GdkRectangle page_area;
875         GtkBorder border;
876         int width, height;
877
878         get_page_extents (view, page, &page_area, &border);
879
880         width = doc_rect->x2 - doc_rect->x1;
881         height = doc_rect->y2 - doc_rect->y1;
882         view_rect->x = floor (doc_rect->x1 * view->scale) + page_area.x;
883         view_rect->y = floor (doc_rect->y1 * view->scale) + page_area.y;
884         view_rect->width = ceil (width * view->scale);
885         view_rect->height = ceil (height * view->scale);
886 }
887
888 static void
889 find_page_at_location (EvView  *view,
890                        gdouble  x,
891                        gdouble  y,
892                        gint    *page,
893                        gint    *x_offset,
894                        gint    *y_offset)
895 {
896         int i;
897
898         if (view->document == NULL)
899                 return;
900
901         g_assert (page);
902         g_assert (x_offset);
903         g_assert (y_offset);
904
905         for (i = view->start_page; i <= view->end_page; i++) {
906                 GdkRectangle page_area;
907                 GtkBorder border;
908
909                 if (! get_page_extents (view, i, &page_area, &border))
910                         continue;
911
912                 if ((x >= page_area.x + border.left) &&
913                     (x < page_area.x + page_area.width - border.right) &&
914                     (y >= page_area.y + border.top) &&
915                     (y < page_area.y + page_area.height - border.bottom)) {
916                         *page = i;
917                         *x_offset = x - (page_area.x + border.left);
918                         *y_offset = y - (page_area.y + border.top);
919                         return;
920                 }
921         }
922
923         *page = -1;
924 }
925
926 /*** Hyperref ***/
927 static EvLink *
928 get_link_at_location (EvView  *view,
929                       gdouble  x,
930                       gdouble  y)
931 {
932         gint page = -1;
933         gint x_offset = 0, y_offset = 0;
934         GList *link_mapping;
935
936         find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
937
938         if (page == -1)
939                 return NULL;
940
941         link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page);
942
943         if (link_mapping)
944                 return ev_link_mapping_find (link_mapping, x_offset / view->scale, y_offset / view->scale);
945         else
946                 return NULL;
947 }
948
949 /* FIXME: standardize this sometime */
950 static void
951 go_to_link (EvView *view, EvLink *link)
952 {
953         EvLinkType type;
954         const char *uri;
955         int page;
956
957         type = ev_link_get_link_type (link);
958
959         switch (type) {
960                 case EV_LINK_TYPE_TITLE:
961                         break;
962                 case EV_LINK_TYPE_PAGE:
963                         page = ev_link_get_page (link);
964                         ev_page_cache_set_current_page (view->page_cache, page);
965                         break;
966                 case EV_LINK_TYPE_EXTERNAL_URI:
967                         uri = ev_link_get_uri (link);
968                         gnome_vfs_url_show (uri);
969                         break;
970         }
971 }
972
973 static char *
974 status_message_from_link (EvView *view, EvLink *link)
975 {
976         EvLinkType type;
977         char *msg = NULL;
978         char *page_label;
979
980         type = ev_link_get_link_type (link);
981
982         switch (type) {
983                 case EV_LINK_TYPE_TITLE:
984                         if (ev_link_get_title (link))
985                                 msg = g_strdup (ev_link_get_title (link));
986                         break;
987                 case EV_LINK_TYPE_PAGE:
988                         page_label = ev_page_cache_get_page_label (view->page_cache, ev_link_get_page (link));
989                         msg = g_strdup_printf (_("Go to page %s"), page_label);
990                         g_free (page_label);
991                         break;
992                 case EV_LINK_TYPE_EXTERNAL_URI:
993                         msg = g_strdup (ev_link_get_uri (link));
994                         break;
995                 default:
996                         break;
997         }
998
999         return msg;
1000 }
1001
1002
1003 /*** GtkWidget implementation ***/
1004
1005 static void
1006 ev_view_size_request_continuous_dual_page (EvView         *view,
1007                                            GtkRequisition *requisition)
1008 {
1009         int max_width;
1010         gint n_pages;
1011         GtkBorder border;
1012
1013         ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width);
1014         compute_border (view, max_width, max_width, &border);
1015
1016         n_pages = ev_page_cache_get_n_pages (view->page_cache) + 1;
1017
1018         requisition->width = (max_width + border.left + border.right) * 2 + (view->spacing * 3);
1019         get_page_y_offset (view, n_pages, view->scale, &requisition->height);
1020
1021         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1022                 requisition->width = 1;
1023         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1024                 requisition->width = 1;
1025                 /* FIXME: This could actually be set on one page docs or docs
1026                  * with a strange aspect ratio. */
1027                 /* requisition->height = 1;*/
1028         }
1029 }
1030
1031 static void
1032 ev_view_size_request_continuous (EvView         *view,
1033                                  GtkRequisition *requisition)
1034 {
1035         int max_width;
1036         int n_pages;
1037         GtkBorder border;
1038
1039
1040         ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width);
1041         n_pages = ev_page_cache_get_n_pages (view->page_cache);
1042         compute_border (view, max_width, max_width, &border);
1043
1044         requisition->width = max_width + (view->spacing * 2) + border.left + border.right;
1045         get_page_y_offset (view, n_pages, view->scale, &requisition->height);
1046
1047         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1048                 requisition->width = 1;
1049         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1050                 requisition->width = 1;
1051                 /* FIXME: This could actually be set on one page docs or docs
1052                  * with a strange aspect ratio. */
1053                 /* requisition->height = 1;*/
1054         }
1055 }
1056
1057 static void
1058 ev_view_size_request_dual_page (EvView         *view,
1059                                 GtkRequisition *requisition)
1060 {
1061         GtkBorder border;
1062         gint width, height;
1063
1064         /* Find the largest of the two. */
1065         ev_page_cache_get_size (view->page_cache,
1066                                 view->current_page,
1067                                 view->scale,
1068                                 &width, &height);
1069         if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache)) {
1070                 gint width_2, height_2;
1071                 ev_page_cache_get_size (view->page_cache,
1072                                         view->current_page + 1,
1073                                         view->scale,
1074                                         &width_2, &height_2);
1075                 if (width_2 > width) {
1076                         width = width_2;
1077                         height = height_2;
1078                 }
1079         }
1080         compute_border (view, width, height, &border);
1081
1082         requisition->width = ((width + border.left + border.right) * 2) +
1083                 (view->spacing * 3);
1084         requisition->height = (height + border.top + border.bottom) +
1085                 (view->spacing * 2);
1086
1087         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1088                 requisition->width = 1;
1089         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1090                 requisition->width = 1;
1091                 requisition->height = 1;
1092         }
1093 }
1094
1095 static void
1096 ev_view_size_request_single_page (EvView         *view,
1097                                   GtkRequisition *requisition)
1098 {
1099         GtkBorder border;
1100         gint width, height;
1101
1102         ev_page_cache_get_size (view->page_cache,
1103                                 view->current_page,
1104                                 view->scale,
1105                                 &width, &height);
1106         compute_border (view, width, height, &border);
1107
1108         requisition->width = width + border.left + border.right + (2 * view->spacing);
1109         requisition->height = height + border.top + border.bottom + (2 * view->spacing);
1110
1111         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
1112                 requisition->width = 1;
1113                 requisition->height = height + border.top + border.bottom + (2 * view->spacing);
1114         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
1115                 requisition->width = 1;
1116                 requisition->height = 1;
1117         }
1118 }
1119
1120 static void
1121 ev_view_size_request (GtkWidget      *widget,
1122                       GtkRequisition *requisition)
1123 {
1124         EvView *view = EV_VIEW (widget);
1125
1126         if (view->document == NULL) {
1127                 requisition->width = 1;
1128                 requisition->height = 1;
1129                 return;
1130         }
1131
1132         if (view->presentation) {
1133                 requisition->width = 1;
1134                 requisition->height = 1;
1135                 return;
1136         }
1137
1138         if (view->continuous && view->dual_page)
1139                 ev_view_size_request_continuous_dual_page (view, requisition);
1140         else if (view->continuous)
1141                 ev_view_size_request_continuous (view, requisition);
1142         else if (view->dual_page)
1143                 ev_view_size_request_dual_page (view, requisition);
1144         else
1145                 ev_view_size_request_single_page (view, requisition);
1146 }
1147
1148 static void
1149 ev_view_size_allocate (GtkWidget      *widget,
1150                        GtkAllocation  *allocation)
1151 {
1152         EvView *view = EV_VIEW (widget);
1153         
1154         if (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
1155                   view->sizing_mode == EV_SIZING_BEST_FIT) {
1156
1157                 g_signal_emit (view, signals[SIGNAL_ZOOM_INVALID], 0);
1158                 
1159                 ev_view_size_request (widget, &widget->requisition);
1160         }
1161         
1162         view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
1163         view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
1164
1165         view->pending_scroll = SCROLL_TO_KEEP_POSITION;
1166         view->pending_resize = FALSE;
1167
1168         if (view->document)
1169                 view_update_range_and_current_page (view);              
1170
1171         GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
1172 }
1173
1174 static void
1175 ev_view_realize (GtkWidget *widget)
1176 {
1177         EvView *view = EV_VIEW (widget);
1178         GdkWindowAttr attributes;
1179
1180         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1181
1182
1183         attributes.window_type = GDK_WINDOW_CHILD;
1184         attributes.wclass = GDK_INPUT_OUTPUT;
1185         attributes.visual = gtk_widget_get_visual (widget);
1186         attributes.colormap = gtk_widget_get_colormap (widget);
1187
1188         attributes.x = widget->allocation.x;
1189         attributes.y = widget->allocation.y;
1190         attributes.width = widget->allocation.width;
1191         attributes.height = widget->allocation.height;
1192         attributes.event_mask = GDK_EXPOSURE_MASK |
1193                                 GDK_BUTTON_PRESS_MASK |
1194                                 GDK_BUTTON_RELEASE_MASK |
1195                                 GDK_SCROLL_MASK |
1196                                 GDK_KEY_PRESS_MASK |
1197                                 GDK_POINTER_MOTION_MASK |
1198                                 GDK_LEAVE_NOTIFY_MASK;
1199
1200         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1201                                          &attributes,
1202                                          GDK_WA_X | GDK_WA_Y |
1203                                          GDK_WA_COLORMAP |
1204                                          GDK_WA_VISUAL);
1205         gdk_window_set_user_data (widget->window, widget);
1206         widget->style = gtk_style_attach (widget->style, widget->window);
1207
1208         if (view->presentation)
1209                 gdk_window_set_background (widget->window, &widget->style->black);
1210         else
1211                 gdk_window_set_background (widget->window, &widget->style->mid [GTK_STATE_NORMAL]);
1212 }
1213
1214 static void
1215 ev_view_unrealize (GtkWidget *widget)
1216 {
1217         GTK_WIDGET_CLASS (ev_view_parent_class)->unrealize (widget);
1218 }
1219
1220 static gboolean
1221 ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event)
1222 {
1223         EvView *view = EV_VIEW (widget); 
1224
1225         if ((event->state & GDK_CONTROL_MASK) != 0) {   
1226
1227                  ev_view_set_sizing_mode (view, EV_SIZING_FREE);         
1228
1229                  if (event->direction == GDK_SCROLL_UP || 
1230                         event->direction == GDK_SCROLL_LEFT) {
1231                             if (ev_view_can_zoom_in (view)) {
1232                                     ev_view_zoom_in (view);
1233                             }
1234                  } else {
1235                             if (ev_view_can_zoom_out (view)) {
1236                                     ev_view_zoom_out (view);
1237                             }   
1238                  }
1239                  return TRUE;
1240         }
1241         
1242         if ((event->state & GDK_SHIFT_MASK) != 0) {     
1243                 if (event->direction == GDK_SCROLL_UP)
1244                         event->direction = GDK_SCROLL_LEFT;
1245                 if (event->direction == GDK_SCROLL_DOWN)
1246                         event->direction = GDK_SCROLL_RIGHT;
1247         }
1248
1249         return FALSE;
1250 }
1251
1252 static gboolean
1253 ev_view_expose_event (GtkWidget      *widget,
1254                       GdkEventExpose *event)
1255 {
1256         EvView *view = EV_VIEW (widget);
1257         GdkRectangle rubberband;
1258         GList *l;
1259         int i;
1260
1261         if (view->document == NULL)
1262                 return FALSE;
1263
1264         for (i = view->start_page; i <= view->end_page; i++) {
1265                 GdkRectangle page_area;
1266                 GtkBorder border;
1267
1268                 if (!get_page_extents (view, i, &page_area, &border))
1269                         continue;
1270                     
1271                 page_area.x -= view->scroll_x;
1272                 page_area.y -= view->scroll_y;
1273                     
1274                 draw_one_page (view, i, &page_area, &border, &(event->area));
1275
1276                 if (EV_IS_DOCUMENT_FIND (view->document)) {
1277                         highlight_find_results (view, i);
1278                 }
1279         }
1280
1281         for (l = view->selections; l != NULL; l = l->next) {
1282                 EvViewSelection *selection = (EvViewSelection *)l->data;
1283
1284                 doc_rect_to_view_rect (view, selection->page,
1285                                        &selection->rect, &rubberband);
1286                 if (rubberband.width > 0 && rubberband.height > 0) {
1287                         draw_rubberband (GTK_WIDGET (view), GTK_WIDGET(view)->window,
1288                                          &rubberband, 0x40);
1289                 }
1290         }
1291
1292         return FALSE;
1293 }
1294
1295 static gboolean
1296 ev_view_button_press_event (GtkWidget      *widget,
1297                             GdkEventButton *event)
1298 {
1299         EvView *view = EV_VIEW (widget);
1300
1301         if (!GTK_WIDGET_HAS_FOCUS (widget)) {
1302                 gtk_widget_grab_focus (widget);
1303         }
1304
1305         view->pressed_button = event->button;
1306
1307         switch (event->button) {
1308                 case 1:
1309                         if (view->selections) {
1310                                 clear_selection (view);
1311                                 gtk_widget_queue_draw (widget);
1312                         }
1313
1314                         view->selection_start.x = event->x;
1315                         view->selection_start.y = event->y;
1316                         return TRUE;
1317                 case 2:
1318                         /* use root coordinates as reference point because
1319                          * scrolling changes window relative coordinates */
1320                         view->drag_info.start.x = event->x_root;
1321                         view->drag_info.start.y = event->y_root;
1322                         view->drag_info.hadj = gtk_adjustment_get_value (view->hadjustment);
1323                         view->drag_info.vadj = gtk_adjustment_get_value (view->vadjustment);
1324
1325                         ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG);
1326
1327                         return TRUE;
1328         }
1329
1330         return FALSE;
1331 }
1332
1333 static gboolean
1334 ev_view_motion_notify_event (GtkWidget      *widget,
1335                              GdkEventMotion *event)
1336 {
1337         EvView *view = EV_VIEW (widget);
1338
1339         if (!view->document)
1340                 return FALSE;
1341
1342         if (view->pressed_button == 1) {
1343                 GdkRectangle selection;
1344
1345                 selection.x = MIN (view->selection_start.x, event->x) + view->scroll_x;
1346                 selection.y = MIN (view->selection_start.y, event->y) + view->scroll_y;
1347                 selection.width = ABS (view->selection_start.x - event->x) + 1;
1348                 selection.height = ABS (view->selection_start.y - event->y) + 1;
1349
1350                 compute_selections (view, &selection);
1351
1352                 gtk_widget_queue_draw (widget);
1353
1354                 return TRUE;
1355         } else if (view->pressed_button == 2) {
1356                 if (!view->drag_info.dragging) {
1357                         gboolean start;
1358
1359                         start = gtk_drag_check_threshold (widget,
1360                                                           view->drag_info.start.x,
1361                                                           view->drag_info.start.y,
1362                                                           event->x_root,
1363                                                           event->y_root);
1364                         view->drag_info.dragging = start;
1365                 }
1366
1367                 if (view->drag_info.dragging) {
1368                         int dx, dy;
1369                         gdouble dhadj_value, dvadj_value;
1370
1371                         dx = event->x_root - view->drag_info.start.x;
1372                         dy = event->y_root - view->drag_info.start.y;
1373
1374                         dhadj_value = view->hadjustment->page_size *
1375                                       (gdouble)dx / widget->allocation.width;
1376                         dvadj_value = view->vadjustment->page_size *
1377                                       (gdouble)dy / widget->allocation.height;
1378
1379                         /* clamp scrolling to visible area */
1380                         gtk_adjustment_set_value (view->hadjustment,
1381                                                   MIN(view->drag_info.hadj - dhadj_value,
1382                                                       view->hadjustment->upper -
1383                                                       view->hadjustment->page_size));
1384                         gtk_adjustment_set_value (view->vadjustment,
1385                                                   MIN(view->drag_info.vadj - dvadj_value,
1386                                                       view->vadjustment->upper -
1387                                                       view->vadjustment->page_size));
1388
1389                         return TRUE;
1390                 }
1391         } else if (view->pressed_button <= 0) {
1392                 EvLink *link;
1393
1394                 link = get_link_at_location (view, event->x + view->scroll_x, event->y + view->scroll_y);
1395                 if (link) {
1396                         char *msg;
1397
1398                         msg = status_message_from_link (view, link);
1399                         ev_view_set_status (view, msg);
1400                         ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
1401                         g_free (msg);
1402                 } else {
1403                         ev_view_set_status (view, NULL);
1404                         if (view->cursor == EV_VIEW_CURSOR_LINK) {
1405                                 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1406                         }
1407                 }
1408                 return TRUE;
1409         }
1410
1411         return FALSE;
1412 }
1413
1414 static gboolean
1415 ev_view_button_release_event (GtkWidget      *widget,
1416                               GdkEventButton *event)
1417 {
1418         EvView *view = EV_VIEW (widget);
1419
1420         if (view->pressed_button == 2) {
1421                 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1422         }
1423
1424         view->pressed_button = -1;
1425         view->drag_info.dragging = FALSE;
1426
1427         if (view->selections) {
1428                 ev_view_update_primary_selection (view);
1429         } else if (view->document) {
1430                 EvLink *link;
1431
1432                 link = get_link_at_location (view, event->x + view->scroll_x, event->y + view->scroll_y);
1433                 if (link) {
1434                         go_to_link (view, link);
1435                 }
1436         }
1437
1438         return FALSE;
1439 }
1440
1441 /*** Drawing ***/
1442
1443 static guint32
1444 ev_gdk_color_to_rgb (const GdkColor *color)
1445 {
1446   guint32 result;
1447   result = (0xff0000 | (color->red & 0xff00));
1448   result <<= 8;
1449   result |= ((color->green & 0xff00) | (color->blue >> 8));
1450   return result;
1451 }
1452
1453 static void
1454 draw_rubberband (GtkWidget *widget, GdkWindow *window,
1455                  const GdkRectangle *rect, guchar alpha)
1456 {
1457         GdkGC *gc;
1458         GdkPixbuf *pixbuf;
1459         GdkColor *fill_color_gdk;
1460         guint fill_color;
1461         
1462         fill_color_gdk = gdk_color_copy (&GTK_WIDGET (widget)->style->base[GTK_STATE_SELECTED]);
1463         fill_color = ev_gdk_color_to_rgb (fill_color_gdk) << 8 | alpha;
1464
1465         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
1466                                  rect->width, rect->height);
1467         gdk_pixbuf_fill (pixbuf, fill_color);
1468
1469         gdk_draw_pixbuf (window, NULL, pixbuf,
1470                          0, 0,
1471                          rect->x - EV_VIEW (widget)->scroll_x, rect->y - EV_VIEW (widget)->scroll_y,
1472                          rect->width, rect->height,
1473                          GDK_RGB_DITHER_NONE,
1474                          0, 0);
1475
1476         g_object_unref (pixbuf);
1477
1478         gc = gdk_gc_new (window);
1479         gdk_gc_set_rgb_fg_color (gc, fill_color_gdk);
1480         gdk_draw_rectangle (window, gc, FALSE,
1481                             rect->x - EV_VIEW (widget)->scroll_x, rect->y - EV_VIEW (widget)->scroll_y,
1482                             rect->width - 1,
1483                             rect->height - 1);
1484         g_object_unref (gc);
1485
1486         gdk_color_free (fill_color_gdk);
1487 }
1488
1489
1490 static void
1491 highlight_find_results (EvView *view, int page)
1492 {
1493         EvDocumentFind *find;
1494         int i, results = 0;
1495
1496         g_return_if_fail (EV_IS_DOCUMENT_FIND (view->document));
1497
1498         find = EV_DOCUMENT_FIND (view->document);
1499
1500         results = ev_document_find_get_n_results (find, page);
1501
1502         for (i = 0; i < results; i++) {
1503                 EvRectangle rectangle;
1504                 GdkRectangle view_rectangle;
1505                 guchar alpha;
1506
1507                 if (i == view->find_result && page == view->find_page) {
1508                         alpha = 0x90;
1509                 } else {
1510                         alpha = 0x20;
1511                 }
1512
1513                 ev_document_find_get_result (find, page,
1514                                              i, &rectangle);
1515                 doc_rect_to_view_rect (view, page, &rectangle, &view_rectangle);
1516                 draw_rubberband (GTK_WIDGET (view), GTK_WIDGET(view)->window,
1517                                  &view_rectangle, alpha);
1518         }
1519 }
1520
1521 static void
1522 draw_one_page (EvView       *view,
1523                gint          page,
1524                GdkRectangle *page_area,
1525                GtkBorder    *border,
1526                GdkRectangle *expose_area)
1527 {
1528         gint width, height;
1529         GdkPixbuf *scaled_image;
1530         GdkPixbuf *current_pixbuf;
1531         GdkRectangle overlap;
1532         GdkRectangle real_page_area;
1533
1534         g_assert (view->document);
1535         if (! gdk_rectangle_intersect (page_area, expose_area, &overlap))
1536                 return;
1537
1538         ev_page_cache_get_size (view->page_cache,
1539                                 page, view->scale,
1540                                 &width, &height);
1541         /* Render the document itself */
1542         real_page_area = *page_area;
1543
1544         real_page_area.x += border->left;
1545         real_page_area.y += border->top;
1546         real_page_area.width -= (border->left + border->right);
1547         real_page_area.height -= (border->top + border->bottom);
1548
1549         ev_document_misc_paint_one_page (GTK_WIDGET(view)->window,
1550                                          GTK_WIDGET (view),
1551                                          page_area, border);
1552
1553         if (gdk_rectangle_intersect (&real_page_area, expose_area, &overlap)) {
1554                 current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, page);
1555
1556                 if (current_pixbuf == NULL)
1557                         scaled_image = NULL;
1558                 else if (width == gdk_pixbuf_get_width (current_pixbuf) &&
1559                          height == gdk_pixbuf_get_height (current_pixbuf))
1560                         scaled_image = g_object_ref (current_pixbuf);
1561                 else
1562                         /* FIXME: We don't want to scale the whole area, just the right
1563                          * area of it */
1564                         scaled_image = gdk_pixbuf_scale_simple (current_pixbuf,
1565                                                                 width, height,
1566                                                                 GDK_INTERP_NEAREST);
1567
1568                 if (scaled_image) {
1569                         gdk_draw_pixbuf (GTK_WIDGET(view)->window,
1570                                          GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
1571                                          scaled_image,
1572                                          overlap.x - real_page_area.x,
1573                                          overlap.y - real_page_area.y,
1574                                          overlap.x, overlap.y,
1575                                          overlap.width, overlap.height,
1576                                          GDK_RGB_DITHER_NORMAL,
1577                                          0, 0);
1578                         g_object_unref (scaled_image);
1579                 }
1580         }
1581 }
1582
1583 /*** GObject functions ***/
1584
1585 static void
1586 ev_view_finalize (GObject *object)
1587 {
1588         EvView *view = EV_VIEW (object);
1589
1590         LOG ("Finalize");
1591
1592         g_free (view->status);
1593         g_free (view->find_status);
1594
1595         clear_selection (view);
1596
1597         G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
1598 }
1599
1600 static void
1601 ev_view_destroy (GtkObject *object)
1602 {
1603         EvView *view = EV_VIEW (object);
1604
1605         if (view->document) {
1606                 g_object_unref (view->document);
1607                 view->document = NULL;
1608         }
1609         if (view->pixbuf_cache) {
1610                 g_object_unref (view->pixbuf_cache);
1611                 view->pixbuf_cache = NULL;
1612         }
1613         ev_view_set_scroll_adjustments (view, NULL, NULL);
1614
1615         GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
1616 }
1617
1618 static void
1619 ev_view_set_property (GObject      *object,
1620                       guint         prop_id,
1621                       const GValue *value,
1622                       GParamSpec   *pspec)
1623 {
1624         EvView *view = EV_VIEW (object);
1625
1626         switch (prop_id)
1627         {
1628         case PROP_CONTINUOUS:
1629                 ev_view_set_continuous (view, g_value_get_boolean (value));
1630                 break;
1631         case PROP_DUAL_PAGE:
1632                 ev_view_set_dual_page (view, g_value_get_boolean (value));
1633                 break;
1634         case PROP_FULLSCREEN:
1635                 ev_view_set_fullscreen (view, g_value_get_boolean (value));
1636                 break;
1637         case PROP_PRESENTATION:
1638                 ev_view_set_presentation (view, g_value_get_boolean (value));
1639                 break;
1640         case PROP_SIZING_MODE:
1641                 ev_view_set_sizing_mode (view, g_value_get_enum (value));
1642                 break;
1643         case PROP_ZOOM:
1644                 ev_view_set_zoom (view, g_value_get_double (value), FALSE);
1645                 break;
1646         default:
1647                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1648         }
1649 }
1650
1651 static void
1652 ev_view_get_property (GObject *object,
1653                       guint prop_id,
1654                       GValue *value,
1655                       GParamSpec *pspec)
1656 {
1657         EvView *view = EV_VIEW (object);
1658
1659         switch (prop_id)
1660         {
1661         case PROP_STATUS:
1662                 g_value_set_string (value, view->status);
1663                 break;
1664         case PROP_FIND_STATUS:
1665                 g_value_set_string (value, view->status);
1666                 break;
1667         case PROP_CONTINUOUS:
1668                 g_value_set_boolean (value, view->continuous);
1669                 break;
1670         case PROP_DUAL_PAGE:
1671                 g_value_set_boolean (value, view->dual_page);
1672                 break;
1673         case PROP_FULLSCREEN:
1674                 g_value_set_boolean (value, view->fullscreen);
1675                 break;
1676         case PROP_PRESENTATION:
1677                 g_value_set_boolean (value, view->presentation);
1678                 break;
1679         case PROP_SIZING_MODE:
1680                 g_value_set_enum (value, view->sizing_mode);
1681                 break;
1682         case PROP_ZOOM:
1683                 g_value_set_double (value, view->scale);
1684                 break;
1685         default:
1686                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1687         }
1688 }
1689
1690 static void
1691 ev_view_class_init (EvViewClass *class)
1692 {
1693         GObjectClass *object_class = G_OBJECT_CLASS (class);
1694         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
1695         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1696         GtkBindingSet *binding_set;
1697
1698         object_class->finalize = ev_view_finalize;
1699         object_class->set_property = ev_view_set_property;
1700         object_class->get_property = ev_view_get_property;
1701
1702         widget_class->expose_event = ev_view_expose_event;
1703         widget_class->button_press_event = ev_view_button_press_event;
1704         widget_class->motion_notify_event = ev_view_motion_notify_event;
1705         widget_class->button_release_event = ev_view_button_release_event;
1706         widget_class->size_request = ev_view_size_request;
1707         widget_class->size_allocate = ev_view_size_allocate;
1708         widget_class->realize = ev_view_realize;
1709         widget_class->unrealize = ev_view_unrealize;
1710         widget_class->scroll_event = ev_view_scroll_event;
1711         gtk_object_class->destroy = ev_view_destroy;
1712
1713         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
1714         class->binding_activated = ev_view_binding_activated;
1715
1716         widget_class->set_scroll_adjustments_signal =  
1717             g_signal_new ("set-scroll-adjustments",
1718                           G_OBJECT_CLASS_TYPE (object_class),
1719                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1720                           G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
1721                           NULL, NULL,
1722                           ev_marshal_VOID__OBJECT_OBJECT,
1723                           G_TYPE_NONE, 2,
1724                           GTK_TYPE_ADJUSTMENT,
1725                           GTK_TYPE_ADJUSTMENT);
1726
1727         signals[SIGNAL_BINDING_ACTIVATED] = g_signal_new ("binding_activated",
1728                          G_TYPE_FROM_CLASS (object_class),
1729                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1730                          G_STRUCT_OFFSET (EvViewClass, binding_activated),
1731                          NULL, NULL,
1732                          ev_marshal_VOID__ENUM_BOOLEAN,
1733                          G_TYPE_NONE, 2,
1734                          GTK_TYPE_SCROLL_TYPE,
1735                          G_TYPE_BOOLEAN);
1736
1737         signals[SIGNAL_ZOOM_INVALID] = g_signal_new ("zoom-invalid",
1738                          G_TYPE_FROM_CLASS (object_class),
1739                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1740                          G_STRUCT_OFFSET (EvViewClass, zoom_invalid),
1741                          NULL, NULL,
1742                          ev_marshal_VOID__VOID,
1743                          G_TYPE_NONE, 0, G_TYPE_NONE);
1744
1745         g_object_class_install_property (object_class,
1746                                          PROP_STATUS,
1747                                          g_param_spec_string ("status",
1748                                                               "Status Message",
1749                                                               "The status message",
1750                                                               NULL,
1751                                                               G_PARAM_READABLE));
1752
1753         g_object_class_install_property (object_class,
1754                                          PROP_FIND_STATUS,
1755                                          g_param_spec_string ("find-status",
1756                                                               "Find Status Message",
1757                                                               "The find status message",
1758                                                               NULL,
1759                                                               G_PARAM_READABLE));
1760
1761         g_object_class_install_property (object_class,
1762                                          PROP_CONTINUOUS,
1763                                          g_param_spec_boolean ("continuous",
1764                                                                "Continuous",
1765                                                                "Continuous scrolling mode",
1766                                                                TRUE,
1767                                                                G_PARAM_READWRITE));
1768
1769         g_object_class_install_property (object_class,
1770                                          PROP_DUAL_PAGE,
1771                                          g_param_spec_boolean ("dual-page",
1772                                                                "Dual Page",
1773                                                                "Two pages visible at once",
1774                                                                FALSE,
1775                                                                G_PARAM_READWRITE));
1776         g_object_class_install_property (object_class,
1777                                          PROP_FULLSCREEN,
1778                                          g_param_spec_boolean ("fullscreen",
1779                                                                "Full Screen",
1780                                                                "Draw page in a fullscreen fashion",
1781                                                                FALSE,
1782                                                                G_PARAM_READWRITE));
1783         g_object_class_install_property (object_class,
1784                                          PROP_PRESENTATION,
1785                                          g_param_spec_boolean ("presentation",
1786                                                                "Presentation",
1787                                                                "Draw page in presentation mode",
1788                                                                TRUE,
1789                                                                G_PARAM_READWRITE));
1790
1791         g_object_class_install_property (object_class,
1792                                          PROP_SIZING_MODE,
1793                                          g_param_spec_enum ("sizing-mode",
1794                                                             "Sizing Mode",
1795                                                             "Sizing Mode",
1796                                                             EV_TYPE_SIZING_MODE,
1797                                                             EV_SIZING_FIT_WIDTH,
1798                                                             G_PARAM_READWRITE));
1799
1800         g_object_class_install_property (object_class,
1801                                          PROP_ZOOM,
1802                                          g_param_spec_double ("zoom",
1803                                                               "Zoom factor",
1804                                                                "Zoom factor",
1805                                                                MIN_SCALE, 
1806                                                                MAX_SCALE,
1807                                                                1.0,
1808                                                                G_PARAM_READWRITE));
1809
1810         binding_set = gtk_binding_set_by_class (class);
1811
1812         add_scroll_binding_keypad (binding_set, GDK_Left,  GTK_SCROLL_STEP_BACKWARD, TRUE);
1813         add_scroll_binding_keypad (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD,  TRUE);
1814         add_scroll_binding_keypad (binding_set, GDK_Up,    GTK_SCROLL_STEP_BACKWARD, FALSE);
1815         add_scroll_binding_keypad (binding_set, GDK_Down,  GTK_SCROLL_STEP_FORWARD,  FALSE);
1816 }
1817
1818 static void
1819 ev_view_init (EvView *view)
1820 {
1821         GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
1822
1823         view->spacing = 5;
1824         view->scale = 1.0;
1825         view->current_page = 0;
1826         view->pressed_button = -1;
1827         view->cursor = EV_VIEW_CURSOR_NORMAL;
1828         view->drag_info.dragging = FALSE;
1829
1830         view->continuous = TRUE;
1831         view->dual_page = FALSE;
1832         view->presentation = FALSE;
1833         view->fullscreen = FALSE;
1834         view->sizing_mode = EV_SIZING_FIT_WIDTH;
1835         view->pending_scroll = SCROLL_TO_KEEP_POSITION;
1836 }
1837
1838 /*** Callbacks ***/
1839
1840 static void
1841 find_changed_cb (EvDocument *document, int page, EvView *view)
1842 {
1843         jump_to_find_page (view);
1844         jump_to_find_result (view);
1845         update_find_status_message (view);
1846
1847         if (view->current_page == page)
1848                 gtk_widget_queue_draw (GTK_WIDGET (view));
1849 }
1850
1851 static void
1852 job_finished_cb (EvPixbufCache *pixbuf_cache,
1853                  EvView        *view)
1854 {
1855         gtk_widget_queue_draw (GTK_WIDGET (view));
1856 }
1857
1858 static void
1859 page_changed_cb (EvPageCache *page_cache,
1860                  int          new_page,
1861                  EvView      *view)
1862 {
1863         if (view->current_page != new_page) {
1864                 
1865                 view->current_page = new_page;
1866                 view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
1867                 gtk_widget_queue_resize (GTK_WIDGET (view));
1868
1869                 if (EV_IS_DOCUMENT_FIND (view->document)) {
1870                         view->find_page = new_page;
1871                         view->find_result = 0;
1872                         update_find_status_message (view);
1873                 }
1874         }
1875 }
1876
1877 static void on_adjustment_value_changed (GtkAdjustment  *adjustment,
1878                                          EvView *view)
1879 {
1880         int dx = 0, dy = 0;
1881
1882         if (! GTK_WIDGET_REALIZED (view))
1883                 return;
1884
1885         if (view->hadjustment) {
1886                 dx = view->scroll_x - (int) view->hadjustment->value;
1887                 view->scroll_x = (int) view->hadjustment->value;
1888         } else {
1889                 view->scroll_x = 0;
1890         }
1891
1892         if (view->vadjustment) {
1893                 dy = view->scroll_y - (int) view->vadjustment->value;
1894                 view->scroll_y = (int) view->vadjustment->value;
1895         } else {
1896                 view->scroll_y = 0;
1897         }
1898         
1899         
1900         if (view->pending_resize)       
1901                 gtk_widget_queue_draw (GTK_WIDGET (view));
1902         else
1903                 gdk_window_scroll (GTK_WIDGET (view)->window, dx, dy);
1904
1905
1906         if (view->document)
1907                 view_update_range_and_current_page (view);
1908 }
1909
1910 GtkWidget*
1911 ev_view_new (void)
1912 {
1913         GtkWidget *view;
1914
1915         view = g_object_new (EV_TYPE_VIEW, NULL);
1916
1917         return view;
1918 }
1919
1920 static void
1921 setup_caches (EvView *view)
1922 {
1923         view->page_cache = ev_page_cache_get (view->document);
1924         g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
1925         view->pixbuf_cache = ev_pixbuf_cache_new (view->document);
1926         g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
1927 }
1928
1929 static void
1930 clear_caches (EvView *view)
1931 {
1932         if (view->pixbuf_cache) {
1933                 g_object_unref (view->pixbuf_cache);
1934                 view->pixbuf_cache = NULL;
1935         }
1936
1937         if (view->document) {
1938                 ev_page_cache_clear (view->document);
1939         }
1940 }
1941
1942 void
1943 ev_view_set_document (EvView     *view,
1944                       EvDocument *document)
1945 {
1946         g_return_if_fail (EV_IS_VIEW (view));
1947
1948         if (document != view->document) {
1949                 clear_caches (view);
1950
1951                 if (view->document) {
1952                         g_signal_handlers_disconnect_by_func (view->document,
1953                                                               find_changed_cb,
1954                                                               view);
1955                         g_object_unref (view->document);
1956                         view->page_cache = NULL;
1957
1958                 }
1959
1960                 view->document = document;
1961                 view->find_page = 0;
1962                 view->find_result = 0;
1963
1964                 if (view->document) {
1965                         g_object_ref (view->document);
1966                         if (EV_IS_DOCUMENT_FIND (view->document)) {
1967                                 g_signal_connect (view->document,
1968                                                   "find_changed",
1969                                                   G_CALLBACK (find_changed_cb),
1970                                                   view);
1971                         }
1972
1973                         setup_caches (view);
1974                 }
1975
1976                 gtk_widget_queue_resize (GTK_WIDGET (view));
1977         }
1978 }
1979
1980 /*** Zoom and sizing mode ***/
1981
1982 #define EPSILON 0.0000001
1983 void
1984 ev_view_set_zoom (EvView   *view,
1985                   double    factor,
1986                   gboolean  relative)
1987 {
1988         double scale;
1989
1990         if (relative)
1991                 scale = view->scale * factor;
1992         else
1993                 scale = factor;
1994
1995         scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
1996
1997         if (ABS (view->scale - scale) < EPSILON)
1998                 return;
1999
2000         view->scale = scale;
2001         view->pending_resize = TRUE;
2002         
2003         gtk_widget_queue_resize (GTK_WIDGET (view));
2004
2005         g_object_notify (G_OBJECT (view), "zoom");
2006 }
2007
2008 double
2009 ev_view_get_zoom (EvView *view)
2010 {
2011         return view->scale;
2012 }
2013
2014 void
2015 ev_view_set_continuous (EvView   *view,
2016                         gboolean  continuous)
2017 {
2018         g_return_if_fail (EV_IS_VIEW (view));
2019
2020         continuous = continuous != FALSE;
2021
2022         if (view->continuous != continuous) {
2023                 view->continuous = continuous;
2024                 view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2025                 gtk_widget_queue_resize (GTK_WIDGET (view));
2026         }
2027
2028         g_object_notify (G_OBJECT (view), "continuous");
2029 }
2030
2031 void
2032 ev_view_set_dual_page (EvView   *view,
2033                        gboolean  dual_page)
2034 {
2035         g_return_if_fail (EV_IS_VIEW (view));
2036
2037         dual_page = dual_page != FALSE;
2038
2039         if (view->dual_page == dual_page)
2040                 return;
2041     
2042         view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
2043         view->dual_page = dual_page;
2044         /* FIXME: if we're keeping the pixbuf cache around, we should extend the
2045          * preload_cache_size to be 2 if dual_page is set.
2046          */
2047         gtk_widget_queue_resize (GTK_WIDGET (view));
2048
2049         g_object_notify (G_OBJECT (view), "dual-page");
2050 }
2051
2052 void
2053 ev_view_set_fullscreen (EvView   *view,
2054                          gboolean  fullscreen)
2055 {
2056         g_return_if_fail (EV_IS_VIEW (view));
2057
2058         fullscreen = fullscreen != FALSE;
2059
2060         if (view->fullscreen != fullscreen) {
2061                 view->fullscreen = fullscreen;
2062                 gtk_widget_queue_resize (GTK_WIDGET (view));
2063         }
2064
2065         g_object_notify (G_OBJECT (view), "fullscreen");
2066 }
2067
2068 gboolean
2069 ev_view_get_fullscreen (EvView *view)
2070 {
2071         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2072
2073         return view->fullscreen;
2074 }
2075
2076 void
2077 ev_view_set_presentation (EvView   *view,
2078                           gboolean  presentation)
2079 {
2080         g_return_if_fail (EV_IS_VIEW (view));
2081
2082         presentation = presentation != FALSE;
2083
2084         if (view->presentation == presentation)
2085                 return;
2086
2087         view->presentation = presentation;
2088         gtk_widget_queue_resize (GTK_WIDGET (view));
2089         if (GTK_WIDGET_REALIZED (view)) {
2090                 if (view->presentation)
2091                         gdk_window_set_background (GTK_WIDGET(view)->window,
2092                                                    &GTK_WIDGET (view)->style->black);
2093                 else
2094                         gdk_window_set_background (GTK_WIDGET(view)->window,
2095                                                    &GTK_WIDGET (view)->style->mid [GTK_STATE_NORMAL]);
2096         }
2097
2098
2099         g_object_notify (G_OBJECT (view), "presentation");
2100 }
2101
2102 gboolean
2103 ev_view_get_presentation (EvView *view)
2104 {
2105         g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2106
2107         return view->presentation;
2108 }
2109
2110 void
2111 ev_view_set_sizing_mode (EvView       *view,
2112                          EvSizingMode  sizing_mode)
2113 {
2114         g_return_if_fail (EV_IS_VIEW (view));
2115
2116         if (view->sizing_mode == sizing_mode)
2117                 return;
2118
2119         view->sizing_mode = sizing_mode;
2120         gtk_widget_queue_resize (GTK_WIDGET (view));
2121         
2122         g_object_notify (G_OBJECT (view), "sizing-mode");
2123 }
2124
2125 EvSizingMode
2126 ev_view_get_sizing_mode (EvView *view)
2127 {
2128         g_return_val_if_fail (EV_IS_VIEW (view), EV_SIZING_FREE);
2129
2130         return view->sizing_mode;
2131 }
2132
2133 gboolean
2134 ev_view_can_zoom_in (EvView *view)
2135 {
2136         return view->scale * ZOOM_IN_FACTOR <= MAX_SCALE;
2137 }
2138
2139 gboolean
2140 ev_view_can_zoom_out (EvView *view)
2141 {
2142         return view->scale * ZOOM_OUT_FACTOR >= MIN_SCALE;
2143 }
2144
2145 void
2146 ev_view_zoom_in (EvView *view)
2147 {
2148         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2149         
2150         view->pending_scroll = SCROLL_TO_CENTER;
2151         ev_view_set_zoom (view, ZOOM_IN_FACTOR, TRUE);
2152 }
2153
2154 void
2155 ev_view_zoom_out (EvView *view)
2156 {
2157         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2158
2159         view->pending_scroll = SCROLL_TO_CENTER;
2160         ev_view_set_zoom (view, ZOOM_OUT_FACTOR, TRUE);
2161 }
2162
2163 static void
2164 ev_view_set_orientation (EvView         *view,
2165                          EvOrientation   orientation)
2166 {
2167         ev_document_set_orientation (view->document, orientation);
2168
2169         clear_caches (view);
2170         setup_caches (view);
2171
2172         gtk_widget_queue_resize (GTK_WIDGET (view));
2173 }
2174
2175 void
2176 ev_view_rotate_right (EvView *view)
2177 {
2178         EvOrientation orientation, new_orientation;
2179
2180         orientation = ev_document_get_orientation (view->document);
2181         if (orientation == EV_ORIENTATION_PORTRAIT) {
2182                 new_orientation = EV_ORIENTATION_LANDSCAPE;
2183         } else if (orientation == EV_ORIENTATION_LANDSCAPE) {
2184                 new_orientation = EV_ORIENTATION_UPSIDEDOWN;
2185         } else if (orientation == EV_ORIENTATION_UPSIDEDOWN) {
2186                 new_orientation = EV_ORIENTATION_SEASCAPE;
2187         } else {
2188                 new_orientation = EV_ORIENTATION_PORTRAIT;
2189         }
2190         ev_view_set_orientation (view, new_orientation);
2191 }
2192
2193 void
2194 ev_view_rotate_left (EvView *view)
2195 {
2196         EvOrientation orientation, new_orientation;
2197
2198         orientation = ev_document_get_orientation (view->document);
2199         if (orientation == EV_ORIENTATION_PORTRAIT) {
2200                 new_orientation = EV_ORIENTATION_SEASCAPE;
2201         } else if (orientation == EV_ORIENTATION_SEASCAPE) {
2202                 new_orientation = EV_ORIENTATION_UPSIDEDOWN;
2203         } else if (orientation == EV_ORIENTATION_UPSIDEDOWN) {
2204                 new_orientation = EV_ORIENTATION_LANDSCAPE;
2205         } else {
2206                 new_orientation = EV_ORIENTATION_PORTRAIT;
2207         }
2208         ev_view_set_orientation (view, new_orientation);
2209 }
2210
2211 static double
2212 zoom_for_size_fit_width (int doc_width,
2213                          int doc_height,
2214                          int target_width,
2215                          int target_height,
2216                          int vsb_width)
2217 {
2218         double scale;
2219
2220         scale = (double)target_width / doc_width;
2221
2222         if (doc_height * scale > target_height)
2223                 scale = (double) (target_width - vsb_width) / doc_width;
2224
2225         return scale;
2226 }
2227
2228 static double
2229 zoom_for_size_best_fit (int doc_width,
2230                         int doc_height,
2231                         int target_width,
2232                         int target_height,
2233                         int vsb_width,
2234                         int hsb_width)
2235 {
2236         double w_scale;
2237         double h_scale;
2238
2239         w_scale = (double)target_width / doc_width;
2240         h_scale = (double)target_height / doc_height;
2241
2242         if (doc_height * w_scale > target_height)
2243                 w_scale = (double) (target_width - vsb_width) / doc_width;
2244         if (doc_width * h_scale > target_width)
2245                 h_scale = (double) (target_height - hsb_width) / doc_height;
2246
2247         return MIN (w_scale, h_scale);
2248 }
2249
2250
2251 static void
2252 ev_view_zoom_for_size_presentation (EvView *view,
2253                                     int     width,
2254                                     int     height)
2255 {
2256         int doc_width, doc_height;
2257         gdouble scale;
2258
2259         ev_page_cache_get_size (view->page_cache,
2260                                 view->current_page,
2261                                 1.0,
2262                                 &doc_width,
2263                                 &doc_height);
2264         scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, 0, 0);
2265         ev_view_set_zoom (view, scale, FALSE);
2266 }
2267
2268 static void
2269 ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
2270                            int     width,
2271                            int     height,
2272                            int     vsb_width,
2273                            int     hsb_height)
2274 {
2275         int doc_width, doc_height;
2276         GtkBorder border;
2277         gdouble scale;
2278
2279         ev_page_cache_get_max_width (view->page_cache,
2280                                      1.0,
2281                                      &doc_width);
2282         ev_page_cache_get_max_height (view->page_cache,
2283                                       1.0,
2284                                       &doc_height);
2285         compute_border (view, doc_width, doc_height, &border);
2286
2287         doc_width = doc_width * 2;
2288         width -= (2 * (border.left + border.right) + 3 * view->spacing);
2289         height -= (border.top + border.bottom + 2 * view->spacing - 1);
2290
2291         /* FIXME: We really need to calculate the overall height here, not the
2292          * page height.  We assume there's always a vertical scrollbar for
2293          * now.  We need to fix this. */
2294         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2295                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2296         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2297                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2298         else
2299                 g_assert_not_reached ();
2300
2301         ev_view_set_zoom (view, scale, FALSE);
2302 }
2303
2304 static void
2305 ev_view_zoom_for_size_continuous (EvView *view,
2306                                   int     width,
2307                                   int     height,
2308                                   int     vsb_width,
2309                                   int     hsb_height)
2310 {
2311         int doc_width, doc_height;
2312         GtkBorder border;
2313         gdouble scale;
2314
2315         ev_page_cache_get_max_width (view->page_cache,
2316                                      1.0,
2317                                      &doc_width);
2318         ev_page_cache_get_max_height (view->page_cache,
2319                                       1.0,
2320                                       &doc_height);
2321         compute_border (view, doc_width, doc_height, &border);
2322
2323         width -= (border.left + border.right + 2 * view->spacing);
2324         height -= (border.top + border.bottom + 2 * view->spacing - 1);
2325
2326         /* FIXME: We really need to calculate the overall height here, not the
2327          * page height.  We assume there's always a vertical scrollbar for
2328          * now.  We need to fix this. */
2329         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2330                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2331         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2332                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2333         else
2334                 g_assert_not_reached ();
2335
2336         ev_view_set_zoom (view, scale, FALSE);
2337 }
2338
2339 static void
2340 ev_view_zoom_for_size_dual_page (EvView *view,
2341                                  int     width,
2342                                  int     height,
2343                                  int     vsb_width,
2344                                  int     hsb_height)
2345 {
2346         GtkBorder border;
2347         gint doc_width, doc_height;
2348         gdouble scale;
2349         gint other_page;
2350
2351         other_page = view->current_page ^ 1;
2352
2353         /* Find the largest of the two. */
2354         ev_page_cache_get_size (view->page_cache,
2355                                 view->current_page,
2356                                 1.0,
2357                                 &doc_width, &doc_height);
2358
2359         if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
2360                 gint width_2, height_2;
2361                 ev_page_cache_get_size (view->page_cache,
2362                                         other_page,
2363                                         1.0,
2364                                         &width_2, &height_2);
2365                 if (width_2 > doc_width)
2366                         doc_width = width_2;
2367                 if (height_2 > doc_height)
2368                         doc_height = height_2;
2369         }
2370         compute_border (view, doc_width, doc_height, &border);
2371
2372         doc_width = doc_width * 2;
2373         width -= ((border.left + border.right)* 2 + 3 * view->spacing);
2374         height -= (border.top + border.bottom + 2 * view->spacing);
2375
2376         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2377                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2378         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2379                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2380         else
2381                 g_assert_not_reached ();
2382
2383         ev_view_set_zoom (view, scale, FALSE);
2384 }
2385
2386 static void
2387 ev_view_zoom_for_size_single_page (EvView *view,
2388                                    int     width,
2389                                    int     height,
2390                                    int     vsb_width,
2391                                    int     hsb_height)
2392 {
2393         int doc_width, doc_height;
2394         GtkBorder border;
2395         gdouble scale;
2396
2397         ev_page_cache_get_size (view->page_cache,
2398                                 view->current_page,
2399                                 1.0,
2400                                 &doc_width,
2401                                 &doc_height);
2402         /* Get an approximate border */
2403         compute_border (view, width, height, &border);
2404
2405         width -= (border.left + border.right + 2 * view->spacing);
2406         height -= (border.top + border.bottom + 2 * view->spacing);
2407
2408         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2409                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2410         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2411                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2412         else
2413                 g_assert_not_reached ();
2414
2415         ev_view_set_zoom (view, scale, FALSE);
2416 }
2417
2418 void
2419 ev_view_set_zoom_for_size (EvView *view,
2420                            int     width,
2421                            int     height,
2422                            int     vsb_width,
2423                            int     hsb_height)
2424 {       
2425         g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
2426                           view->sizing_mode == EV_SIZING_BEST_FIT);
2427         g_return_if_fail (width >= 0);
2428         g_return_if_fail (height >= 0);
2429
2430         if (view->document == NULL)
2431                 return;
2432
2433         if (view->presentation)
2434                 ev_view_zoom_for_size_presentation (view, width, height);
2435         else if (view->continuous && view->dual_page)
2436                 ev_view_zoom_for_size_continuous_and_dual_page (view, width, height, vsb_width, hsb_height);
2437         else if (view->continuous)
2438                 ev_view_zoom_for_size_continuous (view, width, height, vsb_width, hsb_height);
2439         else if (view->dual_page)
2440                 ev_view_zoom_for_size_dual_page (view, width, height, vsb_width, hsb_height);
2441         else
2442                 ev_view_zoom_for_size_single_page (view, width, height, vsb_width, hsb_height);
2443 }
2444
2445 /*** Status text messages ***/
2446
2447 const char *
2448 ev_view_get_status (EvView *view)
2449 {
2450         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2451
2452         return view->status;
2453 }
2454
2455 static void
2456 ev_view_set_status (EvView *view, const char *message)
2457 {
2458         g_return_if_fail (EV_IS_VIEW (view));
2459
2460         if (message != view->status) {
2461                 g_free (view->status);
2462                 view->status = g_strdup (message);
2463                 g_object_notify (G_OBJECT (view), "status");
2464         }
2465 }
2466
2467 static void
2468 update_find_status_message (EvView *view)
2469 {
2470         char *message;
2471
2472         if (view->current_page == view->find_page) {
2473                 int results;
2474
2475                 results = ev_document_find_get_n_results
2476                                 (EV_DOCUMENT_FIND (view->document),
2477                                  view->current_page);
2478                 /* TRANS: Sometimes this could be better translated as
2479                    "%d hit(s) on this page".  Therefore this string
2480                    contains plural cases. */
2481                 message = g_strdup_printf (ngettext ("%d found on this page",
2482                                                      "%d found on this page",
2483                                                      results),
2484                                            results);
2485         } else {
2486                 double percent;
2487
2488                 percent = ev_document_find_get_progress
2489                                 (EV_DOCUMENT_FIND (view->document));
2490                 if (percent >= (1.0 - 1e-10)) {
2491                         message = g_strdup (_("Not found"));
2492                 } else {
2493                         message = g_strdup_printf (_("%3d%% remaining to search"),
2494                                                    (int) ((1.0 - percent) * 100));
2495                 }
2496
2497         }
2498         ev_view_set_find_status (view, message);
2499         g_free (message);
2500 }
2501
2502 const char *
2503 ev_view_get_find_status (EvView *view)
2504 {
2505         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2506
2507         return view->find_status;
2508 }
2509
2510 static void
2511 ev_view_set_find_status (EvView *view, const char *message)
2512 {
2513         g_return_if_fail (EV_IS_VIEW (view));
2514
2515         g_free (view->find_status);
2516         view->find_status = g_strdup (message);
2517         g_object_notify (G_OBJECT (view), "find-status");
2518 }
2519
2520 /*** Find ***/
2521
2522 static void
2523 jump_to_find_result (EvView *view)
2524 {
2525         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2526         EvRectangle rect;
2527         GdkRectangle view_rect;
2528         int n_results;
2529         int page = view->find_page;
2530
2531         n_results = ev_document_find_get_n_results (find, page);
2532
2533         if (n_results > view->find_result) {
2534                 ev_document_find_get_result
2535                         (find, page, view->find_result, &rect);
2536
2537                 doc_rect_to_view_rect (view, page, &rect, &view_rect);
2538                 ensure_rectangle_is_visible (view, &view_rect);
2539         }
2540 }
2541
2542 static void
2543 jump_to_find_page (EvView *view)
2544 {
2545         int n_pages, i;
2546
2547         n_pages = ev_page_cache_get_n_pages (view->page_cache);
2548
2549         for (i = 0; i < n_pages; i++) {
2550                 int has_results;
2551                 int page;
2552
2553                 page = i + view->find_page;
2554                 if (page >= n_pages) {
2555                         page = page - n_pages;
2556                 }
2557
2558                 has_results = ev_document_find_page_has_results
2559                                 (EV_DOCUMENT_FIND (view->document), page);
2560                 if (has_results == -1) {
2561                         view->find_page = page;
2562                         break;
2563                 } else if (has_results == 1) {
2564                         ev_page_cache_set_current_page (view->page_cache, page);
2565                         jump_to_find_result (view);
2566                         break;
2567                 }
2568         }
2569 }
2570
2571 gboolean
2572 ev_view_can_find_next (EvView *view)
2573 {
2574         int n_results = 0;
2575
2576         if (EV_IS_DOCUMENT_FIND (view->document)) {
2577                 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2578
2579                 n_results = ev_document_find_get_n_results (find, view->current_page);
2580         }
2581
2582         return n_results > 0;
2583 }
2584
2585 void
2586 ev_view_find_next (EvView *view)
2587 {
2588         EvPageCache *page_cache;
2589         int n_results, n_pages;
2590         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2591
2592         page_cache = ev_page_cache_get (view->document);
2593         n_results = ev_document_find_get_n_results (find, view->current_page);
2594
2595         n_pages = ev_page_cache_get_n_pages (page_cache);
2596
2597         view->find_result++;
2598
2599         if (view->find_result >= n_results) {
2600                 view->find_result = 0;
2601                 view->find_page++;
2602
2603                 if (view->find_page >= n_pages) {
2604                         view->find_page = 0;
2605                 }
2606
2607                 jump_to_find_page (view);
2608         } else {
2609                 jump_to_find_result (view);
2610                 gtk_widget_queue_draw (GTK_WIDGET (view));
2611         }
2612 }
2613
2614 void
2615 ev_view_find_previous (EvView *view)
2616 {
2617         int n_results, n_pages;
2618         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2619         EvPageCache *page_cache;
2620
2621         page_cache = ev_page_cache_get (view->document);
2622
2623         n_results = ev_document_find_get_n_results (find, view->current_page);
2624
2625         n_pages = ev_page_cache_get_n_pages (page_cache);
2626
2627         view->find_result--;
2628
2629         if (view->find_result < 0) {
2630                 view->find_result = 0;
2631                 view->find_page--;
2632
2633                 if (view->find_page < 0) {
2634                         view->find_page = n_pages - 1;
2635                 }
2636
2637                 jump_to_find_page (view);
2638         } else {
2639                 jump_to_find_result (view);
2640                 gtk_widget_queue_draw (GTK_WIDGET (view));
2641         }
2642 }
2643
2644 /*** Selections ***/
2645
2646 static void
2647 compute_selections (EvView *view, GdkRectangle *view_rect)
2648 {
2649         int n_pages, i;
2650
2651         clear_selection (view);
2652
2653         n_pages = ev_page_cache_get_n_pages (view->page_cache);
2654         for (i = 0; i < n_pages; i++) {
2655                 GdkRectangle page_area;
2656                 GtkBorder border;
2657
2658                 if (get_page_extents (view, i, &page_area, &border)) {
2659                         GdkRectangle overlap;
2660
2661                         if (gdk_rectangle_intersect (&page_area, view_rect, &overlap)) {
2662                                 EvViewSelection *selection;
2663
2664                                 selection = g_new0 (EvViewSelection, 1);
2665                                 selection->page = i;
2666                                 view_rect_to_doc_rect (view, &overlap, &page_area,
2667                                                        &(selection->rect));
2668
2669                                 view->selections = g_list_append
2670                                                 (view->selections, selection);
2671                         }
2672                 }
2673         }
2674 }
2675
2676 static void
2677 clear_selection (EvView *view)
2678 {
2679         g_list_foreach (view->selections, (GFunc)g_free, NULL);
2680         view->selections = NULL;
2681 }
2682
2683
2684 void
2685 ev_view_select_all (EvView *view)
2686 {
2687         int n_pages, i;
2688
2689         clear_selection (view);
2690
2691         n_pages = ev_page_cache_get_n_pages (view->page_cache);
2692         for (i = 0; i < n_pages; i++) {
2693                 int width, height;
2694                 EvViewSelection *selection;
2695
2696                 ev_page_cache_get_size (view->page_cache,
2697                                         i, 1.0, &width, &height);
2698
2699                 selection = g_new0 (EvViewSelection, 1);
2700                 selection->page = i;
2701                 selection->rect.x1 = selection->rect.y1 = 0;
2702                 selection->rect.x2 = width;
2703                 selection->rect.y2 = height;
2704
2705                 view->selections = g_list_append (view->selections, selection);
2706         }
2707
2708         gtk_widget_queue_draw (GTK_WIDGET (view));
2709 }
2710
2711 static char *
2712 get_selected_text (EvView *ev_view)
2713 {
2714         GString *text;
2715         GList *l;
2716
2717         text = g_string_new (NULL);
2718
2719         ev_document_doc_mutex_lock ();
2720
2721         for (l = ev_view->selections; l != NULL; l = l->next) {
2722                 EvViewSelection *selection = (EvViewSelection *)l->data;
2723                 char *tmp;
2724
2725                 tmp = ev_document_get_text (ev_view->document,
2726                                             selection->page,
2727                                             &selection->rect);
2728                 g_string_append (text, tmp);
2729                 g_free (tmp);
2730         }
2731
2732         ev_document_doc_mutex_unlock ();
2733
2734         return g_string_free (text, FALSE);
2735 }
2736
2737 void
2738 ev_view_copy (EvView *ev_view)
2739 {
2740         GtkClipboard *clipboard;
2741         char *text;
2742
2743         if (!ev_document_can_get_text (ev_view->document)) {
2744                 return;
2745         }
2746
2747         text = get_selected_text (ev_view);
2748         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
2749                                               GDK_SELECTION_CLIPBOARD);
2750         gtk_clipboard_set_text (clipboard, text, -1);
2751         g_free (text);
2752 }
2753
2754 static void
2755 ev_view_primary_get_cb (GtkClipboard     *clipboard,
2756                         GtkSelectionData *selection_data,
2757                         guint             info,
2758                         gpointer          data)
2759 {
2760         EvView *ev_view = EV_VIEW (data);
2761         char *text;
2762
2763         if (!ev_document_can_get_text (ev_view->document)) {
2764                 return;
2765         }
2766
2767         text = get_selected_text (ev_view);
2768         gtk_selection_data_set_text (selection_data, text, -1);
2769         g_free (text);
2770 }
2771
2772 static void
2773 ev_view_primary_clear_cb (GtkClipboard *clipboard,
2774                           gpointer      data)
2775 {
2776         EvView *view = EV_VIEW (data);
2777
2778         clear_selection (view);
2779 }
2780
2781 static void
2782 ev_view_update_primary_selection (EvView *ev_view)
2783 {
2784         GtkClipboard *clipboard;
2785
2786         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
2787                                               GDK_SELECTION_PRIMARY);
2788
2789         if (ev_view->selections) {
2790                 if (!gtk_clipboard_set_with_owner (clipboard,
2791                                                    targets,
2792                                                    G_N_ELEMENTS (targets),
2793                                                    ev_view_primary_get_cb,
2794                                                    ev_view_primary_clear_cb,
2795                                                    G_OBJECT (ev_view)))
2796                         ev_view_primary_clear_cb (clipboard, ev_view);
2797         } else {
2798                 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
2799                         gtk_clipboard_clear (clipboard);
2800         }
2801 }
2802
2803 /*** Cursor operations ***/
2804
2805 static GdkCursor *
2806 ev_view_create_invisible_cursor(void)
2807 {
2808        GdkBitmap *empty;
2809        GdkColor black = { 0, 0, 0, 0 };
2810        static char bits[] = { 0x00 };
2811
2812        empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
2813
2814        return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
2815 }
2816
2817 static void
2818 ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
2819 {
2820         GdkCursor *cursor = NULL;
2821         GdkDisplay *display;
2822         GtkWidget *widget;
2823
2824         if (view->cursor == new_cursor) {
2825                 return;
2826         }
2827
2828         widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
2829         display = gtk_widget_get_display (widget);
2830         view->cursor = new_cursor;
2831
2832         switch (new_cursor) {
2833                 case EV_VIEW_CURSOR_NORMAL:
2834                         gdk_window_set_cursor (widget->window, NULL);
2835                         break;
2836                 case EV_VIEW_CURSOR_LINK:
2837                         cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
2838                         break;
2839                 case EV_VIEW_CURSOR_WAIT:
2840                         cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2841                         break;
2842                 case EV_VIEW_CURSOR_HIDDEN:
2843                         cursor = ev_view_create_invisible_cursor ();
2844                         break;
2845                 case EV_VIEW_CURSOR_DRAG:
2846                         cursor = gdk_cursor_new_for_display (display, GDK_FLEUR);
2847                         break;
2848         }
2849
2850         if (cursor) {
2851                 gdk_window_set_cursor (widget->window, cursor);
2852                 gdk_cursor_unref (cursor);
2853                 gdk_flush();
2854         }
2855 }
2856
2857 void
2858 ev_view_hide_cursor (EvView *view)
2859 {
2860        ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN);
2861 }
2862
2863 void
2864 ev_view_show_cursor (EvView *view)
2865 {
2866        ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
2867 }
2868
2869 /*** Enum description for usage in signal ***/
2870
2871 GType
2872 ev_sizing_mode_get_type (void)
2873 {
2874   static GType etype = 0;
2875   if (etype == 0) {
2876     static const GEnumValue values[] = {
2877       { EV_SIZING_FIT_WIDTH, "EV_SIZING_FIT_WIDTH", "fit-width" },
2878       { EV_SIZING_BEST_FIT, "EV_SIZING_BEST_FIT", "best-fit" },
2879       { EV_SIZING_FREE, "EV_SIZING_FREE", "free" },
2880       { 0, NULL, NULL }
2881     };
2882     etype = g_enum_register_static ("EvSizingMode", values);
2883   }
2884   return etype;
2885 }
2886