]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
b9dda6ad573673b597829ec7d242f21a393787ec
[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         GtkAdjustment *adjustment;
462         double value;
463
464         if (horizontal) {
465                 adjustment = view->hadjustment; 
466         } else {
467                 adjustment = view->vadjustment;
468         }
469
470         value = adjustment->value;
471
472         switch (scroll) {
473                 case GTK_SCROLL_STEP_BACKWARD:  
474                         value -= adjustment->step_increment; 
475                         break;
476                 case GTK_SCROLL_STEP_FORWARD:
477                         value += adjustment->step_increment; 
478                         break;
479                 case GTK_SCROLL_PAGE_BACKWARD:  
480                         value -= adjustment->page_increment; 
481                         break;
482                 case GTK_SCROLL_PAGE_FORWARD:
483                         value += adjustment->page_increment; 
484                         break;
485                 default:
486                         break;
487         }
488
489         value = CLAMP (value, adjustment->lower, adjustment->upper - adjustment->page_size);
490
491         gtk_adjustment_set_value (adjustment, value);
492 }
493
494 static void
495 ev_view_class_init (EvViewClass *class)
496 {
497         GObjectClass *object_class = G_OBJECT_CLASS (class);
498         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
499         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
500         GtkBindingSet *binding_set;
501
502         object_class->finalize = ev_view_finalize;
503
504         widget_class->expose_event = ev_view_expose_event;
505         widget_class->button_press_event = ev_view_button_press_event;
506         widget_class->motion_notify_event = ev_view_motion_notify_event;
507         widget_class->button_release_event = ev_view_button_release_event;
508         widget_class->size_request = ev_view_size_request;
509         widget_class->size_allocate = ev_view_size_allocate;
510         widget_class->realize = ev_view_realize;
511         widget_class->unrealize = ev_view_unrealize;
512         widget_class->style_set = ev_view_style_set;
513         widget_class->state_changed = ev_view_state_changed;
514         gtk_object_class->destroy = ev_view_destroy;
515
516         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
517         class->scroll_view = ev_view_scroll_view;
518
519         widget_class->set_scroll_adjustments_signal =  g_signal_new ("set-scroll-adjustments",
520                                                                      G_OBJECT_CLASS_TYPE (object_class),
521                                                                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
522                                                                      G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
523                                                                      NULL, NULL,
524                                                                      ev_marshal_VOID__OBJECT_OBJECT,
525                                                                      G_TYPE_NONE, 2,
526                                                                      GTK_TYPE_ADJUSTMENT,
527                                                                      GTK_TYPE_ADJUSTMENT);
528         page_changed_signal = g_signal_new ("page-changed",
529                                             G_OBJECT_CLASS_TYPE (object_class),
530                                             G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
531                                             G_STRUCT_OFFSET (EvViewClass, page_changed),
532                                             NULL, NULL,
533                                             ev_marshal_VOID__NONE,
534                                             G_TYPE_NONE, 0);
535
536         g_signal_new ("find-status-changed",
537                       G_OBJECT_CLASS_TYPE (object_class),
538                       G_SIGNAL_RUN_LAST,
539                       0,
540                       NULL, NULL,
541                       ev_marshal_VOID__NONE,
542                       G_TYPE_NONE, 0);
543
544         g_signal_new ("scroll_view",
545                       G_TYPE_FROM_CLASS (object_class),
546                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
547                       G_STRUCT_OFFSET (EvViewClass, scroll_view),
548                       NULL, NULL,
549                       ev_marshal_VOID__ENUM_BOOLEAN,
550                       G_TYPE_NONE, 2,
551                       GTK_TYPE_SCROLL_TYPE,
552                       G_TYPE_BOOLEAN);
553
554         binding_set = gtk_binding_set_by_class (class);
555
556         add_scroll_binding (binding_set, GDK_Left,  GTK_SCROLL_STEP_BACKWARD, TRUE);
557         add_scroll_binding (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD,  TRUE);
558         add_scroll_binding (binding_set, GDK_Up,    GTK_SCROLL_STEP_BACKWARD, FALSE);
559         add_scroll_binding (binding_set, GDK_Down,  GTK_SCROLL_STEP_FORWARD,  FALSE);
560
561         add_scroll_binding (binding_set, GDK_Page_Up,   GTK_SCROLL_PAGE_BACKWARD, FALSE);
562         add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_FORWARD,  FALSE);
563 }
564
565 static void
566 ev_view_init (EvView *view)
567 {
568         static const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
569
570         GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
571
572         view->scale = 1.0;
573         
574         gtk_widget_modify_bg (GTK_WIDGET (view), GTK_STATE_NORMAL, &white);
575
576         view->find_results = g_array_new (FALSE,
577                                           FALSE,
578                                           sizeof (EvFindResult));
579         view->results_on_this_page = 0;
580         view->next_page_with_result = 0;
581 }
582
583 static void
584 update_find_results (EvView *view)
585 {
586         const EvFindResult *results;
587         int i;
588         int on_this_page;
589         int next_page_with_result;
590         int earliest_page_with_result;
591         int current_page;
592         gboolean counts_changed;
593         
594         results = (EvFindResult*) view->find_results->data;
595         current_page = ev_document_get_page (view->document);
596         next_page_with_result = 0;
597         on_this_page = 0;
598         earliest_page_with_result = 0;
599         
600         i = 0;
601         while (i < view->find_results->len) {
602                 if (results[i].page_num == current_page) {
603                         ++on_this_page;
604                 } else {
605                         int delta = results[i].page_num - current_page;
606                         
607                         if (delta > 0 && /* result on later page */
608                             (next_page_with_result == 0 ||
609                              results[i].page_num < next_page_with_result))
610                                 next_page_with_result = results[i].page_num;
611
612                         if (delta < 0 && /* result on a previous page */
613                             (earliest_page_with_result == 0 ||
614                              results[i].page_num < earliest_page_with_result))
615                                 earliest_page_with_result = results[i].page_num;
616                 }
617                 ++i;
618         }
619
620         /* If earliest page is just the current page, there is no earliest page */
621         if (earliest_page_with_result == current_page)
622                 earliest_page_with_result = 0;
623         
624         /* If no next page, then wrap and the wrapped page is the next page */
625         if (next_page_with_result == 0)
626                 next_page_with_result = earliest_page_with_result;
627
628         counts_changed = FALSE;
629         if (on_this_page != view->results_on_this_page ||
630             next_page_with_result != view->next_page_with_result) {
631                 view->results_on_this_page = on_this_page;
632                 view->next_page_with_result = next_page_with_result;
633                 counts_changed = TRUE;
634         }
635
636         /* If there are no results at all, then the
637          * results of ev_view_get_find_status_message() will change
638          * to reflect the percent_complete so we have to emit the signal
639          */
640         if (counts_changed ||
641             view->find_results->len == 0) {
642                 g_signal_emit_by_name (view,
643                                        "find-status-changed");
644         }
645 }
646
647 static void
648 found_results_callback (EvDocument         *document,
649                         const EvFindResult *results,
650                         int                 n_results,
651                         double              percent_complete,
652                         void               *data)
653 {
654   EvView *view = EV_VIEW (data);
655   
656   g_array_set_size (view->find_results, 0);
657
658   if (n_results > 0)
659           g_array_append_vals (view->find_results,
660                                results, n_results);
661
662 #if 0
663   {
664           int i;
665
666           g_printerr ("%d results %d%%: ", n_results,
667                       (int) (percent_complete * 100));
668           i = 0;
669           while (i < n_results) {
670                   g_printerr ("%d ", results[i].page_num);
671                   ++i;
672           }
673           g_printerr ("\n");
674   }
675 #endif
676
677   view->find_percent_complete = percent_complete;
678   update_find_results (view);
679   
680   gtk_widget_queue_draw (GTK_WIDGET (view));
681 }
682
683 /*** Public API ***/       
684      
685 GtkWidget*
686 ev_view_new (void)
687 {
688         return g_object_new (EV_TYPE_VIEW, NULL);
689 }
690
691 static void
692 document_changed_callback (EvDocument *document,
693                            EvView     *view)
694 {
695         gtk_widget_queue_draw (GTK_WIDGET (view));
696 }
697
698 void
699 ev_view_set_document (EvView     *view,
700                       EvDocument *document)
701 {
702         g_return_if_fail (EV_IS_VIEW (view));
703
704         if (document != view->document) {
705                 int old_page = ev_view_get_page (view);
706                 
707                 if (view->document) {
708                         g_signal_handlers_disconnect_by_func (view->document,
709                                                               found_results_callback,
710                                                               view);
711                         g_array_set_size (view->find_results, 0);
712                         view->results_on_this_page = 0;
713                         view->next_page_with_result = 0;
714
715                         g_object_unref (view->document);
716                 }
717
718                 view->document = document;
719
720                 if (view->document) {
721                         g_object_ref (view->document);
722                         g_signal_connect (view->document,
723                                           "found",
724                                           G_CALLBACK (found_results_callback),
725                                           view);
726                         g_signal_connect (view->document,
727                                           "changed",
728                                           G_CALLBACK (document_changed_callback),
729                                           view);
730                 }
731
732                 if (GTK_WIDGET_REALIZED (view))
733                         ev_document_set_target (view->document, view->bin_window);
734                 
735                 gtk_widget_queue_resize (GTK_WIDGET (view));
736                 
737                 if (old_page != ev_view_get_page (view))
738                         g_signal_emit (view, page_changed_signal, 0);
739         }
740 }
741
742 void
743 ev_view_set_page (EvView *view,
744                   int     page)
745 {
746         if (view->document) {
747                 int old_page = ev_document_get_page (view->document);
748                 if (old_page != page)
749                         ev_document_set_page (view->document, page);
750                 if (old_page != ev_document_get_page (view->document)) {
751                         g_signal_emit (view, page_changed_signal, 0);
752
753                         view->find_percent_complete = 0.0;
754                         update_find_results (view);     
755                 }
756         }
757 }
758
759 int
760 ev_view_get_page (EvView *view)
761 {
762         if (view->document)
763                 return ev_document_get_page (view->document);
764         else
765                 return 1;
766 }
767
768 #define ZOOM_IN_FACTOR  1.2
769 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)
770
771 #define MIN_SCALE 0.05409
772 #define MAX_SCALE 18.4884
773
774 static void
775 ev_view_zoom (EvView   *view,
776               double    factor,
777               gboolean  relative)
778 {
779         double scale;
780
781         if (relative)
782                 scale = view->scale * factor;
783         else
784                 scale = factor;
785
786         view->scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
787
788         ev_document_set_scale (view->document, view->scale);
789
790         gtk_widget_queue_draw (GTK_WIDGET (view));
791 }
792
793 void
794 ev_view_zoom_in (EvView *view)
795 {
796         ev_view_zoom (view, ZOOM_IN_FACTOR, TRUE);
797 }
798
799 void
800 ev_view_zoom_out (EvView *view)
801 {
802         ev_view_zoom (view, ZOOM_OUT_FACTOR, TRUE);
803 }
804
805 void
806 ev_view_normal_size (EvView *view)
807 {
808         ev_view_zoom (view, 1.0, FALSE);
809 }
810
811 void
812 ev_view_best_fit (EvView *view)
813 {
814         double scale;
815         int width, height;
816
817         width = height = 0;
818         ev_document_get_page_size (view->document, &width, &height);
819
820         scale = 1.0;
821         if (width != 0 && height != 0) {
822                 double scale_w, scale_h;
823
824                 scale_w = (double)GTK_WIDGET (view)->allocation.width * view->scale / width;
825                 scale_h = (double)GTK_WIDGET (view)->allocation.height * view->scale / height;
826
827                 scale = (scale_w < scale_h) ? scale_w : scale_h;
828         }
829
830         ev_view_zoom (view, scale, FALSE);
831 }
832
833 void
834 ev_view_fit_width (EvView *view)
835 {
836         double scale = 1.0;
837         int width;
838
839         width = 0;
840         ev_document_get_page_size (view->document, &width, NULL);
841
842         scale = 1.0;
843         if (width != 0)
844                 scale = (double)GTK_WIDGET (view)->allocation.width * view->scale / width;
845
846         ev_view_zoom (view, scale, FALSE);
847 }
848
849 char*
850 ev_view_get_find_status_message (EvView *view)
851 {
852         if (view->find_results->len == 0) {
853                 if (view->find_percent_complete >= (1.0 - 1e-10)) {
854                         return g_strdup (_("Not found"));
855                 } else {
856                         return g_strdup_printf (_("%3d%% remaining to search"),
857                                                 (int) ((1.0 - view->find_percent_complete) * 100));
858                 }
859         } else if (view->results_on_this_page == 0) {
860                 g_assert (view->next_page_with_result != 0);
861                 return g_strdup_printf (_("Found on page %d"),
862                                         view->next_page_with_result);
863         } else {
864                 return g_strdup_printf (_("%d found on this page"),
865                                         view->results_on_this_page);
866         }
867 }