]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
d8b499b860d1b683efc0677c24ce58e3d18b67ef
[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_BUTTON_PRESS_MASK |
250                                 GDK_SCROLL_MASK |
251                                 GDK_KEY_PRESS_MASK;
252   
253         view->bin_window = gdk_window_new (widget->window,
254                                            &attributes,
255                                            GDK_WA_X | GDK_WA_Y |
256                                            GDK_WA_COLORMAP |
257                                            GDK_WA_VISUAL);
258         gdk_window_set_user_data (view->bin_window, widget);
259         gdk_window_show (view->bin_window);
260
261         if (view->document) {
262                 ev_document_set_target (view->document, view->bin_window);
263
264                 /* We can't get page size without a target, so we have to
265                  * queue a size request at realization. Could be fixed
266                  * with EvDocument changes to allow setting a GdkScreen
267                  * without setting a target.
268                  */
269                 gtk_widget_queue_resize (widget);
270         }
271
272         update_window_backgrounds (view);
273 }
274
275 static void
276 ev_view_unrealize (GtkWidget *widget)
277 {
278         EvView *view = EV_VIEW (widget);
279
280         if (view->document)
281                 ev_document_set_target (view->document, NULL);
282
283         gdk_window_set_user_data (view->bin_window, NULL);
284         gdk_window_destroy (view->bin_window);
285         view->bin_window = NULL;
286
287         GTK_WIDGET_CLASS (ev_view_parent_class)->unrealize (widget);
288 }
289
290 static void
291 ev_view_style_set (GtkWidget      *widget,
292                    GtkStyle       *previous_style)
293 {
294         update_window_backgrounds (EV_VIEW (widget));
295 }
296
297 static void
298 ev_view_state_changed (GtkWidget    *widget,
299                        GtkStateType  previous_state)
300 {
301         update_window_backgrounds (EV_VIEW (widget));
302 }
303
304 static void
305 expose_bin_window (GtkWidget      *widget,
306                    GdkEventExpose *event)
307 {
308         EvView *view = EV_VIEW (widget);
309         int i;
310         int current_page;
311         const EvFindResult *results;
312
313         if (view->document == NULL)
314                 return;
315         
316         ev_document_render (view->document,
317                             event->area.x, event->area.y,
318                             event->area.width, event->area.height);
319
320         results = (EvFindResult*) view->find_results->data;
321         current_page = ev_document_get_page (view->document);
322         i = 0;
323         while (i < view->find_results->len) {
324 #if 0
325                 g_printerr ("highlighting result %d page %d at %d,%d %dx%d\n",
326                             i, results[i].page_num,
327                             results[i].highlight_area.x,
328                             results[i].highlight_area.y,
329                             results[i].highlight_area.width,
330                             results[i].highlight_area.height);
331 #endif
332                 if (results[i].page_num == current_page)
333                         gdk_draw_rectangle (view->bin_window,
334                                             widget->style->base_gc[GTK_STATE_SELECTED],
335                                             FALSE,
336                                             results[i].highlight_area.x,
337                                             results[i].highlight_area.y,
338                                             results[i].highlight_area.width,
339                                             results[i].highlight_area.height);
340                 ++i;
341         }
342 }
343
344 static gboolean
345 ev_view_expose_event (GtkWidget      *widget,
346                       GdkEventExpose *event)
347 {
348         EvView *view = EV_VIEW (widget);
349
350         if (event->window == view->bin_window)
351                 expose_bin_window (widget, event);
352         else
353                 return GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event (widget, event);
354
355         return FALSE;
356 }
357
358 static gboolean
359 ev_view_button_press_event (GtkWidget      *widget,
360                             GdkEventButton *event)
361 {
362         if (event->type == GDK_BUTTON_PRESS) {
363                 if (!GTK_WIDGET_HAS_FOCUS (widget)) {
364                         gtk_widget_grab_focus (widget);
365                 }
366         }
367
368         return TRUE;
369 }
370
371 static gboolean
372 ev_view_motion_notify_event (GtkWidget      *widget,
373                              GdkEventMotion *event)
374 {
375         /* EvView *view = EV_VIEW (widget); */
376   
377         return FALSE;
378 }
379
380 static gboolean
381 ev_view_button_release_event (GtkWidget      *widget,
382                               GdkEventButton *event)
383 {
384         /* EvView *view = EV_VIEW (widget); */
385
386         return FALSE;
387 }
388
389 static void
390 on_adjustment_value_changed (GtkAdjustment  *adjustment,
391                              EvView *view)
392 {
393         view_update_adjustments (view);
394 }
395
396 static void
397 set_scroll_adjustment (EvView *view,
398                        GtkOrientation  orientation,
399                        GtkAdjustment  *adjustment)
400 {
401         GtkAdjustment **to_set;
402
403         if (orientation == GTK_ORIENTATION_HORIZONTAL)
404                 to_set = &view->hadjustment;
405         else
406                 to_set = &view->vadjustment;
407   
408         if (*to_set != adjustment) {
409                 if (*to_set) {
410                         g_signal_handlers_disconnect_by_func (*to_set,
411                                                               (gpointer) on_adjustment_value_changed,
412                                                               view);
413                         g_object_unref (*to_set);
414                 }
415
416                 *to_set = adjustment;
417                 view_set_adjustment_values (view, orientation);
418
419                 if (*to_set) {
420                         g_object_ref (*to_set);
421                         g_signal_connect (*to_set, "value_changed",
422                                           G_CALLBACK (on_adjustment_value_changed), view);
423                 }
424         }
425 }
426
427 static void
428 ev_view_set_scroll_adjustments (EvView *view,
429                                 GtkAdjustment  *hadjustment,
430                                 GtkAdjustment  *vadjustment)
431 {
432         set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
433         set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
434
435         view_update_adjustments (view);
436 }
437
438 static void
439 add_scroll_binding (GtkBindingSet  *binding_set,
440                     guint           keyval,
441                     GtkScrollType   scroll,
442                     gboolean        horizontal)
443 {
444   guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
445   
446   gtk_binding_entry_add_signal (binding_set, keyval, 0,
447                                 "scroll_view", 2,
448                                 GTK_TYPE_SCROLL_TYPE, scroll,
449                                 G_TYPE_BOOLEAN, horizontal);
450   gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
451                                 "scroll_view", 2,
452                                 GTK_TYPE_SCROLL_TYPE, scroll,
453                                 G_TYPE_BOOLEAN, horizontal);
454 }
455
456 static void
457 ev_view_scroll_view (EvView *view,
458                      GtkScrollType scroll,
459                      gboolean horizontal)
460 {
461         if (scroll == GTK_SCROLL_PAGE_BACKWARD) {
462                 ev_view_set_page (view, ev_view_get_page (view) - 1);
463         } else if (scroll == GTK_SCROLL_PAGE_FORWARD) {
464                 ev_view_set_page (view, ev_view_get_page (view) + 1);
465         } else {
466                 GtkAdjustment *adjustment;
467                 double value;
468
469                 if (horizontal) {
470                         adjustment = view->hadjustment; 
471                 } else {
472                         adjustment = view->vadjustment;
473                 }
474
475                 value = adjustment->value;
476
477                 switch (scroll) {
478                         case GTK_SCROLL_STEP_BACKWARD:  
479                                 value -= adjustment->step_increment; 
480                                 break;
481                         case GTK_SCROLL_STEP_FORWARD:
482                                 value += adjustment->step_increment; 
483                                 break;
484                         default:
485                                 break;
486                 }
487
488                 value = CLAMP (value, adjustment->lower,
489                                adjustment->upper - adjustment->page_size);
490
491                 gtk_adjustment_set_value (adjustment, value);
492         }
493 }
494
495 static void
496 ev_view_class_init (EvViewClass *class)
497 {
498         GObjectClass *object_class = G_OBJECT_CLASS (class);
499         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
500         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
501         GtkBindingSet *binding_set;
502
503         object_class->finalize = ev_view_finalize;
504
505         widget_class->expose_event = ev_view_expose_event;
506         widget_class->button_press_event = ev_view_button_press_event;
507         widget_class->motion_notify_event = ev_view_motion_notify_event;
508         widget_class->button_release_event = ev_view_button_release_event;
509         widget_class->size_request = ev_view_size_request;
510         widget_class->size_allocate = ev_view_size_allocate;
511         widget_class->realize = ev_view_realize;
512         widget_class->unrealize = ev_view_unrealize;
513         widget_class->style_set = ev_view_style_set;
514         widget_class->state_changed = ev_view_state_changed;
515         gtk_object_class->destroy = ev_view_destroy;
516
517         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
518         class->scroll_view = ev_view_scroll_view;
519
520         widget_class->set_scroll_adjustments_signal =  g_signal_new ("set-scroll-adjustments",
521                                                                      G_OBJECT_CLASS_TYPE (object_class),
522                                                                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
523                                                                      G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
524                                                                      NULL, NULL,
525                                                                      ev_marshal_VOID__OBJECT_OBJECT,
526                                                                      G_TYPE_NONE, 2,
527                                                                      GTK_TYPE_ADJUSTMENT,
528                                                                      GTK_TYPE_ADJUSTMENT);
529         page_changed_signal = g_signal_new ("page-changed",
530                                             G_OBJECT_CLASS_TYPE (object_class),
531                                             G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
532                                             G_STRUCT_OFFSET (EvViewClass, page_changed),
533                                             NULL, NULL,
534                                             ev_marshal_VOID__NONE,
535                                             G_TYPE_NONE, 0);
536
537         g_signal_new ("find-status-changed",
538                       G_OBJECT_CLASS_TYPE (object_class),
539                       G_SIGNAL_RUN_LAST,
540                       0,
541                       NULL, NULL,
542                       ev_marshal_VOID__NONE,
543                       G_TYPE_NONE, 0);
544
545         g_signal_new ("scroll_view",
546                       G_TYPE_FROM_CLASS (object_class),
547                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
548                       G_STRUCT_OFFSET (EvViewClass, scroll_view),
549                       NULL, NULL,
550                       ev_marshal_VOID__ENUM_BOOLEAN,
551                       G_TYPE_NONE, 2,
552                       GTK_TYPE_SCROLL_TYPE,
553                       G_TYPE_BOOLEAN);
554
555         binding_set = gtk_binding_set_by_class (class);
556
557         add_scroll_binding (binding_set, GDK_Left,  GTK_SCROLL_STEP_BACKWARD, TRUE);
558         add_scroll_binding (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD,  TRUE);
559         add_scroll_binding (binding_set, GDK_Up,    GTK_SCROLL_STEP_BACKWARD, FALSE);
560         add_scroll_binding (binding_set, GDK_Down,  GTK_SCROLL_STEP_FORWARD,  FALSE);
561
562         add_scroll_binding (binding_set, GDK_Page_Up,   GTK_SCROLL_PAGE_BACKWARD, FALSE);
563         add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_FORWARD,  FALSE);
564 }
565
566 static void
567 ev_view_init (EvView *view)
568 {
569         static const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
570
571         GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
572
573         view->scale = 1.0;
574         
575         gtk_widget_modify_bg (GTK_WIDGET (view), GTK_STATE_NORMAL, &white);
576
577         view->find_results = g_array_new (FALSE,
578                                           FALSE,
579                                           sizeof (EvFindResult));
580         view->results_on_this_page = 0;
581         view->next_page_with_result = 0;
582 }
583
584 static void
585 update_find_results (EvView *view)
586 {
587         const EvFindResult *results;
588         int i;
589         int on_this_page;
590         int next_page_with_result;
591         int earliest_page_with_result;
592         int current_page;
593         gboolean counts_changed;
594         
595         results = (EvFindResult*) view->find_results->data;
596         current_page = ev_document_get_page (view->document);
597         next_page_with_result = 0;
598         on_this_page = 0;
599         earliest_page_with_result = 0;
600         
601         i = 0;
602         while (i < view->find_results->len) {
603                 if (results[i].page_num == current_page) {
604                         ++on_this_page;
605                 } else {
606                         int delta = results[i].page_num - current_page;
607                         
608                         if (delta > 0 && /* result on later page */
609                             (next_page_with_result == 0 ||
610                              results[i].page_num < next_page_with_result))
611                                 next_page_with_result = results[i].page_num;
612
613                         if (delta < 0 && /* result on a previous page */
614                             (earliest_page_with_result == 0 ||
615                              results[i].page_num < earliest_page_with_result))
616                                 earliest_page_with_result = results[i].page_num;
617                 }
618                 ++i;
619         }
620
621         /* If earliest page is just the current page, there is no earliest page */
622         if (earliest_page_with_result == current_page)
623                 earliest_page_with_result = 0;
624         
625         /* If no next page, then wrap and the wrapped page is the next page */
626         if (next_page_with_result == 0)
627                 next_page_with_result = earliest_page_with_result;
628
629         counts_changed = FALSE;
630         if (on_this_page != view->results_on_this_page ||
631             next_page_with_result != view->next_page_with_result) {
632                 view->results_on_this_page = on_this_page;
633                 view->next_page_with_result = next_page_with_result;
634                 counts_changed = TRUE;
635         }
636
637         /* If there are no results at all, then the
638          * results of ev_view_get_find_status_message() will change
639          * to reflect the percent_complete so we have to emit the signal
640          */
641         if (counts_changed ||
642             view->find_results->len == 0) {
643                 g_signal_emit_by_name (view,
644                                        "find-status-changed");
645         }
646 }
647
648 static void
649 found_results_callback (EvDocument         *document,
650                         const EvFindResult *results,
651                         int                 n_results,
652                         double              percent_complete,
653                         void               *data)
654 {
655   EvView *view = EV_VIEW (data);
656   
657   g_array_set_size (view->find_results, 0);
658
659   if (n_results > 0)
660           g_array_append_vals (view->find_results,
661                                results, n_results);
662
663 #if 0
664   {
665           int i;
666
667           g_printerr ("%d results %d%%: ", n_results,
668                       (int) (percent_complete * 100));
669           i = 0;
670           while (i < n_results) {
671                   g_printerr ("%d ", results[i].page_num);
672                   ++i;
673           }
674           g_printerr ("\n");
675   }
676 #endif
677
678   view->find_percent_complete = percent_complete;
679   update_find_results (view);
680   
681   gtk_widget_queue_draw (GTK_WIDGET (view));
682 }
683
684 /*** Public API ***/       
685      
686 GtkWidget*
687 ev_view_new (void)
688 {
689         return g_object_new (EV_TYPE_VIEW, NULL);
690 }
691
692 static void
693 document_changed_callback (EvDocument *document,
694                            EvView     *view)
695 {
696         gtk_widget_queue_draw (GTK_WIDGET (view));
697 }
698
699 void
700 ev_view_set_document (EvView     *view,
701                       EvDocument *document)
702 {
703         g_return_if_fail (EV_IS_VIEW (view));
704
705         if (document != view->document) {
706                 int old_page = ev_view_get_page (view);
707                 
708                 if (view->document) {
709                         g_signal_handlers_disconnect_by_func (view->document,
710                                                               found_results_callback,
711                                                               view);
712                         g_array_set_size (view->find_results, 0);
713                         view->results_on_this_page = 0;
714                         view->next_page_with_result = 0;
715
716                         g_object_unref (view->document);
717                 }
718
719                 view->document = document;
720
721                 if (view->document) {
722                         g_object_ref (view->document);
723                         g_signal_connect (view->document,
724                                           "found",
725                                           G_CALLBACK (found_results_callback),
726                                           view);
727                         g_signal_connect (view->document,
728                                           "changed",
729                                           G_CALLBACK (document_changed_callback),
730                                           view);
731                 }
732
733                 if (GTK_WIDGET_REALIZED (view))
734                         ev_document_set_target (view->document, view->bin_window);
735                 
736                 gtk_widget_queue_resize (GTK_WIDGET (view));
737                 
738                 if (old_page != ev_view_get_page (view))
739                         g_signal_emit (view, page_changed_signal, 0);
740         }
741 }
742
743 void
744 ev_view_set_page (EvView *view,
745                   int     page)
746 {
747         if (view->document) {
748                 int old_page = ev_document_get_page (view->document);
749                 if (old_page != page)
750                         ev_document_set_page (view->document, page);
751                 if (old_page != ev_document_get_page (view->document)) {
752                         g_signal_emit (view, page_changed_signal, 0);
753
754                         view->find_percent_complete = 0.0;
755                         update_find_results (view);     
756                 }
757         }
758 }
759
760 int
761 ev_view_get_page (EvView *view)
762 {
763         if (view->document)
764                 return ev_document_get_page (view->document);
765         else
766                 return 1;
767 }
768
769 #define ZOOM_IN_FACTOR  1.2
770 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)
771
772 #define MIN_SCALE 0.05409
773 #define MAX_SCALE 18.4884
774
775 static void
776 ev_view_zoom (EvView   *view,
777               double    factor,
778               gboolean  relative)
779 {
780         double scale;
781
782         if (relative)
783                 scale = view->scale * factor;
784         else
785                 scale = factor;
786
787         view->scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
788
789         ev_document_set_scale (view->document, view->scale);
790
791         gtk_widget_queue_resize (GTK_WIDGET (view));
792 }
793
794 void
795 ev_view_zoom_in (EvView *view)
796 {
797         ev_view_zoom (view, ZOOM_IN_FACTOR, TRUE);
798 }
799
800 void
801 ev_view_zoom_out (EvView *view)
802 {
803         ev_view_zoom (view, ZOOM_OUT_FACTOR, TRUE);
804 }
805
806 void
807 ev_view_normal_size (EvView *view)
808 {
809         ev_view_zoom (view, 1.0, FALSE);
810 }
811
812 void
813 ev_view_best_fit (EvView *view)
814 {
815         double scale;
816         int width, height;
817
818         width = height = 0;
819         ev_document_get_page_size (view->document, &width, &height);
820
821         scale = 1.0;
822         if (width != 0 && height != 0) {
823                 double scale_w, scale_h;
824
825                 scale_w = (double)GTK_WIDGET (view)->allocation.width * view->scale / width;
826                 scale_h = (double)GTK_WIDGET (view)->allocation.height * view->scale / height;
827
828                 scale = (scale_w < scale_h) ? scale_w : scale_h;
829         }
830
831         ev_view_zoom (view, scale, FALSE);
832 }
833
834 void
835 ev_view_fit_width (EvView *view)
836 {
837         double scale = 1.0;
838         int width;
839
840         width = 0;
841         ev_document_get_page_size (view->document, &width, NULL);
842
843         scale = 1.0;
844         if (width != 0)
845                 scale = (double)GTK_WIDGET (view)->allocation.width * view->scale / width;
846
847         ev_view_zoom (view, scale, FALSE);
848 }
849
850 char*
851 ev_view_get_find_status_message (EvView *view)
852 {
853         if (view->find_results->len == 0) {
854                 if (view->find_percent_complete >= (1.0 - 1e-10)) {
855                         return g_strdup (_("Not found"));
856                 } else {
857                         return g_strdup_printf (_("%3d%% remaining to search"),
858                                                 (int) ((1.0 - view->find_percent_complete) * 100));
859                 }
860         } else if (view->results_on_this_page == 0) {
861                 g_assert (view->next_page_with_result != 0);
862                 return g_strdup_printf (_("Found on page %d"),
863                                         view->next_page_with_result);
864         } else {
865                 return g_strdup_printf (_("%d found on this page"),
866                                         view->results_on_this_page);
867         }
868 }