]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
9fbc81afb8c588c2ae794b5bdd6dce6fe9dc3dfe
[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         requisition->width = 500;
153         requisition->height = 500;
154 }
155
156 static void
157 ev_view_size_allocate (GtkWidget      *widget,
158                        GtkAllocation  *allocation)
159 {
160         EvView *view = EV_VIEW (widget);
161
162         GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
163
164         view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
165         view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
166
167         if (GTK_WIDGET_REALIZED (widget)) {
168                 gdk_window_resize (view->bin_window,
169                                    MAX (widget->allocation.width, widget->requisition.width),
170                                    MAX (widget->allocation.height, widget->requisition.height));
171         }
172 }
173
174 static void
175 update_window_backgrounds (EvView *view)
176 {
177         GtkWidget *widget = GTK_WIDGET (view);
178   
179         if (GTK_WIDGET_REALIZED (view)) {
180                 gdk_window_set_background (view->bin_window,
181                                            &widget->style->base[GTK_WIDGET_STATE (widget)]);
182         }
183 }
184
185 static void
186 ev_view_realize (GtkWidget *widget)
187 {
188         EvView *view = EV_VIEW (widget);
189         GdkWindowAttr attributes;
190
191         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
192   
193         attributes.window_type = GDK_WINDOW_CHILD;
194         attributes.wclass = GDK_INPUT_OUTPUT;
195         attributes.visual = gtk_widget_get_visual (widget);
196         attributes.colormap = gtk_widget_get_colormap (widget);
197   
198         attributes.x = widget->allocation.x;
199         attributes.y = widget->allocation.y;
200         attributes.width = widget->allocation.width;
201         attributes.height = widget->allocation.height;
202         attributes.event_mask = 0;
203   
204         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
205                                          &attributes,
206                                          GDK_WA_X | GDK_WA_Y |
207                                          GDK_WA_COLORMAP |
208                                          GDK_WA_VISUAL);
209         gdk_window_set_user_data (widget->window, widget);
210         widget->style = gtk_style_attach (widget->style, widget->window);
211   
212         attributes.x = 0;
213         attributes.y = 0;
214         attributes.width = MAX (widget->allocation.width, widget->requisition.width);
215         attributes.height = MAX (widget->allocation.height, widget->requisition.height);
216         attributes.event_mask = GDK_EXPOSURE_MASK;
217   
218         view->bin_window = gdk_window_new (widget->window,
219                                            &attributes,
220                                            GDK_WA_X | GDK_WA_Y |
221                                            GDK_WA_COLORMAP |
222                                            GDK_WA_VISUAL);
223         gdk_window_set_user_data (view->bin_window, widget);
224         gdk_window_show (view->bin_window);
225
226         if (view->document)
227                 ev_document_set_target (view->document, view->bin_window);
228
229         update_window_backgrounds (view);
230 }
231
232 static void
233 ev_view_unrealize (GtkWidget *widget)
234 {
235         EvView *view = EV_VIEW (widget);
236
237         if (view->document)
238                 ev_document_set_target (view->document, NULL);
239
240         gdk_window_set_user_data (view->bin_window, NULL);
241         gdk_window_destroy (view->bin_window);
242         view->bin_window = NULL;
243
244         GTK_WIDGET_CLASS (ev_view_parent_class)->unrealize (widget);
245 }
246
247 static void
248 ev_view_style_set (GtkWidget      *widget,
249                    GtkStyle       *previous_style)
250 {
251         update_window_backgrounds (EV_VIEW (widget));
252 }
253
254 static void
255 ev_view_state_changed (GtkWidget    *widget,
256                        GtkStateType  previous_state)
257 {
258         update_window_backgrounds (EV_VIEW (widget));
259 }
260
261 static void
262 expose_bin_window (GtkWidget      *widget,
263                    GdkEventExpose *event)
264 {
265         EvView *view = EV_VIEW (widget);
266         
267         if (view->document)
268                 ev_document_render (view->document,
269                                     event->area.x, event->area.y,
270                                     event->area.width, event->area.height);
271 }
272
273 static gboolean
274 ev_view_expose_event (GtkWidget      *widget,
275                       GdkEventExpose *event)
276 {
277         EvView *view = EV_VIEW (widget);
278
279         if (event->window == view->bin_window)
280                 expose_bin_window (widget, event);
281         else
282                 return GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event (widget, event);
283
284         return FALSE;
285   
286 }
287
288 static gboolean
289 ev_view_button_press_event (GtkWidget      *widget,
290                             GdkEventButton *event)
291 {
292         /* EvView *view = EV_VIEW (widget); */
293
294         return FALSE;
295 }
296
297 static gboolean
298 ev_view_motion_notify_event (GtkWidget      *widget,
299                              GdkEventMotion *event)
300 {
301         /* EvView *view = EV_VIEW (widget); */
302   
303         return FALSE;
304 }
305
306 static gboolean
307 ev_view_button_release_event (GtkWidget      *widget,
308                               GdkEventButton *event)
309 {
310         /* EvView *view = EV_VIEW (widget); */
311
312         return FALSE;
313 }
314
315 static void
316 on_adjustment_value_changed (GtkAdjustment  *adjustment,
317                              EvView *view)
318 {
319         view_update_adjustments (view);
320 }
321
322 static void
323 set_scroll_adjustment (EvView *view,
324                        GtkOrientation  orientation,
325                        GtkAdjustment  *adjustment)
326 {
327         GtkAdjustment **to_set;
328
329         if (orientation == GTK_ORIENTATION_HORIZONTAL)
330                 to_set = &view->hadjustment;
331         else
332                 to_set = &view->vadjustment;
333   
334         if (*to_set != adjustment) {
335                 if (*to_set) {
336                         g_signal_handlers_disconnect_by_func (*to_set,
337                                                               (gpointer) on_adjustment_value_changed,
338                                                               view);
339                         g_object_unref (*to_set);
340                 }
341
342                 *to_set = adjustment;
343                 view_set_adjustment_values (view, orientation);
344
345                 if (*to_set) {
346                         g_object_ref (*to_set);
347                         g_signal_connect (*to_set, "value_changed",
348                                           G_CALLBACK (on_adjustment_value_changed), view);
349                 }
350         }
351 }
352
353 static void
354 ev_view_set_scroll_adjustments (EvView *view,
355                                 GtkAdjustment  *hadjustment,
356                                 GtkAdjustment  *vadjustment)
357 {
358         set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
359         set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
360
361         view_update_adjustments (view);
362 }
363
364 static void
365 ev_view_class_init (EvViewClass *class)
366 {
367         GObjectClass *object_class = G_OBJECT_CLASS (class);
368         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
369         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
370
371         object_class->finalize = ev_view_finalize;
372
373         widget_class->expose_event = ev_view_expose_event;
374         widget_class->button_press_event = ev_view_button_press_event;
375         widget_class->motion_notify_event = ev_view_motion_notify_event;
376         widget_class->button_release_event = ev_view_button_release_event;
377         widget_class->size_request = ev_view_size_request;
378         widget_class->size_allocate = ev_view_size_allocate;
379         widget_class->realize = ev_view_realize;
380         widget_class->unrealize = ev_view_unrealize;
381         widget_class->style_set = ev_view_style_set;
382         widget_class->state_changed = ev_view_state_changed;
383         gtk_object_class->destroy = ev_view_destroy;
384   
385         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
386
387         widget_class->set_scroll_adjustments_signal =  g_signal_new ("set-scroll-adjustments",
388                                                                      G_OBJECT_CLASS_TYPE (object_class),
389                                                                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
390                                                                      G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
391                                                                      NULL, NULL,
392                                                                      ev_marshal_VOID__OBJECT_OBJECT,
393                                                                      G_TYPE_NONE, 2,
394                                                                      GTK_TYPE_ADJUSTMENT,
395                                                                      GTK_TYPE_ADJUSTMENT);
396 }
397
398 static void
399 ev_view_init (EvView *view)
400 {
401         static const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
402         
403         gtk_widget_modify_bg (GTK_WIDGET (view), GTK_STATE_NORMAL, &white);
404 }
405
406 /*** Public API ***/       
407      
408 GtkWidget*
409 ev_view_new (void)
410 {
411         return g_object_new (EV_TYPE_VIEW, NULL);
412 }
413
414 void
415 ev_view_set_document (EvView     *view,
416                       EvDocument *document)
417 {
418         g_return_if_fail (EV_IS_VIEW (view));
419
420         if (document != view->document) {
421                 if (view->document)
422                         g_object_unref (view->document);
423
424                 view->document = document;
425
426                 if (view->document)
427                         g_object_ref (view->document);
428
429                 if (GTK_WIDGET_REALIZED (view))
430                         ev_document_set_target (view->document, view->bin_window);
431                 
432                 gtk_widget_queue_resize (GTK_WIDGET (view));
433         }
434 }