]> Pileus Git - ~andy/gtk/blob - gtk/gtkscrolledwindow.c
Move min-display-width/height to GtkScrolledWindow
[~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 "config.h"
28 #include <math.h>
29 #include <gdk/gdkkeysyms.h>
30 #include "gtkbindings.h"
31 #include "gtkmarshalers.h"
32 #include "gtkscrollable.h"
33 #include "gtkscrolledwindow.h"
34 #include "gtkwindow.h"
35 #include "gtktypeutils.h"
36 #include "gtkprivate.h"
37 #include "gtkintl.h"
38
39
40 /**
41  * SECTION:gtkscrolledwindow
42  * @Short_description: Adds scrollbars to its child widget
43  * @Title: GtkScrolledWindow
44  * @See_also: #GtkScrollable, #GtkViewport, #GtkAdjustment
45  *
46  * #GtkScrolledWindow is a #GtkBin subclass: it's a container
47  * the accepts a single child widget. #GtkScrolledWindow adds scrollbars
48  * to the child widget and optionally draws a beveled frame around the
49  * child widget.
50  *
51  * The scrolled window can work in two ways. Some widgets have native
52  * scrolling support; these widgets implement the #GtkScrollable interface.
53  * Widgets with native scroll support include #GtkTreeView, #GtkTextView,
54  * and #GtkLayout.
55  *
56  * For widgets that lack native scrolling support, the #GtkViewport
57  * widget acts as an adaptor class, implementing scrollability for child
58  * widgets that lack their own scrolling capabilities. Use #GtkViewport
59  * to scroll child widgets such as #GtkTable, #GtkBox, and so on.
60  *
61  * If a widget has native scrolling abilities, it can be added to the
62  * #GtkScrolledWindow with gtk_container_add(). If a widget does not, you
63  * must first add the widget to a #GtkViewport, then add the #GtkViewport
64  * to the scrolled window. The convenience function
65  * gtk_scrolled_window_add_with_viewport() does exactly this, so you can
66  * ignore the presence of the viewport.
67  *
68  * The position of the scrollbars is controlled by the scroll
69  * adjustments. See #GtkAdjustment for the fields in an adjustment - for
70  * #GtkScrollbar, used by #GtkScrolledWindow, the "value" field
71  * represents the position of the scrollbar, which must be between the
72  * "lower" field and "upper - page_size." The "page_size" field
73  * represents the size of the visible scrollable area. The
74  * "step_increment" and "page_increment" fields are used when the user
75  * asks to step down (using the small stepper arrows) or page down (using
76  * for example the PageDown key).
77  *
78  * If a #GtkScrolledWindow doesn't behave quite as you would like, or
79  * doesn't have exactly the right layout, it's very possible to set up
80  * your own scrolling with #GtkScrollbar and for example a #GtkTable.
81  */
82
83
84 /* scrolled window policy and size requisition handling:
85  *
86  * gtk size requisition works as follows:
87  *   a widget upon size-request reports the width and height that it finds
88  *   to be best suited to display its contents, including children.
89  *   the width and/or height reported from a widget upon size requisition
90  *   may be overidden by the user by specifying a width and/or height
91  *   other than 0 through gtk_widget_set_size_request().
92  *
93  * a scrolled window needs (for implementing all three policy types) to
94  * request its width and height based on two different rationales.
95  * 1)   the user wants the scrolled window to just fit into the space
96  *      that it gets allocated for a specifc dimension.
97  * 1.1) this does not apply if the user specified a concrete value
98  *      value for that specific dimension by either specifying usize for the
99  *      scrolled window or for its child.
100  * 2)   the user wants the scrolled window to take as much space up as
101  *      is desired by the child for a specifc dimension (i.e. POLICY_NEVER).
102  *
103  * also, kinda obvious:
104  * 3)   a user would certainly not have choosen a scrolled window as a container
105  *      for the child, if the resulting allocation takes up more space than the
106  *      child would have allocated without the scrolled window.
107  *
108  * conclusions:
109  * A) from 1) follows: the scrolled window shouldn't request more space for a
110  *    specifc dimension than is required at minimum.
111  * B) from 1.1) follows: the requisition may be overidden by usize of the scrolled
112  *    window (done automatically) or by usize of the child (needs to be checked).
113  * C) from 2) follows: for POLICY_NEVER, the scrolled window simply reports the
114  *    child's dimension.
115  * D) from 3) follows: the scrolled window child's minimum width and minimum height
116  *    under A) at least correspond to the space taken up by its scrollbars.
117  */
118
119 #define DEFAULT_SCROLLBAR_SPACING  3
120
121 struct _GtkScrolledWindowPrivate
122 {
123   GtkCornerType  real_window_placement;
124   GtkWidget     *hscrollbar;
125   GtkWidget     *vscrollbar;
126
127   gboolean window_placement_set;
128
129   guint16  shadow_type;
130
131   guint    hscrollbar_policy      : 2;
132   guint    vscrollbar_policy      : 2;
133   guint    hscrollbar_visible     : 1;
134   guint    vscrollbar_visible     : 1;
135   guint    window_placement       : 2;
136   guint    focus_out              : 1;   /* Flag used by ::move-focus-out implementation */
137   guint    hscroll_policy         : 1;
138   guint    vscroll_policy         : 1;
139
140   gint     min_content_width;
141   gint     min_content_height;
142 };
143
144
145 enum {
146   PROP_0,
147   PROP_HADJUSTMENT,
148   PROP_VADJUSTMENT,
149   PROP_HSCROLLBAR_POLICY,
150   PROP_VSCROLLBAR_POLICY,
151   PROP_HSCROLL_POLICY,
152   PROP_VSCROLL_POLICY,
153   PROP_WINDOW_PLACEMENT,
154   PROP_WINDOW_PLACEMENT_SET,
155   PROP_SHADOW_TYPE,
156   PROP_MIN_CONTENT_WIDTH,
157   PROP_MIN_CONTENT_HEIGHT
158 };
159
160 /* Signals */
161 enum
162 {
163   SCROLL_CHILD,
164   MOVE_FOCUS_OUT,
165   LAST_SIGNAL
166 };
167
168 static void     gtk_scrolled_window_set_property       (GObject           *object,
169                                                         guint              prop_id,
170                                                         const GValue      *value,
171                                                         GParamSpec        *pspec);
172 static void     gtk_scrolled_window_get_property       (GObject           *object,
173                                                         guint              prop_id,
174                                                         GValue            *value,
175                                                         GParamSpec        *pspec);
176
177 static void     gtk_scrolled_window_destroy            (GtkWidget         *widget);
178 static void     gtk_scrolled_window_screen_changed     (GtkWidget         *widget,
179                                                         GdkScreen         *previous_screen);
180 static gboolean gtk_scrolled_window_draw               (GtkWidget         *widget,
181                                                         cairo_t           *cr);
182 static void     gtk_scrolled_window_size_allocate      (GtkWidget         *widget,
183                                                         GtkAllocation     *allocation);
184 static gboolean gtk_scrolled_window_scroll_event       (GtkWidget         *widget,
185                                                         GdkEventScroll    *event);
186 static gboolean gtk_scrolled_window_focus              (GtkWidget         *widget,
187                                                         GtkDirectionType   direction);
188 static void     gtk_scrolled_window_add                (GtkContainer      *container,
189                                                         GtkWidget         *widget);
190 static void     gtk_scrolled_window_remove             (GtkContainer      *container,
191                                                         GtkWidget         *widget);
192 static void     gtk_scrolled_window_forall             (GtkContainer      *container,
193                                                         gboolean           include_internals,
194                                                         GtkCallback        callback,
195                                                         gpointer           callback_data);
196 static gboolean gtk_scrolled_window_scroll_child       (GtkScrolledWindow *scrolled_window,
197                                                         GtkScrollType      scroll,
198                                                         gboolean           horizontal);
199 static void     gtk_scrolled_window_move_focus_out     (GtkScrolledWindow *scrolled_window,
200                                                         GtkDirectionType   direction_type);
201
202 static void     gtk_scrolled_window_relative_allocation(GtkWidget         *widget,
203                                                         GtkAllocation     *allocation);
204 static void     gtk_scrolled_window_adjustment_changed (GtkAdjustment     *adjustment,
205                                                         gpointer           data);
206
207 static void  gtk_scrolled_window_update_real_placement (GtkScrolledWindow *scrolled_window);
208
209 static void  gtk_scrolled_window_get_preferred_width   (GtkWidget           *widget,
210                                                         gint                *minimum_size,
211                                                         gint                *natural_size);
212 static void  gtk_scrolled_window_get_preferred_height  (GtkWidget           *widget,
213                                                         gint                *minimum_size,
214                                                         gint                *natural_size);
215 static void  gtk_scrolled_window_get_preferred_height_for_width  (GtkWidget           *layout,
216                                                         gint                 width,
217                                                         gint                *minimum_height,
218                                                         gint                *natural_height);
219 static void  gtk_scrolled_window_get_preferred_width_for_height  (GtkWidget           *layout,
220                                                         gint                 width,
221                                                         gint                *minimum_height,
222                                                         gint                *natural_height);
223
224 static guint signals[LAST_SIGNAL] = {0};
225
226 G_DEFINE_TYPE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN)
227
228
229 static void
230 add_scroll_binding (GtkBindingSet  *binding_set,
231                     guint           keyval,
232                     GdkModifierType mask,
233                     GtkScrollType   scroll,
234                     gboolean        horizontal)
235 {
236   guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
237   
238   gtk_binding_entry_add_signal (binding_set, keyval, mask,
239                                 "scroll-child", 2,
240                                 GTK_TYPE_SCROLL_TYPE, scroll,
241                                 G_TYPE_BOOLEAN, horizontal);
242   gtk_binding_entry_add_signal (binding_set, keypad_keyval, mask,
243                                 "scroll-child", 2,
244                                 GTK_TYPE_SCROLL_TYPE, scroll,
245                                 G_TYPE_BOOLEAN, horizontal);
246 }
247
248 static void
249 add_tab_bindings (GtkBindingSet    *binding_set,
250                   GdkModifierType   modifiers,
251                   GtkDirectionType  direction)
252 {
253   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
254                                 "move-focus-out", 1,
255                                 GTK_TYPE_DIRECTION_TYPE, direction);
256   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
257                                 "move-focus-out", 1,
258                                 GTK_TYPE_DIRECTION_TYPE, direction);
259 }
260
261 static void
262 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
263 {
264   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
265   GtkWidgetClass *widget_class;
266   GtkContainerClass *container_class;
267   GtkBindingSet *binding_set;
268
269   widget_class = (GtkWidgetClass*) class;
270   container_class = (GtkContainerClass*) class;
271
272   gobject_class->set_property = gtk_scrolled_window_set_property;
273   gobject_class->get_property = gtk_scrolled_window_get_property;
274
275   widget_class->destroy = gtk_scrolled_window_destroy;
276   widget_class->screen_changed = gtk_scrolled_window_screen_changed;
277   widget_class->draw = gtk_scrolled_window_draw;
278   widget_class->size_allocate = gtk_scrolled_window_size_allocate;
279   widget_class->scroll_event = gtk_scrolled_window_scroll_event;
280   widget_class->focus = gtk_scrolled_window_focus;
281   widget_class->get_preferred_width = gtk_scrolled_window_get_preferred_width;
282   widget_class->get_preferred_height = gtk_scrolled_window_get_preferred_height;
283   widget_class->get_preferred_height_for_width = gtk_scrolled_window_get_preferred_height_for_width;
284   widget_class->get_preferred_width_for_height = gtk_scrolled_window_get_preferred_width_for_height;
285
286   container_class->add = gtk_scrolled_window_add;
287   container_class->remove = gtk_scrolled_window_remove;
288   container_class->forall = gtk_scrolled_window_forall;
289   gtk_container_class_handle_border_width (container_class);
290
291   class->scrollbar_spacing = -1;
292
293   class->scroll_child = gtk_scrolled_window_scroll_child;
294   class->move_focus_out = gtk_scrolled_window_move_focus_out;
295   
296   g_object_class_install_property (gobject_class,
297                                    PROP_HADJUSTMENT,
298                                    g_param_spec_object ("hadjustment",
299                                                         P_("Horizontal Adjustment"),
300                                                         P_("The GtkAdjustment for the horizontal position"),
301                                                         GTK_TYPE_ADJUSTMENT,
302                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
303   g_object_class_install_property (gobject_class,
304                                    PROP_VADJUSTMENT,
305                                    g_param_spec_object ("vadjustment",
306                                                         P_("Vertical Adjustment"),
307                                                         P_("The GtkAdjustment for the vertical position"),
308                                                         GTK_TYPE_ADJUSTMENT,
309                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
310   g_object_class_install_property (gobject_class,
311                                    PROP_HSCROLLBAR_POLICY,
312                                    g_param_spec_enum ("hscrollbar-policy",
313                                                       P_("Horizontal Scrollbar Policy"),
314                                                       P_("When the horizontal scrollbar is displayed"),
315                                                       GTK_TYPE_POLICY_TYPE,
316                                                       GTK_POLICY_AUTOMATIC,
317                                                       GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
318   g_object_class_install_property (gobject_class,
319                                    PROP_VSCROLLBAR_POLICY,
320                                    g_param_spec_enum ("vscrollbar-policy",
321                                                       P_("Vertical Scrollbar Policy"),
322                                                       P_("When the vertical scrollbar is displayed"),
323                                                       GTK_TYPE_POLICY_TYPE,
324                                                       GTK_POLICY_AUTOMATIC,
325                                                       GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
326
327   g_object_class_install_property (gobject_class,
328                                    PROP_WINDOW_PLACEMENT,
329                                    g_param_spec_enum ("window-placement",
330                                                       P_("Window Placement"),
331                                                       P_("Where the contents are located with respect to the scrollbars. This property only takes effect if \"window-placement-set\" is TRUE."),
332                                                       GTK_TYPE_CORNER_TYPE,
333                                                       GTK_CORNER_TOP_LEFT,
334                                                       GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
335   
336   /**
337    * GtkScrolledWindow:window-placement-set:
338    *
339    * Whether "window-placement" should be used to determine the location 
340    * of the contents with respect to the scrollbars. Otherwise, the 
341    * "gtk-scrolled-window-placement" setting is used.
342    *
343    * Since: 2.10
344    */
345   g_object_class_install_property (gobject_class,
346                                    PROP_WINDOW_PLACEMENT_SET,
347                                    g_param_spec_boolean ("window-placement-set",
348                                                          P_("Window Placement Set"),
349                                                          P_("Whether \"window-placement\" should be used to determine the location of the contents with respect to the scrollbars."),
350                                                          FALSE,
351                                                          GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
352   g_object_class_install_property (gobject_class,
353                                    PROP_SHADOW_TYPE,
354                                    g_param_spec_enum ("shadow-type",
355                                                       P_("Shadow Type"),
356                                                       P_("Style of bevel around the contents"),
357                                                       GTK_TYPE_SHADOW_TYPE,
358                                                       GTK_SHADOW_NONE,
359                                                       GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
360
361   /**
362    * GtkScrolledWindow:scrollbars-within-bevel:
363    *
364    * Whether to place scrollbars within the scrolled window's bevel.
365    *
366    * Since: 2.12
367    */
368   gtk_widget_class_install_style_property (widget_class,
369                                            g_param_spec_boolean ("scrollbars-within-bevel",
370                                                                  P_("Scrollbars within bevel"),
371                                                                  P_("Place scrollbars within the scrolled window's bevel"),
372                                                                  FALSE,
373                                                                  GTK_PARAM_READABLE));
374
375   gtk_widget_class_install_style_property (widget_class,
376                                            g_param_spec_int ("scrollbar-spacing",
377                                                              P_("Scrollbar spacing"),
378                                                              P_("Number of pixels between the scrollbars and the scrolled window"),
379                                                              0,
380                                                              G_MAXINT,
381                                                              DEFAULT_SCROLLBAR_SPACING,
382                                                              GTK_PARAM_READABLE));
383
384   g_object_class_install_property (gobject_class,
385                                    PROP_HSCROLL_POLICY,
386                                    g_param_spec_enum ("hscroll-policy",
387                                                       P_("Horizontal Scrollable Policy"),
388                                                       P_("How the size of the content should be determined"),
389                                                       GTK_TYPE_SCROLLABLE_POLICY,
390 GTK_SCROLL_NATURAL,
391                                                       GTK_PARAM_READWRITE));
392   g_object_class_install_property (gobject_class,
393                                    PROP_VSCROLL_POLICY,
394                                    g_param_spec_enum ("vscroll-policy",
395                                                       P_("Vertical Scrollable Policy"),
396                                                       P_("How the size of the content should be determined"),
397                                                       GTK_TYPE_SCROLLABLE_POLICY,
398 GTK_SCROLL_NATURAL,
399                                                       GTK_PARAM_READWRITE));
400
401   g_object_class_install_property (gobject_class,
402                                    PROP_MIN_CONTENT_WIDTH,
403                                    g_param_spec_int ("min-content-width",
404                                                      P_("Minimum Content Width"),
405                                                      P_("The minimum width that the scrolled window will allocate to its content"),
406                                                      -1, G_MAXINT, -1,
407                                                      GTK_PARAM_READWRITE));
408   g_object_class_install_property (gobject_class,
409                                    PROP_MIN_CONTENT_HEIGHT,
410                                    g_param_spec_int ("min-content-height",
411                                                      P_("Minimum Content Height"),
412                                                      P_("The minimum height that the scrolled window will allocate to its content"),
413                                                      -1, G_MAXINT, -1,
414                                                      GTK_PARAM_READWRITE));
415   /**
416    * GtkScrolledWindow::scroll-child:
417    * @scrolled_window: a #GtkScrolledWindow
418    * @scroll: a #GtkScrollType describing how much to scroll
419    * @horizontal: whether the keybinding scrolls the child
420    *   horizontally or not
421    *
422    * The ::scroll-child signal is a
423    * <link linkend="keybinding-signals">keybinding signal</link>
424    * which gets emitted when a keybinding that scrolls is pressed.
425    * The horizontal or vertical adjustment is updated which triggers a
426    * signal that the scrolled windows child may listen to and scroll itself.
427    */
428   signals[SCROLL_CHILD] =
429     g_signal_new (I_("scroll-child"),
430                   G_TYPE_FROM_CLASS (gobject_class),
431                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
432                   G_STRUCT_OFFSET (GtkScrolledWindowClass, scroll_child),
433                   NULL, NULL,
434                   _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
435                   G_TYPE_BOOLEAN, 2,
436                   GTK_TYPE_SCROLL_TYPE,
437                   G_TYPE_BOOLEAN);
438
439   /**
440    * GtkScrolledWindow::move-focus-out:
441    * @scrolled_window: a #GtkScrolledWindow
442    * @direction_type: either %GTK_DIR_TAB_FORWARD or
443    *   %GTK_DIR_TAB_BACKWARD
444    *
445    * The ::move-focus-out signal is a
446    * <link linkend="keybinding-signals">keybinding signal</link>
447    * which gets emitted when focus is moved away from the scrolled
448    * window by a keybinding.
449    * The #GtkWidget::move-focus signal is emitted with @direction_type
450    * on this scrolled windows toplevel parent in the container hierarchy.
451    * The default bindings for this signal are
452    * <keycombo><keycap>Tab</keycap><keycap>Ctrl</keycap></keycombo>
453    * and
454    * <keycombo><keycap>Tab</keycap><keycap>Ctrl</keycap><keycap>Shift</keycap></keycombo>.
455    */
456   signals[MOVE_FOCUS_OUT] =
457     g_signal_new (I_("move-focus-out"),
458                   G_TYPE_FROM_CLASS (gobject_class),
459                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
460                   G_STRUCT_OFFSET (GtkScrolledWindowClass, move_focus_out),
461                   NULL, NULL,
462                   _gtk_marshal_VOID__ENUM,
463                   G_TYPE_NONE, 1,
464                   GTK_TYPE_DIRECTION_TYPE);
465   
466   binding_set = gtk_binding_set_by_class (class);
467
468   add_scroll_binding (binding_set, GDK_KEY_Left,  GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, TRUE);
469   add_scroll_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD,  TRUE);
470   add_scroll_binding (binding_set, GDK_KEY_Up,    GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, FALSE);
471   add_scroll_binding (binding_set, GDK_KEY_Down,  GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD,  FALSE);
472
473   add_scroll_binding (binding_set, GDK_KEY_Page_Up,   GDK_CONTROL_MASK, GTK_SCROLL_PAGE_BACKWARD, TRUE);
474   add_scroll_binding (binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_FORWARD,  TRUE);
475   add_scroll_binding (binding_set, GDK_KEY_Page_Up,   0,                GTK_SCROLL_PAGE_BACKWARD, FALSE);
476   add_scroll_binding (binding_set, GDK_KEY_Page_Down, 0,                GTK_SCROLL_PAGE_FORWARD,  FALSE);
477
478   add_scroll_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_SCROLL_START, TRUE);
479   add_scroll_binding (binding_set, GDK_KEY_End,  GDK_CONTROL_MASK, GTK_SCROLL_END,   TRUE);
480   add_scroll_binding (binding_set, GDK_KEY_Home, 0,                GTK_SCROLL_START, FALSE);
481   add_scroll_binding (binding_set, GDK_KEY_End,  0,                GTK_SCROLL_END,   FALSE);
482
483   add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
484   add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
485
486   g_type_class_add_private (class, sizeof (GtkScrolledWindowPrivate));
487 }
488
489 static void
490 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
491 {
492   GtkScrolledWindowPrivate *priv;
493
494   scrolled_window->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (scrolled_window,
495                                                               GTK_TYPE_SCROLLED_WINDOW,
496                                                               GtkScrolledWindowPrivate);
497
498   gtk_widget_set_has_window (GTK_WIDGET (scrolled_window), FALSE);
499   gtk_widget_set_can_focus (GTK_WIDGET (scrolled_window), TRUE);
500
501   priv->hscrollbar = NULL;
502   priv->vscrollbar = NULL;
503   priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC;
504   priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC;
505   priv->hscrollbar_visible = FALSE;
506   priv->vscrollbar_visible = FALSE;
507   priv->focus_out = FALSE;
508   priv->window_placement = GTK_CORNER_TOP_LEFT;
509   gtk_scrolled_window_update_real_placement (scrolled_window);
510   priv->hscroll_policy = GTK_SCROLL_NATURAL;
511   priv->vscroll_policy = GTK_SCROLL_NATURAL;
512   priv->min_content_width = -1;
513   priv->min_content_height = -1;
514 }
515
516 /**
517  * gtk_scrolled_window_new:
518  * @hadjustment: (allow-none): horizontal adjustment
519  * @vadjustment: (allow-none): vertical adjustment
520  *
521  * Creates a new scrolled window.
522  *
523  * The two arguments are the scrolled window's adjustments; these will be
524  * shared with the scrollbars and the child widget to keep the bars in sync 
525  * with the child. Usually you want to pass %NULL for the adjustments, which 
526  * will cause the scrolled window to create them for you.
527  *
528  * Returns: a new scrolled window
529  */
530 GtkWidget*
531 gtk_scrolled_window_new (GtkAdjustment *hadjustment,
532                          GtkAdjustment *vadjustment)
533 {
534   GtkWidget *scrolled_window;
535
536   if (hadjustment)
537     g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
538
539   if (vadjustment)
540     g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
541
542   scrolled_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
543                                     "hadjustment", hadjustment,
544                                     "vadjustment", vadjustment,
545                                     NULL);
546
547   return scrolled_window;
548 }
549
550 /**
551  * gtk_scrolled_window_set_hadjustment:
552  * @scrolled_window: a #GtkScrolledWindow
553  * @hadjustment: horizontal scroll adjustment
554  *
555  * Sets the #GtkAdjustment for the horizontal scrollbar.
556  */
557 void
558 gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
559                                      GtkAdjustment     *hadjustment)
560 {
561   GtkScrolledWindowPrivate *priv;
562   GtkBin *bin;
563   GtkWidget *child;
564
565   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
566   if (hadjustment)
567     g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
568   else
569     hadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
570
571   bin = GTK_BIN (scrolled_window);
572   priv = scrolled_window->priv;
573
574   if (!priv->hscrollbar)
575     {
576       gtk_widget_push_composite_child ();
577       priv->hscrollbar = gtk_hscrollbar_new (hadjustment);
578       gtk_widget_set_composite_name (priv->hscrollbar, "hscrollbar");
579       gtk_widget_pop_composite_child ();
580
581       gtk_widget_set_parent (priv->hscrollbar, GTK_WIDGET (scrolled_window));
582       g_object_ref (priv->hscrollbar);
583       gtk_widget_show (priv->hscrollbar);
584     }
585   else
586     {
587       GtkAdjustment *old_adjustment;
588
589       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
590       if (old_adjustment == hadjustment)
591         return;
592
593       g_signal_handlers_disconnect_by_func (old_adjustment,
594                                             gtk_scrolled_window_adjustment_changed,
595                                             scrolled_window);
596       gtk_range_set_adjustment (GTK_RANGE (priv->hscrollbar),
597                                 hadjustment);
598     }
599   hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
600   g_signal_connect (hadjustment,
601                     "changed",
602                     G_CALLBACK (gtk_scrolled_window_adjustment_changed),
603                     scrolled_window);
604   gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
605
606   child = gtk_bin_get_child (bin);
607   if (GTK_IS_SCROLLABLE (child))
608     gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (child), hadjustment);
609
610   g_object_notify (G_OBJECT (scrolled_window), "hadjustment");
611 }
612
613 /**
614  * gtk_scrolled_window_set_vadjustment:
615  * @scrolled_window: a #GtkScrolledWindow
616  * @vadjustment: vertical scroll adjustment
617  *
618  * Sets the #GtkAdjustment for the vertical scrollbar.
619  */
620 void
621 gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
622                                      GtkAdjustment     *vadjustment)
623 {
624   GtkScrolledWindowPrivate *priv;
625   GtkBin *bin;
626   GtkWidget *child;
627
628   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
629   if (vadjustment)
630     g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
631   else
632     vadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
633
634   bin = GTK_BIN (scrolled_window);
635   priv = scrolled_window->priv;
636
637   if (!priv->vscrollbar)
638     {
639       gtk_widget_push_composite_child ();
640       priv->vscrollbar = gtk_vscrollbar_new (vadjustment);
641       gtk_widget_set_composite_name (priv->vscrollbar, "vscrollbar");
642       gtk_widget_pop_composite_child ();
643
644       gtk_widget_set_parent (priv->vscrollbar, GTK_WIDGET (scrolled_window));
645       g_object_ref (priv->vscrollbar);
646       gtk_widget_show (priv->vscrollbar);
647     }
648   else
649     {
650       GtkAdjustment *old_adjustment;
651       
652       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
653       if (old_adjustment == vadjustment)
654         return;
655
656       g_signal_handlers_disconnect_by_func (old_adjustment,
657                                             gtk_scrolled_window_adjustment_changed,
658                                             scrolled_window);
659       gtk_range_set_adjustment (GTK_RANGE (priv->vscrollbar),
660                                 vadjustment);
661     }
662   vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
663   g_signal_connect (vadjustment,
664                     "changed",
665                     G_CALLBACK (gtk_scrolled_window_adjustment_changed),
666                     scrolled_window);
667   gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
668
669   child = gtk_bin_get_child (bin);
670   if (GTK_IS_SCROLLABLE (child))
671     gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (child), vadjustment);
672
673   g_object_notify (G_OBJECT (scrolled_window), "vadjustment");
674 }
675
676 /**
677  * gtk_scrolled_window_get_hadjustment:
678  * @scrolled_window: a #GtkScrolledWindow
679  *
680  * Returns the horizontal scrollbar's adjustment, used to connect the
681  * horizontal scrollbar to the child widget's horizontal scroll
682  * functionality.
683  *
684  * Returns: (transfer none): the horizontal #GtkAdjustment
685  */
686 GtkAdjustment*
687 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
688 {
689   GtkScrolledWindowPrivate *priv;
690
691   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
692
693   priv = scrolled_window->priv;
694
695   return (priv->hscrollbar ?
696           gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)) :
697           NULL);
698 }
699
700 /**
701  * gtk_scrolled_window_get_vadjustment:
702  * @scrolled_window: a #GtkScrolledWindow
703  * 
704  * Returns the vertical scrollbar's adjustment, used to connect the
705  * vertical scrollbar to the child widget's vertical scroll functionality.
706  * 
707  * Returns: (transfer none): the vertical #GtkAdjustment
708  */
709 GtkAdjustment*
710 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
711 {
712   GtkScrolledWindowPrivate *priv;
713
714   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
715
716   priv = scrolled_window->priv;
717
718   return (priv->vscrollbar ?
719           gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)) :
720           NULL);
721 }
722
723 /**
724  * gtk_scrolled_window_get_hscrollbar:
725  * @scrolled_window: a #GtkScrolledWindow
726  *
727  * Returns the horizontal scrollbar of @scrolled_window.
728  *
729  * Returns: (transfer none): the horizontal scrollbar of the scrolled window,
730  *     or %NULL if it does not have one.
731  *
732  * Since: 2.8
733  */
734 GtkWidget*
735 gtk_scrolled_window_get_hscrollbar (GtkScrolledWindow *scrolled_window)
736 {
737   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
738
739   return scrolled_window->priv->hscrollbar;
740 }
741
742 /**
743  * gtk_scrolled_window_get_vscrollbar:
744  * @scrolled_window: a #GtkScrolledWindow
745  * 
746  * Returns the vertical scrollbar of @scrolled_window.
747  *
748  * Returns: (transfer none): the vertical scrollbar of the scrolled window,
749  *     or %NULL if it does not have one.
750  *
751  * Since: 2.8
752  */
753 GtkWidget*
754 gtk_scrolled_window_get_vscrollbar (GtkScrolledWindow *scrolled_window)
755 {
756   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
757
758   return scrolled_window->priv->vscrollbar;
759 }
760
761 /**
762  * gtk_scrolled_window_set_policy:
763  * @scrolled_window: a #GtkScrolledWindow
764  * @hscrollbar_policy: policy for horizontal bar
765  * @vscrollbar_policy: policy for vertical bar
766  * 
767  * Sets the scrollbar policy for the horizontal and vertical scrollbars.
768  *
769  * The policy determines when the scrollbar should appear; it is a value
770  * from the #GtkPolicyType enumeration. If %GTK_POLICY_ALWAYS, the
771  * scrollbar is always present; if %GTK_POLICY_NEVER, the scrollbar is
772  * never present; if %GTK_POLICY_AUTOMATIC, the scrollbar is present only
773  * if needed (that is, if the slider part of the bar would be smaller
774  * than the trough - the display is larger than the page size).
775  */
776 void
777 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
778                                 GtkPolicyType      hscrollbar_policy,
779                                 GtkPolicyType      vscrollbar_policy)
780 {
781   GtkScrolledWindowPrivate *priv;
782   GObject *object = G_OBJECT (scrolled_window);
783   
784   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
785
786   priv = scrolled_window->priv;
787
788   if ((priv->hscrollbar_policy != hscrollbar_policy) ||
789       (priv->vscrollbar_policy != vscrollbar_policy))
790     {
791       priv->hscrollbar_policy = hscrollbar_policy;
792       priv->vscrollbar_policy = vscrollbar_policy;
793
794       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
795
796       g_object_freeze_notify (object);
797       g_object_notify (object, "hscrollbar-policy");
798       g_object_notify (object, "vscrollbar-policy");
799       g_object_thaw_notify (object);
800     }
801 }
802
803 /**
804  * gtk_scrolled_window_get_policy:
805  * @scrolled_window: a #GtkScrolledWindow
806  * @hscrollbar_policy: location to store the policy for the horizontal 
807  *     scrollbar, or %NULL.
808  * @vscrollbar_policy: location to store the policy for the vertical
809  *     scrollbar, or %NULL.
810  * 
811  * Retrieves the current policy values for the horizontal and vertical
812  * scrollbars. See gtk_scrolled_window_set_policy().
813  */
814 void
815 gtk_scrolled_window_get_policy (GtkScrolledWindow *scrolled_window,
816                                 GtkPolicyType     *hscrollbar_policy,
817                                 GtkPolicyType     *vscrollbar_policy)
818 {
819   GtkScrolledWindowPrivate *priv;
820
821   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
822
823   priv = scrolled_window->priv;
824
825   if (hscrollbar_policy)
826     *hscrollbar_policy = priv->hscrollbar_policy;
827   if (vscrollbar_policy)
828     *vscrollbar_policy = priv->vscrollbar_policy;
829 }
830
831 static void
832 gtk_scrolled_window_update_real_placement (GtkScrolledWindow *scrolled_window)
833 {
834   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
835   GtkSettings *settings;
836
837   settings = gtk_widget_get_settings (GTK_WIDGET (scrolled_window));
838
839   if (priv->window_placement_set || settings == NULL)
840     priv->real_window_placement = priv->window_placement;
841   else
842     g_object_get (settings,
843                   "gtk-scrolled-window-placement",
844                   &priv->real_window_placement,
845                   NULL);
846 }
847
848 static void
849 gtk_scrolled_window_set_placement_internal (GtkScrolledWindow *scrolled_window,
850                                             GtkCornerType      window_placement)
851 {
852   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
853
854   if (priv->window_placement != window_placement)
855     {
856       priv->window_placement = window_placement;
857
858       gtk_scrolled_window_update_real_placement (scrolled_window);
859       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
860       
861       g_object_notify (G_OBJECT (scrolled_window), "window-placement");
862     }
863 }
864
865 static void
866 gtk_scrolled_window_set_placement_set (GtkScrolledWindow *scrolled_window,
867                                        gboolean           placement_set,
868                                        gboolean           emit_resize)
869 {
870   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
871
872   if (priv->window_placement_set != placement_set)
873     {
874       priv->window_placement_set = placement_set;
875
876       gtk_scrolled_window_update_real_placement (scrolled_window);
877       if (emit_resize)
878         gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
879
880       g_object_notify (G_OBJECT (scrolled_window), "window-placement-set");
881     }
882 }
883
884 /**
885  * gtk_scrolled_window_set_placement:
886  * @scrolled_window: a #GtkScrolledWindow
887  * @window_placement: position of the child window
888  *
889  * Sets the placement of the contents with respect to the scrollbars
890  * for the scrolled window.
891  * 
892  * The default is %GTK_CORNER_TOP_LEFT, meaning the child is
893  * in the top left, with the scrollbars underneath and to the right.
894  * Other values in #GtkCornerType are %GTK_CORNER_TOP_RIGHT,
895  * %GTK_CORNER_BOTTOM_LEFT, and %GTK_CORNER_BOTTOM_RIGHT.
896  *
897  * See also gtk_scrolled_window_get_placement() and
898  * gtk_scrolled_window_unset_placement().
899  */
900 void
901 gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
902                                    GtkCornerType      window_placement)
903 {
904   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
905
906   gtk_scrolled_window_set_placement_set (scrolled_window, TRUE, FALSE);
907   gtk_scrolled_window_set_placement_internal (scrolled_window, window_placement);
908 }
909
910 /**
911  * gtk_scrolled_window_get_placement:
912  * @scrolled_window: a #GtkScrolledWindow
913  *
914  * Gets the placement of the contents with respect to the scrollbars
915  * for the scrolled window. See gtk_scrolled_window_set_placement().
916  *
917  * Return value: the current placement value.
918  *
919  * See also gtk_scrolled_window_set_placement() and
920  * gtk_scrolled_window_unset_placement().
921  **/
922 GtkCornerType
923 gtk_scrolled_window_get_placement (GtkScrolledWindow *scrolled_window)
924 {
925   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_CORNER_TOP_LEFT);
926
927   return scrolled_window->priv->window_placement;
928 }
929
930 /**
931  * gtk_scrolled_window_unset_placement:
932  * @scrolled_window: a #GtkScrolledWindow
933  *
934  * Unsets the placement of the contents with respect to the scrollbars
935  * for the scrolled window. If no window placement is set for a scrolled
936  * window, it obeys the "gtk-scrolled-window-placement" XSETTING.
937  *
938  * See also gtk_scrolled_window_set_placement() and
939  * gtk_scrolled_window_get_placement().
940  *
941  * Since: 2.10
942  **/
943 void
944 gtk_scrolled_window_unset_placement (GtkScrolledWindow *scrolled_window)
945 {
946   GtkScrolledWindowPrivate *priv;
947
948   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
949
950   priv = scrolled_window->priv;
951
952   if (priv->window_placement_set)
953     {
954       priv->window_placement_set = FALSE;
955
956       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
957
958       g_object_notify (G_OBJECT (scrolled_window), "window-placement-set");
959     }
960 }
961
962 /**
963  * gtk_scrolled_window_set_shadow_type:
964  * @scrolled_window: a #GtkScrolledWindow
965  * @type: kind of shadow to draw around scrolled window contents
966  *
967  * Changes the type of shadow drawn around the contents of
968  * @scrolled_window.
969  * 
970  **/
971 void
972 gtk_scrolled_window_set_shadow_type (GtkScrolledWindow *scrolled_window,
973                                      GtkShadowType      type)
974 {
975   GtkScrolledWindowPrivate *priv;
976
977   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
978   g_return_if_fail (type >= GTK_SHADOW_NONE && type <= GTK_SHADOW_ETCHED_OUT);
979
980   priv = scrolled_window->priv;
981
982   if (priv->shadow_type != type)
983     {
984       priv->shadow_type = type;
985
986       if (gtk_widget_is_drawable (GTK_WIDGET (scrolled_window)))
987         gtk_widget_queue_draw (GTK_WIDGET (scrolled_window));
988
989       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
990
991       g_object_notify (G_OBJECT (scrolled_window), "shadow-type");
992     }
993 }
994
995 /**
996  * gtk_scrolled_window_get_shadow_type:
997  * @scrolled_window: a #GtkScrolledWindow
998  *
999  * Gets the shadow type of the scrolled window. See 
1000  * gtk_scrolled_window_set_shadow_type().
1001  *
1002  * Return value: the current shadow type
1003  **/
1004 GtkShadowType
1005 gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolled_window)
1006 {
1007   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_NONE);
1008
1009   return scrolled_window->priv->shadow_type;
1010 }
1011
1012 static void
1013 gtk_scrolled_window_destroy (GtkWidget *widget)
1014 {
1015   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1016   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1017
1018   if (priv->hscrollbar)
1019     {
1020       g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)),
1021                                             gtk_scrolled_window_adjustment_changed,
1022                                             scrolled_window);
1023       gtk_widget_unparent (priv->hscrollbar);
1024       gtk_widget_destroy (priv->hscrollbar);
1025       g_object_unref (priv->hscrollbar);
1026       priv->hscrollbar = NULL;
1027     }
1028   if (priv->vscrollbar)
1029     {
1030       g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)),
1031                                             gtk_scrolled_window_adjustment_changed,
1032                                             scrolled_window);
1033       gtk_widget_unparent (priv->vscrollbar);
1034       gtk_widget_destroy (priv->vscrollbar);
1035       g_object_unref (priv->vscrollbar);
1036       priv->vscrollbar = NULL;
1037     }
1038
1039   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->destroy (widget);
1040 }
1041
1042 static void
1043 gtk_scrolled_window_set_property (GObject      *object,
1044                                   guint         prop_id,
1045                                   const GValue *value,
1046                                   GParamSpec   *pspec)
1047 {
1048   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
1049   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1050
1051   switch (prop_id)
1052     {
1053     case PROP_HADJUSTMENT:
1054       gtk_scrolled_window_set_hadjustment (scrolled_window,
1055                                            g_value_get_object (value));
1056       break;
1057     case PROP_VADJUSTMENT:
1058       gtk_scrolled_window_set_vadjustment (scrolled_window,
1059                                            g_value_get_object (value));
1060       break;
1061     case PROP_HSCROLLBAR_POLICY:
1062       gtk_scrolled_window_set_policy (scrolled_window,
1063                                       g_value_get_enum (value),
1064                                       priv->vscrollbar_policy);
1065       break;
1066     case PROP_VSCROLLBAR_POLICY:
1067       gtk_scrolled_window_set_policy (scrolled_window,
1068                                       priv->hscrollbar_policy,
1069                                       g_value_get_enum (value));
1070       break;
1071     case PROP_WINDOW_PLACEMENT:
1072       gtk_scrolled_window_set_placement_internal (scrolled_window,
1073                                                   g_value_get_enum (value));
1074       break;
1075     case PROP_WINDOW_PLACEMENT_SET:
1076       gtk_scrolled_window_set_placement_set (scrolled_window,
1077                                              g_value_get_boolean (value),
1078                                              TRUE);
1079       break;
1080     case PROP_SHADOW_TYPE:
1081       gtk_scrolled_window_set_shadow_type (scrolled_window,
1082                                            g_value_get_enum (value));
1083       break;
1084     case PROP_HSCROLL_POLICY:
1085       gtk_scrolled_window_set_hscroll_policy (scrolled_window,
1086                                               g_value_get_enum (value));
1087       break;
1088     case PROP_VSCROLL_POLICY:
1089       gtk_scrolled_window_set_vscroll_policy (scrolled_window,
1090                                               g_value_get_enum (value));
1091       break;
1092     case PROP_MIN_CONTENT_WIDTH:
1093       gtk_scrolled_window_set_min_content_width (scrolled_window,
1094                                                  g_value_get_int (value));
1095       break;
1096     case PROP_MIN_CONTENT_HEIGHT:
1097       gtk_scrolled_window_set_min_content_height (scrolled_window,
1098                                                   g_value_get_int (value));
1099       break;
1100     default:
1101       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1102       break;
1103     }
1104 }
1105
1106 static void
1107 gtk_scrolled_window_get_property (GObject    *object,
1108                                   guint       prop_id,
1109                                   GValue     *value,
1110                                   GParamSpec *pspec)
1111 {
1112   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
1113   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1114
1115   switch (prop_id)
1116     {
1117     case PROP_HADJUSTMENT:
1118       g_value_set_object (value,
1119                           G_OBJECT (gtk_scrolled_window_get_hadjustment (scrolled_window)));
1120       break;
1121     case PROP_VADJUSTMENT:
1122       g_value_set_object (value,
1123                           G_OBJECT (gtk_scrolled_window_get_vadjustment (scrolled_window)));
1124       break;
1125     case PROP_WINDOW_PLACEMENT:
1126       g_value_set_enum (value, priv->window_placement);
1127       break;
1128     case PROP_WINDOW_PLACEMENT_SET:
1129       g_value_set_boolean (value, priv->window_placement_set);
1130       break;
1131     case PROP_SHADOW_TYPE:
1132       g_value_set_enum (value, priv->shadow_type);
1133       break;
1134     case PROP_HSCROLLBAR_POLICY:
1135       g_value_set_enum (value, priv->hscrollbar_policy);
1136       break;
1137     case PROP_VSCROLLBAR_POLICY:
1138       g_value_set_enum (value, priv->vscrollbar_policy);
1139       break;
1140     case PROP_HSCROLL_POLICY:
1141       g_value_set_enum (value, priv->hscroll_policy);
1142       break;
1143     case PROP_VSCROLL_POLICY:
1144       g_value_set_enum (value, priv->vscroll_policy);
1145       break;
1146     case PROP_MIN_CONTENT_WIDTH:
1147       g_value_set_int (value, priv->min_content_width);
1148       break;
1149     case PROP_MIN_CONTENT_HEIGHT:
1150       g_value_set_int (value, priv->min_content_height);
1151       break;
1152     default:
1153       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1154       break;
1155     }
1156 }
1157
1158 static void
1159 traverse_container (GtkWidget *widget,
1160                     gpointer   data)
1161 {
1162   if (GTK_IS_SCROLLED_WINDOW (widget))
1163     {
1164       gtk_scrolled_window_update_real_placement (GTK_SCROLLED_WINDOW (widget));
1165       gtk_widget_queue_resize (widget);
1166     }
1167   else if (GTK_IS_CONTAINER (widget))
1168     gtk_container_forall (GTK_CONTAINER (widget), traverse_container, NULL);
1169 }
1170
1171 static void
1172 gtk_scrolled_window_settings_changed (GtkSettings *settings)
1173 {
1174   GList *list, *l;
1175
1176   list = gtk_window_list_toplevels ();
1177
1178   for (l = list; l; l = l->next)
1179     gtk_container_forall (GTK_CONTAINER (l->data), 
1180                           traverse_container, NULL);
1181
1182   g_list_free (list);
1183 }
1184
1185 static void
1186 gtk_scrolled_window_screen_changed (GtkWidget *widget,
1187                                     GdkScreen *previous_screen)
1188 {
1189   GtkSettings *settings;
1190   guint window_placement_connection;
1191
1192   gtk_scrolled_window_update_real_placement (GTK_SCROLLED_WINDOW (widget));
1193
1194   if (!gtk_widget_has_screen (widget))
1195     return;
1196
1197   settings = gtk_widget_get_settings (widget);
1198
1199   window_placement_connection = 
1200     GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (settings), 
1201                                          "gtk-scrolled-window-connection"));
1202   
1203   if (window_placement_connection)
1204     return;
1205
1206   window_placement_connection =
1207     g_signal_connect (settings, "notify::gtk-scrolled-window-placement",
1208                       G_CALLBACK (gtk_scrolled_window_settings_changed), NULL);
1209   g_object_set_data (G_OBJECT (settings), 
1210                      I_("gtk-scrolled-window-connection"),
1211                      GUINT_TO_POINTER (window_placement_connection));
1212 }
1213
1214 static gboolean
1215 gtk_scrolled_window_draw (GtkWidget *widget,
1216                           cairo_t   *cr)
1217 {
1218   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1219   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1220
1221   if (priv->shadow_type != GTK_SHADOW_NONE)
1222     {
1223       GtkAllocation relative_allocation;
1224       GtkStyle *style;
1225       gboolean scrollbars_within_bevel;
1226
1227       style = gtk_widget_get_style (widget);
1228       gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
1229
1230       if (!scrollbars_within_bevel)
1231         {
1232           gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1233
1234           relative_allocation.x -= style->xthickness;
1235           relative_allocation.y -= style->ythickness;
1236           relative_allocation.width += 2 * style->xthickness;
1237           relative_allocation.height += 2 * style->ythickness;
1238         }
1239       else
1240         {
1241           relative_allocation.x = 0;
1242           relative_allocation.y = 0;
1243           relative_allocation.width = gtk_widget_get_allocated_width (widget);
1244           relative_allocation.height = gtk_widget_get_allocated_height (widget);
1245         }
1246
1247       gtk_paint_shadow (style,
1248                         cr,
1249                         GTK_STATE_NORMAL, priv->shadow_type,
1250                         widget, "scrolled_window",
1251                         relative_allocation.x,
1252                         relative_allocation.y,
1253                         relative_allocation.width,
1254                         relative_allocation.height);
1255     }
1256
1257   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->draw (widget, cr);
1258
1259   return FALSE;
1260 }
1261
1262 static void
1263 gtk_scrolled_window_forall (GtkContainer *container,
1264                             gboolean      include_internals,
1265                             GtkCallback   callback,
1266                             gpointer      callback_data)
1267 {
1268   GtkScrolledWindowPrivate *priv;
1269   GtkScrolledWindow *scrolled_window;
1270
1271   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
1272   g_return_if_fail (callback != NULL);
1273
1274   GTK_CONTAINER_CLASS (gtk_scrolled_window_parent_class)->forall (container,
1275                                               include_internals,
1276                                               callback,
1277                                               callback_data);
1278   if (include_internals)
1279     {
1280       scrolled_window = GTK_SCROLLED_WINDOW (container);
1281       priv = scrolled_window->priv;
1282
1283       if (priv->vscrollbar)
1284         callback (priv->vscrollbar, callback_data);
1285       if (priv->hscrollbar)
1286         callback (priv->hscrollbar, callback_data);
1287     }
1288 }
1289
1290 static gboolean
1291 gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
1292                                   GtkScrollType      scroll,
1293                                   gboolean           horizontal)
1294 {
1295   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1296   GtkAdjustment *adjustment = NULL;
1297   
1298   switch (scroll)
1299     {
1300     case GTK_SCROLL_STEP_UP:
1301       scroll = GTK_SCROLL_STEP_BACKWARD;
1302       horizontal = FALSE;
1303       break;
1304     case GTK_SCROLL_STEP_DOWN:
1305       scroll = GTK_SCROLL_STEP_FORWARD;
1306       horizontal = FALSE;
1307       break;
1308     case GTK_SCROLL_STEP_LEFT:
1309       scroll = GTK_SCROLL_STEP_BACKWARD;
1310       horizontal = TRUE;
1311       break;
1312     case GTK_SCROLL_STEP_RIGHT:
1313       scroll = GTK_SCROLL_STEP_FORWARD;
1314       horizontal = TRUE;
1315       break;
1316     case GTK_SCROLL_PAGE_UP:
1317       scroll = GTK_SCROLL_PAGE_BACKWARD;
1318       horizontal = FALSE;
1319       break;
1320     case GTK_SCROLL_PAGE_DOWN:
1321       scroll = GTK_SCROLL_PAGE_FORWARD;
1322       horizontal = FALSE;
1323       break;
1324     case GTK_SCROLL_PAGE_LEFT:
1325       scroll = GTK_SCROLL_STEP_BACKWARD;
1326       horizontal = TRUE;
1327       break;
1328     case GTK_SCROLL_PAGE_RIGHT:
1329       scroll = GTK_SCROLL_STEP_FORWARD;
1330       horizontal = TRUE;
1331       break;
1332     case GTK_SCROLL_STEP_BACKWARD:
1333     case GTK_SCROLL_STEP_FORWARD:
1334     case GTK_SCROLL_PAGE_BACKWARD:
1335     case GTK_SCROLL_PAGE_FORWARD:
1336     case GTK_SCROLL_START:
1337     case GTK_SCROLL_END:
1338       break;
1339     default:
1340       g_warning ("Invalid scroll type %u for GtkScrolledWindow::scroll-child", scroll);
1341       return FALSE;
1342     }
1343
1344   if ((horizontal && (!priv->hscrollbar || !priv->hscrollbar_visible)) ||
1345       (!horizontal && (!priv->vscrollbar || !priv->vscrollbar_visible)))
1346     return FALSE;
1347
1348   if (horizontal)
1349     {
1350       if (priv->hscrollbar)
1351         adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
1352     }
1353   else
1354     {
1355       if (priv->vscrollbar)
1356         adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
1357     }
1358
1359   if (adjustment)
1360     {
1361       gdouble value = adjustment->value;
1362       
1363       switch (scroll)
1364         {
1365         case GTK_SCROLL_STEP_FORWARD:
1366           value += adjustment->step_increment;
1367           break;
1368         case GTK_SCROLL_STEP_BACKWARD:
1369           value -= adjustment->step_increment;
1370           break;
1371         case GTK_SCROLL_PAGE_FORWARD:
1372           value += adjustment->page_increment;
1373           break;
1374         case GTK_SCROLL_PAGE_BACKWARD:
1375           value -= adjustment->page_increment;
1376           break;
1377         case GTK_SCROLL_START:
1378           value = adjustment->lower;
1379           break;
1380         case GTK_SCROLL_END:
1381           value = adjustment->upper;
1382           break;
1383         default:
1384           g_assert_not_reached ();
1385           break;
1386         }
1387
1388       gtk_adjustment_set_value (adjustment, value);
1389
1390       return TRUE;
1391     }
1392
1393   return FALSE;
1394 }
1395
1396 static void
1397 gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
1398                                     GtkDirectionType   direction_type)
1399 {
1400   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1401   GtkWidget *toplevel;
1402   
1403   /* Focus out of the scrolled window entirely. We do this by setting
1404    * a flag, then propagating the focus motion to the notebook.
1405    */
1406   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (scrolled_window));
1407   if (!gtk_widget_is_toplevel (toplevel))
1408     return;
1409
1410   g_object_ref (scrolled_window);
1411
1412   priv->focus_out = TRUE;
1413   g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1414   priv->focus_out = FALSE;
1415
1416   g_object_unref (scrolled_window);
1417 }
1418
1419 static void
1420 gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
1421                                          GtkAllocation *allocation)
1422 {
1423   GtkAllocation widget_allocation;
1424   GtkScrolledWindow *scrolled_window;
1425   GtkScrolledWindowPrivate *priv;
1426   GtkStyle *style;
1427   gint sb_spacing;
1428   gint sb_width;
1429   gint sb_height;
1430
1431   g_return_if_fail (widget != NULL);
1432   g_return_if_fail (allocation != NULL);
1433
1434   scrolled_window = GTK_SCROLLED_WINDOW (widget);
1435   priv = scrolled_window->priv;
1436
1437   /* Get possible scrollbar dimensions */
1438   sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
1439   gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
1440   gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
1441
1442   /* Subtract some things from our available allocation size */
1443   allocation->x = 0;
1444   allocation->y = 0;
1445
1446   if (priv->shadow_type != GTK_SHADOW_NONE)
1447     {
1448       style = gtk_widget_get_style (widget);
1449       allocation->x += style->xthickness;
1450       allocation->y += style->ythickness;
1451     }
1452
1453   gtk_widget_get_allocation (widget, &widget_allocation);
1454   allocation->width = MAX (1, (gint) widget_allocation.width - allocation->x * 2);
1455   allocation->height = MAX (1, (gint) widget_allocation.height - allocation->y * 2);
1456
1457   if (priv->vscrollbar_visible)
1458     {
1459       gboolean is_rtl;
1460
1461       is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1462   
1463       if ((!is_rtl && 
1464            (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
1465             priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
1466           (is_rtl && 
1467            (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1468             priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
1469         allocation->x += (sb_width +  sb_spacing);
1470
1471       allocation->width = MAX (1, allocation->width - (sb_width + sb_spacing));
1472     }
1473   if (priv->hscrollbar_visible)
1474     {
1475
1476       if (priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT ||
1477           priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)
1478         allocation->y += (sb_height + sb_spacing);
1479
1480       allocation->height = MAX (1, allocation->height - (sb_height + sb_spacing));
1481     }
1482 }
1483
1484 static void
1485 gtk_scrolled_window_size_allocate (GtkWidget     *widget,
1486                                    GtkAllocation *allocation)
1487 {
1488   GtkScrolledWindow *scrolled_window;
1489   GtkScrolledWindowPrivate *priv;
1490   GtkStyle *style;
1491   GtkBin *bin;
1492   GtkAllocation relative_allocation;
1493   GtkAllocation child_allocation;
1494   GtkWidget *child;
1495   gboolean scrollbars_within_bevel;
1496   gint sb_spacing;
1497   gint sb_width;
1498   gint sb_height;
1499  
1500
1501
1502   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
1503   g_return_if_fail (allocation != NULL);
1504
1505   scrolled_window = GTK_SCROLLED_WINDOW (widget);
1506   bin = GTK_BIN (scrolled_window);
1507   priv = scrolled_window->priv;
1508
1509   /* Get possible scrollbar dimensions */
1510   sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
1511   gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
1512   gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
1513
1514   style = gtk_widget_get_style (widget);
1515   gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
1516
1517   gtk_widget_set_allocation (widget, allocation);
1518
1519   if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
1520     priv->hscrollbar_visible = TRUE;
1521   else if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
1522     priv->hscrollbar_visible = FALSE;
1523   if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
1524     priv->vscrollbar_visible = TRUE;
1525   else if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
1526     priv->vscrollbar_visible = FALSE;
1527
1528   child = gtk_bin_get_child (bin);
1529   if (child && gtk_widget_get_visible (child))
1530     {
1531       gint child_min_width;
1532       gint child_min_height;
1533       gboolean previous_hvis;
1534       gboolean previous_vvis;
1535       guint count = 0;
1536
1537       /* In the case that both scrollbars are visible in the previous round,
1538        * we dont do our guess-work before hand because it's possible some
1539        * infinite recursion was detected (leave it up to the child scrollable
1540        * widget in this case to drive the scrollbar visibility completely 
1541        * with the adjustment values).
1542        */
1543       if (!priv->vscrollbar_visible || !priv->hscrollbar_visible)
1544         {
1545
1546           /* Determine scrollbar visibility first via hfw apis */
1547           if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1548             {
1549               gtk_widget_get_preferred_width (child, &child_min_width, NULL);
1550               
1551               if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1552                 {
1553                   /* First try without a vertical scrollbar if the content will fit the height
1554                    * given the extra width of the scrollbar */
1555                   gtk_widget_get_preferred_height_for_width (child, allocation->width, 
1556                                                              &child_min_height, NULL);
1557                   
1558                   if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1559                     {
1560                       /* Does the content height fit the allocation height ? */
1561                       priv->vscrollbar_visible = child_min_height > allocation->height;
1562                       
1563                       /* Does the content width fit the allocation with minus a possible scrollbar ? */
1564                       priv->hscrollbar_visible = 
1565                         child_min_width > allocation->width - 
1566                         (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
1567                       
1568                       /* Now that we've guessed the hscrollbar, does the content height fit
1569                        * the possible new allocation height ? */
1570                       priv->vscrollbar_visible = 
1571                         child_min_height > allocation->height - 
1572                         (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
1573                       
1574                       /* Now that we've guessed the vscrollbar, does the content width fit
1575                        * the possible new allocation width ? */
1576                       priv->hscrollbar_visible = 
1577                         child_min_width > allocation->width - 
1578                         (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
1579                     }
1580                   else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
1581                     {
1582                       priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
1583                       priv->vscrollbar_visible = child_min_height > allocation->height - 
1584                         (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
1585                     }
1586                 }
1587               else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
1588                 {
1589                   priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
1590                   
1591                   if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1592                     priv->hscrollbar_visible = 
1593                       child_min_width > allocation->width - 
1594                       (priv->vscrollbar_visible ? 0 : sb_width + sb_spacing);
1595                   else
1596                     priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
1597                 }
1598             } 
1599           else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
1600             {
1601               gtk_widget_get_preferred_height (child, &child_min_height, NULL);
1602               
1603               if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1604                 {
1605                   /* First try without a horizontal scrollbar if the content will fit the width
1606                    * given the extra height of the scrollbar */
1607                   gtk_widget_get_preferred_width_for_height (child, allocation->height, 
1608                                                              &child_min_width, NULL);
1609                   
1610                   if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1611                     {
1612                       /* Does the content width fit the allocation width ? */
1613                       priv->hscrollbar_visible = child_min_width > allocation->width;
1614                       
1615                       /* Does the content height fit the allocation with minus a possible scrollbar ? */
1616                       priv->vscrollbar_visible = 
1617                         child_min_height > allocation->height - 
1618                         (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
1619                       
1620                       /* Now that we've guessed the vscrollbar, does the content width fit
1621                        * the possible new allocation width ? */
1622                       priv->hscrollbar_visible = 
1623                         child_min_width > allocation->width - 
1624                         (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
1625                       
1626                       /* Now that we've guessed the hscrollbar, does the content height fit
1627                        * the possible new allocation height ? */
1628                       priv->vscrollbar_visible = 
1629                         child_min_height > allocation->height - 
1630                         (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
1631                     }
1632                   else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
1633                     {
1634                       priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
1635                       priv->hscrollbar_visible = child_min_width > allocation->width - 
1636                         (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
1637                     }
1638                 }
1639               else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
1640                 {
1641                   priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
1642                   
1643                   if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1644                     priv->vscrollbar_visible = 
1645                       child_min_height > allocation->height - 
1646                       (priv->hscrollbar_visible ? 0 : sb_height + sb_spacing);
1647                   else
1648                     priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
1649                 }
1650             }
1651         }
1652
1653       /* Now after guessing scrollbar visibility; fall back on the allocation loop which 
1654        * observes the adjustments to detect scrollbar visibility and also avoids 
1655        * infinite recursion
1656        */
1657       do
1658         {
1659           gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1660           
1661           child_allocation.x = relative_allocation.x + allocation->x;
1662           child_allocation.y = relative_allocation.y + allocation->y;
1663           child_allocation.width = relative_allocation.width;
1664           child_allocation.height = relative_allocation.height;
1665
1666           previous_hvis = priv->hscrollbar_visible;
1667           previous_vvis = priv->vscrollbar_visible;
1668
1669           gtk_widget_size_allocate (child, &child_allocation);
1670
1671           /* If, after the first iteration, the hscrollbar and the
1672            * vscrollbar flip visiblity, then we need both.
1673            */
1674           if (count &&
1675               previous_hvis != priv->hscrollbar_visible &&
1676               previous_vvis != priv->vscrollbar_visible)
1677             {
1678               priv->hscrollbar_visible = TRUE;
1679               priv->vscrollbar_visible = TRUE;
1680
1681               /* a new resize is already queued at this point,
1682                * so we will immediatedly get reinvoked
1683                */
1684               return;
1685             }
1686           
1687           count++;
1688         }
1689       while (previous_hvis != priv->hscrollbar_visible ||
1690              previous_vvis != priv->vscrollbar_visible);
1691     }
1692   else
1693     {
1694       priv->hscrollbar_visible = priv->hscrollbar_policy == GTK_POLICY_ALWAYS;
1695       priv->vscrollbar_visible = priv->vscrollbar_policy == GTK_POLICY_ALWAYS;
1696       gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1697     }
1698
1699   if (priv->hscrollbar_visible)
1700     {
1701       if (!gtk_widget_get_visible (priv->hscrollbar))
1702         gtk_widget_show (priv->hscrollbar);
1703
1704       child_allocation.x = relative_allocation.x;
1705       if (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1706           priv->real_window_placement == GTK_CORNER_TOP_RIGHT)
1707         child_allocation.y = (relative_allocation.y +
1708                               relative_allocation.height +
1709                               sb_spacing +
1710                               (priv->shadow_type == GTK_SHADOW_NONE ?
1711                                0 : style->ythickness));
1712       else
1713         child_allocation.y = 0;
1714
1715       child_allocation.width = relative_allocation.width;
1716       child_allocation.height = sb_height;
1717       child_allocation.x += allocation->x;
1718       child_allocation.y += allocation->y;
1719
1720       if (priv->shadow_type != GTK_SHADOW_NONE)
1721         {
1722           if (!scrollbars_within_bevel)
1723             {
1724               child_allocation.x -= style->xthickness;
1725               child_allocation.width += 2 * style->xthickness;
1726             }
1727           else if (GTK_CORNER_TOP_RIGHT == priv->real_window_placement ||
1728                    GTK_CORNER_TOP_LEFT == priv->real_window_placement)
1729             {
1730               child_allocation.y -= style->ythickness;
1731             }
1732           else
1733             {
1734               child_allocation.y += style->ythickness;
1735             }
1736         }
1737
1738       gtk_widget_size_allocate (priv->hscrollbar, &child_allocation);
1739     }
1740   else if (gtk_widget_get_visible (priv->hscrollbar))
1741     gtk_widget_hide (priv->hscrollbar);
1742
1743   if (priv->vscrollbar_visible)
1744     {
1745       if (!gtk_widget_get_visible (priv->vscrollbar))
1746         gtk_widget_show (priv->vscrollbar);
1747
1748       if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL && 
1749            (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
1750             priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
1751           (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR && 
1752            (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1753             priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
1754         child_allocation.x = (relative_allocation.x +
1755                               relative_allocation.width +
1756                               sb_spacing +
1757                               (priv->shadow_type == GTK_SHADOW_NONE ?
1758                                0 : style->xthickness));
1759       else
1760         child_allocation.x = 0;
1761
1762       child_allocation.y = relative_allocation.y;
1763       child_allocation.width = sb_width;
1764       child_allocation.height = relative_allocation.height;
1765       child_allocation.x += allocation->x;
1766       child_allocation.y += allocation->y;
1767
1768       if (priv->shadow_type != GTK_SHADOW_NONE)
1769         {
1770           if (!scrollbars_within_bevel)
1771             {
1772               child_allocation.y -= style->ythickness;
1773               child_allocation.height += 2 * style->ythickness;
1774             }
1775           else if (GTK_CORNER_BOTTOM_LEFT == priv->real_window_placement ||
1776                    GTK_CORNER_TOP_LEFT == priv->real_window_placement)
1777             {
1778               child_allocation.x -= style->xthickness;
1779             }
1780           else
1781             {
1782               child_allocation.x += style->xthickness;
1783             }
1784         }
1785
1786       gtk_widget_size_allocate (priv->vscrollbar, &child_allocation);
1787     }
1788   else if (gtk_widget_get_visible (priv->vscrollbar))
1789     gtk_widget_hide (priv->vscrollbar);
1790 }
1791
1792 static gboolean
1793 gtk_scrolled_window_scroll_event (GtkWidget      *widget,
1794                                   GdkEventScroll *event)
1795 {
1796   GtkScrolledWindowPrivate *priv;
1797   GtkScrolledWindow *scrolled_window;
1798   GtkWidget *range;
1799
1800   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE);
1801   g_return_val_if_fail (event != NULL, FALSE);  
1802
1803   scrolled_window = GTK_SCROLLED_WINDOW (widget);
1804   priv = scrolled_window->priv;
1805
1806   if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
1807     range = priv->vscrollbar;
1808   else
1809     range = priv->hscrollbar;
1810
1811   if (range && gtk_widget_get_visible (range))
1812     {
1813       GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (range));
1814       gdouble delta;
1815
1816       delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event->direction);
1817
1818       gtk_adjustment_set_value (adj, adj->value + delta);
1819
1820       return TRUE;
1821     }
1822
1823   return FALSE;
1824 }
1825
1826 static gboolean
1827 gtk_scrolled_window_focus (GtkWidget        *widget,
1828                            GtkDirectionType  direction)
1829 {
1830   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1831   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1832   GtkWidget *child;
1833   gboolean had_focus_child;
1834
1835   had_focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget)) != NULL;
1836
1837   if (priv->focus_out)
1838     {
1839       priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
1840       return FALSE;
1841     }
1842   
1843   if (gtk_widget_is_focus (widget))
1844     return FALSE;
1845
1846   /* We only put the scrolled window itself in the focus chain if it
1847    * isn't possible to focus any children.
1848    */
1849   child = gtk_bin_get_child (GTK_BIN (widget));
1850   if (child)
1851     {
1852       if (gtk_widget_child_focus (child, direction))
1853         return TRUE;
1854     }
1855
1856   if (!had_focus_child && gtk_widget_get_can_focus (widget))
1857     {
1858       gtk_widget_grab_focus (widget);
1859       return TRUE;
1860     }
1861   else
1862     return FALSE;
1863 }
1864
1865 static void
1866 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
1867                                         gpointer       data)
1868 {
1869   GtkScrolledWindowPrivate *priv;;
1870   GtkScrolledWindow *scrolled_window;
1871
1872   g_return_if_fail (adjustment != NULL);
1873   g_return_if_fail (data != NULL);
1874
1875   scrolled_window = GTK_SCROLLED_WINDOW (data);
1876   priv = scrolled_window->priv;
1877
1878   if (priv->hscrollbar &&
1879       adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
1880     {
1881       if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1882         {
1883           gboolean visible;
1884
1885           visible = priv->hscrollbar_visible;
1886           priv->hscrollbar_visible = (adjustment->upper - adjustment->lower >
1887                                               adjustment->page_size);
1888           if (priv->hscrollbar_visible != visible)
1889             gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
1890         }
1891     }
1892   else if (priv->vscrollbar &&
1893            adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
1894     {
1895       if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1896         {
1897           gboolean visible;
1898
1899           visible = priv->vscrollbar_visible;
1900           priv->vscrollbar_visible = (adjustment->upper - adjustment->lower >
1901                                               adjustment->page_size);
1902           if (priv->vscrollbar_visible != visible)
1903             gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
1904         }
1905     }
1906 }
1907
1908 static void
1909 gtk_scrolled_window_add (GtkContainer *container,
1910                          GtkWidget    *child)
1911 {
1912   GtkScrolledWindowPrivate *priv;
1913   GtkScrolledWindow *scrolled_window;
1914   GtkBin *bin;
1915   GtkWidget *child_widget;
1916   GtkAdjustment *hadj, *vadj;
1917
1918   bin = GTK_BIN (container);
1919   child_widget = gtk_bin_get_child (bin);
1920   g_return_if_fail (child_widget == NULL);
1921
1922   scrolled_window = GTK_SCROLLED_WINDOW (container);
1923   priv = scrolled_window->priv;
1924
1925   _gtk_bin_set_child (bin, child);
1926   gtk_widget_set_parent (child, GTK_WIDGET (bin));
1927
1928   hadj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->priv->hscrollbar));
1929   vadj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->priv->vscrollbar));
1930
1931   if (GTK_IS_SCROLLABLE (child))
1932     g_object_set (child, "hadjustment", hadj, "vadjustment", vadj, NULL);
1933   else
1934     g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
1935                "use gtk_scrolled_window_add_with_viewport() instead");
1936 }
1937
1938 static void
1939 gtk_scrolled_window_remove (GtkContainer *container,
1940                             GtkWidget    *child)
1941 {
1942   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
1943   g_return_if_fail (child != NULL);
1944   g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
1945
1946   g_object_set (child, "hadjustment", NULL, "vadjustment", NULL, NULL);
1947
1948   /* chain parent class handler to remove child */
1949   GTK_CONTAINER_CLASS (gtk_scrolled_window_parent_class)->remove (container, child);
1950 }
1951
1952 /**
1953  * gtk_scrolled_window_add_with_viewport:
1954  * @scrolled_window: a #GtkScrolledWindow
1955  * @child: the widget you want to scroll
1956  *
1957  * Used to add children without native scrolling capabilities. This
1958  * is simply a convenience function; it is equivalent to adding the
1959  * unscrollable child to a viewport, then adding the viewport to the
1960  * scrolled window. If a child has native scrolling, use
1961  * gtk_container_add() instead of this function.
1962  *
1963  * The viewport scrolls the child by moving its #GdkWindow, and takes
1964  * the size of the child to be the size of its toplevel #GdkWindow. 
1965  * This will be very wrong for most widgets that support native scrolling;
1966  * for example, if you add a widget such as #GtkTreeView with a viewport,
1967  * the whole widget will scroll, including the column headings. Thus, 
1968  * widgets with native scrolling support should not be used with the 
1969  * #GtkViewport proxy.
1970  *
1971  * A widget supports scrolling natively if it implements the
1972  * #GtkScrollable interface.
1973  */
1974 void
1975 gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
1976                                        GtkWidget         *child)
1977 {
1978   GtkBin *bin;
1979   GtkWidget *viewport;
1980   GtkWidget *child_widget;
1981
1982   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1983   g_return_if_fail (GTK_IS_WIDGET (child));
1984   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
1985
1986   bin = GTK_BIN (scrolled_window);
1987   child_widget = gtk_bin_get_child (bin);
1988
1989   if (child_widget)
1990     {
1991       g_return_if_fail (GTK_IS_VIEWPORT (child_widget));
1992       g_return_if_fail (gtk_bin_get_child (GTK_BIN (child_widget)) == NULL);
1993
1994       viewport = child_widget;
1995     }
1996   else
1997     {
1998       viewport =
1999         gtk_viewport_new (gtk_scrolled_window_get_hadjustment (scrolled_window),
2000                           gtk_scrolled_window_get_vadjustment (scrolled_window));
2001       gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
2002     }
2003
2004   gtk_widget_show (viewport);
2005   gtk_container_add (GTK_CONTAINER (viewport), child);
2006 }
2007
2008 /*
2009  * _gtk_scrolled_window_get_spacing:
2010  * @scrolled_window: a scrolled window
2011  * 
2012  * Gets the spacing between the scrolled window's scrollbars and
2013  * the scrolled widget. Used by GtkCombo
2014  * 
2015  * Return value: the spacing, in pixels.
2016  */
2017 gint
2018 _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
2019 {
2020   GtkScrolledWindowClass *class;
2021     
2022   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
2023
2024   class = GTK_SCROLLED_WINDOW_GET_CLASS (scrolled_window);
2025
2026   if (class->scrollbar_spacing >= 0)
2027     return class->scrollbar_spacing;
2028   else
2029     {
2030       gint scrollbar_spacing;
2031       
2032       gtk_widget_style_get (GTK_WIDGET (scrolled_window),
2033                             "scrollbar-spacing", &scrollbar_spacing,
2034                             NULL);
2035
2036       return scrollbar_spacing;
2037     }
2038 }
2039
2040
2041 static void
2042 gtk_scrolled_window_get_preferred_size (GtkWidget      *widget,
2043                                         GtkOrientation  orientation,
2044                                         gint           *minimum_size,
2045                                         gint           *natural_size)
2046 {
2047   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2048   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2049   GtkBin *bin = GTK_BIN (scrolled_window);
2050   gint extra_width;
2051   gint extra_height;
2052   gint scrollbar_spacing;
2053   GtkRequisition hscrollbar_requisition;
2054   GtkRequisition vscrollbar_requisition;
2055   GtkRequisition minimum_req, natural_req;
2056   GtkStyle *style;
2057   GtkWidget *child;
2058   gint min_child_size, nat_child_size;
2059
2060   scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
2061
2062   extra_width = 0;
2063   extra_height = 0;
2064   minimum_req.width = 0;
2065   minimum_req.height = 0;
2066   natural_req.width = 0;
2067   natural_req.height = 0;
2068
2069   gtk_widget_get_preferred_size (priv->hscrollbar,
2070                                  &hscrollbar_requisition, NULL);
2071   gtk_widget_get_preferred_size (priv->vscrollbar,
2072                                  &vscrollbar_requisition, NULL);
2073
2074   child = gtk_bin_get_child (bin);
2075   if (child && gtk_widget_get_visible (child))
2076     {
2077       if (orientation == GTK_ORIENTATION_HORIZONTAL)
2078         {
2079           gtk_widget_get_preferred_width (child,
2080                                           &min_child_size,
2081                                           &nat_child_size);
2082
2083           if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
2084             {
2085               minimum_req.width += min_child_size;
2086               natural_req.width += nat_child_size;
2087             }
2088           else
2089             {
2090               gint min_content_width = priv->min_content_width;
2091
2092               if (min_content_width >= 0)
2093                 {
2094                   minimum_req.width = MAX (minimum_req.width, min_content_width);
2095                   natural_req.width = MAX (natural_req.width, min_content_width);
2096                   extra_width = -1;
2097                 }
2098               else
2099                 {
2100                   minimum_req.width += vscrollbar_requisition.width;
2101                   natural_req.width += vscrollbar_requisition.width;
2102                 }
2103             }
2104         }
2105       else /* GTK_ORIENTATION_VERTICAL */
2106         {
2107           gtk_widget_get_preferred_height (child,
2108                                            &min_child_size,
2109                                            &nat_child_size);
2110
2111           if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
2112             {
2113               minimum_req.height += min_child_size;
2114               natural_req.height += nat_child_size;
2115             }
2116           else
2117             {
2118               gint min_content_height = priv->min_content_height;
2119
2120               if (min_content_height >= 0)
2121                 {
2122                   minimum_req.height = MAX (minimum_req.height, min_content_height);
2123                   natural_req.height = MAX (natural_req.height, min_content_height);
2124                   extra_height = -1;
2125                 }
2126               else
2127                 {
2128                   minimum_req.height += vscrollbar_requisition.height;
2129                   natural_req.height += vscrollbar_requisition.height;
2130                 }
2131             }
2132         }
2133     }
2134
2135   if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
2136       priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
2137     {
2138       minimum_req.width = MAX (minimum_req.width, hscrollbar_requisition.width);
2139       natural_req.width = MAX (natural_req.width, hscrollbar_requisition.width);
2140       if (!extra_height || priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
2141         extra_height = scrollbar_spacing + hscrollbar_requisition.height;
2142     }
2143
2144   if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
2145       priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
2146     {
2147       minimum_req.height = MAX (minimum_req.height, vscrollbar_requisition.height);
2148       natural_req.height = MAX (natural_req.height, vscrollbar_requisition.height);
2149       if (!extra_width || priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
2150         extra_width = scrollbar_spacing + vscrollbar_requisition.width;
2151     }
2152
2153   minimum_req.width  += MAX (0, extra_width);
2154   minimum_req.height += MAX (0, extra_height);
2155   natural_req.width  += MAX (0, extra_width);
2156   natural_req.height += MAX (0, extra_height);
2157
2158   if (priv->shadow_type != GTK_SHADOW_NONE)
2159     {
2160       style = gtk_widget_get_style (GTK_WIDGET (widget));
2161       minimum_req.width += 2 * style->xthickness;
2162       minimum_req.height += 2 * style->ythickness;
2163       natural_req.width += 2 * style->xthickness;
2164       natural_req.height += 2 * style->ythickness;
2165     }
2166
2167   if (orientation == GTK_ORIENTATION_HORIZONTAL)
2168     {
2169       if (minimum_size)
2170         *minimum_size = minimum_req.width;
2171       if (natural_size)
2172         *natural_size = natural_req.width;
2173     }
2174   else
2175     {
2176       if (minimum_size)
2177         *minimum_size = minimum_req.height;
2178       if (natural_size)
2179         *natural_size = natural_req.height;
2180     }
2181 }
2182
2183 static void     
2184 gtk_scrolled_window_get_preferred_width (GtkWidget *widget,
2185                                          gint      *minimum_size,
2186                                          gint      *natural_size)
2187 {
2188   gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
2189 }
2190
2191 static void
2192 gtk_scrolled_window_get_preferred_height (GtkWidget *widget,
2193                                           gint      *minimum_size,
2194                                           gint      *natural_size)
2195 {  
2196   gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
2197 }
2198
2199 static void
2200 gtk_scrolled_window_get_preferred_height_for_width (GtkWidget *widget,
2201                                                     gint       width,
2202                                                     gint      *minimum_height,
2203                                                     gint      *natural_height)
2204 {
2205   g_return_if_fail (GTK_IS_WIDGET (widget));
2206
2207   GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
2208 }
2209
2210 static void
2211 gtk_scrolled_window_get_preferred_width_for_height (GtkWidget *widget,
2212                                                     gint       height,
2213                                                     gint      *minimum_width,
2214                                                     gint      *natural_width)
2215 {
2216   g_return_if_fail (GTK_IS_WIDGET (widget));
2217
2218   GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
2219 }
2220
2221 gint
2222 gtk_scrolled_window_get_min_content_width (GtkScrolledWindow *scrolled_window)
2223 {
2224   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
2225
2226   return scrolled_window->priv->min_content_width;
2227 }
2228
2229 void
2230 gtk_scrolled_window_set_min_content_width (GtkScrolledWindow *scrolled_window,
2231                                            gint               min_content_width)
2232 {
2233   GtkScrolledWindowPrivate *priv;
2234
2235   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2236
2237   priv = scrolled_window->priv;
2238
2239   if (priv->min_content_width != min_content_width)
2240     {
2241       priv->min_content_width = min_content_width;
2242
2243       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2244
2245       g_object_notify (G_OBJECT (scrolled_window), "min-content-width");
2246     }
2247 }
2248
2249 gint
2250 gtk_scrolled_window_get_min_content_height (GtkScrolledWindow *scrolled_window)
2251 {
2252   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
2253
2254   return scrolled_window->priv->min_content_height;
2255 }
2256
2257 void
2258 gtk_scrolled_window_set_min_content_height (GtkScrolledWindow *scrolled_window,
2259                                             gint               min_content_height)
2260 {
2261   GtkScrolledWindowPrivate *priv;
2262
2263   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2264
2265   priv = scrolled_window->priv;
2266
2267   if (priv->min_content_height != min_content_height)
2268     {
2269       priv->min_content_height = min_content_height;
2270
2271       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2272
2273       g_object_notify (G_OBJECT (scrolled_window), "min-content-height");
2274     }
2275 }
2276
2277 GtkScrollablePolicy
2278 gtk_scrolled_window_get_hscroll_policy (GtkScrolledWindow *scrolled_window)
2279 {
2280   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
2281
2282   return scrolled_window->priv->hscroll_policy;
2283 }
2284
2285 void
2286 gtk_scrolled_window_set_hscroll_policy (GtkScrolledWindow   *scrolled_window,
2287                                         GtkScrollablePolicy  policy)
2288 {
2289   GtkScrolledWindowPrivate *priv;
2290
2291   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2292
2293   priv = scrolled_window->priv;
2294
2295   if (priv->hscroll_policy != policy)
2296     {
2297       priv->hscroll_policy = policy;
2298
2299       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2300
2301       g_object_notify (G_OBJECT (scrolled_window), "hscroll-policy");
2302     }
2303 }
2304
2305 GtkScrollablePolicy
2306 gtk_scrolled_window_get_vscroll_policy (GtkScrolledWindow *scrolled_window)
2307 {
2308   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
2309
2310   return scrolled_window->priv->vscroll_policy;
2311 }
2312
2313 void
2314 gtk_scrolled_window_set_vscroll_policy (GtkScrolledWindow   *scrolled_window,
2315                                         GtkScrollablePolicy  policy)
2316 {
2317   GtkScrolledWindowPrivate *priv;
2318
2319   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2320
2321   priv = scrolled_window->priv;
2322
2323   if (priv->vscroll_policy != policy)
2324     {
2325       priv->vscroll_policy = policy;
2326
2327       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2328
2329       g_object_notify (G_OBJECT (scrolled_window), "vscroll-policy");
2330     }
2331 }