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