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