]> Pileus Git - ~andy/gtk/blob - gtk/gtkscrolledwindow.c
( From: James Henstridge <james@daa.com.au> )
[~andy/gtk] / gtk / gtkscrolledwindow.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include "gtkscrolledwindow.h"
19 #include "gtksignal.h"
20
21
22 #define SCROLLBAR_SPACING(w) (GTK_SCROLLED_WINDOW_CLASS (GTK_OBJECT (w)->klass)->scrollbar_spacing)
23
24
25 static void gtk_scrolled_window_class_init         (GtkScrolledWindowClass *klass);
26 static void gtk_scrolled_window_init               (GtkScrolledWindow      *scrolled_window);
27 static void gtk_scrolled_window_destroy            (GtkObject              *object);
28 static void gtk_scrolled_window_finalize           (GtkObject              *object);
29 static void gtk_scrolled_window_map                (GtkWidget              *widget);
30 static void gtk_scrolled_window_unmap              (GtkWidget              *widget);
31 static void gtk_scrolled_window_draw               (GtkWidget              *widget,
32                                                     GdkRectangle           *area);
33 static void gtk_scrolled_window_size_request       (GtkWidget              *widget,
34                                                     GtkRequisition         *requisition);
35 static void gtk_scrolled_window_size_allocate      (GtkWidget              *widget,
36                                                     GtkAllocation          *allocation);
37 static void gtk_scrolled_window_add                (GtkContainer           *container,
38                                                     GtkWidget              *widget);
39 static void gtk_scrolled_window_remove             (GtkContainer           *container,
40                                                     GtkWidget              *widget);
41 static void gtk_scrolled_window_foreach            (GtkContainer           *container,
42                                                     GtkCallback             callback,
43                                                     gpointer                callback_data);
44 static void gtk_scrolled_window_viewport_allocate  (GtkWidget              *widget,
45                                                     GtkAllocation          *allocation);
46 static void gtk_scrolled_window_adjustment_changed (GtkAdjustment          *adjustment,
47                                                     gpointer                data);
48
49
50 static GtkContainerClass *parent_class = NULL;
51
52
53 guint
54 gtk_scrolled_window_get_type ()
55 {
56   static guint scrolled_window_type = 0;
57
58   if (!scrolled_window_type)
59     {
60       GtkTypeInfo scrolled_window_info =
61       {
62         "GtkScrolledWindow",
63         sizeof (GtkScrolledWindow),
64         sizeof (GtkScrolledWindowClass),
65         (GtkClassInitFunc) gtk_scrolled_window_class_init,
66         (GtkObjectInitFunc) gtk_scrolled_window_init,
67         (GtkArgSetFunc) NULL,
68         (GtkArgGetFunc) NULL,
69       };
70
71       scrolled_window_type = gtk_type_unique (gtk_container_get_type (), &scrolled_window_info);
72     }
73
74   return scrolled_window_type;
75 }
76
77 static void
78 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
79 {
80   GtkObjectClass *object_class;
81   GtkWidgetClass *widget_class;
82   GtkContainerClass *container_class;
83
84   object_class = (GtkObjectClass*) class;
85   widget_class = (GtkWidgetClass*) class;
86   container_class = (GtkContainerClass*) class;
87
88   parent_class = gtk_type_class (gtk_container_get_type ());
89
90   object_class->destroy = gtk_scrolled_window_destroy;
91   object_class->finalize = gtk_scrolled_window_finalize;
92
93   widget_class->map = gtk_scrolled_window_map;
94   widget_class->unmap = gtk_scrolled_window_unmap;
95   widget_class->draw = gtk_scrolled_window_draw;
96   widget_class->size_request = gtk_scrolled_window_size_request;
97   widget_class->size_allocate = gtk_scrolled_window_size_allocate;
98
99   container_class->add = gtk_scrolled_window_add;
100   container_class->remove = gtk_scrolled_window_remove;
101   container_class->foreach = gtk_scrolled_window_foreach;
102
103   class->scrollbar_spacing = 5;
104 }
105
106 static void
107 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
108 {
109   GTK_WIDGET_SET_FLAGS (scrolled_window, GTK_NO_WINDOW);
110
111   scrolled_window->hscrollbar = NULL;
112   scrolled_window->vscrollbar = NULL;
113   scrolled_window->hscrollbar_policy = GTK_POLICY_ALWAYS;
114   scrolled_window->vscrollbar_policy = GTK_POLICY_ALWAYS;
115 }
116
117 GtkWidget*
118 gtk_scrolled_window_new (GtkAdjustment *hadjustment,
119                          GtkAdjustment *vadjustment)
120 {
121   GtkScrolledWindow *scrolled_window;
122
123   scrolled_window = gtk_type_new (gtk_scrolled_window_get_type ());
124
125   gtk_scrolled_window_construct (scrolled_window, hadjustment, vadjustment);
126   
127   return GTK_WIDGET (scrolled_window);
128 }
129
130 void
131 gtk_scrolled_window_construct (GtkScrolledWindow *scrolled_window,
132                                GtkAdjustment     *hadjustment,
133                                GtkAdjustment     *vadjustment)
134 {
135   scrolled_window = gtk_type_new (gtk_scrolled_window_get_type ());
136
137   scrolled_window->viewport = gtk_viewport_new (hadjustment, vadjustment);
138   hadjustment = gtk_viewport_get_hadjustment (GTK_VIEWPORT (scrolled_window->viewport));
139   vadjustment = gtk_viewport_get_vadjustment (GTK_VIEWPORT (scrolled_window->viewport));
140
141   gtk_signal_connect (GTK_OBJECT (hadjustment), "changed",
142                       (GtkSignalFunc) gtk_scrolled_window_adjustment_changed,
143                       (gpointer) scrolled_window);
144   gtk_signal_connect (GTK_OBJECT (vadjustment), "changed",
145                       (GtkSignalFunc) gtk_scrolled_window_adjustment_changed,
146                       (gpointer) scrolled_window);
147
148   scrolled_window->hscrollbar = gtk_hscrollbar_new (hadjustment);
149   scrolled_window->vscrollbar = gtk_vscrollbar_new (vadjustment);
150
151   gtk_widget_set_parent (scrolled_window->viewport, GTK_WIDGET (scrolled_window));
152   gtk_widget_set_parent (scrolled_window->hscrollbar, GTK_WIDGET (scrolled_window));
153   gtk_widget_set_parent (scrolled_window->vscrollbar, GTK_WIDGET (scrolled_window));
154
155   gtk_widget_show (scrolled_window->viewport);
156   gtk_widget_show (scrolled_window->hscrollbar);
157   gtk_widget_show (scrolled_window->vscrollbar);
158   
159   gtk_widget_ref (scrolled_window->viewport);
160   gtk_widget_ref (scrolled_window->hscrollbar);
161   gtk_widget_ref (scrolled_window->vscrollbar);
162 }
163
164 GtkAdjustment*
165 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
166 {
167   g_return_val_if_fail (scrolled_window != NULL, NULL);
168   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
169
170   return gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
171 }
172
173 GtkAdjustment*
174 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
175 {
176   g_return_val_if_fail (scrolled_window != NULL, NULL);
177   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
178
179   return gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
180 }
181
182 void
183 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
184                                 GtkPolicyType      hscrollbar_policy,
185                                 GtkPolicyType      vscrollbar_policy)
186 {
187   g_return_if_fail (scrolled_window != NULL);
188   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
189
190   if ((scrolled_window->hscrollbar_policy != hscrollbar_policy) ||
191       (scrolled_window->vscrollbar_policy != vscrollbar_policy))
192     {
193       scrolled_window->hscrollbar_policy = hscrollbar_policy;
194       scrolled_window->vscrollbar_policy = vscrollbar_policy;
195
196       if (GTK_WIDGET (scrolled_window)->parent)
197         gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
198     }
199 }
200
201
202 static void
203 gtk_scrolled_window_destroy (GtkObject *object)
204 {
205   GtkScrolledWindow *scrolled_window;
206
207   g_return_if_fail (object != NULL);
208   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (object));
209
210   scrolled_window = GTK_SCROLLED_WINDOW (object);
211
212   gtk_widget_destroy (scrolled_window->viewport);
213   gtk_widget_destroy (scrolled_window->hscrollbar);
214   gtk_widget_destroy (scrolled_window->vscrollbar);
215
216   if (GTK_OBJECT_CLASS (parent_class)->destroy)
217     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
218 }
219
220 static void
221 gtk_scrolled_window_finalize (GtkObject *object)
222 {
223   GtkScrolledWindow *scrolled_window;
224
225   scrolled_window = GTK_SCROLLED_WINDOW (object);
226   gtk_widget_unref (scrolled_window->viewport);
227   gtk_widget_unref (scrolled_window->hscrollbar);
228   gtk_widget_unref (scrolled_window->vscrollbar);
229
230   GTK_OBJECT_CLASS (parent_class)->finalize (object);
231 }
232
233 static void
234 gtk_scrolled_window_map (GtkWidget *widget)
235 {
236   GtkScrolledWindow *scrolled_window;
237
238   g_return_if_fail (widget != NULL);
239   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
240
241   if (!GTK_WIDGET_MAPPED (widget))
242     {
243       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
244       scrolled_window = GTK_SCROLLED_WINDOW (widget);
245
246       if (GTK_WIDGET_VISIBLE (scrolled_window->viewport) &&
247           !GTK_WIDGET_MAPPED (scrolled_window->viewport))
248         gtk_widget_map (scrolled_window->viewport);
249
250       if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar) &&
251           !GTK_WIDGET_MAPPED (scrolled_window->hscrollbar))
252         gtk_widget_map (scrolled_window->hscrollbar);
253
254       if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar) &&
255           !GTK_WIDGET_MAPPED (scrolled_window->vscrollbar))
256         gtk_widget_map (scrolled_window->vscrollbar);
257     }
258 }
259
260 static void
261 gtk_scrolled_window_unmap (GtkWidget *widget)
262 {
263   GtkScrolledWindow *scrolled_window;
264
265   g_return_if_fail (widget != NULL);
266   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
267
268   if (GTK_WIDGET_MAPPED (widget))
269     {
270       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
271       scrolled_window = GTK_SCROLLED_WINDOW (widget);
272
273       if (GTK_WIDGET_MAPPED (scrolled_window->viewport))
274         gtk_widget_unmap (scrolled_window->viewport);
275
276       if (GTK_WIDGET_MAPPED (scrolled_window->hscrollbar))
277         gtk_widget_unmap (scrolled_window->hscrollbar);
278
279       if (GTK_WIDGET_MAPPED (scrolled_window->vscrollbar))
280         gtk_widget_unmap (scrolled_window->vscrollbar);
281     }
282 }
283
284 static void
285 gtk_scrolled_window_draw (GtkWidget    *widget,
286                           GdkRectangle *area)
287 {
288   GtkScrolledWindow *scrolled_window;
289   GdkRectangle child_area;
290
291   g_return_if_fail (widget != NULL);
292   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
293   g_return_if_fail (area != NULL);
294
295   if (GTK_WIDGET_DRAWABLE (widget))
296     {
297       scrolled_window = GTK_SCROLLED_WINDOW (widget);
298
299       if (gtk_widget_intersect (scrolled_window->viewport, area, &child_area))
300         gtk_widget_draw (scrolled_window->viewport, &child_area);
301
302       if (gtk_widget_intersect (scrolled_window->hscrollbar, area, &child_area))
303         gtk_widget_draw (scrolled_window->hscrollbar, &child_area);
304
305       if (gtk_widget_intersect (scrolled_window->vscrollbar, area, &child_area))
306         gtk_widget_draw (scrolled_window->vscrollbar, &child_area);
307     }
308 }
309
310 static void
311 gtk_scrolled_window_size_request (GtkWidget      *widget,
312                                   GtkRequisition *requisition)
313 {
314   GtkScrolledWindow *scrolled_window;
315   gint extra_height;
316   gint extra_width;
317
318   g_return_if_fail (widget != NULL);
319   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
320   g_return_if_fail (requisition != NULL);
321
322   scrolled_window = GTK_SCROLLED_WINDOW (widget);
323
324   requisition->width = 0;
325   requisition->height = 0;
326
327   if (GTK_WIDGET_VISIBLE (scrolled_window->viewport))
328     {
329       gtk_widget_size_request (scrolled_window->viewport, &scrolled_window->viewport->requisition);
330
331       requisition->width += scrolled_window->viewport->requisition.width;
332       requisition->height += scrolled_window->viewport->requisition.height;
333     }
334
335   extra_width = 0;
336   extra_height = 0;
337
338   if ((scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
339       GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
340     {
341       gtk_widget_size_request (scrolled_window->hscrollbar,
342                                &scrolled_window->hscrollbar->requisition);
343
344       requisition->width = MAX (requisition->width, scrolled_window->hscrollbar->requisition.width);
345       extra_height = SCROLLBAR_SPACING (scrolled_window) + scrolled_window->hscrollbar->requisition.height;
346     }
347
348   if ((scrolled_window->vscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
349       GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
350     {
351       gtk_widget_size_request (scrolled_window->vscrollbar,
352                                &scrolled_window->vscrollbar->requisition);
353
354       requisition->height = MAX (requisition->height, scrolled_window->vscrollbar->requisition.height);
355       extra_width = SCROLLBAR_SPACING (scrolled_window) + scrolled_window->vscrollbar->requisition.width;
356     }
357
358   requisition->width += GTK_CONTAINER (widget)->border_width * 2 + extra_width;
359   requisition->height += GTK_CONTAINER (widget)->border_width * 2 + extra_height;
360 }
361
362 static void
363 gtk_scrolled_window_size_allocate (GtkWidget     *widget,
364                                    GtkAllocation *allocation)
365 {
366   GtkScrolledWindow *scrolled_window;
367   GtkAllocation viewport_allocation;
368   GtkAllocation child_allocation;
369   guint previous_hvis;
370   guint previous_vvis;
371
372   g_return_if_fail (widget != NULL);
373   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
374   g_return_if_fail (allocation != NULL);
375
376   scrolled_window = GTK_SCROLLED_WINDOW (widget);
377   widget->allocation = *allocation;
378
379   gtk_scrolled_window_viewport_allocate (widget, &viewport_allocation);
380
381   gtk_container_disable_resize (GTK_CONTAINER (scrolled_window));
382
383   if (GTK_WIDGET_VISIBLE (scrolled_window->viewport))
384     {
385       do {
386         gtk_scrolled_window_viewport_allocate (widget, &viewport_allocation);
387
388         child_allocation.x = viewport_allocation.x + allocation->x;
389         child_allocation.y = viewport_allocation.y + allocation->y;
390         child_allocation.width = viewport_allocation.width;
391         child_allocation.height = viewport_allocation.height;
392
393         previous_hvis = GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar);
394         previous_vvis = GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar);
395
396         gtk_widget_size_allocate (scrolled_window->viewport, &child_allocation);
397       } while ((previous_hvis != GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar)) ||
398                (previous_vvis != GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar)));
399     }
400
401   if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
402     {
403       child_allocation.x = viewport_allocation.x;
404       child_allocation.y = viewport_allocation.y + viewport_allocation.height + SCROLLBAR_SPACING (scrolled_window);
405       child_allocation.width = viewport_allocation.width;
406       child_allocation.height = scrolled_window->hscrollbar->requisition.height;
407       child_allocation.x += allocation->x;
408       child_allocation.y += allocation->y;
409
410       gtk_widget_size_allocate (scrolled_window->hscrollbar, &child_allocation);
411     }
412
413   if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
414     {
415       child_allocation.x = viewport_allocation.x + viewport_allocation.width + SCROLLBAR_SPACING (scrolled_window);
416       child_allocation.y = viewport_allocation.y;
417       child_allocation.width = scrolled_window->vscrollbar->requisition.width;
418       child_allocation.height = viewport_allocation.height;
419       child_allocation.x += allocation->x;
420       child_allocation.y += allocation->y;
421
422       gtk_widget_size_allocate (scrolled_window->vscrollbar, &child_allocation);
423     }
424
425   gtk_container_enable_resize (GTK_CONTAINER (scrolled_window));
426 }
427
428 static void
429 gtk_scrolled_window_add (GtkContainer *container,
430                          GtkWidget    *widget)
431 {
432   GtkScrolledWindow *scrolled_window;
433
434   g_return_if_fail (container != NULL);
435   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
436   g_return_if_fail (widget != NULL);
437
438   scrolled_window = GTK_SCROLLED_WINDOW (container);
439   gtk_container_add (GTK_CONTAINER (scrolled_window->viewport), widget);
440 }
441
442 static void
443 gtk_scrolled_window_remove (GtkContainer *container,
444                             GtkWidget    *widget)
445 {
446   GtkScrolledWindow *scrolled_window;
447
448   g_return_if_fail (container != NULL);
449   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
450   g_return_if_fail (widget != NULL);
451
452   scrolled_window = GTK_SCROLLED_WINDOW (container);
453
454   if (scrolled_window->viewport == widget ||
455       scrolled_window->hscrollbar == widget ||
456       scrolled_window->vscrollbar == widget)
457     {
458       /* this happens during destroy */
459       gtk_widget_unparent (widget);
460     }
461   else
462     gtk_container_remove (GTK_CONTAINER (scrolled_window->viewport), widget);
463 }
464
465 static void
466 gtk_scrolled_window_foreach (GtkContainer *container,
467                              GtkCallback   callback,
468                              gpointer      callback_data)
469 {
470   GtkScrolledWindow *scrolled_window;
471
472   g_return_if_fail (container != NULL);
473   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
474   g_return_if_fail (callback != NULL);
475
476   scrolled_window = GTK_SCROLLED_WINDOW (container);
477
478   (* callback) (scrolled_window->viewport, callback_data);
479 }
480
481 static void
482 gtk_scrolled_window_viewport_allocate (GtkWidget     *widget,
483                                        GtkAllocation *allocation)
484 {
485   GtkScrolledWindow *scrolled_window;
486
487   g_return_if_fail (widget != NULL);
488   g_return_if_fail (allocation != NULL);
489
490   scrolled_window = GTK_SCROLLED_WINDOW (widget);
491
492   allocation->x = GTK_CONTAINER (widget)->border_width;
493   allocation->y = GTK_CONTAINER (widget)->border_width;
494   allocation->width = widget->allocation.width - allocation->x * 2;
495   allocation->height = widget->allocation.height - allocation->y * 2;
496
497   if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
498     allocation->width -= scrolled_window->vscrollbar->requisition.width + SCROLLBAR_SPACING (scrolled_window);
499   if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
500     allocation->height -= scrolled_window->hscrollbar->requisition.height + SCROLLBAR_SPACING (scrolled_window);
501 }
502
503 static void
504 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
505                                         gpointer       data)
506 {
507   GtkScrolledWindow *scrolled_win;
508   GtkWidget *scrollbar;
509   gint hide_scrollbar;
510   gint policy;
511
512   g_return_if_fail (adjustment != NULL);
513   g_return_if_fail (data != NULL);
514
515   scrolled_win = GTK_SCROLLED_WINDOW (data);
516
517   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->hscrollbar)))
518     {
519       scrollbar = scrolled_win->hscrollbar;
520       policy = scrolled_win->hscrollbar_policy;
521     }
522   else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->vscrollbar)))
523     {
524       scrollbar = scrolled_win->vscrollbar;
525       policy = scrolled_win->vscrollbar_policy;
526     }
527   else
528     {
529       g_warning ("could not determine which adjustment scrollbar received change signal for");
530       return;
531     }
532
533   if (policy == GTK_POLICY_AUTOMATIC)
534     {
535       hide_scrollbar = FALSE;
536
537       if ((adjustment->upper - adjustment->lower) <= adjustment->page_size)
538         hide_scrollbar = TRUE;
539
540       if (hide_scrollbar)
541         {
542           if (GTK_WIDGET_VISIBLE (scrollbar))
543             gtk_widget_hide (scrollbar);
544         }
545       else
546         {
547           if (!GTK_WIDGET_VISIBLE (scrollbar))
548             gtk_widget_show (scrollbar);
549         }
550     }
551 }