]> Pileus Git - ~andy/gtk/blob - gtk/gtkscrolledwindow.c
scrolledwindow: Fix scroll event handling
[~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   GtkAllocation wid_allocation, hscr_allocation, vscr_allocation;
1437   GtkStyleContext *context;
1438   GdkRectangle junction_rect;
1439   gboolean is_rtl;
1440
1441   is_rtl = gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL;
1442   gtk_widget_get_allocation (GTK_WIDGET (scrolled_window), &wid_allocation);
1443   gtk_widget_get_allocation (GTK_WIDGET (priv->hscrollbar), &hscr_allocation);
1444   gtk_widget_get_allocation (GTK_WIDGET (priv->vscrollbar), &vscr_allocation);
1445
1446   junction_rect.x = 0;
1447   junction_rect.y = 0;
1448   junction_rect.width = vscr_allocation.width;
1449   junction_rect.height = hscr_allocation.height;
1450   
1451   if ((is_rtl && 
1452        (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
1453         priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
1454       (!is_rtl && 
1455        (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1456         priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
1457     junction_rect.x = hscr_allocation.width;
1458
1459   if (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1460       priv->real_window_placement == GTK_CORNER_TOP_RIGHT)
1461     junction_rect.y = vscr_allocation.height;
1462
1463   context = gtk_widget_get_style_context (GTK_WIDGET (scrolled_window));
1464   gtk_style_context_save (context);
1465   gtk_style_context_add_class (context, GTK_STYLE_CLASS_SCROLLBARS_JUNCTION);
1466
1467   gtk_render_background (context, cr,
1468                          junction_rect.x, junction_rect.y,
1469                          junction_rect.width, junction_rect.height);
1470   gtk_render_frame (context, cr,
1471                     junction_rect.x, junction_rect.y,
1472                     junction_rect.width, junction_rect.height);
1473
1474   gtk_style_context_restore (context);
1475 }
1476
1477 static gboolean
1478 gtk_scrolled_window_draw (GtkWidget *widget,
1479                           cairo_t   *cr)
1480 {
1481   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1482   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1483   GtkAllocation relative_allocation;
1484   cairo_pattern_t *pattern = NULL;
1485   GtkStyleContext *context;
1486   GtkWidget *child;
1487
1488   context = gtk_widget_get_style_context (widget);
1489   gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1490
1491   /* Use child's background if possible */
1492   child = gtk_bin_get_child (GTK_BIN (widget));
1493
1494   if (child && gtk_widget_get_has_window (child))
1495     pattern = gdk_window_get_background_pattern (gtk_widget_get_window (child));
1496
1497   if (pattern &&
1498       cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SOLID)
1499     {
1500       cairo_set_source (cr, pattern);
1501
1502       cairo_rectangle (cr, relative_allocation.x, relative_allocation.y,
1503                        relative_allocation.width, relative_allocation.height);
1504       cairo_fill (cr);
1505     }
1506   else
1507     gtk_render_background (context, cr,
1508                            relative_allocation.x, relative_allocation.y,
1509                            relative_allocation.width, relative_allocation.height);
1510
1511   if (priv->hscrollbar_visible && 
1512       priv->vscrollbar_visible)
1513     gtk_scrolled_window_draw_scrollbars_junction (scrolled_window, cr);
1514
1515   if (priv->shadow_type != GTK_SHADOW_NONE)
1516     {
1517       gboolean scrollbars_within_bevel;
1518
1519       gtk_style_context_save (context);
1520       gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
1521
1522       gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
1523
1524       if (!scrollbars_within_bevel)
1525         {
1526           GtkStateFlags state;
1527           GtkBorder padding, border;
1528
1529           state = gtk_widget_get_state_flags (widget);
1530           gtk_style_context_get_padding (context, state, &padding);
1531           gtk_style_context_get_border (context, state, &border);
1532
1533           relative_allocation.x -= padding.left + border.left;
1534           relative_allocation.y -= padding.top + border.top;
1535           relative_allocation.width += padding.left + padding.right + border.left + border.right;
1536           relative_allocation.height += padding.top + padding.bottom + border.top + border.bottom;
1537         }
1538       else
1539         {
1540           relative_allocation.x = 0;
1541           relative_allocation.y = 0;
1542           relative_allocation.width = gtk_widget_get_allocated_width (widget);
1543           relative_allocation.height = gtk_widget_get_allocated_height (widget);
1544         }
1545
1546       gtk_render_frame (context, cr,
1547                         relative_allocation.x,
1548                         relative_allocation.y,
1549                         relative_allocation.width,
1550                         relative_allocation.height);
1551
1552       gtk_style_context_restore (context);
1553     }
1554
1555   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->draw (widget, cr);
1556
1557   return FALSE;
1558 }
1559
1560 static void
1561 gtk_scrolled_window_forall (GtkContainer *container,
1562                             gboolean      include_internals,
1563                             GtkCallback   callback,
1564                             gpointer      callback_data)
1565 {
1566   GtkScrolledWindowPrivate *priv;
1567   GtkScrolledWindow *scrolled_window;
1568
1569   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
1570   g_return_if_fail (callback != NULL);
1571
1572   GTK_CONTAINER_CLASS (gtk_scrolled_window_parent_class)->forall (container,
1573                                               include_internals,
1574                                               callback,
1575                                               callback_data);
1576   if (include_internals)
1577     {
1578       scrolled_window = GTK_SCROLLED_WINDOW (container);
1579       priv = scrolled_window->priv;
1580
1581       if (priv->vscrollbar)
1582         callback (priv->vscrollbar, callback_data);
1583       if (priv->hscrollbar)
1584         callback (priv->hscrollbar, callback_data);
1585     }
1586 }
1587
1588 static gboolean
1589 gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
1590                                   GtkScrollType      scroll,
1591                                   gboolean           horizontal)
1592 {
1593   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1594   GtkAdjustment *adjustment = NULL;
1595   
1596   switch (scroll)
1597     {
1598     case GTK_SCROLL_STEP_UP:
1599       scroll = GTK_SCROLL_STEP_BACKWARD;
1600       horizontal = FALSE;
1601       break;
1602     case GTK_SCROLL_STEP_DOWN:
1603       scroll = GTK_SCROLL_STEP_FORWARD;
1604       horizontal = FALSE;
1605       break;
1606     case GTK_SCROLL_STEP_LEFT:
1607       scroll = GTK_SCROLL_STEP_BACKWARD;
1608       horizontal = TRUE;
1609       break;
1610     case GTK_SCROLL_STEP_RIGHT:
1611       scroll = GTK_SCROLL_STEP_FORWARD;
1612       horizontal = TRUE;
1613       break;
1614     case GTK_SCROLL_PAGE_UP:
1615       scroll = GTK_SCROLL_PAGE_BACKWARD;
1616       horizontal = FALSE;
1617       break;
1618     case GTK_SCROLL_PAGE_DOWN:
1619       scroll = GTK_SCROLL_PAGE_FORWARD;
1620       horizontal = FALSE;
1621       break;
1622     case GTK_SCROLL_PAGE_LEFT:
1623       scroll = GTK_SCROLL_STEP_BACKWARD;
1624       horizontal = TRUE;
1625       break;
1626     case GTK_SCROLL_PAGE_RIGHT:
1627       scroll = GTK_SCROLL_STEP_FORWARD;
1628       horizontal = TRUE;
1629       break;
1630     case GTK_SCROLL_STEP_BACKWARD:
1631     case GTK_SCROLL_STEP_FORWARD:
1632     case GTK_SCROLL_PAGE_BACKWARD:
1633     case GTK_SCROLL_PAGE_FORWARD:
1634     case GTK_SCROLL_START:
1635     case GTK_SCROLL_END:
1636       break;
1637     default:
1638       g_warning ("Invalid scroll type %u for GtkScrolledWindow::scroll-child", scroll);
1639       return FALSE;
1640     }
1641
1642   if ((horizontal && (!priv->hscrollbar || !priv->hscrollbar_visible)) ||
1643       (!horizontal && (!priv->vscrollbar || !priv->vscrollbar_visible)))
1644     return FALSE;
1645
1646   if (horizontal)
1647     {
1648       if (priv->hscrollbar)
1649         adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
1650     }
1651   else
1652     {
1653       if (priv->vscrollbar)
1654         adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
1655     }
1656
1657   if (adjustment)
1658     {
1659       gdouble value = gtk_adjustment_get_value (adjustment);
1660       
1661       switch (scroll)
1662         {
1663         case GTK_SCROLL_STEP_FORWARD:
1664           value += gtk_adjustment_get_step_increment (adjustment);
1665           break;
1666         case GTK_SCROLL_STEP_BACKWARD:
1667           value -= gtk_adjustment_get_step_increment (adjustment);
1668           break;
1669         case GTK_SCROLL_PAGE_FORWARD:
1670           value += gtk_adjustment_get_page_increment (adjustment);
1671           break;
1672         case GTK_SCROLL_PAGE_BACKWARD:
1673           value -= gtk_adjustment_get_page_increment (adjustment);
1674           break;
1675         case GTK_SCROLL_START:
1676           value = gtk_adjustment_get_lower (adjustment);
1677           break;
1678         case GTK_SCROLL_END:
1679           value = gtk_adjustment_get_upper (adjustment);
1680           break;
1681         default:
1682           g_assert_not_reached ();
1683           break;
1684         }
1685
1686       gtk_adjustment_set_value (adjustment, value);
1687
1688       return TRUE;
1689     }
1690
1691   return FALSE;
1692 }
1693
1694 static void
1695 gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
1696                                     GtkDirectionType   direction_type)
1697 {
1698   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1699   GtkWidget *toplevel;
1700   
1701   /* Focus out of the scrolled window entirely. We do this by setting
1702    * a flag, then propagating the focus motion to the notebook.
1703    */
1704   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (scrolled_window));
1705   if (!gtk_widget_is_toplevel (toplevel))
1706     return;
1707
1708   g_object_ref (scrolled_window);
1709
1710   priv->focus_out = TRUE;
1711   g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1712   priv->focus_out = FALSE;
1713
1714   g_object_unref (scrolled_window);
1715 }
1716
1717 static void
1718 gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
1719                                          GtkAllocation *allocation)
1720 {
1721   GtkAllocation widget_allocation;
1722   GtkScrolledWindow *scrolled_window;
1723   GtkScrolledWindowPrivate *priv;
1724   gint sb_spacing;
1725   gint sb_width;
1726   gint sb_height;
1727
1728   g_return_if_fail (widget != NULL);
1729   g_return_if_fail (allocation != NULL);
1730
1731   scrolled_window = GTK_SCROLLED_WINDOW (widget);
1732   priv = scrolled_window->priv;
1733
1734   /* Get possible scrollbar dimensions */
1735   sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
1736   gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
1737   gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
1738
1739   /* Subtract some things from our available allocation size */
1740   allocation->x = 0;
1741   allocation->y = 0;
1742
1743   if (priv->shadow_type != GTK_SHADOW_NONE)
1744     {
1745       GtkStyleContext *context;
1746       GtkStateFlags state;
1747       GtkBorder padding, border;
1748
1749       context = gtk_widget_get_style_context (widget);
1750       state = gtk_widget_get_state_flags (widget);
1751
1752       gtk_style_context_save (context);
1753       gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
1754
1755       gtk_style_context_get_border (context, state, &border);
1756       gtk_style_context_get_padding (context, state, &padding);
1757
1758       allocation->x += padding.left + border.left;
1759       allocation->y += padding.top + border.top;
1760
1761       gtk_style_context_restore (context);
1762     }
1763
1764   gtk_widget_get_allocation (widget, &widget_allocation);
1765   allocation->width = MAX (1, (gint) widget_allocation.width - allocation->x * 2);
1766   allocation->height = MAX (1, (gint) widget_allocation.height - allocation->y * 2);
1767
1768   if (priv->vscrollbar_visible)
1769     {
1770       gboolean is_rtl;
1771
1772       is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1773   
1774       if ((!is_rtl && 
1775            (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
1776             priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
1777           (is_rtl && 
1778            (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1779             priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
1780         allocation->x += (sb_width +  sb_spacing);
1781
1782       allocation->width = MAX (1, allocation->width - (sb_width + sb_spacing));
1783     }
1784   if (priv->hscrollbar_visible)
1785     {
1786
1787       if (priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT ||
1788           priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)
1789         allocation->y += (sb_height + sb_spacing);
1790
1791       allocation->height = MAX (1, allocation->height - (sb_height + sb_spacing));
1792     }
1793 }
1794
1795 static gboolean
1796 _gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
1797                                     gint              *overshoot_x,
1798                                     gint              *overshoot_y)
1799 {
1800   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1801   GtkAdjustment *vadjustment, *hadjustment;
1802   gdouble lower, upper, x, y;
1803
1804   /* Vertical overshoot */
1805   vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
1806   lower = gtk_adjustment_get_lower (vadjustment);
1807   upper = gtk_adjustment_get_upper (vadjustment) -
1808     gtk_adjustment_get_page_size (vadjustment);
1809
1810   if (priv->unclamped_vadj_value < lower)
1811     y = priv->unclamped_vadj_value - lower;
1812   else if (priv->unclamped_vadj_value > upper)
1813     y = priv->unclamped_vadj_value - upper;
1814   else
1815     y = 0;
1816
1817   /* Horizontal overshoot */
1818   hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
1819   lower = gtk_adjustment_get_lower (hadjustment);
1820   upper = gtk_adjustment_get_upper (hadjustment) -
1821     gtk_adjustment_get_page_size (hadjustment);
1822
1823   if (priv->unclamped_hadj_value < lower)
1824     x = priv->unclamped_hadj_value - lower;
1825   else if (priv->unclamped_hadj_value > upper)
1826     x = priv->unclamped_hadj_value - upper;
1827   else
1828     x = 0;
1829
1830   if (overshoot_x)
1831     *overshoot_x = x;
1832
1833   if (overshoot_y)
1834     *overshoot_y = y;
1835
1836   return (x != 0 || y != 0);
1837 }
1838
1839 static void
1840 _gtk_scrolled_window_allocate_overshoot_window (GtkScrolledWindow *scrolled_window)
1841 {
1842   GtkAllocation window_allocation, relative_allocation, allocation;
1843   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1844   GtkWidget *widget = GTK_WIDGET (scrolled_window);
1845   gint overshoot_x, overshoot_y;
1846
1847   if (!gtk_widget_get_realized (widget))
1848     return;
1849
1850   gtk_widget_get_allocation (widget, &allocation);
1851   gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1852   _gtk_scrolled_window_get_overshoot (scrolled_window,
1853                                       &overshoot_x, &overshoot_y);
1854
1855   window_allocation = relative_allocation;
1856   window_allocation.x += allocation.x;
1857   window_allocation.y += allocation.y;
1858
1859   /* Handle displacement to the left/top by moving the overshoot
1860    * window, overshooting to the bottom/right is handled in
1861    * gtk_scrolled_window_allocate_child()
1862    */
1863   if (overshoot_x < 0)
1864     window_allocation.x += -overshoot_x;
1865
1866   if (overshoot_y < 0)
1867     window_allocation.y += -overshoot_y;
1868
1869   window_allocation.width -= ABS (overshoot_x);
1870   window_allocation.height -= ABS (overshoot_y);
1871
1872   gdk_window_move_resize (priv->overshoot_window,
1873                           window_allocation.x, window_allocation.y,
1874                           window_allocation.width, window_allocation.height);
1875 }
1876
1877 static void
1878 gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
1879                                     GtkAllocation     *relative_allocation)
1880 {
1881   GtkWidget     *widget = GTK_WIDGET (swindow), *child;
1882   GtkAllocation  allocation;
1883   GtkAllocation  child_allocation;
1884   gint           overshoot_x, overshoot_y;
1885
1886   child = gtk_bin_get_child (GTK_BIN (widget));
1887
1888   gtk_widget_get_allocation (widget, &allocation);
1889
1890   gtk_scrolled_window_relative_allocation (widget, relative_allocation);
1891   _gtk_scrolled_window_get_overshoot (swindow, &overshoot_x, &overshoot_y);
1892
1893   /* Handle overshooting to the right/bottom by relocating the
1894    * widget allocation to negative coordinates, so these edges
1895    * stick to the overshoot window border.
1896    */
1897   if (overshoot_x > 0)
1898     child_allocation.x = -overshoot_x;
1899   else
1900     child_allocation.x = 0;
1901
1902   if (overshoot_y > 0)
1903     child_allocation.y = -overshoot_y;
1904   else
1905     child_allocation.y = 0;
1906
1907   child_allocation.width = relative_allocation->width;
1908   child_allocation.height = relative_allocation->height;
1909
1910   gtk_widget_size_allocate (child, &child_allocation);
1911 }
1912
1913 static void
1914 gtk_scrolled_window_size_allocate (GtkWidget     *widget,
1915                                    GtkAllocation *allocation)
1916 {
1917   GtkScrolledWindow *scrolled_window;
1918   GtkScrolledWindowPrivate *priv;
1919   GtkStyleContext *context;
1920   GtkStateFlags state;
1921   GtkBorder padding, border;
1922   GtkBin *bin;
1923   GtkAllocation relative_allocation;
1924   GtkAllocation child_allocation;
1925   GtkWidget *child;
1926   gboolean scrollbars_within_bevel;
1927   gint sb_spacing;
1928   gint sb_width;
1929   gint sb_height;
1930
1931   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
1932   g_return_if_fail (allocation != NULL);
1933
1934   scrolled_window = GTK_SCROLLED_WINDOW (widget);
1935   bin = GTK_BIN (scrolled_window);
1936   priv = scrolled_window->priv;
1937
1938   /* Get possible scrollbar dimensions */
1939   sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
1940   gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
1941   gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
1942
1943   context = gtk_widget_get_style_context (widget);
1944   state = gtk_widget_get_state_flags (widget);
1945
1946   gtk_style_context_save (context);
1947   gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
1948
1949   gtk_style_context_get_padding (context, state, &padding);
1950   gtk_style_context_get_border (context, state, &border);
1951
1952   gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
1953
1954   gtk_widget_set_allocation (widget, allocation);
1955   gtk_style_context_restore (context);
1956
1957   if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
1958     priv->hscrollbar_visible = TRUE;
1959   else if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
1960     priv->hscrollbar_visible = FALSE;
1961   if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
1962     priv->vscrollbar_visible = TRUE;
1963   else if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
1964     priv->vscrollbar_visible = FALSE;
1965
1966   child = gtk_bin_get_child (bin);
1967   if (child && gtk_widget_get_visible (child))
1968     {
1969       gint child_scroll_width;
1970       gint child_scroll_height;
1971       gboolean previous_hvis;
1972       gboolean previous_vvis;
1973       guint count = 0;
1974
1975       /* Determine scrollbar visibility first via hfw apis */
1976       if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1977         {
1978           if (gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
1979             gtk_widget_get_preferred_width (child, &child_scroll_width, NULL);
1980           else
1981             gtk_widget_get_preferred_width (child, NULL, &child_scroll_width);
1982           
1983           if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1984             {
1985               /* First try without a vertical scrollbar if the content will fit the height
1986                * given the extra width of the scrollbar */
1987               if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
1988                 gtk_widget_get_preferred_height_for_width (child, 
1989                                                            MAX (allocation->width, child_scroll_width), 
1990                                                            &child_scroll_height, NULL);
1991               else
1992                 gtk_widget_get_preferred_height_for_width (child,
1993                                                            MAX (allocation->width, child_scroll_width), 
1994                                                            NULL, &child_scroll_height);
1995               
1996               if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1997                 {
1998                   /* Does the content height fit the allocation height ? */
1999                   priv->vscrollbar_visible = child_scroll_height > allocation->height;
2000                   
2001                   /* Does the content width fit the allocation with minus a possible scrollbar ? */
2002                   priv->hscrollbar_visible = 
2003                     child_scroll_width > allocation->width - 
2004                     (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2005                   
2006                   /* Now that we've guessed the hscrollbar, does the content height fit
2007                    * the possible new allocation height ? */
2008                   priv->vscrollbar_visible = 
2009                     child_scroll_height > allocation->height - 
2010                     (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2011                   
2012                   /* Now that we've guessed the vscrollbar, does the content width fit
2013                    * the possible new allocation width ? */
2014                   priv->hscrollbar_visible = 
2015                     child_scroll_width > allocation->width - 
2016                     (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2017                 }
2018               else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
2019                 {
2020                   priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
2021                   priv->vscrollbar_visible = child_scroll_height > allocation->height - 
2022                     (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2023                 }
2024             }
2025           else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
2026             {
2027               priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
2028               
2029               if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2030                 priv->hscrollbar_visible = 
2031                   child_scroll_width > allocation->width - 
2032                   (priv->vscrollbar_visible ? 0 : sb_width + sb_spacing);
2033               else
2034                 priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
2035             }
2036         } 
2037       else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
2038         {
2039           if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
2040             gtk_widget_get_preferred_height (child, &child_scroll_height, NULL);
2041           else
2042             gtk_widget_get_preferred_height (child, NULL, &child_scroll_height);
2043           
2044           if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2045             {
2046               /* First try without a horizontal scrollbar if the content will fit the width
2047                * given the extra height of the scrollbar */
2048               if (gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
2049                 gtk_widget_get_preferred_width_for_height (child, 
2050                                                            MAX (allocation->height, child_scroll_height), 
2051                                                            &child_scroll_width, NULL);
2052               else
2053                 gtk_widget_get_preferred_width_for_height (child, 
2054                                                            MAX (allocation->height, child_scroll_height), 
2055                                                            NULL, &child_scroll_width);
2056               
2057               if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
2058                 {
2059                   /* Does the content width fit the allocation width ? */
2060                   priv->hscrollbar_visible = child_scroll_width > allocation->width;
2061                   
2062                   /* Does the content height fit the allocation with minus a possible scrollbar ? */
2063                   priv->vscrollbar_visible = 
2064                     child_scroll_height > allocation->height - 
2065                     (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2066                   
2067                   /* Now that we've guessed the vscrollbar, does the content width fit
2068                    * the possible new allocation width ? */
2069                   priv->hscrollbar_visible = 
2070                     child_scroll_width > allocation->width - 
2071                     (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2072                   
2073                   /* Now that we've guessed the hscrollbar, does the content height fit
2074                    * the possible new allocation height ? */
2075                   priv->vscrollbar_visible = 
2076                     child_scroll_height > allocation->height - 
2077                     (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2078                 }
2079               else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
2080                 {
2081                   priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
2082                   priv->hscrollbar_visible = child_scroll_width > allocation->width - 
2083                     (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2084                 }
2085             }
2086           else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
2087             {
2088               priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
2089               
2090               if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
2091                 priv->vscrollbar_visible = 
2092                   child_scroll_height > allocation->height - 
2093                   (priv->hscrollbar_visible ? 0 : sb_height + sb_spacing);
2094               else
2095                 priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
2096             }
2097         }
2098
2099       /* Now after guessing scrollbar visibility; fall back on the allocation loop which 
2100        * observes the adjustments to detect scrollbar visibility and also avoids 
2101        * infinite recursion
2102        */
2103       do
2104         {
2105           previous_hvis = priv->hscrollbar_visible;
2106           previous_vvis = priv->vscrollbar_visible;
2107           gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
2108
2109           /* Explicitly force scrollbar visibility checks.
2110            *
2111            * Since we make a guess above, the child might not decide to update the adjustments 
2112            * if they logically did not change since the last configuration
2113            */
2114           if (priv->hscrollbar)
2115             gtk_scrolled_window_adjustment_changed 
2116               (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)), scrolled_window);
2117
2118           if (priv->vscrollbar)
2119             gtk_scrolled_window_adjustment_changed 
2120               (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)), scrolled_window);
2121
2122           /* If, after the first iteration, the hscrollbar and the
2123            * vscrollbar flip visiblity... or if one of the scrollbars flip
2124            * on each itteration indefinitly/infinitely, then we just need both 
2125            * at this size.
2126            */
2127           if ((count &&
2128                previous_hvis != priv->hscrollbar_visible &&
2129                previous_vvis != priv->vscrollbar_visible) || count > 3)
2130             {
2131               priv->hscrollbar_visible = TRUE;
2132               priv->vscrollbar_visible = TRUE;
2133
2134               gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
2135
2136               break;
2137             }
2138           
2139           count++;
2140         }
2141       while (previous_hvis != priv->hscrollbar_visible ||
2142              previous_vvis != priv->vscrollbar_visible);
2143     }
2144   else
2145     {
2146       priv->hscrollbar_visible = priv->hscrollbar_policy == GTK_POLICY_ALWAYS;
2147       priv->vscrollbar_visible = priv->vscrollbar_policy == GTK_POLICY_ALWAYS;
2148       gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
2149     }
2150
2151   if (priv->hscrollbar_visible)
2152     {
2153       if (!gtk_widget_get_visible (priv->hscrollbar))
2154         gtk_widget_show (priv->hscrollbar);
2155
2156       child_allocation.x = relative_allocation.x;
2157       if (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
2158           priv->real_window_placement == GTK_CORNER_TOP_RIGHT)
2159         child_allocation.y = (relative_allocation.y +
2160                               relative_allocation.height +
2161                               sb_spacing +
2162                               (priv->shadow_type == GTK_SHADOW_NONE ?
2163                                0 : padding.top + border.top));
2164       else
2165         child_allocation.y = 0;
2166
2167       child_allocation.width = relative_allocation.width;
2168       child_allocation.height = sb_height;
2169       child_allocation.x += allocation->x;
2170       child_allocation.y += allocation->y;
2171
2172       if (priv->shadow_type != GTK_SHADOW_NONE)
2173         {
2174           if (!scrollbars_within_bevel)
2175             {
2176               child_allocation.x -= padding.left + border.left;
2177               child_allocation.width += padding.left + padding.right + border.left + border.right;
2178             }
2179           else if (GTK_CORNER_TOP_RIGHT == priv->real_window_placement ||
2180                    GTK_CORNER_TOP_LEFT == priv->real_window_placement)
2181             {
2182               child_allocation.y -= padding.top + border.top;
2183             }
2184           else
2185             {
2186               child_allocation.y += padding.top + border.top;
2187             }
2188         }
2189
2190       gtk_widget_size_allocate (priv->hscrollbar, &child_allocation);
2191     }
2192   else if (gtk_widget_get_visible (priv->hscrollbar))
2193     gtk_widget_hide (priv->hscrollbar);
2194
2195   if (priv->vscrollbar_visible)
2196     {
2197       if (!gtk_widget_get_visible (priv->vscrollbar))
2198         gtk_widget_show (priv->vscrollbar);
2199
2200       if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL && 
2201            (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
2202             priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
2203           (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR && 
2204            (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
2205             priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
2206         child_allocation.x = (relative_allocation.x +
2207                               relative_allocation.width +
2208                               sb_spacing +
2209                               (priv->shadow_type == GTK_SHADOW_NONE ?
2210                                0 : padding.left + border.left));
2211       else
2212         child_allocation.x = 0;
2213
2214       child_allocation.y = relative_allocation.y;
2215       child_allocation.width = sb_width;
2216       child_allocation.height = relative_allocation.height;
2217       child_allocation.x += allocation->x;
2218       child_allocation.y += allocation->y;
2219
2220       if (priv->shadow_type != GTK_SHADOW_NONE)
2221         {
2222           if (!scrollbars_within_bevel)
2223             {
2224               child_allocation.y -= padding.top + border.top;
2225               child_allocation.height += padding.top + padding.bottom + border.top + border.bottom;
2226             }
2227           else if (GTK_CORNER_BOTTOM_LEFT == priv->real_window_placement ||
2228                    GTK_CORNER_TOP_LEFT == priv->real_window_placement)
2229             {
2230               child_allocation.x -= padding.left + border.left;
2231             }
2232           else
2233             {
2234               child_allocation.x += padding.left + border.left;
2235             }
2236         }
2237
2238       gtk_widget_size_allocate (priv->vscrollbar, &child_allocation);
2239     }
2240   else if (gtk_widget_get_visible (priv->vscrollbar))
2241     gtk_widget_hide (priv->vscrollbar);
2242
2243   _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
2244 }
2245
2246 static gboolean
2247 gtk_scrolled_window_scroll_event (GtkWidget      *widget,
2248                                   GdkEventScroll *event)
2249 {
2250   GtkScrolledWindowPrivate *priv;
2251   GtkScrolledWindow *scrolled_window;
2252   gboolean handled = FALSE;
2253   gdouble delta_x;
2254   gdouble delta_y;
2255   gdouble delta;
2256
2257   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE);
2258   g_return_val_if_fail (event != NULL, FALSE);
2259
2260   scrolled_window = GTK_SCROLLED_WINDOW (widget);
2261   priv = scrolled_window->priv;
2262
2263   if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y))
2264     {
2265       if (delta_x != 0.0 && priv->hscrollbar &&
2266           gtk_widget_get_visible (priv->hscrollbar))
2267         {
2268           GtkAdjustment *adj;
2269           gdouble new_value;
2270           gdouble page_size;
2271           gdouble scroll_unit;
2272
2273           adj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2274           page_size = gtk_adjustment_get_page_size (adj);
2275           scroll_unit = pow (page_size, 2.0 / 3.0);
2276
2277           new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_x * scroll_unit,
2278                              gtk_adjustment_get_lower (adj),
2279                              gtk_adjustment_get_upper (adj) -
2280                              gtk_adjustment_get_page_size (adj));
2281
2282           gtk_adjustment_set_value (adj, new_value);
2283
2284           handled = TRUE;
2285         }
2286
2287       if (delta_y != 0.0 && priv->vscrollbar &&
2288           gtk_widget_get_visible (priv->vscrollbar))
2289         {
2290           GtkAdjustment *adj;
2291           gdouble new_value;
2292           gdouble page_size;
2293           gdouble scroll_unit;
2294
2295           adj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2296           page_size = gtk_adjustment_get_page_size (adj);
2297           scroll_unit = pow (page_size, 2.0 / 3.0);
2298
2299           new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_y * scroll_unit,
2300                              gtk_adjustment_get_lower (adj),
2301                              gtk_adjustment_get_upper (adj) -
2302                              gtk_adjustment_get_page_size (adj));
2303
2304           gtk_adjustment_set_value (adj, new_value);
2305
2306           handled = TRUE;
2307         }
2308     }
2309   else
2310     {
2311       GtkWidget *range;
2312
2313       if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2314         range = priv->vscrollbar;
2315       else
2316         range = priv->hscrollbar;
2317
2318       if (range && gtk_widget_get_visible (range))
2319         {
2320           GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (range));
2321           gdouble new_value;
2322
2323           delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event);
2324
2325           new_value = CLAMP (gtk_adjustment_get_value (adj) + delta,
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
2336   return handled;
2337 }
2338
2339 static gboolean
2340 _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
2341                                            GtkAdjustment     *adjustment,
2342                                            gdouble            value,
2343                                            gboolean           allow_overshooting,
2344                                            gboolean           snap_to_border)
2345 {
2346   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2347   gdouble lower, upper, *prev_value;
2348
2349   lower = gtk_adjustment_get_lower (adjustment);
2350   upper = gtk_adjustment_get_upper (adjustment) -
2351     gtk_adjustment_get_page_size (adjustment);
2352
2353   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
2354     prev_value = &priv->unclamped_hadj_value;
2355   else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
2356     prev_value = &priv->unclamped_vadj_value;
2357   else
2358     return FALSE;
2359
2360   if (snap_to_border)
2361     {
2362       if (*prev_value < 0 && value > 0)
2363         value = 0;
2364       else if (*prev_value > upper && value < upper)
2365         value = upper;
2366     }
2367
2368   if (allow_overshooting)
2369     {
2370       lower -= MAX_OVERSHOOT_DISTANCE;
2371       upper += MAX_OVERSHOOT_DISTANCE;
2372     }
2373
2374   *prev_value = CLAMP (value, lower, upper);
2375   gtk_adjustment_set_value (adjustment, value);
2376
2377   return (*prev_value != value);
2378 }
2379
2380 static gboolean
2381 scrolled_window_deceleration_cb (gpointer user_data)
2382 {
2383   KineticScrollData *data = user_data;
2384   GtkScrolledWindow *scrolled_window = data->scrolled_window;
2385   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2386   GtkAdjustment *hadjustment, *vadjustment;
2387   gint old_overshoot_x, old_overshoot_y, overshoot_x, overshoot_y;
2388   gdouble value;
2389   gint64 current_time;
2390   guint elapsed;
2391
2392   hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2393   vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2394
2395   _gtk_scrolled_window_get_overshoot (scrolled_window,
2396                                       &old_overshoot_x, &old_overshoot_y);
2397
2398   current_time = g_get_monotonic_time ();
2399   elapsed = (current_time - data->last_deceleration_time) / 1000;
2400   data->last_deceleration_time = current_time;
2401
2402   if (hadjustment && priv->hscrollbar_visible)
2403     {
2404       value = priv->unclamped_hadj_value + (data->x_velocity * elapsed);
2405
2406       if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
2407                                                      hadjustment,
2408                                                      value, TRUE, TRUE))
2409         data->x_velocity = 0;
2410     }
2411   else
2412     data->x_velocity = 0;
2413
2414   if (vadjustment && priv->vscrollbar_visible)
2415     {
2416       value = priv->unclamped_vadj_value + (data->y_velocity * elapsed);
2417
2418       if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
2419                                                      vadjustment,
2420                                                      value, TRUE, TRUE))
2421         data->y_velocity = 0;
2422     }
2423   else
2424     data->y_velocity = 0;
2425
2426   _gtk_scrolled_window_get_overshoot (scrolled_window,
2427                                       &overshoot_x, &overshoot_y);
2428
2429   if (overshoot_x == 0)
2430     {
2431       if (old_overshoot_x != 0)
2432         {
2433           /* Overshooting finished snapping back */
2434           data->x_velocity = 0;
2435         }
2436       else if (data->x_velocity > 0)
2437         {
2438           data->x_velocity -= FRICTION_DECELERATION * elapsed * data->vel_sine;
2439           data->x_velocity = MAX (0, data->x_velocity);
2440         }
2441       else if (data->x_velocity < 0)
2442         {
2443           data->x_velocity += FRICTION_DECELERATION * elapsed * data->vel_sine;
2444           data->x_velocity = MIN (0, data->x_velocity);
2445         }
2446     }
2447   else if (overshoot_x < 0)
2448     data->x_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2449   else if (overshoot_x > 0)
2450     data->x_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2451
2452   if (overshoot_y == 0)
2453     {
2454       if (old_overshoot_y != 0)
2455         {
2456           /* Overshooting finished snapping back */
2457           data->y_velocity = 0;
2458         }
2459       else if (data->y_velocity > 0)
2460         {
2461           data->y_velocity -= FRICTION_DECELERATION * elapsed * data->vel_cosine;
2462           data->y_velocity = MAX (0, data->y_velocity);
2463         }
2464       else if (data->y_velocity < 0)
2465         {
2466           data->y_velocity += FRICTION_DECELERATION * elapsed * data->vel_cosine;
2467           data->y_velocity = MIN (0, data->y_velocity);
2468         }
2469     }
2470   else if (overshoot_y < 0)
2471     data->y_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2472   else if (overshoot_y > 0)
2473     data->y_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2474
2475   if (old_overshoot_x != overshoot_x ||
2476       old_overshoot_y != overshoot_y)
2477     {
2478       if (overshoot_x >= 0 || overshoot_y >= 0)
2479         {
2480           /* We need to reallocate the widget to have it at
2481            * negative offset, so there's a "gravity" on the
2482            * bottom/right corner
2483            */
2484           gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2485         }
2486       else if (overshoot_x < 0 || overshoot_y < 0)
2487         _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
2488     }
2489
2490   if (overshoot_x != 0 || overshoot_y != 0 ||
2491       data->x_velocity != 0 || data->y_velocity != 0)
2492     return TRUE;
2493   else
2494     {
2495       priv->deceleration_id = 0;
2496       return FALSE;
2497     }
2498 }
2499
2500 static void
2501 gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window)
2502 {
2503   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2504
2505   if (priv->deceleration_id)
2506     {
2507       g_source_remove (priv->deceleration_id);
2508       priv->deceleration_id = 0;
2509     }
2510 }
2511
2512 static void
2513 gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
2514 {
2515   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2516   KineticScrollData *data;
2517   gdouble angle;
2518
2519   data = g_new0 (KineticScrollData, 1);
2520   data->scrolled_window = scrolled_window;
2521   data->last_deceleration_time = g_get_monotonic_time ();
2522   data->x_velocity = priv->x_velocity;
2523   data->y_velocity = priv->y_velocity;
2524
2525   /* We use sine/cosine as a factor to deceleration x/y components
2526    * of the vector, so we care about the sign later.
2527    */
2528   angle = atan2 (ABS (data->x_velocity), ABS (data->y_velocity));
2529   data->vel_cosine = cos (angle);
2530   data->vel_sine = sin (angle);
2531
2532   scrolled_window->priv->deceleration_id =
2533     gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
2534                                   FRAME_INTERVAL,
2535                                   scrolled_window_deceleration_cb,
2536                                   data, (GDestroyNotify) g_free);
2537 }
2538
2539 static gboolean
2540 gtk_scrolled_window_release_captured_event (GtkScrolledWindow *scrolled_window)
2541 {
2542   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2543
2544   /* Cancel the scrolling and send the button press
2545    * event to the child widget
2546    */
2547   if (!priv->button_press_event)
2548     return FALSE;
2549
2550   if (priv->drag_device)
2551     {
2552       gtk_device_grab_remove (GTK_WIDGET (scrolled_window), priv->drag_device);
2553       priv->drag_device = NULL;
2554     }
2555
2556   if (priv->capture_button_press)
2557     {
2558       GtkWidget *event_widget;
2559
2560       event_widget = gtk_get_event_widget (priv->button_press_event);
2561
2562       if (!_gtk_propagate_captured_event (event_widget,
2563                                           priv->button_press_event,
2564                                           gtk_bin_get_child (GTK_BIN (scrolled_window))))
2565         gtk_propagate_event (event_widget, priv->button_press_event);
2566
2567       gdk_event_free (priv->button_press_event);
2568       priv->button_press_event = NULL;
2569     }
2570
2571   if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
2572     gtk_scrolled_window_start_deceleration (scrolled_window);
2573
2574   return FALSE;
2575 }
2576
2577 static gboolean
2578 gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
2579                                         GdkEvent          *event)
2580 {
2581   GtkScrolledWindowPrivate *priv;
2582   gdouble x_root, y_root;
2583   guint32 _time;
2584
2585 #define STILL_THRESHOLD 40
2586
2587   if (!gdk_event_get_root_coords (event, &x_root, &y_root))
2588     return FALSE;
2589
2590   priv = scrolled_window->priv;
2591   _time = gdk_event_get_time (event);
2592
2593   if (priv->last_motion_event_x_root != x_root ||
2594       priv->last_motion_event_y_root != y_root ||
2595       ABS (_time - priv->last_motion_event_time) > STILL_THRESHOLD)
2596     {
2597       priv->x_velocity = (priv->last_motion_event_x_root - x_root) /
2598         (gdouble) (_time - priv->last_motion_event_time);
2599       priv->y_velocity = (priv->last_motion_event_y_root - y_root) /
2600         (gdouble) (_time - priv->last_motion_event_time);
2601     }
2602
2603   priv->last_motion_event_x_root = x_root;
2604   priv->last_motion_event_y_root = y_root;
2605   priv->last_motion_event_time = _time;
2606
2607 #undef STILL_THRESHOLD
2608
2609   return TRUE;
2610 }
2611
2612 static gboolean
2613 gtk_scrolled_window_captured_button_release (GtkWidget *widget,
2614                                              GdkEvent  *event)
2615 {
2616   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2617   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2618   GtkWidget *child;
2619   gboolean overshoot;
2620   guint button;
2621   gdouble x_root, y_root;
2622
2623   if (gdk_event_get_button (event, &button) && button != 1)
2624     return FALSE;
2625
2626   child = gtk_bin_get_child (GTK_BIN (widget));
2627   if (!child)
2628     return FALSE;
2629
2630   gtk_device_grab_remove (widget, priv->drag_device);
2631   priv->drag_device = NULL;
2632
2633   if (priv->release_timeout_id)
2634     {
2635       g_source_remove (priv->release_timeout_id);
2636       priv->release_timeout_id = 0;
2637     }
2638
2639   overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL);
2640
2641   if (priv->in_drag)
2642     gdk_device_ungrab (gdk_event_get_device (event), gdk_event_get_time (event));
2643   else
2644     {
2645       /* There hasn't been scrolling at all, so just let the
2646        * child widget handle the button press normally
2647        */
2648       gtk_scrolled_window_release_captured_event (scrolled_window);
2649
2650       if (!overshoot)
2651         return FALSE;
2652     }
2653   priv->in_drag = FALSE;
2654
2655   if (priv->button_press_event)
2656     {
2657       gdk_event_free (priv->button_press_event);
2658       priv->button_press_event = NULL;
2659     }
2660
2661   gtk_scrolled_window_calculate_velocity (scrolled_window, event);
2662
2663   /* Zero out vector components without a visible scrollbar */
2664   if (!priv->hscrollbar_visible)
2665     priv->x_velocity = 0;
2666   if (!priv->vscrollbar_visible)
2667     priv->y_velocity = 0;
2668
2669   if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot)
2670     {
2671       gtk_scrolled_window_start_deceleration (scrolled_window);
2672       priv->x_velocity = priv->y_velocity = 0;
2673       priv->last_button_event_valid = FALSE;
2674     }
2675   else
2676     {
2677       gdk_event_get_root_coords (event, &x_root, &y_root);
2678       priv->last_button_event_x_root = x_root;
2679       priv->last_button_event_y_root = y_root;
2680       priv->last_button_event_valid = TRUE;
2681     }
2682
2683   if (priv->capture_button_press)
2684     return TRUE;
2685   else
2686     return FALSE;
2687 }
2688
2689 static gboolean
2690 gtk_scrolled_window_captured_motion_notify (GtkWidget *widget,
2691                                             GdkEvent  *event)
2692 {
2693   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2694   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2695   gint old_overshoot_x, old_overshoot_y;
2696   gint new_overshoot_x, new_overshoot_y;
2697   GtkWidget *child;
2698   GtkAdjustment *hadjustment;
2699   GtkAdjustment *vadjustment;
2700   gdouble dx, dy;
2701   GdkModifierType state;
2702   gdouble x_root, y_root;
2703
2704   gdk_event_get_state (event, &state);
2705   if (!(state & GDK_BUTTON1_MASK))
2706     return FALSE;
2707
2708   child = gtk_bin_get_child (GTK_BIN (widget));
2709   if (!child)
2710     return FALSE;
2711
2712   /* Check if we've passed the drag threshold */
2713   gdk_event_get_root_coords (event, &x_root, &y_root);
2714   if (!priv->in_drag)
2715     {
2716       if (gtk_drag_check_threshold (widget,
2717                                     priv->last_button_event_x_root,
2718                                     priv->last_button_event_y_root,
2719                                     x_root, y_root))
2720         {
2721           if (priv->release_timeout_id)
2722             {
2723               g_source_remove (priv->release_timeout_id);
2724               priv->release_timeout_id = 0;
2725             }
2726
2727           priv->last_button_event_valid = FALSE;
2728           priv->in_drag = TRUE;
2729         }
2730       else
2731         return TRUE;
2732     }
2733
2734   gdk_device_grab (priv->drag_device,
2735                    gtk_widget_get_window (widget),
2736                    GDK_OWNERSHIP_WINDOW,
2737                    TRUE,
2738                    GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK,
2739                    NULL,
2740                    gdk_event_get_time (event));
2741
2742   priv->last_button_event_valid = FALSE;
2743
2744   if (priv->button_press_event)
2745     {
2746       gdk_event_free (priv->button_press_event);
2747       priv->button_press_event = NULL;
2748     }
2749
2750   _gtk_scrolled_window_get_overshoot (scrolled_window,
2751                                       &old_overshoot_x, &old_overshoot_y);
2752
2753   hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2754   if (hadjustment && priv->hscrollbar_visible)
2755     {
2756       dx = (priv->last_motion_event_x_root - x_root) + priv->unclamped_hadj_value;
2757       _gtk_scrolled_window_set_adjustment_value (scrolled_window, hadjustment,
2758                                                  dx, TRUE, FALSE);
2759     }
2760
2761   vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2762   if (vadjustment && priv->vscrollbar_visible)
2763     {
2764       dy = (priv->last_motion_event_y_root - y_root) + priv->unclamped_vadj_value;
2765       _gtk_scrolled_window_set_adjustment_value (scrolled_window, vadjustment,
2766                                                  dy, TRUE, FALSE);
2767     }
2768
2769   _gtk_scrolled_window_get_overshoot (scrolled_window,
2770                                       &new_overshoot_x, &new_overshoot_y);
2771
2772   if (old_overshoot_x != new_overshoot_x ||
2773       old_overshoot_y != new_overshoot_y)
2774     {
2775       if (new_overshoot_x >= 0 || new_overshoot_y >= 0)
2776         {
2777           /* We need to reallocate the widget to have it at
2778            * negative offset, so there's a "gravity" on the
2779            * bottom/right corner
2780            */
2781           gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2782         }
2783       else if (new_overshoot_x < 0 || new_overshoot_y < 0)
2784         _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
2785     }
2786
2787   gtk_scrolled_window_calculate_velocity (scrolled_window, event);
2788
2789   return TRUE;
2790 }
2791
2792 static gboolean
2793 gtk_scrolled_window_captured_button_press (GtkWidget *widget,
2794                                            GdkEvent  *event)
2795 {
2796   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2797   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2798   GtkWidget *child;
2799   GtkWidget *event_widget;
2800   GdkDevice *source_device;
2801   GdkInputSource source;
2802   gdouble x_root, y_root;
2803   guint button;
2804
2805   /* If scrollbars are not visible, we don't do kinetic scrolling */
2806   if (!priv->vscrollbar_visible && !priv->hscrollbar_visible)
2807     return FALSE;
2808
2809   source_device = gdk_event_get_source_device (event);
2810   source = gdk_device_get_source (source_device);
2811
2812   if (source != GDK_SOURCE_TOUCHSCREEN)
2813     return FALSE;
2814
2815   event_widget = gtk_get_event_widget (event);
2816
2817   /* If there's another scrolled window between the widget
2818    * receiving the event and this capturing scrolled window,
2819    * let it handle the events.
2820    */
2821   if (widget != gtk_widget_get_ancestor (event_widget, GTK_TYPE_SCROLLED_WINDOW))
2822     return FALSE;
2823
2824   /* Check whether the button press is close to the previous one,
2825    * take that as a shortcut to get the child widget handle events
2826    */
2827   gdk_event_get_root_coords (event, &x_root, &y_root);
2828   if (priv->last_button_event_valid &&
2829       ABS (x_root - priv->last_button_event_x_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD &&
2830       ABS (y_root - priv->last_button_event_y_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD)
2831     {
2832       priv->last_button_event_valid = FALSE;
2833       return FALSE;
2834     }
2835
2836   priv->last_button_event_x_root = priv->last_motion_event_x_root = x_root;
2837   priv->last_button_event_y_root = priv->last_motion_event_y_root = y_root;
2838   priv->last_motion_event_time = gdk_event_get_time (event);
2839   priv->last_button_event_valid = TRUE;
2840
2841   if (gdk_event_get_button (event, &button) && button != 1)
2842     return FALSE;
2843
2844   child = gtk_bin_get_child (GTK_BIN (widget));
2845   if (!child)
2846     return FALSE;
2847
2848   if (priv->hscrollbar == event_widget || priv->vscrollbar == event_widget)
2849     return FALSE;
2850
2851   priv->drag_device = gdk_event_get_device (event);
2852   gtk_device_grab_add (widget, priv->drag_device, TRUE);
2853
2854   gtk_scrolled_window_cancel_deceleration (scrolled_window);
2855
2856   /* Only set the timeout if we're going to store an event */
2857   if (priv->capture_button_press)
2858     priv->release_timeout_id =
2859       gdk_threads_add_timeout (RELEASE_EVENT_TIMEOUT,
2860                                (GSourceFunc) gtk_scrolled_window_release_captured_event,
2861                                scrolled_window);
2862
2863   priv->in_drag = FALSE;
2864
2865   if (priv->capture_button_press)
2866     {
2867       /* Store the button press event in
2868        * case we need to propagate it later
2869        */
2870       priv->button_press_event = gdk_event_copy (event);
2871       return TRUE;
2872     }
2873   else
2874     return FALSE;
2875 }
2876
2877 static gboolean
2878 gtk_scrolled_window_captured_event (GtkWidget *widget,
2879                                     GdkEvent  *event)
2880 {
2881   gboolean retval = FALSE;
2882   GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW (widget)->priv;
2883
2884   switch (event->type)
2885     {
2886     case GDK_TOUCH_BEGIN:
2887     case GDK_BUTTON_PRESS:
2888       retval = gtk_scrolled_window_captured_button_press (widget, event);
2889       break;
2890     case GDK_TOUCH_END:
2891     case GDK_BUTTON_RELEASE:
2892       if (priv->drag_device)
2893         retval = gtk_scrolled_window_captured_button_release (widget, event);
2894       else
2895         priv->last_button_event_valid = FALSE;
2896       break;
2897     case GDK_TOUCH_UPDATE:
2898     case GDK_MOTION_NOTIFY:
2899       if (priv->drag_device)
2900         retval = gtk_scrolled_window_captured_motion_notify (widget, event);
2901       break;
2902     case GDK_LEAVE_NOTIFY:
2903     case GDK_ENTER_NOTIFY:
2904       if (priv->in_drag &&
2905           event->crossing.mode != GDK_CROSSING_GRAB)
2906         retval = TRUE;
2907       break;
2908     default:
2909       break;
2910     }
2911
2912   return retval;
2913 }
2914
2915 static gboolean
2916 gtk_scrolled_window_focus (GtkWidget        *widget,
2917                            GtkDirectionType  direction)
2918 {
2919   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2920   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2921   GtkWidget *child;
2922   gboolean had_focus_child;
2923
2924   had_focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget)) != NULL;
2925
2926   if (priv->focus_out)
2927     {
2928       priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
2929       return FALSE;
2930     }
2931   
2932   if (gtk_widget_is_focus (widget))
2933     return FALSE;
2934
2935   /* We only put the scrolled window itself in the focus chain if it
2936    * isn't possible to focus any children.
2937    */
2938   child = gtk_bin_get_child (GTK_BIN (widget));
2939   if (child)
2940     {
2941       if (gtk_widget_child_focus (child, direction))
2942         return TRUE;
2943     }
2944
2945   if (!had_focus_child && gtk_widget_get_can_focus (widget))
2946     {
2947       gtk_widget_grab_focus (widget);
2948       return TRUE;
2949     }
2950   else
2951     return FALSE;
2952 }
2953
2954 static void
2955 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
2956                                         gpointer       data)
2957 {
2958   GtkScrolledWindowPrivate *priv;
2959   GtkScrolledWindow *scrolled_window;
2960
2961   g_return_if_fail (adjustment != NULL);
2962   g_return_if_fail (data != NULL);
2963
2964   scrolled_window = GTK_SCROLLED_WINDOW (data);
2965   priv = scrolled_window->priv;
2966
2967   if (priv->hscrollbar &&
2968       adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
2969     {
2970       if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2971         {
2972           gboolean visible;
2973
2974           visible = priv->hscrollbar_visible;
2975           priv->hscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
2976                                       gtk_adjustment_get_page_size (adjustment));
2977
2978           if (priv->hscrollbar_visible != visible)
2979             gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2980         }
2981     }
2982   else if (priv->vscrollbar &&
2983            adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
2984     {
2985       if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
2986         {
2987           gboolean visible;
2988
2989           visible = priv->vscrollbar_visible;
2990           priv->vscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
2991                                       gtk_adjustment_get_page_size (adjustment));
2992
2993           if (priv->vscrollbar_visible != visible)
2994             gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2995         }
2996     }
2997 }
2998
2999 static void
3000 gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
3001                                               gpointer       user_data)
3002 {
3003   GtkScrolledWindow *scrolled_window = user_data;
3004   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3005
3006   /* Allow overshooting for kinetic scrolling operations */
3007   if (priv->drag_device || priv->deceleration_id)
3008     return;
3009
3010   /* Ensure GtkAdjustment and unclamped values are in sync */
3011   if (priv->vscrollbar &&
3012       adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
3013     priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
3014   else if (priv->hscrollbar &&
3015            adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
3016     priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
3017 }
3018
3019 static void
3020 gtk_scrolled_window_add (GtkContainer *container,
3021                          GtkWidget    *child)
3022 {
3023   GtkScrolledWindowPrivate *priv;
3024   GtkScrolledWindow *scrolled_window;
3025   GtkBin *bin;
3026   GtkWidget *child_widget;
3027   GtkAdjustment *hadj, *vadj;
3028
3029   bin = GTK_BIN (container);
3030   child_widget = gtk_bin_get_child (bin);
3031   g_return_if_fail (child_widget == NULL);
3032
3033   scrolled_window = GTK_SCROLLED_WINDOW (container);
3034   priv = scrolled_window->priv;
3035
3036   if (gtk_widget_get_realized (GTK_WIDGET (bin)))
3037     gtk_widget_set_parent_window (child, priv->overshoot_window);
3038
3039   _gtk_bin_set_child (bin, child);
3040   gtk_widget_set_parent (child, GTK_WIDGET (bin));
3041
3042   hadj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
3043   vadj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
3044
3045   if (GTK_IS_SCROLLABLE (child))
3046     g_object_set (child, "hadjustment", hadj, "vadjustment", vadj, NULL);
3047   else
3048     g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
3049                "use gtk_scrolled_window_add_with_viewport() instead");
3050 }
3051
3052 static void
3053 gtk_scrolled_window_remove (GtkContainer *container,
3054                             GtkWidget    *child)
3055 {
3056   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
3057   g_return_if_fail (child != NULL);
3058   g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
3059
3060   g_object_set (child, "hadjustment", NULL, "vadjustment", NULL, NULL);
3061
3062   /* chain parent class handler to remove child */
3063   GTK_CONTAINER_CLASS (gtk_scrolled_window_parent_class)->remove (container, child);
3064 }
3065
3066 /**
3067  * gtk_scrolled_window_add_with_viewport:
3068  * @scrolled_window: a #GtkScrolledWindow
3069  * @child: the widget you want to scroll
3070  *
3071  * Used to add children without native scrolling capabilities. This
3072  * is simply a convenience function; it is equivalent to adding the
3073  * unscrollable child to a viewport, then adding the viewport to the
3074  * scrolled window. If a child has native scrolling, use
3075  * gtk_container_add() instead of this function.
3076  *
3077  * The viewport scrolls the child by moving its #GdkWindow, and takes
3078  * the size of the child to be the size of its toplevel #GdkWindow. 
3079  * This will be very wrong for most widgets that support native scrolling;
3080  * for example, if you add a widget such as #GtkTreeView with a viewport,
3081  * the whole widget will scroll, including the column headings. Thus, 
3082  * widgets with native scrolling support should not be used with the 
3083  * #GtkViewport proxy.
3084  *
3085  * A widget supports scrolling natively if it implements the
3086  * #GtkScrollable interface.
3087  */
3088 void
3089 gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
3090                                        GtkWidget         *child)
3091 {
3092   GtkBin *bin;
3093   GtkWidget *viewport;
3094   GtkWidget *child_widget;
3095
3096   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3097   g_return_if_fail (GTK_IS_WIDGET (child));
3098   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
3099
3100   bin = GTK_BIN (scrolled_window);
3101   child_widget = gtk_bin_get_child (bin);
3102
3103   if (child_widget)
3104     {
3105       g_return_if_fail (GTK_IS_VIEWPORT (child_widget));
3106       g_return_if_fail (gtk_bin_get_child (GTK_BIN (child_widget)) == NULL);
3107
3108       viewport = child_widget;
3109     }
3110   else
3111     {
3112       viewport =
3113         gtk_viewport_new (gtk_scrolled_window_get_hadjustment (scrolled_window),
3114                           gtk_scrolled_window_get_vadjustment (scrolled_window));
3115       gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
3116     }
3117
3118   gtk_widget_show (viewport);
3119   gtk_container_add (GTK_CONTAINER (viewport), child);
3120 }
3121
3122 /*
3123  * _gtk_scrolled_window_get_spacing:
3124  * @scrolled_window: a scrolled window
3125  * 
3126  * Gets the spacing between the scrolled window's scrollbars and
3127  * the scrolled widget. Used by GtkCombo
3128  * 
3129  * Return value: the spacing, in pixels.
3130  */
3131 gint
3132 _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
3133 {
3134   GtkScrolledWindowClass *class;
3135     
3136   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
3137
3138   class = GTK_SCROLLED_WINDOW_GET_CLASS (scrolled_window);
3139
3140   if (class->scrollbar_spacing >= 0)
3141     return class->scrollbar_spacing;
3142   else
3143     {
3144       gint scrollbar_spacing;
3145       
3146       gtk_widget_style_get (GTK_WIDGET (scrolled_window),
3147                             "scrollbar-spacing", &scrollbar_spacing,
3148                             NULL);
3149
3150       return scrollbar_spacing;
3151     }
3152 }
3153
3154
3155 static void
3156 gtk_scrolled_window_get_preferred_size (GtkWidget      *widget,
3157                                         GtkOrientation  orientation,
3158                                         gint           *minimum_size,
3159                                         gint           *natural_size)
3160 {
3161   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3162   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3163   GtkBin *bin = GTK_BIN (scrolled_window);
3164   gint extra_width;
3165   gint extra_height;
3166   gint scrollbar_spacing;
3167   GtkRequisition hscrollbar_requisition;
3168   GtkRequisition vscrollbar_requisition;
3169   GtkRequisition minimum_req, natural_req;
3170   GtkWidget *child;
3171   gint min_child_size, nat_child_size;
3172
3173   scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
3174
3175   extra_width = 0;
3176   extra_height = 0;
3177   minimum_req.width = 0;
3178   minimum_req.height = 0;
3179   natural_req.width = 0;
3180   natural_req.height = 0;
3181
3182   gtk_widget_get_preferred_size (priv->hscrollbar,
3183                                  &hscrollbar_requisition, NULL);
3184   gtk_widget_get_preferred_size (priv->vscrollbar,
3185                                  &vscrollbar_requisition, NULL);
3186
3187   child = gtk_bin_get_child (bin);
3188   if (child && gtk_widget_get_visible (child))
3189     {
3190       if (orientation == GTK_ORIENTATION_HORIZONTAL)
3191         {
3192           gtk_widget_get_preferred_width (child,
3193                                           &min_child_size,
3194                                           &nat_child_size);
3195
3196           if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
3197             {
3198               minimum_req.width += min_child_size;
3199               natural_req.width += nat_child_size;
3200             }
3201           else
3202             {
3203               gint min_content_width = priv->min_content_width;
3204
3205               if (min_content_width >= 0)
3206                 {
3207                   minimum_req.width = MAX (minimum_req.width, min_content_width);
3208                   natural_req.width = MAX (natural_req.width, min_content_width);
3209                   extra_width = -1;
3210                 }
3211               else
3212                 {
3213                   minimum_req.width += vscrollbar_requisition.width;
3214                   natural_req.width += vscrollbar_requisition.width;
3215                 }
3216             }
3217         }
3218       else /* GTK_ORIENTATION_VERTICAL */
3219         {
3220           gtk_widget_get_preferred_height (child,
3221                                            &min_child_size,
3222                                            &nat_child_size);
3223
3224           if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
3225             {
3226               minimum_req.height += min_child_size;
3227               natural_req.height += nat_child_size;
3228             }
3229           else
3230             {
3231               gint min_content_height = priv->min_content_height;
3232
3233               if (min_content_height >= 0)
3234                 {
3235                   minimum_req.height = MAX (minimum_req.height, min_content_height);
3236                   natural_req.height = MAX (natural_req.height, min_content_height);
3237                   extra_height = -1;
3238                 }
3239               else
3240                 {
3241                   minimum_req.height += vscrollbar_requisition.height;
3242                   natural_req.height += vscrollbar_requisition.height;
3243                 }
3244             }
3245         }
3246     }
3247
3248   if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
3249       priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
3250     {
3251       minimum_req.width = MAX (minimum_req.width, hscrollbar_requisition.width);
3252       natural_req.width = MAX (natural_req.width, hscrollbar_requisition.width);
3253       if (!extra_height || priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
3254         extra_height = scrollbar_spacing + hscrollbar_requisition.height;
3255     }
3256
3257   if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
3258       priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
3259     {
3260       minimum_req.height = MAX (minimum_req.height, vscrollbar_requisition.height);
3261       natural_req.height = MAX (natural_req.height, vscrollbar_requisition.height);
3262       if (!extra_width || priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
3263         extra_width = scrollbar_spacing + vscrollbar_requisition.width;
3264     }
3265
3266   minimum_req.width  += MAX (0, extra_width);
3267   minimum_req.height += MAX (0, extra_height);
3268   natural_req.width  += MAX (0, extra_width);
3269   natural_req.height += MAX (0, extra_height);
3270
3271   if (priv->shadow_type != GTK_SHADOW_NONE)
3272     {
3273       GtkStyleContext *context;
3274       GtkStateFlags state;
3275       GtkBorder padding, border;
3276
3277       context = gtk_widget_get_style_context (GTK_WIDGET (widget));
3278       state = gtk_widget_get_state_flags (GTK_WIDGET (widget));
3279
3280       gtk_style_context_save (context);
3281       gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
3282
3283       gtk_style_context_get_padding (context, state, &padding);
3284       gtk_style_context_get_border (context, state, &border);
3285
3286       minimum_req.width += padding.left + padding.right + border.left + border.right;
3287       minimum_req.height += padding.top + padding.bottom + border.top + border.bottom;
3288       natural_req.width += padding.left + padding.right + border.left + border.right;
3289       natural_req.height += padding.top + padding.bottom + border.top + border.bottom;
3290
3291       gtk_style_context_restore (context);
3292     }
3293
3294   if (orientation == GTK_ORIENTATION_HORIZONTAL)
3295     {
3296       if (minimum_size)
3297         *minimum_size = minimum_req.width;
3298       if (natural_size)
3299         *natural_size = natural_req.width;
3300     }
3301   else
3302     {
3303       if (minimum_size)
3304         *minimum_size = minimum_req.height;
3305       if (natural_size)
3306         *natural_size = natural_req.height;
3307     }
3308 }
3309
3310 static void     
3311 gtk_scrolled_window_get_preferred_width (GtkWidget *widget,
3312                                          gint      *minimum_size,
3313                                          gint      *natural_size)
3314 {
3315   gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
3316 }
3317
3318 static void
3319 gtk_scrolled_window_get_preferred_height (GtkWidget *widget,
3320                                           gint      *minimum_size,
3321                                           gint      *natural_size)
3322 {  
3323   gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
3324 }
3325
3326 static void
3327 gtk_scrolled_window_get_preferred_height_for_width (GtkWidget *widget,
3328                                                     gint       width,
3329                                                     gint      *minimum_height,
3330                                                     gint      *natural_height)
3331 {
3332   g_return_if_fail (GTK_IS_WIDGET (widget));
3333
3334   GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
3335 }
3336
3337 static void
3338 gtk_scrolled_window_get_preferred_width_for_height (GtkWidget *widget,
3339                                                     gint       height,
3340                                                     gint      *minimum_width,
3341                                                     gint      *natural_width)
3342 {
3343   g_return_if_fail (GTK_IS_WIDGET (widget));
3344
3345   GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
3346 }
3347
3348 static void
3349 gtk_scrolled_window_realize (GtkWidget *widget)
3350 {
3351   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3352   GtkAllocation allocation, relative_allocation;
3353   GdkWindowAttr attributes;
3354   GtkWidget *child_widget;
3355   gint attributes_mask;
3356
3357   gtk_widget_set_realized (widget, TRUE);
3358   gtk_widget_get_allocation (widget, &allocation);
3359   gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
3360
3361   attributes.window_type = GDK_WINDOW_CHILD;
3362   attributes.x = allocation.x + relative_allocation.x;
3363   attributes.y = allocation.y + relative_allocation.y;
3364   attributes.width = allocation.width;
3365   attributes.height = allocation.height;
3366   attributes.wclass = GDK_INPUT_OUTPUT;
3367   attributes.visual = gtk_widget_get_visual (widget);
3368   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK |
3369     GDK_BUTTON_MOTION_MASK | GDK_TOUCH_MASK;
3370
3371   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3372
3373   scrolled_window->priv->overshoot_window =
3374     gdk_window_new (gtk_widget_get_parent_window (widget),
3375                     &attributes, attributes_mask);
3376
3377   gdk_window_set_user_data (scrolled_window->priv->overshoot_window, widget);
3378
3379   child_widget = gtk_bin_get_child (GTK_BIN (widget));
3380
3381   if (child_widget)
3382     gtk_widget_set_parent_window (child_widget,
3383                                   scrolled_window->priv->overshoot_window);
3384
3385   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->realize (widget);
3386 }
3387
3388 static void
3389 gtk_scrolled_window_unrealize (GtkWidget *widget)
3390 {
3391   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3392
3393   gdk_window_set_user_data (scrolled_window->priv->overshoot_window, NULL);
3394   gdk_window_destroy (scrolled_window->priv->overshoot_window);
3395   scrolled_window->priv->overshoot_window = NULL;
3396
3397   gtk_widget_set_realized (widget, FALSE);
3398
3399   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unrealize (widget);
3400 }
3401
3402 static void
3403 gtk_scrolled_window_map (GtkWidget *widget)
3404 {
3405   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3406
3407   gdk_window_show (scrolled_window->priv->overshoot_window);
3408
3409   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->map (widget);
3410 }
3411
3412 static void
3413 gtk_scrolled_window_unmap (GtkWidget *widget)
3414 {
3415   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3416
3417   gdk_window_hide (scrolled_window->priv->overshoot_window);
3418
3419   GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unmap (widget);
3420 }
3421
3422 static void
3423 gtk_scrolled_window_grab_notify (GtkWidget *widget,
3424                                  gboolean   was_grabbed)
3425 {
3426   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3427   GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3428
3429   if (priv->drag_device &&
3430       gtk_widget_device_is_shadowed (widget,
3431                                      priv->drag_device))
3432     {
3433       gdk_device_ungrab (priv->drag_device,
3434                          gtk_get_current_event_time ());
3435       priv->drag_device = NULL;
3436       priv->in_drag = FALSE;
3437
3438       if (priv->release_timeout_id)
3439         {
3440           g_source_remove (priv->release_timeout_id);
3441           priv->release_timeout_id = 0;
3442         }
3443
3444       if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
3445         gtk_scrolled_window_start_deceleration (scrolled_window);
3446       else
3447         gtk_scrolled_window_cancel_deceleration (scrolled_window);
3448
3449       priv->last_button_event_valid = FALSE;
3450     }
3451 }
3452
3453 /**
3454  * gtk_scrolled_window_get_min_content_width:
3455  * @scrolled_window: a #GtkScrolledWindow
3456  *
3457  * Gets the minimum content width of @scrolled_window, or -1 if not set.
3458  *
3459  * Returns: the minimum content width
3460  *
3461  * Since: 3.0
3462  */
3463 gint
3464 gtk_scrolled_window_get_min_content_width (GtkScrolledWindow *scrolled_window)
3465 {
3466   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
3467
3468   return scrolled_window->priv->min_content_width;
3469 }
3470
3471 /**
3472  * gtk_scrolled_window_set_min_content_width:
3473  * @scrolled_window: a #GtkScrolledWindow
3474  * @width: the minimal content width
3475  *
3476  * Sets the minimum width that @scrolled_window should keep visible.
3477  * Note that this can and (usually will) be smaller than the minimum
3478  * size of the content.
3479  *
3480  * Since: 3.0
3481  */
3482 void
3483 gtk_scrolled_window_set_min_content_width (GtkScrolledWindow *scrolled_window,
3484                                            gint               width)
3485 {
3486   GtkScrolledWindowPrivate *priv;
3487
3488   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3489
3490   priv = scrolled_window->priv;
3491
3492   if (priv->min_content_width != width)
3493     {
3494       priv->min_content_width = width;
3495
3496       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3497
3498       g_object_notify (G_OBJECT (scrolled_window), "min-content-width");
3499     }
3500 }
3501
3502 /**
3503  * gtk_scrolled_window_get_min_content_height:
3504  * @scrolled_window: a #GtkScrolledWindow
3505  *
3506  * Gets the minimal content height of @scrolled_window, or -1 if not set.
3507  *
3508  * Returns: the minimal content height
3509  *
3510  * Since: 3.0
3511  */
3512 gint
3513 gtk_scrolled_window_get_min_content_height (GtkScrolledWindow *scrolled_window)
3514 {
3515   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
3516
3517   return scrolled_window->priv->min_content_height;
3518 }
3519
3520 /**
3521  * gtk_scrolled_window_set_min_content_height:
3522  * @scrolled_window: a #GtkScrolledWindow
3523  * @height: the minimal content height
3524  *
3525  * Sets the minimum height that @scrolled_window should keep visible.
3526  * Note that this can and (usually will) be smaller than the minimum
3527  * size of the content.
3528  *
3529  * Since: 3.0
3530  */
3531 void
3532 gtk_scrolled_window_set_min_content_height (GtkScrolledWindow *scrolled_window,
3533                                             gint               height)
3534 {
3535   GtkScrolledWindowPrivate *priv;
3536
3537   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3538
3539   priv = scrolled_window->priv;
3540
3541   if (priv->min_content_height != height)
3542     {
3543       priv->min_content_height = height;
3544
3545       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3546
3547       g_object_notify (G_OBJECT (scrolled_window), "min-content-height");
3548     }
3549 }