]> Pileus Git - ~andy/gtk/blob - gtk/gtkscrolledwindow.c
Fix #56586
[~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 Lesser 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser 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
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "gtkscrolledwindow.h"
28 #include "gtksignal.h"
29 #include "gtkintl.h"
30
31
32 /* scrolled window policy and size requisition handling:
33  *
34  * gtk size requisition works as follows:
35  *   a widget upon size-request reports the width and height that it finds
36  *   to be best suited to display its contents, including children.
37  *   the width and/or height reported from a widget upon size requisition
38  *   may be overidden by the user by specifying a width and/or height
39  *   other than 0 through gtk_widget_set_usize().
40  *
41  * a scrolled window needs (for imlementing all three policy types) to
42  * request its width and height based on two different rationales.
43  * 1)   the user wants the scrolled window to just fit into the space
44  *      that it gets allocated for a specifc dimension.
45  * 1.1) this does not apply if the user specified a concrete value
46  *      value for that specific dimension by either specifying usize for the
47  *      scrolled window or for its child.
48  * 2)   the user wants the scrolled window to take as much space up as
49  *      is desired by the child for a specifc dimension (i.e. POLICY_NEVER).
50  *
51  * also, kinda obvious:
52  * 3)   a user would certainly not have choosen a scrolled window as a container
53  *      for the child, if the resulting allocation takes up more space than the
54  *      child would have allocated without the scrolled window.
55  *
56  * conclusions:
57  * A) from 1) follows: the scrolled window shouldn't request more space for a
58  *    specifc dimension than is required at minimum.
59  * B) from 1.1) follows: the requisition may be overidden by usize of the scrolled
60  *    window (done automatically) or by usize of the child (needs to be checked).
61  * C) from 2) follows: for POLICY_NEVER, the scrolled window simply reports the
62  *    child's dimension.
63  * D) from 3) follows: the scrolled window child's minimum width and minimum height
64  *    under A) at least correspond to the space taken up by its scrollbars.
65  */
66
67 #define SCROLLBAR_SPACING(w) (GTK_SCROLLED_WINDOW_GET_CLASS (w)->scrollbar_spacing)
68
69 #define DEFAULT_SCROLLBAR_SPACING  3
70
71 enum {
72   PROP_0,
73   PROP_HADJUSTMENT,
74   PROP_VADJUSTMENT,
75   PROP_HSCROLLBAR_POLICY,
76   PROP_VSCROLLBAR_POLICY,
77   PROP_WINDOW_PLACEMENT,
78   PROP_SHADOW_TYPE,
79   PROP_LAST
80 };
81
82 static void gtk_scrolled_window_class_init         (GtkScrolledWindowClass *klass);
83 static void gtk_scrolled_window_init               (GtkScrolledWindow      *scrolled_window);
84 static void gtk_scrolled_window_destroy            (GtkObject              *object);
85 static void gtk_scrolled_window_finalize           (GObject                *object);
86 static void gtk_scrolled_window_set_property       (GObject                *object,
87                                                     guint                   prop_id,
88                                                     const GValue           *value,
89                                                     GParamSpec             *pspec);
90 static void gtk_scrolled_window_get_property       (GObject                *object,
91                                                     guint                   prop_id,
92                                                     GValue                 *value,
93                                                     GParamSpec             *pspec);
94
95 static gint gtk_scrolled_window_expose             (GtkWidget              *widget,
96                                                     GdkEventExpose         *event);
97 static void gtk_scrolled_window_size_request       (GtkWidget              *widget,
98                                                     GtkRequisition         *requisition);
99 static void gtk_scrolled_window_size_allocate      (GtkWidget              *widget,
100                                                     GtkAllocation          *allocation);
101 static gint gtk_scrolled_window_scroll_event       (GtkWidget              *widget,
102                                                     GdkEventScroll         *event);
103 static void gtk_scrolled_window_add                (GtkContainer           *container,
104                                                     GtkWidget              *widget);
105 static void gtk_scrolled_window_remove             (GtkContainer           *container,
106                                                     GtkWidget              *widget);
107 static void gtk_scrolled_window_forall             (GtkContainer           *container,
108                                                     gboolean                include_internals,
109                                                     GtkCallback             callback,
110                                                     gpointer                callback_data);
111 static void gtk_scrolled_window_relative_allocation(GtkWidget              *widget,
112                                                     GtkAllocation          *allocation);
113 static void gtk_scrolled_window_adjustment_changed (GtkAdjustment          *adjustment,
114                                                     gpointer                data);
115
116 static GtkContainerClass *parent_class = NULL;
117
118
119 GtkType
120 gtk_scrolled_window_get_type (void)
121 {
122   static GtkType scrolled_window_type = 0;
123
124   if (!scrolled_window_type)
125     {
126       static const GtkTypeInfo scrolled_window_info =
127       {
128         "GtkScrolledWindow",
129         sizeof (GtkScrolledWindow),
130         sizeof (GtkScrolledWindowClass),
131         (GtkClassInitFunc) gtk_scrolled_window_class_init,
132         (GtkObjectInitFunc) gtk_scrolled_window_init,
133         /* reserved_1 */ NULL,
134         /* reserved_2 */ NULL,
135         (GtkClassInitFunc) NULL,
136       };
137
138       scrolled_window_type = gtk_type_unique (GTK_TYPE_BIN, &scrolled_window_info);
139     }
140
141   return scrolled_window_type;
142 }
143
144 static void
145 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
146 {
147   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
148   GtkObjectClass *object_class;
149   GtkWidgetClass *widget_class;
150   GtkContainerClass *container_class;
151
152   object_class = (GtkObjectClass*) class;
153   widget_class = (GtkWidgetClass*) class;
154   container_class = (GtkContainerClass*) class;
155   parent_class = gtk_type_class (GTK_TYPE_BIN);
156
157   gobject_class->finalize = gtk_scrolled_window_finalize;
158   gobject_class->set_property = gtk_scrolled_window_set_property;
159   gobject_class->get_property = gtk_scrolled_window_get_property;
160
161   object_class->destroy = gtk_scrolled_window_destroy;
162
163   widget_class->expose_event = gtk_scrolled_window_expose;
164   widget_class->size_request = gtk_scrolled_window_size_request;
165   widget_class->size_allocate = gtk_scrolled_window_size_allocate;
166   widget_class->scroll_event = gtk_scrolled_window_scroll_event;
167
168   container_class->add = gtk_scrolled_window_add;
169   container_class->remove = gtk_scrolled_window_remove;
170   container_class->forall = gtk_scrolled_window_forall;
171
172   class->scrollbar_spacing = DEFAULT_SCROLLBAR_SPACING;
173
174   g_object_class_install_property (gobject_class,
175                                    PROP_HADJUSTMENT,
176                                    g_param_spec_object ("hadjustment",
177                                                         _("Horizontal Adjustment"),
178                                                         _("The GtkAdjustment for the horizontal position."),
179                                                         GTK_TYPE_ADJUSTMENT,
180                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
181   g_object_class_install_property (gobject_class,
182                                    PROP_VADJUSTMENT,
183                                    g_param_spec_object ("vadjustment",
184                                                         _("Vertical Adjustment"),
185                                                         _("The GtkAdjustment for the vertical position."),
186                                                         GTK_TYPE_ADJUSTMENT,
187                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
188   g_object_class_install_property (gobject_class,
189                                    PROP_HSCROLLBAR_POLICY,
190                                    g_param_spec_enum ("hscrollbar_policy",
191                                                       _("Horizontal Scrollbar Policy"),
192                                                       _("When the horizontal scrollbar is displayed"),
193                                                       GTK_TYPE_POLICY_TYPE,
194                                                       GTK_POLICY_ALWAYS,
195                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
196   g_object_class_install_property (gobject_class,
197                                    PROP_VSCROLLBAR_POLICY,
198                                    g_param_spec_enum ("vscrollbar_policy",
199                                                       _("Vertical Scrollbar Policy"),
200                                                       _("When the vertical scrollbar is displayed"),
201                                                       GTK_TYPE_POLICY_TYPE,
202                                                       GTK_POLICY_ALWAYS,
203                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
204
205   g_object_class_install_property (gobject_class,
206                                    PROP_WINDOW_PLACEMENT,
207                                    g_param_spec_enum ("window_placement",
208                                                       _("Window Placement"),
209                                                       _("Where the contents are located with respect to the scrollbars"),
210                                                       GTK_TYPE_CORNER_TYPE,
211                                                       GTK_CORNER_TOP_LEFT,
212                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
213   g_object_class_install_property (gobject_class,
214                                    PROP_SHADOW_TYPE,
215                                    g_param_spec_enum ("shadow_type",
216                                                       _("Shadow Type"),
217                                                       _("Style of bevel around the contents"),
218                                                       GTK_TYPE_SHADOW_TYPE,
219                                                       GTK_SHADOW_NONE,
220                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
221 }
222
223 static void
224 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
225 {
226   GTK_WIDGET_SET_FLAGS (scrolled_window, GTK_NO_WINDOW);
227
228   gtk_container_set_resize_mode (GTK_CONTAINER (scrolled_window), GTK_RESIZE_QUEUE);
229
230   scrolled_window->hscrollbar = NULL;
231   scrolled_window->vscrollbar = NULL;
232   scrolled_window->hscrollbar_policy = GTK_POLICY_ALWAYS;
233   scrolled_window->vscrollbar_policy = GTK_POLICY_ALWAYS;
234   scrolled_window->hscrollbar_visible = FALSE;
235   scrolled_window->vscrollbar_visible = FALSE;
236   scrolled_window->window_placement = GTK_CORNER_TOP_LEFT;
237   
238 }
239
240 GtkWidget*
241 gtk_scrolled_window_new (GtkAdjustment *hadjustment,
242                          GtkAdjustment *vadjustment)
243 {
244   GtkWidget *scrolled_window;
245
246   if (hadjustment)
247     g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
248
249   if (vadjustment)
250     g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
251
252   scrolled_window = gtk_widget_new (GTK_TYPE_SCROLLED_WINDOW,
253                                     "hadjustment", hadjustment,
254                                     "vadjustment", vadjustment,
255                                     NULL);
256
257   return scrolled_window;
258 }
259
260 void
261 gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
262                                      GtkAdjustment     *hadjustment)
263 {
264   GtkBin *bin;
265
266   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
267   if (hadjustment)
268     g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
269   else
270     hadjustment = (GtkAdjustment*) gtk_object_new (GTK_TYPE_ADJUSTMENT, NULL);
271
272   bin = GTK_BIN (scrolled_window);
273
274   if (!scrolled_window->hscrollbar)
275     {
276       gtk_widget_push_composite_child ();
277       scrolled_window->hscrollbar = gtk_hscrollbar_new (hadjustment);
278       gtk_widget_set_composite_name (scrolled_window->hscrollbar, "hscrollbar");
279       gtk_widget_pop_composite_child ();
280
281       gtk_widget_set_parent (scrolled_window->hscrollbar, GTK_WIDGET (scrolled_window));
282       gtk_widget_ref (scrolled_window->hscrollbar);
283       gtk_widget_show (scrolled_window->hscrollbar);
284     }
285   else
286     {
287       GtkAdjustment *old_adjustment;
288       
289       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
290       if (old_adjustment == hadjustment)
291         return;
292
293       gtk_signal_disconnect_by_func (GTK_OBJECT (old_adjustment),
294                                      GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
295                                      scrolled_window);
296       gtk_range_set_adjustment (GTK_RANGE (scrolled_window->hscrollbar),
297                                 hadjustment);
298     }
299   hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
300   gtk_signal_connect (GTK_OBJECT (hadjustment),
301                       "changed",
302                       GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
303                       scrolled_window);
304   gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
305   
306   if (bin->child)
307     gtk_widget_set_scroll_adjustments (bin->child,
308                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
309                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
310
311   g_object_notify (G_OBJECT (scrolled_window), "hadjustment");
312 }
313
314 void
315 gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
316                                      GtkAdjustment     *vadjustment)
317 {
318   GtkBin *bin;
319
320   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
321   if (vadjustment)
322     g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
323   else
324     vadjustment = (GtkAdjustment*) gtk_object_new (GTK_TYPE_ADJUSTMENT, NULL);
325
326   bin = GTK_BIN (scrolled_window);
327
328   if (!scrolled_window->vscrollbar)
329     {
330       gtk_widget_push_composite_child ();
331       scrolled_window->vscrollbar = gtk_vscrollbar_new (vadjustment);
332       gtk_widget_set_composite_name (scrolled_window->vscrollbar, "vscrollbar");
333       gtk_widget_pop_composite_child ();
334
335       gtk_widget_set_parent (scrolled_window->vscrollbar, GTK_WIDGET (scrolled_window));
336       gtk_widget_ref (scrolled_window->vscrollbar);
337       gtk_widget_show (scrolled_window->vscrollbar);
338     }
339   else
340     {
341       GtkAdjustment *old_adjustment;
342       
343       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
344       if (old_adjustment == vadjustment)
345         return;
346
347       gtk_signal_disconnect_by_func (GTK_OBJECT (old_adjustment),
348                                      GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
349                                      scrolled_window);
350       gtk_range_set_adjustment (GTK_RANGE (scrolled_window->vscrollbar),
351                                 vadjustment);
352     }
353   vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
354   gtk_signal_connect (GTK_OBJECT (vadjustment),
355                       "changed",
356                       GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
357                       scrolled_window);
358   gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
359
360   if (bin->child)
361     gtk_widget_set_scroll_adjustments (bin->child,
362                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
363                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
364
365   g_object_notify (G_OBJECT (scrolled_window), "vadjustment");
366 }
367
368 GtkAdjustment*
369 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
370 {
371   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
372
373   return (scrolled_window->hscrollbar ?
374           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)) :
375           NULL);
376 }
377
378 GtkAdjustment*
379 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
380 {
381   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
382
383   return (scrolled_window->vscrollbar ?
384           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)) :
385           NULL);
386 }
387
388 void
389 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
390                                 GtkPolicyType      hscrollbar_policy,
391                                 GtkPolicyType      vscrollbar_policy)
392 {
393   GObject *object = G_OBJECT (scrolled_window);
394   
395   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
396
397   if ((scrolled_window->hscrollbar_policy != hscrollbar_policy) ||
398       (scrolled_window->vscrollbar_policy != vscrollbar_policy))
399     {
400       scrolled_window->hscrollbar_policy = hscrollbar_policy;
401       scrolled_window->vscrollbar_policy = vscrollbar_policy;
402
403       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
404
405       g_object_freeze_notify (object);
406       g_object_notify (object, "hscrollbar_policy");
407       g_object_notify (object, "vscrollbar_policy");
408       g_object_thaw_notify (object);
409     }
410 }
411
412 /**
413  * gtk_scrolled_window_get_policy:
414  * @scrolled_window: a #GtkScrolledWindow
415  * @hscrollbar_policy: location to store the policy for the horizontal scrollbar, or %NULL.
416  * @vscrollbar_policy: location to store the policy for the horizontal scrollbar, or %NULL.
417  * 
418  * Retrieves the current policy values for the horizontal and vertical
419  * scrollbars. See gtk_scrolled_window_set_policy().
420  **/
421 void
422 gtk_scrolled_window_get_policy (GtkScrolledWindow *scrolled_window,
423                                 GtkPolicyType     *hscrollbar_policy,
424                                 GtkPolicyType     *vscrollbar_policy)
425 {
426   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
427
428   if (hscrollbar_policy)
429     *hscrollbar_policy = scrolled_window->hscrollbar_policy;
430   if (vscrollbar_policy)
431     *vscrollbar_policy = scrolled_window->vscrollbar_policy;
432 }
433
434 void
435 gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
436                                    GtkCornerType      window_placement)
437 {
438   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
439
440   if (scrolled_window->window_placement != window_placement)
441     {
442       scrolled_window->window_placement = window_placement;
443
444       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
445       
446       g_object_notify (G_OBJECT (scrolled_window), "window_placement");
447     }
448 }
449
450 /**
451  * gtk_scrolled_window_get_placement:
452  * @scrolled_window: a #GtkScrolledWindow
453  *
454  * Gets the placement of the scrollbars for the scrolled window. See 
455  * gtk_scrolled_window_set_placement().
456  *
457  * Return value: the current placement value.
458  **/
459 GtkCornerType
460 gtk_scrolled_window_get_placement (GtkScrolledWindow *scrolled_window)
461 {
462   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_CORNER_TOP_LEFT);
463
464   return scrolled_window->window_placement;
465 }
466
467 /**
468  * gtk_scrolled_window_set_shadow_type:
469  * @scrolled_window: a #GtkScrolledWindow
470  * @type: kind of shadow to draw around scrolled window contents
471  *
472  * Changes the type of shadow drawn around the contents of
473  * @scrolled_window.
474  * 
475  **/
476 void
477 gtk_scrolled_window_set_shadow_type (GtkScrolledWindow *scrolled_window,
478                                      GtkShadowType      type)
479 {
480   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
481   g_return_if_fail (type >= GTK_SHADOW_NONE && type <= GTK_SHADOW_ETCHED_OUT);
482   
483   if (scrolled_window->shadow_type != type)
484     {
485       scrolled_window->shadow_type = type;
486
487       if (GTK_WIDGET_DRAWABLE (scrolled_window))
488         gtk_widget_queue_clear (GTK_WIDGET (scrolled_window));
489
490       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
491
492       g_object_notify (G_OBJECT (scrolled_window), "shadow_type");
493     }
494 }
495
496 /**
497  * gtk_scrolled_window_get_shadow_type:
498  * @scrolled_window: a #GtkScrolledWindow
499  *
500  * Gets the shadow type of the scrolled window. See 
501  * gtk_scrolled_window_set_shadow_type().
502  *
503  * Return value: the current shadow type
504  **/
505 GtkShadowType
506 gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolled_window)
507 {
508   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_NONE);
509
510   return scrolled_window->shadow_type;
511 }
512
513 static void
514 gtk_scrolled_window_destroy (GtkObject *object)
515 {
516   GtkScrolledWindow *scrolled_window;
517
518   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (object));
519
520   scrolled_window = GTK_SCROLLED_WINDOW (object);
521
522   gtk_widget_unparent (scrolled_window->hscrollbar);
523   gtk_widget_unparent (scrolled_window->vscrollbar);
524   gtk_widget_destroy (scrolled_window->hscrollbar);
525   gtk_widget_destroy (scrolled_window->vscrollbar);
526
527   GTK_OBJECT_CLASS (parent_class)->destroy (object);
528 }
529
530 static void
531 gtk_scrolled_window_finalize (GObject *object)
532 {
533   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
534
535   gtk_widget_unref (scrolled_window->hscrollbar);
536   gtk_widget_unref (scrolled_window->vscrollbar);
537
538   G_OBJECT_CLASS (parent_class)->finalize (object);
539 }
540
541 static void
542 gtk_scrolled_window_set_property (GObject      *object,
543                                   guint         prop_id,
544                                   const GValue *value,
545                                   GParamSpec   *pspec)
546 {
547   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
548   
549   switch (prop_id)
550     {
551     case PROP_HADJUSTMENT:
552       gtk_scrolled_window_set_hadjustment (scrolled_window,
553                                            g_value_get_object (value));
554       break;
555     case PROP_VADJUSTMENT:
556       gtk_scrolled_window_set_vadjustment (scrolled_window,
557                                            g_value_get_object (value));
558       break;
559     case PROP_HSCROLLBAR_POLICY:
560       gtk_scrolled_window_set_policy (scrolled_window,
561                                       g_value_get_enum (value),
562                                       scrolled_window->vscrollbar_policy);
563       break;
564     case PROP_VSCROLLBAR_POLICY:
565       gtk_scrolled_window_set_policy (scrolled_window,
566                                       scrolled_window->hscrollbar_policy,
567                                       g_value_get_enum (value));
568       break;
569     case PROP_WINDOW_PLACEMENT:
570       gtk_scrolled_window_set_placement (scrolled_window,
571                                          g_value_get_enum (value));
572       break;
573     case PROP_SHADOW_TYPE:
574       gtk_scrolled_window_set_shadow_type (scrolled_window,
575                                            g_value_get_enum (value));
576       break;
577     default:
578       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
579       break;
580     }
581 }
582
583 static void
584 gtk_scrolled_window_get_property (GObject    *object,
585                                   guint       prop_id,
586                                   GValue     *value,
587                                   GParamSpec *pspec)
588 {
589   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
590   
591   switch (prop_id)
592     {
593     case PROP_HADJUSTMENT:
594       g_value_set_object (value,
595                           G_OBJECT (gtk_scrolled_window_get_hadjustment (scrolled_window)));
596       break;
597     case PROP_VADJUSTMENT:
598       g_value_set_object (value,
599                           G_OBJECT (gtk_scrolled_window_get_vadjustment (scrolled_window)));
600       break;
601     case PROP_HSCROLLBAR_POLICY:
602       g_value_set_enum (value, scrolled_window->hscrollbar_policy);
603       break;
604     case PROP_VSCROLLBAR_POLICY:
605       g_value_set_enum (value, scrolled_window->vscrollbar_policy);
606       break;
607     case PROP_WINDOW_PLACEMENT:
608       g_value_set_enum (value, scrolled_window->window_placement);
609       break;
610     case PROP_SHADOW_TYPE:
611       g_value_set_enum (value, scrolled_window->shadow_type);
612       break;
613     default:
614       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
615       break;
616     }
617 }
618
619 static void
620 gtk_scrolled_window_paint (GtkWidget    *widget,
621                            GdkRectangle *area)
622 {
623   GtkAllocation relative_allocation;
624   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
625
626   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
627     {
628       gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
629       
630       relative_allocation.x -= widget->style->xthickness;
631       relative_allocation.y -= widget->style->ythickness;
632       relative_allocation.width += 2 * widget->style->xthickness;
633       relative_allocation.height += 2 * widget->style->ythickness;
634       
635       gtk_paint_shadow (widget->style, widget->window,
636                         GTK_STATE_NORMAL, scrolled_window->shadow_type,
637                         area, widget, "scrolled_window",
638                         widget->allocation.x + relative_allocation.x,
639                         widget->allocation.y + relative_allocation.y,
640                         relative_allocation.width,
641                         relative_allocation.height);
642     }
643 }
644
645 static gint
646 gtk_scrolled_window_expose (GtkWidget      *widget,
647                             GdkEventExpose *event)
648 {
649   if (GTK_WIDGET_DRAWABLE (widget))
650     {
651       gtk_scrolled_window_paint (widget, &event->area);
652
653       (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
654     }
655
656   return FALSE;
657 }
658
659 static void
660 gtk_scrolled_window_forall (GtkContainer *container,
661                             gboolean      include_internals,
662                             GtkCallback   callback,
663                             gpointer      callback_data)
664 {
665   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
666   g_return_if_fail (callback != NULL);
667
668   GTK_CONTAINER_CLASS (parent_class)->forall (container,
669                                               include_internals,
670                                               callback,
671                                               callback_data);
672   if (include_internals)
673     {
674       GtkScrolledWindow *scrolled_window;
675
676       scrolled_window = GTK_SCROLLED_WINDOW (container);
677       
678       if (scrolled_window->vscrollbar)
679         callback (scrolled_window->vscrollbar, callback_data);
680       if (scrolled_window->hscrollbar)
681         callback (scrolled_window->hscrollbar, callback_data);
682     }
683 }
684
685 static void
686 gtk_scrolled_window_size_request (GtkWidget      *widget,
687                                   GtkRequisition *requisition)
688 {
689   GtkScrolledWindow *scrolled_window;
690   GtkBin *bin;
691   gint extra_width;
692   gint extra_height;
693   GtkRequisition hscrollbar_requisition;
694   GtkRequisition vscrollbar_requisition;
695   GtkRequisition child_requisition;
696
697   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
698   g_return_if_fail (requisition != NULL);
699
700   scrolled_window = GTK_SCROLLED_WINDOW (widget);
701   bin = GTK_BIN (scrolled_window);
702
703   extra_width = 0;
704   extra_height = 0;
705   requisition->width = 0;
706   requisition->height = 0;
707   
708   gtk_widget_size_request (scrolled_window->hscrollbar,
709                            &hscrollbar_requisition);
710   gtk_widget_size_request (scrolled_window->vscrollbar,
711                            &vscrollbar_requisition);
712   
713   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
714     {
715       gtk_widget_size_request (bin->child, &child_requisition);
716
717       if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
718         requisition->width += child_requisition.width;
719       else
720         {
721           GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (bin->child, FALSE);
722
723           if (aux_info && aux_info->width > 0)
724             {
725               requisition->width += aux_info->width;
726               extra_width = -1;
727             }
728           else
729             requisition->width += vscrollbar_requisition.width;
730         }
731
732       if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
733         requisition->height += child_requisition.height;
734       else
735         {
736           GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (bin->child, FALSE);
737
738           if (aux_info && aux_info->height > 0)
739             {
740               requisition->height += aux_info->height;
741               extra_height = -1;
742             }
743           else
744             requisition->height += hscrollbar_requisition.height;
745         }
746     }
747
748   if (scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
749       scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
750     {
751       requisition->width = MAX (requisition->width, hscrollbar_requisition.width);
752       if (!extra_height || scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
753         extra_height = SCROLLBAR_SPACING (scrolled_window) + hscrollbar_requisition.height;
754     }
755
756   if (scrolled_window->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
757       scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
758     {
759       requisition->height = MAX (requisition->height, vscrollbar_requisition.height);
760       if (!extra_height || scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
761         extra_width = SCROLLBAR_SPACING (scrolled_window) + vscrollbar_requisition.width;
762     }
763
764   requisition->width += GTK_CONTAINER (widget)->border_width * 2 + MAX (0, extra_width);
765   requisition->height += GTK_CONTAINER (widget)->border_width * 2 + MAX (0, extra_height);
766
767   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
768     {
769       requisition->width += 2 * widget->style->xthickness;
770       requisition->height += 2 * widget->style->ythickness;
771     }
772 }
773
774 static void
775 gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
776                                          GtkAllocation *allocation)
777 {
778   GtkScrolledWindow *scrolled_window;
779
780   g_return_if_fail (widget != NULL);
781   g_return_if_fail (allocation != NULL);
782
783   scrolled_window = GTK_SCROLLED_WINDOW (widget);
784
785   allocation->x = GTK_CONTAINER (widget)->border_width;
786   allocation->y = GTK_CONTAINER (widget)->border_width;
787
788   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
789     {
790       allocation->x += widget->style->xthickness;
791       allocation->y += widget->style->ythickness;
792     }
793   
794   allocation->width = MAX (1, (gint)widget->allocation.width - allocation->x * 2);
795   allocation->height = MAX (1, (gint)widget->allocation.height - allocation->y * 2);
796
797   if (scrolled_window->vscrollbar_visible)
798     {
799       GtkRequisition vscrollbar_requisition;
800       gtk_widget_get_child_requisition (scrolled_window->vscrollbar,
801                                         &vscrollbar_requisition);
802   
803       if (scrolled_window->window_placement == GTK_CORNER_TOP_RIGHT ||
804           scrolled_window->window_placement == GTK_CORNER_BOTTOM_RIGHT)
805         allocation->x += (vscrollbar_requisition.width +
806                           SCROLLBAR_SPACING (scrolled_window));
807
808       allocation->width = MAX (1, (gint)allocation->width -
809                                ((gint)vscrollbar_requisition.width +
810                                 (gint)SCROLLBAR_SPACING (scrolled_window)));
811     }
812   if (scrolled_window->hscrollbar_visible)
813     {
814       GtkRequisition hscrollbar_requisition;
815       gtk_widget_get_child_requisition (scrolled_window->hscrollbar,
816                                         &hscrollbar_requisition);
817   
818       if (scrolled_window->window_placement == GTK_CORNER_BOTTOM_LEFT ||
819           scrolled_window->window_placement == GTK_CORNER_BOTTOM_RIGHT)
820         allocation->y += (hscrollbar_requisition.height +
821                           SCROLLBAR_SPACING (scrolled_window));
822
823       allocation->height = MAX (1, (gint)allocation->height -
824                                 ((gint)hscrollbar_requisition.height +
825                                  (gint)SCROLLBAR_SPACING (scrolled_window)));
826     }
827 }
828
829 static void
830 gtk_scrolled_window_size_allocate (GtkWidget     *widget,
831                                    GtkAllocation *allocation)
832 {
833   GtkScrolledWindow *scrolled_window;
834   GtkBin *bin;
835   GtkAllocation relative_allocation;
836   GtkAllocation child_allocation;
837   
838   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
839   g_return_if_fail (allocation != NULL);
840
841   scrolled_window = GTK_SCROLLED_WINDOW (widget);
842   bin = GTK_BIN (scrolled_window);
843
844   widget->allocation = *allocation;
845
846   if (scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
847     scrolled_window->hscrollbar_visible = TRUE;
848   else if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
849     scrolled_window->hscrollbar_visible = FALSE;
850   if (scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
851     scrolled_window->vscrollbar_visible = TRUE;
852   else if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
853     scrolled_window->vscrollbar_visible = FALSE;
854
855   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
856     {
857       gboolean previous_hvis;
858       gboolean previous_vvis;
859       guint count = 0;
860       
861       do
862         {
863           gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
864           
865           child_allocation.x = relative_allocation.x + allocation->x;
866           child_allocation.y = relative_allocation.y + allocation->y;
867           child_allocation.width = relative_allocation.width;
868           child_allocation.height = relative_allocation.height;
869           
870           previous_hvis = scrolled_window->hscrollbar_visible;
871           previous_vvis = scrolled_window->vscrollbar_visible;
872           
873           gtk_widget_size_allocate (bin->child, &child_allocation);
874
875           /* If, after the first iteration, the hscrollbar and the
876            * vscrollbar flip visiblity, then we need both.
877            */
878           if (count &&
879               previous_hvis != scrolled_window->hscrollbar_visible &&
880               previous_vvis != scrolled_window->vscrollbar_visible)
881             {
882               scrolled_window->hscrollbar_visible = TRUE;
883               scrolled_window->vscrollbar_visible = TRUE;
884
885               /* a new resize is already queued at this point,
886                * so we will immediatedly get reinvoked
887                */
888               return;
889             }
890           
891           count++;
892         }
893       while (previous_hvis != scrolled_window->hscrollbar_visible ||
894              previous_vvis != scrolled_window->vscrollbar_visible);
895     }
896   else
897     gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
898   
899   if (scrolled_window->hscrollbar_visible)
900     {
901       GtkRequisition hscrollbar_requisition;
902       gtk_widget_get_child_requisition (scrolled_window->hscrollbar,
903                                         &hscrollbar_requisition);
904   
905       if (!GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
906         gtk_widget_show (scrolled_window->hscrollbar);
907
908       child_allocation.x = relative_allocation.x;
909       if (scrolled_window->window_placement == GTK_CORNER_TOP_LEFT ||
910           scrolled_window->window_placement == GTK_CORNER_TOP_RIGHT)
911         child_allocation.y = (relative_allocation.y +
912                               relative_allocation.height +
913                               SCROLLBAR_SPACING (scrolled_window) +
914                               (scrolled_window->shadow_type == GTK_SHADOW_NONE ?
915                                0 : widget->style->ythickness));
916       else
917         child_allocation.y = GTK_CONTAINER (scrolled_window)->border_width;
918
919       child_allocation.width = relative_allocation.width;
920       child_allocation.height = hscrollbar_requisition.height;
921       child_allocation.x += allocation->x;
922       child_allocation.y += allocation->y;
923
924       if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
925         {
926           child_allocation.x -= widget->style->xthickness;
927           child_allocation.width += 2 * widget->style->xthickness;
928         }
929
930       gtk_widget_size_allocate (scrolled_window->hscrollbar, &child_allocation);
931     }
932   else if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
933     gtk_widget_hide (scrolled_window->hscrollbar);
934
935   if (scrolled_window->vscrollbar_visible)
936     {
937       GtkRequisition vscrollbar_requisition;
938       if (!GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
939         gtk_widget_show (scrolled_window->vscrollbar);
940
941       gtk_widget_get_child_requisition (scrolled_window->vscrollbar,
942                                         &vscrollbar_requisition);
943
944       if (scrolled_window->window_placement == GTK_CORNER_TOP_LEFT ||
945           scrolled_window->window_placement == GTK_CORNER_BOTTOM_LEFT)
946         child_allocation.x = (relative_allocation.x +
947                               relative_allocation.width +
948                               SCROLLBAR_SPACING (scrolled_window) +
949                               (scrolled_window->shadow_type == GTK_SHADOW_NONE ?
950                                0 : widget->style->xthickness));
951       else
952         child_allocation.x = GTK_CONTAINER (scrolled_window)->border_width;
953
954       child_allocation.y = relative_allocation.y;
955       child_allocation.width = vscrollbar_requisition.width;
956       child_allocation.height = relative_allocation.height;
957       child_allocation.x += allocation->x;
958       child_allocation.y += allocation->y;
959
960       if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
961         {
962           child_allocation.y -= widget->style->ythickness;
963           child_allocation.height += 2 * widget->style->ythickness;
964         }
965
966       gtk_widget_size_allocate (scrolled_window->vscrollbar, &child_allocation);
967     }
968   else if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
969     gtk_widget_hide (scrolled_window->vscrollbar);
970 }
971
972 static gint
973 gtk_scrolled_window_scroll_event (GtkWidget *widget,
974                                   GdkEventScroll *event)
975 {
976   GtkWidget *range;
977
978   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE);
979   g_return_val_if_fail (event != NULL, FALSE);  
980
981   if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
982     range = GTK_SCROLLED_WINDOW (widget)->vscrollbar;
983   else
984     range = GTK_SCROLLED_WINDOW (widget)->hscrollbar;
985
986   if (range && GTK_WIDGET_VISIBLE (range))
987     {
988       GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
989       gdouble new_value;
990
991       if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT)
992         new_value = adj->value - adj->page_increment / 2;
993       else
994         new_value = adj->value + adj->page_increment / 2;
995
996       new_value = CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
997       gtk_adjustment_set_value (adj, new_value);
998
999       return TRUE;
1000     }
1001
1002   return FALSE;
1003 }
1004
1005 static void
1006 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
1007                                         gpointer       data)
1008 {
1009   GtkScrolledWindow *scrolled_win;
1010
1011   g_return_if_fail (adjustment != NULL);
1012   g_return_if_fail (data != NULL);
1013
1014   scrolled_win = GTK_SCROLLED_WINDOW (data);
1015
1016   if (scrolled_win->hscrollbar &&
1017       adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->hscrollbar)))
1018     {
1019       if (scrolled_win->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1020         {
1021           gboolean visible;
1022           
1023           visible = scrolled_win->hscrollbar_visible;
1024           scrolled_win->hscrollbar_visible = (adjustment->upper - adjustment->lower >
1025                                               adjustment->page_size);
1026           if (scrolled_win->hscrollbar_visible != visible)
1027             gtk_widget_queue_resize (GTK_WIDGET (scrolled_win));
1028         }
1029     }
1030   else if (scrolled_win->vscrollbar &&
1031            adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->vscrollbar)))
1032     {
1033       if (scrolled_win->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1034         {
1035           gboolean visible;
1036
1037           visible = scrolled_win->vscrollbar_visible;
1038           scrolled_win->vscrollbar_visible = (adjustment->upper - adjustment->lower >
1039                                               adjustment->page_size);
1040           if (scrolled_win->vscrollbar_visible != visible)
1041             gtk_widget_queue_resize (GTK_WIDGET (scrolled_win));
1042         }
1043     }
1044 }
1045
1046 static void
1047 gtk_scrolled_window_add (GtkContainer *container,
1048                          GtkWidget    *child)
1049 {
1050   GtkScrolledWindow *scrolled_window;
1051   GtkBin *bin;
1052
1053   bin = GTK_BIN (container);
1054   g_return_if_fail (bin->child == NULL);
1055
1056   scrolled_window = GTK_SCROLLED_WINDOW (container);
1057
1058   bin->child = child;
1059   gtk_widget_set_parent (child, GTK_WIDGET (bin));
1060
1061   /* this is a temporary message */
1062   if (!gtk_widget_set_scroll_adjustments (child,
1063                                           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
1064                                           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar))))
1065     g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
1066                "use gtk_scrolled_window_add_with_viewport() instead");
1067 }
1068
1069 static void
1070 gtk_scrolled_window_remove (GtkContainer *container,
1071                             GtkWidget    *child)
1072 {
1073   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
1074   g_return_if_fail (child != NULL);
1075   g_return_if_fail (GTK_BIN (container)->child == child);
1076   
1077   gtk_widget_set_scroll_adjustments (child, NULL, NULL);
1078
1079   /* chain parent class handler to remove child */
1080   GTK_CONTAINER_CLASS (parent_class)->remove (container, child);
1081 }
1082
1083 void
1084 gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
1085                                        GtkWidget         *child)
1086 {
1087   GtkBin *bin;
1088   GtkWidget *viewport;
1089
1090   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1091   g_return_if_fail (GTK_IS_WIDGET (child));
1092   g_return_if_fail (child->parent == NULL);
1093
1094   bin = GTK_BIN (scrolled_window);
1095
1096   if (bin->child != NULL)
1097     {
1098       g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
1099       g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
1100
1101       viewport = bin->child;
1102     }
1103   else
1104     {
1105       viewport =
1106         gtk_viewport_new (gtk_scrolled_window_get_hadjustment (scrolled_window),
1107                           gtk_scrolled_window_get_vadjustment (scrolled_window));
1108       gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
1109     }
1110
1111   gtk_widget_show (viewport);
1112   gtk_container_add (GTK_CONTAINER (viewport), child);
1113 }