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