]> Pileus Git - ~andy/gtk/blob - gtk/gtkscrolledwindow.c
Implement "fuzzy" key binding lookups; allow matches on key and level but
[~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 <gdk/gdkkeysyms.h>
28 #include "gtkbindings.h"
29 #include "gtkmarshalers.h"
30 #include "gtkscrolledwindow.h"
31 #include "gtksignal.h"
32 #include "gtkintl.h"
33
34
35 /* scrolled window policy and size requisition handling:
36  *
37  * gtk size requisition works as follows:
38  *   a widget upon size-request reports the width and height that it finds
39  *   to be best suited to display its contents, including children.
40  *   the width and/or height reported from a widget upon size requisition
41  *   may be overidden by the user by specifying a width and/or height
42  *   other than 0 through gtk_widget_set_usize().
43  *
44  * a scrolled window needs (for imlementing all three policy types) to
45  * request its width and height based on two different rationales.
46  * 1)   the user wants the scrolled window to just fit into the space
47  *      that it gets allocated for a specifc dimension.
48  * 1.1) this does not apply if the user specified a concrete value
49  *      value for that specific dimension by either specifying usize for the
50  *      scrolled window or for its child.
51  * 2)   the user wants the scrolled window to take as much space up as
52  *      is desired by the child for a specifc dimension (i.e. POLICY_NEVER).
53  *
54  * also, kinda obvious:
55  * 3)   a user would certainly not have choosen a scrolled window as a container
56  *      for the child, if the resulting allocation takes up more space than the
57  *      child would have allocated without the scrolled window.
58  *
59  * conclusions:
60  * A) from 1) follows: the scrolled window shouldn't request more space for a
61  *    specifc dimension than is required at minimum.
62  * B) from 1.1) follows: the requisition may be overidden by usize of the scrolled
63  *    window (done automatically) or by usize of the child (needs to be checked).
64  * C) from 2) follows: for POLICY_NEVER, the scrolled window simply reports the
65  *    child's dimension.
66  * D) from 3) follows: the scrolled window child's minimum width and minimum height
67  *    under A) at least correspond to the space taken up by its scrollbars.
68  */
69
70 #define SCROLLBAR_SPACING(w) (GTK_SCROLLED_WINDOW_GET_CLASS (w)->scrollbar_spacing)
71
72 #define DEFAULT_SCROLLBAR_SPACING  3
73
74 enum {
75   PROP_0,
76   PROP_HADJUSTMENT,
77   PROP_VADJUSTMENT,
78   PROP_HSCROLLBAR_POLICY,
79   PROP_VSCROLLBAR_POLICY,
80   PROP_WINDOW_PLACEMENT,
81   PROP_SHADOW_TYPE,
82   PROP_LAST
83 };
84
85 /* Signals */
86 enum
87 {
88   SCROLL_CHILD,
89   MOVE_FOCUS_OUT,
90   LAST_SIGNAL
91 };
92
93 static void gtk_scrolled_window_class_init         (GtkScrolledWindowClass *klass);
94 static void gtk_scrolled_window_init               (GtkScrolledWindow      *scrolled_window);
95 static void gtk_scrolled_window_destroy            (GtkObject              *object);
96 static void gtk_scrolled_window_finalize           (GObject                *object);
97 static void gtk_scrolled_window_set_property       (GObject                *object,
98                                                     guint                   prop_id,
99                                                     const GValue           *value,
100                                                     GParamSpec             *pspec);
101 static void gtk_scrolled_window_get_property       (GObject                *object,
102                                                     guint                   prop_id,
103                                                     GValue                 *value,
104                                                     GParamSpec             *pspec);
105
106 static gint gtk_scrolled_window_expose             (GtkWidget              *widget,
107                                                     GdkEventExpose         *event);
108 static void gtk_scrolled_window_size_request       (GtkWidget              *widget,
109                                                     GtkRequisition         *requisition);
110 static void gtk_scrolled_window_size_allocate      (GtkWidget              *widget,
111                                                     GtkAllocation          *allocation);
112 static gint gtk_scrolled_window_scroll_event       (GtkWidget              *widget,
113                                                     GdkEventScroll         *event);
114 static gint gtk_scrolled_window_focus              (GtkWidget              *widget,
115                                                     GtkDirectionType        direction);
116 static void gtk_scrolled_window_add                (GtkContainer           *container,
117                                                     GtkWidget              *widget);
118 static void gtk_scrolled_window_remove             (GtkContainer           *container,
119                                                     GtkWidget              *widget);
120 static void gtk_scrolled_window_forall             (GtkContainer           *container,
121                                                     gboolean                include_internals,
122                                                     GtkCallback             callback,
123                                                     gpointer                callback_data);
124 static void gtk_scrolled_window_scroll_child       (GtkScrolledWindow      *scrolled_window,
125                                                     GtkScrollType           scroll,
126                                                     gboolean                horizontal);
127 static void gtk_scrolled_window_move_focus_out     (GtkScrolledWindow      *scrolled_window,
128                                                     GtkDirectionType        direction_type);
129
130 static void gtk_scrolled_window_relative_allocation(GtkWidget              *widget,
131                                                     GtkAllocation          *allocation);
132 static void gtk_scrolled_window_adjustment_changed (GtkAdjustment          *adjustment,
133                                                     gpointer                data);
134
135 static GtkContainerClass *parent_class = NULL;
136
137 static guint signals[LAST_SIGNAL] = {0};
138
139 GtkType
140 gtk_scrolled_window_get_type (void)
141 {
142   static GtkType scrolled_window_type = 0;
143
144   if (!scrolled_window_type)
145     {
146       static const GtkTypeInfo scrolled_window_info =
147       {
148         "GtkScrolledWindow",
149         sizeof (GtkScrolledWindow),
150         sizeof (GtkScrolledWindowClass),
151         (GtkClassInitFunc) gtk_scrolled_window_class_init,
152         (GtkObjectInitFunc) gtk_scrolled_window_init,
153         /* reserved_1 */ NULL,
154         /* reserved_2 */ NULL,
155         (GtkClassInitFunc) NULL,
156       };
157
158       scrolled_window_type = gtk_type_unique (GTK_TYPE_BIN, &scrolled_window_info);
159     }
160
161   return scrolled_window_type;
162 }
163
164 static void
165 add_scroll_binding (GtkBindingSet  *binding_set,
166                     guint           keyval,
167                     GdkModifierType mask,
168                     GtkScrollType   scroll,
169                     gboolean        horizontal)
170 {
171   guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
172   
173   gtk_binding_entry_add_signal (binding_set, keyval, mask,
174                                 "scroll_child", 2,
175                                 GTK_TYPE_SCROLL_TYPE, scroll,
176                                 G_TYPE_BOOLEAN, horizontal);
177   gtk_binding_entry_add_signal (binding_set, keypad_keyval, mask,
178                                 "scroll_child", 2,
179                                 GTK_TYPE_SCROLL_TYPE, scroll,
180                                 G_TYPE_BOOLEAN, horizontal);
181 }
182
183 static void
184 add_tab_bindings (GtkBindingSet    *binding_set,
185                   GdkModifierType   modifiers,
186                   GtkDirectionType  direction)
187 {
188   gtk_binding_entry_add_signal (binding_set, GDK_Tab, modifiers,
189                                 "move_focus_out", 1,
190                                 GTK_TYPE_DIRECTION_TYPE, direction);
191   gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, modifiers,
192                                 "move_focus_out", 1,
193                                 GTK_TYPE_DIRECTION_TYPE, direction);
194 }
195
196 static void
197 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
198 {
199   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
200   GtkObjectClass *object_class;
201   GtkWidgetClass *widget_class;
202   GtkContainerClass *container_class;
203   GtkBindingSet *binding_set;
204
205   object_class = (GtkObjectClass*) class;
206   widget_class = (GtkWidgetClass*) class;
207   container_class = (GtkContainerClass*) class;
208   parent_class = gtk_type_class (GTK_TYPE_BIN);
209
210   gobject_class->finalize = gtk_scrolled_window_finalize;
211   gobject_class->set_property = gtk_scrolled_window_set_property;
212   gobject_class->get_property = gtk_scrolled_window_get_property;
213
214   object_class->destroy = gtk_scrolled_window_destroy;
215
216   widget_class->expose_event = gtk_scrolled_window_expose;
217   widget_class->size_request = gtk_scrolled_window_size_request;
218   widget_class->size_allocate = gtk_scrolled_window_size_allocate;
219   widget_class->scroll_event = gtk_scrolled_window_scroll_event;
220   widget_class->focus = gtk_scrolled_window_focus;
221
222   container_class->add = gtk_scrolled_window_add;
223   container_class->remove = gtk_scrolled_window_remove;
224   container_class->forall = gtk_scrolled_window_forall;
225
226   class->scrollbar_spacing = DEFAULT_SCROLLBAR_SPACING;
227
228   class->scroll_child = gtk_scrolled_window_scroll_child;
229   class->move_focus_out = gtk_scrolled_window_move_focus_out;
230   
231   g_object_class_install_property (gobject_class,
232                                    PROP_HADJUSTMENT,
233                                    g_param_spec_object ("hadjustment",
234                                                         _("Horizontal Adjustment"),
235                                                         _("The GtkAdjustment for the horizontal position."),
236                                                         GTK_TYPE_ADJUSTMENT,
237                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
238   g_object_class_install_property (gobject_class,
239                                    PROP_VADJUSTMENT,
240                                    g_param_spec_object ("vadjustment",
241                                                         _("Vertical Adjustment"),
242                                                         _("The GtkAdjustment for the vertical position."),
243                                                         GTK_TYPE_ADJUSTMENT,
244                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
245   g_object_class_install_property (gobject_class,
246                                    PROP_HSCROLLBAR_POLICY,
247                                    g_param_spec_enum ("hscrollbar_policy",
248                                                       _("Horizontal Scrollbar Policy"),
249                                                       _("When the horizontal scrollbar is displayed"),
250                                                       GTK_TYPE_POLICY_TYPE,
251                                                       GTK_POLICY_ALWAYS,
252                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
253   g_object_class_install_property (gobject_class,
254                                    PROP_VSCROLLBAR_POLICY,
255                                    g_param_spec_enum ("vscrollbar_policy",
256                                                       _("Vertical Scrollbar Policy"),
257                                                       _("When the vertical scrollbar is displayed"),
258                                                       GTK_TYPE_POLICY_TYPE,
259                                                       GTK_POLICY_ALWAYS,
260                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
261
262   g_object_class_install_property (gobject_class,
263                                    PROP_WINDOW_PLACEMENT,
264                                    g_param_spec_enum ("window_placement",
265                                                       _("Window Placement"),
266                                                       _("Where the contents are located with respect to the scrollbars"),
267                                                       GTK_TYPE_CORNER_TYPE,
268                                                       GTK_CORNER_TOP_LEFT,
269                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
270   g_object_class_install_property (gobject_class,
271                                    PROP_SHADOW_TYPE,
272                                    g_param_spec_enum ("shadow_type",
273                                                       _("Shadow Type"),
274                                                       _("Style of bevel around the contents"),
275                                                       GTK_TYPE_SHADOW_TYPE,
276                                                       GTK_SHADOW_NONE,
277                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
278
279   signals[SCROLL_CHILD] =
280     g_signal_new ("scroll_child",
281                   G_TYPE_FROM_CLASS (object_class),
282                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
283                   G_STRUCT_OFFSET (GtkScrolledWindowClass, scroll_child),
284                   NULL, NULL,
285                   _gtk_marshal_VOID__ENUM_BOOLEAN,
286                   G_TYPE_NONE, 2,
287                   GTK_TYPE_SCROLL_TYPE,
288                   G_TYPE_BOOLEAN);
289   signals[MOVE_FOCUS_OUT] =
290     g_signal_new ("move_focus_out",
291                   G_TYPE_FROM_CLASS (object_class),
292                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
293                   G_STRUCT_OFFSET (GtkScrolledWindowClass, move_focus_out),
294                   NULL, NULL,
295                   _gtk_marshal_VOID__ENUM,
296                   G_TYPE_NONE, 1,
297                   GTK_TYPE_DIRECTION_TYPE);
298   
299   binding_set = gtk_binding_set_by_class (class);
300
301   add_scroll_binding (binding_set, GDK_Left,  GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, TRUE);
302   add_scroll_binding (binding_set, GDK_Right, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD,  TRUE);
303   add_scroll_binding (binding_set, GDK_Up,    GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, FALSE);
304   add_scroll_binding (binding_set, GDK_Down,  GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD,  FALSE);
305
306   add_scroll_binding (binding_set, GDK_Page_Up,   GDK_CONTROL_MASK, GTK_SCROLL_PAGE_BACKWARD, TRUE);
307   add_scroll_binding (binding_set, GDK_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_FORWARD,  TRUE);
308   add_scroll_binding (binding_set, GDK_Page_Up,   0,                GTK_SCROLL_PAGE_BACKWARD, FALSE);
309   add_scroll_binding (binding_set, GDK_Page_Down, 0,                GTK_SCROLL_PAGE_FORWARD,  FALSE);
310
311   add_scroll_binding (binding_set, GDK_Page_Up,   GDK_CONTROL_MASK, GTK_SCROLL_PAGE_BACKWARD, TRUE);
312   add_scroll_binding (binding_set, GDK_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_FORWARD,  TRUE);
313   add_scroll_binding (binding_set, GDK_Page_Up,   0,                GTK_SCROLL_PAGE_BACKWARD, FALSE);
314   add_scroll_binding (binding_set, GDK_Page_Down, 0,                GTK_SCROLL_PAGE_FORWARD,  FALSE);
315
316   add_scroll_binding (binding_set, GDK_Home, 0,                GTK_SCROLL_START, TRUE);
317   add_scroll_binding (binding_set, GDK_End,  0,                GTK_SCROLL_END,   TRUE);
318   add_scroll_binding (binding_set, GDK_Home, GDK_CONTROL_MASK, GTK_SCROLL_START, FALSE);
319   add_scroll_binding (binding_set, GDK_End,  GDK_CONTROL_MASK, GTK_SCROLL_END,   FALSE);
320
321   add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
322   add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
323 }
324
325 static void
326 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
327 {
328   GTK_WIDGET_SET_FLAGS (scrolled_window, GTK_NO_WINDOW | GTK_CAN_FOCUS);
329
330   gtk_container_set_resize_mode (GTK_CONTAINER (scrolled_window), GTK_RESIZE_QUEUE);
331
332   scrolled_window->hscrollbar = NULL;
333   scrolled_window->vscrollbar = NULL;
334   scrolled_window->hscrollbar_policy = GTK_POLICY_ALWAYS;
335   scrolled_window->vscrollbar_policy = GTK_POLICY_ALWAYS;
336   scrolled_window->hscrollbar_visible = FALSE;
337   scrolled_window->vscrollbar_visible = FALSE;
338   scrolled_window->focus_out = FALSE;
339   scrolled_window->window_placement = GTK_CORNER_TOP_LEFT;
340   
341 }
342
343 GtkWidget*
344 gtk_scrolled_window_new (GtkAdjustment *hadjustment,
345                          GtkAdjustment *vadjustment)
346 {
347   GtkWidget *scrolled_window;
348
349   if (hadjustment)
350     g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
351
352   if (vadjustment)
353     g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
354
355   scrolled_window = gtk_widget_new (GTK_TYPE_SCROLLED_WINDOW,
356                                     "hadjustment", hadjustment,
357                                     "vadjustment", vadjustment,
358                                     NULL);
359
360   return scrolled_window;
361 }
362
363 void
364 gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
365                                      GtkAdjustment     *hadjustment)
366 {
367   GtkBin *bin;
368
369   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
370   if (hadjustment)
371     g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
372   else
373     hadjustment = (GtkAdjustment*) gtk_object_new (GTK_TYPE_ADJUSTMENT, NULL);
374
375   bin = GTK_BIN (scrolled_window);
376
377   if (!scrolled_window->hscrollbar)
378     {
379       gtk_widget_push_composite_child ();
380       scrolled_window->hscrollbar = gtk_hscrollbar_new (hadjustment);
381       gtk_widget_set_composite_name (scrolled_window->hscrollbar, "hscrollbar");
382       gtk_widget_pop_composite_child ();
383
384       gtk_widget_set_parent (scrolled_window->hscrollbar, GTK_WIDGET (scrolled_window));
385       gtk_widget_ref (scrolled_window->hscrollbar);
386       gtk_widget_show (scrolled_window->hscrollbar);
387     }
388   else
389     {
390       GtkAdjustment *old_adjustment;
391       
392       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
393       if (old_adjustment == hadjustment)
394         return;
395
396       gtk_signal_disconnect_by_func (GTK_OBJECT (old_adjustment),
397                                      GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
398                                      scrolled_window);
399       gtk_range_set_adjustment (GTK_RANGE (scrolled_window->hscrollbar),
400                                 hadjustment);
401     }
402   hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
403   gtk_signal_connect (GTK_OBJECT (hadjustment),
404                       "changed",
405                       GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
406                       scrolled_window);
407   gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
408   
409   if (bin->child)
410     gtk_widget_set_scroll_adjustments (bin->child,
411                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
412                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
413
414   g_object_notify (G_OBJECT (scrolled_window), "hadjustment");
415 }
416
417 void
418 gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
419                                      GtkAdjustment     *vadjustment)
420 {
421   GtkBin *bin;
422
423   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
424   if (vadjustment)
425     g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
426   else
427     vadjustment = (GtkAdjustment*) gtk_object_new (GTK_TYPE_ADJUSTMENT, NULL);
428
429   bin = GTK_BIN (scrolled_window);
430
431   if (!scrolled_window->vscrollbar)
432     {
433       gtk_widget_push_composite_child ();
434       scrolled_window->vscrollbar = gtk_vscrollbar_new (vadjustment);
435       gtk_widget_set_composite_name (scrolled_window->vscrollbar, "vscrollbar");
436       gtk_widget_pop_composite_child ();
437
438       gtk_widget_set_parent (scrolled_window->vscrollbar, GTK_WIDGET (scrolled_window));
439       gtk_widget_ref (scrolled_window->vscrollbar);
440       gtk_widget_show (scrolled_window->vscrollbar);
441     }
442   else
443     {
444       GtkAdjustment *old_adjustment;
445       
446       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
447       if (old_adjustment == vadjustment)
448         return;
449
450       gtk_signal_disconnect_by_func (GTK_OBJECT (old_adjustment),
451                                      GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
452                                      scrolled_window);
453       gtk_range_set_adjustment (GTK_RANGE (scrolled_window->vscrollbar),
454                                 vadjustment);
455     }
456   vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
457   gtk_signal_connect (GTK_OBJECT (vadjustment),
458                       "changed",
459                       GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
460                       scrolled_window);
461   gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
462
463   if (bin->child)
464     gtk_widget_set_scroll_adjustments (bin->child,
465                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
466                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
467
468   g_object_notify (G_OBJECT (scrolled_window), "vadjustment");
469 }
470
471 GtkAdjustment*
472 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
473 {
474   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
475
476   return (scrolled_window->hscrollbar ?
477           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)) :
478           NULL);
479 }
480
481 GtkAdjustment*
482 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
483 {
484   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
485
486   return (scrolled_window->vscrollbar ?
487           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)) :
488           NULL);
489 }
490
491 void
492 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
493                                 GtkPolicyType      hscrollbar_policy,
494                                 GtkPolicyType      vscrollbar_policy)
495 {
496   GObject *object = G_OBJECT (scrolled_window);
497   
498   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
499
500   if ((scrolled_window->hscrollbar_policy != hscrollbar_policy) ||
501       (scrolled_window->vscrollbar_policy != vscrollbar_policy))
502     {
503       scrolled_window->hscrollbar_policy = hscrollbar_policy;
504       scrolled_window->vscrollbar_policy = vscrollbar_policy;
505
506       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
507
508       g_object_freeze_notify (object);
509       g_object_notify (object, "hscrollbar_policy");
510       g_object_notify (object, "vscrollbar_policy");
511       g_object_thaw_notify (object);
512     }
513 }
514
515 /**
516  * gtk_scrolled_window_get_policy:
517  * @scrolled_window: a #GtkScrolledWindow
518  * @hscrollbar_policy: location to store the policy for the horizontal scrollbar, or %NULL.
519  * @vscrollbar_policy: location to store the policy for the horizontal scrollbar, or %NULL.
520  * 
521  * Retrieves the current policy values for the horizontal and vertical
522  * scrollbars. See gtk_scrolled_window_set_policy().
523  **/
524 void
525 gtk_scrolled_window_get_policy (GtkScrolledWindow *scrolled_window,
526                                 GtkPolicyType     *hscrollbar_policy,
527                                 GtkPolicyType     *vscrollbar_policy)
528 {
529   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
530
531   if (hscrollbar_policy)
532     *hscrollbar_policy = scrolled_window->hscrollbar_policy;
533   if (vscrollbar_policy)
534     *vscrollbar_policy = scrolled_window->vscrollbar_policy;
535 }
536
537 void
538 gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
539                                    GtkCornerType      window_placement)
540 {
541   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
542
543   if (scrolled_window->window_placement != window_placement)
544     {
545       scrolled_window->window_placement = window_placement;
546
547       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
548       
549       g_object_notify (G_OBJECT (scrolled_window), "window_placement");
550     }
551 }
552
553 /**
554  * gtk_scrolled_window_get_placement:
555  * @scrolled_window: a #GtkScrolledWindow
556  *
557  * Gets the placement of the scrollbars for the scrolled window. See 
558  * gtk_scrolled_window_set_placement().
559  *
560  * Return value: the current placement value.
561  **/
562 GtkCornerType
563 gtk_scrolled_window_get_placement (GtkScrolledWindow *scrolled_window)
564 {
565   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_CORNER_TOP_LEFT);
566
567   return scrolled_window->window_placement;
568 }
569
570 /**
571  * gtk_scrolled_window_set_shadow_type:
572  * @scrolled_window: a #GtkScrolledWindow
573  * @type: kind of shadow to draw around scrolled window contents
574  *
575  * Changes the type of shadow drawn around the contents of
576  * @scrolled_window.
577  * 
578  **/
579 void
580 gtk_scrolled_window_set_shadow_type (GtkScrolledWindow *scrolled_window,
581                                      GtkShadowType      type)
582 {
583   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
584   g_return_if_fail (type >= GTK_SHADOW_NONE && type <= GTK_SHADOW_ETCHED_OUT);
585   
586   if (scrolled_window->shadow_type != type)
587     {
588       scrolled_window->shadow_type = type;
589
590       if (GTK_WIDGET_DRAWABLE (scrolled_window))
591         gtk_widget_queue_clear (GTK_WIDGET (scrolled_window));
592
593       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
594
595       g_object_notify (G_OBJECT (scrolled_window), "shadow_type");
596     }
597 }
598
599 /**
600  * gtk_scrolled_window_get_shadow_type:
601  * @scrolled_window: a #GtkScrolledWindow
602  *
603  * Gets the shadow type of the scrolled window. See 
604  * gtk_scrolled_window_set_shadow_type().
605  *
606  * Return value: the current shadow type
607  **/
608 GtkShadowType
609 gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolled_window)
610 {
611   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_NONE);
612
613   return scrolled_window->shadow_type;
614 }
615
616 static void
617 gtk_scrolled_window_destroy (GtkObject *object)
618 {
619   GtkScrolledWindow *scrolled_window;
620
621   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (object));
622
623   scrolled_window = GTK_SCROLLED_WINDOW (object);
624
625   gtk_widget_unparent (scrolled_window->hscrollbar);
626   gtk_widget_unparent (scrolled_window->vscrollbar);
627   gtk_widget_destroy (scrolled_window->hscrollbar);
628   gtk_widget_destroy (scrolled_window->vscrollbar);
629
630   GTK_OBJECT_CLASS (parent_class)->destroy (object);
631 }
632
633 static void
634 gtk_scrolled_window_finalize (GObject *object)
635 {
636   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
637
638   gtk_widget_unref (scrolled_window->hscrollbar);
639   gtk_widget_unref (scrolled_window->vscrollbar);
640
641   G_OBJECT_CLASS (parent_class)->finalize (object);
642 }
643
644 static void
645 gtk_scrolled_window_set_property (GObject      *object,
646                                   guint         prop_id,
647                                   const GValue *value,
648                                   GParamSpec   *pspec)
649 {
650   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
651   
652   switch (prop_id)
653     {
654     case PROP_HADJUSTMENT:
655       gtk_scrolled_window_set_hadjustment (scrolled_window,
656                                            g_value_get_object (value));
657       break;
658     case PROP_VADJUSTMENT:
659       gtk_scrolled_window_set_vadjustment (scrolled_window,
660                                            g_value_get_object (value));
661       break;
662     case PROP_HSCROLLBAR_POLICY:
663       gtk_scrolled_window_set_policy (scrolled_window,
664                                       g_value_get_enum (value),
665                                       scrolled_window->vscrollbar_policy);
666       break;
667     case PROP_VSCROLLBAR_POLICY:
668       gtk_scrolled_window_set_policy (scrolled_window,
669                                       scrolled_window->hscrollbar_policy,
670                                       g_value_get_enum (value));
671       break;
672     case PROP_WINDOW_PLACEMENT:
673       gtk_scrolled_window_set_placement (scrolled_window,
674                                          g_value_get_enum (value));
675       break;
676     case PROP_SHADOW_TYPE:
677       gtk_scrolled_window_set_shadow_type (scrolled_window,
678                                            g_value_get_enum (value));
679       break;
680     default:
681       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
682       break;
683     }
684 }
685
686 static void
687 gtk_scrolled_window_get_property (GObject    *object,
688                                   guint       prop_id,
689                                   GValue     *value,
690                                   GParamSpec *pspec)
691 {
692   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
693   
694   switch (prop_id)
695     {
696     case PROP_HADJUSTMENT:
697       g_value_set_object (value,
698                           G_OBJECT (gtk_scrolled_window_get_hadjustment (scrolled_window)));
699       break;
700     case PROP_VADJUSTMENT:
701       g_value_set_object (value,
702                           G_OBJECT (gtk_scrolled_window_get_vadjustment (scrolled_window)));
703       break;
704     case PROP_HSCROLLBAR_POLICY:
705       g_value_set_enum (value, scrolled_window->hscrollbar_policy);
706       break;
707     case PROP_VSCROLLBAR_POLICY:
708       g_value_set_enum (value, scrolled_window->vscrollbar_policy);
709       break;
710     case PROP_WINDOW_PLACEMENT:
711       g_value_set_enum (value, scrolled_window->window_placement);
712       break;
713     case PROP_SHADOW_TYPE:
714       g_value_set_enum (value, scrolled_window->shadow_type);
715       break;
716     default:
717       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
718       break;
719     }
720 }
721
722 static void
723 gtk_scrolled_window_paint (GtkWidget    *widget,
724                            GdkRectangle *area)
725 {
726   GtkAllocation relative_allocation;
727   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
728
729   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
730     {
731       gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
732       
733       relative_allocation.x -= widget->style->xthickness;
734       relative_allocation.y -= widget->style->ythickness;
735       relative_allocation.width += 2 * widget->style->xthickness;
736       relative_allocation.height += 2 * widget->style->ythickness;
737       
738       gtk_paint_shadow (widget->style, widget->window,
739                         GTK_STATE_NORMAL, scrolled_window->shadow_type,
740                         area, widget, "scrolled_window",
741                         widget->allocation.x + relative_allocation.x,
742                         widget->allocation.y + relative_allocation.y,
743                         relative_allocation.width,
744                         relative_allocation.height);
745     }
746 }
747
748 static gint
749 gtk_scrolled_window_expose (GtkWidget      *widget,
750                             GdkEventExpose *event)
751 {
752   if (GTK_WIDGET_DRAWABLE (widget))
753     {
754       gtk_scrolled_window_paint (widget, &event->area);
755
756       (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
757     }
758
759   return FALSE;
760 }
761
762 static void
763 gtk_scrolled_window_forall (GtkContainer *container,
764                             gboolean      include_internals,
765                             GtkCallback   callback,
766                             gpointer      callback_data)
767 {
768   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
769   g_return_if_fail (callback != NULL);
770
771   GTK_CONTAINER_CLASS (parent_class)->forall (container,
772                                               include_internals,
773                                               callback,
774                                               callback_data);
775   if (include_internals)
776     {
777       GtkScrolledWindow *scrolled_window;
778
779       scrolled_window = GTK_SCROLLED_WINDOW (container);
780       
781       if (scrolled_window->vscrollbar)
782         callback (scrolled_window->vscrollbar, callback_data);
783       if (scrolled_window->hscrollbar)
784         callback (scrolled_window->hscrollbar, callback_data);
785     }
786 }
787
788 static void
789 gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
790                                   GtkScrollType      scroll,
791                                   gboolean           horizontal)
792 {
793   GtkAdjustment *adjustment = NULL;
794   
795   switch (scroll)
796     {
797     case GTK_SCROLL_STEP_UP:
798       scroll = GTK_SCROLL_STEP_BACKWARD;
799       horizontal = FALSE;
800       break;
801     case GTK_SCROLL_STEP_DOWN:
802       scroll = GTK_SCROLL_STEP_FORWARD;
803       horizontal = FALSE;
804       break;
805     case GTK_SCROLL_STEP_LEFT:
806       scroll = GTK_SCROLL_STEP_BACKWARD;
807       horizontal = TRUE;
808       break;
809     case GTK_SCROLL_STEP_RIGHT:
810       scroll = GTK_SCROLL_STEP_FORWARD;
811       horizontal = TRUE;
812       break;
813     case GTK_SCROLL_PAGE_UP:
814       scroll = GTK_SCROLL_PAGE_BACKWARD;
815       horizontal = FALSE;
816       break;
817     case GTK_SCROLL_PAGE_DOWN:
818       scroll = GTK_SCROLL_PAGE_FORWARD;
819       horizontal = FALSE;
820       break;
821     case GTK_SCROLL_PAGE_LEFT:
822       scroll = GTK_SCROLL_STEP_BACKWARD;
823       horizontal = TRUE;
824       break;
825     case GTK_SCROLL_PAGE_RIGHT:
826       scroll = GTK_SCROLL_STEP_FORWARD;
827       horizontal = TRUE;
828       break;
829     case GTK_SCROLL_STEP_BACKWARD:
830     case GTK_SCROLL_STEP_FORWARD:
831     case GTK_SCROLL_PAGE_BACKWARD:
832     case GTK_SCROLL_PAGE_FORWARD:
833     case GTK_SCROLL_START:
834     case GTK_SCROLL_END:
835       break;
836     default:
837       g_warning ("Invalid scroll type %d for GtkSpinButton::change-value", scroll);
838       return;
839     }
840
841   if (horizontal)
842     {
843       if (scrolled_window->hscrollbar)
844         adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
845     }
846   else
847     {
848       if (scrolled_window->vscrollbar)
849         adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
850     }
851
852   if (adjustment)
853     {
854       gdouble value = adjustment->value;
855       
856       switch (scroll)
857         {
858         case GTK_SCROLL_STEP_FORWARD:
859           value += adjustment->step_increment;
860           break;
861         case GTK_SCROLL_STEP_BACKWARD:
862           value -= adjustment->step_increment;
863           break;
864         case GTK_SCROLL_PAGE_FORWARD:
865           value += adjustment->page_increment;
866           break;
867         case GTK_SCROLL_PAGE_BACKWARD:
868           value -= adjustment->page_increment;
869           break;
870         case GTK_SCROLL_START:
871           value = adjustment->lower;
872           break;
873         case GTK_SCROLL_END:
874           value = adjustment->upper;
875           break;
876         default:
877           g_assert_not_reached ();
878           break;
879         }
880
881       value = CLAMP (value, adjustment->lower, adjustment->upper - adjustment->page_size);
882       
883       gtk_adjustment_set_value (adjustment, value);
884     }
885 }
886
887 static void
888 gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
889                                     GtkDirectionType   direction_type)
890 {
891   GtkWidget *toplevel;
892   
893   /* Focus out of the scrolled window entirely. We do this by setting
894    * a flag, then propagating the focus motion to the notebook.
895    */
896   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (scrolled_window));
897   if (!GTK_WIDGET_TOPLEVEL (toplevel))
898     return;
899
900   g_object_ref (scrolled_window);
901   
902   scrolled_window->focus_out = TRUE;
903   g_signal_emit_by_name (G_OBJECT (toplevel), "move_focus", direction_type);
904   scrolled_window->focus_out = FALSE;
905   
906   g_object_unref (scrolled_window);
907 }
908
909 static void
910 gtk_scrolled_window_size_request (GtkWidget      *widget,
911                                   GtkRequisition *requisition)
912 {
913   GtkScrolledWindow *scrolled_window;
914   GtkBin *bin;
915   gint extra_width;
916   gint extra_height;
917   GtkRequisition hscrollbar_requisition;
918   GtkRequisition vscrollbar_requisition;
919   GtkRequisition child_requisition;
920
921   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
922   g_return_if_fail (requisition != NULL);
923
924   scrolled_window = GTK_SCROLLED_WINDOW (widget);
925   bin = GTK_BIN (scrolled_window);
926
927   extra_width = 0;
928   extra_height = 0;
929   requisition->width = 0;
930   requisition->height = 0;
931   
932   gtk_widget_size_request (scrolled_window->hscrollbar,
933                            &hscrollbar_requisition);
934   gtk_widget_size_request (scrolled_window->vscrollbar,
935                            &vscrollbar_requisition);
936   
937   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
938     {
939       gtk_widget_size_request (bin->child, &child_requisition);
940
941       if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
942         requisition->width += child_requisition.width;
943       else
944         {
945           GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (bin->child, FALSE);
946
947           if (aux_info && aux_info->width > 0)
948             {
949               requisition->width += aux_info->width;
950               extra_width = -1;
951             }
952           else
953             requisition->width += vscrollbar_requisition.width;
954         }
955
956       if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
957         requisition->height += child_requisition.height;
958       else
959         {
960           GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (bin->child, FALSE);
961
962           if (aux_info && aux_info->height > 0)
963             {
964               requisition->height += aux_info->height;
965               extra_height = -1;
966             }
967           else
968             requisition->height += hscrollbar_requisition.height;
969         }
970     }
971
972   if (scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
973       scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
974     {
975       requisition->width = MAX (requisition->width, hscrollbar_requisition.width);
976       if (!extra_height || scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
977         extra_height = SCROLLBAR_SPACING (scrolled_window) + hscrollbar_requisition.height;
978     }
979
980   if (scrolled_window->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
981       scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
982     {
983       requisition->height = MAX (requisition->height, vscrollbar_requisition.height);
984       if (!extra_height || scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
985         extra_width = SCROLLBAR_SPACING (scrolled_window) + vscrollbar_requisition.width;
986     }
987
988   requisition->width += GTK_CONTAINER (widget)->border_width * 2 + MAX (0, extra_width);
989   requisition->height += GTK_CONTAINER (widget)->border_width * 2 + MAX (0, extra_height);
990
991   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
992     {
993       requisition->width += 2 * widget->style->xthickness;
994       requisition->height += 2 * widget->style->ythickness;
995     }
996 }
997
998 static void
999 gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
1000                                          GtkAllocation *allocation)
1001 {
1002   GtkScrolledWindow *scrolled_window;
1003
1004   g_return_if_fail (widget != NULL);
1005   g_return_if_fail (allocation != NULL);
1006
1007   scrolled_window = GTK_SCROLLED_WINDOW (widget);
1008
1009   allocation->x = GTK_CONTAINER (widget)->border_width;
1010   allocation->y = GTK_CONTAINER (widget)->border_width;
1011
1012   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
1013     {
1014       allocation->x += widget->style->xthickness;
1015       allocation->y += widget->style->ythickness;
1016     }
1017   
1018   allocation->width = MAX (1, (gint)widget->allocation.width - allocation->x * 2);
1019   allocation->height = MAX (1, (gint)widget->allocation.height - allocation->y * 2);
1020
1021   if (scrolled_window->vscrollbar_visible)
1022     {
1023       GtkRequisition vscrollbar_requisition;
1024       gtk_widget_get_child_requisition (scrolled_window->vscrollbar,
1025                                         &vscrollbar_requisition);
1026   
1027       if (scrolled_window->window_placement == GTK_CORNER_TOP_RIGHT ||
1028           scrolled_window->window_placement == GTK_CORNER_BOTTOM_RIGHT)
1029         allocation->x += (vscrollbar_requisition.width +
1030                           SCROLLBAR_SPACING (scrolled_window));
1031
1032       allocation->width = MAX (1, (gint)allocation->width -
1033                                ((gint)vscrollbar_requisition.width +
1034                                 (gint)SCROLLBAR_SPACING (scrolled_window)));
1035     }
1036   if (scrolled_window->hscrollbar_visible)
1037     {
1038       GtkRequisition hscrollbar_requisition;
1039       gtk_widget_get_child_requisition (scrolled_window->hscrollbar,
1040                                         &hscrollbar_requisition);
1041   
1042       if (scrolled_window->window_placement == GTK_CORNER_BOTTOM_LEFT ||
1043           scrolled_window->window_placement == GTK_CORNER_BOTTOM_RIGHT)
1044         allocation->y += (hscrollbar_requisition.height +
1045                           SCROLLBAR_SPACING (scrolled_window));
1046
1047       allocation->height = MAX (1, (gint)allocation->height -
1048                                 ((gint)hscrollbar_requisition.height +
1049                                  (gint)SCROLLBAR_SPACING (scrolled_window)));
1050     }
1051 }
1052
1053 static void
1054 gtk_scrolled_window_size_allocate (GtkWidget     *widget,
1055                                    GtkAllocation *allocation)
1056 {
1057   GtkScrolledWindow *scrolled_window;
1058   GtkBin *bin;
1059   GtkAllocation relative_allocation;
1060   GtkAllocation child_allocation;
1061   
1062   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
1063   g_return_if_fail (allocation != NULL);
1064
1065   scrolled_window = GTK_SCROLLED_WINDOW (widget);
1066   bin = GTK_BIN (scrolled_window);
1067
1068   widget->allocation = *allocation;
1069
1070   if (scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
1071     scrolled_window->hscrollbar_visible = TRUE;
1072   else if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
1073     scrolled_window->hscrollbar_visible = FALSE;
1074   if (scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
1075     scrolled_window->vscrollbar_visible = TRUE;
1076   else if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
1077     scrolled_window->vscrollbar_visible = FALSE;
1078
1079   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
1080     {
1081       gboolean previous_hvis;
1082       gboolean previous_vvis;
1083       guint count = 0;
1084       
1085       do
1086         {
1087           gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1088           
1089           child_allocation.x = relative_allocation.x + allocation->x;
1090           child_allocation.y = relative_allocation.y + allocation->y;
1091           child_allocation.width = relative_allocation.width;
1092           child_allocation.height = relative_allocation.height;
1093           
1094           previous_hvis = scrolled_window->hscrollbar_visible;
1095           previous_vvis = scrolled_window->vscrollbar_visible;
1096           
1097           gtk_widget_size_allocate (bin->child, &child_allocation);
1098
1099           /* If, after the first iteration, the hscrollbar and the
1100            * vscrollbar flip visiblity, then we need both.
1101            */
1102           if (count &&
1103               previous_hvis != scrolled_window->hscrollbar_visible &&
1104               previous_vvis != scrolled_window->vscrollbar_visible)
1105             {
1106               scrolled_window->hscrollbar_visible = TRUE;
1107               scrolled_window->vscrollbar_visible = TRUE;
1108
1109               /* a new resize is already queued at this point,
1110                * so we will immediatedly get reinvoked
1111                */
1112               return;
1113             }
1114           
1115           count++;
1116         }
1117       while (previous_hvis != scrolled_window->hscrollbar_visible ||
1118              previous_vvis != scrolled_window->vscrollbar_visible);
1119     }
1120   else
1121     gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1122   
1123   if (scrolled_window->hscrollbar_visible)
1124     {
1125       GtkRequisition hscrollbar_requisition;
1126       gtk_widget_get_child_requisition (scrolled_window->hscrollbar,
1127                                         &hscrollbar_requisition);
1128   
1129       if (!GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
1130         gtk_widget_show (scrolled_window->hscrollbar);
1131
1132       child_allocation.x = relative_allocation.x;
1133       if (scrolled_window->window_placement == GTK_CORNER_TOP_LEFT ||
1134           scrolled_window->window_placement == GTK_CORNER_TOP_RIGHT)
1135         child_allocation.y = (relative_allocation.y +
1136                               relative_allocation.height +
1137                               SCROLLBAR_SPACING (scrolled_window) +
1138                               (scrolled_window->shadow_type == GTK_SHADOW_NONE ?
1139                                0 : widget->style->ythickness));
1140       else
1141         child_allocation.y = GTK_CONTAINER (scrolled_window)->border_width;
1142
1143       child_allocation.width = relative_allocation.width;
1144       child_allocation.height = hscrollbar_requisition.height;
1145       child_allocation.x += allocation->x;
1146       child_allocation.y += allocation->y;
1147
1148       if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
1149         {
1150           child_allocation.x -= widget->style->xthickness;
1151           child_allocation.width += 2 * widget->style->xthickness;
1152         }
1153
1154       gtk_widget_size_allocate (scrolled_window->hscrollbar, &child_allocation);
1155     }
1156   else if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
1157     gtk_widget_hide (scrolled_window->hscrollbar);
1158
1159   if (scrolled_window->vscrollbar_visible)
1160     {
1161       GtkRequisition vscrollbar_requisition;
1162       if (!GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
1163         gtk_widget_show (scrolled_window->vscrollbar);
1164
1165       gtk_widget_get_child_requisition (scrolled_window->vscrollbar,
1166                                         &vscrollbar_requisition);
1167
1168       if (scrolled_window->window_placement == GTK_CORNER_TOP_LEFT ||
1169           scrolled_window->window_placement == GTK_CORNER_BOTTOM_LEFT)
1170         child_allocation.x = (relative_allocation.x +
1171                               relative_allocation.width +
1172                               SCROLLBAR_SPACING (scrolled_window) +
1173                               (scrolled_window->shadow_type == GTK_SHADOW_NONE ?
1174                                0 : widget->style->xthickness));
1175       else
1176         child_allocation.x = GTK_CONTAINER (scrolled_window)->border_width;
1177
1178       child_allocation.y = relative_allocation.y;
1179       child_allocation.width = vscrollbar_requisition.width;
1180       child_allocation.height = relative_allocation.height;
1181       child_allocation.x += allocation->x;
1182       child_allocation.y += allocation->y;
1183
1184       if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
1185         {
1186           child_allocation.y -= widget->style->ythickness;
1187           child_allocation.height += 2 * widget->style->ythickness;
1188         }
1189
1190       gtk_widget_size_allocate (scrolled_window->vscrollbar, &child_allocation);
1191     }
1192   else if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
1193     gtk_widget_hide (scrolled_window->vscrollbar);
1194 }
1195
1196 static gint
1197 gtk_scrolled_window_scroll_event (GtkWidget *widget,
1198                                   GdkEventScroll *event)
1199 {
1200   GtkWidget *range;
1201
1202   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE);
1203   g_return_val_if_fail (event != NULL, FALSE);  
1204
1205   if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
1206     range = GTK_SCROLLED_WINDOW (widget)->vscrollbar;
1207   else
1208     range = GTK_SCROLLED_WINDOW (widget)->hscrollbar;
1209
1210   if (range && GTK_WIDGET_VISIBLE (range))
1211     {
1212       GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
1213       gdouble new_value;
1214
1215       if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT)
1216         new_value = adj->value - adj->page_increment / 2;
1217       else
1218         new_value = adj->value + adj->page_increment / 2;
1219
1220       new_value = CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
1221       gtk_adjustment_set_value (adj, new_value);
1222
1223       return TRUE;
1224     }
1225
1226   return FALSE;
1227 }
1228
1229 static gint
1230 gtk_scrolled_window_focus (GtkWidget        *widget,
1231                            GtkDirectionType  direction)
1232 {
1233   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1234   gboolean had_focus_child = GTK_CONTAINER (widget)->focus_child != NULL;
1235   
1236   if (scrolled_window->focus_out)
1237     {
1238       scrolled_window->focus_out = FALSE; /* Clear this to catch the wrap-around case */
1239       return FALSE;
1240     }
1241   
1242   if (gtk_widget_is_focus (widget))
1243     return FALSE;
1244
1245   /* We only put the scrolled window itself in the focus chain if it
1246    * isn't possible to focus any children.
1247    */
1248   if (GTK_BIN (widget)->child)
1249     {
1250       if (gtk_widget_child_focus (GTK_BIN (widget)->child, direction))
1251         return TRUE;
1252     }
1253
1254   if (!had_focus_child)
1255     {
1256       gtk_widget_grab_focus (widget);
1257       return TRUE;
1258     }
1259   else
1260     return FALSE;
1261 }
1262
1263 static void
1264 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
1265                                         gpointer       data)
1266 {
1267   GtkScrolledWindow *scrolled_win;
1268
1269   g_return_if_fail (adjustment != NULL);
1270   g_return_if_fail (data != NULL);
1271
1272   scrolled_win = GTK_SCROLLED_WINDOW (data);
1273
1274   if (scrolled_win->hscrollbar &&
1275       adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->hscrollbar)))
1276     {
1277       if (scrolled_win->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1278         {
1279           gboolean visible;
1280           
1281           visible = scrolled_win->hscrollbar_visible;
1282           scrolled_win->hscrollbar_visible = (adjustment->upper - adjustment->lower >
1283                                               adjustment->page_size);
1284           if (scrolled_win->hscrollbar_visible != visible)
1285             gtk_widget_queue_resize (GTK_WIDGET (scrolled_win));
1286         }
1287     }
1288   else if (scrolled_win->vscrollbar &&
1289            adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->vscrollbar)))
1290     {
1291       if (scrolled_win->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1292         {
1293           gboolean visible;
1294
1295           visible = scrolled_win->vscrollbar_visible;
1296           scrolled_win->vscrollbar_visible = (adjustment->upper - adjustment->lower >
1297                                               adjustment->page_size);
1298           if (scrolled_win->vscrollbar_visible != visible)
1299             gtk_widget_queue_resize (GTK_WIDGET (scrolled_win));
1300         }
1301     }
1302 }
1303
1304 static void
1305 gtk_scrolled_window_add (GtkContainer *container,
1306                          GtkWidget    *child)
1307 {
1308   GtkScrolledWindow *scrolled_window;
1309   GtkBin *bin;
1310
1311   bin = GTK_BIN (container);
1312   g_return_if_fail (bin->child == NULL);
1313
1314   scrolled_window = GTK_SCROLLED_WINDOW (container);
1315
1316   bin->child = child;
1317   gtk_widget_set_parent (child, GTK_WIDGET (bin));
1318
1319   /* this is a temporary message */
1320   if (!gtk_widget_set_scroll_adjustments (child,
1321                                           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
1322                                           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar))))
1323     g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
1324                "use gtk_scrolled_window_add_with_viewport() instead");
1325 }
1326
1327 static void
1328 gtk_scrolled_window_remove (GtkContainer *container,
1329                             GtkWidget    *child)
1330 {
1331   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
1332   g_return_if_fail (child != NULL);
1333   g_return_if_fail (GTK_BIN (container)->child == child);
1334   
1335   gtk_widget_set_scroll_adjustments (child, NULL, NULL);
1336
1337   /* chain parent class handler to remove child */
1338   GTK_CONTAINER_CLASS (parent_class)->remove (container, child);
1339 }
1340
1341 void
1342 gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
1343                                        GtkWidget         *child)
1344 {
1345   GtkBin *bin;
1346   GtkWidget *viewport;
1347
1348   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1349   g_return_if_fail (GTK_IS_WIDGET (child));
1350   g_return_if_fail (child->parent == NULL);
1351
1352   bin = GTK_BIN (scrolled_window);
1353
1354   if (bin->child != NULL)
1355     {
1356       g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
1357       g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
1358
1359       viewport = bin->child;
1360     }
1361   else
1362     {
1363       viewport =
1364         gtk_viewport_new (gtk_scrolled_window_get_hadjustment (scrolled_window),
1365                           gtk_scrolled_window_get_vadjustment (scrolled_window));
1366       gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
1367     }
1368
1369   gtk_widget_show (viewport);
1370   gtk_container_add (GTK_CONTAINER (viewport), child);
1371 }