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