1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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/>.
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/.
29 #include "gtkbindings.h"
30 #include "gtkmarshalers.h"
31 #include "gtkscrollable.h"
32 #include "gtkscrollbar.h"
33 #include "gtkscrolledwindow.h"
34 #include "gtkwindow.h"
35 #include "gtkviewport.h"
38 #include "gtkprivate.h"
39 #include "gtktypebuiltins.h"
41 #include "gtkviewport.h"
42 #include "gtkwidgetprivate.h"
43 #include "a11y/gtkscrolledwindowaccessible.h"
46 * SECTION:gtkscrolledwindow
47 * @Short_description: Adds scrollbars to its child widget
48 * @Title: GtkScrolledWindow
49 * @See_also: #GtkScrollable, #GtkViewport, #GtkAdjustment
51 * #GtkScrolledWindow is a #GtkBin subclass: it's a container
52 * the accepts a single child widget. #GtkScrolledWindow adds scrollbars
53 * to the child widget and optionally draws a beveled frame around the
56 * The scrolled window can work in two ways. Some widgets have native
57 * scrolling support; these widgets implement the #GtkScrollable interface.
58 * Widgets with native scroll support include #GtkTreeView, #GtkTextView,
61 * For widgets that lack native scrolling support, the #GtkViewport
62 * widget acts as an adaptor class, implementing scrollability for child
63 * widgets that lack their own scrolling capabilities. Use #GtkViewport
64 * to scroll child widgets such as #GtkGrid, #GtkBox, and so on.
66 * If a widget has native scrolling abilities, it can be added to the
67 * #GtkScrolledWindow with gtk_container_add(). If a widget does not, you
68 * must first add the widget to a #GtkViewport, then add the #GtkViewport
69 * to the scrolled window. The convenience function
70 * gtk_scrolled_window_add_with_viewport() does exactly this, so you can
71 * ignore the presence of the viewport.
73 * The position of the scrollbars is controlled by the scroll
74 * adjustments. See #GtkAdjustment for the fields in an adjustment - for
75 * #GtkScrollbar, used by #GtkScrolledWindow, the "value" field
76 * represents the position of the scrollbar, which must be between the
77 * "lower" field and "upper - page_size." The "page_size" field
78 * represents the size of the visible scrollable area. The
79 * "step_increment" and "page_increment" fields are used when the user
80 * asks to step down (using the small stepper arrows) or page down (using
81 * for example the PageDown key).
83 * If a #GtkScrolledWindow doesn't behave quite as you would like, or
84 * doesn't have exactly the right layout, it's very possible to set up
85 * your own scrolling with #GtkScrollbar and for example a #GtkGrid.
89 /* scrolled window policy and size requisition handling:
91 * gtk size requisition works as follows:
92 * a widget upon size-request reports the width and height that it finds
93 * to be best suited to display its contents, including children.
94 * the width and/or height reported from a widget upon size requisition
95 * may be overidden by the user by specifying a width and/or height
96 * other than 0 through gtk_widget_set_size_request().
98 * a scrolled window needs (for implementing all three policy types) to
99 * request its width and height based on two different rationales.
100 * 1) the user wants the scrolled window to just fit into the space
101 * that it gets allocated for a specifc dimension.
102 * 1.1) this does not apply if the user specified a concrete value
103 * value for that specific dimension by either specifying usize for the
104 * scrolled window or for its child.
105 * 2) the user wants the scrolled window to take as much space up as
106 * is desired by the child for a specifc dimension (i.e. POLICY_NEVER).
108 * also, kinda obvious:
109 * 3) a user would certainly not have choosen a scrolled window as a container
110 * for the child, if the resulting allocation takes up more space than the
111 * child would have allocated without the scrolled window.
114 * A) from 1) follows: the scrolled window shouldn't request more space for a
115 * specifc dimension than is required at minimum.
116 * B) from 1.1) follows: the requisition may be overidden by usize of the scrolled
117 * window (done automatically) or by usize of the child (needs to be checked).
118 * C) from 2) follows: for POLICY_NEVER, the scrolled window simply reports the
120 * D) from 3) follows: the scrolled window child's minimum width and minimum height
121 * under A) at least correspond to the space taken up by its scrollbars.
124 #define DEFAULT_SCROLLBAR_SPACING 3
125 #define TOUCH_BYPASS_CAPTURED_THRESHOLD 30
127 /* Kinetic scrolling */
128 #define FRAME_INTERVAL (1000 / 60)
129 #define MAX_OVERSHOOT_DISTANCE 50
130 #define FRICTION_DECELERATION 0.003
131 #define OVERSHOOT_INVERSE_ACCELERATION 0.003
132 #define RELEASE_EVENT_TIMEOUT 1000
134 struct _GtkScrolledWindowPrivate
136 GtkWidget *hscrollbar;
137 GtkWidget *vscrollbar;
139 GtkCornerType real_window_placement;
142 guint window_placement_set : 1;
143 guint hscrollbar_policy : 2;
144 guint vscrollbar_policy : 2;
145 guint hscrollbar_visible : 1;
146 guint vscrollbar_visible : 1;
147 guint window_placement : 2;
148 guint focus_out : 1; /* Flag used by ::move-focus-out implementation */
150 gint min_content_width;
151 gint min_content_height;
153 /* Kinetic scrolling */
154 GdkEvent *button_press_event;
155 GdkWindow *overshoot_window;
156 GdkDevice *drag_device;
157 guint kinetic_scrolling : 1;
158 guint capture_button_press : 1;
160 guint last_button_event_valid : 1;
162 guint release_timeout_id;
163 guint deceleration_id;
165 gdouble last_button_event_x_root;
166 gdouble last_button_event_y_root;
168 gdouble last_motion_event_x_root;
169 gdouble last_motion_event_y_root;
170 guint32 last_motion_event_time;
175 gdouble unclamped_hadj_value;
176 gdouble unclamped_vadj_value;
181 GtkScrolledWindow *scrolled_window;
182 gint64 last_deceleration_time;
194 PROP_HSCROLLBAR_POLICY,
195 PROP_VSCROLLBAR_POLICY,
196 PROP_WINDOW_PLACEMENT,
197 PROP_WINDOW_PLACEMENT_SET,
199 PROP_MIN_CONTENT_WIDTH,
200 PROP_MIN_CONTENT_HEIGHT,
201 PROP_KINETIC_SCROLLING
212 static void gtk_scrolled_window_set_property (GObject *object,
216 static void gtk_scrolled_window_get_property (GObject *object,
221 static void gtk_scrolled_window_destroy (GtkWidget *widget);
222 static void gtk_scrolled_window_screen_changed (GtkWidget *widget,
223 GdkScreen *previous_screen);
224 static gboolean gtk_scrolled_window_draw (GtkWidget *widget,
226 static void gtk_scrolled_window_size_allocate (GtkWidget *widget,
227 GtkAllocation *allocation);
228 static gboolean gtk_scrolled_window_scroll_event (GtkWidget *widget,
229 GdkEventScroll *event);
230 static gboolean gtk_scrolled_window_captured_event (GtkWidget *widget,
232 static gboolean gtk_scrolled_window_focus (GtkWidget *widget,
233 GtkDirectionType direction);
234 static void gtk_scrolled_window_add (GtkContainer *container,
236 static void gtk_scrolled_window_remove (GtkContainer *container,
238 static void gtk_scrolled_window_forall (GtkContainer *container,
239 gboolean include_internals,
240 GtkCallback callback,
241 gpointer callback_data);
242 static gboolean gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
243 GtkScrollType scroll,
244 gboolean horizontal);
245 static void gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
246 GtkDirectionType direction_type);
248 static void gtk_scrolled_window_relative_allocation(GtkWidget *widget,
249 GtkAllocation *allocation);
250 static void gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
252 static void gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
255 static void gtk_scrolled_window_update_real_placement (GtkScrolledWindow *scrolled_window);
257 static void gtk_scrolled_window_get_preferred_width (GtkWidget *widget,
260 static void gtk_scrolled_window_get_preferred_height (GtkWidget *widget,
263 static void gtk_scrolled_window_get_preferred_height_for_width (GtkWidget *layout,
265 gint *minimum_height,
266 gint *natural_height);
267 static void gtk_scrolled_window_get_preferred_width_for_height (GtkWidget *layout,
269 gint *minimum_height,
270 gint *natural_height);
272 static void gtk_scrolled_window_realize (GtkWidget *widget);
273 static void gtk_scrolled_window_unrealize (GtkWidget *widget);
274 static void gtk_scrolled_window_map (GtkWidget *widget);
275 static void gtk_scrolled_window_unmap (GtkWidget *widget);
276 static void gtk_scrolled_window_grab_notify (GtkWidget *widget,
277 gboolean was_grabbed);
279 static gboolean _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
280 GtkAdjustment *adjustment,
282 gboolean allow_overshooting,
283 gboolean snap_to_border);
285 static guint signals[LAST_SIGNAL] = {0};
287 G_DEFINE_TYPE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN)
291 add_scroll_binding (GtkBindingSet *binding_set,
293 GdkModifierType mask,
294 GtkScrollType scroll,
297 guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
299 gtk_binding_entry_add_signal (binding_set, keyval, mask,
301 GTK_TYPE_SCROLL_TYPE, scroll,
302 G_TYPE_BOOLEAN, horizontal);
303 gtk_binding_entry_add_signal (binding_set, keypad_keyval, mask,
305 GTK_TYPE_SCROLL_TYPE, scroll,
306 G_TYPE_BOOLEAN, horizontal);
310 add_tab_bindings (GtkBindingSet *binding_set,
311 GdkModifierType modifiers,
312 GtkDirectionType direction)
314 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
316 GTK_TYPE_DIRECTION_TYPE, direction);
317 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
319 GTK_TYPE_DIRECTION_TYPE, direction);
323 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
325 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
326 GtkWidgetClass *widget_class;
327 GtkContainerClass *container_class;
328 GtkBindingSet *binding_set;
330 widget_class = (GtkWidgetClass*) class;
331 container_class = (GtkContainerClass*) class;
333 gobject_class->set_property = gtk_scrolled_window_set_property;
334 gobject_class->get_property = gtk_scrolled_window_get_property;
336 widget_class->destroy = gtk_scrolled_window_destroy;
337 widget_class->screen_changed = gtk_scrolled_window_screen_changed;
338 widget_class->draw = gtk_scrolled_window_draw;
339 widget_class->size_allocate = gtk_scrolled_window_size_allocate;
340 widget_class->scroll_event = gtk_scrolled_window_scroll_event;
341 widget_class->focus = gtk_scrolled_window_focus;
342 widget_class->get_preferred_width = gtk_scrolled_window_get_preferred_width;
343 widget_class->get_preferred_height = gtk_scrolled_window_get_preferred_height;
344 widget_class->get_preferred_height_for_width = gtk_scrolled_window_get_preferred_height_for_width;
345 widget_class->get_preferred_width_for_height = gtk_scrolled_window_get_preferred_width_for_height;
346 widget_class->realize = gtk_scrolled_window_realize;
347 widget_class->unrealize = gtk_scrolled_window_unrealize;
348 widget_class->map = gtk_scrolled_window_map;
349 widget_class->unmap = gtk_scrolled_window_unmap;
350 widget_class->grab_notify = gtk_scrolled_window_grab_notify;
352 container_class->add = gtk_scrolled_window_add;
353 container_class->remove = gtk_scrolled_window_remove;
354 container_class->forall = gtk_scrolled_window_forall;
355 gtk_container_class_handle_border_width (container_class);
357 class->scrollbar_spacing = -1;
359 class->scroll_child = gtk_scrolled_window_scroll_child;
360 class->move_focus_out = gtk_scrolled_window_move_focus_out;
362 g_object_class_install_property (gobject_class,
364 g_param_spec_object ("hadjustment",
365 P_("Horizontal Adjustment"),
366 P_("The GtkAdjustment for the horizontal position"),
368 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
369 g_object_class_install_property (gobject_class,
371 g_param_spec_object ("vadjustment",
372 P_("Vertical Adjustment"),
373 P_("The GtkAdjustment for the vertical position"),
375 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
376 g_object_class_install_property (gobject_class,
377 PROP_HSCROLLBAR_POLICY,
378 g_param_spec_enum ("hscrollbar-policy",
379 P_("Horizontal Scrollbar Policy"),
380 P_("When the horizontal scrollbar is displayed"),
381 GTK_TYPE_POLICY_TYPE,
382 GTK_POLICY_AUTOMATIC,
383 GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
384 g_object_class_install_property (gobject_class,
385 PROP_VSCROLLBAR_POLICY,
386 g_param_spec_enum ("vscrollbar-policy",
387 P_("Vertical Scrollbar Policy"),
388 P_("When the vertical scrollbar is displayed"),
389 GTK_TYPE_POLICY_TYPE,
390 GTK_POLICY_AUTOMATIC,
391 GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
393 g_object_class_install_property (gobject_class,
394 PROP_WINDOW_PLACEMENT,
395 g_param_spec_enum ("window-placement",
396 P_("Window Placement"),
397 P_("Where the contents are located with respect to the scrollbars. This property only takes effect if \"window-placement-set\" is TRUE."),
398 GTK_TYPE_CORNER_TYPE,
400 GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
403 * GtkScrolledWindow:window-placement-set:
405 * Whether "window-placement" should be used to determine the location
406 * of the contents with respect to the scrollbars. Otherwise, the
407 * "gtk-scrolled-window-placement" setting is used.
411 g_object_class_install_property (gobject_class,
412 PROP_WINDOW_PLACEMENT_SET,
413 g_param_spec_boolean ("window-placement-set",
414 P_("Window Placement Set"),
415 P_("Whether \"window-placement\" should be used to determine the location of the contents with respect to the scrollbars."),
417 GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
418 g_object_class_install_property (gobject_class,
420 g_param_spec_enum ("shadow-type",
422 P_("Style of bevel around the contents"),
423 GTK_TYPE_SHADOW_TYPE,
425 GTK_PARAM_READABLE | GTK_PARAM_WRITABLE));
428 * GtkScrolledWindow:scrollbars-within-bevel:
430 * Whether to place scrollbars within the scrolled window's bevel.
434 gtk_widget_class_install_style_property (widget_class,
435 g_param_spec_boolean ("scrollbars-within-bevel",
436 P_("Scrollbars within bevel"),
437 P_("Place scrollbars within the scrolled window's bevel"),
439 GTK_PARAM_READABLE));
441 gtk_widget_class_install_style_property (widget_class,
442 g_param_spec_int ("scrollbar-spacing",
443 P_("Scrollbar spacing"),
444 P_("Number of pixels between the scrollbars and the scrolled window"),
447 DEFAULT_SCROLLBAR_SPACING,
448 GTK_PARAM_READABLE));
451 * GtkScrolledWindow:min-content-width:
453 * The minimum content width of @scrolled_window, or -1 if not set.
457 g_object_class_install_property (gobject_class,
458 PROP_MIN_CONTENT_WIDTH,
459 g_param_spec_int ("min-content-width",
460 P_("Minimum Content Width"),
461 P_("The minimum width that the scrolled window will allocate to its content"),
463 GTK_PARAM_READWRITE));
466 * GtkScrolledWindow:min-content-height:
468 * The minimum content height of @scrolled_window, or -1 if not set.
472 g_object_class_install_property (gobject_class,
473 PROP_MIN_CONTENT_HEIGHT,
474 g_param_spec_int ("min-content-height",
475 P_("Minimum Content Height"),
476 P_("The minimum height that the scrolled window will allocate to its content"),
478 GTK_PARAM_READWRITE));
481 * GtkScrolledWindow:kinetic-scrolling:
483 * The kinetic scrolling behavior flags. Kinetic scrolling
484 * only applies to devices with source %GDK_SOURCE_TOUCHSCREEN
488 g_object_class_install_property (gobject_class,
489 PROP_KINETIC_SCROLLING,
490 g_param_spec_boolean ("kinetic-scrolling",
491 P_("Kinetic Scrolling"),
492 P_("Kinetic scrolling mode."),
495 GTK_PARAM_WRITABLE));
497 * GtkScrolledWindow::scroll-child:
498 * @scrolled_window: a #GtkScrolledWindow
499 * @scroll: a #GtkScrollType describing how much to scroll
500 * @horizontal: whether the keybinding scrolls the child
501 * horizontally or not
503 * The ::scroll-child signal is a
504 * <link linkend="keybinding-signals">keybinding signal</link>
505 * which gets emitted when a keybinding that scrolls is pressed.
506 * The horizontal or vertical adjustment is updated which triggers a
507 * signal that the scrolled windows child may listen to and scroll itself.
509 signals[SCROLL_CHILD] =
510 g_signal_new (I_("scroll-child"),
511 G_TYPE_FROM_CLASS (gobject_class),
512 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
513 G_STRUCT_OFFSET (GtkScrolledWindowClass, scroll_child),
515 _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
517 GTK_TYPE_SCROLL_TYPE,
521 * GtkScrolledWindow::move-focus-out:
522 * @scrolled_window: a #GtkScrolledWindow
523 * @direction_type: either %GTK_DIR_TAB_FORWARD or
524 * %GTK_DIR_TAB_BACKWARD
526 * The ::move-focus-out signal is a
527 * <link linkend="keybinding-signals">keybinding signal</link>
528 * which gets emitted when focus is moved away from the scrolled
529 * window by a keybinding.
530 * The #GtkWidget::move-focus signal is emitted with @direction_type
531 * on this scrolled windows toplevel parent in the container hierarchy.
532 * The default bindings for this signal are
533 * <keycombo><keycap>Tab</keycap><keycap>Ctrl</keycap></keycombo>
535 * <keycombo><keycap>Tab</keycap><keycap>Ctrl</keycap><keycap>Shift</keycap></keycombo>.
537 signals[MOVE_FOCUS_OUT] =
538 g_signal_new (I_("move-focus-out"),
539 G_TYPE_FROM_CLASS (gobject_class),
540 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
541 G_STRUCT_OFFSET (GtkScrolledWindowClass, move_focus_out),
543 _gtk_marshal_VOID__ENUM,
545 GTK_TYPE_DIRECTION_TYPE);
547 binding_set = gtk_binding_set_by_class (class);
549 add_scroll_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, TRUE);
550 add_scroll_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD, TRUE);
551 add_scroll_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, FALSE);
552 add_scroll_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD, FALSE);
554 add_scroll_binding (binding_set, GDK_KEY_Page_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_BACKWARD, TRUE);
555 add_scroll_binding (binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_FORWARD, TRUE);
556 add_scroll_binding (binding_set, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_BACKWARD, FALSE);
557 add_scroll_binding (binding_set, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_FORWARD, FALSE);
559 add_scroll_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_SCROLL_START, TRUE);
560 add_scroll_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK, GTK_SCROLL_END, TRUE);
561 add_scroll_binding (binding_set, GDK_KEY_Home, 0, GTK_SCROLL_START, FALSE);
562 add_scroll_binding (binding_set, GDK_KEY_End, 0, GTK_SCROLL_END, FALSE);
564 add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
565 add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
567 g_type_class_add_private (class, sizeof (GtkScrolledWindowPrivate));
569 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SCROLLED_WINDOW_ACCESSIBLE);
573 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
575 GtkScrolledWindowPrivate *priv;
577 scrolled_window->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (scrolled_window,
578 GTK_TYPE_SCROLLED_WINDOW,
579 GtkScrolledWindowPrivate);
581 gtk_widget_set_has_window (GTK_WIDGET (scrolled_window), FALSE);
582 gtk_widget_set_can_focus (GTK_WIDGET (scrolled_window), TRUE);
584 priv->hscrollbar = NULL;
585 priv->vscrollbar = NULL;
586 priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC;
587 priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC;
588 priv->hscrollbar_visible = FALSE;
589 priv->vscrollbar_visible = FALSE;
590 priv->focus_out = FALSE;
591 priv->window_placement = GTK_CORNER_TOP_LEFT;
592 gtk_scrolled_window_update_real_placement (scrolled_window);
593 priv->min_content_width = -1;
594 priv->min_content_height = -1;
596 gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
597 gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
601 * gtk_scrolled_window_new:
602 * @hadjustment: (allow-none): horizontal adjustment
603 * @vadjustment: (allow-none): vertical adjustment
605 * Creates a new scrolled window.
607 * The two arguments are the scrolled window's adjustments; these will be
608 * shared with the scrollbars and the child widget to keep the bars in sync
609 * with the child. Usually you want to pass %NULL for the adjustments, which
610 * will cause the scrolled window to create them for you.
612 * Returns: a new scrolled window
615 gtk_scrolled_window_new (GtkAdjustment *hadjustment,
616 GtkAdjustment *vadjustment)
618 GtkWidget *scrolled_window;
621 g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
624 g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
626 scrolled_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
627 "hadjustment", hadjustment,
628 "vadjustment", vadjustment,
631 return scrolled_window;
635 * gtk_scrolled_window_set_hadjustment:
636 * @scrolled_window: a #GtkScrolledWindow
637 * @hadjustment: horizontal scroll adjustment
639 * Sets the #GtkAdjustment for the horizontal scrollbar.
642 gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
643 GtkAdjustment *hadjustment)
645 GtkScrolledWindowPrivate *priv;
649 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
651 g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
653 hadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
655 bin = GTK_BIN (scrolled_window);
656 priv = scrolled_window->priv;
658 if (!priv->hscrollbar)
660 gtk_widget_push_composite_child ();
661 priv->hscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, hadjustment);
662 gtk_widget_set_composite_name (priv->hscrollbar, "hscrollbar");
663 gtk_widget_pop_composite_child ();
665 gtk_widget_set_parent (priv->hscrollbar, GTK_WIDGET (scrolled_window));
666 g_object_ref (priv->hscrollbar);
667 gtk_widget_show (priv->hscrollbar);
671 GtkAdjustment *old_adjustment;
673 old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
674 if (old_adjustment == hadjustment)
677 g_signal_handlers_disconnect_by_func (old_adjustment,
678 gtk_scrolled_window_adjustment_changed,
680 gtk_range_set_adjustment (GTK_RANGE (priv->hscrollbar),
683 hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
684 g_signal_connect (hadjustment,
686 G_CALLBACK (gtk_scrolled_window_adjustment_changed),
688 g_signal_connect (hadjustment,
690 G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
692 gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
693 gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
695 child = gtk_bin_get_child (bin);
696 if (GTK_IS_SCROLLABLE (child))
697 gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (child), hadjustment);
699 g_object_notify (G_OBJECT (scrolled_window), "hadjustment");
703 * gtk_scrolled_window_set_vadjustment:
704 * @scrolled_window: a #GtkScrolledWindow
705 * @vadjustment: vertical scroll adjustment
707 * Sets the #GtkAdjustment for the vertical scrollbar.
710 gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
711 GtkAdjustment *vadjustment)
713 GtkScrolledWindowPrivate *priv;
717 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
719 g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
721 vadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
723 bin = GTK_BIN (scrolled_window);
724 priv = scrolled_window->priv;
726 if (!priv->vscrollbar)
728 gtk_widget_push_composite_child ();
729 priv->vscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, vadjustment);
730 gtk_widget_set_composite_name (priv->vscrollbar, "vscrollbar");
731 gtk_widget_pop_composite_child ();
733 gtk_widget_set_parent (priv->vscrollbar, GTK_WIDGET (scrolled_window));
734 g_object_ref (priv->vscrollbar);
735 gtk_widget_show (priv->vscrollbar);
739 GtkAdjustment *old_adjustment;
741 old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
742 if (old_adjustment == vadjustment)
745 g_signal_handlers_disconnect_by_func (old_adjustment,
746 gtk_scrolled_window_adjustment_changed,
748 gtk_range_set_adjustment (GTK_RANGE (priv->vscrollbar),
751 vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
752 g_signal_connect (vadjustment,
754 G_CALLBACK (gtk_scrolled_window_adjustment_changed),
756 g_signal_connect (vadjustment,
758 G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
760 gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
761 gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);
763 child = gtk_bin_get_child (bin);
764 if (GTK_IS_SCROLLABLE (child))
765 gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (child), vadjustment);
767 g_object_notify (G_OBJECT (scrolled_window), "vadjustment");
771 * gtk_scrolled_window_get_hadjustment:
772 * @scrolled_window: a #GtkScrolledWindow
774 * Returns the horizontal scrollbar's adjustment, used to connect the
775 * horizontal scrollbar to the child widget's horizontal scroll
778 * Returns: (transfer none): the horizontal #GtkAdjustment
781 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
783 GtkScrolledWindowPrivate *priv;
785 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
787 priv = scrolled_window->priv;
789 return (priv->hscrollbar ?
790 gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)) :
795 * gtk_scrolled_window_get_vadjustment:
796 * @scrolled_window: a #GtkScrolledWindow
798 * Returns the vertical scrollbar's adjustment, used to connect the
799 * vertical scrollbar to the child widget's vertical scroll functionality.
801 * Returns: (transfer none): the vertical #GtkAdjustment
804 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
806 GtkScrolledWindowPrivate *priv;
808 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
810 priv = scrolled_window->priv;
812 return (priv->vscrollbar ?
813 gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)) :
818 * gtk_scrolled_window_get_hscrollbar:
819 * @scrolled_window: a #GtkScrolledWindow
821 * Returns the horizontal scrollbar of @scrolled_window.
823 * Returns: (transfer none): the horizontal scrollbar of the scrolled window,
824 * or %NULL if it does not have one.
829 gtk_scrolled_window_get_hscrollbar (GtkScrolledWindow *scrolled_window)
831 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
833 return scrolled_window->priv->hscrollbar;
837 * gtk_scrolled_window_get_vscrollbar:
838 * @scrolled_window: a #GtkScrolledWindow
840 * Returns the vertical scrollbar of @scrolled_window.
842 * Returns: (transfer none): the vertical scrollbar of the scrolled window,
843 * or %NULL if it does not have one.
848 gtk_scrolled_window_get_vscrollbar (GtkScrolledWindow *scrolled_window)
850 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
852 return scrolled_window->priv->vscrollbar;
856 * gtk_scrolled_window_set_policy:
857 * @scrolled_window: a #GtkScrolledWindow
858 * @hscrollbar_policy: policy for horizontal bar
859 * @vscrollbar_policy: policy for vertical bar
861 * Sets the scrollbar policy for the horizontal and vertical scrollbars.
863 * The policy determines when the scrollbar should appear; it is a value
864 * from the #GtkPolicyType enumeration. If %GTK_POLICY_ALWAYS, the
865 * scrollbar is always present; if %GTK_POLICY_NEVER, the scrollbar is
866 * never present; if %GTK_POLICY_AUTOMATIC, the scrollbar is present only
867 * if needed (that is, if the slider part of the bar would be smaller
868 * than the trough - the display is larger than the page size).
871 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
872 GtkPolicyType hscrollbar_policy,
873 GtkPolicyType vscrollbar_policy)
875 GtkScrolledWindowPrivate *priv;
876 GObject *object = G_OBJECT (scrolled_window);
878 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
880 priv = scrolled_window->priv;
882 if ((priv->hscrollbar_policy != hscrollbar_policy) ||
883 (priv->vscrollbar_policy != vscrollbar_policy))
885 priv->hscrollbar_policy = hscrollbar_policy;
886 priv->vscrollbar_policy = vscrollbar_policy;
888 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
890 g_object_freeze_notify (object);
891 g_object_notify (object, "hscrollbar-policy");
892 g_object_notify (object, "vscrollbar-policy");
893 g_object_thaw_notify (object);
898 * gtk_scrolled_window_get_policy:
899 * @scrolled_window: a #GtkScrolledWindow
900 * @hscrollbar_policy: (out) (allow-none): location to store the policy
901 * for the horizontal scrollbar, or %NULL.
902 * @vscrollbar_policy: (out) (allow-none): location to store the policy
903 * for the vertical scrollbar, or %NULL.
905 * Retrieves the current policy values for the horizontal and vertical
906 * scrollbars. See gtk_scrolled_window_set_policy().
909 gtk_scrolled_window_get_policy (GtkScrolledWindow *scrolled_window,
910 GtkPolicyType *hscrollbar_policy,
911 GtkPolicyType *vscrollbar_policy)
913 GtkScrolledWindowPrivate *priv;
915 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
917 priv = scrolled_window->priv;
919 if (hscrollbar_policy)
920 *hscrollbar_policy = priv->hscrollbar_policy;
921 if (vscrollbar_policy)
922 *vscrollbar_policy = priv->vscrollbar_policy;
926 gtk_scrolled_window_update_real_placement (GtkScrolledWindow *scrolled_window)
928 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
929 GtkSettings *settings;
931 settings = gtk_widget_get_settings (GTK_WIDGET (scrolled_window));
933 if (priv->window_placement_set || settings == NULL)
934 priv->real_window_placement = priv->window_placement;
936 g_object_get (settings,
937 "gtk-scrolled-window-placement",
938 &priv->real_window_placement,
943 gtk_scrolled_window_set_placement_internal (GtkScrolledWindow *scrolled_window,
944 GtkCornerType window_placement)
946 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
948 if (priv->window_placement != window_placement)
950 priv->window_placement = window_placement;
952 gtk_scrolled_window_update_real_placement (scrolled_window);
953 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
955 g_object_notify (G_OBJECT (scrolled_window), "window-placement");
960 gtk_scrolled_window_set_placement_set (GtkScrolledWindow *scrolled_window,
961 gboolean placement_set,
962 gboolean emit_resize)
964 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
966 if (priv->window_placement_set != placement_set)
968 priv->window_placement_set = placement_set;
970 gtk_scrolled_window_update_real_placement (scrolled_window);
972 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
974 g_object_notify (G_OBJECT (scrolled_window), "window-placement-set");
979 * gtk_scrolled_window_set_placement:
980 * @scrolled_window: a #GtkScrolledWindow
981 * @window_placement: position of the child window
983 * Sets the placement of the contents with respect to the scrollbars
984 * for the scrolled window.
986 * The default is %GTK_CORNER_TOP_LEFT, meaning the child is
987 * in the top left, with the scrollbars underneath and to the right.
988 * Other values in #GtkCornerType are %GTK_CORNER_TOP_RIGHT,
989 * %GTK_CORNER_BOTTOM_LEFT, and %GTK_CORNER_BOTTOM_RIGHT.
991 * See also gtk_scrolled_window_get_placement() and
992 * gtk_scrolled_window_unset_placement().
995 gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
996 GtkCornerType window_placement)
998 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1000 gtk_scrolled_window_set_placement_set (scrolled_window, TRUE, FALSE);
1001 gtk_scrolled_window_set_placement_internal (scrolled_window, window_placement);
1005 * gtk_scrolled_window_get_placement:
1006 * @scrolled_window: a #GtkScrolledWindow
1008 * Gets the placement of the contents with respect to the scrollbars
1009 * for the scrolled window. See gtk_scrolled_window_set_placement().
1011 * Return value: the current placement value.
1013 * See also gtk_scrolled_window_set_placement() and
1014 * gtk_scrolled_window_unset_placement().
1017 gtk_scrolled_window_get_placement (GtkScrolledWindow *scrolled_window)
1019 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_CORNER_TOP_LEFT);
1021 return scrolled_window->priv->window_placement;
1025 * gtk_scrolled_window_unset_placement:
1026 * @scrolled_window: a #GtkScrolledWindow
1028 * Unsets the placement of the contents with respect to the scrollbars
1029 * for the scrolled window. If no window placement is set for a scrolled
1030 * window, it obeys the "gtk-scrolled-window-placement" XSETTING.
1032 * See also gtk_scrolled_window_set_placement() and
1033 * gtk_scrolled_window_get_placement().
1038 gtk_scrolled_window_unset_placement (GtkScrolledWindow *scrolled_window)
1040 GtkScrolledWindowPrivate *priv;
1042 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1044 priv = scrolled_window->priv;
1046 if (priv->window_placement_set)
1048 priv->window_placement_set = FALSE;
1050 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
1052 g_object_notify (G_OBJECT (scrolled_window), "window-placement-set");
1057 * gtk_scrolled_window_set_shadow_type:
1058 * @scrolled_window: a #GtkScrolledWindow
1059 * @type: kind of shadow to draw around scrolled window contents
1061 * Changes the type of shadow drawn around the contents of
1066 gtk_scrolled_window_set_shadow_type (GtkScrolledWindow *scrolled_window,
1069 GtkScrolledWindowPrivate *priv;
1071 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1072 g_return_if_fail (type >= GTK_SHADOW_NONE && type <= GTK_SHADOW_ETCHED_OUT);
1074 priv = scrolled_window->priv;
1076 if (priv->shadow_type != type)
1078 priv->shadow_type = type;
1080 if (gtk_widget_is_drawable (GTK_WIDGET (scrolled_window)))
1081 gtk_widget_queue_draw (GTK_WIDGET (scrolled_window));
1083 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
1085 g_object_notify (G_OBJECT (scrolled_window), "shadow-type");
1090 * gtk_scrolled_window_get_shadow_type:
1091 * @scrolled_window: a #GtkScrolledWindow
1093 * Gets the shadow type of the scrolled window. See
1094 * gtk_scrolled_window_set_shadow_type().
1096 * Return value: the current shadow type
1099 gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolled_window)
1101 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_NONE);
1103 return scrolled_window->priv->shadow_type;
1107 * gtk_scrolled_window_set_kinetic_scrolling:
1108 * @scrolled_window: a #GtkScrolledWindow
1109 * @kinetic_scrolling: %TRUE to enable kinetic scrolling
1111 * Turns kinetic scrolling on or off.
1112 * Kinetic scrolling only applies to devices with source
1113 * %GDK_SOURCE_TOUCHSCREEN.
1118 gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
1119 gboolean kinetic_scrolling)
1121 GtkScrolledWindowPrivate *priv;
1123 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1125 priv = scrolled_window->priv;
1126 if (priv->kinetic_scrolling == kinetic_scrolling)
1129 priv->kinetic_scrolling = kinetic_scrolling;
1130 if (priv->kinetic_scrolling)
1132 _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window),
1133 gtk_scrolled_window_captured_event);
1137 _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window), NULL);
1138 if (priv->release_timeout_id)
1140 g_source_remove (priv->release_timeout_id);
1141 priv->release_timeout_id = 0;
1143 if (priv->deceleration_id)
1145 g_source_remove (priv->deceleration_id);
1146 priv->deceleration_id = 0;
1149 g_object_notify (G_OBJECT (scrolled_window), "kinetic-scrolling");
1153 * gtk_scrolled_window_get_kinetic_scrolling:
1154 * @scrolled_window: a #GtkScrolledWindow
1156 * Returns the specified kinetic scrolling behavior.
1158 * Return value: the scrolling behavior flags.
1163 gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window)
1165 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
1167 return scrolled_window->priv->kinetic_scrolling;
1171 * gtk_scrolled_window_set_capture_button_press:
1172 * @scrolled_window: a #GtkScrolledWindow
1173 * @capture_button_press: %TRUE to capture button presses
1175 * Changes the behaviour of @scrolled_window wrt. to the initial
1176 * event that possibly starts kinetic scrolling. When @capture_button_press
1177 * is set to %TRUE, the event is captured by the scrolled window, and
1178 * then later replayed if it is meant to go to the child widget.
1180 * This should be enabled if any child widgets perform non-reversible
1181 * actions on #GtkWidget::button-press-event. If they don't, and handle
1182 * additionally handle #GtkWidget::grab-broken-event, it might be better
1183 * to set @capture_button_press to %FALSE.
1185 * This setting only has an effect if kinetic scrolling is enabled.
1190 gtk_scrolled_window_set_capture_button_press (GtkScrolledWindow *scrolled_window,
1191 gboolean capture_button_press)
1193 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1195 scrolled_window->priv->capture_button_press = capture_button_press;
1199 * gtk_scrolled_window_get_capture_button_press:
1200 * @scrolled_window: a #GtkScrolledWindow
1202 * Return whether button presses are captured during kinetic
1203 * scrolling. See gtk_scrolled_window_set_capture_button_press().
1205 * Returns: %TRUE if button presses are captured during kinetic scrolling
1210 gtk_scrolled_window_get_capture_button_press (GtkScrolledWindow *scrolled_window)
1212 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
1214 return scrolled_window->priv->capture_button_press;
1219 gtk_scrolled_window_destroy (GtkWidget *widget)
1221 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1222 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1224 if (priv->hscrollbar)
1226 g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)),
1227 gtk_scrolled_window_adjustment_changed,
1229 gtk_widget_unparent (priv->hscrollbar);
1230 gtk_widget_destroy (priv->hscrollbar);
1231 g_object_unref (priv->hscrollbar);
1232 priv->hscrollbar = NULL;
1234 if (priv->vscrollbar)
1236 g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)),
1237 gtk_scrolled_window_adjustment_changed,
1239 gtk_widget_unparent (priv->vscrollbar);
1240 gtk_widget_destroy (priv->vscrollbar);
1241 g_object_unref (priv->vscrollbar);
1242 priv->vscrollbar = NULL;
1245 if (priv->release_timeout_id)
1247 g_source_remove (priv->release_timeout_id);
1248 priv->release_timeout_id = 0;
1250 if (priv->deceleration_id)
1252 g_source_remove (priv->deceleration_id);
1253 priv->deceleration_id = 0;
1256 if (priv->button_press_event)
1258 gdk_event_free (priv->button_press_event);
1259 priv->button_press_event = NULL;
1262 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->destroy (widget);
1266 gtk_scrolled_window_set_property (GObject *object,
1268 const GValue *value,
1271 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
1272 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1276 case PROP_HADJUSTMENT:
1277 gtk_scrolled_window_set_hadjustment (scrolled_window,
1278 g_value_get_object (value));
1280 case PROP_VADJUSTMENT:
1281 gtk_scrolled_window_set_vadjustment (scrolled_window,
1282 g_value_get_object (value));
1284 case PROP_HSCROLLBAR_POLICY:
1285 gtk_scrolled_window_set_policy (scrolled_window,
1286 g_value_get_enum (value),
1287 priv->vscrollbar_policy);
1289 case PROP_VSCROLLBAR_POLICY:
1290 gtk_scrolled_window_set_policy (scrolled_window,
1291 priv->hscrollbar_policy,
1292 g_value_get_enum (value));
1294 case PROP_WINDOW_PLACEMENT:
1295 gtk_scrolled_window_set_placement_internal (scrolled_window,
1296 g_value_get_enum (value));
1298 case PROP_WINDOW_PLACEMENT_SET:
1299 gtk_scrolled_window_set_placement_set (scrolled_window,
1300 g_value_get_boolean (value),
1303 case PROP_SHADOW_TYPE:
1304 gtk_scrolled_window_set_shadow_type (scrolled_window,
1305 g_value_get_enum (value));
1307 case PROP_MIN_CONTENT_WIDTH:
1308 gtk_scrolled_window_set_min_content_width (scrolled_window,
1309 g_value_get_int (value));
1311 case PROP_MIN_CONTENT_HEIGHT:
1312 gtk_scrolled_window_set_min_content_height (scrolled_window,
1313 g_value_get_int (value));
1315 case PROP_KINETIC_SCROLLING:
1316 gtk_scrolled_window_set_kinetic_scrolling (scrolled_window,
1317 g_value_get_boolean (value));
1320 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1326 gtk_scrolled_window_get_property (GObject *object,
1331 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
1332 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1336 case PROP_HADJUSTMENT:
1337 g_value_set_object (value,
1338 G_OBJECT (gtk_scrolled_window_get_hadjustment (scrolled_window)));
1340 case PROP_VADJUSTMENT:
1341 g_value_set_object (value,
1342 G_OBJECT (gtk_scrolled_window_get_vadjustment (scrolled_window)));
1344 case PROP_WINDOW_PLACEMENT:
1345 g_value_set_enum (value, priv->window_placement);
1347 case PROP_WINDOW_PLACEMENT_SET:
1348 g_value_set_boolean (value, priv->window_placement_set);
1350 case PROP_SHADOW_TYPE:
1351 g_value_set_enum (value, priv->shadow_type);
1353 case PROP_HSCROLLBAR_POLICY:
1354 g_value_set_enum (value, priv->hscrollbar_policy);
1356 case PROP_VSCROLLBAR_POLICY:
1357 g_value_set_enum (value, priv->vscrollbar_policy);
1359 case PROP_MIN_CONTENT_WIDTH:
1360 g_value_set_int (value, priv->min_content_width);
1362 case PROP_MIN_CONTENT_HEIGHT:
1363 g_value_set_int (value, priv->min_content_height);
1365 case PROP_KINETIC_SCROLLING:
1366 g_value_set_boolean (value, priv->kinetic_scrolling);
1369 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1375 traverse_container (GtkWidget *widget,
1378 if (GTK_IS_SCROLLED_WINDOW (widget))
1380 gtk_scrolled_window_update_real_placement (GTK_SCROLLED_WINDOW (widget));
1381 gtk_widget_queue_resize (widget);
1383 else if (GTK_IS_CONTAINER (widget))
1384 gtk_container_forall (GTK_CONTAINER (widget), traverse_container, NULL);
1388 gtk_scrolled_window_settings_changed (GtkSettings *settings)
1392 list = gtk_window_list_toplevels ();
1394 for (l = list; l; l = l->next)
1395 gtk_container_forall (GTK_CONTAINER (l->data),
1396 traverse_container, NULL);
1402 gtk_scrolled_window_screen_changed (GtkWidget *widget,
1403 GdkScreen *previous_screen)
1405 GtkSettings *settings;
1406 guint window_placement_connection;
1408 gtk_scrolled_window_update_real_placement (GTK_SCROLLED_WINDOW (widget));
1410 if (!gtk_widget_has_screen (widget))
1413 settings = gtk_widget_get_settings (widget);
1415 window_placement_connection =
1416 GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (settings),
1417 "gtk-scrolled-window-connection"));
1419 if (window_placement_connection)
1422 window_placement_connection =
1423 g_signal_connect (settings, "notify::gtk-scrolled-window-placement",
1424 G_CALLBACK (gtk_scrolled_window_settings_changed), NULL);
1425 g_object_set_data (G_OBJECT (settings),
1426 I_("gtk-scrolled-window-connection"),
1427 GUINT_TO_POINTER (window_placement_connection));
1431 gtk_scrolled_window_draw_scrollbars_junction (GtkScrolledWindow *scrolled_window,
1434 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1435 GtkAllocation wid_allocation, hscr_allocation, vscr_allocation;
1436 GtkStyleContext *context;
1437 GdkRectangle junction_rect;
1440 is_rtl = gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL;
1441 gtk_widget_get_allocation (GTK_WIDGET (scrolled_window), &wid_allocation);
1442 gtk_widget_get_allocation (GTK_WIDGET (priv->hscrollbar), &hscr_allocation);
1443 gtk_widget_get_allocation (GTK_WIDGET (priv->vscrollbar), &vscr_allocation);
1445 junction_rect.x = 0;
1446 junction_rect.y = 0;
1447 junction_rect.width = vscr_allocation.width;
1448 junction_rect.height = hscr_allocation.height;
1451 (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
1452 priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
1454 (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1455 priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
1456 junction_rect.x = hscr_allocation.width;
1458 if (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1459 priv->real_window_placement == GTK_CORNER_TOP_RIGHT)
1460 junction_rect.y = vscr_allocation.height;
1462 context = gtk_widget_get_style_context (GTK_WIDGET (scrolled_window));
1463 gtk_style_context_save (context);
1464 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SCROLLBARS_JUNCTION);
1466 gtk_render_background (context, cr,
1467 junction_rect.x, junction_rect.y,
1468 junction_rect.width, junction_rect.height);
1469 gtk_render_frame (context, cr,
1470 junction_rect.x, junction_rect.y,
1471 junction_rect.width, junction_rect.height);
1473 gtk_style_context_restore (context);
1477 gtk_scrolled_window_draw (GtkWidget *widget,
1480 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1481 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1482 GtkAllocation relative_allocation;
1483 cairo_pattern_t *pattern = NULL;
1484 GtkStyleContext *context;
1487 context = gtk_widget_get_style_context (widget);
1488 gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1490 /* Use child's background if possible */
1491 child = gtk_bin_get_child (GTK_BIN (widget));
1493 if (child && gtk_widget_get_has_window (child))
1494 pattern = gdk_window_get_background_pattern (gtk_widget_get_window (child));
1497 cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SOLID)
1499 cairo_set_source (cr, pattern);
1501 cairo_rectangle (cr, relative_allocation.x, relative_allocation.y,
1502 relative_allocation.width, relative_allocation.height);
1506 gtk_render_background (context, cr,
1507 relative_allocation.x, relative_allocation.y,
1508 relative_allocation.width, relative_allocation.height);
1510 if (priv->hscrollbar_visible &&
1511 priv->vscrollbar_visible)
1512 gtk_scrolled_window_draw_scrollbars_junction (scrolled_window, cr);
1514 if (priv->shadow_type != GTK_SHADOW_NONE)
1516 gboolean scrollbars_within_bevel;
1518 gtk_style_context_save (context);
1519 gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
1521 gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
1523 if (!scrollbars_within_bevel)
1525 GtkStateFlags state;
1526 GtkBorder padding, border;
1528 state = gtk_widget_get_state_flags (widget);
1529 gtk_style_context_get_padding (context, state, &padding);
1530 gtk_style_context_get_border (context, state, &border);
1532 relative_allocation.x -= padding.left + border.left;
1533 relative_allocation.y -= padding.top + border.top;
1534 relative_allocation.width += padding.left + padding.right + border.left + border.right;
1535 relative_allocation.height += padding.top + padding.bottom + border.top + border.bottom;
1539 relative_allocation.x = 0;
1540 relative_allocation.y = 0;
1541 relative_allocation.width = gtk_widget_get_allocated_width (widget);
1542 relative_allocation.height = gtk_widget_get_allocated_height (widget);
1545 gtk_render_frame (context, cr,
1546 relative_allocation.x,
1547 relative_allocation.y,
1548 relative_allocation.width,
1549 relative_allocation.height);
1551 gtk_style_context_restore (context);
1554 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->draw (widget, cr);
1560 gtk_scrolled_window_forall (GtkContainer *container,
1561 gboolean include_internals,
1562 GtkCallback callback,
1563 gpointer callback_data)
1565 GtkScrolledWindowPrivate *priv;
1566 GtkScrolledWindow *scrolled_window;
1568 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
1569 g_return_if_fail (callback != NULL);
1571 GTK_CONTAINER_CLASS (gtk_scrolled_window_parent_class)->forall (container,
1575 if (include_internals)
1577 scrolled_window = GTK_SCROLLED_WINDOW (container);
1578 priv = scrolled_window->priv;
1580 if (priv->vscrollbar)
1581 callback (priv->vscrollbar, callback_data);
1582 if (priv->hscrollbar)
1583 callback (priv->hscrollbar, callback_data);
1588 gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
1589 GtkScrollType scroll,
1590 gboolean horizontal)
1592 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1593 GtkAdjustment *adjustment = NULL;
1597 case GTK_SCROLL_STEP_UP:
1598 scroll = GTK_SCROLL_STEP_BACKWARD;
1601 case GTK_SCROLL_STEP_DOWN:
1602 scroll = GTK_SCROLL_STEP_FORWARD;
1605 case GTK_SCROLL_STEP_LEFT:
1606 scroll = GTK_SCROLL_STEP_BACKWARD;
1609 case GTK_SCROLL_STEP_RIGHT:
1610 scroll = GTK_SCROLL_STEP_FORWARD;
1613 case GTK_SCROLL_PAGE_UP:
1614 scroll = GTK_SCROLL_PAGE_BACKWARD;
1617 case GTK_SCROLL_PAGE_DOWN:
1618 scroll = GTK_SCROLL_PAGE_FORWARD;
1621 case GTK_SCROLL_PAGE_LEFT:
1622 scroll = GTK_SCROLL_STEP_BACKWARD;
1625 case GTK_SCROLL_PAGE_RIGHT:
1626 scroll = GTK_SCROLL_STEP_FORWARD;
1629 case GTK_SCROLL_STEP_BACKWARD:
1630 case GTK_SCROLL_STEP_FORWARD:
1631 case GTK_SCROLL_PAGE_BACKWARD:
1632 case GTK_SCROLL_PAGE_FORWARD:
1633 case GTK_SCROLL_START:
1634 case GTK_SCROLL_END:
1637 g_warning ("Invalid scroll type %u for GtkScrolledWindow::scroll-child", scroll);
1641 if ((horizontal && (!priv->hscrollbar || !priv->hscrollbar_visible)) ||
1642 (!horizontal && (!priv->vscrollbar || !priv->vscrollbar_visible)))
1647 if (priv->hscrollbar)
1648 adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
1652 if (priv->vscrollbar)
1653 adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
1658 gdouble value = gtk_adjustment_get_value (adjustment);
1662 case GTK_SCROLL_STEP_FORWARD:
1663 value += gtk_adjustment_get_step_increment (adjustment);
1665 case GTK_SCROLL_STEP_BACKWARD:
1666 value -= gtk_adjustment_get_step_increment (adjustment);
1668 case GTK_SCROLL_PAGE_FORWARD:
1669 value += gtk_adjustment_get_page_increment (adjustment);
1671 case GTK_SCROLL_PAGE_BACKWARD:
1672 value -= gtk_adjustment_get_page_increment (adjustment);
1674 case GTK_SCROLL_START:
1675 value = gtk_adjustment_get_lower (adjustment);
1677 case GTK_SCROLL_END:
1678 value = gtk_adjustment_get_upper (adjustment);
1681 g_assert_not_reached ();
1685 gtk_adjustment_set_value (adjustment, value);
1694 gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
1695 GtkDirectionType direction_type)
1697 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1698 GtkWidget *toplevel;
1700 /* Focus out of the scrolled window entirely. We do this by setting
1701 * a flag, then propagating the focus motion to the notebook.
1703 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (scrolled_window));
1704 if (!gtk_widget_is_toplevel (toplevel))
1707 g_object_ref (scrolled_window);
1709 priv->focus_out = TRUE;
1710 g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1711 priv->focus_out = FALSE;
1713 g_object_unref (scrolled_window);
1717 gtk_scrolled_window_relative_allocation (GtkWidget *widget,
1718 GtkAllocation *allocation)
1720 GtkAllocation widget_allocation;
1721 GtkScrolledWindow *scrolled_window;
1722 GtkScrolledWindowPrivate *priv;
1727 g_return_if_fail (widget != NULL);
1728 g_return_if_fail (allocation != NULL);
1730 scrolled_window = GTK_SCROLLED_WINDOW (widget);
1731 priv = scrolled_window->priv;
1733 /* Get possible scrollbar dimensions */
1734 sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
1735 gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
1736 gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
1738 /* Subtract some things from our available allocation size */
1742 if (priv->shadow_type != GTK_SHADOW_NONE)
1744 GtkStyleContext *context;
1745 GtkStateFlags state;
1746 GtkBorder padding, border;
1748 context = gtk_widget_get_style_context (widget);
1749 state = gtk_widget_get_state_flags (widget);
1751 gtk_style_context_save (context);
1752 gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
1754 gtk_style_context_get_border (context, state, &border);
1755 gtk_style_context_get_padding (context, state, &padding);
1757 allocation->x += padding.left + border.left;
1758 allocation->y += padding.top + border.top;
1760 gtk_style_context_restore (context);
1763 gtk_widget_get_allocation (widget, &widget_allocation);
1764 allocation->width = MAX (1, (gint) widget_allocation.width - allocation->x * 2);
1765 allocation->height = MAX (1, (gint) widget_allocation.height - allocation->y * 2);
1767 if (priv->vscrollbar_visible)
1771 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1774 (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
1775 priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
1777 (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
1778 priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
1779 allocation->x += (sb_width + sb_spacing);
1781 allocation->width = MAX (1, allocation->width - (sb_width + sb_spacing));
1783 if (priv->hscrollbar_visible)
1786 if (priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT ||
1787 priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)
1788 allocation->y += (sb_height + sb_spacing);
1790 allocation->height = MAX (1, allocation->height - (sb_height + sb_spacing));
1795 _gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
1799 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1800 GtkAdjustment *vadjustment, *hadjustment;
1801 gdouble lower, upper, x, y;
1803 /* Vertical overshoot */
1804 vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
1805 lower = gtk_adjustment_get_lower (vadjustment);
1806 upper = gtk_adjustment_get_upper (vadjustment) -
1807 gtk_adjustment_get_page_size (vadjustment);
1809 if (priv->unclamped_vadj_value < lower)
1810 y = priv->unclamped_vadj_value - lower;
1811 else if (priv->unclamped_vadj_value > upper)
1812 y = priv->unclamped_vadj_value - upper;
1816 /* Horizontal overshoot */
1817 hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
1818 lower = gtk_adjustment_get_lower (hadjustment);
1819 upper = gtk_adjustment_get_upper (hadjustment) -
1820 gtk_adjustment_get_page_size (hadjustment);
1822 if (priv->unclamped_hadj_value < lower)
1823 x = priv->unclamped_hadj_value - lower;
1824 else if (priv->unclamped_hadj_value > upper)
1825 x = priv->unclamped_hadj_value - upper;
1835 return (x != 0 || y != 0);
1839 _gtk_scrolled_window_allocate_overshoot_window (GtkScrolledWindow *scrolled_window)
1841 GtkAllocation window_allocation, relative_allocation, allocation;
1842 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1843 GtkWidget *widget = GTK_WIDGET (scrolled_window);
1844 gint overshoot_x, overshoot_y;
1846 if (!gtk_widget_get_realized (widget))
1849 gtk_widget_get_allocation (widget, &allocation);
1850 gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
1851 _gtk_scrolled_window_get_overshoot (scrolled_window,
1852 &overshoot_x, &overshoot_y);
1854 window_allocation = relative_allocation;
1855 window_allocation.x += allocation.x;
1856 window_allocation.y += allocation.y;
1858 /* Handle displacement to the left/top by moving the overshoot
1859 * window, overshooting to the bottom/right is handled in
1860 * gtk_scrolled_window_allocate_child()
1862 if (overshoot_x < 0)
1863 window_allocation.x += -overshoot_x;
1865 if (overshoot_y < 0)
1866 window_allocation.y += -overshoot_y;
1868 window_allocation.width -= ABS (overshoot_x);
1869 window_allocation.height -= ABS (overshoot_y);
1871 gdk_window_move_resize (priv->overshoot_window,
1872 window_allocation.x, window_allocation.y,
1873 window_allocation.width, window_allocation.height);
1877 gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
1878 GtkAllocation *relative_allocation)
1880 GtkWidget *widget = GTK_WIDGET (swindow), *child;
1881 GtkAllocation allocation;
1882 GtkAllocation child_allocation;
1883 gint overshoot_x, overshoot_y;
1885 child = gtk_bin_get_child (GTK_BIN (widget));
1887 gtk_widget_get_allocation (widget, &allocation);
1889 gtk_scrolled_window_relative_allocation (widget, relative_allocation);
1890 _gtk_scrolled_window_get_overshoot (swindow, &overshoot_x, &overshoot_y);
1892 /* Handle overshooting to the right/bottom by relocating the
1893 * widget allocation to negative coordinates, so these edges
1894 * stick to the overshoot window border.
1896 if (overshoot_x > 0)
1897 child_allocation.x = -overshoot_x;
1899 child_allocation.x = 0;
1901 if (overshoot_y > 0)
1902 child_allocation.y = -overshoot_y;
1904 child_allocation.y = 0;
1906 child_allocation.width = relative_allocation->width;
1907 child_allocation.height = relative_allocation->height;
1909 gtk_widget_size_allocate (child, &child_allocation);
1913 gtk_scrolled_window_size_allocate (GtkWidget *widget,
1914 GtkAllocation *allocation)
1916 GtkScrolledWindow *scrolled_window;
1917 GtkScrolledWindowPrivate *priv;
1918 GtkStyleContext *context;
1919 GtkStateFlags state;
1920 GtkBorder padding, border;
1922 GtkAllocation relative_allocation;
1923 GtkAllocation child_allocation;
1925 gboolean scrollbars_within_bevel;
1930 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
1931 g_return_if_fail (allocation != NULL);
1933 scrolled_window = GTK_SCROLLED_WINDOW (widget);
1934 bin = GTK_BIN (scrolled_window);
1935 priv = scrolled_window->priv;
1937 /* Get possible scrollbar dimensions */
1938 sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
1939 gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
1940 gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
1942 context = gtk_widget_get_style_context (widget);
1943 state = gtk_widget_get_state_flags (widget);
1945 gtk_style_context_save (context);
1946 gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
1948 gtk_style_context_get_padding (context, state, &padding);
1949 gtk_style_context_get_border (context, state, &border);
1951 gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
1953 gtk_widget_set_allocation (widget, allocation);
1954 gtk_style_context_restore (context);
1956 if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
1957 priv->hscrollbar_visible = TRUE;
1958 else if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
1959 priv->hscrollbar_visible = FALSE;
1960 if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
1961 priv->vscrollbar_visible = TRUE;
1962 else if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
1963 priv->vscrollbar_visible = FALSE;
1965 child = gtk_bin_get_child (bin);
1966 if (child && gtk_widget_get_visible (child))
1968 gint child_scroll_width;
1969 gint child_scroll_height;
1970 gboolean previous_hvis;
1971 gboolean previous_vvis;
1974 /* Determine scrollbar visibility first via hfw apis */
1975 if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1977 if (gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
1978 gtk_widget_get_preferred_width (child, &child_scroll_width, NULL);
1980 gtk_widget_get_preferred_width (child, NULL, &child_scroll_width);
1982 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1984 /* First try without a vertical scrollbar if the content will fit the height
1985 * given the extra width of the scrollbar */
1986 if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
1987 gtk_widget_get_preferred_height_for_width (child,
1988 MAX (allocation->width, child_scroll_width),
1989 &child_scroll_height, NULL);
1991 gtk_widget_get_preferred_height_for_width (child,
1992 MAX (allocation->width, child_scroll_width),
1993 NULL, &child_scroll_height);
1995 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1997 /* Does the content height fit the allocation height ? */
1998 priv->vscrollbar_visible = child_scroll_height > allocation->height;
2000 /* Does the content width fit the allocation with minus a possible scrollbar ? */
2001 priv->hscrollbar_visible =
2002 child_scroll_width > allocation->width -
2003 (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2005 /* Now that we've guessed the hscrollbar, does the content height fit
2006 * the possible new allocation height ? */
2007 priv->vscrollbar_visible =
2008 child_scroll_height > allocation->height -
2009 (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2011 /* Now that we've guessed the vscrollbar, does the content width fit
2012 * the possible new allocation width ? */
2013 priv->hscrollbar_visible =
2014 child_scroll_width > allocation->width -
2015 (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2017 else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
2019 priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
2020 priv->vscrollbar_visible = child_scroll_height > allocation->height -
2021 (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2024 else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
2026 priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
2028 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2029 priv->hscrollbar_visible =
2030 child_scroll_width > allocation->width -
2031 (priv->vscrollbar_visible ? 0 : sb_width + sb_spacing);
2033 priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
2036 else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
2038 if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
2039 gtk_widget_get_preferred_height (child, &child_scroll_height, NULL);
2041 gtk_widget_get_preferred_height (child, NULL, &child_scroll_height);
2043 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2045 /* First try without a horizontal scrollbar if the content will fit the width
2046 * given the extra height of the scrollbar */
2047 if (gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (child)) == GTK_SCROLL_MINIMUM)
2048 gtk_widget_get_preferred_width_for_height (child,
2049 MAX (allocation->height, child_scroll_height),
2050 &child_scroll_width, NULL);
2052 gtk_widget_get_preferred_width_for_height (child,
2053 MAX (allocation->height, child_scroll_height),
2054 NULL, &child_scroll_width);
2056 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
2058 /* Does the content width fit the allocation width ? */
2059 priv->hscrollbar_visible = child_scroll_width > allocation->width;
2061 /* Does the content height fit the allocation with minus a possible scrollbar ? */
2062 priv->vscrollbar_visible =
2063 child_scroll_height > allocation->height -
2064 (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2066 /* Now that we've guessed the vscrollbar, does the content width fit
2067 * the possible new allocation width ? */
2068 priv->hscrollbar_visible =
2069 child_scroll_width > allocation->width -
2070 (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2072 /* Now that we've guessed the hscrollbar, does the content height fit
2073 * the possible new allocation height ? */
2074 priv->vscrollbar_visible =
2075 child_scroll_height > allocation->height -
2076 (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
2078 else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
2080 priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
2081 priv->hscrollbar_visible = child_scroll_width > allocation->width -
2082 (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
2085 else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
2087 priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
2089 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
2090 priv->vscrollbar_visible =
2091 child_scroll_height > allocation->height -
2092 (priv->hscrollbar_visible ? 0 : sb_height + sb_spacing);
2094 priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
2098 /* Now after guessing scrollbar visibility; fall back on the allocation loop which
2099 * observes the adjustments to detect scrollbar visibility and also avoids
2100 * infinite recursion
2104 previous_hvis = priv->hscrollbar_visible;
2105 previous_vvis = priv->vscrollbar_visible;
2106 gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
2108 /* Explicitly force scrollbar visibility checks.
2110 * Since we make a guess above, the child might not decide to update the adjustments
2111 * if they logically did not change since the last configuration
2113 if (priv->hscrollbar)
2114 gtk_scrolled_window_adjustment_changed
2115 (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)), scrolled_window);
2117 if (priv->vscrollbar)
2118 gtk_scrolled_window_adjustment_changed
2119 (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)), scrolled_window);
2121 /* If, after the first iteration, the hscrollbar and the
2122 * vscrollbar flip visiblity... or if one of the scrollbars flip
2123 * on each itteration indefinitly/infinitely, then we just need both
2127 previous_hvis != priv->hscrollbar_visible &&
2128 previous_vvis != priv->vscrollbar_visible) || count > 3)
2130 priv->hscrollbar_visible = TRUE;
2131 priv->vscrollbar_visible = TRUE;
2133 gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
2140 while (previous_hvis != priv->hscrollbar_visible ||
2141 previous_vvis != priv->vscrollbar_visible);
2145 priv->hscrollbar_visible = priv->hscrollbar_policy == GTK_POLICY_ALWAYS;
2146 priv->vscrollbar_visible = priv->vscrollbar_policy == GTK_POLICY_ALWAYS;
2147 gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
2150 if (priv->hscrollbar_visible)
2152 if (!gtk_widget_get_visible (priv->hscrollbar))
2153 gtk_widget_show (priv->hscrollbar);
2155 child_allocation.x = relative_allocation.x;
2156 if (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
2157 priv->real_window_placement == GTK_CORNER_TOP_RIGHT)
2158 child_allocation.y = (relative_allocation.y +
2159 relative_allocation.height +
2161 (priv->shadow_type == GTK_SHADOW_NONE ?
2162 0 : padding.top + border.top));
2164 child_allocation.y = 0;
2166 child_allocation.width = relative_allocation.width;
2167 child_allocation.height = sb_height;
2168 child_allocation.x += allocation->x;
2169 child_allocation.y += allocation->y;
2171 if (priv->shadow_type != GTK_SHADOW_NONE)
2173 if (!scrollbars_within_bevel)
2175 child_allocation.x -= padding.left + border.left;
2176 child_allocation.width += padding.left + padding.right + border.left + border.right;
2178 else if (GTK_CORNER_TOP_RIGHT == priv->real_window_placement ||
2179 GTK_CORNER_TOP_LEFT == priv->real_window_placement)
2181 child_allocation.y -= padding.top + border.top;
2185 child_allocation.y += padding.top + border.top;
2189 gtk_widget_size_allocate (priv->hscrollbar, &child_allocation);
2191 else if (gtk_widget_get_visible (priv->hscrollbar))
2192 gtk_widget_hide (priv->hscrollbar);
2194 if (priv->vscrollbar_visible)
2196 if (!gtk_widget_get_visible (priv->vscrollbar))
2197 gtk_widget_show (priv->vscrollbar);
2199 if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL &&
2200 (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
2201 priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
2202 (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR &&
2203 (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
2204 priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
2205 child_allocation.x = (relative_allocation.x +
2206 relative_allocation.width +
2208 (priv->shadow_type == GTK_SHADOW_NONE ?
2209 0 : padding.left + border.left));
2211 child_allocation.x = 0;
2213 child_allocation.y = relative_allocation.y;
2214 child_allocation.width = sb_width;
2215 child_allocation.height = relative_allocation.height;
2216 child_allocation.x += allocation->x;
2217 child_allocation.y += allocation->y;
2219 if (priv->shadow_type != GTK_SHADOW_NONE)
2221 if (!scrollbars_within_bevel)
2223 child_allocation.y -= padding.top + border.top;
2224 child_allocation.height += padding.top + padding.bottom + border.top + border.bottom;
2226 else if (GTK_CORNER_BOTTOM_LEFT == priv->real_window_placement ||
2227 GTK_CORNER_TOP_LEFT == priv->real_window_placement)
2229 child_allocation.x -= padding.left + border.left;
2233 child_allocation.x += padding.left + border.left;
2237 gtk_widget_size_allocate (priv->vscrollbar, &child_allocation);
2239 else if (gtk_widget_get_visible (priv->vscrollbar))
2240 gtk_widget_hide (priv->vscrollbar);
2242 _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
2246 gtk_scrolled_window_scroll_event (GtkWidget *widget,
2247 GdkEventScroll *event)
2249 GtkScrolledWindowPrivate *priv;
2250 GtkScrolledWindow *scrolled_window;
2253 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE);
2254 g_return_val_if_fail (event != NULL, FALSE);
2256 scrolled_window = GTK_SCROLLED_WINDOW (widget);
2257 priv = scrolled_window->priv;
2259 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2260 range = priv->vscrollbar;
2262 range = priv->hscrollbar;
2264 if (range && gtk_widget_get_visible (range))
2266 GtkAdjustment *adjustment = gtk_range_get_adjustment (GTK_RANGE (range));
2269 delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event->direction);
2270 gtk_adjustment_set_value (adjustment, gtk_adjustment_get_value (adjustment) + delta);
2279 _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
2280 GtkAdjustment *adjustment,
2282 gboolean allow_overshooting,
2283 gboolean snap_to_border)
2285 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2286 gdouble lower, upper, *prev_value;
2288 lower = gtk_adjustment_get_lower (adjustment);
2289 upper = gtk_adjustment_get_upper (adjustment) -
2290 gtk_adjustment_get_page_size (adjustment);
2292 if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
2293 prev_value = &priv->unclamped_hadj_value;
2294 else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
2295 prev_value = &priv->unclamped_vadj_value;
2301 if (*prev_value < 0 && value > 0)
2303 else if (*prev_value > upper && value < upper)
2307 if (allow_overshooting)
2309 lower -= MAX_OVERSHOOT_DISTANCE;
2310 upper += MAX_OVERSHOOT_DISTANCE;
2313 *prev_value = CLAMP (value, lower, upper);
2314 gtk_adjustment_set_value (adjustment, value);
2316 return (*prev_value != value);
2320 scrolled_window_deceleration_cb (gpointer user_data)
2322 KineticScrollData *data = user_data;
2323 GtkScrolledWindow *scrolled_window = data->scrolled_window;
2324 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2325 GtkAdjustment *hadjustment, *vadjustment;
2326 gint old_overshoot_x, old_overshoot_y, overshoot_x, overshoot_y;
2328 gint64 current_time;
2331 hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2332 vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2334 _gtk_scrolled_window_get_overshoot (scrolled_window,
2335 &old_overshoot_x, &old_overshoot_y);
2337 current_time = g_get_monotonic_time ();
2338 elapsed = (current_time - data->last_deceleration_time) / 1000;
2339 data->last_deceleration_time = current_time;
2341 if (hadjustment && priv->hscrollbar_visible)
2343 value = priv->unclamped_hadj_value + (data->x_velocity * elapsed);
2345 if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
2348 data->x_velocity = 0;
2351 data->x_velocity = 0;
2353 if (vadjustment && priv->vscrollbar_visible)
2355 value = priv->unclamped_vadj_value + (data->y_velocity * elapsed);
2357 if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
2360 data->y_velocity = 0;
2363 data->y_velocity = 0;
2365 _gtk_scrolled_window_get_overshoot (scrolled_window,
2366 &overshoot_x, &overshoot_y);
2368 if (overshoot_x == 0)
2370 if (old_overshoot_x != 0)
2372 /* Overshooting finished snapping back */
2373 data->x_velocity = 0;
2375 else if (data->x_velocity > 0)
2377 data->x_velocity -= FRICTION_DECELERATION * elapsed * data->vel_sine;
2378 data->x_velocity = MAX (0, data->x_velocity);
2380 else if (data->x_velocity < 0)
2382 data->x_velocity += FRICTION_DECELERATION * elapsed * data->vel_sine;
2383 data->x_velocity = MIN (0, data->x_velocity);
2386 else if (overshoot_x < 0)
2387 data->x_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2388 else if (overshoot_x > 0)
2389 data->x_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2391 if (overshoot_y == 0)
2393 if (old_overshoot_y != 0)
2395 /* Overshooting finished snapping back */
2396 data->y_velocity = 0;
2398 else if (data->y_velocity > 0)
2400 data->y_velocity -= FRICTION_DECELERATION * elapsed * data->vel_cosine;
2401 data->y_velocity = MAX (0, data->y_velocity);
2403 else if (data->y_velocity < 0)
2405 data->y_velocity += FRICTION_DECELERATION * elapsed * data->vel_cosine;
2406 data->y_velocity = MIN (0, data->y_velocity);
2409 else if (overshoot_y < 0)
2410 data->y_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2411 else if (overshoot_y > 0)
2412 data->y_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
2414 if (old_overshoot_x != overshoot_x ||
2415 old_overshoot_y != overshoot_y)
2417 if (overshoot_x >= 0 || overshoot_y >= 0)
2419 /* We need to reallocate the widget to have it at
2420 * negative offset, so there's a "gravity" on the
2421 * bottom/right corner
2423 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2425 else if (overshoot_x < 0 || overshoot_y < 0)
2426 _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
2429 if (overshoot_x != 0 || overshoot_y != 0 ||
2430 data->x_velocity != 0 || data->y_velocity != 0)
2434 priv->deceleration_id = 0;
2440 gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window)
2442 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2444 if (priv->deceleration_id)
2446 g_source_remove (priv->deceleration_id);
2447 priv->deceleration_id = 0;
2452 gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
2454 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2455 KineticScrollData *data;
2458 data = g_new0 (KineticScrollData, 1);
2459 data->scrolled_window = scrolled_window;
2460 data->last_deceleration_time = g_get_monotonic_time ();
2461 data->x_velocity = priv->x_velocity;
2462 data->y_velocity = priv->y_velocity;
2464 /* We use sine/cosine as a factor to deceleration x/y components
2465 * of the vector, so we care about the sign later.
2467 angle = atan2 (ABS (data->x_velocity), ABS (data->y_velocity));
2468 data->vel_cosine = cos (angle);
2469 data->vel_sine = sin (angle);
2471 scrolled_window->priv->deceleration_id =
2472 gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
2474 scrolled_window_deceleration_cb,
2475 data, (GDestroyNotify) g_free);
2479 gtk_scrolled_window_release_captured_event (GtkScrolledWindow *scrolled_window)
2481 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2483 /* Cancel the scrolling and send the button press
2484 * event to the child widget
2486 if (!priv->button_press_event)
2489 if (priv->drag_device)
2491 gtk_device_grab_remove (GTK_WIDGET (scrolled_window), priv->drag_device);
2492 priv->drag_device = NULL;
2495 if (priv->capture_button_press)
2497 GtkWidget *event_widget;
2499 event_widget = gtk_get_event_widget (priv->button_press_event);
2501 if (!_gtk_propagate_captured_event (event_widget,
2502 priv->button_press_event,
2503 gtk_bin_get_child (GTK_BIN (scrolled_window))))
2504 gtk_propagate_event (event_widget, priv->button_press_event);
2506 gdk_event_free (priv->button_press_event);
2507 priv->button_press_event = NULL;
2510 if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
2511 gtk_scrolled_window_start_deceleration (scrolled_window);
2517 gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
2520 GtkScrolledWindowPrivate *priv;
2521 gdouble x_root, y_root;
2524 #define STILL_THRESHOLD 40
2526 if (!gdk_event_get_root_coords (event, &x_root, &y_root))
2529 priv = scrolled_window->priv;
2530 _time = gdk_event_get_time (event);
2532 if (priv->last_motion_event_x_root != x_root ||
2533 priv->last_motion_event_y_root != y_root ||
2534 ABS (_time - priv->last_motion_event_time) > STILL_THRESHOLD)
2536 priv->x_velocity = (priv->last_motion_event_x_root - x_root) /
2537 (gdouble) (_time - priv->last_motion_event_time);
2538 priv->y_velocity = (priv->last_motion_event_y_root - y_root) /
2539 (gdouble) (_time - priv->last_motion_event_time);
2542 priv->last_motion_event_x_root = x_root;
2543 priv->last_motion_event_y_root = y_root;
2544 priv->last_motion_event_time = _time;
2546 #undef STILL_THRESHOLD
2552 gtk_scrolled_window_captured_button_release (GtkWidget *widget,
2555 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2556 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2560 gdouble x_root, y_root;
2562 if (gdk_event_get_button (event, &button) && button != 1)
2565 child = gtk_bin_get_child (GTK_BIN (widget));
2569 gtk_device_grab_remove (widget, priv->drag_device);
2570 priv->drag_device = NULL;
2572 if (priv->release_timeout_id)
2574 g_source_remove (priv->release_timeout_id);
2575 priv->release_timeout_id = 0;
2578 overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL);
2581 gdk_device_ungrab (gdk_event_get_device (event), gdk_event_get_time (event));
2584 /* There hasn't been scrolling at all, so just let the
2585 * child widget handle the button press normally
2587 gtk_scrolled_window_release_captured_event (scrolled_window);
2592 priv->in_drag = FALSE;
2594 if (priv->button_press_event)
2596 gdk_event_free (priv->button_press_event);
2597 priv->button_press_event = NULL;
2600 gtk_scrolled_window_calculate_velocity (scrolled_window, event);
2602 /* Zero out vector components without a visible scrollbar */
2603 if (!priv->hscrollbar_visible)
2604 priv->x_velocity = 0;
2605 if (!priv->vscrollbar_visible)
2606 priv->y_velocity = 0;
2608 if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot)
2610 gtk_scrolled_window_start_deceleration (scrolled_window);
2611 priv->x_velocity = priv->y_velocity = 0;
2612 priv->last_button_event_valid = FALSE;
2616 gdk_event_get_root_coords (event, &x_root, &y_root);
2617 priv->last_button_event_x_root = x_root;
2618 priv->last_button_event_y_root = y_root;
2619 priv->last_button_event_valid = TRUE;
2622 if (priv->capture_button_press)
2629 gtk_scrolled_window_captured_motion_notify (GtkWidget *widget,
2632 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2633 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2634 gint old_overshoot_x, old_overshoot_y;
2635 gint new_overshoot_x, new_overshoot_y;
2637 GtkAdjustment *hadjustment;
2638 GtkAdjustment *vadjustment;
2640 GdkModifierType state;
2641 gdouble x_root, y_root;
2643 gdk_event_get_state (event, &state);
2644 if (!(state & GDK_BUTTON1_MASK))
2647 child = gtk_bin_get_child (GTK_BIN (widget));
2651 /* Check if we've passed the drag threshold */
2652 gdk_event_get_root_coords (event, &x_root, &y_root);
2655 if (gtk_drag_check_threshold (widget,
2656 priv->last_button_event_x_root,
2657 priv->last_button_event_y_root,
2660 if (priv->release_timeout_id)
2662 g_source_remove (priv->release_timeout_id);
2663 priv->release_timeout_id = 0;
2666 priv->last_button_event_valid = FALSE;
2667 priv->in_drag = TRUE;
2673 gdk_device_grab (priv->drag_device,
2674 gtk_widget_get_window (widget),
2675 GDK_OWNERSHIP_WINDOW,
2677 GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK,
2679 gdk_event_get_time (event));
2681 priv->last_button_event_valid = FALSE;
2683 if (priv->button_press_event)
2685 gdk_event_free (priv->button_press_event);
2686 priv->button_press_event = NULL;
2689 _gtk_scrolled_window_get_overshoot (scrolled_window,
2690 &old_overshoot_x, &old_overshoot_y);
2692 hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2693 if (hadjustment && priv->hscrollbar_visible)
2695 dx = (priv->last_motion_event_x_root - x_root) + priv->unclamped_hadj_value;
2696 _gtk_scrolled_window_set_adjustment_value (scrolled_window, hadjustment,
2700 vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2701 if (vadjustment && priv->vscrollbar_visible)
2703 dy = (priv->last_motion_event_y_root - y_root) + priv->unclamped_vadj_value;
2704 _gtk_scrolled_window_set_adjustment_value (scrolled_window, vadjustment,
2708 _gtk_scrolled_window_get_overshoot (scrolled_window,
2709 &new_overshoot_x, &new_overshoot_y);
2711 if (old_overshoot_x != new_overshoot_x ||
2712 old_overshoot_y != new_overshoot_y)
2714 if (new_overshoot_x >= 0 || new_overshoot_y >= 0)
2716 /* We need to reallocate the widget to have it at
2717 * negative offset, so there's a "gravity" on the
2718 * bottom/right corner
2720 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2722 else if (new_overshoot_x < 0 || new_overshoot_y < 0)
2723 _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
2726 gtk_scrolled_window_calculate_velocity (scrolled_window, event);
2732 gtk_scrolled_window_captured_button_press (GtkWidget *widget,
2735 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2736 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2738 GtkWidget *event_widget;
2739 GdkDevice *source_device;
2740 GdkInputSource source;
2741 gdouble x_root, y_root;
2744 /* If scrollbars are not visible, we don't do kinetic scrolling */
2745 if (!priv->vscrollbar_visible && !priv->hscrollbar_visible)
2748 source_device = gdk_event_get_source_device (event);
2749 source = gdk_device_get_source (source_device);
2751 if (source != GDK_SOURCE_TOUCHSCREEN)
2754 event_widget = gtk_get_event_widget (event);
2756 /* If there's another scrolled window between the widget
2757 * receiving the event and this capturing scrolled window,
2758 * let it handle the events.
2760 if (widget != gtk_widget_get_ancestor (event_widget, GTK_TYPE_SCROLLED_WINDOW))
2763 /* Check whether the button press is close to the previous one,
2764 * take that as a shortcut to get the child widget handle events
2766 gdk_event_get_root_coords (event, &x_root, &y_root);
2767 if (priv->last_button_event_valid &&
2768 ABS (x_root - priv->last_button_event_x_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD &&
2769 ABS (y_root - priv->last_button_event_y_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD)
2771 priv->last_button_event_valid = FALSE;
2775 priv->last_button_event_x_root = priv->last_motion_event_x_root = x_root;
2776 priv->last_button_event_y_root = priv->last_motion_event_y_root = y_root;
2777 priv->last_motion_event_time = gdk_event_get_time (event);
2778 priv->last_button_event_valid = TRUE;
2780 if (gdk_event_get_button (event, &button) && button != 1)
2783 child = gtk_bin_get_child (GTK_BIN (widget));
2787 if (priv->hscrollbar == event_widget || priv->vscrollbar == event_widget)
2790 priv->drag_device = gdk_event_get_device (event);
2791 gtk_device_grab_add (widget, priv->drag_device, TRUE);
2793 gtk_scrolled_window_cancel_deceleration (scrolled_window);
2795 /* Only set the timeout if we're going to store an event */
2796 if (priv->capture_button_press)
2797 priv->release_timeout_id =
2798 gdk_threads_add_timeout (RELEASE_EVENT_TIMEOUT,
2799 (GSourceFunc) gtk_scrolled_window_release_captured_event,
2802 priv->in_drag = FALSE;
2804 if (priv->capture_button_press)
2806 /* Store the button press event in
2807 * case we need to propagate it later
2809 priv->button_press_event = gdk_event_copy (event);
2817 gtk_scrolled_window_captured_event (GtkWidget *widget,
2820 gboolean retval = FALSE;
2821 GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW (widget)->priv;
2823 switch (event->type)
2825 case GDK_TOUCH_BEGIN:
2826 case GDK_BUTTON_PRESS:
2827 retval = gtk_scrolled_window_captured_button_press (widget, event);
2830 case GDK_BUTTON_RELEASE:
2831 if (priv->drag_device)
2832 retval = gtk_scrolled_window_captured_button_release (widget, event);
2834 priv->last_button_event_valid = FALSE;
2836 case GDK_TOUCH_UPDATE:
2837 case GDK_MOTION_NOTIFY:
2838 if (priv->drag_device)
2839 retval = gtk_scrolled_window_captured_motion_notify (widget, event);
2841 case GDK_LEAVE_NOTIFY:
2842 case GDK_ENTER_NOTIFY:
2843 if (priv->in_drag &&
2844 event->crossing.mode != GDK_CROSSING_GRAB)
2855 gtk_scrolled_window_focus (GtkWidget *widget,
2856 GtkDirectionType direction)
2858 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2859 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2861 gboolean had_focus_child;
2863 had_focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget)) != NULL;
2865 if (priv->focus_out)
2867 priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
2871 if (gtk_widget_is_focus (widget))
2874 /* We only put the scrolled window itself in the focus chain if it
2875 * isn't possible to focus any children.
2877 child = gtk_bin_get_child (GTK_BIN (widget));
2880 if (gtk_widget_child_focus (child, direction))
2884 if (!had_focus_child && gtk_widget_get_can_focus (widget))
2886 gtk_widget_grab_focus (widget);
2894 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
2897 GtkScrolledWindowPrivate *priv;
2898 GtkScrolledWindow *scrolled_window;
2900 g_return_if_fail (adjustment != NULL);
2901 g_return_if_fail (data != NULL);
2903 scrolled_window = GTK_SCROLLED_WINDOW (data);
2904 priv = scrolled_window->priv;
2906 if (priv->hscrollbar &&
2907 adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
2909 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2913 visible = priv->hscrollbar_visible;
2914 priv->hscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
2915 gtk_adjustment_get_page_size (adjustment));
2917 if (priv->hscrollbar_visible != visible)
2918 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2921 else if (priv->vscrollbar &&
2922 adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
2924 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
2928 visible = priv->vscrollbar_visible;
2929 priv->vscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
2930 gtk_adjustment_get_page_size (adjustment));
2932 if (priv->vscrollbar_visible != visible)
2933 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2939 gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
2942 GtkScrolledWindow *scrolled_window = user_data;
2943 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2945 /* Allow overshooting for kinetic scrolling operations */
2946 if (priv->drag_device || priv->deceleration_id)
2949 /* Ensure GtkAdjustment and unclamped values are in sync */
2950 if (priv->vscrollbar &&
2951 adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
2952 priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
2953 else if (priv->hscrollbar &&
2954 adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
2955 priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
2959 gtk_scrolled_window_add (GtkContainer *container,
2962 GtkScrolledWindowPrivate *priv;
2963 GtkScrolledWindow *scrolled_window;
2965 GtkWidget *child_widget;
2966 GtkAdjustment *hadj, *vadj;
2968 bin = GTK_BIN (container);
2969 child_widget = gtk_bin_get_child (bin);
2970 g_return_if_fail (child_widget == NULL);
2972 scrolled_window = GTK_SCROLLED_WINDOW (container);
2973 priv = scrolled_window->priv;
2975 if (gtk_widget_get_realized (GTK_WIDGET (bin)))
2976 gtk_widget_set_parent_window (child, priv->overshoot_window);
2978 _gtk_bin_set_child (bin, child);
2979 gtk_widget_set_parent (child, GTK_WIDGET (bin));
2981 hadj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2982 vadj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2984 if (GTK_IS_SCROLLABLE (child))
2985 g_object_set (child, "hadjustment", hadj, "vadjustment", vadj, NULL);
2987 g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
2988 "use gtk_scrolled_window_add_with_viewport() instead");
2992 gtk_scrolled_window_remove (GtkContainer *container,
2995 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
2996 g_return_if_fail (child != NULL);
2997 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2999 g_object_set (child, "hadjustment", NULL, "vadjustment", NULL, NULL);
3001 /* chain parent class handler to remove child */
3002 GTK_CONTAINER_CLASS (gtk_scrolled_window_parent_class)->remove (container, child);
3006 * gtk_scrolled_window_add_with_viewport:
3007 * @scrolled_window: a #GtkScrolledWindow
3008 * @child: the widget you want to scroll
3010 * Used to add children without native scrolling capabilities. This
3011 * is simply a convenience function; it is equivalent to adding the
3012 * unscrollable child to a viewport, then adding the viewport to the
3013 * scrolled window. If a child has native scrolling, use
3014 * gtk_container_add() instead of this function.
3016 * The viewport scrolls the child by moving its #GdkWindow, and takes
3017 * the size of the child to be the size of its toplevel #GdkWindow.
3018 * This will be very wrong for most widgets that support native scrolling;
3019 * for example, if you add a widget such as #GtkTreeView with a viewport,
3020 * the whole widget will scroll, including the column headings. Thus,
3021 * widgets with native scrolling support should not be used with the
3022 * #GtkViewport proxy.
3024 * A widget supports scrolling natively if it implements the
3025 * #GtkScrollable interface.
3028 gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
3032 GtkWidget *viewport;
3033 GtkWidget *child_widget;
3035 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3036 g_return_if_fail (GTK_IS_WIDGET (child));
3037 g_return_if_fail (gtk_widget_get_parent (child) == NULL);
3039 bin = GTK_BIN (scrolled_window);
3040 child_widget = gtk_bin_get_child (bin);
3044 g_return_if_fail (GTK_IS_VIEWPORT (child_widget));
3045 g_return_if_fail (gtk_bin_get_child (GTK_BIN (child_widget)) == NULL);
3047 viewport = child_widget;
3052 gtk_viewport_new (gtk_scrolled_window_get_hadjustment (scrolled_window),
3053 gtk_scrolled_window_get_vadjustment (scrolled_window));
3054 gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
3057 gtk_widget_show (viewport);
3058 gtk_container_add (GTK_CONTAINER (viewport), child);
3062 * _gtk_scrolled_window_get_spacing:
3063 * @scrolled_window: a scrolled window
3065 * Gets the spacing between the scrolled window's scrollbars and
3066 * the scrolled widget. Used by GtkCombo
3068 * Return value: the spacing, in pixels.
3071 _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
3073 GtkScrolledWindowClass *class;
3075 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
3077 class = GTK_SCROLLED_WINDOW_GET_CLASS (scrolled_window);
3079 if (class->scrollbar_spacing >= 0)
3080 return class->scrollbar_spacing;
3083 gint scrollbar_spacing;
3085 gtk_widget_style_get (GTK_WIDGET (scrolled_window),
3086 "scrollbar-spacing", &scrollbar_spacing,
3089 return scrollbar_spacing;
3095 gtk_scrolled_window_get_preferred_size (GtkWidget *widget,
3096 GtkOrientation orientation,
3100 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3101 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3102 GtkBin *bin = GTK_BIN (scrolled_window);
3105 gint scrollbar_spacing;
3106 GtkRequisition hscrollbar_requisition;
3107 GtkRequisition vscrollbar_requisition;
3108 GtkRequisition minimum_req, natural_req;
3110 gint min_child_size, nat_child_size;
3112 scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
3116 minimum_req.width = 0;
3117 minimum_req.height = 0;
3118 natural_req.width = 0;
3119 natural_req.height = 0;
3121 gtk_widget_get_preferred_size (priv->hscrollbar,
3122 &hscrollbar_requisition, NULL);
3123 gtk_widget_get_preferred_size (priv->vscrollbar,
3124 &vscrollbar_requisition, NULL);
3126 child = gtk_bin_get_child (bin);
3127 if (child && gtk_widget_get_visible (child))
3129 if (orientation == GTK_ORIENTATION_HORIZONTAL)
3131 gtk_widget_get_preferred_width (child,
3135 if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
3137 minimum_req.width += min_child_size;
3138 natural_req.width += nat_child_size;
3142 gint min_content_width = priv->min_content_width;
3144 if (min_content_width >= 0)
3146 minimum_req.width = MAX (minimum_req.width, min_content_width);
3147 natural_req.width = MAX (natural_req.width, min_content_width);
3152 minimum_req.width += vscrollbar_requisition.width;
3153 natural_req.width += vscrollbar_requisition.width;
3157 else /* GTK_ORIENTATION_VERTICAL */
3159 gtk_widget_get_preferred_height (child,
3163 if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
3165 minimum_req.height += min_child_size;
3166 natural_req.height += nat_child_size;
3170 gint min_content_height = priv->min_content_height;
3172 if (min_content_height >= 0)
3174 minimum_req.height = MAX (minimum_req.height, min_content_height);
3175 natural_req.height = MAX (natural_req.height, min_content_height);
3180 minimum_req.height += vscrollbar_requisition.height;
3181 natural_req.height += vscrollbar_requisition.height;
3187 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
3188 priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
3190 minimum_req.width = MAX (minimum_req.width, hscrollbar_requisition.width);
3191 natural_req.width = MAX (natural_req.width, hscrollbar_requisition.width);
3192 if (!extra_height || priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
3193 extra_height = scrollbar_spacing + hscrollbar_requisition.height;
3196 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
3197 priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
3199 minimum_req.height = MAX (minimum_req.height, vscrollbar_requisition.height);
3200 natural_req.height = MAX (natural_req.height, vscrollbar_requisition.height);
3201 if (!extra_width || priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
3202 extra_width = scrollbar_spacing + vscrollbar_requisition.width;
3205 minimum_req.width += MAX (0, extra_width);
3206 minimum_req.height += MAX (0, extra_height);
3207 natural_req.width += MAX (0, extra_width);
3208 natural_req.height += MAX (0, extra_height);
3210 if (priv->shadow_type != GTK_SHADOW_NONE)
3212 GtkStyleContext *context;
3213 GtkStateFlags state;
3214 GtkBorder padding, border;
3216 context = gtk_widget_get_style_context (GTK_WIDGET (widget));
3217 state = gtk_widget_get_state_flags (GTK_WIDGET (widget));
3219 gtk_style_context_save (context);
3220 gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
3222 gtk_style_context_get_padding (context, state, &padding);
3223 gtk_style_context_get_border (context, state, &border);
3225 minimum_req.width += padding.left + padding.right + border.left + border.right;
3226 minimum_req.height += padding.top + padding.bottom + border.top + border.bottom;
3227 natural_req.width += padding.left + padding.right + border.left + border.right;
3228 natural_req.height += padding.top + padding.bottom + border.top + border.bottom;
3230 gtk_style_context_restore (context);
3233 if (orientation == GTK_ORIENTATION_HORIZONTAL)
3236 *minimum_size = minimum_req.width;
3238 *natural_size = natural_req.width;
3243 *minimum_size = minimum_req.height;
3245 *natural_size = natural_req.height;
3250 gtk_scrolled_window_get_preferred_width (GtkWidget *widget,
3254 gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
3258 gtk_scrolled_window_get_preferred_height (GtkWidget *widget,
3262 gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
3266 gtk_scrolled_window_get_preferred_height_for_width (GtkWidget *widget,
3268 gint *minimum_height,
3269 gint *natural_height)
3271 g_return_if_fail (GTK_IS_WIDGET (widget));
3273 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
3277 gtk_scrolled_window_get_preferred_width_for_height (GtkWidget *widget,
3279 gint *minimum_width,
3280 gint *natural_width)
3282 g_return_if_fail (GTK_IS_WIDGET (widget));
3284 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
3288 gtk_scrolled_window_realize (GtkWidget *widget)
3290 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3291 GtkAllocation allocation, relative_allocation;
3292 GdkWindowAttr attributes;
3293 GtkWidget *child_widget;
3294 gint attributes_mask;
3296 gtk_widget_set_realized (widget, TRUE);
3297 gtk_widget_get_allocation (widget, &allocation);
3298 gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
3300 attributes.window_type = GDK_WINDOW_CHILD;
3301 attributes.x = allocation.x + relative_allocation.x;
3302 attributes.y = allocation.y + relative_allocation.y;
3303 attributes.width = allocation.width;
3304 attributes.height = allocation.height;
3305 attributes.wclass = GDK_INPUT_OUTPUT;
3306 attributes.visual = gtk_widget_get_visual (widget);
3307 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK |
3308 GDK_BUTTON_MOTION_MASK | GDK_TOUCH_MASK;
3310 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3312 scrolled_window->priv->overshoot_window =
3313 gdk_window_new (gtk_widget_get_parent_window (widget),
3314 &attributes, attributes_mask);
3316 gdk_window_set_user_data (scrolled_window->priv->overshoot_window, widget);
3318 child_widget = gtk_bin_get_child (GTK_BIN (widget));
3321 gtk_widget_set_parent_window (child_widget,
3322 scrolled_window->priv->overshoot_window);
3324 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->realize (widget);
3328 gtk_scrolled_window_unrealize (GtkWidget *widget)
3330 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3332 gdk_window_set_user_data (scrolled_window->priv->overshoot_window, NULL);
3333 gdk_window_destroy (scrolled_window->priv->overshoot_window);
3334 scrolled_window->priv->overshoot_window = NULL;
3336 gtk_widget_set_realized (widget, FALSE);
3338 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unrealize (widget);
3342 gtk_scrolled_window_map (GtkWidget *widget)
3344 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3346 gdk_window_show (scrolled_window->priv->overshoot_window);
3348 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->map (widget);
3352 gtk_scrolled_window_unmap (GtkWidget *widget)
3354 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3356 gdk_window_hide (scrolled_window->priv->overshoot_window);
3358 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unmap (widget);
3362 gtk_scrolled_window_grab_notify (GtkWidget *widget,
3363 gboolean was_grabbed)
3365 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3366 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3368 if (priv->drag_device &&
3369 gtk_widget_device_is_shadowed (widget,
3372 gdk_device_ungrab (priv->drag_device,
3373 gtk_get_current_event_time ());
3374 priv->drag_device = NULL;
3375 priv->in_drag = FALSE;
3377 if (priv->release_timeout_id)
3379 g_source_remove (priv->release_timeout_id);
3380 priv->release_timeout_id = 0;
3383 if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
3384 gtk_scrolled_window_start_deceleration (scrolled_window);
3386 gtk_scrolled_window_cancel_deceleration (scrolled_window);
3388 priv->last_button_event_valid = FALSE;
3393 * gtk_scrolled_window_get_min_content_width:
3394 * @scrolled_window: a #GtkScrolledWindow
3396 * Gets the minimum content width of @scrolled_window, or -1 if not set.
3398 * Returns: the minimum content width
3403 gtk_scrolled_window_get_min_content_width (GtkScrolledWindow *scrolled_window)
3405 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
3407 return scrolled_window->priv->min_content_width;
3411 * gtk_scrolled_window_set_min_content_width:
3412 * @scrolled_window: a #GtkScrolledWindow
3413 * @width: the minimal content width
3415 * Sets the minimum width that @scrolled_window should keep visible.
3416 * Note that this can and (usually will) be smaller than the minimum
3417 * size of the content.
3422 gtk_scrolled_window_set_min_content_width (GtkScrolledWindow *scrolled_window,
3425 GtkScrolledWindowPrivate *priv;
3427 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3429 priv = scrolled_window->priv;
3431 if (priv->min_content_width != width)
3433 priv->min_content_width = width;
3435 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3437 g_object_notify (G_OBJECT (scrolled_window), "min-content-width");
3442 * gtk_scrolled_window_get_min_content_height:
3443 * @scrolled_window: a #GtkScrolledWindow
3445 * Gets the minimal content height of @scrolled_window, or -1 if not set.
3447 * Returns: the minimal content height
3452 gtk_scrolled_window_get_min_content_height (GtkScrolledWindow *scrolled_window)
3454 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
3456 return scrolled_window->priv->min_content_height;
3460 * gtk_scrolled_window_set_min_content_height:
3461 * @scrolled_window: a #GtkScrolledWindow
3462 * @height: the minimal content height
3464 * Sets the minimum height that @scrolled_window should keep visible.
3465 * Note that this can and (usually will) be smaller than the minimum
3466 * size of the content.
3471 gtk_scrolled_window_set_min_content_height (GtkScrolledWindow *scrolled_window,
3474 GtkScrolledWindowPrivate *priv;
3476 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3478 priv = scrolled_window->priv;
3480 if (priv->min_content_height != height)
3482 priv->min_content_height = height;
3484 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3486 g_object_notify (G_OBJECT (scrolled_window), "min-content-height");