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