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