]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
7aaa59aba9a611ab083baa466a81e181a439a03b
[evince.git] / shell / ev-view.c
1 /* this file is part of evince, a gnome document viewer
2  *
3  *  Copyright (C) 2004 Red Hat, Inc
4  *
5  * Evince is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * Evince is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include <gtk/gtkalignment.h>
21
22 #include "ev-marshal.h"
23 #include "ev-view.h"
24
25 #define EV_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass))
26 #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW))
27 #define EV_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_VIEW, EvViewClass))
28
29 struct _EvView {
30         GtkWidget parent_instance;
31
32         EvDocument *document;
33         
34         GdkWindow *bin_window;
35         
36         int scroll_x;
37         int scroll_y;
38
39         GtkAdjustment *hadjustment;
40         GtkAdjustment *vadjustment;
41 };
42
43 struct _EvViewClass {
44         GtkWidgetClass parent_class;
45
46         void    (*set_scroll_adjustments) (EvView         *view,
47                                            GtkAdjustment  *hadjustment,
48                                            GtkAdjustment  *vadjustment);
49         
50         /* Should this be notify::page? */
51         void    (*page_changed)           (EvView         *view);
52 };
53
54 static guint page_changed_signal = 0;
55
56 static void ev_view_set_scroll_adjustments (EvView         *view,
57                                             GtkAdjustment  *hadjustment,
58                                             GtkAdjustment  *vadjustment);
59      
60 G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET)
61
62 /*** Helper functions ***/       
63      
64 static void
65 view_update_adjustments (EvView *view)
66 {
67         int old_x = view->scroll_x;
68         int old_y = view->scroll_y;
69   
70         if (view->hadjustment)
71                 view->scroll_x = view->hadjustment->value;
72         else
73                 view->scroll_x = 0;
74
75         if (view->vadjustment)
76                 view->scroll_y = view->vadjustment->value;
77         else
78                 view->scroll_y = 0;
79   
80         if (GTK_WIDGET_REALIZED (view) &&
81             (view->scroll_x != old_x || view->scroll_y != old_y)) {
82                 gdk_window_move (view->bin_window, - view->scroll_x, - view->scroll_y);
83                 gdk_window_process_updates (view->bin_window, TRUE);
84         }
85 }
86
87 static void
88 view_set_adjustment_values (EvView         *view,
89                             GtkOrientation  orientation)
90 {
91         GtkWidget *widget = GTK_WIDGET (view);
92         GtkAdjustment *adjustment;
93         gboolean value_changed = FALSE;
94         int requisition;
95         int allocation;
96
97         if (orientation == GTK_ORIENTATION_HORIZONTAL)  {
98                 requisition = widget->requisition.width;
99                 allocation = widget->allocation.width;
100                 adjustment = view->hadjustment;
101         } else {
102                 requisition = widget->requisition.height;
103                 allocation = widget->allocation.height;
104                 adjustment = view->vadjustment;
105         }
106
107         if (!adjustment)
108                 return;
109   
110         adjustment->page_size = allocation;
111         adjustment->step_increment = allocation * 0.1;
112         adjustment->page_increment = allocation * 0.9;
113         adjustment->lower = 0;
114         adjustment->upper = MAX (allocation, requisition);
115
116         if (adjustment->value > adjustment->upper - adjustment->page_size) {
117                 adjustment->value = adjustment->upper - adjustment->page_size;
118                 value_changed = TRUE;
119         }
120
121         gtk_adjustment_changed (adjustment);
122         if (value_changed)
123                 gtk_adjustment_value_changed (adjustment);
124 }
125
126 /*** Virtual function implementations ***/       
127      
128 static void
129 ev_view_finalize (GObject *object)
130 {
131         EvView *view = EV_VIEW (object);
132
133         if (view->document)
134                 g_object_unref (view->document);
135
136         ev_view_set_scroll_adjustments (view, NULL, NULL);
137
138         G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
139 }
140
141 static void
142 ev_view_destroy (GtkObject *object)
143 {
144         EvView *view = EV_VIEW (object);
145
146         ev_view_set_scroll_adjustments (view, NULL, NULL);
147   
148         GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
149 }
150
151 static void
152 ev_view_size_request (GtkWidget      *widget,
153                       GtkRequisition *requisition)
154 {
155         EvView *view = EV_VIEW (widget);
156
157         if (GTK_WIDGET_REALIZED (widget)) {
158                 if (view->document) {
159                         ev_document_get_page_size (view->document,
160                                                    &requisition->width,
161                                                    &requisition->height);
162                 } else {
163                         requisition->width = 10;
164                         requisition->height = 10;
165                 }
166         }
167   
168 }
169
170 static void
171 ev_view_size_allocate (GtkWidget      *widget,
172                        GtkAllocation  *allocation)
173 {
174         EvView *view = EV_VIEW (widget);
175
176         GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
177
178         view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
179         view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
180
181         if (GTK_WIDGET_REALIZED (widget)) {
182                 gdk_window_resize (view->bin_window,
183                                    MAX (widget->allocation.width, widget->requisition.width),
184                                    MAX (widget->allocation.height, widget->requisition.height));
185         }
186 }
187
188 static void
189 update_window_backgrounds (EvView *view)
190 {
191         GtkWidget *widget = GTK_WIDGET (view);
192   
193         if (GTK_WIDGET_REALIZED (view)) {
194                 gdk_window_set_background (view->bin_window,
195                                            &widget->style->base[GTK_WIDGET_STATE (widget)]);
196         }
197 }
198
199 static void
200 ev_view_realize (GtkWidget *widget)
201 {
202         EvView *view = EV_VIEW (widget);
203         GdkWindowAttr attributes;
204
205         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
206   
207         attributes.window_type = GDK_WINDOW_CHILD;
208         attributes.wclass = GDK_INPUT_OUTPUT;
209         attributes.visual = gtk_widget_get_visual (widget);
210         attributes.colormap = gtk_widget_get_colormap (widget);
211   
212         attributes.x = widget->allocation.x;
213         attributes.y = widget->allocation.y;
214         attributes.width = widget->allocation.width;
215         attributes.height = widget->allocation.height;
216         attributes.event_mask = 0;
217   
218         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
219                                          &attributes,
220                                          GDK_WA_X | GDK_WA_Y |
221                                          GDK_WA_COLORMAP |
222                                          GDK_WA_VISUAL);
223         gdk_window_set_user_data (widget->window, widget);
224         widget->style = gtk_style_attach (widget->style, widget->window);
225   
226         attributes.x = 0;
227         attributes.y = 0;
228         attributes.width = MAX (widget->allocation.width, widget->requisition.width);
229         attributes.height = MAX (widget->allocation.height, widget->requisition.height);
230         attributes.event_mask = GDK_EXPOSURE_MASK;
231   
232         view->bin_window = gdk_window_new (widget->window,
233                                            &attributes,
234                                            GDK_WA_X | GDK_WA_Y |
235                                            GDK_WA_COLORMAP |
236                                            GDK_WA_VISUAL);
237         gdk_window_set_user_data (view->bin_window, widget);
238         gdk_window_show (view->bin_window);
239
240         if (view->document) {
241                 ev_document_set_target (view->document, view->bin_window);
242
243                 /* We can't get page size without a target, so we have to
244                  * queue a size request at realization. Could be fixed
245                  * with EvDocument changes to allow setting a GdkScreen
246                  * without setting a target.
247                  */
248                 gtk_widget_queue_resize (widget);
249         }
250
251         update_window_backgrounds (view);
252 }
253
254 static void
255 ev_view_unrealize (GtkWidget *widget)
256 {
257         EvView *view = EV_VIEW (widget);
258
259         if (view->document)
260                 ev_document_set_target (view->document, NULL);
261
262         gdk_window_set_user_data (view->bin_window, NULL);
263         gdk_window_destroy (view->bin_window);
264         view->bin_window = NULL;
265
266         GTK_WIDGET_CLASS (ev_view_parent_class)->unrealize (widget);
267 }
268
269 static void
270 ev_view_style_set (GtkWidget      *widget,
271                    GtkStyle       *previous_style)
272 {
273         update_window_backgrounds (EV_VIEW (widget));
274 }
275
276 static void
277 ev_view_state_changed (GtkWidget    *widget,
278                        GtkStateType  previous_state)
279 {
280         update_window_backgrounds (EV_VIEW (widget));
281 }
282
283 static void
284 expose_bin_window (GtkWidget      *widget,
285                    GdkEventExpose *event)
286 {
287         EvView *view = EV_VIEW (widget);
288         
289         if (view->document)
290                 ev_document_render (view->document,
291                                     event->area.x, event->area.y,
292                                     event->area.width, event->area.height);
293 }
294
295 static gboolean
296 ev_view_expose_event (GtkWidget      *widget,
297                       GdkEventExpose *event)
298 {
299         EvView *view = EV_VIEW (widget);
300
301         if (event->window == view->bin_window)
302                 expose_bin_window (widget, event);
303         else
304                 return GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event (widget, event);
305
306         return FALSE;
307   
308 }
309
310 static gboolean
311 ev_view_button_press_event (GtkWidget      *widget,
312                             GdkEventButton *event)
313 {
314         /* EvView *view = EV_VIEW (widget); */
315
316         return FALSE;
317 }
318
319 static gboolean
320 ev_view_motion_notify_event (GtkWidget      *widget,
321                              GdkEventMotion *event)
322 {
323         /* EvView *view = EV_VIEW (widget); */
324   
325         return FALSE;
326 }
327
328 static gboolean
329 ev_view_button_release_event (GtkWidget      *widget,
330                               GdkEventButton *event)
331 {
332         /* EvView *view = EV_VIEW (widget); */
333
334         return FALSE;
335 }
336
337 static void
338 on_adjustment_value_changed (GtkAdjustment  *adjustment,
339                              EvView *view)
340 {
341         view_update_adjustments (view);
342 }
343
344 static void
345 set_scroll_adjustment (EvView *view,
346                        GtkOrientation  orientation,
347                        GtkAdjustment  *adjustment)
348 {
349         GtkAdjustment **to_set;
350
351         if (orientation == GTK_ORIENTATION_HORIZONTAL)
352                 to_set = &view->hadjustment;
353         else
354                 to_set = &view->vadjustment;
355   
356         if (*to_set != adjustment) {
357                 if (*to_set) {
358                         g_signal_handlers_disconnect_by_func (*to_set,
359                                                               (gpointer) on_adjustment_value_changed,
360                                                               view);
361                         g_object_unref (*to_set);
362                 }
363
364                 *to_set = adjustment;
365                 view_set_adjustment_values (view, orientation);
366
367                 if (*to_set) {
368                         g_object_ref (*to_set);
369                         g_signal_connect (*to_set, "value_changed",
370                                           G_CALLBACK (on_adjustment_value_changed), view);
371                 }
372         }
373 }
374
375 static void
376 ev_view_set_scroll_adjustments (EvView *view,
377                                 GtkAdjustment  *hadjustment,
378                                 GtkAdjustment  *vadjustment)
379 {
380         set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
381         set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
382
383         view_update_adjustments (view);
384 }
385
386 static void
387 ev_view_class_init (EvViewClass *class)
388 {
389         GObjectClass *object_class = G_OBJECT_CLASS (class);
390         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
391         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
392
393         object_class->finalize = ev_view_finalize;
394
395         widget_class->expose_event = ev_view_expose_event;
396         widget_class->button_press_event = ev_view_button_press_event;
397         widget_class->motion_notify_event = ev_view_motion_notify_event;
398         widget_class->button_release_event = ev_view_button_release_event;
399         widget_class->size_request = ev_view_size_request;
400         widget_class->size_allocate = ev_view_size_allocate;
401         widget_class->realize = ev_view_realize;
402         widget_class->unrealize = ev_view_unrealize;
403         widget_class->style_set = ev_view_style_set;
404         widget_class->state_changed = ev_view_state_changed;
405         gtk_object_class->destroy = ev_view_destroy;
406   
407         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
408
409         widget_class->set_scroll_adjustments_signal =  g_signal_new ("set-scroll-adjustments",
410                                                                      G_OBJECT_CLASS_TYPE (object_class),
411                                                                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
412                                                                      G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
413                                                                      NULL, NULL,
414                                                                      ev_marshal_VOID__OBJECT_OBJECT,
415                                                                      G_TYPE_NONE, 2,
416                                                                      GTK_TYPE_ADJUSTMENT,
417                                                                      GTK_TYPE_ADJUSTMENT);
418         page_changed_signal = g_signal_new ("page-changed",
419                                             G_OBJECT_CLASS_TYPE (object_class),
420                                             G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
421                                             G_STRUCT_OFFSET (EvViewClass, page_changed),
422                                             NULL, NULL,
423                                             ev_marshal_VOID__NONE,
424                                             G_TYPE_NONE, 0);
425 }
426
427 static void
428 ev_view_init (EvView *view)
429 {
430         static const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
431         
432         gtk_widget_modify_bg (GTK_WIDGET (view), GTK_STATE_NORMAL, &white);
433 }
434
435 /*** Public API ***/       
436      
437 GtkWidget*
438 ev_view_new (void)
439 {
440         return g_object_new (EV_TYPE_VIEW, NULL);
441 }
442
443 void
444 ev_view_set_document (EvView     *view,
445                       EvDocument *document)
446 {
447         g_return_if_fail (EV_IS_VIEW (view));
448
449         if (document != view->document) {
450                 int old_page = ev_view_get_page (view);
451                 
452                 if (view->document)
453                         g_object_unref (view->document);
454
455                 view->document = document;
456
457                 if (view->document)
458                         g_object_ref (view->document);
459
460                 if (GTK_WIDGET_REALIZED (view))
461                         ev_document_set_target (view->document, view->bin_window);
462                 
463                 gtk_widget_queue_resize (GTK_WIDGET (view));
464                 
465                 if (old_page != ev_view_get_page (view))
466                         g_signal_emit (view, page_changed_signal, 0);
467         }
468 }
469
470 void
471 ev_view_set_page (EvView *view,
472                   int     page)
473 {
474         if (view->document) {
475                 int old_page = ev_document_get_page (view->document);
476                 if (old_page != page)
477                         ev_document_set_page (view->document, page);
478                 if (old_page != ev_document_get_page (view->document)) {
479                         g_signal_emit (view, page_changed_signal, 0);
480                         gtk_widget_queue_draw (GTK_WIDGET (view));
481                 }
482         }
483 }
484
485 int
486 ev_view_get_page (EvView *view)
487 {
488         if (view->document)
489                 return ev_document_get_page (view->document);
490         else
491                 return 1;
492 }