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