]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
Add key bindings to the view. Now if the focus would work right...
[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 <gdk/gdkkeysyms.h>
25
26 #include "ev-marshal.h"
27 #include "ev-view.h"
28 #include "ev-document-find.h"
29
30 #define EV_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass))
31 #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW))
32 #define EV_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_VIEW, EvViewClass))
33
34 struct _EvView {
35         GtkWidget parent_instance;
36
37         EvDocument *document;
38         
39         GdkWindow *bin_window;
40         
41         int scroll_x;
42         int scroll_y;
43
44         GtkAdjustment *hadjustment;
45         GtkAdjustment *vadjustment;
46
47         GArray *find_results;
48         int results_on_this_page;
49         int next_page_with_result;
50         double find_percent_complete;
51
52         double scale;
53 };
54
55 struct _EvViewClass {
56         GtkWidgetClass parent_class;
57
58         void    (*set_scroll_adjustments) (EvView         *view,
59                                            GtkAdjustment  *hadjustment,
60                                            GtkAdjustment  *vadjustment);
61         void    (*scroll_view)            (EvView         *view,
62                                            GtkScrollType   scroll,
63                                            gboolean        horizontal);
64         
65         /* Should this be notify::page? */
66         void    (*page_changed)           (EvView         *view);
67 };
68
69 static guint page_changed_signal = 0;
70
71 static void ev_view_set_scroll_adjustments (EvView         *view,
72                                             GtkAdjustment  *hadjustment,
73                                             GtkAdjustment  *vadjustment);
74      
75 G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET)
76
77 /*** Helper functions ***/       
78      
79 static void
80 view_update_adjustments (EvView *view)
81 {
82         int old_x = view->scroll_x;
83         int old_y = view->scroll_y;
84   
85         if (view->hadjustment)
86                 view->scroll_x = view->hadjustment->value;
87         else
88                 view->scroll_x = 0;
89
90         if (view->vadjustment)
91                 view->scroll_y = view->vadjustment->value;
92         else
93                 view->scroll_y = 0;
94   
95         if (GTK_WIDGET_REALIZED (view) &&
96             (view->scroll_x != old_x || view->scroll_y != old_y)) {
97                 gdk_window_move (view->bin_window, - view->scroll_x, - view->scroll_y);
98                 gdk_window_process_updates (view->bin_window, TRUE);
99         }
100 }
101
102 static void
103 view_set_adjustment_values (EvView         *view,
104                             GtkOrientation  orientation)
105 {
106         GtkWidget *widget = GTK_WIDGET (view);
107         GtkAdjustment *adjustment;
108         gboolean value_changed = FALSE;
109         int requisition;
110         int allocation;
111
112         if (orientation == GTK_ORIENTATION_HORIZONTAL)  {
113                 requisition = widget->requisition.width;
114                 allocation = widget->allocation.width;
115                 adjustment = view->hadjustment;
116         } else {
117                 requisition = widget->requisition.height;
118                 allocation = widget->allocation.height;
119                 adjustment = view->vadjustment;
120         }
121
122         if (!adjustment)
123                 return;
124   
125         adjustment->page_size = allocation;
126         adjustment->step_increment = allocation * 0.1;
127         adjustment->page_increment = allocation * 0.9;
128         adjustment->lower = 0;
129         adjustment->upper = MAX (allocation, requisition);
130
131         if (adjustment->value > adjustment->upper - adjustment->page_size) {
132                 adjustment->value = adjustment->upper - adjustment->page_size;
133                 value_changed = TRUE;
134         }
135
136         gtk_adjustment_changed (adjustment);
137         if (value_changed)
138                 gtk_adjustment_value_changed (adjustment);
139 }
140
141 /*** Virtual function implementations ***/       
142      
143 static void
144 ev_view_finalize (GObject *object)
145 {
146         EvView *view = EV_VIEW (object);
147
148         if (view->document)
149                 g_object_unref (view->document);
150
151         ev_view_set_scroll_adjustments (view, NULL, NULL);
152
153         g_array_free (view->find_results, TRUE);
154         view->find_results = NULL;
155         
156         G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
157 }
158
159 static void
160 ev_view_destroy (GtkObject *object)
161 {
162         EvView *view = EV_VIEW (object);
163
164         ev_view_set_scroll_adjustments (view, NULL, NULL);
165   
166         GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
167 }
168
169 static void
170 ev_view_size_request (GtkWidget      *widget,
171                       GtkRequisition *requisition)
172 {
173         EvView *view = EV_VIEW (widget);
174
175         if (GTK_WIDGET_REALIZED (widget)) {
176                 if (view->document) {
177                         ev_document_get_page_size (view->document,
178                                                    &requisition->width,
179                                                    &requisition->height);
180                 } else {
181                         requisition->width = 10;
182                         requisition->height = 10;
183                 }
184         }
185   
186 }
187
188 static void
189 ev_view_size_allocate (GtkWidget      *widget,
190                        GtkAllocation  *allocation)
191 {
192         EvView *view = EV_VIEW (widget);
193
194         GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
195
196         view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
197         view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
198
199         if (GTK_WIDGET_REALIZED (widget)) {
200                 gdk_window_resize (view->bin_window,
201                                    MAX (widget->allocation.width, widget->requisition.width),
202                                    MAX (widget->allocation.height, widget->requisition.height));
203         }
204 }
205
206 static void
207 update_window_backgrounds (EvView *view)
208 {
209         GtkWidget *widget = GTK_WIDGET (view);
210   
211         if (GTK_WIDGET_REALIZED (view)) {
212                 gdk_window_set_background (view->bin_window,
213                                            &widget->style->base[GTK_WIDGET_STATE (widget)]);
214         }
215 }
216
217 static void
218 ev_view_realize (GtkWidget *widget)
219 {
220         EvView *view = EV_VIEW (widget);
221         GdkWindowAttr attributes;
222
223         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
224   
225         attributes.window_type = GDK_WINDOW_CHILD;
226         attributes.wclass = GDK_INPUT_OUTPUT;
227         attributes.visual = gtk_widget_get_visual (widget);
228         attributes.colormap = gtk_widget_get_colormap (widget);
229   
230         attributes.x = widget->allocation.x;
231         attributes.y = widget->allocation.y;
232         attributes.width = widget->allocation.width;
233         attributes.height = widget->allocation.height;
234         attributes.event_mask = 0;
235   
236         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
237                                          &attributes,
238                                          GDK_WA_X | GDK_WA_Y |
239                                          GDK_WA_COLORMAP |
240                                          GDK_WA_VISUAL);
241         gdk_window_set_user_data (widget->window, widget);
242         widget->style = gtk_style_attach (widget->style, widget->window);
243   
244         attributes.x = 0;
245         attributes.y = 0;
246         attributes.width = MAX (widget->allocation.width, widget->requisition.width);
247         attributes.height = MAX (widget->allocation.height, widget->requisition.height);
248         attributes.event_mask = GDK_EXPOSURE_MASK |
249                                 GDK_SCROLL_MASK |
250                                 GDK_KEY_PRESS_MASK;
251   
252         view->bin_window = gdk_window_new (widget->window,
253                                            &attributes,
254                                            GDK_WA_X | GDK_WA_Y |
255                                            GDK_WA_COLORMAP |
256                                            GDK_WA_VISUAL);
257         gdk_window_set_user_data (view->bin_window, widget);
258         gdk_window_show (view->bin_window);
259
260         if (view->document) {
261                 ev_document_set_target (view->document, view->bin_window);
262
263                 /* We can't get page size without a target, so we have to
264                  * queue a size request at realization. Could be fixed
265                  * with EvDocument changes to allow setting a GdkScreen
266                  * without setting a target.
267                  */
268                 gtk_widget_queue_resize (widget);
269         }
270
271         update_window_backgrounds (view);
272 }
273
274 static void
275 ev_view_unrealize (GtkWidget *widget)
276 {
277         EvView *view = EV_VIEW (widget);
278
279         if (view->document)
280                 ev_document_set_target (view->document, NULL);
281
282         gdk_window_set_user_data (view->bin_window, NULL);
283         gdk_window_destroy (view->bin_window);
284         view->bin_window = NULL;
285
286         GTK_WIDGET_CLASS (ev_view_parent_class)->unrealize (widget);
287 }
288
289 static void
290 ev_view_style_set (GtkWidget      *widget,
291                    GtkStyle       *previous_style)
292 {
293         update_window_backgrounds (EV_VIEW (widget));
294 }
295
296 static void
297 ev_view_state_changed (GtkWidget    *widget,
298                        GtkStateType  previous_state)
299 {
300         update_window_backgrounds (EV_VIEW (widget));
301 }
302
303 static void
304 expose_bin_window (GtkWidget      *widget,
305                    GdkEventExpose *event)
306 {
307         EvView *view = EV_VIEW (widget);
308         int i;
309         int current_page;
310         const EvFindResult *results;
311
312         if (view->document == NULL)
313                 return;
314         
315         ev_document_render (view->document,
316                             event->area.x, event->area.y,
317                             event->area.width, event->area.height);
318
319         results = (EvFindResult*) view->find_results->data;
320         current_page = ev_document_get_page (view->document);
321         i = 0;
322         while (i < view->find_results->len) {
323 #if 0
324                 g_printerr ("highlighting result %d page %d at %d,%d %dx%d\n",
325                             i, results[i].page_num,
326                             results[i].highlight_area.x,
327                             results[i].highlight_area.y,
328                             results[i].highlight_area.width,
329                             results[i].highlight_area.height);
330 #endif
331                 if (results[i].page_num == current_page)
332                         gdk_draw_rectangle (view->bin_window,
333                                             widget->style->base_gc[GTK_STATE_SELECTED],
334                                             FALSE,
335                                             results[i].highlight_area.x,
336                                             results[i].highlight_area.y,
337                                             results[i].highlight_area.width,
338                                             results[i].highlight_area.height);
339                 ++i;
340         }
341 }
342
343 static gboolean
344 ev_view_expose_event (GtkWidget      *widget,
345                       GdkEventExpose *event)
346 {
347         EvView *view = EV_VIEW (widget);
348
349         if (event->window == view->bin_window)
350                 expose_bin_window (widget, event);
351         else
352                 return GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event (widget, event);
353
354         return FALSE;
355 }
356
357 static gboolean
358 ev_view_button_press_event (GtkWidget      *widget,
359                             GdkEventButton *event)
360 {
361         /* EvView *view = EV_VIEW (widget); */
362
363         return FALSE;
364 }
365
366 static gboolean
367 ev_view_motion_notify_event (GtkWidget      *widget,
368                              GdkEventMotion *event)
369 {
370         /* EvView *view = EV_VIEW (widget); */
371   
372         return FALSE;
373 }
374
375 static gboolean
376 ev_view_button_release_event (GtkWidget      *widget,
377                               GdkEventButton *event)
378 {
379         /* EvView *view = EV_VIEW (widget); */
380
381         return FALSE;
382 }
383
384 static void
385 on_adjustment_value_changed (GtkAdjustment  *adjustment,
386                              EvView *view)
387 {
388         view_update_adjustments (view);
389 }
390
391 static void
392 set_scroll_adjustment (EvView *view,
393                        GtkOrientation  orientation,
394                        GtkAdjustment  *adjustment)
395 {
396         GtkAdjustment **to_set;
397
398         if (orientation == GTK_ORIENTATION_HORIZONTAL)
399                 to_set = &view->hadjustment;
400         else
401                 to_set = &view->vadjustment;
402   
403         if (*to_set != adjustment) {
404                 if (*to_set) {
405                         g_signal_handlers_disconnect_by_func (*to_set,
406                                                               (gpointer) on_adjustment_value_changed,
407                                                               view);
408                         g_object_unref (*to_set);
409                 }
410
411                 *to_set = adjustment;
412                 view_set_adjustment_values (view, orientation);
413
414                 if (*to_set) {
415                         g_object_ref (*to_set);
416                         g_signal_connect (*to_set, "value_changed",
417                                           G_CALLBACK (on_adjustment_value_changed), view);
418                 }
419         }
420 }
421
422 static void
423 ev_view_set_scroll_adjustments (EvView *view,
424                                 GtkAdjustment  *hadjustment,
425                                 GtkAdjustment  *vadjustment)
426 {
427         set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
428         set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
429
430         view_update_adjustments (view);
431 }
432
433 static void
434 add_scroll_binding (GtkBindingSet  *binding_set,
435                     guint           keyval,
436                     GtkScrollType   scroll,
437                     gboolean        horizontal)
438 {
439   guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
440   
441   gtk_binding_entry_add_signal (binding_set, keyval, 0,
442                                 "scroll_view", 2,
443                                 GTK_TYPE_SCROLL_TYPE, scroll,
444                                 G_TYPE_BOOLEAN, horizontal);
445   gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
446                                 "scroll_view", 2,
447                                 GTK_TYPE_SCROLL_TYPE, scroll,
448                                 G_TYPE_BOOLEAN, horizontal);
449 }
450
451 static void
452 ev_view_scroll_view (EvView *view,
453                       GtkScrollType scroll,
454                       gboolean horizontal)
455 {
456         GtkAdjustment *adjustment;
457         double value;
458
459         if (horizontal) {
460                 adjustment = view->hadjustment; 
461         } else {
462                 adjustment = view->vadjustment;
463         }
464
465         value = adjustment->value;
466
467         switch (scroll) {
468                 case GTK_SCROLL_STEP_BACKWARD:  
469                         value -= adjustment->step_increment; 
470                         break;
471                 case GTK_SCROLL_STEP_FORWARD:
472                         value += adjustment->step_increment; 
473                         break;
474                 case GTK_SCROLL_PAGE_BACKWARD:  
475                         value -= adjustment->page_increment; 
476                         break;
477                 case GTK_SCROLL_PAGE_FORWARD:
478                         value += adjustment->page_increment; 
479                         break;
480                 default:
481                         break;
482         }
483
484         value = CLAMP (value, adjustment->lower, adjustment->upper - adjustment->page_size);
485
486         gtk_adjustment_set_value (adjustment, value);
487 }
488
489 static void
490 ev_view_class_init (EvViewClass *class)
491 {
492         GObjectClass *object_class = G_OBJECT_CLASS (class);
493         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
494         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
495         GtkBindingSet *binding_set;
496
497         object_class->finalize = ev_view_finalize;
498
499         widget_class->expose_event = ev_view_expose_event;
500         widget_class->button_press_event = ev_view_button_press_event;
501         widget_class->motion_notify_event = ev_view_motion_notify_event;
502         widget_class->button_release_event = ev_view_button_release_event;
503         widget_class->size_request = ev_view_size_request;
504         widget_class->size_allocate = ev_view_size_allocate;
505         widget_class->realize = ev_view_realize;
506         widget_class->unrealize = ev_view_unrealize;
507         widget_class->style_set = ev_view_style_set;
508         widget_class->state_changed = ev_view_state_changed;
509         gtk_object_class->destroy = ev_view_destroy;
510
511         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
512         class->scroll_view = ev_view_scroll_view;
513
514         widget_class->set_scroll_adjustments_signal =  g_signal_new ("set-scroll-adjustments",
515                                                                      G_OBJECT_CLASS_TYPE (object_class),
516                                                                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
517                                                                      G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
518                                                                      NULL, NULL,
519                                                                      ev_marshal_VOID__OBJECT_OBJECT,
520                                                                      G_TYPE_NONE, 2,
521                                                                      GTK_TYPE_ADJUSTMENT,
522                                                                      GTK_TYPE_ADJUSTMENT);
523         page_changed_signal = g_signal_new ("page-changed",
524                                             G_OBJECT_CLASS_TYPE (object_class),
525                                             G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
526                                             G_STRUCT_OFFSET (EvViewClass, page_changed),
527                                             NULL, NULL,
528                                             ev_marshal_VOID__NONE,
529                                             G_TYPE_NONE, 0);
530
531         g_signal_new ("find-status-changed",
532                       G_OBJECT_CLASS_TYPE (object_class),
533                       G_SIGNAL_RUN_LAST,
534                       0,
535                       NULL, NULL,
536                       ev_marshal_VOID__NONE,
537                       G_TYPE_NONE, 0);
538
539         g_signal_new ("scroll_view",
540                       G_TYPE_FROM_CLASS (object_class),
541                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
542                       G_STRUCT_OFFSET (EvViewClass, scroll_view),
543                       NULL, NULL,
544                       ev_marshal_VOID__ENUM_BOOLEAN,
545                       G_TYPE_NONE, 2,
546                       GTK_TYPE_SCROLL_TYPE,
547                       G_TYPE_BOOLEAN);
548
549         binding_set = gtk_binding_set_by_class (class);
550
551         add_scroll_binding (binding_set, GDK_Left,  GTK_SCROLL_STEP_BACKWARD, TRUE);
552         add_scroll_binding (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD,  TRUE);
553         add_scroll_binding (binding_set, GDK_Up,    GTK_SCROLL_STEP_BACKWARD, FALSE);
554         add_scroll_binding (binding_set, GDK_Down,  GTK_SCROLL_STEP_FORWARD,  FALSE);
555
556         add_scroll_binding (binding_set, GDK_Page_Up,   GTK_SCROLL_PAGE_BACKWARD, FALSE);
557         add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_FORWARD,  FALSE);
558 }
559
560 static void
561 ev_view_init (EvView *view)
562 {
563         static const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
564
565         GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
566
567         view->scale = 1.0;
568         
569         gtk_widget_modify_bg (GTK_WIDGET (view), GTK_STATE_NORMAL, &white);
570
571         view->find_results = g_array_new (FALSE,
572                                           FALSE,
573                                           sizeof (EvFindResult));
574         view->results_on_this_page = 0;
575         view->next_page_with_result = 0;
576 }
577
578 static void
579 update_find_results (EvView *view)
580 {
581         const EvFindResult *results;
582         int i;
583         int on_this_page;
584         int next_page_with_result;
585         int earliest_page_with_result;
586         int current_page;
587         gboolean counts_changed;
588         
589         results = (EvFindResult*) view->find_results->data;
590         current_page = ev_document_get_page (view->document);
591         next_page_with_result = 0;
592         on_this_page = 0;
593         earliest_page_with_result = 0;
594         
595         i = 0;
596         while (i < view->find_results->len) {
597                 if (results[i].page_num == current_page) {
598                         ++on_this_page;
599                 } else {
600                         int delta = results[i].page_num - current_page;
601                         
602                         if (delta > 0 && /* result on later page */
603                             (next_page_with_result == 0 ||
604                              results[i].page_num < next_page_with_result))
605                                 next_page_with_result = results[i].page_num;
606
607                         if (delta < 0 && /* result on a previous page */
608                             (earliest_page_with_result == 0 ||
609                              results[i].page_num < earliest_page_with_result))
610                                 earliest_page_with_result = results[i].page_num;
611                 }
612                 ++i;
613         }
614
615         /* If earliest page is just the current page, there is no earliest page */
616         if (earliest_page_with_result == current_page)
617                 earliest_page_with_result = 0;
618         
619         /* If no next page, then wrap and the wrapped page is the next page */
620         if (next_page_with_result == 0)
621                 next_page_with_result = earliest_page_with_result;
622
623         counts_changed = FALSE;
624         if (on_this_page != view->results_on_this_page ||
625             next_page_with_result != view->next_page_with_result) {
626                 view->results_on_this_page = on_this_page;
627                 view->next_page_with_result = next_page_with_result;
628                 counts_changed = TRUE;
629         }
630
631         /* If there are no results at all, then the
632          * results of ev_view_get_find_status_message() will change
633          * to reflect the percent_complete so we have to emit the signal
634          */
635         if (counts_changed ||
636             view->find_results->len == 0) {
637                 g_signal_emit_by_name (view,
638                                        "find-status-changed");
639         }
640 }
641
642 static void
643 found_results_callback (EvDocument         *document,
644                         const EvFindResult *results,
645                         int                 n_results,
646                         double              percent_complete,
647                         void               *data)
648 {
649   EvView *view = EV_VIEW (data);
650   
651   g_array_set_size (view->find_results, 0);
652
653   if (n_results > 0)
654           g_array_append_vals (view->find_results,
655                                results, n_results);
656
657 #if 0
658   {
659           int i;
660
661           g_printerr ("%d results %d%%: ", n_results,
662                       (int) (percent_complete * 100));
663           i = 0;
664           while (i < n_results) {
665                   g_printerr ("%d ", results[i].page_num);
666                   ++i;
667           }
668           g_printerr ("\n");
669   }
670 #endif
671
672   view->find_percent_complete = percent_complete;
673   update_find_results (view);
674   
675   gtk_widget_queue_draw (GTK_WIDGET (view));
676 }
677
678 /*** Public API ***/       
679      
680 GtkWidget*
681 ev_view_new (void)
682 {
683         return g_object_new (EV_TYPE_VIEW, NULL);
684 }
685
686 static void
687 document_changed_callback (EvDocument *document,
688                            EvView     *view)
689 {
690         gtk_widget_queue_draw (GTK_WIDGET (view));
691 }
692
693 void
694 ev_view_set_document (EvView     *view,
695                       EvDocument *document)
696 {
697         g_return_if_fail (EV_IS_VIEW (view));
698
699         if (document != view->document) {
700                 int old_page = ev_view_get_page (view);
701                 
702                 if (view->document) {
703                         g_signal_handlers_disconnect_by_func (view->document,
704                                                               found_results_callback,
705                                                               view);
706                         g_array_set_size (view->find_results, 0);
707                         view->results_on_this_page = 0;
708                         view->next_page_with_result = 0;
709
710                         g_object_unref (view->document);
711                 }
712
713                 view->document = document;
714
715                 if (view->document) {
716                         g_object_ref (view->document);
717                         g_signal_connect (view->document,
718                                           "found",
719                                           G_CALLBACK (found_results_callback),
720                                           view);
721                         g_signal_connect (view->document,
722                                           "changed",
723                                           G_CALLBACK (document_changed_callback),
724                                           view);
725                 }
726
727                 if (GTK_WIDGET_REALIZED (view))
728                         ev_document_set_target (view->document, view->bin_window);
729                 
730                 gtk_widget_queue_resize (GTK_WIDGET (view));
731                 
732                 if (old_page != ev_view_get_page (view))
733                         g_signal_emit (view, page_changed_signal, 0);
734         }
735 }
736
737 void
738 ev_view_set_page (EvView *view,
739                   int     page)
740 {
741         if (view->document) {
742                 int old_page = ev_document_get_page (view->document);
743                 if (old_page != page)
744                         ev_document_set_page (view->document, page);
745                 if (old_page != ev_document_get_page (view->document)) {
746                         g_signal_emit (view, page_changed_signal, 0);
747
748                         view->find_percent_complete = 0.0;
749                         update_find_results (view);     
750                 }
751         }
752 }
753
754 int
755 ev_view_get_page (EvView *view)
756 {
757         if (view->document)
758                 return ev_document_get_page (view->document);
759         else
760                 return 1;
761 }
762
763 #define ZOOM_IN_FACTOR  1.2
764 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)
765
766 #define MIN_SCALE 0.05409
767 #define MAX_SCALE 18.4884
768
769 static void
770 ev_view_zoom (EvView   *view,
771               double    factor,
772               gboolean  relative)
773 {
774         double scale;
775
776         if (relative)
777                 scale = view->scale * factor;
778         else
779                 scale = factor;
780
781         view->scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
782
783         ev_document_set_scale (view->document, view->scale);
784
785         gtk_widget_queue_draw (GTK_WIDGET (view));
786 }
787
788 void
789 ev_view_zoom_in (EvView *view)
790 {
791         ev_view_zoom (view, ZOOM_IN_FACTOR, TRUE);
792 }
793
794 void
795 ev_view_zoom_out (EvView *view)
796 {
797         ev_view_zoom (view, ZOOM_OUT_FACTOR, TRUE);
798 }
799
800 void
801 ev_view_normal_size (EvView *view)
802 {
803         ev_view_zoom (view, 1.0, FALSE);
804 }
805
806 void
807 ev_view_best_fit (EvView *view)
808 {
809         double scale;
810         int width, height;
811
812         width = height = 0;
813         ev_document_get_page_size (view->document, &width, &height);
814
815         scale = 1.0;
816         if (width != 0 && height != 0) {
817                 double scale_w, scale_h;
818
819                 scale_w = (double)GTK_WIDGET (view)->allocation.width * view->scale / width;
820                 scale_h = (double)GTK_WIDGET (view)->allocation.height * view->scale / height;
821
822                 scale = (scale_w < scale_h) ? scale_w : scale_h;
823         }
824
825         ev_view_zoom (view, scale, FALSE);
826 }
827
828 void
829 ev_view_fit_width (EvView *view)
830 {
831         double scale = 1.0;
832         int width;
833
834         width = 0;
835         ev_document_get_page_size (view->document, &width, NULL);
836
837         scale = 1.0;
838         if (width != 0)
839                 scale = (double)GTK_WIDGET (view)->allocation.width * view->scale / width;
840
841         ev_view_zoom (view, scale, FALSE);
842 }
843
844 char*
845 ev_view_get_find_status_message (EvView *view)
846 {
847         if (view->find_results->len == 0) {
848                 if (view->find_percent_complete >= (1.0 - 1e-10)) {
849                         return g_strdup (_("Not found"));
850                 } else {
851                         return g_strdup_printf (_("%3d%% remaining to search"),
852                                                 (int) ((1.0 - view->find_percent_complete) * 100));
853                 }
854         } else if (view->results_on_this_page == 0) {
855                 g_assert (view->next_page_with_result != 0);
856                 return g_strdup_printf (_("Found on page %d"),
857                                         view->next_page_with_result);
858         } else {
859                 return g_strdup_printf (_("%d found on this page"),
860                                         view->results_on_this_page);
861         }
862 }