]> Pileus Git - ~andy/gtk/blob - gtk/gtkscrolledwindow.c
scrolled-window: fix rendering of scrolled window background
[~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, see <http://www.gnu.org/licenses/>.
16  */
17
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24
25 #include "config.h"
26
27 #include "gtkscrolledwindow.h"
28
29 #include "gtkadjustment.h"
30 #include "gtkbindings.h"
31 #include "gtkdnd.h"
32 #include "gtkintl.h"
33 #include "gtkmain.h"
34 #include "gtkmarshalers.h"
35 #include "gtkprivate.h"
36 #include "gtkscrollable.h"
37 #include "gtkscrollbar.h"
38 #include "gtktypebuiltins.h"
39 #include "gtkviewport.h"
40 #include "gtkwidgetprivate.h"
41 #include "gtkwindow.h"
42 #include "a11y/gtkscrolledwindowaccessible.h"
43
44 #include <math.h>
45
46 /**
47  * SECTION:gtkscrolledwindow
48  * @Short_description: Adds scrollbars to its child widget
49  * @Title: GtkScrolledWindow
50  * @See_also: #GtkScrollable, #GtkViewport, #GtkAdjustment
51  *
52  * #GtkScrolledWindow is a #GtkBin subclass: it's a container
53  * the accepts a single child widget. #GtkScrolledWindow adds scrollbars
54  * to the child widget and optionally draws a beveled frame around the
55  * child widget.
56  *
57  * The scrolled window can work in two ways. Some widgets have native
58  * scrolling support; these widgets implement the #GtkScrollable interface.
59  * Widgets with native scroll support include #GtkTreeView, #GtkTextView,
60  * and #GtkLayout.
61  *
62  * For widgets that lack native scrolling support, the #GtkViewport
63  * widget acts as an adaptor class, implementing scrollability for child
64  * widgets that lack their own scrolling capabilities. Use #GtkViewport
65  * to scroll child widgets such as #GtkGrid, #GtkBox, and so on.
66  *
67  * If a widget has native scrolling abilities, it can be added to the
68  * #GtkScrolledWindow with gtk_container_add(). If a widget does not, you
69  * must first add the widget to a #GtkViewport, then add the #GtkViewport
70  * to the scrolled window. The convenience function
71  * gtk_scrolled_window_add_with_viewport() does exactly this, so you can
72  * ignore the presence of the viewport.
73  *
74  * The position of the scrollbars is controlled by the scroll
75  * adjustments. See #GtkAdjustment for the fields in an adjustment - for
76  * #GtkScrollbar, used by #GtkScrolledWindow, the "value" field
77  * represents the position of the scrollbar, which must be between the
78  * "lower" field and "upper - page_size." The "page_size" field
79  * represents the size of the visible scrollable area. The
80  * "step_increment" and "page_increment" fields are used when the user
81  * asks to step down (using the small stepper arrows) or page down (using
82  * for example the PageDown key).
83  *
84  * If a #GtkScrolledWindow doesn't behave quite as you would like, or
85  * doesn't have exactly the right layout, it's very possible to set up
86  * your own scrolling with #GtkScrollbar and for example a #GtkGrid.
87  */
88
89
90 /* scrolled window policy and size requisition handling:
91  *
92  * gtk size requisition works as follows:
93  *   a widget upon size-request reports the width and height that it finds
94  *   to be best suited to display its contents, including children.
95  *   the width and/or height reported from a widget upon size requisition
96  *   may be overidden by the user by specifying a width and/or height
97  *   other than 0 through gtk_widget_set_size_request().
98  *
99  * a scrolled window needs (for implementing all three policy types) to
100  * request its width and height based on two different rationales.
101  * 1)   the user wants the scrolled window to just fit into the space
102  *      that it gets allocated for a specifc dimension.
103  * 1.1) this does not apply if the user specified a concrete value
104  *      value for that specific dimension by either specifying usize for the
105  *      scrolled window or for its child.
106  * 2)   the user wants the scrolled window to take as much space up as
107  *      is desired by the child for a specifc dimension (i.e. POLICY_NEVER).
108  *
109  * also, kinda obvious:
110  * 3)   a user would certainly not have choosen a scrolled window as a container
111  *      for the child, if the resulting allocation takes up more space than the
112  *      child would have allocated without the scrolled window.
113  *
114  * conclusions:
115  * A) from 1) follows: the scrolled window shouldn't request more space for a
116  *    specifc dimension than is required at minimum.
117  * B) from 1.1) follows: the requisition may be overidden by usize of the scrolled
118  *    window (done automatically) or by usize of the child (needs to be checked).
119  * C) from 2) follows: for POLICY_NEVER, the scrolled window simply reports the
120  *    child's dimension.
121  * D) from 3) follows: the scrolled window child's minimum width and minimum height
122  *    under A) at least correspond to the space taken up by its scrollbars.
123  */
124
125 #define DEFAULT_SCROLLBAR_SPACING  3
126 #define TOUCH_BYPASS_CAPTURED_THRESHOLD 30
127
128 /* Kinetic scrolling */
129 #define FRAME_INTERVAL (1000 / 60)
130 #define MAX_OVERSHOOT_DISTANCE 50
131 #define FRICTION_DECELERATION 0.003
132 #define OVERSHOOT_INVERSE_ACCELERATION 0.003
133 #define RELEASE_EVENT_TIMEOUT 1000
134
135 struct _GtkScrolledWindowPrivate
136 {
137   GtkWidget     *hscrollbar;
138   GtkWidget     *vscrollbar;
139
140   GtkCornerType  real_window_placement;
141   guint16  shadow_type;
142
143   guint    window_placement_set   : 1;
144   guint    hscrollbar_policy      : 2;
145   guint    vscrollbar_policy      : 2;
146   guint    hscrollbar_visible     : 1;
147   guint    vscrollbar_visible     : 1;
148   guint    window_placement       : 2;
149   guint    focus_out              : 1; /* Flag used by ::move-focus-out implementation */
150
151   gint     min_content_width;
152   gint     min_content_height;
153
154   /* Kinetic scrolling */
155   GdkEvent              *button_press_event;
156   GdkWindow             *overshoot_window;
157   GdkDevice             *drag_device;
158   guint                  kinetic_scrolling         : 1;
159   guint                  capture_button_press      : 1;
160   guint                  in_drag                   : 1;
161   guint                  last_button_event_valid   : 1;
162
163   guint                  release_timeout_id;
164   guint                  deceleration_id;
165
166   gdouble                last_button_event_x_root;
167   gdouble                last_button_event_y_root;
168
169   gdouble                last_motion_event_x_root;
170   gdouble                last_motion_event_y_root;
171   guint32                last_motion_event_time;
172
173   gdouble                x_velocity;
174   gdouble                y_velocity;
175
176   gdouble                unclamped_hadj_value;
177   gdouble                unclamped_vadj_value;
178 };
179
180 typedef struct
181 {
182   GtkScrolledWindow     *scrolled_window;
183   gint64                 last_deceleration_time;
184
185   gdouble                x_velocity;
186   gdouble                y_velocity;
187   gdouble                vel_cosine;
188   gdouble                vel_sine;
189 } KineticScrollData;
190
191 enum {
192   PROP_0,
193   PROP_HADJUSTMENT,
194   PROP_VADJUSTMENT,
195   PROP_HSCROLLBAR_POLICY,
196   PROP_VSCROLLBAR_POLICY,
197   PROP_WINDOW_PLACEMENT,
198   PROP_WINDOW_PLACEMENT_SET,
199   PROP_SHADOW_TYPE,
200   PROP_MIN_CONTENT_WIDTH,
201   PROP_MIN_CONTENT_HEIGHT,
202   PROP_KINETIC_SCROLLING
203 };
204
205 /* Signals */
206 enum
207 {
208   SCROLL_CHILD,
209   MOVE_FOCUS_OUT,
210   LAST_SIGNAL
211 };
212
213 static void     gtk_scrolled_window_set_property       (GObject           *object,
214                                                         guint              prop_id,
215                                                         const GValue      *value,
216                                                         GParamSpec        *pspec);
217 static void     gtk_scrolled_window_get_property       (GObject           *object,
218                                                         guint              prop_id,
219                                                         GValue            *value,
220                                                         GParamSpec        *pspec);
221
222 static void     gtk_scrolled_window_destroy            (GtkWidget         *widget);
223 static void     gtk_scrolled_window_screen_changed     (GtkWidget         *widget,
224                                                         GdkScreen         *previous_screen);
225 static gboolean gtk_scrolled_window_draw               (GtkWidget         *widget,
226                                                         cairo_t           *cr);
227 static void     gtk_scrolled_window_size_allocate      (GtkWidget         *widget,
228                                                         GtkAllocation     *allocation);
229 static gboolean gtk_scrolled_window_scroll_event       (GtkWidget         *widget,
230                                                         GdkEventScroll    *event);
231 static gboolean gtk_scrolled_window_captured_event     (GtkWidget         *widget,
232                                                         GdkEvent          *event);
233 static gboolean gtk_scrolled_window_focus              (GtkWidget         *widget,
234                                                         GtkDirectionType   direction);
235 static void     gtk_scrolled_window_add                (GtkContainer      *container,
236                                                         GtkWidget         *widget);
237 static void     gtk_scrolled_window_remove             (GtkContainer      *container,
238                                                         GtkWidget         *widget);
239 static void     gtk_scrolled_window_forall             (GtkContainer      *container,
240                                                         gboolean           include_internals,
241                                                         GtkCallback        callback,
242                                                         gpointer           callback_data);
243 static gboolean gtk_scrolled_window_scroll_child       (GtkScrolledWindow *scrolled_window,
244                                                         GtkScrollType      scroll,
245                                                         gboolean           horizontal);
246 static void     gtk_scrolled_window_move_focus_out     (GtkScrolledWindow *scrolled_window,
247                                                         GtkDirectionType   direction_type);
248
249 static void     gtk_scrolled_window_relative_allocation(GtkWidget         *widget,
250                                                         GtkAllocation     *allocation);
251 static void     gtk_scrolled_window_adjustment_changed (GtkAdjustment     *adjustment,
252                                                         gpointer           data);
253 static void     gtk_scrolled_window_adjustment_value_changed (GtkAdjustment     *adjustment,
254                                                               gpointer           data);
255
256 static void  gtk_scrolled_window_update_real_placement (GtkScrolledWindow *scrolled_window);
257
258 static void  gtk_scrolled_window_get_preferred_width   (GtkWidget           *widget,
259                                                         gint                *minimum_size,
260                                                         gint                *natural_size);
261 static void  gtk_scrolled_window_get_preferred_height  (GtkWidget           *widget,
262                                                         gint                *minimum_size,
263                                                         gint                *natural_size);
264 static void  gtk_scrolled_window_get_preferred_height_for_width  (GtkWidget           *layout,
265                                                         gint                 width,
266                                                         gint                *minimum_height,
267                                                         gint                *natural_height);
268 static void  gtk_scrolled_window_get_preferred_width_for_height  (GtkWidget           *layout,
269                                                         gint                 width,
270                                                         gint                *minimum_height,
271                                                         gint                *natural_height);
272
273 static void  gtk_scrolled_window_realize               (GtkWidget           *widget);
274 static void  gtk_scrolled_window_unrealize             (GtkWidget           *widget);
275 static void  gtk_scrolled_window_map                   (GtkWidget           *widget);
276 static void  gtk_scrolled_window_unmap                 (GtkWidget           *widget);
277 static void  gtk_scrolled_window_state_flags_changed   (GtkWidget           *widget,
278                                                         GtkStateFlags        previous_state);
279 static void  gtk_scrolled_window_style_updated         (GtkWidget           *widget);
280
281 static void  gtk_scrolled_window_grab_notify           (GtkWidget           *widget,
282                                                         gboolean             was_grabbed);
283
284 static gboolean _gtk_scrolled_window_set_adjustment_value      (GtkScrolledWindow *scrolled_window,
285                                                                 GtkAdjustment     *adjustment,
286                                                                 gdouble            value,
287                                                                 gboolean           allow_overshooting,
288                                                                 gboolean           snap_to_border);
289
290 static guint signals[LAST_SIGNAL] = {0};
291
292 G_DEFINE_TYPE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN)
293
294
295 static void
296 add_scroll_binding (GtkBindingSet  *binding_set,
297                     guint           keyval,
298                     GdkModifierType mask,
299                     GtkScrollType   scroll,
300                     gboolean        horizontal)
301 {
302   guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
303   
304   gtk_binding_entry_add_signal (binding_set, keyval, mask,
305                                 "scroll-child", 2,
306                                 GTK_TYPE_SCROLL_TYPE, scroll,
307                                 G_TYPE_BOOLEAN, horizontal);
308   gtk_binding_entry_add_signal (binding_set, keypad_keyval, mask,
309                                 "scroll-child", 2,
310                                 GTK_TYPE_SCROLL_TYPE, scroll,
311                                 G_TYPE_BOOLEAN, horizontal);
312 }
313
314 static void
315 add_tab_bindings (GtkBindingSet    *binding_set,
316                   GdkModifierType   modifiers,
317                   GtkDirectionType  direction)
318 {
319   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
320                                 "move-focus-out", 1,
321                                 GTK_TYPE_DIRECTION_TYPE, direction);
322   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
323                                 "move-focus-out", 1,
324                                 GTK_TYPE_DIRECTION_TYPE, direction);
325 }
326
327 static void
328 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
329 {
330   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
331   GtkWidgetClass *widget_class;
332   GtkContainerClass *container_class;
333   GtkBindingSet *binding_set;
334
335   widget_class = (GtkWidgetClass*) class;
336   container_class = (GtkContainerClass*) class;
337
338   gobject_class->set_property = gtk_scrolled_window_set_property;
339   gobject_class->get_property = gtk_scrolled_window_get_property;
340
341   widget_class->destroy = gtk_scrolled_window_destroy;
342   widget_class->screen_changed = gtk_scrolled_window_screen_changed;
343   widget_class->draw = gtk_scrolled_window_draw;
344   widget_class->size_allocate = gtk_scrolled_window_size_allocate;
345   widget_class->scroll_event = gtk_scrolled_window_scroll_event;
346   widget_class->focus = gtk_scrolled_window_focus;
347   widget_class->get_preferred_width = gtk_scrolled_window_get_preferred_width;
348   widget_class->get_preferred_height = gtk_scrolled_window_get_preferred_height;
349   widget_class->get_preferred_height_for_width = gtk_scrolled_window_get_preferred_height_for_width;
350   widget_class->get_preferred_width_for_height = gtk_scrolled_window_get_preferred_width_for_height;
351   widget_class->realize = gtk_scrolled_window_realize;
352   widget_class->unrealize = gtk_scrolled_window_unrealize;
353   widget_class->map = gtk_scrolled_window_map;
354   widget_class->unmap = gtk_scrolled_window_unmap;
355   widget_class->state_flags_changed = gtk_scrolled_window_state_flags_changed;
356   widget_class->style_updated = gtk_scrolled_window_style_updated;
357   widget_class->grab_notify = gtk_scrolled_window_grab_notify;
358
359   container_class->add = gtk_scrolled_window_add;
360   container_class->remove = gtk_scrolled_window_remove;
361   container_class->forall = gtk_scrolled_window_forall;
362   gtk_container_class_handle_border_width (container_class);
363
364   class->scrollbar_spacing = -1;
365
366   class->scroll_child = gtk_scrolled_window_scroll_child;
367   class->move_focus_out = gtk_scrolled_window_move_focus_out;
368   
369   g_object_class_install_property (gobject_class,
370                                    PROP_HADJUSTMENT,
371                                    g_param_spec_object ("hadjustment",
372                                                         P_("Horizontal Adjustment"),
373                                                         P_("The GtkAdjustment for the horizontal position"),
374                                                         GTK_TYPE_ADJUSTMENT,
375                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
376   g_object_class_install_property (gobject_class,
377                                    PROP_VADJUSTMENT,
378                                    g_param_spec_object ("vadjustment",
379                                                         P_("Vertical Adjustment"),
380                                                         P_("The GtkAdjustment for the vertical position"),
381                                                         GTK_TYPE_ADJUSTMENT,
382                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
383   g_object_class_install_property (gobject_class,
384                                    PROP_HSCROLLBAR_POLICY,
385                                    g_param_spec_enum ("hscrollbar-policy",
386                                                       P_("Horizontal Scrollbar Policy"),
387                                                       P_("When the horizontal scrollbar is displayed"),
388                                                       GTK_TYPE_POLICY_TYPE,
389                                                       GTK_POLICY_AUTOMATIC,
390                                                       GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
391   g_object_class_install_property (gobject_class,
392                                    PROP_VSCROLLBAR_POLICY,
393                                    g_param_spec_enum ("vscrollbar-policy",
394                                                       P_("Vertical Scrollbar Policy"),
395                                                       P_("When the vertical scrollbar is displayed"),
396                                                       GTK_TYPE_POLICY_TYPE,
397                                                       GTK_POLICY_AUTOMATIC,
398                                                       GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
399
400   g_object_class_install_property (gobject_class,
401                                    PROP_WINDOW_PLACEMENT,
402                                    g_param_spec_enum ("window-placement",
403                                                       P_("Window Placement"),
404                                                       P_("Where the contents are located with respect to the scrollbars. This property only takes effect if \"window-placement-set\" is TRUE."),
405                                                       GTK_TYPE_CORNER_TYPE,
406                                                       GTK_CORNER_TOP_LEFT,
407                                                       GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
408   
409   /**
410    * GtkScrolledWindow:window-placement-set:
411    *
412    * Whether "window-placement" should be used to determine the location 
413    * of the contents with respect to the scrollbars. Otherwise, the 
414    * "gtk-scrolled-window-placement" setting is used.
415    *
416    * Since: 2.10
417    */
418   g_object_class_install_property (gobject_class,
419                                    PROP_WINDOW_PLACEMENT_SET,
420                                    g_param_spec_boolean ("window-placement-set",
421                                                          P_("Window Placement Set"),
422                                                          P_("Whether \"window-placement\" should be used to determine the location of the contents with respect to the scrollbars."),
423                                                          FALSE,
424                                                          GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
425   g_object_class_install_property (gobject_class,
426                                    PROP_SHADOW_TYPE,
427                                    g_param_spec_enum ("shadow-type",
428                                                       P_("Shadow Type"),
429                                                       P_("Style of bevel around the contents"),
430                                                       GTK_TYPE_SHADOW_TYPE,
431                                                       GTK_SHADOW_NONE,
432                                                       GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
433
434   /**
435    * GtkScrolledWindow:scrollbars-within-bevel:
436    *
437    * Whether to place scrollbars within the scrolled window's bevel.
438    *
439    * Since: 2.12
440    */
441   gtk_widget_class_install_style_property (widget_class,
442                                            g_param_spec_boolean ("scrollbars-within-bevel",
443                                                                  P_("Scrollbars within bevel"),
444                                                                  P_("Place scrollbars within the scrolled window's bevel"),
445                                                                  FALSE,
446                                                                  GTK_PARAM_READABLE));
447
448   gtk_widget_class_install_style_property (widget_class,
449                                            g_param_spec_int ("scrollbar-spacing",
450                                                              P_("Scrollbar spacing"),
451                                                              P_("Number of pixels between the scrollbars and the scrolled window"),
452                                                              0,
453                                                              G_MAXINT,
454                                                              DEFAULT_SCROLLBAR_SPACING,
455                                                              GTK_PARAM_READABLE));
456
457   /**
458    * GtkScrolledWindow:min-content-width:
459    *
460    * The minimum content width of @scrolled_window, or -1 if not set.
461    *
462    * Since: 3.0
463    */
464   g_object_class_install_property (gobject_class,
465                                    PROP_MIN_CONTENT_WIDTH,
466                                    g_param_spec_int ("min-content-width",
467                                                      P_("Minimum Content Width"),
468                                                      P_("The minimum width that the scrolled window will allocate to its content"),
469                                                      -1, G_MAXINT, -1,
470                                                      GTK_PARAM_READWRITE));
471
472   /**
473    * GtkScrolledWindow:min-content-height:
474    *
475    * The minimum content height of @scrolled_window, or -1 if not set.
476    *
477    * Since: 3.0
478    */
479   g_object_class_install_property (gobject_class,
480                                    PROP_MIN_CONTENT_HEIGHT,
481                                    g_param_spec_int ("min-content-height",
482                                                      P_("Minimum Content Height"),
483                                                      P_("The minimum height that the scrolled window will allocate to its content"),
484                                                      -1, G_MAXINT, -1,
485                                                      GTK_PARAM_READWRITE));
486
487   /**
488    * GtkScrolledWindow:kinetic-scrolling:
489    *
490    * The kinetic scrolling behavior flags. Kinetic scrolling
491    * only applies to devices with source %GDK_SOURCE_TOUCHSCREEN
492    *
493    * Since: 3.4
494    */
495   g_object_class_install_property (gobject_class,
496                                    PROP_KINETIC_SCROLLING,
497                                    g_param_spec_boolean ("kinetic-scrolling",
498                                                          P_("Kinetic Scrolling"),
499                                                          P_("Kinetic scrolling mode."),
500                                                          TRUE,
501                                                          GTK_PARAM_READABLE |
502                                                          GTK_PARAM_WRITABLE));
503   /**
504    * GtkScrolledWindow::scroll-child:
505    * @scrolled_window: a #GtkScrolledWindow
506    * @scroll: a #GtkScrollType describing how much to scroll
507    * @horizontal: whether the keybinding scrolls the child
508    *   horizontally or not
509    *
510    * The ::scroll-child signal is a
511    * <link linkend="keybinding-signals">keybinding signal</link>
512    * which gets emitted when a keybinding that scrolls is pressed.
513    * The horizontal or vertical adjustment is updated which triggers a
514    * signal that the scrolled windows child may listen to and scroll itself.
515    */
516   signals[SCROLL_CHILD] =
517     g_signal_new (I_("scroll-child"),
518                   G_TYPE_FROM_CLASS (gobject_class),
519                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
520                   G_STRUCT_OFFSET (GtkScrolledWindowClass, scroll_child),
521                   NULL, NULL,
522                   _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
523                   G_TYPE_BOOLEAN, 2,
524                   GTK_TYPE_SCROLL_TYPE,
525                   G_TYPE_BOOLEAN);
526
527   /**
528    * GtkScrolledWindow::move-focus-out:
529    * @scrolled_window: a #GtkScrolledWindow
530    * @direction_type: either %GTK_DIR_TAB_FORWARD or
531    *   %GTK_DIR_TAB_BACKWARD
532    *
533    * The ::move-focus-out signal is a
534    * <link linkend="keybinding-signals">keybinding signal</link>
535    * which gets emitted when focus is moved away from the scrolled
536    * window by a keybinding.
537    * The #GtkWidget::move-focus signal is emitted with @direction_type
538    * on this scrolled windows toplevel parent in the container hierarchy.
539    * The default bindings for this signal are
540    * <keycombo><keycap>Tab</keycap><keycap>Ctrl</keycap></keycombo>
541    * and
542    * <keycombo><keycap>Tab</keycap><keycap>Ctrl</keycap><keycap>Shift</keycap></keycombo>.
543    */
544   signals[MOVE_FOCUS_OUT] =
545     g_signal_new (I_("move-focus-out"),
546                   G_TYPE_FROM_CLASS (gobject_class),
547                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
548                   G_STRUCT_OFFSET (GtkScrolledWindowClass, move_focus_out),
549                   NULL, NULL,
550                   _gtk_marshal_VOID__ENUM,
551                   G_TYPE_NONE, 1,
552                   GTK_TYPE_DIRECTION_TYPE);
553   
554   binding_set = gtk_binding_set_by_class (class);
555
556   add_scroll_binding (binding_set, GDK_KEY_Left,  GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, TRUE);
557   add_scroll_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD,  TRUE);
558   add_scroll_binding (binding_set, GDK_KEY_Up,    GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, FALSE);
559   add_scroll_binding (binding_set, GDK_KEY_Down,  GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD,  FALSE);
560
561   add_scroll_binding (binding_set, GDK_KEY_Page_Up,   GDK_CONTROL_MASK, GTK_SCROLL_PAGE_BACKWARD, TRUE);
562   add_scroll_binding (binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_FORWARD,  TRUE);
563   add_scroll_binding (binding_set, GDK_KEY_Page_Up,   0,                GTK_SCROLL_PAGE_BACKWARD, FALSE);
564   add_scroll_binding (binding_set, GDK_KEY_Page_Down, 0,                GTK_SCROLL_PAGE_FORWARD,  FALSE);
565
566   add_scroll_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_SCROLL_START, TRUE);
567   add_scroll_binding (binding_set, GDK_KEY_End,  GDK_CONTROL_MASK, GTK_SCROLL_END,   TRUE);
568   add_scroll_binding (binding_set, GDK_KEY_Home, 0,                GTK_SCROLL_START, FALSE);
569   add_scroll_binding (binding_set, GDK_KEY_End,  0,                GTK_SCROLL_END,   FALSE);
570
571   add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
572   add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
573
574   g_type_class_add_private (class, sizeof (GtkScrolledWindowPrivate));
575
576   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SCROLLED_WINDOW_ACCESSIBLE);
577 }
578
579 static void
580 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
581 {
582   GtkScrolledWindowPrivate *priv;
583
584   scrolled_window->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (scrolled_window,
585                                                               GTK_TYPE_SCROLLED_WINDOW,
586                                                               GtkScrolledWindowPrivate);
587
588   gtk_widget_set_has_window (GTK_WIDGET (scrolled_window), FALSE);
589   gtk_widget_set_can_focus (GTK_WIDGET (scrolled_window), TRUE);
590
591   priv->hscrollbar = NULL;
592   priv->vscrollbar = NULL;
593   priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC;
594   priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC;
595   priv->hscrollbar_visible = FALSE;
596   priv->vscrollbar_visible = FALSE;
597   priv->focus_out = FALSE;
598   priv->window_placement = GTK_CORNER_TOP_LEFT;
599   gtk_scrolled_window_update_real_placement (scrolled_window);
600   priv->min_content_width = -1;
601   priv->min_content_height = -1;
602
603   gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
604   gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
605 }
606
607 /**
608  * gtk_scrolled_window_new:
609  * @hadjustment: (allow-none): horizontal adjustment
610  * @vadjustment: (allow-none): vertical adjustment
611  *
612  * Creates a new scrolled window.
613  *
614  * The two arguments are the scrolled window's adjustments; these will be
615  * shared with the scrollbars and the child widget to keep the bars in sync 
616  * with the child. Usually you want to pass %NULL for the adjustments, which 
617  * will cause the scrolled window to create them for you.
618  *
619  * Returns: a new scrolled window
620  */
621 GtkWidget*
622 gtk_scrolled_window_new (GtkAdjustment *hadjustment,
623                          GtkAdjustment *vadjustment)
624 {
625   GtkWidget *scrolled_window;
626
627   if (hadjustment)
628     g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
629
630   if (vadjustment)
631     g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
632
633   scrolled_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
634                                     "hadjustment", hadjustment,
635                                     "vadjustment", vadjustment,
636                                     NULL);
637
638   return scrolled_window;
639 }
640
641 /**
642  * gtk_scrolled_window_set_hadjustment:
643  * @scrolled_window: a #GtkScrolledWindow
644  * @hadjustment: horizontal scroll adjustment
645  *
646  * Sets the #GtkAdjustment for the horizontal scrollbar.
647  */
648 void
649 gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
650                                      GtkAdjustment     *hadjustment)
651 {
652   GtkScrolledWindowPrivate *priv;
653   GtkBin *bin;
654   GtkWidget *child;
655
656   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
657   if (hadjustment)
658     g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
659   else
660     hadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
661
662   bin = GTK_BIN (scrolled_window);
663   priv = scrolled_window->priv;
664
665   if (!priv->hscrollbar)
666     {
667       gtk_widget_push_composite_child ();
668       priv->hscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, hadjustment);
669       gtk_widget_set_composite_name (priv->hscrollbar, "hscrollbar");
670       gtk_widget_pop_composite_child ();
671
672       gtk_widget_set_parent (priv->hscrollbar, GTK_WIDGET (scrolled_window));
673       g_object_ref (priv->hscrollbar);
674       gtk_widget_show (priv->hscrollbar);
675     }
676   else
677     {
678       GtkAdjustment *old_adjustment;
679
680       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
681       if (old_adjustment == hadjustment)
682         return;
683
684       g_signal_handlers_disconnect_by_func (old_adjustment,
685                                             gtk_scrolled_window_adjustment_changed,
686                                             scrolled_window);
687       gtk_range_set_adjustment (GTK_RANGE (priv->hscrollbar),
688                                 hadjustment);
689     }
690   hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
691   g_signal_connect (hadjustment,
692                     "changed",
693                     G_CALLBACK (gtk_scrolled_window_adjustment_changed),
694                     scrolled_window);
695   g_signal_connect (hadjustment,
696                     "value-changed",
697                     G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
698                     scrolled_window);
699   gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
700   gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
701
702   child = gtk_bin_get_child (bin);
703   if (GTK_IS_SCROLLABLE (child))
704     gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (child), hadjustment);
705
706   g_object_notify (G_OBJECT (scrolled_window), "hadjustment");
707 }
708
709 /**
710  * gtk_scrolled_window_set_vadjustment:
711  * @scrolled_window: a #GtkScrolledWindow
712  * @vadjustment: vertical scroll adjustment
713  *
714  * Sets the #GtkAdjustment for the vertical scrollbar.
715  */
716 void
717 gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
718                                      GtkAdjustment     *vadjustment)
719 {
720   GtkScrolledWindowPrivate *priv;
721   GtkBin *bin;
722   GtkWidget *child;
723
724   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
725   if (vadjustment)
726     g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
727   else
728     vadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
729
730   bin = GTK_BIN (scrolled_window);
731   priv = scrolled_window->priv;
732
733   if (!priv->vscrollbar)
734     {
735       gtk_widget_push_composite_child ();
736       priv->vscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, vadjustment);
737       gtk_widget_set_composite_name (priv->vscrollbar, "vscrollbar");
738       gtk_widget_pop_composite_child ();
739
740       gtk_widget_set_parent (priv->vscrollbar, GTK_WIDGET (scrolled_window));
741       g_object_ref (priv->vscrollbar);
742       gtk_widget_show (priv->vscrollbar);
743     }
744   else
745     {
746       GtkAdjustment *old_adjustment;
747       
748       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
749       if (old_adjustment == vadjustment)
750         return;
751
752       g_signal_handlers_disconnect_by_func (old_adjustment,
753                                             gtk_scrolled_window_adjustment_changed,
754                                             scrolled_window);
755       gtk_range_set_adjustment (GTK_RANGE (priv->vscrollbar),
756                                 vadjustment);
757     }
758   vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
759   g_signal_connect (vadjustment,
760                     "changed",
761                     G_CALLBACK (gtk_scrolled_window_adjustment_changed),
762                     scrolled_window);
763   g_signal_connect (vadjustment,
764                     "value-changed",
765                     G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
766                     scrolled_window);
767   gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
768   gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);
769
770   child = gtk_bin_get_child (bin);
771   if (GTK_IS_SCROLLABLE (child))
772     gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (child), vadjustment);
773
774   g_object_notify (G_OBJECT (scrolled_window), "vadjustment");
775 }
776
777 /**
778  * gtk_scrolled_window_get_hadjustment:
779  * @scrolled_window: a #GtkScrolledWindow
780  *
781  * Returns the horizontal scrollbar's adjustment, used to connect the
782  * horizontal scrollbar to the child widget's horizontal scroll
783  * functionality.
784  *
785  * Returns: (transfer none): the horizontal #GtkAdjustment
786  */
787 GtkAdjustment*
788 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
789 {
790   GtkScrolledWindowPrivate *priv;
791
792   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
793
794   priv = scrolled_window->priv;
795
796   return (priv->hscrollbar ?
797           gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)) :
798           NULL);
799 }
800
801 /**
802  * gtk_scrolled_window_get_vadjustment:
803  * @scrolled_window: a #GtkScrolledWindow
804  * 
805  * Returns the vertical scrollbar's adjustment, used to connect the
806  * vertical scrollbar to the child widget's vertical scroll functionality.
807  * 
808  * Returns: (transfer none): the vertical #GtkAdjustment
809  */
810 GtkAdjustment*
811 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
812 {
813   GtkScrolledWindowPrivate *priv;
814
815   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
816
817   priv = scrolled_window->priv;
818
819   return (priv->vscrollbar ?
820           gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)) :
821           NULL);
822 }
823
824 /**
825  * gtk_scrolled_window_get_hscrollbar:
826  * @scrolled_window: a #GtkScrolledWindow
827  *
828  * Returns the horizontal scrollbar of @scrolled_window.
829  *
830  * Returns: (transfer none): the horizontal scrollbar of the scrolled window,
831  *     or %NULL if it does not have one.
832  *
833  * Since: 2.8
834  */
835 GtkWidget*
836 gtk_scrolled_window_get_hscrollbar (GtkScrolledWindow *scrolled_window)
837 {
838   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
839
840   return scrolled_window->priv->hscrollbar;
841 }
842
843 /**
844  * gtk_scrolled_window_get_vscrollbar:
845  * @scrolled_window: a #GtkScrolledWindow
846  * 
847  * Returns the vertical scrollbar of @scrolled_window.
848  *
849  * Returns: (transfer none): the vertical scrollbar of the scrolled window,
850  *     or %NULL if it does not have one.
851  *
852  * Since: 2.8
853  */
854 GtkWidget*
855 gtk_scrolled_window_get_vscrollbar (GtkScrolledWindow *scrolled_window)
856 {
857   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
858
859   return scrolled_window->priv->vscrollbar;
860 }
861
862 /**
863  * gtk_scrolled_window_set_policy:
864  * @scrolled_window: a #GtkScrolledWindow
865  * @hscrollbar_policy: policy for horizontal bar
866  * @vscrollbar_policy: policy for vertical bar
867  * 
868  * Sets the scrollbar policy for the horizontal and vertical scrollbars.
869  *
870  * The policy determines when the scrollbar should appear; it is a value
871  * from the #GtkPolicyType enumeration. If %GTK_POLICY_ALWAYS, the
872  * scrollbar is always present; if %GTK_POLICY_NEVER, the scrollbar is
873  * never present; if %GTK_POLICY_AUTOMATIC, the scrollbar is present only
874  * if needed (that is, if the slider part of the bar would be smaller
875  * than the trough - the display is larger than the page size).
876  */
877 void
878 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
879                                 GtkPolicyType      hscrollbar_policy,
880                                 GtkPolicyType      vscrollbar_policy)
881 {
882   GtkScrolledWindowPrivate *priv;
883   GObject *object = G_OBJECT (scrolled_window);
884   
885   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
886
887   priv = scrolled_window->priv;
888
889   if ((priv->hscrollbar_policy != hscrollbar_policy) ||
890       (priv->vscrollbar_policy != vscrollbar_policy))
891     {
892       priv->hscrollbar_policy = hscrollbar_policy;
893       priv->vscrollbar_policy = vscrollbar_policy;
894
895       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
896
897       g_object_freeze_notify (object);
898       g_object_notify (object, "hscrollbar-policy");
899       g_object_notify (object, "vscrollbar-policy");
900       g_object_thaw_notify (object);
901     }
902 }
903
904 /**
905  * gtk_scrolled_window_get_policy:
906  * @scrolled_window: a #GtkScrolledWindow
907  * @hscrollbar_policy: (out) (allow-none): location to store the policy 
908  *     for the horizontal scrollbar, or %NULL.
909  * @vscrollbar_policy: (out) (allow-none): location to store the policy
910  *     for the vertical scrollbar, or %NULL.
911  * 
912  * Retrieves the current policy values for the horizontal and vertical
913  * scrollbars. See gtk_scrolled_window_set_policy().
914  */
915 void
916 gtk_scrolled_window_get_policy (GtkScrolledWindow *scrolled_window,
917                                 GtkPolicyType     *hscrollbar_policy,
918                                 GtkPolicyType     *vscrollbar_policy)
919 {
920   GtkScrolledWindowPrivate *priv;
921
922   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
923
924   priv = scrolled_window->priv;
925
926   if (hscrollbar_policy)
927     *hscrollbar_policy = priv->hscrollbar_policy;
928   if (vscrollbar_policy)
929     *vscrollbar_policy = priv->vscrollbar_policy;
930 }
931
932 static void
933 gtk_scrolled_window_update_real_placement (GtkScrolledWindow *scrolled_window)
934 {
935   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
936   GtkSettings *settings;
937
938   settings = gtk_widget_get_settings (GTK_WIDGET (scrolled_window));
939
940   if (priv->window_placement_set || settings == NULL)
941     priv->real_window_placement = priv->window_placement;
942   else
943     g_object_get (settings,
944                   "gtk-scrolled-window-placement",
945                   &priv->real_window_placement,
946                   NULL);
947 }
948
949 static void
950 gtk_scrolled_window_set_placement_internal (GtkScrolledWindow *scrolled_window,
951                                             GtkCornerType      window_placement)
952 {
953   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
954
955   if (priv->window_placement != window_placement)
956     {
957       priv->window_placement = window_placement;
958
959       gtk_scrolled_window_update_real_placement (scrolled_window);
960       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
961       
962       g_object_notify (G_OBJECT (scrolled_window), "window-placement");
963     }
964 }
965
966 static void
967 gtk_scrolled_window_set_placement_set (GtkScrolledWindow *scrolled_window,
968                                        gboolean           placement_set,
969                                        gboolean           emit_resize)
970 {
971   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
972
973   if (priv->window_placement_set != placement_set)
974     {
975       priv->window_placement_set = placement_set;
976
977       gtk_scrolled_window_update_real_placement (scrolled_window);
978       if (emit_resize)
979         gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
980
981       g_object_notify (G_OBJECT (scrolled_window), "window-placement-set");
982     }
983 }
984
985 /**
986  * gtk_scrolled_window_set_placement:
987  * @scrolled_window: a #GtkScrolledWindow
988  * @window_placement: position of the child window
989  *
990  * Sets the placement of the contents with respect to the scrollbars
991  * for the scrolled window.
992  * 
993  * The default is %GTK_CORNER_TOP_LEFT, meaning the child is
994  * in the top left, with the scrollbars underneath and to the right.
995  * Other values in #GtkCornerType are %GTK_CORNER_TOP_RIGHT,
996  * %GTK_CORNER_BOTTOM_LEFT, and %GTK_CORNER_BOTTOM_RIGHT.
997  *
998  * See also gtk_scrolled_window_get_placement() and
999  * gtk_scrolled_window_unset_placement().
1000  */
1001 void
1002 gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
1003                                    GtkCornerType      window_placement)
1004 {
1005   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1006
1007   gtk_scrolled_window_set_placement_set (scrolled_window, TRUE, FALSE);
1008   gtk_scrolled_window_set_placement_internal (scrolled_window, window_placement);
1009 }
1010
1011 /**
1012  * gtk_scrolled_window_get_placement:
1013  * @scrolled_window: a #GtkScrolledWindow
1014  *
1015  * Gets the placement of the contents with respect to the scrollbars
1016  * for the scrolled window. See gtk_scrolled_window_set_placement().
1017  *
1018  * Return value: the current placement value.
1019  *
1020  * See also gtk_scrolled_window_set_placement() and
1021  * gtk_scrolled_window_unset_placement().
1022  **/
1023 GtkCornerType
1024 gtk_scrolled_window_get_placement (GtkScrolledWindow *scrolled_window)
1025 {
1026   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_CORNER_TOP_LEFT);
1027
1028   return scrolled_window->priv->window_placement;
1029 }
1030
1031 /**
1032  * gtk_scrolled_window_unset_placement:
1033  * @scrolled_window: a #GtkScrolledWindow
1034  *
1035  * Unsets the placement of the contents with respect to the scrollbars
1036  * for the scrolled window. If no window placement is set for a scrolled
1037  * window, it obeys the "gtk-scrolled-window-placement" XSETTING.
1038  *
1039  * See also gtk_scrolled_window_set_placement() and
1040  * gtk_scrolled_window_get_placement().
1041  *
1042  * Since: 2.10
1043  **/
1044 void
1045 gtk_scrolled_window_unset_placement (GtkScrolledWindow *scrolled_window)
1046 {
1047   GtkScrolledWindowPrivate *priv;
1048
1049   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1050
1051   priv = scrolled_window->priv;
1052
1053   if (priv->window_placement_set)
1054     {
1055       priv->window_placement_set = FALSE;
1056
1057       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
1058
1059       g_object_notify (G_OBJECT (scrolled_window), "window-placement-set");
1060     }
1061 }
1062
1063 /**
1064  * gtk_scrolled_window_set_shadow_type:
1065  * @scrolled_window: a #GtkScrolledWindow
1066  * @type: kind of shadow to draw around scrolled window contents
1067  *
1068  * Changes the type of shadow drawn around the contents of
1069  * @scrolled_window.
1070  * 
1071  **/
1072 void
1073 gtk_scrolled_window_set_shadow_type (GtkScrolledWindow *scrolled_window,
1074                                      GtkShadowType      type)
1075 {
1076   GtkScrolledWindowPrivate *priv;
1077
1078   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1079   g_return_if_fail (type >= GTK_SHADOW_NONE && type <= GTK_SHADOW_ETCHED_OUT);
1080
1081   priv = scrolled_window->priv;
1082
1083   if (priv->shadow_type != type)
1084     {
1085       priv->shadow_type = type;
1086
1087       if (gtk_widget_is_drawable (GTK_WIDGET (scrolled_window)))
1088         gtk_widget_queue_draw (GTK_WIDGET (scrolled_window));
1089
1090       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
1091
1092       g_object_notify (G_OBJECT (scrolled_window), "shadow-type");
1093     }
1094 }
1095
1096 /**
1097  * gtk_scrolled_window_get_shadow_type:
1098  * @scrolled_window: a #GtkScrolledWindow
1099  *
1100  * Gets the shadow type of the scrolled window. See 
1101  * gtk_scrolled_window_set_shadow_type().
1102  *
1103  * Return value: the current shadow type
1104  **/
1105 GtkShadowType
1106 gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolled_window)
1107 {
1108   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_NONE);
1109
1110   return scrolled_window->priv->shadow_type;
1111 }
1112
1113 /**
1114  * gtk_scrolled_window_set_kinetic_scrolling:
1115  * @scrolled_window: a #GtkScrolledWindow
1116  * @kinetic_scrolling: %TRUE to enable kinetic scrolling
1117  *
1118  * Turns kinetic scrolling on or off.
1119  * Kinetic scrolling only applies to devices with source
1120  * %GDK_SOURCE_TOUCHSCREEN.
1121  *
1122  * Since: 3.4
1123  **/
1124 void
1125 gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
1126                                            gboolean           kinetic_scrolling)
1127 {
1128   GtkScrolledWindowPrivate *priv;
1129
1130   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1131
1132   priv = scrolled_window->priv;
1133   if (priv->kinetic_scrolling == kinetic_scrolling)
1134     return;
1135
1136   priv->kinetic_scrolling = kinetic_scrolling;
1137   if (priv->kinetic_scrolling)
1138     {
1139       _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window),
1140                                               gtk_scrolled_window_captured_event);
1141     }
1142   else
1143     {
1144       _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window), NULL);
1145       if (priv->release_timeout_id)
1146         {
1147           g_source_remove (priv->release_timeout_id);
1148           priv->release_timeout_id = 0;
1149         }
1150       if (priv->deceleration_id)
1151         {
1152           g_source_remove (priv->deceleration_id);
1153           priv->deceleration_id = 0;
1154         }
1155     }
1156   g_object_notify (G_OBJECT (scrolled_window), "kinetic-scrolling");
1157 }
1158
1159 /**
1160  * gtk_scrolled_window_get_kinetic_scrolling:
1161  * @scrolled_window: a #GtkScrolledWindow
1162  *
1163  * Returns the specified kinetic scrolling behavior.
1164  *
1165  * Return value: the scrolling behavior flags.
1166  *
1167  * Since: 3.4
1168  */
1169 gboolean
1170 gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window)
1171 {
1172   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
1173
1174   return scrolled_window->priv->kinetic_scrolling;
1175 }
1176
1177 /**
1178  * gtk_scrolled_window_set_capture_button_press:
1179  * @scrolled_window: a #GtkScrolledWindow
1180  * @capture_button_press: %TRUE to capture button presses
1181  *
1182  * Changes the behaviour of @scrolled_window wrt. to the initial
1183  * event that possibly starts kinetic scrolling. When @capture_button_press
1184  * is set to %TRUE, the event is captured by the scrolled window, and
1185  * then later replayed if it is meant to go to the child widget.
1186  *
1187  * This should be enabled if any child widgets perform non-reversible
1188  * actions on #GtkWidget::button-press-event. If they don't, and handle
1189  * additionally handle #GtkWidget::grab-broken-event, it might be better
1190  * to set @capture_button_press to %FALSE.
1191  *
1192  * This setting only has an effect if kinetic scrolling is enabled.
1193  *
1194  * Since: 3.4
1195  */
1196 void
1197 gtk_scrolled_window_set_capture_button_press (GtkScrolledWindow *scrolled_window,
1198                                               gboolean           capture_button_press)
1199 {
1200   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1201
1202   scrolled_window->priv->capture_button_press = capture_button_press;
1203 }
1204
1205 /**
1206  * gtk_scrolled_window_get_capture_button_press:
1207  * @scrolled_window: a #GtkScrolledWindow
1208  *
1209  * Return whether button presses are captured during kinetic
1210  * scrolling. See gtk_scrolled_window_set_capture_button_press().
1211  *
1212  * Returns: %TRUE if button presses are captured during kinetic scrolling
1213  *
1214  * Since: 3.4
1215  */
1216 gboolean
1217 gtk_scrolled_window_get_capture_button_press (GtkScrolledWindow *scrolled_window)
1218 {
1219   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
1220
1221   return scrolled_window->priv->capture_button_press;
1222 }
1223
1224
1225 static void
1226 gtk_scrolled_window_destroy (GtkWidget *widget)
1227 {
1228   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1229   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1230
1231   if (priv->hscrollbar)
1232     {
1233       g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)),
1234                                             gtk_scrolled_window_adjustment_changed,
1235                                             scrolled_window);
1236       gtk_widget_unparent (priv->hscrollbar);
1237       gtk_widget_destroy (priv->hscrollbar);
1238       g_object_unref (priv->hscrollbar);
1239       priv->hscrollbar = NULL;
1240     }
1241   if (priv->vscrollbar)
1242     {
1243       g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)),
1244                                             gtk_scrolled_window_adjustment_changed,
1245                                             scrolled_window);
1246       gtk_widget_unparent (priv->vscrollbar);
1247       gtk_widget_destroy (priv->vscrollbar);
1248       g_object_unref (priv->vscrollbar);
1249       priv->vscrollbar = NULL;
1250     }
1251
1252   if (priv->release_timeout_id)
1253     {
1254       g_source_remove (priv->release_timeout_id);
1255       priv->release_timeout_id = 0;
1256     }
1257   if (priv->deceleration_id)
1258     {
1259       g_source_remove (priv->deceleration_id);
1260       priv->deceleration_id = 0;
1261     }
1262
1263   if (priv->button_press_event)
1264     {
1265       gdk_event_free (priv->button_press_event);
1266       priv->button_press_event = NULL;
1267     }
1268
1269   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->destroy (widget);
1270 }
1271
1272 static void
1273 gtk_scrolled_window_set_property (GObject      *object,
1274                                   guint         prop_id,
1275                                   const GValue *value,
1276                                   GParamSpec   *pspec)
1277 {
1278   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
1279   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1280
1281   switch (prop_id)
1282     {
1283     case PROP_HADJUSTMENT:
1284       gtk_scrolled_window_set_hadjustment (scrolled_window,
1285                                            g_value_get_object (value));
1286       break;
1287     case PROP_VADJUSTMENT:
1288       gtk_scrolled_window_set_vadjustment (scrolled_window,
1289                                            g_value_get_object (value));
1290       break;
1291     case PROP_HSCROLLBAR_POLICY:
1292       gtk_scrolled_window_set_policy (scrolled_window,
1293                                       g_value_get_enum (value),
1294                                       priv->vscrollbar_policy);
1295       break;
1296     case PROP_VSCROLLBAR_POLICY:
1297       gtk_scrolled_window_set_policy (scrolled_window,
1298                                       priv->hscrollbar_policy,
1299                                       g_value_get_enum (value));
1300       break;
1301     case PROP_WINDOW_PLACEMENT:
1302       gtk_scrolled_window_set_placement_internal (scrolled_window,
1303                                                   g_value_get_enum (value));
1304       break;
1305     case PROP_WINDOW_PLACEMENT_SET:
1306       gtk_scrolled_window_set_placement_set (scrolled_window,
1307                                              g_value_get_boolean (value),
1308                                              TRUE);
1309       break;
1310     case PROP_SHADOW_TYPE:
1311       gtk_scrolled_window_set_shadow_type (scrolled_window,
1312                                            g_value_get_enum (value));
1313       break;
1314     case PROP_MIN_CONTENT_WIDTH:
1315       gtk_scrolled_window_set_min_content_width (scrolled_window,
1316                                                  g_value_get_int (value));
1317       break;
1318     case PROP_MIN_CONTENT_HEIGHT:
1319       gtk_scrolled_window_set_min_content_height (scrolled_window,
1320                                                   g_value_get_int (value));
1321       break;
1322     case PROP_KINETIC_SCROLLING:
1323       gtk_scrolled_window_set_kinetic_scrolling (scrolled_window,
1324                                                  g_value_get_boolean (value));
1325       break;
1326     default:
1327       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1328       break;
1329     }
1330 }
1331
1332 static void
1333 gtk_scrolled_window_get_property (GObject    *object,
1334                                   guint       prop_id,
1335                                   GValue     *value,
1336                                   GParamSpec *pspec)
1337 {
1338   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
1339   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1340
1341   switch (prop_id)
1342     {
1343     case PROP_HADJUSTMENT:
1344       g_value_set_object (value,
1345                           G_OBJECT (gtk_scrolled_window_get_hadjustment (scrolled_window)));
1346       break;
1347     case PROP_VADJUSTMENT:
1348       g_value_set_object (value,
1349                           G_OBJECT (gtk_scrolled_window_get_vadjustment (scrolled_window)));
1350       break;
1351     case PROP_WINDOW_PLACEMENT:
1352       g_value_set_enum (value, priv->window_placement);
1353       break;
1354     case PROP_WINDOW_PLACEMENT_SET:
1355       g_value_set_boolean (value, priv->window_placement_set);
1356       break;
1357     case PROP_SHADOW_TYPE:
1358       g_value_set_enum (value, priv->shadow_type);
1359       break;
1360     case PROP_HSCROLLBAR_POLICY:
1361       g_value_set_enum (value, priv->hscrollbar_policy);
1362       break;
1363     case PROP_VSCROLLBAR_POLICY:
1364       g_value_set_enum (value, priv->vscrollbar_policy);
1365       break;
1366     case PROP_MIN_CONTENT_WIDTH:
1367       g_value_set_int (value, priv->min_content_width);
1368       break;
1369     case PROP_MIN_CONTENT_HEIGHT:
1370       g_value_set_int (value, priv->min_content_height);
1371       break;
1372     case PROP_KINETIC_SCROLLING:
1373       g_value_set_boolean (value, priv->kinetic_scrolling);
1374       break;
1375     default:
1376       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1377       break;
1378     }
1379 }
1380
1381 static void
1382 traverse_container (GtkWidget *widget,
1383                     gpointer   data)
1384 {
1385   if (GTK_IS_SCROLLED_WINDOW (widget))
1386     {
1387       gtk_scrolled_window_update_real_placement (GTK_SCROLLED_WINDOW (widget));
1388       gtk_widget_queue_resize (widget);
1389     }
1390   else if (GTK_IS_CONTAINER (widget))
1391     gtk_container_forall (GTK_CONTAINER (widget), traverse_container, NULL);
1392 }
1393
1394 static void
1395 gtk_scrolled_window_settings_changed (GtkSettings *settings)
1396 {
1397   GList *list, *l;
1398
1399   list = gtk_window_list_toplevels ();
1400
1401   for (l = list; l; l = l->next)
1402     gtk_container_forall (GTK_CONTAINER (l->data), 
1403                           traverse_container, NULL);
1404
1405   g_list_free (list);
1406 }
1407
1408 static void
1409 gtk_scrolled_window_screen_changed (GtkWidget *widget,
1410                                     GdkScreen *previous_screen)
1411 {
1412   GtkSettings *settings;
1413   guint window_placement_connection;
1414
1415   gtk_scrolled_window_update_real_placement (GTK_SCROLLED_WINDOW (widget));
1416
1417   if (!gtk_widget_has_screen (widget))
1418     return;
1419
1420   settings = gtk_widget_get_settings (widget);
1421
1422   window_placement_connection = 
1423     GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (settings), 
1424                                          "gtk-scrolled-window-connection"));
1425   
1426   if (window_placement_connection)
1427     return;
1428
1429   window_placement_connection =
1430     g_signal_connect (settings, "notify::gtk-scrolled-window-placement",
1431                       G_CALLBACK (gtk_scrolled_window_settings_changed), NULL);
1432   g_object_set_data (G_OBJECT (settings), 
1433                      I_("gtk-scrolled-window-connection"),
1434                      GUINT_TO_POINTER (window_placement_connection));
1435 }
1436
1437 static void
1438 gtk_scrolled_window_draw_scrollbars_junction (GtkScrolledWindow *scrolled_window,
1439                                               cairo_t *cr)
1440 {
1441   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1442   GtkWidget *widget = GTK_WIDGET (scrolled_window);
1443   GtkAllocation wid_allocation, hscr_allocation, vscr_allocation;
1444   GtkStyleContext *context;
1445   GdkRectangle junction_rect;
1446   gboolean is_rtl, scrollbars_within_bevel;
1447
1448   is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1449   gtk_widget_get_allocation (widget, &wid_allocation);
1450   gtk_widget_get_allocation (GTK_WIDGET (priv->hscrollbar), &hscr_allocation);
1451   gtk_widget_get_allocation (GTK_WIDGET (priv->vscrollbar), &vscr_allocation);
1452
1453   gtk_widget_style_get (widget,
1454                         "scrollbars-within-bevel", &scrollbars_within_bevel,
1455                         NULL);
1456   context = gtk_widget_get_style_context (widget);
1457
1458   if (scrollbars_within_bevel &&
1459       priv->shadow_type != GTK_SHADOW_NONE)
1460     {
1461       GtkStateFlags state;
1462       GtkBorder padding, border;
1463
1464       state = gtk_widget_get_state_flags (widget);
1465
1466       gtk_style_context_save (context);
1467       gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
1468       gtk_style_context_get_padding (context, state, &padding);
1469       gtk_style_context_get_border (context, state, &border);
1470       gtk_style_context_restore (context);
1471
1472       junction_rect.x = padding.left + border.left;
1473       junction_rect.y = padding.top + border.top;
1474     }
1475   else
1476     {
1477       junction_rect.x = 0;
1478       junction_rect.y = 0;
1479     }
1480
1481   junction_rect.width = vscr_allocation.width;
1482   junction_rect.height = hscr_allocation.height;
1483   
1484   if ((is_rtl && 
1485        (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
1486         priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
1487       (!is_rtl && 
1488        (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1489         priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
1490     junction_rect.x += hscr_allocation.width;
1491
1492   if (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1493       priv->real_window_placement == GTK_CORNER_TOP_RIGHT)
1494     junction_rect.y += vscr_allocation.height;
1495
1496   gtk_style_context_save (context);
1497   gtk_style_context_add_class (context, GTK_STYLE_CLASS_SCROLLBARS_JUNCTION);
1498
1499   gtk_render_background (context, cr,
1500                          junction_rect.x, junction_rect.y,
1501                          junction_rect.width, junction_rect.height);
1502   gtk_render_frame (context, cr,
1503                     junction_rect.x, junction_rect.y,
1504                     junction_rect.width, junction_rect.height);
1505
1506   gtk_style_context_restore (context);
1507 }
1508
1509 static gboolean
1510 gtk_scrolled_window_draw (GtkWidget *widget,
1511                           cairo_t   *cr)
1512 {
1513   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1514   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1515   GtkAllocation relative_allocation;
1516   GtkStyleContext *context;
1517
1518   context = gtk_widget_get_style_context (widget);
1519   gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1520
1521   gtk_render_background (context, cr,
1522                          0, 0,
1523                          gtk_widget_get_allocated_width (widget), gtk_widget_get_allocated_height (widget));
1524
1525   if (priv->hscrollbar_visible && 
1526       priv->vscrollbar_visible)
1527     gtk_scrolled_window_draw_scrollbars_junction (scrolled_window, cr);
1528
1529   if (priv->shadow_type != GTK_SHADOW_NONE)
1530     {
1531       gboolean scrollbars_within_bevel;
1532
1533       gtk_style_context_save (context);
1534       gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
1535
1536       gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
1537
1538       if (!scrollbars_within_bevel)
1539         {
1540           GtkStateFlags state;
1541           GtkBorder padding, border;
1542
1543           state = gtk_widget_get_state_flags (widget);
1544           gtk_style_context_get_padding (context, state, &padding);
1545           gtk_style_context_get_border (context, state, &border);
1546
1547           relative_allocation.x -= padding.left + border.left;
1548           relative_allocation.y -= padding.top + border.top;
1549           relative_allocation.width += padding.left + padding.right + border.left + border.right;
1550           relative_allocation.height += padding.top + padding.bottom + border.top + border.bottom;
1551         }
1552       else
1553         {
1554           relative_allocation.x = 0;
1555           relative_allocation.y = 0;
1556           relative_allocation.width = gtk_widget_get_allocated_width (widget);
1557           relative_allocation.height = gtk_widget_get_allocated_height (widget);
1558         }
1559
1560       gtk_render_frame (context, cr,
1561                         relative_allocation.x,
1562                         relative_allocation.y,
1563                         relative_allocation.width,
1564                         relative_allocation.height);
1565
1566       gtk_style_context_restore (context);
1567     }
1568
1569   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->draw (widget, cr);
1570
1571   return FALSE;
1572 }
1573
1574 static void
1575 gtk_scrolled_window_forall (GtkContainer *container,
1576                             gboolean      include_internals,
1577                             GtkCallback   callback,
1578                             gpointer      callback_data)
1579 {
1580   GtkScrolledWindowPrivate *priv;
1581   GtkScrolledWindow *scrolled_window;
1582
1583   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
1584   g_return_if_fail (callback != NULL);
1585
1586   GTK_CONTAINER_CLASS (gtk_scrolled_window_parent_class)->forall (container,
1587                                               include_internals,
1588                                               callback,
1589                                               callback_data);
1590   if (include_internals)
1591     {
1592       scrolled_window = GTK_SCROLLED_WINDOW (container);
1593       priv = scrolled_window->priv;
1594
1595       if (priv->vscrollbar)
1596         callback (priv->vscrollbar, callback_data);
1597       if (priv->hscrollbar)
1598         callback (priv->hscrollbar, callback_data);
1599     }
1600 }
1601
1602 static gboolean
1603 gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
1604                                   GtkScrollType      scroll,
1605                                   gboolean           horizontal)
1606 {
1607   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1608   GtkAdjustment *adjustment = NULL;
1609   
1610   switch (scroll)
1611     {
1612     case GTK_SCROLL_STEP_UP:
1613       scroll = GTK_SCROLL_STEP_BACKWARD;
1614       horizontal = FALSE;
1615       break;
1616     case GTK_SCROLL_STEP_DOWN:
1617       scroll = GTK_SCROLL_STEP_FORWARD;
1618       horizontal = FALSE;
1619       break;
1620     case GTK_SCROLL_STEP_LEFT:
1621       scroll = GTK_SCROLL_STEP_BACKWARD;
1622       horizontal = TRUE;
1623       break;
1624     case GTK_SCROLL_STEP_RIGHT:
1625       scroll = GTK_SCROLL_STEP_FORWARD;
1626       horizontal = TRUE;
1627       break;
1628     case GTK_SCROLL_PAGE_UP:
1629       scroll = GTK_SCROLL_PAGE_BACKWARD;
1630       horizontal = FALSE;
1631       break;
1632     case GTK_SCROLL_PAGE_DOWN:
1633       scroll = GTK_SCROLL_PAGE_FORWARD;
1634       horizontal = FALSE;
1635       break;
1636     case GTK_SCROLL_PAGE_LEFT:
1637       scroll = GTK_SCROLL_STEP_BACKWARD;
1638       horizontal = TRUE;
1639       break;
1640     case GTK_SCROLL_PAGE_RIGHT:
1641       scroll = GTK_SCROLL_STEP_FORWARD;
1642       horizontal = TRUE;
1643       break;
1644     case GTK_SCROLL_STEP_BACKWARD:
1645     case GTK_SCROLL_STEP_FORWARD:
1646     case GTK_SCROLL_PAGE_BACKWARD:
1647     case GTK_SCROLL_PAGE_FORWARD:
1648     case GTK_SCROLL_START:
1649     case GTK_SCROLL_END:
1650       break;
1651     default:
1652       g_warning ("Invalid scroll type %u for GtkScrolledWindow::scroll-child", scroll);
1653       return FALSE;
1654     }
1655
1656   if ((horizontal && (!priv->hscrollbar || !priv->hscrollbar_visible)) ||
1657       (!horizontal && (!priv->vscrollbar || !priv->vscrollbar_visible)))
1658     return FALSE;
1659
1660   if (horizontal)
1661     {
1662       if (priv->hscrollbar)
1663         adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
1664     }
1665   else
1666     {
1667       if (priv->vscrollbar)
1668         adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
1669     }
1670
1671   if (adjustment)
1672     {
1673       gdouble value = gtk_adjustment_get_value (adjustment);
1674       
1675       switch (scroll)
1676         {
1677         case GTK_SCROLL_STEP_FORWARD:
1678           value += gtk_adjustment_get_step_increment (adjustment);
1679           break;
1680         case GTK_SCROLL_STEP_BACKWARD:
1681           value -= gtk_adjustment_get_step_increment (adjustment);
1682           break;
1683         case GTK_SCROLL_PAGE_FORWARD:
1684           value += gtk_adjustment_get_page_increment (adjustment);
1685           break;
1686         case GTK_SCROLL_PAGE_BACKWARD:
1687           value -= gtk_adjustment_get_page_increment (adjustment);
1688           break;
1689         case GTK_SCROLL_START:
1690           value = gtk_adjustment_get_lower (adjustment);
1691           break;
1692         case GTK_SCROLL_END:
1693           value = gtk_adjustment_get_upper (adjustment);
1694           break;
1695         default:
1696           g_assert_not_reached ();
1697           break;
1698         }
1699
1700       gtk_adjustment_set_value (adjustment, value);
1701
1702       return TRUE;
1703     }
1704
1705   return FALSE;
1706 }
1707
1708 static void
1709 gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
1710                                     GtkDirectionType   direction_type)
1711 {
1712   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1713   GtkWidget *toplevel;
1714   
1715   /* Focus out of the scrolled window entirely. We do this by setting
1716    * a flag, then propagating the focus motion to the notebook.
1717    */
1718   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (scrolled_window));
1719   if (!gtk_widget_is_toplevel (toplevel))
1720     return;
1721
1722   g_object_ref (scrolled_window);
1723
1724   priv->focus_out = TRUE;
1725   g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1726   priv->focus_out = FALSE;
1727
1728   g_object_unref (scrolled_window);
1729 }
1730
1731 static void
1732 gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
1733                                          GtkAllocation *allocation)
1734 {
1735   GtkAllocation widget_allocation;
1736   GtkScrolledWindow *scrolled_window;
1737   GtkScrolledWindowPrivate *priv;
1738   gint sb_spacing;
1739   gint sb_width;
1740   gint sb_height;
1741
1742   g_return_if_fail (widget != NULL);
1743   g_return_if_fail (allocation != NULL);
1744
1745   scrolled_window = GTK_SCROLLED_WINDOW (widget);
1746   priv = scrolled_window->priv;
1747
1748   /* Get possible scrollbar dimensions */
1749   sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
1750   gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
1751   gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
1752
1753   /* Subtract some things from our available allocation size */
1754   allocation->x = 0;
1755   allocation->y = 0;
1756
1757   if (priv->shadow_type != GTK_SHADOW_NONE)
1758     {
1759       GtkStyleContext *context;
1760       GtkStateFlags state;
1761       GtkBorder padding, border;
1762
1763       context = gtk_widget_get_style_context (widget);
1764       state = gtk_widget_get_state_flags (widget);
1765
1766       gtk_style_context_save (context);
1767       gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
1768
1769       gtk_style_context_get_border (context, state, &border);
1770       gtk_style_context_get_padding (context, state, &padding);
1771
1772       allocation->x += padding.left + border.left;
1773       allocation->y += padding.top + border.top;
1774
1775       gtk_style_context_restore (context);
1776     }
1777
1778   gtk_widget_get_allocation (widget, &widget_allocation);
1779   allocation->width = MAX (1, (gint) widget_allocation.width - allocation->x * 2);
1780   allocation->height = MAX (1, (gint) widget_allocation.height - allocation->y * 2);
1781
1782   if (priv->vscrollbar_visible)
1783     {
1784       gboolean is_rtl;
1785
1786       is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1787   
1788       if ((!is_rtl && 
1789            (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
1790             priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
1791           (is_rtl && 
1792            (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1793             priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
1794         allocation->x += (sb_width +  sb_spacing);
1795
1796       allocation->width = MAX (1, allocation->width - (sb_width + sb_spacing));
1797     }
1798   if (priv->hscrollbar_visible)
1799     {
1800
1801       if (priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT ||
1802           priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)
1803         allocation->y += (sb_height + sb_spacing);
1804
1805       allocation->height = MAX (1, allocation->height - (sb_height + sb_spacing));
1806     }
1807 }
1808
1809 static gboolean
1810 _gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
1811                                     gint              *overshoot_x,
1812                                     gint              *overshoot_y)
1813 {
1814   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1815   GtkAdjustment *vadjustment, *hadjustment;
1816   gdouble lower, upper, x, y;
1817
1818   /* Vertical overshoot */
1819   vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
1820   lower = gtk_adjustment_get_lower (vadjustment);
1821   upper = gtk_adjustment_get_upper (vadjustment) -
1822     gtk_adjustment_get_page_size (vadjustment);
1823
1824   if (priv->unclamped_vadj_value < lower)
1825     y = priv->unclamped_vadj_value - lower;
1826   else if (priv->unclamped_vadj_value > upper)
1827     y = priv->unclamped_vadj_value - upper;
1828   else
1829     y = 0;
1830
1831   /* Horizontal overshoot */
1832   hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
1833   lower = gtk_adjustment_get_lower (hadjustment);
1834   upper = gtk_adjustment_get_upper (hadjustment) -
1835     gtk_adjustment_get_page_size (hadjustment);
1836
1837   if (priv->unclamped_hadj_value < lower)
1838     x = priv->unclamped_hadj_value - lower;
1839   else if (priv->unclamped_hadj_value > upper)
1840     x = priv->unclamped_hadj_value - upper;
1841   else
1842     x = 0;
1843
1844   if (overshoot_x)
1845     *overshoot_x = x;
1846
1847   if (overshoot_y)
1848     *overshoot_y = y;
1849
1850   return (x != 0 || y != 0);
1851 }
1852
1853 static void
1854 _gtk_scrolled_window_allocate_overshoot_window (GtkScrolledWindow *scrolled_window)
1855 {
1856   GtkAllocation window_allocation, relative_allocation, allocation;
1857   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1858   GtkWidget *widget = GTK_WIDGET (scrolled_window);
1859   gint overshoot_x, overshoot_y;
1860
1861   if (!gtk_widget_get_realized (widget))
1862     return;
1863
1864   gtk_widget_get_allocation (widget, &allocation);
1865   gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1866   _gtk_scrolled_window_get_overshoot (scrolled_window,
1867                                       &overshoot_x, &overshoot_y);
1868
1869   window_allocation = relative_allocation;
1870   window_allocation.x += allocation.x;
1871   window_allocation.y += allocation.y;
1872
1873   /* Handle displacement to the left/top by moving the overshoot
1874    * window, overshooting to the bottom/right is handled in
1875    * gtk_scrolled_window_allocate_child()
1876    */
1877   if (overshoot_x < 0)
1878     window_allocation.x += -overshoot_x;
1879
1880   if (overshoot_y < 0)
1881     window_allocation.y += -overshoot_y;
1882
1883   window_allocation.width -= ABS (overshoot_x);
1884   window_allocation.height -= ABS (overshoot_y);
1885
1886   gdk_window_move_resize (priv->overshoot_window,
1887                           window_allocation.x, window_allocation.y,
1888                           window_allocation.width, window_allocation.height);
1889 }
1890
1891 static void
1892 gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
1893                                     GtkAllocation     *relative_allocation)
1894 {
1895   GtkWidget     *widget = GTK_WIDGET (swindow), *child;
1896   GtkAllocation  allocation;
1897   GtkAllocation  child_allocation;
1898   gint           overshoot_x, overshoot_y;
1899
1900   child = gtk_bin_get_child (GTK_BIN (widget));
1901
1902   gtk_widget_get_allocation (widget, &allocation);
1903
1904   gtk_scrolled_window_relative_allocation (widget, relative_allocation);
1905   _gtk_scrolled_window_get_overshoot (swindow, &overshoot_x, &overshoot_y);
1906
1907   /* Handle overshooting to the right/bottom by relocating the
1908    * widget allocation to negative coordinates, so these edges
1909    * stick to the overshoot window border.
1910    */
1911   if (overshoot_x > 0)
1912     child_allocation.x = -overshoot_x;
1913   else
1914     child_allocation.x = 0;
1915
1916   if (overshoot_y > 0)
1917     child_allocation.y = -overshoot_y;
1918   else
1919     child_allocation.y = 0;
1920
1921   child_allocation.width = relative_allocation->width;
1922   child_allocation.height = relative_allocation->height;
1923
1924   gtk_widget_size_allocate (child, &child_allocation);
1925 }
1926
1927 static void
1928 gtk_scrolled_window_size_allocate (GtkWidget     *widget,
1929                                    GtkAllocation *allocation)
1930 {
1931   GtkScrolledWindow *scrolled_window;
1932   GtkScrolledWindowPrivate *priv;
1933   GtkStyleContext *context;
1934   GtkStateFlags state;
1935   GtkBorder padding, border;
1936   GtkBin *bin;
1937   GtkAllocation relative_allocation;
1938   GtkAllocation child_allocation;
1939   GtkWidget *child;
1940   gboolean scrollbars_within_bevel;
1941   gint sb_spacing;
1942   gint sb_width;
1943   gint sb_height;
1944
1945   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
1946   g_return_if_fail (allocation != NULL);
1947
1948   scrolled_window = GTK_SCROLLED_WINDOW (widget);
1949   bin = GTK_BIN (scrolled_window);
1950   priv = scrolled_window->priv;
1951
1952   /* Get possible scrollbar dimensions */
1953   sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
1954   gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
1955   gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
1956
1957   context = gtk_widget_get_style_context (widget);
1958   state = gtk_widget_get_state_flags (widget);
1959
1960   gtk_style_context_save (context);
1961   gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
1962
1963   gtk_style_context_get_padding (context, state, &padding);
1964   gtk_style_context_get_border (context, state, &border);
1965
1966   gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
1967
1968   gtk_widget_set_allocation (widget, allocation);
1969   gtk_style_context_restore (context);
1970
1971   if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
1972     priv->hscrollbar_visible = TRUE;
1973   else if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
1974     priv->hscrollbar_visible = FALSE;
1975   if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
1976     priv->vscrollbar_visible = TRUE;
1977   else if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
1978     priv->vscrollbar_visible = FALSE;
1979
1980   child = gtk_bin_get_child (bin);
1981   if (child && gtk_widget_get_visible (child))
1982     {
1983       gint child_scroll_width;
1984       gint child_scroll_height;
1985       gboolean previous_hvis;
1986       gboolean previous_vvis;
1987       guint count = 0;
1988
1989       /* Determine scrollbar visibility first via hfw apis */
1990       if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1991         {
1992           if (gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
1993             gtk_widget_get_preferred_width (child, &child_scroll_width, NULL);
1994           else
1995             gtk_widget_get_preferred_width (child, NULL, &child_scroll_width);
1996           
1997           if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1998             {
1999               /* First try without a vertical scrollbar if the content will fit the height
2000                * given the extra width of the scrollbar */
2001               if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
2002                 gtk_widget_get_preferred_height_for_width (child, 
2003                                                            MAX (allocation->width, child_scroll_width), 
2004                                                            &child_scroll_height, NULL);
2005               else
2006                 gtk_widget_get_preferred_height_for_width (child,
2007                                                            MAX (allocation->width, child_scroll_width), 
2008                                                            NULL, &child_scroll_height);
2009               
2010               if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2011                 {
2012                   /* Does the content height fit the allocation height ? */
2013                   priv->vscrollbar_visible = child_scroll_height > allocation->height;
2014                   
2015                   /* Does the content width fit the allocation with minus a possible scrollbar ? */
2016                   priv->hscrollbar_visible = 
2017                     child_scroll_width > allocation->width - 
2018                     (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2019                   
2020                   /* Now that we've guessed the hscrollbar, does the content height fit
2021                    * the possible new allocation height ? */
2022                   priv->vscrollbar_visible = 
2023                     child_scroll_height > allocation->height - 
2024                     (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2025                   
2026                   /* Now that we've guessed the vscrollbar, does the content width fit
2027                    * the possible new allocation width ? */
2028                   priv->hscrollbar_visible = 
2029                     child_scroll_width > allocation->width - 
2030                     (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2031                 }
2032               else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
2033                 {
2034                   priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
2035                   priv->vscrollbar_visible = child_scroll_height > allocation->height - 
2036                     (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2037                 }
2038             }
2039           else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
2040             {
2041               priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
2042               
2043               if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2044                 priv->hscrollbar_visible = 
2045                   child_scroll_width > allocation->width - 
2046                   (priv->vscrollbar_visible ? 0 : sb_width + sb_spacing);
2047               else
2048                 priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
2049             }
2050         } 
2051       else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
2052         {
2053           if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
2054             gtk_widget_get_preferred_height (child, &child_scroll_height, NULL);
2055           else
2056             gtk_widget_get_preferred_height (child, NULL, &child_scroll_height);
2057           
2058           if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2059             {
2060               /* First try without a horizontal scrollbar if the content will fit the width
2061                * given the extra height of the scrollbar */
2062               if (gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
2063                 gtk_widget_get_preferred_width_for_height (child, 
2064                                                            MAX (allocation->height, child_scroll_height), 
2065                                                            &child_scroll_width, NULL);
2066               else
2067                 gtk_widget_get_preferred_width_for_height (child, 
2068                                                            MAX (allocation->height, child_scroll_height), 
2069                                                            NULL, &child_scroll_width);
2070               
2071               if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
2072                 {
2073                   /* Does the content width fit the allocation width ? */
2074                   priv->hscrollbar_visible = child_scroll_width > allocation->width;
2075                   
2076                   /* Does the content height fit the allocation with minus a possible scrollbar ? */
2077                   priv->vscrollbar_visible = 
2078                     child_scroll_height > allocation->height - 
2079                     (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2080                   
2081                   /* Now that we've guessed the vscrollbar, does the content width fit
2082                    * the possible new allocation width ? */
2083                   priv->hscrollbar_visible = 
2084                     child_scroll_width > allocation->width - 
2085                     (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2086                   
2087                   /* Now that we've guessed the hscrollbar, does the content height fit
2088                    * the possible new allocation height ? */
2089                   priv->vscrollbar_visible = 
2090                     child_scroll_height > allocation->height - 
2091                     (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2092                 }
2093               else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
2094                 {
2095                   priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
2096                   priv->hscrollbar_visible = child_scroll_width > allocation->width - 
2097                     (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2098                 }
2099             }
2100           else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
2101             {
2102               priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
2103               
2104               if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
2105                 priv->vscrollbar_visible = 
2106                   child_scroll_height > allocation->height - 
2107                   (priv->hscrollbar_visible ? 0 : sb_height + sb_spacing);
2108               else
2109                 priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
2110             }
2111         }
2112
2113       /* Now after guessing scrollbar visibility; fall back on the allocation loop which 
2114        * observes the adjustments to detect scrollbar visibility and also avoids 
2115        * infinite recursion
2116        */
2117       do
2118         {
2119           previous_hvis = priv->hscrollbar_visible;
2120           previous_vvis = priv->vscrollbar_visible;
2121           gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
2122
2123           /* Explicitly force scrollbar visibility checks.
2124            *
2125            * Since we make a guess above, the child might not decide to update the adjustments 
2126            * if they logically did not change since the last configuration
2127            */
2128           if (priv->hscrollbar)
2129             gtk_scrolled_window_adjustment_changed 
2130               (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)), scrolled_window);
2131
2132           if (priv->vscrollbar)
2133             gtk_scrolled_window_adjustment_changed 
2134               (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)), scrolled_window);
2135
2136           /* If, after the first iteration, the hscrollbar and the
2137            * vscrollbar flip visiblity... or if one of the scrollbars flip
2138            * on each itteration indefinitly/infinitely, then we just need both 
2139            * at this size.
2140            */
2141           if ((count &&
2142                previous_hvis != priv->hscrollbar_visible &&
2143                previous_vvis != priv->vscrollbar_visible) || count > 3)
2144             {
2145               priv->hscrollbar_visible = TRUE;
2146               priv->vscrollbar_visible = TRUE;
2147
2148               gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
2149
2150               break;
2151             }
2152           
2153           count++;
2154         }
2155       while (previous_hvis != priv->hscrollbar_visible ||
2156              previous_vvis != priv->vscrollbar_visible);
2157     }
2158   else
2159     {
2160       priv->hscrollbar_visible = priv->hscrollbar_policy == GTK_POLICY_ALWAYS;
2161       priv->vscrollbar_visible = priv->vscrollbar_policy == GTK_POLICY_ALWAYS;
2162       gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
2163     }
2164
2165   if (priv->hscrollbar_visible)
2166     {
2167       if (!gtk_widget_get_visible (priv->hscrollbar))
2168         gtk_widget_show (priv->hscrollbar);
2169
2170       child_allocation.x = relative_allocation.x;
2171       if (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
2172           priv->real_window_placement == GTK_CORNER_TOP_RIGHT)
2173         child_allocation.y = (relative_allocation.y +
2174                               relative_allocation.height +
2175                               sb_spacing +
2176                               (priv->shadow_type == GTK_SHADOW_NONE ?
2177                                0 : padding.top + border.top));
2178       else
2179         child_allocation.y = 0;
2180
2181       child_allocation.width = relative_allocation.width;
2182       child_allocation.height = sb_height;
2183       child_allocation.x += allocation->x;
2184       child_allocation.y += allocation->y;
2185
2186       if (priv->shadow_type != GTK_SHADOW_NONE)
2187         {
2188           if (!scrollbars_within_bevel)
2189             {
2190               child_allocation.x -= padding.left + border.left;
2191               child_allocation.width += padding.left + padding.right + border.left + border.right;
2192             }
2193           else if (GTK_CORNER_TOP_RIGHT == priv->real_window_placement ||
2194                    GTK_CORNER_TOP_LEFT == priv->real_window_placement)
2195             {
2196               child_allocation.y -= padding.top + border.top;
2197             }
2198           else
2199             {
2200               child_allocation.y += padding.top + border.top;
2201             }
2202         }
2203
2204       gtk_widget_size_allocate (priv->hscrollbar, &child_allocation);
2205     }
2206   else if (gtk_widget_get_visible (priv->hscrollbar))
2207     gtk_widget_hide (priv->hscrollbar);
2208
2209   if (priv->vscrollbar_visible)
2210     {
2211       if (!gtk_widget_get_visible (priv->vscrollbar))
2212         gtk_widget_show (priv->vscrollbar);
2213
2214       if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL && 
2215            (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
2216             priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
2217           (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR && 
2218            (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
2219             priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
2220         child_allocation.x = (relative_allocation.x +
2221                               relative_allocation.width +
2222                               sb_spacing +
2223                               (priv->shadow_type == GTK_SHADOW_NONE ?
2224                                0 : padding.left + border.left));
2225       else
2226         child_allocation.x = 0;
2227
2228       child_allocation.y = relative_allocation.y;
2229       child_allocation.width = sb_width;
2230       child_allocation.height = relative_allocation.height;
2231       child_allocation.x += allocation->x;
2232       child_allocation.y += allocation->y;
2233
2234       if (priv->shadow_type != GTK_SHADOW_NONE)
2235         {
2236           if (!scrollbars_within_bevel)
2237             {
2238               child_allocation.y -= padding.top + border.top;
2239               child_allocation.height += padding.top + padding.bottom + border.top + border.bottom;
2240             }
2241           else if (GTK_CORNER_BOTTOM_LEFT == priv->real_window_placement ||
2242                    GTK_CORNER_TOP_LEFT == priv->real_window_placement)
2243             {
2244               child_allocation.x -= padding.left + border.left;
2245             }
2246           else
2247             {
2248               child_allocation.x += padding.left + border.left;
2249             }
2250         }
2251
2252       gtk_widget_size_allocate (priv->vscrollbar, &child_allocation);
2253     }
2254   else if (gtk_widget_get_visible (priv->vscrollbar))
2255     gtk_widget_hide (priv->vscrollbar);
2256
2257   _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
2258 }
2259
2260 static gboolean
2261 gtk_scrolled_window_scroll_event (GtkWidget      *widget,
2262                                   GdkEventScroll *event)
2263 {
2264   GtkScrolledWindowPrivate *priv;
2265   GtkScrolledWindow *scrolled_window;
2266   gboolean handled = FALSE;
2267   gdouble delta_x;
2268   gdouble delta_y;
2269   gdouble delta;
2270
2271   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE);
2272   g_return_val_if_fail (event != NULL, FALSE);
2273
2274   scrolled_window = GTK_SCROLLED_WINDOW (widget);
2275   priv = scrolled_window->priv;
2276
2277   if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y))
2278     {
2279       if (delta_x != 0.0 && priv->hscrollbar &&
2280           gtk_widget_get_visible (priv->hscrollbar))
2281         {
2282           GtkAdjustment *adj;
2283           gdouble new_value;
2284           gdouble page_size;
2285           gdouble scroll_unit;
2286
2287           adj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2288           page_size = gtk_adjustment_get_page_size (adj);
2289           scroll_unit = pow (page_size, 2.0 / 3.0);
2290
2291           new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_x * scroll_unit,
2292                              gtk_adjustment_get_lower (adj),
2293                              gtk_adjustment_get_upper (adj) -
2294                              gtk_adjustment_get_page_size (adj));
2295
2296           gtk_adjustment_set_value (adj, new_value);
2297
2298           handled = TRUE;
2299         }
2300
2301       if (delta_y != 0.0 && priv->vscrollbar &&
2302           gtk_widget_get_visible (priv->vscrollbar))
2303         {
2304           GtkAdjustment *adj;
2305           gdouble new_value;
2306           gdouble page_size;
2307           gdouble scroll_unit;
2308
2309           adj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2310           page_size = gtk_adjustment_get_page_size (adj);
2311           scroll_unit = pow (page_size, 2.0 / 3.0);
2312
2313           new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_y * scroll_unit,
2314                              gtk_adjustment_get_lower (adj),
2315                              gtk_adjustment_get_upper (adj) -
2316                              gtk_adjustment_get_page_size (adj));
2317
2318           gtk_adjustment_set_value (adj, new_value);
2319
2320           handled = TRUE;
2321         }
2322     }
2323   else
2324     {
2325       GtkWidget *range;
2326
2327       if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2328         range = priv->vscrollbar;
2329       else
2330         range = priv->hscrollbar;
2331
2332       if (range && gtk_widget_get_visible (range))
2333         {
2334           GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (range));
2335           gdouble new_value;
2336
2337           delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event);
2338
2339           new_value = CLAMP (gtk_adjustment_get_value (adj) + delta,
2340                              gtk_adjustment_get_lower (adj),
2341                              gtk_adjustment_get_upper (adj) -
2342                              gtk_adjustment_get_page_size (adj));
2343
2344           gtk_adjustment_set_value (adj, new_value);
2345
2346           handled = TRUE;
2347         }
2348     }
2349
2350   return handled;
2351 }
2352
2353 static gboolean
2354 _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
2355                                            GtkAdjustment     *adjustment,
2356                                            gdouble            value,
2357                                            gboolean           allow_overshooting,
2358                                            gboolean           snap_to_border)
2359 {
2360   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2361   gdouble lower, upper, *prev_value;
2362
2363   lower = gtk_adjustment_get_lower (adjustment);
2364   upper = gtk_adjustment_get_upper (adjustment) -
2365     gtk_adjustment_get_page_size (adjustment);
2366
2367   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
2368     prev_value = &priv->unclamped_hadj_value;
2369   else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
2370     prev_value = &priv->unclamped_vadj_value;
2371   else
2372     return FALSE;
2373
2374   if (snap_to_border)
2375     {
2376       if (*prev_value < 0 && value > 0)
2377         value = 0;
2378       else if (*prev_value > upper && value < upper)
2379         value = upper;
2380     }
2381
2382   if (allow_overshooting)
2383     {
2384       lower -= MAX_OVERSHOOT_DISTANCE;
2385       upper += MAX_OVERSHOOT_DISTANCE;
2386     }
2387
2388   *prev_value = CLAMP (value, lower, upper);
2389   gtk_adjustment_set_value (adjustment, value);
2390
2391   return (*prev_value != value);
2392 }
2393
2394 static gboolean
2395 scrolled_window_deceleration_cb (gpointer user_data)
2396 {
2397   KineticScrollData *data = user_data;
2398   GtkScrolledWindow *scrolled_window = data->scrolled_window;
2399   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2400   GtkAdjustment *hadjustment, *vadjustment;
2401   gint old_overshoot_x, old_overshoot_y, overshoot_x, overshoot_y;
2402   gdouble value;
2403   gint64 current_time;
2404   guint elapsed;
2405
2406   hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2407   vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2408
2409   _gtk_scrolled_window_get_overshoot (scrolled_window,
2410                                       &old_overshoot_x, &old_overshoot_y);
2411
2412   current_time = g_get_monotonic_time ();
2413   elapsed = (current_time - data->last_deceleration_time) / 1000;
2414   data->last_deceleration_time = current_time;
2415
2416   if (hadjustment && priv->hscrollbar_visible)
2417     {
2418       value = priv->unclamped_hadj_value + (data->x_velocity * elapsed);
2419
2420       if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
2421                                                      hadjustment,
2422                                                      value, TRUE, TRUE))
2423         data->x_velocity = 0;
2424     }
2425   else
2426     data->x_velocity = 0;
2427
2428   if (vadjustment && priv->vscrollbar_visible)
2429     {
2430       value = priv->unclamped_vadj_value + (data->y_velocity * elapsed);
2431
2432       if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
2433                                                      vadjustment,
2434                                                      value, TRUE, TRUE))
2435         data->y_velocity = 0;
2436     }
2437   else
2438     data->y_velocity = 0;
2439
2440   _gtk_scrolled_window_get_overshoot (scrolled_window,
2441                                       &overshoot_x, &overshoot_y);
2442
2443   if (overshoot_x == 0)
2444     {
2445       if (old_overshoot_x != 0)
2446         {
2447           /* Overshooting finished snapping back */
2448           data->x_velocity = 0;
2449         }
2450       else if (data->x_velocity > 0)
2451         {
2452           data->x_velocity -= FRICTION_DECELERATION * elapsed * data->vel_sine;
2453           data->x_velocity = MAX (0, data->x_velocity);
2454         }
2455       else if (data->x_velocity < 0)
2456         {
2457           data->x_velocity += FRICTION_DECELERATION * elapsed * data->vel_sine;
2458           data->x_velocity = MIN (0, data->x_velocity);
2459         }
2460     }
2461   else if (overshoot_x < 0)
2462     data->x_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2463   else if (overshoot_x > 0)
2464     data->x_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2465
2466   if (overshoot_y == 0)
2467     {
2468       if (old_overshoot_y != 0)
2469         {
2470           /* Overshooting finished snapping back */
2471           data->y_velocity = 0;
2472         }
2473       else if (data->y_velocity > 0)
2474         {
2475           data->y_velocity -= FRICTION_DECELERATION * elapsed * data->vel_cosine;
2476           data->y_velocity = MAX (0, data->y_velocity);
2477         }
2478       else if (data->y_velocity < 0)
2479         {
2480           data->y_velocity += FRICTION_DECELERATION * elapsed * data->vel_cosine;
2481           data->y_velocity = MIN (0, data->y_velocity);
2482         }
2483     }
2484   else if (overshoot_y < 0)
2485     data->y_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2486   else if (overshoot_y > 0)
2487     data->y_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2488
2489   if (old_overshoot_x != overshoot_x ||
2490       old_overshoot_y != overshoot_y)
2491     {
2492       if (overshoot_x >= 0 || overshoot_y >= 0)
2493         {
2494           /* We need to reallocate the widget to have it at
2495            * negative offset, so there's a "gravity" on the
2496            * bottom/right corner
2497            */
2498           gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2499         }
2500       else if (overshoot_x < 0 || overshoot_y < 0)
2501         _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
2502     }
2503
2504   if (overshoot_x != 0 || overshoot_y != 0 ||
2505       data->x_velocity != 0 || data->y_velocity != 0)
2506     return TRUE;
2507   else
2508     {
2509       priv->deceleration_id = 0;
2510       return FALSE;
2511     }
2512 }
2513
2514 static void
2515 gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window)
2516 {
2517   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2518
2519   if (priv->deceleration_id)
2520     {
2521       g_source_remove (priv->deceleration_id);
2522       priv->deceleration_id = 0;
2523     }
2524 }
2525
2526 static void
2527 gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
2528 {
2529   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2530   KineticScrollData *data;
2531   gdouble angle;
2532
2533   data = g_new0 (KineticScrollData, 1);
2534   data->scrolled_window = scrolled_window;
2535   data->last_deceleration_time = g_get_monotonic_time ();
2536   data->x_velocity = priv->x_velocity;
2537   data->y_velocity = priv->y_velocity;
2538
2539   /* We use sine/cosine as a factor to deceleration x/y components
2540    * of the vector, so we care about the sign later.
2541    */
2542   angle = atan2 (ABS (data->x_velocity), ABS (data->y_velocity));
2543   data->vel_cosine = cos (angle);
2544   data->vel_sine = sin (angle);
2545
2546   scrolled_window->priv->deceleration_id =
2547     gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
2548                                   FRAME_INTERVAL,
2549                                   scrolled_window_deceleration_cb,
2550                                   data, (GDestroyNotify) g_free);
2551 }
2552
2553 static gboolean
2554 gtk_scrolled_window_release_captured_event (GtkScrolledWindow *scrolled_window)
2555 {
2556   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2557
2558   /* Cancel the scrolling and send the button press
2559    * event to the child widget
2560    */
2561   if (!priv->button_press_event)
2562     return FALSE;
2563
2564   if (priv->drag_device)
2565     {
2566       gtk_device_grab_remove (GTK_WIDGET (scrolled_window), priv->drag_device);
2567       priv->drag_device = NULL;
2568     }
2569
2570   if (priv->capture_button_press)
2571     {
2572       GtkWidget *event_widget;
2573
2574       event_widget = gtk_get_event_widget (priv->button_press_event);
2575
2576       if (!_gtk_propagate_captured_event (event_widget,
2577                                           priv->button_press_event,
2578                                           gtk_bin_get_child (GTK_BIN (scrolled_window))))
2579         gtk_propagate_event (event_widget, priv->button_press_event);
2580
2581       gdk_event_free (priv->button_press_event);
2582       priv->button_press_event = NULL;
2583     }
2584
2585   if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
2586     gtk_scrolled_window_start_deceleration (scrolled_window);
2587
2588   return FALSE;
2589 }
2590
2591 static gboolean
2592 gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
2593                                         GdkEvent          *event)
2594 {
2595   GtkScrolledWindowPrivate *priv;
2596   gdouble x_root, y_root;
2597   guint32 _time;
2598
2599 #define STILL_THRESHOLD 40
2600
2601   if (!gdk_event_get_root_coords (event, &x_root, &y_root))
2602     return FALSE;
2603
2604   priv = scrolled_window->priv;
2605   _time = gdk_event_get_time (event);
2606
2607   if (priv->last_motion_event_x_root != x_root ||
2608       priv->last_motion_event_y_root != y_root ||
2609       ABS (_time - priv->last_motion_event_time) > STILL_THRESHOLD)
2610     {
2611       priv->x_velocity = (priv->last_motion_event_x_root - x_root) /
2612         (gdouble) (_time - priv->last_motion_event_time);
2613       priv->y_velocity = (priv->last_motion_event_y_root - y_root) /
2614         (gdouble) (_time - priv->last_motion_event_time);
2615     }
2616
2617   priv->last_motion_event_x_root = x_root;
2618   priv->last_motion_event_y_root = y_root;
2619   priv->last_motion_event_time = _time;
2620
2621 #undef STILL_THRESHOLD
2622
2623   return TRUE;
2624 }
2625
2626 static gboolean
2627 gtk_scrolled_window_captured_button_release (GtkWidget *widget,
2628                                              GdkEvent  *event)
2629 {
2630   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2631   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2632   GtkWidget *child;
2633   gboolean overshoot;
2634   guint button;
2635   gdouble x_root, y_root;
2636
2637   if (gdk_event_get_button (event, &button) && button != 1)
2638     return FALSE;
2639
2640   child = gtk_bin_get_child (GTK_BIN (widget));
2641   if (!child)
2642     return FALSE;
2643
2644   gtk_device_grab_remove (widget, priv->drag_device);
2645   priv->drag_device = NULL;
2646
2647   if (priv->release_timeout_id)
2648     {
2649       g_source_remove (priv->release_timeout_id);
2650       priv->release_timeout_id = 0;
2651     }
2652
2653   overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL);
2654
2655   if (priv->in_drag)
2656     gdk_device_ungrab (gdk_event_get_device (event), gdk_event_get_time (event));
2657   else
2658     {
2659       /* There hasn't been scrolling at all, so just let the
2660        * child widget handle the button press normally
2661        */
2662       gtk_scrolled_window_release_captured_event (scrolled_window);
2663
2664       if (!overshoot)
2665         return FALSE;
2666     }
2667   priv->in_drag = FALSE;
2668
2669   if (priv->button_press_event)
2670     {
2671       gdk_event_free (priv->button_press_event);
2672       priv->button_press_event = NULL;
2673     }
2674
2675   gtk_scrolled_window_calculate_velocity (scrolled_window, event);
2676
2677   /* Zero out vector components without a visible scrollbar */
2678   if (!priv->hscrollbar_visible)
2679     priv->x_velocity = 0;
2680   if (!priv->vscrollbar_visible)
2681     priv->y_velocity = 0;
2682
2683   if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot)
2684     {
2685       gtk_scrolled_window_start_deceleration (scrolled_window);
2686       priv->x_velocity = priv->y_velocity = 0;
2687       priv->last_button_event_valid = FALSE;
2688     }
2689   else
2690     {
2691       gdk_event_get_root_coords (event, &x_root, &y_root);
2692       priv->last_button_event_x_root = x_root;
2693       priv->last_button_event_y_root = y_root;
2694       priv->last_button_event_valid = TRUE;
2695     }
2696
2697   if (priv->capture_button_press)
2698     return TRUE;
2699   else
2700     return FALSE;
2701 }
2702
2703 static gboolean
2704 gtk_scrolled_window_captured_motion_notify (GtkWidget *widget,
2705                                             GdkEvent  *event)
2706 {
2707   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2708   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2709   gint old_overshoot_x, old_overshoot_y;
2710   gint new_overshoot_x, new_overshoot_y;
2711   GtkWidget *child;
2712   GtkAdjustment *hadjustment;
2713   GtkAdjustment *vadjustment;
2714   gdouble dx, dy;
2715   GdkModifierType state;
2716   gdouble x_root, y_root;
2717
2718   gdk_event_get_state (event, &state);
2719   if (!(state & GDK_BUTTON1_MASK))
2720     return FALSE;
2721
2722   child = gtk_bin_get_child (GTK_BIN (widget));
2723   if (!child)
2724     return FALSE;
2725
2726   /* Check if we've passed the drag threshold */
2727   gdk_event_get_root_coords (event, &x_root, &y_root);
2728   if (!priv->in_drag)
2729     {
2730       if (gtk_drag_check_threshold (widget,
2731                                     priv->last_button_event_x_root,
2732                                     priv->last_button_event_y_root,
2733                                     x_root, y_root))
2734         {
2735           if (priv->release_timeout_id)
2736             {
2737               g_source_remove (priv->release_timeout_id);
2738               priv->release_timeout_id = 0;
2739             }
2740
2741           priv->last_button_event_valid = FALSE;
2742           priv->in_drag = TRUE;
2743         }
2744       else
2745         return TRUE;
2746     }
2747
2748   gdk_device_grab (priv->drag_device,
2749                    gtk_widget_get_window (widget),
2750                    GDK_OWNERSHIP_WINDOW,
2751                    TRUE,
2752                    GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK,
2753                    NULL,
2754                    gdk_event_get_time (event));
2755
2756   priv->last_button_event_valid = FALSE;
2757
2758   if (priv->button_press_event)
2759     {
2760       gdk_event_free (priv->button_press_event);
2761       priv->button_press_event = NULL;
2762     }
2763
2764   _gtk_scrolled_window_get_overshoot (scrolled_window,
2765                                       &old_overshoot_x, &old_overshoot_y);
2766
2767   hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2768   if (hadjustment && priv->hscrollbar_visible)
2769     {
2770       dx = (priv->last_motion_event_x_root - x_root) + priv->unclamped_hadj_value;
2771       _gtk_scrolled_window_set_adjustment_value (scrolled_window, hadjustment,
2772                                                  dx, TRUE, FALSE);
2773     }
2774
2775   vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2776   if (vadjustment && priv->vscrollbar_visible)
2777     {
2778       dy = (priv->last_motion_event_y_root - y_root) + priv->unclamped_vadj_value;
2779       _gtk_scrolled_window_set_adjustment_value (scrolled_window, vadjustment,
2780                                                  dy, TRUE, FALSE);
2781     }
2782
2783   _gtk_scrolled_window_get_overshoot (scrolled_window,
2784                                       &new_overshoot_x, &new_overshoot_y);
2785
2786   if (old_overshoot_x != new_overshoot_x ||
2787       old_overshoot_y != new_overshoot_y)
2788     {
2789       if (new_overshoot_x >= 0 || new_overshoot_y >= 0)
2790         {
2791           /* We need to reallocate the widget to have it at
2792            * negative offset, so there's a "gravity" on the
2793            * bottom/right corner
2794            */
2795           gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2796         }
2797       else if (new_overshoot_x < 0 || new_overshoot_y < 0)
2798         _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
2799     }
2800
2801   gtk_scrolled_window_calculate_velocity (scrolled_window, event);
2802
2803   return TRUE;
2804 }
2805
2806 static gboolean
2807 gtk_scrolled_window_captured_button_press (GtkWidget *widget,
2808                                            GdkEvent  *event)
2809 {
2810   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2811   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2812   GtkWidget *child;
2813   GtkWidget *event_widget;
2814   GdkDevice *source_device;
2815   GdkInputSource source;
2816   gdouble x_root, y_root;
2817   guint button;
2818
2819   /* If scrollbars are not visible, we don't do kinetic scrolling */
2820   if (!priv->vscrollbar_visible && !priv->hscrollbar_visible)
2821     return FALSE;
2822
2823   source_device = gdk_event_get_source_device (event);
2824   source = gdk_device_get_source (source_device);
2825
2826   if (source != GDK_SOURCE_TOUCHSCREEN)
2827     return FALSE;
2828
2829   event_widget = gtk_get_event_widget (event);
2830
2831   /* If there's another scrolled window between the widget
2832    * receiving the event and this capturing scrolled window,
2833    * let it handle the events.
2834    */
2835   if (widget != gtk_widget_get_ancestor (event_widget, GTK_TYPE_SCROLLED_WINDOW))
2836     return FALSE;
2837
2838   /* Check whether the button press is close to the previous one,
2839    * take that as a shortcut to get the child widget handle events
2840    */
2841   gdk_event_get_root_coords (event, &x_root, &y_root);
2842   if (priv->last_button_event_valid &&
2843       ABS (x_root - priv->last_button_event_x_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD &&
2844       ABS (y_root - priv->last_button_event_y_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD)
2845     {
2846       priv->last_button_event_valid = FALSE;
2847       return FALSE;
2848     }
2849
2850   priv->last_button_event_x_root = priv->last_motion_event_x_root = x_root;
2851   priv->last_button_event_y_root = priv->last_motion_event_y_root = y_root;
2852   priv->last_motion_event_time = gdk_event_get_time (event);
2853   priv->last_button_event_valid = TRUE;
2854
2855   if (gdk_event_get_button (event, &button) && button != 1)
2856     return FALSE;
2857
2858   child = gtk_bin_get_child (GTK_BIN (widget));
2859   if (!child)
2860     return FALSE;
2861
2862   if (priv->hscrollbar == event_widget || priv->vscrollbar == event_widget)
2863     return FALSE;
2864
2865   priv->drag_device = gdk_event_get_device (event);
2866   gtk_device_grab_add (widget, priv->drag_device, TRUE);
2867
2868   gtk_scrolled_window_cancel_deceleration (scrolled_window);
2869
2870   /* Only set the timeout if we're going to store an event */
2871   if (priv->capture_button_press)
2872     priv->release_timeout_id =
2873       gdk_threads_add_timeout (RELEASE_EVENT_TIMEOUT,
2874                                (GSourceFunc) gtk_scrolled_window_release_captured_event,
2875                                scrolled_window);
2876
2877   priv->in_drag = FALSE;
2878
2879   if (priv->capture_button_press)
2880     {
2881       /* Store the button press event in
2882        * case we need to propagate it later
2883        */
2884       priv->button_press_event = gdk_event_copy (event);
2885       return TRUE;
2886     }
2887   else
2888     return FALSE;
2889 }
2890
2891 static gboolean
2892 gtk_scrolled_window_captured_event (GtkWidget *widget,
2893                                     GdkEvent  *event)
2894 {
2895   gboolean retval = FALSE;
2896   GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW (widget)->priv;
2897
2898   switch (event->type)
2899     {
2900     case GDK_TOUCH_BEGIN:
2901     case GDK_BUTTON_PRESS:
2902       retval = gtk_scrolled_window_captured_button_press (widget, event);
2903       break;
2904     case GDK_TOUCH_END:
2905     case GDK_BUTTON_RELEASE:
2906       if (priv->drag_device)
2907         retval = gtk_scrolled_window_captured_button_release (widget, event);
2908       else
2909         priv->last_button_event_valid = FALSE;
2910       break;
2911     case GDK_TOUCH_UPDATE:
2912     case GDK_MOTION_NOTIFY:
2913       if (priv->drag_device)
2914         retval = gtk_scrolled_window_captured_motion_notify (widget, event);
2915       break;
2916     case GDK_LEAVE_NOTIFY:
2917     case GDK_ENTER_NOTIFY:
2918       if (priv->in_drag &&
2919           event->crossing.mode != GDK_CROSSING_GRAB)
2920         retval = TRUE;
2921       break;
2922     default:
2923       break;
2924     }
2925
2926   return retval;
2927 }
2928
2929 static gboolean
2930 gtk_scrolled_window_focus (GtkWidget        *widget,
2931                            GtkDirectionType  direction)
2932 {
2933   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2934   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2935   GtkWidget *child;
2936   gboolean had_focus_child;
2937
2938   had_focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget)) != NULL;
2939
2940   if (priv->focus_out)
2941     {
2942       priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
2943       return FALSE;
2944     }
2945   
2946   if (gtk_widget_is_focus (widget))
2947     return FALSE;
2948
2949   /* We only put the scrolled window itself in the focus chain if it
2950    * isn't possible to focus any children.
2951    */
2952   child = gtk_bin_get_child (GTK_BIN (widget));
2953   if (child)
2954     {
2955       if (gtk_widget_child_focus (child, direction))
2956         return TRUE;
2957     }
2958
2959   if (!had_focus_child && gtk_widget_get_can_focus (widget))
2960     {
2961       gtk_widget_grab_focus (widget);
2962       return TRUE;
2963     }
2964   else
2965     return FALSE;
2966 }
2967
2968 static void
2969 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
2970                                         gpointer       data)
2971 {
2972   GtkScrolledWindowPrivate *priv;
2973   GtkScrolledWindow *scrolled_window;
2974
2975   g_return_if_fail (adjustment != NULL);
2976   g_return_if_fail (data != NULL);
2977
2978   scrolled_window = GTK_SCROLLED_WINDOW (data);
2979   priv = scrolled_window->priv;
2980
2981   if (priv->hscrollbar &&
2982       adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
2983     {
2984       if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2985         {
2986           gboolean visible;
2987
2988           visible = priv->hscrollbar_visible;
2989           priv->hscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
2990                                       gtk_adjustment_get_page_size (adjustment));
2991
2992           if (priv->hscrollbar_visible != visible)
2993             gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2994         }
2995     }
2996   else if (priv->vscrollbar &&
2997            adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
2998     {
2999       if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
3000         {
3001           gboolean visible;
3002
3003           visible = priv->vscrollbar_visible;
3004           priv->vscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
3005                                       gtk_adjustment_get_page_size (adjustment));
3006
3007           if (priv->vscrollbar_visible != visible)
3008             gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3009         }
3010     }
3011 }
3012
3013 static void
3014 gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
3015                                               gpointer       user_data)
3016 {
3017   GtkScrolledWindow *scrolled_window = user_data;
3018   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3019
3020   /* Allow overshooting for kinetic scrolling operations */
3021   if (priv->drag_device || priv->deceleration_id)
3022     return;
3023
3024   /* Ensure GtkAdjustment and unclamped values are in sync */
3025   if (priv->vscrollbar &&
3026       adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
3027     priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
3028   else if (priv->hscrollbar &&
3029            adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
3030     priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
3031 }
3032
3033 static void
3034 gtk_scrolled_window_add (GtkContainer *container,
3035                          GtkWidget    *child)
3036 {
3037   GtkScrolledWindowPrivate *priv;
3038   GtkScrolledWindow *scrolled_window;
3039   GtkBin *bin;
3040   GtkWidget *child_widget;
3041   GtkAdjustment *hadj, *vadj;
3042
3043   bin = GTK_BIN (container);
3044   child_widget = gtk_bin_get_child (bin);
3045   g_return_if_fail (child_widget == NULL);
3046
3047   scrolled_window = GTK_SCROLLED_WINDOW (container);
3048   priv = scrolled_window->priv;
3049
3050   if (gtk_widget_get_realized (GTK_WIDGET (bin)))
3051     gtk_widget_set_parent_window (child, priv->overshoot_window);
3052
3053   _gtk_bin_set_child (bin, child);
3054   gtk_widget_set_parent (child, GTK_WIDGET (bin));
3055
3056   hadj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
3057   vadj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
3058
3059   if (GTK_IS_SCROLLABLE (child))
3060     g_object_set (child, "hadjustment", hadj, "vadjustment", vadj, NULL);
3061   else
3062     g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
3063                "use gtk_scrolled_window_add_with_viewport() instead");
3064 }
3065
3066 static void
3067 gtk_scrolled_window_remove (GtkContainer *container,
3068                             GtkWidget    *child)
3069 {
3070   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
3071   g_return_if_fail (child != NULL);
3072   g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
3073
3074   g_object_set (child, "hadjustment", NULL, "vadjustment", NULL, NULL);
3075
3076   /* chain parent class handler to remove child */
3077   GTK_CONTAINER_CLASS (gtk_scrolled_window_parent_class)->remove (container, child);
3078 }
3079
3080 /**
3081  * gtk_scrolled_window_add_with_viewport:
3082  * @scrolled_window: a #GtkScrolledWindow
3083  * @child: the widget you want to scroll
3084  *
3085  * Used to add children without native scrolling capabilities. This
3086  * is simply a convenience function; it is equivalent to adding the
3087  * unscrollable child to a viewport, then adding the viewport to the
3088  * scrolled window. If a child has native scrolling, use
3089  * gtk_container_add() instead of this function.
3090  *
3091  * The viewport scrolls the child by moving its #GdkWindow, and takes
3092  * the size of the child to be the size of its toplevel #GdkWindow. 
3093  * This will be very wrong for most widgets that support native scrolling;
3094  * for example, if you add a widget such as #GtkTreeView with a viewport,
3095  * the whole widget will scroll, including the column headings. Thus, 
3096  * widgets with native scrolling support should not be used with the 
3097  * #GtkViewport proxy.
3098  *
3099  * A widget supports scrolling natively if it implements the
3100  * #GtkScrollable interface.
3101  */
3102 void
3103 gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
3104                                        GtkWidget         *child)
3105 {
3106   GtkBin *bin;
3107   GtkWidget *viewport;
3108   GtkWidget *child_widget;
3109
3110   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3111   g_return_if_fail (GTK_IS_WIDGET (child));
3112   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
3113
3114   bin = GTK_BIN (scrolled_window);
3115   child_widget = gtk_bin_get_child (bin);
3116
3117   if (child_widget)
3118     {
3119       g_return_if_fail (GTK_IS_VIEWPORT (child_widget));
3120       g_return_if_fail (gtk_bin_get_child (GTK_BIN (child_widget)) == NULL);
3121
3122       viewport = child_widget;
3123     }
3124   else
3125     {
3126       viewport =
3127         gtk_viewport_new (gtk_scrolled_window_get_hadjustment (scrolled_window),
3128                           gtk_scrolled_window_get_vadjustment (scrolled_window));
3129       gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
3130     }
3131
3132   gtk_widget_show (viewport);
3133   gtk_container_add (GTK_CONTAINER (viewport), child);
3134 }
3135
3136 /*
3137  * _gtk_scrolled_window_get_spacing:
3138  * @scrolled_window: a scrolled window
3139  * 
3140  * Gets the spacing between the scrolled window's scrollbars and
3141  * the scrolled widget. Used by GtkCombo
3142  * 
3143  * Return value: the spacing, in pixels.
3144  */
3145 gint
3146 _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
3147 {
3148   GtkScrolledWindowClass *class;
3149     
3150   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
3151
3152   class = GTK_SCROLLED_WINDOW_GET_CLASS (scrolled_window);
3153
3154   if (class->scrollbar_spacing >= 0)
3155     return class->scrollbar_spacing;
3156   else
3157     {
3158       gint scrollbar_spacing;
3159       
3160       gtk_widget_style_get (GTK_WIDGET (scrolled_window),
3161                             "scrollbar-spacing", &scrollbar_spacing,
3162                             NULL);
3163
3164       return scrollbar_spacing;
3165     }
3166 }
3167
3168
3169 static void
3170 gtk_scrolled_window_get_preferred_size (GtkWidget      *widget,
3171                                         GtkOrientation  orientation,
3172                                         gint           *minimum_size,
3173                                         gint           *natural_size)
3174 {
3175   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3176   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3177   GtkBin *bin = GTK_BIN (scrolled_window);
3178   gint extra_width;
3179   gint extra_height;
3180   gint scrollbar_spacing;
3181   GtkRequisition hscrollbar_requisition;
3182   GtkRequisition vscrollbar_requisition;
3183   GtkRequisition minimum_req, natural_req;
3184   GtkWidget *child;
3185   gint min_child_size, nat_child_size;
3186
3187   scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
3188
3189   extra_width = 0;
3190   extra_height = 0;
3191   minimum_req.width = 0;
3192   minimum_req.height = 0;
3193   natural_req.width = 0;
3194   natural_req.height = 0;
3195
3196   gtk_widget_get_preferred_size (priv->hscrollbar,
3197                                  &hscrollbar_requisition, NULL);
3198   gtk_widget_get_preferred_size (priv->vscrollbar,
3199                                  &vscrollbar_requisition, NULL);
3200
3201   child = gtk_bin_get_child (bin);
3202   if (child && gtk_widget_get_visible (child))
3203     {
3204       if (orientation == GTK_ORIENTATION_HORIZONTAL)
3205         {
3206           gtk_widget_get_preferred_width (child,
3207                                           &min_child_size,
3208                                           &nat_child_size);
3209
3210           if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
3211             {
3212               minimum_req.width += min_child_size;
3213               natural_req.width += nat_child_size;
3214             }
3215           else
3216             {
3217               gint min_content_width = priv->min_content_width;
3218
3219               if (min_content_width >= 0)
3220                 {
3221                   minimum_req.width = MAX (minimum_req.width, min_content_width);
3222                   natural_req.width = MAX (natural_req.width, min_content_width);
3223                   extra_width = -1;
3224                 }
3225               else
3226                 {
3227                   minimum_req.width += vscrollbar_requisition.width;
3228                   natural_req.width += vscrollbar_requisition.width;
3229                 }
3230             }
3231         }
3232       else /* GTK_ORIENTATION_VERTICAL */
3233         {
3234           gtk_widget_get_preferred_height (child,
3235                                            &min_child_size,
3236                                            &nat_child_size);
3237
3238           if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
3239             {
3240               minimum_req.height += min_child_size;
3241               natural_req.height += nat_child_size;
3242             }
3243           else
3244             {
3245               gint min_content_height = priv->min_content_height;
3246
3247               if (min_content_height >= 0)
3248                 {
3249                   minimum_req.height = MAX (minimum_req.height, min_content_height);
3250                   natural_req.height = MAX (natural_req.height, min_content_height);
3251                   extra_height = -1;
3252                 }
3253               else
3254                 {
3255                   minimum_req.height += vscrollbar_requisition.height;
3256                   natural_req.height += vscrollbar_requisition.height;
3257                 }
3258             }
3259         }
3260     }
3261
3262   if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
3263       priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
3264     {
3265       minimum_req.width = MAX (minimum_req.width, hscrollbar_requisition.width);
3266       natural_req.width = MAX (natural_req.width, hscrollbar_requisition.width);
3267       if (!extra_height || priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
3268         extra_height = scrollbar_spacing + hscrollbar_requisition.height;
3269     }
3270
3271   if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
3272       priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
3273     {
3274       minimum_req.height = MAX (minimum_req.height, vscrollbar_requisition.height);
3275       natural_req.height = MAX (natural_req.height, vscrollbar_requisition.height);
3276       if (!extra_width || priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
3277         extra_width = scrollbar_spacing + vscrollbar_requisition.width;
3278     }
3279
3280   minimum_req.width  += MAX (0, extra_width);
3281   minimum_req.height += MAX (0, extra_height);
3282   natural_req.width  += MAX (0, extra_width);
3283   natural_req.height += MAX (0, extra_height);
3284
3285   if (priv->shadow_type != GTK_SHADOW_NONE)
3286     {
3287       GtkStyleContext *context;
3288       GtkStateFlags state;
3289       GtkBorder padding, border;
3290
3291       context = gtk_widget_get_style_context (GTK_WIDGET (widget));
3292       state = gtk_widget_get_state_flags (GTK_WIDGET (widget));
3293
3294       gtk_style_context_save (context);
3295       gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
3296
3297       gtk_style_context_get_padding (context, state, &padding);
3298       gtk_style_context_get_border (context, state, &border);
3299
3300       minimum_req.width += padding.left + padding.right + border.left + border.right;
3301       minimum_req.height += padding.top + padding.bottom + border.top + border.bottom;
3302       natural_req.width += padding.left + padding.right + border.left + border.right;
3303       natural_req.height += padding.top + padding.bottom + border.top + border.bottom;
3304
3305       gtk_style_context_restore (context);
3306     }
3307
3308   if (orientation == GTK_ORIENTATION_HORIZONTAL)
3309     {
3310       if (minimum_size)
3311         *minimum_size = minimum_req.width;
3312       if (natural_size)
3313         *natural_size = natural_req.width;
3314     }
3315   else
3316     {
3317       if (minimum_size)
3318         *minimum_size = minimum_req.height;
3319       if (natural_size)
3320         *natural_size = natural_req.height;
3321     }
3322 }
3323
3324 static void     
3325 gtk_scrolled_window_get_preferred_width (GtkWidget *widget,
3326                                          gint      *minimum_size,
3327                                          gint      *natural_size)
3328 {
3329   gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
3330 }
3331
3332 static void
3333 gtk_scrolled_window_get_preferred_height (GtkWidget *widget,
3334                                           gint      *minimum_size,
3335                                           gint      *natural_size)
3336 {  
3337   gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
3338 }
3339
3340 static void
3341 gtk_scrolled_window_get_preferred_height_for_width (GtkWidget *widget,
3342                                                     gint       width,
3343                                                     gint      *minimum_height,
3344                                                     gint      *natural_height)
3345 {
3346   g_return_if_fail (GTK_IS_WIDGET (widget));
3347
3348   GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
3349 }
3350
3351 static void
3352 gtk_scrolled_window_get_preferred_width_for_height (GtkWidget *widget,
3353                                                     gint       height,
3354                                                     gint      *minimum_width,
3355                                                     gint      *natural_width)
3356 {
3357   g_return_if_fail (GTK_IS_WIDGET (widget));
3358
3359   GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
3360 }
3361
3362 static void
3363 gtk_scrolled_window_realize (GtkWidget *widget)
3364 {
3365   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3366   GtkAllocation allocation, relative_allocation;
3367   GdkWindowAttr attributes;
3368   GtkWidget *child_widget;
3369   gint attributes_mask;
3370
3371   gtk_widget_set_realized (widget, TRUE);
3372   gtk_widget_get_allocation (widget, &allocation);
3373   gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
3374
3375   attributes.window_type = GDK_WINDOW_CHILD;
3376   attributes.x = allocation.x + relative_allocation.x;
3377   attributes.y = allocation.y + relative_allocation.y;
3378   attributes.width = allocation.width;
3379   attributes.height = allocation.height;
3380   attributes.wclass = GDK_INPUT_OUTPUT;
3381   attributes.visual = gtk_widget_get_visual (widget);
3382   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK |
3383     GDK_BUTTON_MOTION_MASK | GDK_TOUCH_MASK;
3384
3385   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3386
3387   scrolled_window->priv->overshoot_window =
3388     gdk_window_new (gtk_widget_get_parent_window (widget),
3389                     &attributes, attributes_mask);
3390
3391   gdk_window_set_user_data (scrolled_window->priv->overshoot_window, widget);
3392
3393   child_widget = gtk_bin_get_child (GTK_BIN (widget));
3394
3395   if (child_widget)
3396     gtk_widget_set_parent_window (child_widget,
3397                                   scrolled_window->priv->overshoot_window);
3398
3399   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->realize (widget);
3400 }
3401
3402 static void
3403 gtk_scrolled_window_unrealize (GtkWidget *widget)
3404 {
3405   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3406
3407   gdk_window_set_user_data (scrolled_window->priv->overshoot_window, NULL);
3408   gdk_window_destroy (scrolled_window->priv->overshoot_window);
3409   scrolled_window->priv->overshoot_window = NULL;
3410
3411   gtk_widget_set_realized (widget, FALSE);
3412
3413   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unrealize (widget);
3414 }
3415
3416 static void
3417 gtk_scrolled_window_map (GtkWidget *widget)
3418 {
3419   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3420
3421   gdk_window_show (scrolled_window->priv->overshoot_window);
3422
3423   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->map (widget);
3424 }
3425
3426 static void
3427 gtk_scrolled_window_unmap (GtkWidget *widget)
3428 {
3429   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3430
3431   gdk_window_hide (scrolled_window->priv->overshoot_window);
3432
3433   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unmap (widget);
3434 }
3435
3436 static void
3437 _gtk_scrolled_window_update_background (GtkScrolledWindow *scrolled_window)
3438 {
3439   GtkWidget *widget = GTK_WIDGET (scrolled_window);
3440
3441   if (gtk_widget_get_realized (widget))
3442     {
3443       GtkStyleContext *context;
3444
3445       context = gtk_widget_get_style_context (widget);
3446       gtk_style_context_set_background (context, scrolled_window->priv->overshoot_window);
3447     }
3448 }
3449
3450 static void
3451 gtk_scrolled_window_state_flags_changed (GtkWidget     *widget,
3452                                          GtkStateFlags  previous_state)
3453 {
3454   _gtk_scrolled_window_update_background (GTK_SCROLLED_WINDOW (widget));
3455   gtk_widget_queue_draw (widget);
3456 }
3457
3458 static void
3459 gtk_scrolled_window_style_updated (GtkWidget *widget)
3460 {
3461   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->style_updated (widget);
3462
3463   _gtk_scrolled_window_update_background (GTK_SCROLLED_WINDOW (widget));
3464   gtk_widget_queue_draw (widget);
3465 }
3466
3467 static void
3468 gtk_scrolled_window_grab_notify (GtkWidget *widget,
3469                                  gboolean   was_grabbed)
3470 {
3471   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3472   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3473
3474   if (priv->drag_device &&
3475       gtk_widget_device_is_shadowed (widget,
3476                                      priv->drag_device))
3477     {
3478       gdk_device_ungrab (priv->drag_device,
3479                          gtk_get_current_event_time ());
3480       priv->drag_device = NULL;
3481       priv->in_drag = FALSE;
3482
3483       if (priv->release_timeout_id)
3484         {
3485           g_source_remove (priv->release_timeout_id);
3486           priv->release_timeout_id = 0;
3487         }
3488
3489       if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
3490         gtk_scrolled_window_start_deceleration (scrolled_window);
3491       else
3492         gtk_scrolled_window_cancel_deceleration (scrolled_window);
3493
3494       priv->last_button_event_valid = FALSE;
3495     }
3496 }
3497
3498 /**
3499  * gtk_scrolled_window_get_min_content_width:
3500  * @scrolled_window: a #GtkScrolledWindow
3501  *
3502  * Gets the minimum content width of @scrolled_window, or -1 if not set.
3503  *
3504  * Returns: the minimum content width
3505  *
3506  * Since: 3.0
3507  */
3508 gint
3509 gtk_scrolled_window_get_min_content_width (GtkScrolledWindow *scrolled_window)
3510 {
3511   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
3512
3513   return scrolled_window->priv->min_content_width;
3514 }
3515
3516 /**
3517  * gtk_scrolled_window_set_min_content_width:
3518  * @scrolled_window: a #GtkScrolledWindow
3519  * @width: the minimal content width
3520  *
3521  * Sets the minimum width that @scrolled_window should keep visible.
3522  * Note that this can and (usually will) be smaller than the minimum
3523  * size of the content.
3524  *
3525  * Since: 3.0
3526  */
3527 void
3528 gtk_scrolled_window_set_min_content_width (GtkScrolledWindow *scrolled_window,
3529                                            gint               width)
3530 {
3531   GtkScrolledWindowPrivate *priv;
3532
3533   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3534
3535   priv = scrolled_window->priv;
3536
3537   if (priv->min_content_width != width)
3538     {
3539       priv->min_content_width = width;
3540
3541       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3542
3543       g_object_notify (G_OBJECT (scrolled_window), "min-content-width");
3544     }
3545 }
3546
3547 /**
3548  * gtk_scrolled_window_get_min_content_height:
3549  * @scrolled_window: a #GtkScrolledWindow
3550  *
3551  * Gets the minimal content height of @scrolled_window, or -1 if not set.
3552  *
3553  * Returns: the minimal content height
3554  *
3555  * Since: 3.0
3556  */
3557 gint
3558 gtk_scrolled_window_get_min_content_height (GtkScrolledWindow *scrolled_window)
3559 {
3560   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
3561
3562   return scrolled_window->priv->min_content_height;
3563 }
3564
3565 /**
3566  * gtk_scrolled_window_set_min_content_height:
3567  * @scrolled_window: a #GtkScrolledWindow
3568  * @height: the minimal content height
3569  *
3570  * Sets the minimum height that @scrolled_window should keep visible.
3571  * Note that this can and (usually will) be smaller than the minimum
3572  * size of the content.
3573  *
3574  * Since: 3.0
3575  */
3576 void
3577 gtk_scrolled_window_set_min_content_height (GtkScrolledWindow *scrolled_window,
3578                                             gint               height)
3579 {
3580   GtkScrolledWindowPrivate *priv;
3581
3582   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3583
3584   priv = scrolled_window->priv;
3585
3586   if (priv->min_content_height != height)
3587     {
3588       priv->min_content_height = height;
3589
3590       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3591
3592       g_object_notify (G_OBJECT (scrolled_window), "min-content-height");
3593     }
3594 }