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