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