--- /dev/null
+#include "gtkscrollingwindow.h"
+
+/**
+ * SECTION:gtkscrollingwindow
+ * @Short_description: Adds scrollbars to its child widget
+ * @Title: GtkScrollingWindow
+ * @See_also: #GtkScrollable, #GtkViewport, #GtkAdjustment
+ *
+ * #GtkScrollingWindow is a #GtkBin subclass: it's a container that
+ * accepts a single child widget (a GtkScrollable) and adds an
+ * optional beveled frame and scrollbars.
+ *
+ * The position of the scrollbars is controlled by the scroll
+ * adjustments. See #GtkAdjustment for the fields in an adjustment.
+ *
+ * #GtkScrollingWindow is a #GtkScrolledWindow subclass that overrides
+ * the geometry protocol to work as follows:
+ *
+ * A scrolled window (GtkScrolledWindow) is about squeezing an overly
+ * large widget into a small space and allowing the user to scroll it.
+ * It ignores the natural size of the problem widget, expecting a
+ * gtk_widget_set_size_request to override (squeeze) it.
+ *
+ * A scrolling window (GtkScrollingWindow) is about sticking
+ * scrollbars on a viewport -- a GtkScrollable. Its natural size is
+ * the natural size of the viewport, scrollbars and frame (with
+ * spacing). Using gtk_widget_set_size_request interferes with the
+ * natural sizing (esp. shrinking of expandable viewports).
+ */
+
+struct _GtkScrolledWindowPrivate
+{
+ GtkWidget *hscrollbar;
+ GtkWidget *vscrollbar;
+
+ GtkCornerType real_window_placement;
+ guint16 shadow_type;
+
+ guint window_placement_set : 1;
+ guint hscrollbar_policy : 2;
+ guint vscrollbar_policy : 2;
+ guint hscrollbar_visible : 1;
+ guint vscrollbar_visible : 1;
+ guint window_placement : 2;
+ guint focus_out : 1; /* Flag used by ::move-focus-out implementation */
+
+ gint min_content_width;
+ gint min_content_height;
+
+ /* Kinetic scrolling */
+ GdkEvent *button_press_event;
+ GdkWindow *overshoot_window;
+ GdkDevice *drag_device;
+ guint kinetic_scrolling : 1;
+ guint capture_button_press : 1;
+ guint in_drag : 1;
+ guint last_button_event_valid : 1;
+
+ guint release_timeout_id;
+ guint deceleration_id;
+
+ gdouble last_button_event_x_root;
+ gdouble last_button_event_y_root;
+
+ gdouble last_motion_event_x_root;
+ gdouble last_motion_event_y_root;
+ guint32 last_motion_event_time;
+
+ gdouble x_velocity;
+ gdouble y_velocity;
+
+ gdouble unclamped_hadj_value;
+ gdouble unclamped_vadj_value;
+};
+
+static void
+gtk_scrolled_window_update_real_placement (GtkScrolledWindow *scrolled_window)
+{
+ GtkScrolledWindowPrivate *priv = scrolled_window->priv;
+ GtkSettings *settings;
+
+ settings = gtk_widget_get_settings (GTK_WIDGET (scrolled_window));
+
+ if (priv->window_placement_set || settings == NULL)
+ priv->real_window_placement = priv->window_placement;
+ else
+ g_object_get (settings,
+ "gtk-scrolled-window-placement",
+ &priv->real_window_placement,
+ NULL);
+}
+
+static void
+gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+{
+ GtkScrolledWindowPrivate *priv;
+
+ scrolled_window->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (scrolled_window,
+ GTK_TYPE_SCROLLED_WINDOW,
+ GtkScrolledWindowPrivate);
+
+ gtk_widget_set_has_window (GTK_WIDGET (scrolled_window), FALSE);
+ gtk_widget_set_can_focus (GTK_WIDGET (scrolled_window), TRUE);
+
+ priv->hscrollbar = NULL;
+ priv->vscrollbar = NULL;
+ priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC;
+ priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC;
+ priv->hscrollbar_visible = FALSE;
+ priv->vscrollbar_visible = FALSE;
+ priv->focus_out = FALSE;
+ priv->window_placement = GTK_CORNER_TOP_LEFT;
+ gtk_scrolled_window_update_real_placement (scrolled_window);
+ priv->min_content_width = -1;
+ priv->min_content_height = -1;
+
+ gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
+ gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
+}
+
+static void gtk_scrolling_window_get_preferred_width (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size);
+static void gtk_scrolling_window_get_preferred_height (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size);
+static void gtk_scrolling_window_get_preferred_height_for_width (GtkWidget *layout,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height);
+static void gtk_scrolling_window_get_preferred_width_for_height (GtkWidget *layout,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height);
+
+G_DEFINE_TYPE (GtkScrollingWindow, gtk_scrolling_window,
+ GTK_TYPE_SCROLLED_WINDOW)
+
+static void
+gtk_scrolling_window_class_init (GtkScrollingWindowClass *class)
+{
+ GtkWidgetClass *widget_class = (GtkWidgetClass*) class;
+
+ widget_class->get_preferred_width = gtk_scrolling_window_get_preferred_width;
+ widget_class->get_preferred_height = gtk_scrolling_window_get_preferred_height;
+ widget_class->get_preferred_height_for_width = gtk_scrolling_window_get_preferred_height_for_width;
+ widget_class->get_preferred_width_for_height = gtk_scrolling_window_get_preferred_width_for_height;
+}
+
+static void
+gtk_scrolling_window_init (GtkScrollingWindow *scrolling_window)
+{
+ gtk_scrolled_window_init (GTK_SCROLLED_WINDOW (scrolling_window));
+}
+
+/**
+ * gtk_scrolling_window_new:
+ * @hadjustment: (allow-none): horizontal adjustment
+ * @vadjustment: (allow-none): vertical adjustment
+ *
+ * Creates a new scrolling window.
+ *
+ * The two arguments are the scrolling window's adjustments; these will be
+ * shared with the scrollbars and the child widget to keep the bars in sync
+ * with the child. Usually you want to pass %NULL for the adjustments, which
+ * will cause the scrolling window to create them for you.
+ *
+ * Returns: a new scrolling window
+ */
+GtkWidget*
+gtk_scrolling_window_new (GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment)
+{
+ GtkWidget *scrolling_window;
+
+ if (hadjustment)
+ g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
+
+ if (vadjustment)
+ g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
+
+ scrolling_window = g_object_new (GTK_TYPE_SCROLLING_WINDOW,
+ "hadjustment", hadjustment,
+ "vadjustment", vadjustment,
+ NULL);
+
+ return scrolling_window;
+}
+
+extern gboolean ubuntu_overlay_scrollbar_get_enabled (void);
+
+static gint
+gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
+{
+ GtkScrolledWindowClass *class;
+
+ g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
+
+ if (ubuntu_overlay_scrollbar_get_enabled ())
+ return 0;
+
+ class = GTK_SCROLLED_WINDOW_GET_CLASS (scrolled_window);
+
+ if (class->scrollbar_spacing >= 0)
+ return class->scrollbar_spacing;
+ else
+ {
+ gint scrollbar_spacing;
+
+ gtk_widget_style_get (GTK_WIDGET (scrolled_window),
+ "scrollbar-spacing", &scrollbar_spacing,
+ NULL);
+
+ return scrollbar_spacing;
+ }
+}
+
+static void
+gtk_scrolling_window_get_preferred_size (GtkWidget *widget,
+ GtkOrientation orientation,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ GtkScrolledWindowPrivate *priv = scrolled_window->priv;
+ GtkBin *bin = GTK_BIN (scrolled_window);
+ gint minimum;
+ gint natural;
+ GtkWidget *child;
+
+ /* Init to child size. */
+ child = gtk_bin_get_child (bin);
+ if (child && gtk_widget_get_visible (child))
+ {
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_widget_get_preferred_width (child, &minimum, &natural);
+ else
+ gtk_widget_get_preferred_height (child, &minimum, &natural);
+ }
+
+ /* Add min_content_width/height. */
+ {
+ gint min_content_size = (orientation == GTK_ORIENTATION_HORIZONTAL
+ ? priv->min_content_width
+ : priv->min_content_height);
+ if (min_content_size >= 0)
+ {
+ minimum = MAX (minimum, min_content_size);
+ natural = MAX (natural, min_content_size);
+ }
+ }
+
+ /* Add scrollbar size. */
+ if (priv->vscrollbar_policy != GTK_POLICY_NEVER)
+ {
+ gint min, nat;
+ gint space = gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+ if (orientation == GTK_ORIENTATION_VERTICAL)
+ gtk_widget_get_preferred_height (priv->hscrollbar, &min, &nat);
+ else
+ gtk_widget_get_preferred_width (priv->vscrollbar, &min, &nat);
+ minimum += space + min;
+ natural += space + nat;
+ }
+
+ /* Add shadow size. */
+ if (priv->shadow_type != GTK_SHADOW_NONE)
+ {
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder padding, border;
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (widget));
+ state = gtk_widget_get_state_flags (GTK_WIDGET (widget));
+
+ gtk_style_context_save (context);
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
+ gtk_style_context_get_padding (context, state, &padding);
+ gtk_style_context_get_border (context, state, &border);
+ gtk_style_context_restore (context);
+
+ if (orientation == GTK_ORIENTATION_VERTICAL)
+ {
+ minimum += padding.top + padding.bottom + border.top + border.bottom;
+ natural += padding.top + padding.bottom + border.top + border.bottom;
+ }
+ else
+ {
+ minimum += padding.left + padding.right + border.left + border.right;
+ natural += padding.left + padding.right + border.left + border.right;
+ }
+ }
+
+ if (minimum_size)
+ *minimum_size = minimum;
+ if (natural_size)
+ *natural_size = natural;
+}
+
+static void
+gtk_scrolling_window_get_preferred_width (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ gtk_scrolling_window_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
+}
+
+static void
+gtk_scrolling_window_get_preferred_height (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ gtk_scrolling_window_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
+}
+
+static void
+gtk_scrolling_window_get_preferred_height_for_width (GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
+}
+
+static void
+gtk_scrolling_window_get_preferred_width_for_height (GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
+}