]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
Rework border calculation, so that I can more easily disable it. Code
[evince.git] / shell / ev-view.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /* this file is part of evince, a gnome document viewer
3  *
4  *  Copyright (C) 2004 Red Hat, Inc
5  *
6  * Evince is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Evince is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include <math.h>
22 #include <gtk/gtkalignment.h>
23 #include <glib/gi18n.h>
24 #include <gtk/gtkbindings.h>
25 #include <gtk/gtkselection.h>
26 #include <gtk/gtkclipboard.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <libgnomevfs/gnome-vfs-utils.h>
29
30 #include "ev-marshal.h"
31 #include "ev-view.h"
32 #include "ev-document-find.h"
33 #include "ev-document-misc.h"
34 #include "ev-debug.h"
35 #include "ev-job-queue.h"
36 #include "ev-page-cache.h"
37 #include "ev-pixbuf-cache.h"
38
39 #define EV_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass))
40 #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW))
41 #define EV_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_VIEW, EvViewClass))
42
43 enum {
44         PROP_0,
45         PROP_STATUS,
46         PROP_FIND_STATUS
47 };
48
49 enum {
50   TARGET_STRING,
51   TARGET_TEXT,
52   TARGET_COMPOUND_TEXT,
53   TARGET_UTF8_STRING,
54   TARGET_TEXT_BUFFER_CONTENTS
55 };
56
57 enum {
58         EV_SCROLL_PAGE_FORWARD,
59         EV_SCROLL_PAGE_BACKWARD
60 };
61
62 static const GtkTargetEntry targets[] = {
63         { "STRING", 0, TARGET_STRING },
64         { "TEXT",   0, TARGET_TEXT },
65         { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
66         { "UTF8_STRING", 0, TARGET_UTF8_STRING },
67 };
68
69 typedef enum {
70         EV_VIEW_CURSOR_NORMAL,
71         EV_VIEW_CURSOR_LINK,
72         EV_VIEW_CURSOR_WAIT,
73         EV_VIEW_CURSOR_HIDDEN
74 } EvViewCursor;
75
76 #define ZOOM_IN_FACTOR  1.2
77 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)
78
79 #define MIN_SCALE 0.05409
80 #define MAX_SCALE 18.4884
81
82 struct _EvView {
83         GtkWidget parent_instance;
84
85         EvDocument *document;
86
87         GdkWindow *bin_window;
88
89         char *status;
90         char *find_status;
91
92         int scroll_x;
93         int scroll_y;
94
95         gboolean pressed_button;
96         gboolean has_selection;
97         GdkPoint selection_start;
98         EvRectangle selection;
99         EvViewCursor cursor;
100
101         GtkAdjustment *hadjustment;
102         GtkAdjustment *vadjustment;
103
104         EvPageCache *page_cache;
105         EvPixbufCache *pixbuf_cache;
106
107         gint current_page;
108         EvJobRender *current_job;
109
110         int find_page;
111         int find_result;
112         int spacing;
113
114         double scale;
115         int width;
116         int height;
117         GtkBorder border;
118 };
119
120 struct _EvViewClass {
121         GtkWidgetClass parent_class;
122
123         void    (*set_scroll_adjustments) (EvView         *view,
124                                            GtkAdjustment  *hadjustment,
125                                            GtkAdjustment  *vadjustment);
126         void    (*scroll_view)            (EvView         *view,
127                                            GtkScrollType   scroll,
128                                            gboolean        horizontal);
129
130 };
131
132
133 static void ev_view_set_scroll_adjustments (EvView         *view,
134                                             GtkAdjustment  *hadjustment,
135                                             GtkAdjustment  *vadjustment);
136
137 G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET)
138
139 /*** Helper functions ***/
140
141 static void
142 view_update_adjustments (EvView *view)
143 {
144         int old_x = view->scroll_x;
145         int old_y = view->scroll_y;
146
147         if (view->hadjustment)
148                 view->scroll_x = view->hadjustment->value;
149         else
150                 view->scroll_x = 0;
151
152         if (view->vadjustment)
153                 view->scroll_y = view->vadjustment->value;
154         else
155                 view->scroll_y = 0;
156
157         if (GTK_WIDGET_REALIZED (view) &&
158             (view->scroll_x != old_x || view->scroll_y != old_y)) {
159                 gdk_window_move (view->bin_window, - view->scroll_x, - view->scroll_y);
160                 gdk_window_process_updates (view->bin_window, TRUE);
161         }
162 }
163
164 static void
165 view_set_adjustment_values (EvView         *view,
166                             GtkOrientation  orientation)
167 {
168         GtkWidget *widget = GTK_WIDGET (view);
169         GtkAdjustment *adjustment;
170         gboolean value_changed = FALSE;
171         int requisition;
172         int allocation;
173
174         if (orientation == GTK_ORIENTATION_HORIZONTAL)  {
175                 requisition = widget->requisition.width;
176                 allocation = widget->allocation.width;
177                 adjustment = view->hadjustment;
178         } else {
179                 requisition = widget->requisition.height;
180                 allocation = widget->allocation.height;
181                 adjustment = view->vadjustment;
182         }
183
184         if (!adjustment)
185                 return;
186
187         adjustment->page_size = allocation;
188         adjustment->step_increment = allocation * 0.1;
189         adjustment->page_increment = allocation * 0.9;
190         adjustment->lower = 0;
191         adjustment->upper = MAX (allocation, requisition);
192
193         if (adjustment->value > adjustment->upper - adjustment->page_size) {
194                 adjustment->value = adjustment->upper - adjustment->page_size;
195                 value_changed = TRUE;
196         }
197
198         gtk_adjustment_changed (adjustment);
199         if (value_changed)
200                 gtk_adjustment_value_changed (adjustment);
201 }
202
203 /*** Virtual function implementations ***/
204
205 static void
206 ev_view_finalize (GObject *object)
207 {
208         EvView *view = EV_VIEW (object);
209
210         LOG ("Finalize");
211
212
213         ev_view_set_scroll_adjustments (view, NULL, NULL);
214
215         G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
216 }
217
218 static void
219 ev_view_destroy (GtkObject *object)
220 {
221         EvView *view = EV_VIEW (object);
222
223         if (view->document) {
224                 g_object_unref (view->document);
225                 view->document = NULL;
226         }
227         if (view->pixbuf_cache) {
228                 g_object_unref (view->pixbuf_cache);
229                 view->pixbuf_cache = NULL;
230         }
231         ev_view_set_scroll_adjustments (view, NULL, NULL);
232
233         GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
234 }
235
236 static void
237 ev_view_get_offsets (EvView *view, int *x_offset, int *y_offset)
238 {
239         EvDocument *document = view->document;
240         GtkWidget *widget = GTK_WIDGET (view);
241         int width, height, target_width, target_height;
242
243         g_return_if_fail (EV_IS_DOCUMENT (document));
244
245         ev_page_cache_get_size (view->page_cache,
246                                 view->current_page,
247                                 view->scale,
248                                 &width, &height);
249
250         *x_offset = view->spacing;
251         *y_offset = view->spacing;
252         target_width = width + view->border.left +
253                        view->border.right + view->spacing * 2;
254         target_height = height + view->border.top +
255                         view->border.bottom + view->spacing * 2;
256         *x_offset += MAX (0, (widget->allocation.width - target_width) / 2);
257         *y_offset += MAX (0, (widget->allocation.height - target_height) / 2);
258 }
259
260 static void
261 view_rect_to_doc_rect (EvView *view, GdkRectangle *view_rect, EvRectangle *doc_rect)
262 {
263         int x_offset, y_offset;
264
265         ev_view_get_offsets (view, &x_offset, &y_offset);
266         doc_rect->x1 = (double) (view_rect->x - x_offset) / view->scale;
267         doc_rect->y1 = (double) (view_rect->y - y_offset) / view->scale;
268         doc_rect->x2 = doc_rect->x1 + (double) view_rect->width / view->scale;
269         doc_rect->y2 = doc_rect->y1 + (double) view_rect->height / view->scale;
270 }
271
272 static void
273 doc_rect_to_view_rect (EvView *view, EvRectangle *doc_rect, GdkRectangle *view_rect)
274 {
275         int x_offset, y_offset;
276
277         ev_view_get_offsets (view, &x_offset, &y_offset);
278         view_rect->x = floor (doc_rect->x1 * view->scale) + x_offset;
279         view_rect->y = floor (doc_rect->y1 * view->scale) + y_offset;
280         view_rect->width = ceil (doc_rect->x2 * view->scale) + x_offset - view_rect->x;
281         view_rect->height = ceil (doc_rect->y2 * view->scale) + y_offset - view_rect->y;
282 }
283
284 static void
285 compute_zoom_factor (EvView *view)
286 {
287         int doc_width, doc_height;
288         double scale, scale_w, scale_h;
289         GtkBorder border;
290
291         if (view->width <= 0 && view->height <= 0) {
292                 return;
293         }
294
295         doc_width = doc_height = 0;
296         scale = scale_w = scale_h = 1.0;
297         ev_page_cache_get_size (view->page_cache,
298                                 view->current_page,
299                                 1.0,
300                                 &doc_width,
301                                 &doc_height);
302
303         ev_document_misc_get_page_border_size (doc_width, doc_height, &border);
304
305         if (doc_width == 0 || doc_height == 0) {
306                 return;
307         }
308
309         if (view->width >= 0) {
310                 int target_width;
311
312                 target_width = view->width - (view->spacing * 2 + view->border.left +
313                                               view->border.right);
314                 scale = scale_w = (double)target_width / doc_width;
315         }
316
317         if (view->height >= 0) {
318                 int target_height;
319
320                 target_height = view->height - (view->spacing * 2 + view->border.top +
321                                                 view->border.bottom);
322                 scale = scale_h = (double)target_height / doc_height;
323         }
324
325         if (view->width >= 0 && view->height >= 0) {
326                 scale = (scale_w < scale_h) ? scale_w : scale_h;
327         }
328
329         view->scale = scale;
330 }
331
332 /* Called by size_request to make sure we have appropriate jobs running.
333  */
334 static void
335 ev_view_size_request (GtkWidget      *widget,
336                       GtkRequisition *requisition)
337 {
338         EvView *view = EV_VIEW (widget);
339         gint width, height;
340
341         if (!GTK_WIDGET_REALIZED (widget))
342                 return;
343
344         if (!view->document) {
345                 requisition->width = 1;
346                 requisition->height = 1;
347                 return;
348         }
349
350         compute_zoom_factor (view);
351
352         ev_page_cache_get_size (view->page_cache,
353                                 view->current_page,
354                                 view->scale,
355                                 &width, &height);
356
357         ev_pixbuf_cache_set_page_range (view->pixbuf_cache,
358                                         view->current_page,
359                                         view->current_page,
360                                         view->scale);
361         ev_document_misc_get_page_border_size (width, height, &(view->border));
362
363         if (view->width >= 0) {
364                 requisition->width = 0;
365         } else {
366                 requisition->width = width + view->border.left +
367                                      view->border.right + view->spacing * 2;
368         }
369
370         if (view->height >= 0) {
371                 requisition->height = 0;
372         } else {
373                 requisition->height = height + view->border.top +
374                                       view->border.bottom + view->spacing * 2;
375         }
376 }
377
378 static void
379 ev_view_size_allocate (GtkWidget      *widget,
380                        GtkAllocation  *allocation)
381 {
382         EvView *view = EV_VIEW (widget);
383
384         GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
385
386         view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
387         view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
388
389         if (GTK_WIDGET_REALIZED (widget)) {
390                 gdk_window_resize (view->bin_window,
391                                    MAX (widget->allocation.width, widget->requisition.width),
392                                    MAX (widget->allocation.height, widget->requisition.height));
393         }
394 }
395
396 static void
397 ev_view_realize (GtkWidget *widget)
398 {
399         EvView *view = EV_VIEW (widget);
400         GdkWindowAttr attributes;
401
402         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
403
404
405         attributes.window_type = GDK_WINDOW_CHILD;
406         attributes.wclass = GDK_INPUT_OUTPUT;
407         attributes.visual = gtk_widget_get_visual (widget);
408         attributes.colormap = gtk_widget_get_colormap (widget);
409
410         attributes.x = widget->allocation.x;
411         attributes.y = widget->allocation.y;
412         attributes.width = widget->allocation.width;
413         attributes.height = widget->allocation.height;
414         attributes.event_mask = 0;
415
416         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
417                                          &attributes,
418                                          GDK_WA_X | GDK_WA_Y |
419                                          GDK_WA_COLORMAP |
420                                          GDK_WA_VISUAL);
421         gdk_window_set_user_data (widget->window, widget);
422         widget->style = gtk_style_attach (widget->style, widget->window);
423         gdk_window_set_background (widget->window, &widget->style->mid[widget->state]);
424
425         attributes.x = 0;
426         attributes.y = 0;
427         attributes.width = MAX (widget->allocation.width, widget->requisition.width);
428         attributes.height = MAX (widget->allocation.height, widget->requisition.height);
429         attributes.event_mask = GDK_EXPOSURE_MASK |
430                                 GDK_BUTTON_PRESS_MASK |
431                                 GDK_BUTTON_RELEASE_MASK |
432                                 GDK_SCROLL_MASK |
433                                 GDK_KEY_PRESS_MASK |
434                                 GDK_POINTER_MOTION_MASK |
435                                 GDK_LEAVE_NOTIFY_MASK;
436
437         view->bin_window = gdk_window_new (widget->window,
438                                            &attributes,
439                                            GDK_WA_X | GDK_WA_Y |
440                                            GDK_WA_COLORMAP |
441                                            GDK_WA_VISUAL);
442         gdk_window_set_user_data (view->bin_window, widget);
443         gdk_window_show (view->bin_window);
444
445         widget->style = gtk_style_attach (widget->style, view->bin_window);
446         gdk_window_set_background (view->bin_window, &widget->style->mid[widget->state]);
447
448         if (view->document) {
449                 /* We can't get page size without a target, so we have to
450                  * queue a size request at realization. Could be fixed
451                  * with EvDocument changes to allow setting a GdkScreen
452                  * without setting a target.
453                  */
454                 gtk_widget_queue_resize (widget);
455         }
456 }
457
458 static void
459 ev_view_unrealize (GtkWidget *widget)
460 {
461         EvView *view = EV_VIEW (widget);
462
463         gdk_window_set_user_data (view->bin_window, NULL);
464         gdk_window_destroy (view->bin_window);
465         view->bin_window = NULL;
466
467         GTK_WIDGET_CLASS (ev_view_parent_class)->unrealize (widget);
468 }
469
470 static guint32
471 ev_gdk_color_to_rgb (const GdkColor *color)
472 {
473   guint32 result;
474   result = (0xff0000 | (color->red & 0xff00));
475   result <<= 8;
476   result |= ((color->green & 0xff00) | (color->blue >> 8));
477   return result;
478 }
479
480 static void
481 draw_rubberband (GtkWidget *widget, GdkWindow *window,
482                  const GdkRectangle *rect, guchar alpha)
483 {
484         GdkGC *gc;
485         GdkPixbuf *pixbuf;
486         GdkColor *fill_color_gdk;
487         guint fill_color;
488
489         fill_color_gdk = gdk_color_copy (&GTK_WIDGET (widget)->style->base[GTK_STATE_SELECTED]);
490         fill_color = ev_gdk_color_to_rgb (fill_color_gdk) << 8 | alpha;
491
492         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
493                                  rect->width, rect->height);
494         gdk_pixbuf_fill (pixbuf, fill_color);
495
496         gdk_draw_pixbuf (window, NULL, pixbuf,
497                          0, 0,
498                          rect->x, rect->y,
499                          rect->width, rect->height,
500                          GDK_RGB_DITHER_NONE,
501                          0, 0);
502
503         g_object_unref (pixbuf);
504
505         gc = gdk_gc_new (window);
506         gdk_gc_set_rgb_fg_color (gc, fill_color_gdk);
507         gdk_draw_rectangle (window, gc, FALSE,
508                             rect->x, rect->y,
509                             rect->width - 1,
510                             rect->height - 1);
511         g_object_unref (gc);
512
513         gdk_color_free (fill_color_gdk);
514 }
515
516 static void
517 highlight_find_results (EvView *view)
518 {
519         EvDocumentFind *find;
520         int i, results = 0;
521
522         g_return_if_fail (EV_IS_DOCUMENT_FIND (view->document));
523
524         find = EV_DOCUMENT_FIND (view->document);
525
526         g_mutex_lock (EV_DOC_MUTEX);
527         results = ev_document_find_get_n_results (find, view->current_page);
528         g_mutex_unlock (EV_DOC_MUTEX);
529
530         for (i = 0; i < results; i++) {
531                 EvRectangle rectangle;
532                 GdkRectangle view_rectangle;
533                 guchar alpha;
534
535                 alpha = (i == view->find_result) ? 0x90 : 0x20;
536                 g_mutex_lock (EV_DOC_MUTEX);
537                 ev_document_find_get_result (find, view->current_page,
538                                              i, &rectangle);
539                 g_mutex_unlock (EV_DOC_MUTEX);
540                 doc_rect_to_view_rect (view, &rectangle, &view_rectangle);
541                 draw_rubberband (GTK_WIDGET (view), view->bin_window,
542                                  &view_rectangle, alpha);
543         }
544 }
545
546
547 static void
548 expose_bin_window (GtkWidget      *widget,
549                    GdkEventExpose *event)
550 {
551         EvView *view = EV_VIEW (widget);
552         gint width, height;
553         GdkRectangle area;
554         int x_offset, y_offset;
555         GdkPixbuf *scaled_image;
556         GdkPixbuf *current_pixbuf;
557
558         if (view->document == NULL)
559                 return;
560
561         ev_view_get_offsets (view, &x_offset, &y_offset);
562         ev_page_cache_get_size (view->page_cache,
563                                 view->current_page,
564                                 view->scale,
565                                 &width, &height);
566
567         /* Paint the frame */
568         area.x = x_offset;
569         area.y = y_offset;
570         area.width = width + view->border.left + view->border.right;
571         area.height = height + view->border.top + view->border.bottom;
572         ev_document_misc_paint_one_page (view->bin_window, widget, &area,
573                                          &(view->border));
574
575         /* Render the document itself */
576         LOG ("Render area %d %d %d %d - Offset %d %d",
577              event->area.x, event->area.y,
578              event->area.width, event->area.height,
579              x_offset, y_offset);
580
581         current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, view->current_page);
582
583         if (current_pixbuf == NULL)
584                 scaled_image = NULL;
585         else if (width == gdk_pixbuf_get_width (current_pixbuf) &&
586                  height == gdk_pixbuf_get_height (current_pixbuf))
587                 scaled_image = g_object_ref (current_pixbuf);
588         else
589                 scaled_image = gdk_pixbuf_scale_simple (current_pixbuf,
590                                                         width, height,
591                                                         GDK_INTERP_NEAREST);
592         if (scaled_image) {
593                 gdk_draw_pixbuf (view->bin_window,
594                                  GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
595                                  scaled_image,
596                                  0, 0,
597                                  area.x + view->border.left,
598                                  area.y + view->border.top,
599                                  width, height,
600                                  GDK_RGB_DITHER_NORMAL,
601                                  0, 0);
602                 g_object_unref (scaled_image);
603         }
604
605         if (EV_IS_DOCUMENT_FIND (view->document)) {
606                 highlight_find_results (view);
607         }
608
609         if (view->has_selection) {
610                 GdkRectangle rubberband;
611
612                 doc_rect_to_view_rect (view, &view->selection, &rubberband);
613                 if (rubberband.width > 0 && rubberband.height > 0) {
614                         draw_rubberband (widget, view->bin_window,
615                                          &rubberband, 0x40);
616                 }
617         }
618 }
619
620 static gboolean
621 ev_view_expose_event (GtkWidget      *widget,
622                       GdkEventExpose *event)
623 {
624         EvView *view = EV_VIEW (widget);
625
626         if (event->window == view->bin_window)
627                 expose_bin_window (widget, event);
628         else
629                 return GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event (widget, event);
630
631         return FALSE;
632 }
633
634 void
635 ev_view_select_all (EvView *ev_view)
636 {
637         GtkWidget *widget = GTK_WIDGET (ev_view);
638         GdkRectangle selection;
639         int width, height;
640         int x_offset, y_offset;
641
642         g_return_if_fail (EV_IS_VIEW (ev_view));
643
644
645         ev_view_get_offsets (ev_view, &x_offset, &y_offset);
646         ev_page_cache_get_size (ev_view->page_cache,
647                                 ev_view->current_page,
648                                 ev_view->scale,
649                                 &width, &height);
650
651         ev_view->has_selection = TRUE;
652         selection.x = x_offset + ev_view->border.left;
653         selection.y = y_offset + ev_view->border.top;
654         selection.width = width;
655         selection.height = height;
656         view_rect_to_doc_rect (ev_view, &selection, &ev_view->selection);
657
658         gtk_widget_queue_draw (widget);
659 }
660
661 void
662 ev_view_copy (EvView *ev_view)
663 {
664         GtkClipboard *clipboard;
665         char *text;
666
667         if (!ev_document_can_get_text (ev_view->document)) {
668                 return;
669         }
670
671         g_mutex_lock (EV_DOC_MUTEX);
672         text = ev_document_get_text (ev_view->document,
673                                      ev_view->current_page,
674                                      &ev_view->selection);
675         g_mutex_unlock (EV_DOC_MUTEX);
676
677         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
678                                               GDK_SELECTION_CLIPBOARD);
679         gtk_clipboard_set_text (clipboard, text, -1);
680         g_free (text);
681 }
682
683 static void
684 ev_view_primary_get_cb (GtkClipboard     *clipboard,
685                         GtkSelectionData *selection_data,
686                         guint             info,
687                         gpointer          data)
688 {
689         EvView *ev_view = EV_VIEW (data);
690         char *text;
691
692         if (!ev_document_can_get_text (ev_view->document)) {
693                 return;
694         }
695
696         g_mutex_lock (EV_DOC_MUTEX);
697         text = ev_document_get_text (ev_view->document,
698                                      ev_view->current_page,
699                                      &ev_view->selection);
700         g_mutex_unlock (EV_DOC_MUTEX);
701         gtk_selection_data_set_text (selection_data, text, -1);
702 }
703
704 static void
705 ev_view_primary_clear_cb (GtkClipboard *clipboard,
706                           gpointer      data)
707 {
708         EvView *ev_view = EV_VIEW (data);
709
710         ev_view->has_selection = FALSE;
711 }
712
713 static void
714 ev_view_update_primary_selection (EvView *ev_view)
715 {
716         GtkClipboard *clipboard;
717
718         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
719                                               GDK_SELECTION_PRIMARY);
720
721         if (ev_view->has_selection) {
722                 if (!gtk_clipboard_set_with_owner (clipboard,
723                                                    targets,
724                                                    G_N_ELEMENTS (targets),
725                                                    ev_view_primary_get_cb,
726                                                    ev_view_primary_clear_cb,
727                                                    G_OBJECT (ev_view)))
728                         ev_view_primary_clear_cb (clipboard, ev_view);
729         } else {
730                 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
731                         gtk_clipboard_clear (clipboard);
732         }
733 }
734
735 static gboolean
736 ev_view_button_press_event (GtkWidget      *widget,
737                             GdkEventButton *event)
738 {
739         EvView *view = EV_VIEW (widget);
740
741         if (!GTK_WIDGET_HAS_FOCUS (widget)) {
742                 gtk_widget_grab_focus (widget);
743         }
744
745         view->pressed_button = event->button;
746
747         switch (event->button) {
748                 case 1:
749                         if (view->has_selection) {
750                                 view->has_selection = FALSE;
751                                 gtk_widget_queue_draw (widget);
752                         }
753
754                         view->selection_start.x = event->x;
755                         view->selection_start.y = event->y;
756                         break;
757         }
758
759         return TRUE;
760 }
761
762 static char *
763 status_message_from_link (EvView *view, EvLink *link)
764 {
765         EvLinkType type;
766         char *msg = NULL;
767         char *page_label;
768
769         type = ev_link_get_link_type (link);
770
771         switch (type) {
772                 case EV_LINK_TYPE_TITLE:
773                         if (ev_link_get_title (link))
774                                 msg = g_strdup (ev_link_get_title (link));
775                         break;
776                 case EV_LINK_TYPE_PAGE:
777                         page_label = ev_page_cache_get_page_label (view->page_cache, ev_link_get_page (link));
778                         msg = g_strdup_printf (_("Go to page %s"), page_label);
779                         g_free (page_label);
780                         break;
781                 case EV_LINK_TYPE_EXTERNAL_URI:
782                         msg = g_strdup (ev_link_get_uri (link));
783                         break;
784                 default:
785                         break;
786         }
787
788         return msg;
789 }
790
791 static void
792 ev_view_set_status (EvView *view, const char *message)
793 {
794         g_return_if_fail (EV_IS_VIEW (view));
795
796         if (message != view->status) {
797                 g_free (view->status);
798                 view->status = g_strdup (message);
799                 g_object_notify (G_OBJECT (view), "status");
800         }
801 }
802
803 static void
804 ev_view_set_find_status (EvView *view, const char *message)
805 {
806         g_return_if_fail (EV_IS_VIEW (view));
807
808         g_free (view->find_status);
809         view->find_status = g_strdup (message);
810         g_object_notify (G_OBJECT (view), "find-status");
811 }
812
813 static GdkCursor *
814 ev_view_create_invisible_cursor(void)
815 {
816        GdkBitmap *empty;
817        GdkColor black = { 0, 0, 0, 0 };
818        static char bits[] = { 0x00 };
819
820        empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
821
822        return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
823 }
824
825 static void
826 ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
827 {
828         GdkCursor *cursor = NULL;
829         GdkDisplay *display;
830         GtkWidget *widget;
831
832         if (view->cursor == new_cursor) {
833                 return;
834         }
835
836         widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
837         display = gtk_widget_get_display (widget);
838         view->cursor = new_cursor;
839
840         switch (new_cursor) {
841                 case EV_VIEW_CURSOR_NORMAL:
842                         gdk_window_set_cursor (widget->window, NULL);
843                         break;
844                 case EV_VIEW_CURSOR_LINK:
845                         cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
846                         break;
847                 case EV_VIEW_CURSOR_WAIT:
848                         cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
849                         break;
850                 case EV_VIEW_CURSOR_HIDDEN:
851                         cursor = ev_view_create_invisible_cursor ();
852                         break;
853
854         }
855
856         if (cursor) {
857                 gdk_window_set_cursor (widget->window, cursor);
858                 gdk_cursor_unref (cursor);
859                 gdk_flush();
860         }
861 }
862
863
864 static void
865 find_page_at_location (EvView  *view,
866                        gdouble  x,
867                        gdouble  y,
868                        gint    *page,
869                        gint    *x_offset,
870                        gint    *y_offset)
871 {
872         gint width, height;
873
874         ev_page_cache_get_size (view->page_cache,
875                                 view->current_page,
876                                 view->scale,
877                                 &width, &height);
878
879         x -= (view->border.left + view->spacing);
880         y -= (view->border.top + view->spacing);
881
882         if ((x < 0) || (y < 0) ||
883             (x >= width) || (y >= height)) {
884                 *page = -1;
885                 return;
886         }
887         *page = view->current_page;
888         *x_offset = (gint) x;
889         *y_offset = (gint) y;
890 }
891
892 static EvLink *
893 get_link_at_location (EvView  *view,
894                       gdouble  x,
895                       gdouble  y)
896 {
897         gint page;
898         gint x_offset, y_offset;
899         GList *link_mapping;
900
901         find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
902         if (page == -1)
903                 return NULL;
904
905         link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page);
906
907         return ev_link_mapping_find (link_mapping, x_offset /view->scale, y_offset /view->scale);
908 }
909
910
911 static gboolean
912 ev_view_motion_notify_event (GtkWidget      *widget,
913                              GdkEventMotion *event)
914 {
915         EvView *view = EV_VIEW (widget);
916
917         if (view->pressed_button > 0) {
918                 GdkRectangle selection;
919
920                 view->has_selection = TRUE;
921                 selection.x = MIN (view->selection_start.x, event->x);
922                 selection.y = MIN (view->selection_start.y, event->y);
923                 selection.width = ABS (view->selection_start.x - event->x) + 1;
924                 selection.height = ABS (view->selection_start.y - event->y) + 1;
925                 view_rect_to_doc_rect (view, &selection, &view->selection);
926
927                 gtk_widget_queue_draw (widget);
928         } else if (view->document) {
929                 EvLink *link;
930
931                 link = get_link_at_location (view, event->x, event->y);
932                 if (link) {
933                         char *msg;
934
935                         msg = status_message_from_link (view, link);
936                         ev_view_set_status (view, msg);
937                         ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
938                         g_free (msg);
939                 } else {
940                         ev_view_set_status (view, NULL);
941                         if (view->cursor == EV_VIEW_CURSOR_LINK) {
942                                 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
943                         }
944                 }
945         }
946
947         return TRUE;
948 }
949
950 static gboolean
951 ev_view_button_release_event (GtkWidget      *widget,
952                               GdkEventButton *event)
953 {
954         EvView *view = EV_VIEW (widget);
955
956         view->pressed_button = -1;
957
958         if (view->has_selection) {
959                 ev_view_update_primary_selection (view);
960         } else if (view->document) {
961                 EvLink *link;
962
963                 link = get_link_at_location (view, event->x, event->y);
964                 if (link) {
965                         ev_view_go_to_link (view, link);
966                 }
967         }
968
969         return FALSE;
970 }
971
972 static void
973 on_adjustment_value_changed (GtkAdjustment  *adjustment,
974                              EvView *view)
975 {
976         view_update_adjustments (view);
977 }
978
979 static void
980 set_scroll_adjustment (EvView *view,
981                        GtkOrientation  orientation,
982                        GtkAdjustment  *adjustment)
983 {
984         GtkAdjustment **to_set;
985
986         if (orientation == GTK_ORIENTATION_HORIZONTAL)
987                 to_set = &view->hadjustment;
988         else
989                 to_set = &view->vadjustment;
990
991         if (*to_set != adjustment) {
992                 if (*to_set) {
993                         g_signal_handlers_disconnect_by_func (*to_set,
994                                                               (gpointer) on_adjustment_value_changed,
995                                                               view);
996                         g_object_unref (*to_set);
997                 }
998
999                 *to_set = adjustment;
1000                 view_set_adjustment_values (view, orientation);
1001
1002                 if (*to_set) {
1003                         g_object_ref (*to_set);
1004                         g_signal_connect (*to_set, "value_changed",
1005                                           G_CALLBACK (on_adjustment_value_changed), view);
1006                 }
1007         }
1008 }
1009
1010 static void
1011 ev_view_set_scroll_adjustments (EvView *view,
1012                                 GtkAdjustment  *hadjustment,
1013                                 GtkAdjustment  *vadjustment)
1014 {
1015         set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
1016         set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
1017
1018         view_update_adjustments (view);
1019 }
1020
1021 static void
1022 add_scroll_binding_keypad (GtkBindingSet  *binding_set,
1023                            guint           keyval,
1024                            GtkScrollType   scroll,
1025                            gboolean        horizontal)
1026 {
1027   guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
1028
1029   gtk_binding_entry_add_signal (binding_set, keyval, 0,
1030                                 "scroll_view", 2,
1031                                 GTK_TYPE_SCROLL_TYPE, scroll,
1032                                 G_TYPE_BOOLEAN, horizontal);
1033   gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
1034                                 "scroll_view", 2,
1035                                 GTK_TYPE_SCROLL_TYPE, scroll,
1036                                 G_TYPE_BOOLEAN, horizontal);
1037 }
1038
1039 static void
1040 add_scroll_binding_shifted (GtkBindingSet  *binding_set,
1041                             guint           keyval,
1042                             GtkScrollType   scroll_normal,
1043                             GtkScrollType   scroll_shifted,
1044                             gboolean        horizontal)
1045 {
1046   gtk_binding_entry_add_signal (binding_set, keyval, 0,
1047                                 "scroll_view", 2,
1048                                 GTK_TYPE_SCROLL_TYPE, scroll_normal,
1049                                 G_TYPE_BOOLEAN, horizontal);
1050   gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
1051                                 "scroll_view", 2,
1052                                 GTK_TYPE_SCROLL_TYPE, scroll_shifted,
1053                                 G_TYPE_BOOLEAN, horizontal);
1054 }
1055
1056 static void
1057 ev_view_jump (EvView        *view,
1058               GtkScrollType  scroll)
1059 {
1060         GtkAdjustment *adjustment;
1061         double value, increment;
1062         gboolean first_page = FALSE;
1063         gboolean last_page = FALSE;
1064
1065         /* Assign values for increment and vertical adjustment */
1066         adjustment = view->vadjustment;
1067         increment = adjustment->page_size * 0.75;
1068         value = adjustment->value;
1069
1070         /* Assign boolean for first and last page */
1071         if (view->current_page == 0)
1072                 first_page = TRUE;
1073         if (view->current_page == ev_page_cache_get_n_pages (view->page_cache) - 1)
1074                 last_page = TRUE;
1075
1076         switch (scroll) {
1077                 case EV_SCROLL_PAGE_BACKWARD:
1078                         /* Do not jump backwards if at the first page */
1079                         if (value == (adjustment->lower) && first_page) {
1080                                 /* Do nothing */
1081                                 /* At the top of a page, assign the upper bound limit of previous page */
1082                         } else if (value == (adjustment->lower)) {
1083                                 value = adjustment->upper - adjustment->page_size;
1084                                 ev_page_cache_set_current_page (view->page_cache, view->current_page - 1);
1085                                 /* Jump to the top */
1086                         } else {
1087                                 value = MAX (value - increment, adjustment->lower);
1088                         }
1089                         break;
1090                 case EV_SCROLL_PAGE_FORWARD:
1091                         /* Do not jump forward if at the last page */
1092                         if (value == (adjustment->upper - adjustment->page_size) && last_page) {
1093                                 /* Do nothing */
1094                         /* At the bottom of a page, assign the lower bound limit of next page */
1095                         } else if (value == (adjustment->upper - adjustment->page_size)) {
1096                                 value = 0;
1097                                 ev_page_cache_set_current_page (view->page_cache, view->current_page + 1);
1098                         /* Jump to the bottom */
1099                         } else {
1100                                 value = MIN (value + increment, adjustment->upper - adjustment->page_size);
1101                         }
1102                         break;
1103                 default:
1104                         break;
1105         }
1106
1107         gtk_adjustment_set_value (adjustment, value);
1108 }
1109
1110 static void
1111 ev_view_scroll_view (EvView *view,
1112                      GtkScrollType scroll,
1113                      gboolean horizontal)
1114 {
1115         if (scroll == GTK_SCROLL_PAGE_BACKWARD) {
1116                 ev_page_cache_prev_page (view->page_cache);
1117         } else if (scroll == GTK_SCROLL_PAGE_FORWARD) {
1118                 ev_page_cache_next_page (view->page_cache);
1119         } else if (scroll == EV_SCROLL_PAGE_BACKWARD || scroll == EV_SCROLL_PAGE_FORWARD) {
1120                 ev_view_jump (view, scroll);
1121         } else {
1122                 GtkAdjustment *adjustment;
1123                 double value;
1124
1125                 if (horizontal) {
1126                         adjustment = view->hadjustment;
1127                 } else {
1128                         adjustment = view->vadjustment;
1129                 }
1130
1131                 value = adjustment->value;
1132
1133                 switch (scroll) {
1134                         case GTK_SCROLL_STEP_BACKWARD:
1135                                 value -= adjustment->step_increment;
1136                                 break;
1137                         case GTK_SCROLL_STEP_FORWARD:
1138                                 value += adjustment->step_increment;
1139                                 break;
1140                         default:
1141                                 break;
1142                 }
1143
1144                 value = CLAMP (value, adjustment->lower,
1145                                adjustment->upper - adjustment->page_size);
1146
1147                 gtk_adjustment_set_value (adjustment, value);
1148         }
1149 }
1150
1151 static void
1152 ev_view_set_property (GObject *object,
1153                       guint prop_id,
1154                       const GValue *value,
1155                       GParamSpec *pspec)
1156 {
1157         switch (prop_id)
1158         {
1159                 /* Read only */
1160                 case PROP_STATUS:
1161                 case PROP_FIND_STATUS:
1162                         break;
1163         }
1164 }
1165
1166 static void
1167 ev_view_get_property (GObject *object,
1168                       guint prop_id,
1169                       GValue *value,
1170                       GParamSpec *pspec)
1171 {
1172         EvView *view = EV_VIEW (object);
1173
1174         switch (prop_id)
1175         {
1176                 case PROP_STATUS:
1177                         g_value_set_string (value, view->status);
1178                         break;
1179                 case PROP_FIND_STATUS:
1180                         g_value_set_string (value, view->status);
1181                         break;
1182         }
1183 }
1184
1185 static void
1186 ev_view_class_init (EvViewClass *class)
1187 {
1188         GObjectClass *object_class = G_OBJECT_CLASS (class);
1189         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
1190         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1191         GtkBindingSet *binding_set;
1192
1193         object_class->finalize = ev_view_finalize;
1194         object_class->set_property = ev_view_set_property;
1195         object_class->get_property = ev_view_get_property;
1196
1197         widget_class->expose_event = ev_view_expose_event;
1198         widget_class->button_press_event = ev_view_button_press_event;
1199         widget_class->motion_notify_event = ev_view_motion_notify_event;
1200         widget_class->button_release_event = ev_view_button_release_event;
1201         widget_class->size_request = ev_view_size_request;
1202         widget_class->size_allocate = ev_view_size_allocate;
1203         widget_class->realize = ev_view_realize;
1204         widget_class->unrealize = ev_view_unrealize;
1205         gtk_object_class->destroy = ev_view_destroy;
1206
1207         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
1208         class->scroll_view = ev_view_scroll_view;
1209
1210         widget_class->set_scroll_adjustments_signal =  g_signal_new ("set-scroll-adjustments",
1211                                                                      G_OBJECT_CLASS_TYPE (object_class),
1212                                                                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1213                                                                      G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
1214                                                                      NULL, NULL,
1215                                                                      ev_marshal_VOID__OBJECT_OBJECT,
1216                                                                      G_TYPE_NONE, 2,
1217                                                                      GTK_TYPE_ADJUSTMENT,
1218                                                                      GTK_TYPE_ADJUSTMENT);
1219
1220         g_signal_new ("scroll_view",
1221                       G_TYPE_FROM_CLASS (object_class),
1222                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1223                       G_STRUCT_OFFSET (EvViewClass, scroll_view),
1224                       NULL, NULL,
1225                       ev_marshal_VOID__ENUM_BOOLEAN,
1226                       G_TYPE_NONE, 2,
1227                       GTK_TYPE_SCROLL_TYPE,
1228                       G_TYPE_BOOLEAN);
1229
1230         g_object_class_install_property (object_class,
1231                                          PROP_STATUS,
1232                                          g_param_spec_string ("status",
1233                                                               "Status Message",
1234                                                               "The status message",
1235                                                               NULL,
1236                                                               G_PARAM_READABLE));
1237
1238         g_object_class_install_property (object_class,
1239                                          PROP_FIND_STATUS,
1240                                          g_param_spec_string ("find-status",
1241                                                               "Find Status Message",
1242                                                               "The find status message",
1243                                                               NULL,
1244                                                               G_PARAM_READABLE));
1245
1246         binding_set = gtk_binding_set_by_class (class);
1247
1248         add_scroll_binding_keypad (binding_set, GDK_Left,  GTK_SCROLL_STEP_BACKWARD, TRUE);
1249         add_scroll_binding_keypad (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD,  TRUE);
1250         add_scroll_binding_keypad (binding_set, GDK_Up,    GTK_SCROLL_STEP_BACKWARD, FALSE);
1251         add_scroll_binding_keypad (binding_set, GDK_Down,  GTK_SCROLL_STEP_FORWARD,  FALSE);
1252
1253         add_scroll_binding_keypad (binding_set, GDK_Page_Up,   GTK_SCROLL_PAGE_BACKWARD, FALSE);
1254         add_scroll_binding_keypad (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_FORWARD,  FALSE);
1255
1256         add_scroll_binding_shifted (binding_set, GDK_space, EV_SCROLL_PAGE_FORWARD, EV_SCROLL_PAGE_BACKWARD, FALSE);
1257         add_scroll_binding_shifted (binding_set, GDK_BackSpace, EV_SCROLL_PAGE_BACKWARD, EV_SCROLL_PAGE_FORWARD, FALSE);
1258 }
1259
1260 static void
1261 ev_view_init (EvView *view)
1262 {
1263         GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
1264
1265         view->spacing = 10;
1266         view->scale = 1.0;
1267         view->current_page = 0;
1268         view->pressed_button = -1;
1269         view->cursor = EV_VIEW_CURSOR_NORMAL;
1270 }
1271
1272 static void
1273 update_find_status_message (EvView *view)
1274 {
1275         char *message;
1276
1277 //      g_mutex_lock (EV_DOC_MUTEX);
1278         if (view->current_page == view->find_page) {
1279                 int results;
1280
1281 //              g_mutex_lock (EV_DOC_MUTEX);
1282                 results = ev_document_find_get_n_results
1283                                 (EV_DOCUMENT_FIND (view->document),
1284                                  view->current_page);
1285 //              g_mutex_unlock (EV_DOC_MUTEX);
1286                 /* TRANS: Sometimes this could be better translated as
1287                    "%d hit(s) on this page".  Therefore this string
1288                    contains plural cases. */
1289                 message = g_strdup_printf (ngettext ("%d found on this page",
1290                                                      "%d found on this page",
1291                                                      results),
1292                                            results);
1293         } else {
1294                 double percent;
1295
1296                 g_mutex_lock (EV_DOC_MUTEX);
1297                 percent = ev_document_find_get_progress
1298                                 (EV_DOCUMENT_FIND (view->document));
1299                 g_mutex_unlock (EV_DOC_MUTEX);
1300                 if (percent >= (1.0 - 1e-10)) {
1301                         message = g_strdup (_("Not found"));
1302                 } else {
1303                         message = g_strdup_printf (_("%3d%% remaining to search"),
1304                                                    (int) ((1.0 - percent) * 100));
1305                 }
1306
1307         }
1308 //      g_mutex_unlock (EV_DOC_MUTEX);
1309
1310         ev_view_set_find_status (view, message);
1311 //      g_free (message);
1312 }
1313
1314 #define MARGIN 5
1315
1316 static void
1317 ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect)
1318 {
1319         GtkWidget *widget = GTK_WIDGET (view);
1320         GtkAdjustment *adjustment;
1321         int value;
1322
1323         adjustment = view->vadjustment;
1324
1325         if (rect->y < adjustment->value) {
1326                 value = MAX (adjustment->lower, rect->y - MARGIN);
1327                 gtk_adjustment_set_value (view->vadjustment, value);
1328         } else if (rect->y + rect->height >
1329                    adjustment->value + widget->allocation.height) {
1330                 value = MIN (adjustment->upper, rect->y + rect->height -
1331                              widget->allocation.height + MARGIN);
1332                 gtk_adjustment_set_value (view->vadjustment, value);
1333         }
1334
1335         adjustment = view->hadjustment;
1336
1337         if (rect->x < adjustment->value) {
1338                 value = MAX (adjustment->lower, rect->x - MARGIN);
1339                 gtk_adjustment_set_value (view->hadjustment, value);
1340         } else if (rect->x + rect->height >
1341                    adjustment->value + widget->allocation.width) {
1342                 value = MIN (adjustment->upper, rect->x + rect->width -
1343                              widget->allocation.width + MARGIN);
1344                 gtk_adjustment_set_value (view->hadjustment, value);
1345         }
1346 }
1347
1348 static void
1349 jump_to_find_result (EvView *view)
1350 {
1351         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
1352         EvRectangle rect;
1353         GdkRectangle view_rect;
1354         int n_results;
1355
1356         g_mutex_lock (EV_DOC_MUTEX);
1357         n_results = ev_document_find_get_n_results (find, view->current_page);
1358         g_mutex_unlock (EV_DOC_MUTEX);
1359
1360         if (n_results > view->find_result) {
1361                 g_mutex_lock (EV_DOC_MUTEX);
1362                 ev_document_find_get_result
1363                         (find, view->current_page, view->find_result, &rect);
1364                 g_mutex_unlock (EV_DOC_MUTEX);
1365
1366                 doc_rect_to_view_rect (view, &rect, &view_rect);
1367                 ensure_rectangle_is_visible (view, &view_rect);
1368         }
1369 }
1370
1371 static void
1372 jump_to_find_page (EvView *view)
1373 {
1374         int n_pages, i;
1375
1376         n_pages = ev_page_cache_get_n_pages (view->page_cache);
1377
1378         for (i = 0; i < n_pages; i++) {
1379                 int has_results;
1380                 int page;
1381
1382                 page = i + view->find_page;
1383                 if (page >= n_pages) {
1384                         page = page - n_pages;
1385                 }
1386
1387                 //              g_mutex_lock (EV_DOC_MUTEX);
1388                 has_results = ev_document_find_page_has_results
1389                                 (EV_DOCUMENT_FIND (view->document), page);
1390                 if (has_results == -1) {
1391                         view->find_page = page;
1392                         break;
1393                 } else if (has_results == 1) {
1394                         ev_page_cache_set_current_page (view->page_cache, page);
1395                         jump_to_find_result (view);
1396                         break;
1397                 }
1398         }
1399 }
1400
1401 static void
1402 find_changed_cb (EvDocument *document, int page, EvView *view)
1403 {
1404         jump_to_find_page (view);
1405         jump_to_find_result (view);
1406         update_find_status_message (view);
1407
1408         if (view->current_page == page)
1409                 gtk_widget_queue_draw (GTK_WIDGET (view));
1410 }
1411 /*** Public API ***/
1412
1413 GtkWidget*
1414 ev_view_new (void)
1415 {
1416         return g_object_new (EV_TYPE_VIEW, NULL);
1417 }
1418
1419 static void
1420 job_finished_cb (EvPixbufCache *pixbuf_cache,
1421                  EvView        *view)
1422 {
1423         gtk_widget_queue_draw (GTK_WIDGET (view));
1424 }
1425
1426
1427 static void
1428 page_changed_cb (EvPageCache *page_cache,
1429                  int          new_page,
1430                  EvView      *view)
1431 {
1432         int old_page = view->current_page;
1433         int old_width, old_height;
1434         int new_width, new_height;
1435
1436         if (old_page == new_page)
1437                 return;
1438
1439         ev_page_cache_get_size (page_cache,
1440                                 old_page,
1441                                 view->scale,
1442                                 &old_width, &old_height);
1443
1444         view->current_page = new_page;
1445         view->has_selection = FALSE;
1446
1447         compute_zoom_factor (view);
1448
1449         ev_page_cache_get_size (page_cache,
1450                                 new_page,
1451                                 view->scale,
1452                                 &new_width, &new_height);
1453
1454         ev_document_misc_get_page_border_size
1455                 (new_width, new_height, &(view->border));
1456
1457         ev_pixbuf_cache_set_page_range (view->pixbuf_cache,
1458                                         view->current_page,
1459                                         view->current_page,
1460                                         view->scale);
1461
1462         if (new_width != old_width || new_height != old_height)
1463                 gtk_widget_queue_resize (GTK_WIDGET (view));
1464         else
1465                 gtk_widget_queue_draw (GTK_WIDGET (view));
1466
1467         gtk_adjustment_set_value (view->vadjustment,
1468                                   view->vadjustment->lower);
1469
1470         if (EV_IS_DOCUMENT_FIND (view->document)) {
1471                 view->find_page = new_page;
1472                 view->find_result = 0;
1473                 update_find_status_message (view);
1474         }
1475 }
1476
1477 void
1478 ev_view_set_document (EvView     *view,
1479                       EvDocument *document)
1480 {
1481         g_return_if_fail (EV_IS_VIEW (view));
1482
1483         if (document != view->document) {
1484                 if (view->document) {
1485                         g_signal_handlers_disconnect_by_func (view->document,
1486                                                               find_changed_cb,
1487                                                               view);
1488                         g_object_unref (view->document);
1489                         view->page_cache = NULL;
1490
1491                 }
1492
1493                 view->document = document;
1494                 view->find_page = 0;
1495                 view->find_result = 0;
1496
1497                 if (view->document) {
1498                         g_object_ref (view->document);
1499                         if (EV_IS_DOCUMENT_FIND (view->document)) {
1500                                 g_signal_connect (view->document,
1501                                                   "find_changed",
1502                                                   G_CALLBACK (find_changed_cb),
1503                                                   view);
1504                         }
1505                         view->page_cache = ev_document_get_page_cache (view->document);
1506                         g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
1507                         view->pixbuf_cache = ev_pixbuf_cache_new (view->document);
1508                         g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
1509                 }
1510
1511                 gtk_widget_queue_resize (GTK_WIDGET (view));
1512         }
1513 }
1514
1515 int
1516 ev_view_get_page        (EvView     *view)
1517 {
1518         return view->current_page;
1519 }
1520
1521 static void
1522 go_to_link (EvView *view, EvLink *link)
1523 {
1524         EvLinkType type;
1525         const char *uri;
1526         int page;
1527
1528         type = ev_link_get_link_type (link);
1529
1530         switch (type) {
1531                 case EV_LINK_TYPE_TITLE:
1532                         break;
1533                 case EV_LINK_TYPE_PAGE:
1534                         page = ev_link_get_page (link);
1535                         ev_page_cache_set_current_page (view->page_cache, page);
1536                         break;
1537                 case EV_LINK_TYPE_EXTERNAL_URI:
1538                         uri = ev_link_get_uri (link);
1539                         gnome_vfs_url_show (uri);
1540                         break;
1541         }
1542 }
1543
1544 void
1545 ev_view_go_to_link (EvView *view, EvLink *link)
1546 {
1547         go_to_link (view, link);
1548 }
1549
1550 static void
1551 ev_view_zoom (EvView   *view,
1552               double    factor,
1553               gboolean  relative)
1554 {
1555         double scale;
1556
1557         if (relative)
1558                 scale = view->scale * factor;
1559         else
1560                 scale = factor;
1561
1562         scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
1563
1564         view->scale = scale;
1565         view->width = view->height = -1;
1566         gtk_widget_queue_resize (GTK_WIDGET (view));
1567 }
1568
1569 void
1570 ev_view_zoom_in (EvView *view)
1571 {
1572         ev_view_zoom (view, ZOOM_IN_FACTOR, TRUE);
1573 }
1574
1575 void
1576 ev_view_zoom_out (EvView *view)
1577 {
1578         ev_view_zoom (view, ZOOM_OUT_FACTOR, TRUE);
1579 }
1580
1581 void
1582 ev_view_set_size (EvView     *view,
1583                   int         width,
1584                   int         height)
1585 {
1586         if (!view->document) {
1587                 return;
1588         }
1589
1590         if (width != view->width || height != view->height) {
1591                 view->width = width;
1592                 view->height = height;
1593                 gtk_widget_queue_resize (GTK_WIDGET (view));
1594         }
1595 }
1596
1597 const char *
1598 ev_view_get_status (EvView *view)
1599 {
1600         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
1601
1602         return view->status;
1603 }
1604
1605 const char *
1606 ev_view_get_find_status (EvView *view)
1607 {
1608         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
1609
1610         return view->find_status;
1611 }
1612
1613 gboolean
1614 ev_view_can_find_next (EvView *view)
1615 {
1616         int n_results = 0;
1617
1618         if (EV_IS_DOCUMENT_FIND (view->document)) {
1619                 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
1620
1621                 g_mutex_lock (EV_DOC_MUTEX);
1622                 n_results = ev_document_find_get_n_results (find, view->current_page);
1623                 g_mutex_unlock (EV_DOC_MUTEX);
1624         }
1625
1626         return n_results > 0;
1627 }
1628
1629 void
1630 ev_view_find_next (EvView *view)
1631 {
1632         EvPageCache *page_cache;
1633         int n_results, n_pages;
1634         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
1635
1636         page_cache = ev_document_get_page_cache (view->document);
1637         g_mutex_lock (EV_DOC_MUTEX);
1638         n_results = ev_document_find_get_n_results (find, view->current_page);
1639         g_mutex_unlock (EV_DOC_MUTEX);
1640
1641         n_pages = ev_page_cache_get_n_pages (page_cache);
1642
1643         view->find_result++;
1644
1645         if (view->find_result >= n_results) {
1646                 view->find_result = 0;
1647                 view->find_page++;
1648
1649                 if (view->find_page >= n_pages) {
1650                         view->find_page = 0;
1651                 }
1652
1653                 jump_to_find_page (view);
1654         } else {
1655                 jump_to_find_result (view);
1656                 gtk_widget_queue_draw (GTK_WIDGET (view));
1657         }
1658 }
1659
1660 void
1661 ev_view_find_previous (EvView *view)
1662 {
1663         int n_results, n_pages;
1664         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
1665         EvPageCache *page_cache;
1666
1667         page_cache = ev_document_get_page_cache (view->document);
1668
1669         g_mutex_lock (EV_DOC_MUTEX);
1670         n_results = ev_document_find_get_n_results (find, view->current_page);
1671         g_mutex_unlock (EV_DOC_MUTEX);
1672
1673         n_pages = ev_page_cache_get_n_pages (page_cache);
1674
1675         view->find_result--;
1676
1677         if (view->find_result < 0) {
1678                 view->find_result = 0;
1679                 view->find_page--;
1680
1681                 if (view->find_page < 0) {
1682                         view->find_page = n_pages - 1;
1683                 }
1684
1685                 jump_to_find_page (view);
1686         } else {
1687                 jump_to_find_result (view);
1688                 gtk_widget_queue_draw (GTK_WIDGET (view));
1689         }
1690 }
1691 void
1692 ev_view_hide_cursor (EvView *view)
1693 {
1694        ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN);
1695 }
1696
1697 void
1698 ev_view_show_cursor (EvView *view)
1699 {
1700        ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1701 }