]> Pileus Git - ~andy/gtk/blob - gtk/gtkscrolledwindow.c
Allow a NULL marshaller. (gtk_signal_connect_by_type): Allow only
[~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
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include "gtkscrolledwindow.h"
20 #include "gtksignal.h"
21
22
23 /* scrolled window policy and size requisition handling:
24  *
25  * gtk size requisition works as follows:
26  *   a widget upon size-request reports the width and height that it finds
27  *   to be best suited to display its contents, including children.
28  *   the width and/or height reported from a widget upon size requisition
29  *   may be overidden by the user by specifying a width and/or height
30  *   other than 0 through gtk_widget_set_usize().
31  *
32  * a scrolled window needs (for imlementing all three policy types) to
33  * request its width and height based on two different rationales.
34  * 1)   the user wants the scrolled window to just fit into the space
35  *      that it gets allocated for a specifc dimension.
36  * 1.1) this does not apply if the user specified a concrete value
37  *      value for that specific dimension by either specifying usize for the
38  *      scrolled window or for its child.
39  * 2)   the user wants the scrolled window to take as much space up as
40  *      is desired by the child for a specifc dimension (i.e. POLICY_NEVER).
41  *
42  * also, kinda obvious:
43  * 3)   a user would certainly not have choosen a scrolled window as a container
44  *      for the child, if the resulting allocation takes up more space than the
45  *      child would have allocated without the scrolled window.
46  *
47  * conclusions:
48  * A) from 1) follows: the scrolled window shouldn't request more space for a
49  *    specifc dimension than is required at minimum.
50  * B) from 1.1) follows: the requisition may be overidden by usize of the scrolled
51  *    window (done automatically) or by usize of the child (needs to be checked).
52  * C) from 2) follows: for POLICY_NEVER, the scrolled window simply reports the
53  *    child's dimension.
54  * D) from 3) follows: the scrolled window child's minimum width and minimum height
55  *    under A) at least correspond to the space taken up by its scrollbars.
56  */
57
58 #define SCROLLBAR_SPACING(w) (GTK_SCROLLED_WINDOW_CLASS (GTK_OBJECT (w)->klass)->scrollbar_spacing)
59
60 enum {
61   ARG_0,
62   ARG_HADJUSTMENT,
63   ARG_VADJUSTMENT,
64   ARG_HSCROLLBAR_POLICY,
65   ARG_VSCROLLBAR_POLICY,
66   ARG_WINDOW_PLACEMENT
67 };
68
69
70 static void gtk_scrolled_window_class_init         (GtkScrolledWindowClass *klass);
71 static void gtk_scrolled_window_init               (GtkScrolledWindow      *scrolled_window);
72 static void gtk_scrolled_window_set_arg            (GtkObject              *object,
73                                                     GtkArg                 *arg,
74                                                     guint                   arg_id);
75 static void gtk_scrolled_window_get_arg            (GtkObject              *object,
76                                                     GtkArg                 *arg,
77                                                     guint                   arg_id);
78 static void gtk_scrolled_window_destroy            (GtkObject              *object);
79 static void gtk_scrolled_window_finalize           (GtkObject              *object);
80 static void gtk_scrolled_window_map                (GtkWidget              *widget);
81 static void gtk_scrolled_window_unmap              (GtkWidget              *widget);
82 static void gtk_scrolled_window_draw               (GtkWidget              *widget,
83                                                     GdkRectangle           *area);
84 static void gtk_scrolled_window_size_request       (GtkWidget              *widget,
85                                                     GtkRequisition         *requisition);
86 static void gtk_scrolled_window_size_allocate      (GtkWidget              *widget,
87                                                     GtkAllocation          *allocation);
88 static void gtk_scrolled_window_add                (GtkContainer           *container,
89                                                     GtkWidget              *widget);
90 static void gtk_scrolled_window_remove             (GtkContainer           *container,
91                                                     GtkWidget              *widget);
92 static void gtk_scrolled_window_forall             (GtkContainer           *container,
93                                                     gboolean                include_internals,
94                                                     GtkCallback             callback,
95                                                     gpointer                callback_data);
96 static void gtk_scrolled_window_relative_allocation(GtkWidget              *widget,
97                                                     GtkAllocation          *allocation);
98 static void gtk_scrolled_window_adjustment_changed (GtkAdjustment          *adjustment,
99                                                     gpointer                data);
100
101
102 static GtkContainerClass *parent_class = NULL;
103
104
105 GtkType
106 gtk_scrolled_window_get_type (void)
107 {
108   static GtkType scrolled_window_type = 0;
109
110   if (!scrolled_window_type)
111     {
112       static const GtkTypeInfo scrolled_window_info =
113       {
114         "GtkScrolledWindow",
115         sizeof (GtkScrolledWindow),
116         sizeof (GtkScrolledWindowClass),
117         (GtkClassInitFunc) gtk_scrolled_window_class_init,
118         (GtkObjectInitFunc) gtk_scrolled_window_init,
119         /* reserved_1 */ NULL,
120         /* reserved_2 */ NULL,
121         (GtkClassInitFunc) NULL,
122       };
123
124       scrolled_window_type = gtk_type_unique (GTK_TYPE_BIN, &scrolled_window_info);
125     }
126
127   return scrolled_window_type;
128 }
129
130 static void
131 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
132 {
133   GtkObjectClass *object_class;
134   GtkWidgetClass *widget_class;
135   GtkContainerClass *container_class;
136
137   object_class = (GtkObjectClass*) class;
138   widget_class = (GtkWidgetClass*) class;
139   container_class = (GtkContainerClass*) class;
140   parent_class = gtk_type_class (GTK_TYPE_BIN);
141
142   gtk_object_add_arg_type ("GtkScrolledWindow::hadjustment",
143                            GTK_TYPE_ADJUSTMENT,
144                            GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
145                            ARG_HADJUSTMENT);
146   gtk_object_add_arg_type ("GtkScrolledWindow::vadjustment",
147                            GTK_TYPE_ADJUSTMENT,
148                            GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
149                            ARG_VADJUSTMENT);
150   gtk_object_add_arg_type ("GtkScrolledWindow::hscrollbar_policy",
151                            GTK_TYPE_POLICY_TYPE,
152                            GTK_ARG_READWRITE,
153                            ARG_HSCROLLBAR_POLICY);
154   gtk_object_add_arg_type ("GtkScrolledWindow::vscrollbar_policy",
155                            GTK_TYPE_POLICY_TYPE,
156                            GTK_ARG_READWRITE,
157                            ARG_VSCROLLBAR_POLICY);
158   gtk_object_add_arg_type ("GtkScrolledWindow::window_placement",
159                            GTK_TYPE_CORNER_TYPE,
160                            GTK_ARG_READWRITE,
161                            ARG_WINDOW_PLACEMENT);
162
163   object_class->set_arg = gtk_scrolled_window_set_arg;
164   object_class->get_arg = gtk_scrolled_window_get_arg;
165   object_class->destroy = gtk_scrolled_window_destroy;
166   object_class->finalize = gtk_scrolled_window_finalize;
167
168   widget_class->map = gtk_scrolled_window_map;
169   widget_class->unmap = gtk_scrolled_window_unmap;
170   widget_class->draw = gtk_scrolled_window_draw;
171   widget_class->size_request = gtk_scrolled_window_size_request;
172   widget_class->size_allocate = gtk_scrolled_window_size_allocate;
173
174   container_class->add = gtk_scrolled_window_add;
175   container_class->remove = gtk_scrolled_window_remove;
176   container_class->forall = gtk_scrolled_window_forall;
177
178   class->scrollbar_spacing = 5;
179 }
180
181 static void
182 gtk_scrolled_window_set_arg (GtkObject        *object,
183                              GtkArg           *arg,
184                              guint             arg_id)
185 {
186   GtkScrolledWindow *scrolled_window;
187
188   scrolled_window = GTK_SCROLLED_WINDOW (object);
189
190   switch (arg_id)
191     {
192     case ARG_HADJUSTMENT:
193       gtk_scrolled_window_set_hadjustment (scrolled_window, GTK_VALUE_POINTER (*arg));
194       break;
195     case ARG_VADJUSTMENT:
196       gtk_scrolled_window_set_vadjustment (scrolled_window, GTK_VALUE_POINTER (*arg));
197       break;
198     case ARG_HSCROLLBAR_POLICY:
199       gtk_scrolled_window_set_policy (scrolled_window,
200                                       GTK_VALUE_ENUM (*arg),
201                                       scrolled_window->vscrollbar_policy);
202       break;
203     case ARG_VSCROLLBAR_POLICY:
204       gtk_scrolled_window_set_policy (scrolled_window,
205                                       scrolled_window->hscrollbar_policy,
206                                       GTK_VALUE_ENUM (*arg));
207       break;
208     case ARG_WINDOW_PLACEMENT:
209       gtk_scrolled_window_set_placement (scrolled_window,
210                                          GTK_VALUE_ENUM (*arg));
211       break;
212     default:
213       break;
214     }
215 }
216
217 static void
218 gtk_scrolled_window_get_arg (GtkObject        *object,
219                              GtkArg           *arg,
220                              guint             arg_id)
221 {
222   GtkScrolledWindow *scrolled_window;
223
224   scrolled_window = GTK_SCROLLED_WINDOW (object);
225
226   switch (arg_id)
227     {
228     case ARG_HADJUSTMENT:
229       GTK_VALUE_POINTER (*arg) = gtk_scrolled_window_get_hadjustment (scrolled_window);
230       break;
231     case ARG_VADJUSTMENT:
232       GTK_VALUE_POINTER (*arg) = gtk_scrolled_window_get_vadjustment (scrolled_window);
233       break;
234     case ARG_HSCROLLBAR_POLICY:
235       GTK_VALUE_ENUM (*arg) = scrolled_window->hscrollbar_policy;
236       break;
237     case ARG_VSCROLLBAR_POLICY:
238       GTK_VALUE_ENUM (*arg) = scrolled_window->vscrollbar_policy;
239       break;
240     case ARG_WINDOW_PLACEMENT:
241       GTK_VALUE_ENUM (*arg) = scrolled_window->window_placement;
242       break;
243     default:
244       arg->type = GTK_TYPE_INVALID;
245       break;
246     }
247 }
248
249 static void
250 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
251 {
252   GTK_WIDGET_SET_FLAGS (scrolled_window, GTK_NO_WINDOW);
253
254   gtk_container_set_resize_mode (GTK_CONTAINER (scrolled_window), GTK_RESIZE_QUEUE);
255
256   scrolled_window->hscrollbar = NULL;
257   scrolled_window->vscrollbar = NULL;
258   scrolled_window->hscrollbar_policy = GTK_POLICY_ALWAYS;
259   scrolled_window->vscrollbar_policy = GTK_POLICY_ALWAYS;
260   scrolled_window->hscrollbar_visible = FALSE;
261   scrolled_window->vscrollbar_visible = FALSE;
262   scrolled_window->window_placement = GTK_CORNER_TOP_LEFT;
263 }
264
265 GtkWidget*
266 gtk_scrolled_window_new (GtkAdjustment *hadjustment,
267                          GtkAdjustment *vadjustment)
268 {
269   GtkWidget *scrolled_window;
270
271   if (hadjustment)
272     g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
273
274   if (vadjustment)
275     g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
276
277   scrolled_window = gtk_widget_new (GTK_TYPE_SCROLLED_WINDOW,
278                                     "hadjustment", hadjustment,
279                                     "vadjustment", vadjustment,
280                                     NULL);
281
282   return scrolled_window;
283 }
284
285 void
286 gtk_scrolled_window_construct (GtkScrolledWindow *scrolled_window,
287                                GtkAdjustment     *hadjustment,
288                                GtkAdjustment     *vadjustment)
289 {
290   g_message ("gtk_scrolled_window_construct() is deprecated");
291   gtk_scrolled_window_set_hadjustment (scrolled_window, hadjustment);
292   gtk_scrolled_window_set_vadjustment (scrolled_window, vadjustment);
293   gtk_object_default_construct (GTK_OBJECT (scrolled_window));
294 }
295
296 void
297 gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
298                                      GtkAdjustment     *hadjustment)
299 {
300   GtkBin *bin;
301
302   g_return_if_fail (scrolled_window != NULL);
303   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
304   if (hadjustment)
305     g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
306   else
307     hadjustment = (GtkAdjustment*) gtk_object_new (GTK_TYPE_ADJUSTMENT, NULL);
308
309   bin = GTK_BIN (scrolled_window);
310
311   if (!scrolled_window->hscrollbar)
312     {
313       gtk_widget_push_composite_child ();
314       scrolled_window->hscrollbar = gtk_hscrollbar_new (hadjustment);
315       gtk_widget_set_composite_name (scrolled_window->hscrollbar, "hscrollbar");
316       gtk_widget_pop_composite_child ();
317
318       gtk_widget_set_parent (scrolled_window->hscrollbar, GTK_WIDGET (scrolled_window));
319       gtk_widget_ref (scrolled_window->hscrollbar);
320       gtk_widget_show (scrolled_window->hscrollbar);
321     }
322   else
323     {
324       GtkAdjustment *old_adjustment;
325       
326       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
327       if (old_adjustment == hadjustment)
328         return;
329
330       gtk_signal_disconnect_by_func (GTK_OBJECT (old_adjustment),
331                                      GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
332                                      scrolled_window);
333       gtk_range_set_adjustment (GTK_RANGE (scrolled_window->hscrollbar),
334                                 hadjustment);
335     }
336   hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
337   gtk_signal_connect (GTK_OBJECT (hadjustment),
338                       "changed",
339                       GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
340                       scrolled_window);
341   gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
342   
343   if (bin->child)
344     gtk_widget_set_scroll_adjustments (bin->child,
345                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
346                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
347 }
348
349 void
350 gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
351                                      GtkAdjustment     *vadjustment)
352 {
353   GtkBin *bin;
354
355   g_return_if_fail (scrolled_window != NULL);
356   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
357   if (vadjustment)
358     g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
359   else
360     vadjustment = (GtkAdjustment*) gtk_object_new (GTK_TYPE_ADJUSTMENT, NULL);
361
362   bin = GTK_BIN (scrolled_window);
363
364   if (!scrolled_window->vscrollbar)
365     {
366       gtk_widget_push_composite_child ();
367       scrolled_window->vscrollbar = gtk_vscrollbar_new (vadjustment);
368       gtk_widget_set_composite_name (scrolled_window->vscrollbar, "vscrollbar");
369       gtk_widget_pop_composite_child ();
370
371       gtk_widget_set_parent (scrolled_window->vscrollbar, GTK_WIDGET (scrolled_window));
372       gtk_widget_ref (scrolled_window->vscrollbar);
373       gtk_widget_show (scrolled_window->vscrollbar);
374     }
375   else
376     {
377       GtkAdjustment *old_adjustment;
378       
379       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
380       if (old_adjustment == vadjustment)
381         return;
382
383       gtk_signal_disconnect_by_func (GTK_OBJECT (old_adjustment),
384                                      GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
385                                      scrolled_window);
386       gtk_range_set_adjustment (GTK_RANGE (scrolled_window->vscrollbar),
387                                 vadjustment);
388     }
389   vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
390   gtk_signal_connect (GTK_OBJECT (vadjustment),
391                       "changed",
392                       GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
393                       scrolled_window);
394   gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
395
396   if (bin->child)
397     gtk_widget_set_scroll_adjustments (bin->child,
398                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
399                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
400 }
401
402 GtkAdjustment*
403 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
404 {
405   g_return_val_if_fail (scrolled_window != NULL, NULL);
406   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
407
408   return (scrolled_window->hscrollbar ?
409           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)) :
410           NULL);
411 }
412
413 GtkAdjustment*
414 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
415 {
416   g_return_val_if_fail (scrolled_window != NULL, NULL);
417   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
418
419   return (scrolled_window->vscrollbar ?
420           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)) :
421           NULL);
422 }
423
424 void
425 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
426                                 GtkPolicyType      hscrollbar_policy,
427                                 GtkPolicyType      vscrollbar_policy)
428 {
429   g_return_if_fail (scrolled_window != NULL);
430   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
431
432   if ((scrolled_window->hscrollbar_policy != hscrollbar_policy) ||
433       (scrolled_window->vscrollbar_policy != vscrollbar_policy))
434     {
435       scrolled_window->hscrollbar_policy = hscrollbar_policy;
436       scrolled_window->vscrollbar_policy = vscrollbar_policy;
437
438       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
439     }
440 }
441
442 void
443 gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
444                                    GtkCornerType      window_placement)
445 {
446   g_return_if_fail (scrolled_window != NULL);
447   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
448
449   if (scrolled_window->window_placement != window_placement)
450     {
451       scrolled_window->window_placement = window_placement;
452
453       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
454     }
455 }
456
457 static void
458 gtk_scrolled_window_destroy (GtkObject *object)
459 {
460   GtkScrolledWindow *scrolled_window;
461
462   g_return_if_fail (object != NULL);
463   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (object));
464
465   scrolled_window = GTK_SCROLLED_WINDOW (object);
466
467   gtk_widget_unparent (scrolled_window->hscrollbar);
468   gtk_widget_unparent (scrolled_window->vscrollbar);
469   gtk_widget_destroy (scrolled_window->hscrollbar);
470   gtk_widget_destroy (scrolled_window->vscrollbar);
471
472   GTK_OBJECT_CLASS (parent_class)->destroy (object);
473 }
474
475 static void
476 gtk_scrolled_window_finalize (GtkObject *object)
477 {
478   GtkScrolledWindow *scrolled_window;
479
480   scrolled_window = GTK_SCROLLED_WINDOW (object);
481
482   gtk_widget_unref (scrolled_window->hscrollbar);
483   gtk_widget_unref (scrolled_window->vscrollbar);
484
485   GTK_OBJECT_CLASS (parent_class)->finalize (object);
486 }
487
488 static void
489 gtk_scrolled_window_map (GtkWidget *widget)
490 {
491   GtkScrolledWindow *scrolled_window;
492
493   g_return_if_fail (widget != NULL);
494   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
495
496   scrolled_window = GTK_SCROLLED_WINDOW (widget);
497
498   /* chain parent class handler to map self and child */
499   GTK_WIDGET_CLASS (parent_class)->map (widget);
500   
501   if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar) &&
502       !GTK_WIDGET_MAPPED (scrolled_window->hscrollbar))
503     gtk_widget_map (scrolled_window->hscrollbar);
504   
505   if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar) &&
506       !GTK_WIDGET_MAPPED (scrolled_window->vscrollbar))
507     gtk_widget_map (scrolled_window->vscrollbar);
508 }
509
510 static void
511 gtk_scrolled_window_unmap (GtkWidget *widget)
512 {
513   GtkScrolledWindow *scrolled_window;
514
515   g_return_if_fail (widget != NULL);
516   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
517
518   scrolled_window = GTK_SCROLLED_WINDOW (widget);
519
520   /* chain parent class handler to unmap self and child */
521   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
522   
523   if (GTK_WIDGET_MAPPED (scrolled_window->hscrollbar))
524     gtk_widget_unmap (scrolled_window->hscrollbar);
525   
526   if (GTK_WIDGET_MAPPED (scrolled_window->vscrollbar))
527     gtk_widget_unmap (scrolled_window->vscrollbar);
528 }
529
530 static void
531 gtk_scrolled_window_draw (GtkWidget    *widget,
532                           GdkRectangle *area)
533 {
534   GtkScrolledWindow *scrolled_window;
535   GtkBin *bin;
536   GdkRectangle child_area;
537
538   g_return_if_fail (widget != NULL);
539   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
540   g_return_if_fail (area != NULL);
541   
542   scrolled_window = GTK_SCROLLED_WINDOW (widget);
543   bin = GTK_BIN (widget);
544
545   if (bin->child && GTK_WIDGET_VISIBLE (bin->child) &&
546       gtk_widget_intersect (bin->child, area, &child_area))
547     gtk_widget_draw (bin->child, &child_area);
548   
549   if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar) &&
550       gtk_widget_intersect (scrolled_window->hscrollbar, area, &child_area))
551     gtk_widget_draw (scrolled_window->hscrollbar, &child_area);
552   
553   if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar) &&
554       gtk_widget_intersect (scrolled_window->vscrollbar, area, &child_area))
555     gtk_widget_draw (scrolled_window->vscrollbar, &child_area);
556 }
557
558 static void
559 gtk_scrolled_window_forall (GtkContainer *container,
560                             gboolean      include_internals,
561                             GtkCallback   callback,
562                             gpointer      callback_data)
563 {
564   g_return_if_fail (container != NULL);
565   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
566   g_return_if_fail (callback != NULL);
567
568   GTK_CONTAINER_CLASS (parent_class)->forall (container,
569                                               include_internals,
570                                               callback,
571                                               callback_data);
572   if (include_internals)
573     {
574       GtkScrolledWindow *scrolled_window;
575
576       scrolled_window = GTK_SCROLLED_WINDOW (container);
577       
578       if (scrolled_window->vscrollbar)
579         callback (scrolled_window->vscrollbar, callback_data);
580       if (scrolled_window->hscrollbar)
581         callback (scrolled_window->hscrollbar, callback_data);
582     }
583 }
584
585 static void
586 gtk_scrolled_window_size_request (GtkWidget      *widget,
587                                   GtkRequisition *requisition)
588 {
589   GtkScrolledWindow *scrolled_window;
590   GtkBin *bin;
591   gint extra_height;
592   gint extra_width;
593
594   g_return_if_fail (widget != NULL);
595   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
596   g_return_if_fail (requisition != NULL);
597
598   scrolled_window = GTK_SCROLLED_WINDOW (widget);
599   bin = GTK_BIN (scrolled_window);
600
601   requisition->width = 0;
602   requisition->height = 0;
603   
604   gtk_widget_size_request (scrolled_window->hscrollbar,
605                            &scrolled_window->hscrollbar->requisition);
606   gtk_widget_size_request (scrolled_window->vscrollbar,
607                            &scrolled_window->vscrollbar->requisition);
608   
609   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
610     {
611       static guint quark_aux_info = 0;
612
613       if (!quark_aux_info)
614         quark_aux_info = g_quark_from_static_string ("gtk-aux-info");
615
616       gtk_widget_size_request (bin->child, &bin->child->requisition);
617
618       if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
619         requisition->width += bin->child->requisition.width;
620       else
621         {
622           GtkWidgetAuxInfo *aux_info;
623
624           aux_info = gtk_object_get_data_by_id (GTK_OBJECT (bin->child), quark_aux_info);
625           if (aux_info && aux_info->width > 0)
626             requisition->width += aux_info->width;
627           else
628             requisition->width += scrolled_window->vscrollbar->requisition.width;
629         }
630
631       if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
632         requisition->height += bin->child->requisition.height;
633       else
634         {
635           GtkWidgetAuxInfo *aux_info;
636
637           aux_info = gtk_object_get_data_by_id (GTK_OBJECT (bin->child), quark_aux_info);
638           if (aux_info && aux_info->height > 0)
639             requisition->height += aux_info->height;
640           else
641             requisition->height += scrolled_window->hscrollbar->requisition.height;
642         }
643     }
644
645   extra_width = 0;
646   extra_height = 0;
647
648   if ((scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
649       GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
650     {
651       requisition->width = MAX (requisition->width, scrolled_window->hscrollbar->requisition.width);
652       extra_height = SCROLLBAR_SPACING (scrolled_window) + scrolled_window->hscrollbar->requisition.height;
653     }
654
655   if ((scrolled_window->vscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
656       GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
657     {
658       requisition->height = MAX (requisition->height, scrolled_window->vscrollbar->requisition.height);
659       extra_width = SCROLLBAR_SPACING (scrolled_window) + scrolled_window->vscrollbar->requisition.width;
660     }
661
662   requisition->width += GTK_CONTAINER (widget)->border_width * 2 + extra_width;
663   requisition->height += GTK_CONTAINER (widget)->border_width * 2 + extra_height;
664 }
665
666 static void
667 gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
668                                          GtkAllocation *allocation)
669 {
670   GtkScrolledWindow *scrolled_window;
671
672   g_return_if_fail (widget != NULL);
673   g_return_if_fail (allocation != NULL);
674
675   scrolled_window = GTK_SCROLLED_WINDOW (widget);
676
677   allocation->x = GTK_CONTAINER (widget)->border_width;
678   allocation->y = GTK_CONTAINER (widget)->border_width;
679   allocation->width = MAX (1, widget->allocation.width - allocation->x * 2);
680   allocation->height = MAX (1, widget->allocation.height - allocation->y * 2);
681
682   if (scrolled_window->vscrollbar_visible)
683     {
684       if (scrolled_window->window_placement == GTK_CORNER_TOP_RIGHT ||
685           scrolled_window->window_placement == GTK_CORNER_BOTTOM_RIGHT)
686         allocation->x += (scrolled_window->vscrollbar->requisition.width +
687                           SCROLLBAR_SPACING (scrolled_window));
688
689       allocation->width = MAX (1, allocation->width -
690                                (scrolled_window->vscrollbar->requisition.width +
691                                 SCROLLBAR_SPACING (scrolled_window)));
692     }
693   if (scrolled_window->hscrollbar_visible)
694     {
695       if (scrolled_window->window_placement == GTK_CORNER_BOTTOM_LEFT ||
696           scrolled_window->window_placement == GTK_CORNER_BOTTOM_RIGHT)
697         allocation->y += (scrolled_window->hscrollbar->requisition.height +
698                           SCROLLBAR_SPACING (scrolled_window));
699
700       allocation->height = MAX (1, allocation->height -
701                                 (scrolled_window->hscrollbar->requisition.height +
702                                  SCROLLBAR_SPACING (scrolled_window)));
703     }
704 }
705
706 static void
707 gtk_scrolled_window_size_allocate (GtkWidget     *widget,
708                                    GtkAllocation *allocation)
709 {
710   GtkScrolledWindow *scrolled_window;
711   GtkBin *bin;
712   GtkAllocation relative_allocation;
713   GtkAllocation child_allocation;
714   
715   g_return_if_fail (widget != NULL);
716   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
717   g_return_if_fail (allocation != NULL);
718
719   scrolled_window = GTK_SCROLLED_WINDOW (widget);
720   bin = GTK_BIN (scrolled_window);
721
722   widget->allocation = *allocation;
723
724   if (scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
725     scrolled_window->hscrollbar_visible = TRUE;
726   else if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
727     scrolled_window->hscrollbar_visible = FALSE;
728   if (scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
729     scrolled_window->vscrollbar_visible = TRUE;
730   else if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
731     scrolled_window->vscrollbar_visible = FALSE;
732
733   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
734     {
735       gboolean previous_hvis;
736       gboolean previous_vvis;
737       guint count = 0;
738       
739       do
740         {
741           gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
742           
743           child_allocation.x = relative_allocation.x + allocation->x;
744           child_allocation.y = relative_allocation.y + allocation->y;
745           child_allocation.width = relative_allocation.width;
746           child_allocation.height = relative_allocation.height;
747           
748           previous_hvis = scrolled_window->hscrollbar_visible;
749           previous_vvis = scrolled_window->vscrollbar_visible;
750           
751           gtk_widget_size_allocate (bin->child, &child_allocation);
752
753           /* If, after the first iteration, the hscrollbar and the
754            * vscrollbar flip visiblity, then we need both.
755            */
756           if (count &&
757               previous_hvis != scrolled_window->hscrollbar_visible &&
758               previous_vvis != scrolled_window->vscrollbar_visible)
759             {
760               scrolled_window->hscrollbar_visible = TRUE;
761               scrolled_window->vscrollbar_visible = TRUE;
762
763               /* a new resize is already queued at this point,
764                * so we will immediatedly get reinvoked
765                */
766               return;
767             }
768           
769           count++;
770         }
771       while (previous_hvis != scrolled_window->hscrollbar_visible ||
772              previous_vvis != scrolled_window->vscrollbar_visible);
773     }
774   else
775     gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
776   
777   if (scrolled_window->hscrollbar_visible)
778     {
779       if (!GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
780         gtk_widget_show (scrolled_window->hscrollbar);
781
782       child_allocation.x = relative_allocation.x;
783       if (scrolled_window->window_placement == GTK_CORNER_TOP_LEFT ||
784           scrolled_window->window_placement == GTK_CORNER_TOP_RIGHT)
785         child_allocation.y = (relative_allocation.y +
786                               relative_allocation.height +
787                               SCROLLBAR_SPACING (scrolled_window));
788       else
789         child_allocation.y = GTK_CONTAINER (scrolled_window)->border_width;
790
791       child_allocation.width = relative_allocation.width;
792       child_allocation.height = scrolled_window->hscrollbar->requisition.height;
793       child_allocation.x += allocation->x;
794       child_allocation.y += allocation->y;
795
796       gtk_widget_size_allocate (scrolled_window->hscrollbar, &child_allocation);
797     }
798   else if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
799     gtk_widget_hide (scrolled_window->hscrollbar);
800
801   if (scrolled_window->vscrollbar_visible)
802     {
803       if (!GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
804         gtk_widget_show (scrolled_window->vscrollbar);
805
806       if (scrolled_window->window_placement == GTK_CORNER_TOP_LEFT ||
807           scrolled_window->window_placement == GTK_CORNER_BOTTOM_LEFT)
808         child_allocation.x = (relative_allocation.x +
809                               relative_allocation.width +
810                               SCROLLBAR_SPACING (scrolled_window));
811       else
812         child_allocation.x = GTK_CONTAINER (scrolled_window)->border_width;
813
814       child_allocation.y = relative_allocation.y;
815       child_allocation.width = scrolled_window->vscrollbar->requisition.width;
816       child_allocation.height = relative_allocation.height;
817       child_allocation.x += allocation->x;
818       child_allocation.y += allocation->y;
819
820       gtk_widget_size_allocate (scrolled_window->vscrollbar, &child_allocation);
821     }
822   else if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
823     gtk_widget_hide (scrolled_window->vscrollbar);
824 }
825
826
827 static void
828 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
829                                         gpointer       data)
830 {
831   GtkScrolledWindow *scrolled_win;
832
833   g_return_if_fail (adjustment != NULL);
834   g_return_if_fail (data != NULL);
835
836   scrolled_win = GTK_SCROLLED_WINDOW (data);
837
838   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->hscrollbar)))
839     {
840       if (scrolled_win->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
841         {
842           gboolean visible;
843           
844           visible = scrolled_win->hscrollbar_visible;
845           scrolled_win->hscrollbar_visible = (adjustment->upper - adjustment->lower >
846                                               adjustment->page_size);
847           if (scrolled_win->hscrollbar_visible != visible)
848             gtk_widget_queue_resize (GTK_WIDGET (scrolled_win));
849         }
850     }
851   else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->vscrollbar)))
852     {
853       if (scrolled_win->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
854         {
855           gboolean visible;
856
857           visible = scrolled_win->vscrollbar_visible;
858           scrolled_win->vscrollbar_visible = (adjustment->upper - adjustment->lower >
859                                               adjustment->page_size);
860           if (scrolled_win->vscrollbar_visible != visible)
861             gtk_widget_queue_resize (GTK_WIDGET (scrolled_win));
862         }
863     }
864 }
865
866 static void
867 gtk_scrolled_window_add (GtkContainer *container,
868                          GtkWidget    *child)
869 {
870   GtkScrolledWindow *scrolled_window;
871   GtkBin *bin;
872
873   bin = GTK_BIN (container);
874   g_return_if_fail (bin->child == NULL);
875
876   scrolled_window = GTK_SCROLLED_WINDOW (container);
877
878   gtk_widget_set_parent (child, GTK_WIDGET (bin));
879   bin->child = child;
880
881   /* this is a temporary message */
882   if (!gtk_widget_set_scroll_adjustments (child,
883                                           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
884                                           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar))))
885     g_message ("gtk_scrolled_window_add(): cannot add non scrollable widget "
886                "use gtk_scrolled_window_add_with_viewport() instead");
887
888   if (GTK_WIDGET_VISIBLE (child->parent))
889     {
890       if (GTK_WIDGET_REALIZED (child->parent) &&
891           !GTK_WIDGET_REALIZED (child))
892         gtk_widget_realize (child);
893
894       if (GTK_WIDGET_MAPPED (child->parent) &&
895           !GTK_WIDGET_MAPPED (child))
896         gtk_widget_map (child);
897     }
898
899   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (container))
900     gtk_widget_queue_resize (child);
901 }
902
903 static void
904 gtk_scrolled_window_remove (GtkContainer *container,
905                             GtkWidget    *child)
906 {
907   g_return_if_fail (container != NULL);
908   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
909   g_return_if_fail (child != NULL);
910   g_return_if_fail (GTK_BIN (container)->child == child);
911   
912   gtk_widget_set_scroll_adjustments (child, NULL, NULL);
913
914   /* chain parent class handler to remove child */
915   GTK_CONTAINER_CLASS (parent_class)->remove (container, child);
916 }
917
918 void
919 gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
920                                        GtkWidget         *child)
921 {
922   GtkBin *bin;
923   GtkWidget *viewport;
924
925   g_return_if_fail (scrolled_window != NULL);
926   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
927   g_return_if_fail (child != NULL);
928   g_return_if_fail (GTK_IS_WIDGET (child));
929   g_return_if_fail (child->parent == NULL);
930
931   bin = GTK_BIN (scrolled_window);
932
933   if (bin->child != NULL)
934     {
935       g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
936       g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
937
938       viewport = bin->child;
939     }
940   else
941     {
942       viewport =
943         gtk_viewport_new (gtk_scrolled_window_get_hadjustment (scrolled_window),
944                           gtk_scrolled_window_get_vadjustment (scrolled_window));
945       gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
946     }
947
948   gtk_widget_show (viewport);
949   gtk_container_add (GTK_CONTAINER (viewport), child);
950 }