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