]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
f52b372778c11d56b31ac5991454292b3405a9fe
[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
51 static void ev_view_set_scroll_adjustments (EvView         *view,
52                                             GtkAdjustment  *hadjustment,
53                                             GtkAdjustment  *vadjustment);
54      
55 G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET)
56
57 /*** Helper functions ***/       
58      
59 static void
60 view_update_adjustments (EvView *view)
61 {
62         int old_x = view->scroll_x;
63         int old_y = view->scroll_y;
64   
65         if (view->hadjustment)
66                 view->scroll_x = view->hadjustment->value;
67         else
68                 view->scroll_x = 0;
69
70         if (view->vadjustment)
71                 view->scroll_y = view->vadjustment->value;
72         else
73                 view->scroll_y = 0;
74   
75         if (GTK_WIDGET_REALIZED (view) &&
76             (view->scroll_x != old_x || view->scroll_y != old_y)) {
77                 gdk_window_move (view->bin_window, - view->scroll_x, - view->scroll_y);
78                 gdk_window_process_updates (view->bin_window, TRUE);
79         }
80 }
81
82 static void
83 view_set_adjustment_values (EvView         *view,
84                             GtkOrientation  orientation)
85 {
86         GtkWidget *widget = GTK_WIDGET (view);
87         GtkAdjustment *adjustment;
88         gboolean value_changed = FALSE;
89         int requisition;
90         int allocation;
91
92         if (orientation == GTK_ORIENTATION_HORIZONTAL)  {
93                 requisition = widget->requisition.width;
94                 allocation = widget->allocation.width;
95                 adjustment = view->hadjustment;
96         } else {
97                 requisition = widget->requisition.height;
98                 allocation = widget->allocation.height;
99                 adjustment = view->vadjustment;
100         }
101
102         if (!adjustment)
103                 return;
104   
105         adjustment->page_size = allocation;
106         adjustment->step_increment = allocation * 0.1;
107         adjustment->page_increment = allocation * 0.9;
108         adjustment->lower = 0;
109         adjustment->upper = MAX (allocation, requisition);
110
111         if (adjustment->value > adjustment->upper - adjustment->page_size) {
112                 adjustment->value = adjustment->upper - adjustment->page_size;
113                 value_changed = TRUE;
114         }
115
116         gtk_adjustment_changed (adjustment);
117         if (value_changed)
118                 gtk_adjustment_value_changed (adjustment);
119 }
120
121 /*** Virtual function implementations ***/       
122      
123 static void
124 ev_view_finalize (GObject *object)
125 {
126         EvView *view = EV_VIEW (object);
127
128         if (view->document)
129                 g_object_unref (view->document);
130
131         ev_view_set_scroll_adjustments (view, NULL, NULL);
132
133         G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
134 }
135
136 static void
137 ev_view_destroy (GtkObject *object)
138 {
139         EvView *view = EV_VIEW (object);
140
141         ev_view_set_scroll_adjustments (view, NULL, NULL);
142   
143         GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
144 }
145
146 static void
147 ev_view_size_request (GtkWidget      *widget,
148                       GtkRequisition *requisition)
149 {
150         EvView *view = EV_VIEW (widget);
151
152         if (GTK_WIDGET_REALIZED (widget)) {
153                 if (view->document) {
154                         ev_document_get_page_size (view->document,
155                                                    &requisition->width,
156                                                    &requisition->height);
157                 } else {
158                         requisition->width = 10;
159                         requisition->height = 10;
160                 }
161         }
162   
163 }
164
165 static void
166 ev_view_size_allocate (GtkWidget      *widget,
167                        GtkAllocation  *allocation)
168 {
169         EvView *view = EV_VIEW (widget);
170
171         GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
172
173         view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
174         view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
175
176         if (GTK_WIDGET_REALIZED (widget)) {
177                 gdk_window_resize (view->bin_window,
178                                    MAX (widget->allocation.width, widget->requisition.width),
179                                    MAX (widget->allocation.height, widget->requisition.height));
180         }
181 }
182
183 static void
184 update_window_backgrounds (EvView *view)
185 {
186         GtkWidget *widget = GTK_WIDGET (view);
187   
188         if (GTK_WIDGET_REALIZED (view)) {
189                 gdk_window_set_background (view->bin_window,
190                                            &widget->style->base[GTK_WIDGET_STATE (widget)]);
191         }
192 }
193
194 static void
195 ev_view_realize (GtkWidget *widget)
196 {
197         EvView *view = EV_VIEW (widget);
198         GdkWindowAttr attributes;
199
200         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
201   
202         attributes.window_type = GDK_WINDOW_CHILD;
203         attributes.wclass = GDK_INPUT_OUTPUT;
204         attributes.visual = gtk_widget_get_visual (widget);
205         attributes.colormap = gtk_widget_get_colormap (widget);
206   
207         attributes.x = widget->allocation.x;
208         attributes.y = widget->allocation.y;
209         attributes.width = widget->allocation.width;
210         attributes.height = widget->allocation.height;
211         attributes.event_mask = 0;
212   
213         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
214                                          &attributes,
215                                          GDK_WA_X | GDK_WA_Y |
216                                          GDK_WA_COLORMAP |
217                                          GDK_WA_VISUAL);
218         gdk_window_set_user_data (widget->window, widget);
219         widget->style = gtk_style_attach (widget->style, widget->window);
220   
221         attributes.x = 0;
222         attributes.y = 0;
223         attributes.width = MAX (widget->allocation.width, widget->requisition.width);
224         attributes.height = MAX (widget->allocation.height, widget->requisition.height);
225         attributes.event_mask = GDK_EXPOSURE_MASK;
226   
227         view->bin_window = gdk_window_new (widget->window,
228                                            &attributes,
229                                            GDK_WA_X | GDK_WA_Y |
230                                            GDK_WA_COLORMAP |
231                                            GDK_WA_VISUAL);
232         gdk_window_set_user_data (view->bin_window, widget);
233         gdk_window_show (view->bin_window);
234
235         if (view->document) {
236                 ev_document_set_target (view->document, view->bin_window);
237
238                 /* We can't get page size without a target, so we have to
239                  * queue a size request at realization. Could be fixed
240                  * with EvDocument changes to allow setting a GdkScreen
241                  * without setting a target.
242                  */
243                 gtk_widget_queue_resize (widget);
244         }
245
246         update_window_backgrounds (view);
247 }
248
249 static void
250 ev_view_unrealize (GtkWidget *widget)
251 {
252         EvView *view = EV_VIEW (widget);
253
254         if (view->document)
255                 ev_document_set_target (view->document, NULL);
256
257         gdk_window_set_user_data (view->bin_window, NULL);
258         gdk_window_destroy (view->bin_window);
259         view->bin_window = NULL;
260
261         GTK_WIDGET_CLASS (ev_view_parent_class)->unrealize (widget);
262 }
263
264 static void
265 ev_view_style_set (GtkWidget      *widget,
266                    GtkStyle       *previous_style)
267 {
268         update_window_backgrounds (EV_VIEW (widget));
269 }
270
271 static void
272 ev_view_state_changed (GtkWidget    *widget,
273                        GtkStateType  previous_state)
274 {
275         update_window_backgrounds (EV_VIEW (widget));
276 }
277
278 static void
279 expose_bin_window (GtkWidget      *widget,
280                    GdkEventExpose *event)
281 {
282         EvView *view = EV_VIEW (widget);
283         
284         if (view->document)
285                 ev_document_render (view->document,
286                                     event->area.x, event->area.y,
287                                     event->area.width, event->area.height);
288 }
289
290 static gboolean
291 ev_view_expose_event (GtkWidget      *widget,
292                       GdkEventExpose *event)
293 {
294         EvView *view = EV_VIEW (widget);
295
296         if (event->window == view->bin_window)
297                 expose_bin_window (widget, event);
298         else
299                 return GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event (widget, event);
300
301         return FALSE;
302   
303 }
304
305 static gboolean
306 ev_view_button_press_event (GtkWidget      *widget,
307                             GdkEventButton *event)
308 {
309         /* EvView *view = EV_VIEW (widget); */
310
311         return FALSE;
312 }
313
314 static gboolean
315 ev_view_motion_notify_event (GtkWidget      *widget,
316                              GdkEventMotion *event)
317 {
318         /* EvView *view = EV_VIEW (widget); */
319   
320         return FALSE;
321 }
322
323 static gboolean
324 ev_view_button_release_event (GtkWidget      *widget,
325                               GdkEventButton *event)
326 {
327         /* EvView *view = EV_VIEW (widget); */
328
329         return FALSE;
330 }
331
332 static void
333 on_adjustment_value_changed (GtkAdjustment  *adjustment,
334                              EvView *view)
335 {
336         view_update_adjustments (view);
337 }
338
339 static void
340 set_scroll_adjustment (EvView *view,
341                        GtkOrientation  orientation,
342                        GtkAdjustment  *adjustment)
343 {
344         GtkAdjustment **to_set;
345
346         if (orientation == GTK_ORIENTATION_HORIZONTAL)
347                 to_set = &view->hadjustment;
348         else
349                 to_set = &view->vadjustment;
350   
351         if (*to_set != adjustment) {
352                 if (*to_set) {
353                         g_signal_handlers_disconnect_by_func (*to_set,
354                                                               (gpointer) on_adjustment_value_changed,
355                                                               view);
356                         g_object_unref (*to_set);
357                 }
358
359                 *to_set = adjustment;
360                 view_set_adjustment_values (view, orientation);
361
362                 if (*to_set) {
363                         g_object_ref (*to_set);
364                         g_signal_connect (*to_set, "value_changed",
365                                           G_CALLBACK (on_adjustment_value_changed), view);
366                 }
367         }
368 }
369
370 static void
371 ev_view_set_scroll_adjustments (EvView *view,
372                                 GtkAdjustment  *hadjustment,
373                                 GtkAdjustment  *vadjustment)
374 {
375         set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
376         set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
377
378         view_update_adjustments (view);
379 }
380
381 static void
382 ev_view_class_init (EvViewClass *class)
383 {
384         GObjectClass *object_class = G_OBJECT_CLASS (class);
385         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
386         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
387
388         object_class->finalize = ev_view_finalize;
389
390         widget_class->expose_event = ev_view_expose_event;
391         widget_class->button_press_event = ev_view_button_press_event;
392         widget_class->motion_notify_event = ev_view_motion_notify_event;
393         widget_class->button_release_event = ev_view_button_release_event;
394         widget_class->size_request = ev_view_size_request;
395         widget_class->size_allocate = ev_view_size_allocate;
396         widget_class->realize = ev_view_realize;
397         widget_class->unrealize = ev_view_unrealize;
398         widget_class->style_set = ev_view_style_set;
399         widget_class->state_changed = ev_view_state_changed;
400         gtk_object_class->destroy = ev_view_destroy;
401   
402         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
403
404         widget_class->set_scroll_adjustments_signal =  g_signal_new ("set-scroll-adjustments",
405                                                                      G_OBJECT_CLASS_TYPE (object_class),
406                                                                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
407                                                                      G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
408                                                                      NULL, NULL,
409                                                                      ev_marshal_VOID__OBJECT_OBJECT,
410                                                                      G_TYPE_NONE, 2,
411                                                                      GTK_TYPE_ADJUSTMENT,
412                                                                      GTK_TYPE_ADJUSTMENT);
413 }
414
415 static void
416 ev_view_init (EvView *view)
417 {
418         static const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
419         
420         gtk_widget_modify_bg (GTK_WIDGET (view), GTK_STATE_NORMAL, &white);
421 }
422
423 /*** Public API ***/       
424      
425 GtkWidget*
426 ev_view_new (void)
427 {
428         return g_object_new (EV_TYPE_VIEW, NULL);
429 }
430
431 void
432 ev_view_set_document (EvView     *view,
433                       EvDocument *document)
434 {
435         g_return_if_fail (EV_IS_VIEW (view));
436
437         if (document != view->document) {
438                 if (view->document)
439                         g_object_unref (view->document);
440
441                 view->document = document;
442
443                 if (view->document)
444                         g_object_ref (view->document);
445
446                 if (GTK_WIDGET_REALIZED (view))
447                         ev_document_set_target (view->document, view->bin_window);
448                 
449                 gtk_widget_queue_resize (GTK_WIDGET (view));
450         }
451 }