[gnome-shell] Import MxScrollView and dependencies
- From: Owen Taylor <otaylor src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnome-shell] Import MxScrollView and dependencies
- Date: Thu, 1 Oct 2009 18:53:07 +0000 (UTC)
commit f6b80d5ed4dacaddc688a64f2d60dcfd61e05e98
Author: Owen W. Taylor <otaylor fishsoup net>
Date: Wed Sep 9 23:13:35 2009 -0400
Import MxScrollView and dependencies
https://bugzilla.gnome.org/show_bug.cgi?id=591245
src/Makefile-st.am | 10 +
src/st/st-adjustment.c | 776 +++++++++++++++++++++++++++++++++
src/st/st-adjustment.h | 121 ++++++
src/st/st-button.c | 759 ++++++++++++++++++++++++++++++++
src/st/st-button.h | 93 ++++
src/st/st-scroll-bar.c | 1105 +++++++++++++++++++++++++++++++++++++++++++++++
src/st/st-scroll-bar.h | 82 ++++
src/st/st-scroll-view.c | 850 ++++++++++++++++++++++++++++++++++++
src/st/st-scroll-view.h | 89 ++++
src/st/st-scrollable.c | 88 ++++
src/st/st-scrollable.h | 69 +++
11 files changed, 4042 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile-st.am b/src/Makefile-st.am
index 24bc10c..c069c71 100644
--- a/src/Makefile-st.am
+++ b/src/Makefile-st.am
@@ -65,10 +65,15 @@ st-enum-types.c: stamp-st-enum-types.h st/st-enum-types.c.in
# please, keep this sorted alphabetically
st_source_h = \
+ st/st-adjustment.h \
st/st-bin.h \
+ st/st-button.h \
st/st-private.h \
st/st-stylable.h \
st/st-style.h \
+ st/st-scrollable.h \
+ st/st-scroll-bar.h \
+ st/st-scroll-view.h \
st/st-subtexture.h \
st/st-texture-cache.h \
st/st-texture-frame.h \
@@ -79,8 +84,13 @@ st_source_h = \
# please, keep this sorted alphabetically
st_source_c = \
+ st/st-adjustment.c \
st/st-bin.c \
+ st/st-button.c \
st/st-private.c \
+ st/st-scrollable.c \
+ st/st-scroll-bar.c \
+ st/st-scroll-view.c \
st/st-stylable.c \
st/st-style.c \
st/st-subtexture.c \
diff --git a/src/st/st-adjustment.c b/src/st/st-adjustment.c
new file mode 100644
index 0000000..a2c2af1
--- /dev/null
+++ b/src/st/st-adjustment.c
@@ -0,0 +1,776 @@
+/*
+ * st-adjustment.c: Adjustment object
+ *
+ * Copyright (C) 2008 OpenedHand
+ * Copyright (c) 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>, inspired by GtkAdjustment
+ * Port to St by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+/**
+ * SECTION:st-adjustment
+ * @short_description: A GObject representing an adjustable bounded value
+ *
+ * The #StAdjustment object represents a range of values bounded between a
+ * minimum and maximum, together with step and page increments and a page size.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+#include "st-adjustment.h"
+#include "st-marshal.h"
+#include "st-private.h"
+
+G_DEFINE_TYPE (StAdjustment, st_adjustment, G_TYPE_OBJECT)
+
+#define ADJUSTMENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_ADJUSTMENT, StAdjustmentPrivate))
+
+struct _StAdjustmentPrivate
+{
+ /* Do not sanity-check values while constructing,
+ * not all properties may be set yet. */
+ gboolean is_constructing : 1;
+
+ gdouble lower;
+ gdouble upper;
+ gdouble value;
+ gdouble step_increment;
+ gdouble page_increment;
+ gdouble page_size;
+
+ /* For interpolation */
+ ClutterTimeline *interpolation;
+ gdouble old_position;
+ gdouble new_position;
+
+ /* For elasticity */
+ gboolean elastic;
+ guint bounce_source;
+ ClutterAlpha *bounce_alpha;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_LOWER,
+ PROP_UPPER,
+ PROP_VALUE,
+ PROP_STEP_INC,
+ PROP_PAGE_INC,
+ PROP_PAGE_SIZE,
+
+ PROP_ELASTIC,
+};
+
+enum
+{
+ CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static gboolean st_adjustment_set_lower (StAdjustment *adjustment,
+ gdouble lower);
+static gboolean st_adjustment_set_upper (StAdjustment *adjustment,
+ gdouble upper);
+static gboolean st_adjustment_set_step_increment (StAdjustment *adjustment,
+ gdouble step);
+static gboolean st_adjustment_set_page_increment (StAdjustment *adjustment,
+ gdouble page);
+static gboolean st_adjustment_set_page_size (StAdjustment *adjustment,
+ gdouble size);
+
+static void
+st_adjustment_constructed (GObject *object)
+{
+ GObjectClass *g_class;
+ StAdjustment *self = ST_ADJUSTMENT (object);
+
+ g_class = G_OBJECT_CLASS (st_adjustment_parent_class);
+ /* The docs say we're suppose to chain up, but would crash without
+ * some extra care. */
+ if (g_class && g_class->constructed &&
+ g_class->constructed != st_adjustment_constructed)
+ {
+ g_class->constructed (object);
+ }
+
+ ST_ADJUSTMENT (self)->priv->is_constructing = FALSE;
+ st_adjustment_clamp_page (self, self->priv->lower, self->priv->upper);
+}
+
+static void
+st_adjustment_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StAdjustmentPrivate *priv = ST_ADJUSTMENT (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_LOWER:
+ g_value_set_double (value, priv->lower);
+ break;
+
+ case PROP_UPPER:
+ g_value_set_double (value, priv->upper);
+ break;
+
+ case PROP_VALUE:
+ g_value_set_double (value, priv->value);
+ break;
+
+ case PROP_STEP_INC:
+ g_value_set_double (value, priv->step_increment);
+ break;
+
+ case PROP_PAGE_INC:
+ g_value_set_double (value, priv->page_increment);
+ break;
+
+ case PROP_PAGE_SIZE:
+ g_value_set_double (value, priv->page_size);
+ break;
+
+ case PROP_ELASTIC:
+ g_value_set_boolean (value, priv->elastic);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_adjustment_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StAdjustment *adj = ST_ADJUSTMENT (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_LOWER:
+ st_adjustment_set_lower (adj, g_value_get_double (value));
+ break;
+
+ case PROP_UPPER:
+ st_adjustment_set_upper (adj, g_value_get_double (value));
+ break;
+
+ case PROP_VALUE:
+ st_adjustment_set_value (adj, g_value_get_double (value));
+ break;
+
+ case PROP_STEP_INC:
+ st_adjustment_set_step_increment (adj, g_value_get_double (value));
+ break;
+
+ case PROP_PAGE_INC:
+ st_adjustment_set_page_increment (adj, g_value_get_double (value));
+ break;
+
+ case PROP_PAGE_SIZE:
+ st_adjustment_set_page_size (adj, g_value_get_double (value));
+ break;
+
+ case PROP_ELASTIC:
+ st_adjustment_set_elastic (adj, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+stop_interpolation (StAdjustment *adjustment)
+{
+ StAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->interpolation)
+ {
+ clutter_timeline_stop (priv->interpolation);
+ g_object_unref (priv->interpolation);
+ priv->interpolation = NULL;
+
+ if (priv->bounce_alpha)
+ {
+ g_object_unref (priv->bounce_alpha);
+ priv->bounce_alpha = NULL;
+ }
+ }
+
+ if (priv->bounce_source)
+ {
+ g_source_remove (priv->bounce_source);
+ priv->bounce_source = 0;
+ }
+}
+
+static void
+st_adjustment_dispose (GObject *object)
+{
+ stop_interpolation (ST_ADJUSTMENT (object));
+
+ G_OBJECT_CLASS (st_adjustment_parent_class)->dispose (object);
+}
+
+static void
+st_adjustment_class_init (StAdjustmentClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (StAdjustmentPrivate));
+
+ object_class->constructed = st_adjustment_constructed;
+ object_class->get_property = st_adjustment_get_property;
+ object_class->set_property = st_adjustment_set_property;
+ object_class->dispose = st_adjustment_dispose;
+
+ g_object_class_install_property (object_class,
+ PROP_LOWER,
+ g_param_spec_double ("lower",
+ "Lower",
+ "Lower bound",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_UPPER,
+ g_param_spec_double ("upper",
+ "Upper",
+ "Upper bound",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_VALUE,
+ g_param_spec_double ("value",
+ "Value",
+ "Current value",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_STEP_INC,
+ g_param_spec_double ("step-increment",
+ "Step Increment",
+ "Step increment",
+ 0.0,
+ G_MAXDOUBLE,
+ 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_PAGE_INC,
+ g_param_spec_double ("page-increment",
+ "Page Increment",
+ "Page increment",
+ 0.0,
+ G_MAXDOUBLE,
+ 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_PAGE_SIZE,
+ g_param_spec_double ("page-size",
+ "Page Size",
+ "Page size",
+ 0.0,
+ G_MAXDOUBLE,
+ 0.0,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_ELASTIC,
+ g_param_spec_boolean ("elastic",
+ "Elastic",
+ "Make interpolation "
+ "behave in an "
+ "'elastic' way and "
+ "stop clamping value.",
+ FALSE,
+ ST_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ /**
+ * StAdjustment::changed:
+ *
+ * Emitted when any of the adjustment values have changed
+ */
+ signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StAdjustmentClass, changed),
+ NULL, NULL,
+ _st_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+st_adjustment_init (StAdjustment *self)
+{
+ self->priv = ADJUSTMENT_PRIVATE (self);
+
+ self->priv->is_constructing = TRUE;
+}
+
+StAdjustment *
+st_adjustment_new (gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size)
+{
+ return g_object_new (ST_TYPE_ADJUSTMENT,
+ "value", value,
+ "lower", lower,
+ "upper", upper,
+ "step-increment", step_increment,
+ "page-increment", page_increment,
+ "page-size", page_size,
+ NULL);
+}
+
+gdouble
+st_adjustment_get_value (StAdjustment *adjustment)
+{
+ StAdjustmentPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_ADJUSTMENT (adjustment), 0);
+
+ priv = adjustment->priv;
+
+ if (priv->interpolation)
+ {
+ return MAX (priv->lower,
+ MIN (priv->upper - priv->page_size,
+ priv->new_position));
+ }
+ else
+ return priv->value;
+}
+
+void
+st_adjustment_set_value (StAdjustment *adjustment,
+ gdouble value)
+{
+ StAdjustmentPrivate *priv;
+
+ g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
+
+ priv = adjustment->priv;
+
+ stop_interpolation (adjustment);
+
+ /* Defer clamp until after construction. */
+ if (!priv->is_constructing)
+ {
+ if (!priv->elastic)
+ value = CLAMP (value,
+ priv->lower,
+ MAX (priv->lower, priv->upper - priv->page_size));
+ }
+
+ if (priv->value != value)
+ {
+ priv->value = value;
+
+ g_object_notify (G_OBJECT (adjustment), "value");
+ }
+}
+
+void
+st_adjustment_clamp_page (StAdjustment *adjustment,
+ gdouble lower,
+ gdouble upper)
+{
+ StAdjustmentPrivate *priv;
+ gboolean changed;
+
+ g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
+
+ priv = adjustment->priv;
+
+ stop_interpolation (adjustment);
+
+ lower = CLAMP (lower, priv->lower, priv->upper - priv->page_size);
+ upper = CLAMP (upper, priv->lower + priv->page_size, priv->upper);
+
+ changed = FALSE;
+
+ if (priv->value + priv->page_size > upper)
+ {
+ priv->value = upper - priv->page_size;
+ changed = TRUE;
+ }
+
+ if (priv->value < lower)
+ {
+ priv->value = lower;
+ changed = TRUE;
+ }
+
+ if (changed)
+ g_object_notify (G_OBJECT (adjustment), "value");
+}
+
+static gboolean
+st_adjustment_set_lower (StAdjustment *adjustment,
+ gdouble lower)
+{
+ StAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->lower != lower)
+ {
+ priv->lower = lower;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify (G_OBJECT (adjustment), "lower");
+
+ /* Defer clamp until after construction. */
+ if (!priv->is_constructing)
+ st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+st_adjustment_set_upper (StAdjustment *adjustment,
+ gdouble upper)
+{
+ StAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->upper != upper)
+ {
+ priv->upper = upper;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify (G_OBJECT (adjustment), "upper");
+
+ /* Defer clamp until after construction. */
+ if (!priv->is_constructing)
+ st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+st_adjustment_set_step_increment (StAdjustment *adjustment,
+ gdouble step)
+{
+ StAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->step_increment != step)
+ {
+ priv->step_increment = step;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify (G_OBJECT (adjustment), "step-increment");
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+st_adjustment_set_page_increment (StAdjustment *adjustment,
+ gdouble page)
+{
+ StAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->page_increment != page)
+ {
+ priv->page_increment = page;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify (G_OBJECT (adjustment), "page-increment");
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+st_adjustment_set_page_size (StAdjustment *adjustment,
+ gdouble size)
+{
+ StAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->page_size != size)
+ {
+ priv->page_size = size;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify (G_OBJECT (adjustment), "page_size");
+
+ /* Well explicitely clamp after construction. */
+ if (!priv->is_constructing)
+ st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+st_adjustment_set_values (StAdjustment *adjustment,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size)
+{
+ StAdjustmentPrivate *priv;
+ gboolean emit_changed = FALSE;
+
+ g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
+ g_return_if_fail (page_size >= 0 && page_size <= G_MAXDOUBLE);
+ g_return_if_fail (step_increment >= 0 && step_increment <= G_MAXDOUBLE);
+ g_return_if_fail (page_increment >= 0 && page_increment <= G_MAXDOUBLE);
+
+ priv = adjustment->priv;
+
+ stop_interpolation (adjustment);
+
+ emit_changed = FALSE;
+
+ g_object_freeze_notify (G_OBJECT (adjustment));
+
+ emit_changed |= st_adjustment_set_lower (adjustment, lower);
+ emit_changed |= st_adjustment_set_upper (adjustment, upper);
+ emit_changed |= st_adjustment_set_step_increment (adjustment, step_increment);
+ emit_changed |= st_adjustment_set_page_increment (adjustment, page_increment);
+ emit_changed |= st_adjustment_set_page_size (adjustment, page_size);
+
+ if (value != priv->value)
+ {
+ st_adjustment_set_value (adjustment, value);
+ emit_changed = TRUE;
+ }
+
+ if (emit_changed)
+ g_signal_emit (G_OBJECT (adjustment), signals[CHANGED], 0);
+
+ g_object_thaw_notify (G_OBJECT (adjustment));
+}
+
+void
+st_adjustment_get_values (StAdjustment *adjustment,
+ gdouble *value,
+ gdouble *lower,
+ gdouble *upper,
+ gdouble *step_increment,
+ gdouble *page_increment,
+ gdouble *page_size)
+{
+ StAdjustmentPrivate *priv;
+
+ g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
+
+ priv = adjustment->priv;
+
+ if (lower)
+ *lower = priv->lower;
+
+ if (upper)
+ *upper = priv->upper;
+
+ if (value)
+ *value = st_adjustment_get_value (adjustment);
+
+ if (step_increment)
+ *step_increment = priv->step_increment;
+
+ if (page_increment)
+ *page_increment = priv->page_increment;
+
+ if (page_size)
+ *page_size = priv->page_size;
+}
+
+static void
+interpolation_new_frame_cb (ClutterTimeline *timeline,
+ guint msecs,
+ StAdjustment *adjustment)
+{
+ StAdjustmentPrivate *priv = adjustment->priv;
+
+ priv->interpolation = NULL;
+
+ if (priv->elastic)
+ {
+ gdouble progress = clutter_alpha_get_alpha (priv->bounce_alpha) / 1.0;
+ gdouble dx = priv->old_position
+ + (priv->new_position - priv->old_position)
+ * progress;
+
+ st_adjustment_set_value (adjustment, dx);
+ }
+ else
+ st_adjustment_set_value (adjustment,
+ priv->old_position +
+ (priv->new_position - priv->old_position) *
+ clutter_timeline_get_progress (timeline));
+
+ priv->interpolation = timeline;
+}
+
+static void
+interpolation_completed_cb (ClutterTimeline *timeline,
+ StAdjustment *adjustment)
+{
+ StAdjustmentPrivate *priv = adjustment->priv;
+
+ stop_interpolation (adjustment);
+ st_adjustment_set_value (adjustment, priv->new_position);
+}
+
+/* Note, there's super-optimal code that does a similar thing in
+ * clutter-alpha.c
+ *
+ * Tried this instead of CLUTTER_ALPHA_SINE_INC, but I think SINE_INC looks
+ * better. Leaving code here in case this is revisited.
+ */
+/*
+ static guint32
+ bounce_alpha_func (ClutterAlpha *alpha,
+ gpointer user_data)
+ {
+ ClutterFixed progress, angle;
+ ClutterTimeline *timeline = clutter_alpha_get_timeline (alpha);
+
+ progress = clutter_timeline_get_progressx (timeline);
+ angle = clutter_qmulx (CFX_PI_2 + CFX_PI_4/2, progress);
+
+ return clutter_sinx (angle) +
+ (CFX_ONE - clutter_sinx (CFX_PI_2 + CFX_PI_4/2));
+ }
+ */
+
+void
+st_adjustment_interpolate (StAdjustment *adjustment,
+ gdouble value,
+ guint duration)
+{
+ StAdjustmentPrivate *priv = adjustment->priv;
+
+ stop_interpolation (adjustment);
+
+ if (duration <= 1)
+ {
+ st_adjustment_set_value (adjustment, value);
+ return;
+ }
+
+ priv->old_position = priv->value;
+ priv->new_position = value;
+
+ priv->interpolation = clutter_timeline_new (duration);
+
+ if (priv->elastic)
+ priv->bounce_alpha = clutter_alpha_new_full (priv->interpolation,
+ CLUTTER_LINEAR);
+
+ g_signal_connect (priv->interpolation,
+ "new-frame",
+ G_CALLBACK (interpolation_new_frame_cb),
+ adjustment);
+ g_signal_connect (priv->interpolation,
+ "completed",
+ G_CALLBACK (interpolation_completed_cb),
+ adjustment);
+
+ clutter_timeline_start (priv->interpolation);
+}
+
+gboolean
+st_adjustment_get_elastic (StAdjustment *adjustment)
+{
+ return adjustment->priv->elastic;
+}
+
+void
+st_adjustment_set_elastic (StAdjustment *adjustment,
+ gboolean elastic)
+{
+ adjustment->priv->elastic = elastic;
+}
+
+gboolean
+st_adjustment_clamp (StAdjustment *adjustment,
+ gboolean interpolate,
+ guint duration)
+{
+ StAdjustmentPrivate *priv = adjustment->priv;
+ gdouble dest = priv->value;
+
+ if (priv->value < priv->lower)
+ dest = priv->lower;
+
+ if (priv->value > priv->upper - priv->page_size)
+ dest = priv->upper - priv->page_size;
+
+ if (dest != priv->value)
+ {
+ if (interpolate)
+ st_adjustment_interpolate (adjustment, dest, duration);
+ else
+ st_adjustment_set_value (adjustment, dest);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/src/st/st-adjustment.h b/src/st/st-adjustment.h
new file mode 100644
index 0000000..179ea91
--- /dev/null
+++ b/src/st/st-adjustment.h
@@ -0,0 +1,121 @@
+/*
+ * st-adjustment.h: Adjustment object
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>, inspired by GtkAdjustment
+ * Port to St by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_ADJUSTMENT_H__
+#define __ST_ADJUSTMENT_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_ADJUSTMENT (st_adjustment_get_type())
+#define ST_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_ADJUSTMENT, StAdjustment))
+#define ST_IS_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_ADJUSTMENT))
+#define ST_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_ADJUSTMENT, StAdjustmentClass))
+#define ST_IS_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_ADJUSTMENT))
+#define ST_ADJUSTMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_ADJUSTMENT, StAdjustmentClass))
+
+typedef struct _StAdjustment StAdjustment;
+typedef struct _StAdjustmentPrivate StAdjustmentPrivate;
+typedef struct _StAdjustmentClass StAdjustmentClass;
+
+/**
+ * StAdjustment:
+ *
+ * Class for handling an interval between to values. The contents of
+ * the #StAdjustment are private and should be accessed using the
+ * public API.
+ */
+struct _StAdjustment
+{
+ /*< private >*/
+ GObject parent_instance;
+
+ StAdjustmentPrivate *priv;
+};
+
+/**
+ * StAdjustmentClass
+ * @changed: Class handler for the ::changed signal.
+ *
+ * Base class for #StAdjustment.
+ */
+struct _StAdjustmentClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ /*< public >*/
+ void (* changed) (StAdjustment *adjustment);
+};
+
+GType st_adjustment_get_type (void) G_GNUC_CONST;
+
+StAdjustment *st_adjustment_new (gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size);
+gdouble st_adjustment_get_value (StAdjustment *adjustment);
+void st_adjustment_set_value (StAdjustment *adjustment,
+ gdouble value);
+void st_adjustment_clamp_page (StAdjustment *adjustment,
+ gdouble lower,
+ gdouble upper);
+void st_adjustment_set_values (StAdjustment *adjustment,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size);
+void st_adjustment_get_values (StAdjustment *adjustment,
+ gdouble *value,
+ gdouble *lower,
+ gdouble *upper,
+ gdouble *step_increment,
+ gdouble *page_increment,
+ gdouble *page_size);
+
+void st_adjustment_interpolate (StAdjustment *adjustment,
+ gdouble value,
+ guint duration);
+
+gboolean st_adjustment_get_elastic (StAdjustment *adjustment);
+void st_adjustment_set_elastic (StAdjustment *adjustment,
+ gboolean elastic);
+
+gboolean st_adjustment_clamp (StAdjustment *adjustment,
+ gboolean interpolate,
+ guint duration);
+
+G_END_DECLS
+
+#endif /* __ST_ADJUSTMENT_H__ */
diff --git a/src/st/st-button.c b/src/st/st-button.c
new file mode 100644
index 0000000..0f8c513
--- /dev/null
+++ b/src/st/st-button.c
@@ -0,0 +1,759 @@
+/*
+ * st-button.c: Plain button actor
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ * Thomas Wood <thomas linux intel com>
+ *
+ */
+
+/**
+ * SECTION:st-button
+ * @short_description: Button widget
+ *
+ * A button widget with support for either a text label or icon, toggle mode
+ * and transitions effects between states.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <clutter/clutter.h>
+
+#include "st-button.h"
+
+#include "st-marshal.h"
+#include "st-stylable.h"
+#include "st-style.h"
+#include "st-texture-frame.h"
+#include "st-texture-cache.h"
+#include "st-private.h"
+
+enum
+{
+ PROP_0,
+
+ PROP_LABEL,
+ PROP_TOGGLE,
+ PROP_ACTIVE,
+ PROP_TRANSITION
+};
+
+enum
+{
+ CLICKED,
+
+ LAST_SIGNAL
+};
+
+#define ST_BUTTON_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_BUTTON, StButtonPrivate))
+
+struct _StButtonPrivate
+{
+ gchar *text;
+
+ ClutterActor *old_bg;
+ gboolean old_bg_parented; /* TRUE if we have adopted old_bg */
+
+ guint8 old_opacity;
+
+ guint is_pressed : 1;
+ guint is_hover : 1;
+ guint is_checked : 1;
+ guint is_toggle : 1;
+
+ gint transition_duration;
+
+ ClutterAnimation *animation;
+
+ gint spacing;
+};
+
+static guint button_signals[LAST_SIGNAL] = { 0, };
+
+static void st_stylable_iface_init (StStylableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (StButton, st_button, ST_TYPE_BIN,
+ G_IMPLEMENT_INTERFACE (ST_TYPE_STYLABLE,
+ st_stylable_iface_init));
+
+static void
+st_stylable_iface_init (StStylableIface *iface)
+{
+ static gboolean is_initialized = FALSE;
+
+ if (G_UNLIKELY (!is_initialized))
+ {
+ ClutterColor bg_color = { 0xcc, 0xcc, 0xcc, 0x00 };
+ GParamSpec *pspec;
+
+ is_initialized = TRUE;
+
+ pspec = g_param_spec_int ("border-spacing",
+ "Border Spacing",
+ "Spacing between internal elements",
+ 0, G_MAXINT, 6,
+ G_PARAM_READWRITE);
+ st_stylable_iface_install_property (iface, ST_TYPE_BUTTON, pspec);
+
+
+ is_initialized = TRUE;
+
+ pspec = clutter_param_spec_color ("background-color",
+ "Background Color",
+ "The background color of an actor",
+ &bg_color,
+ G_PARAM_READWRITE);
+ st_stylable_iface_install_property (iface, ST_TYPE_BUTTON, pspec);
+ }
+}
+
+static void
+st_button_update_label_style (StButton *button)
+{
+ ClutterColor *real_color = NULL;
+ gchar *font_string = NULL;
+ gchar *font_name = NULL;
+ gint font_size = 0;
+ ClutterActor *label;
+
+ label = st_bin_get_child ((StBin*) button);
+
+ /* check the child is really a label */
+ if (!CLUTTER_IS_TEXT (label))
+ return;
+
+ st_stylable_get (ST_STYLABLE (button),
+ "color", &real_color,
+ "font-family", &font_name,
+ "font-size", &font_size,
+ NULL);
+
+ if (font_name || font_size)
+ {
+ if (font_name && font_size)
+ font_string = g_strdup_printf ("%s %dpx", font_name, font_size);
+ else
+ {
+ if (font_size)
+ font_string = g_strdup_printf ("%dpx", font_size);
+ else
+ font_string = font_name;
+ }
+
+ clutter_text_set_font_name (CLUTTER_TEXT (label), font_string);
+
+ if (font_string != font_name)
+ g_free (font_string);
+ }
+
+ g_free (font_name);
+
+ if (real_color)
+ {
+ clutter_text_set_color (CLUTTER_TEXT (label), real_color);
+ clutter_color_free (real_color);
+ }
+}
+
+static void
+st_button_dispose_old_bg (StButton *button)
+{
+ StButtonPrivate *priv = button->priv;
+
+ if (priv->old_bg)
+ {
+ if (priv->old_bg_parented)
+ {
+ clutter_actor_unparent (priv->old_bg);
+ priv->old_bg_parented = FALSE;
+ }
+ g_object_unref (priv->old_bg);
+ priv->old_bg = NULL;
+ }
+}
+
+static void
+st_button_stylable_changed (StStylable *stylable)
+{
+ StButton *button = ST_BUTTON (stylable);
+ ClutterActor *bg_image;
+
+ st_button_dispose_old_bg (button);
+
+ bg_image = st_widget_get_border_image ((StWidget*) button);
+ if (bg_image)
+ button->priv->old_bg = g_object_ref (bg_image);
+}
+
+static void
+st_animation_completed (ClutterAnimation *animation,
+ StButton *button)
+{
+ st_button_dispose_old_bg (button);
+}
+
+static void
+st_button_style_changed (StWidget *widget)
+{
+ StButton *button = ST_BUTTON (widget);
+ StButtonPrivate *priv = button->priv;
+ StButtonClass *button_class = ST_BUTTON_GET_CLASS (button);
+
+ /* get the spacing value */
+ st_stylable_get (ST_STYLABLE (widget),
+ "border-spacing", &priv->spacing,
+ NULL);
+
+ /* update the label styling */
+ st_button_update_label_style (button);
+
+ /* run a transition if applicable */
+ if (button_class->transition)
+ {
+ button_class->transition (button, priv->old_bg);
+ }
+ else
+ {
+ if (priv->old_bg &&
+ (!st_widget_get_style_pseudo_class (widget)))
+ {
+ ClutterAnimation *animation;
+ if (!clutter_actor_get_parent (priv->old_bg))
+ {
+ clutter_actor_set_parent (priv->old_bg, (ClutterActor*) widget);
+ priv->old_bg_parented = TRUE;
+ }
+ if (priv->transition_duration > 0)
+ {
+ animation = clutter_actor_animate (priv->old_bg,
+ CLUTTER_LINEAR,
+ priv->transition_duration,
+ "opacity", 0,
+ NULL);
+ g_signal_connect (animation, "completed",
+ G_CALLBACK (st_animation_completed), button);
+ }
+ else
+ {
+ st_button_dispose_old_bg (button);
+ }
+
+ }
+ }
+}
+
+static void
+st_button_real_pressed (StButton *button)
+{
+ st_widget_set_style_pseudo_class ((StWidget*) button, "active");
+}
+
+static void
+st_button_real_released (StButton *button)
+{
+ StButtonPrivate *priv = button->priv;
+
+ if (priv->is_checked)
+ st_widget_set_style_pseudo_class ((StWidget*) button, "checked");
+ else if (!priv->is_hover)
+ st_widget_set_style_pseudo_class ((StWidget*) button, NULL);
+ else
+ st_widget_set_style_pseudo_class ((StWidget*) button, "hover");
+
+}
+
+static gboolean
+st_button_button_press (ClutterActor *actor,
+ ClutterButtonEvent *event)
+{
+ st_widget_hide_tooltip (ST_WIDGET (actor));
+
+ if (event->button == 1)
+ {
+ StButton *button = ST_BUTTON (actor);
+ StButtonClass *klass = ST_BUTTON_GET_CLASS (button);
+
+ button->priv->is_pressed = TRUE;
+
+ clutter_grab_pointer (actor);
+
+ if (klass->pressed)
+ klass->pressed (button);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+st_button_button_release (ClutterActor *actor,
+ ClutterButtonEvent *event)
+{
+ if (event->button == 1)
+ {
+ StButton *button = ST_BUTTON (actor);
+ StButtonClass *klass = ST_BUTTON_GET_CLASS (button);
+
+ if (!button->priv->is_pressed)
+ return FALSE;
+
+ clutter_ungrab_pointer ();
+
+ if (button->priv->is_toggle)
+ {
+ st_button_set_checked (button, !button->priv->is_checked);
+ }
+
+ button->priv->is_pressed = FALSE;
+
+ if (klass->released)
+ klass->released (button);
+
+ g_signal_emit (button, button_signals[CLICKED], 0);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+st_button_enter (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ StButton *button = ST_BUTTON (actor);
+
+ if (!button->priv->is_checked)
+ st_widget_set_style_pseudo_class ((StWidget*) button, "hover");
+
+ button->priv->is_hover = 1;
+
+ return CLUTTER_ACTOR_CLASS (st_button_parent_class)->enter_event (actor, event);
+}
+
+static gboolean
+st_button_leave (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ StButton *button = ST_BUTTON (actor);
+
+ button->priv->is_hover = 0;
+
+ if (button->priv->is_pressed)
+ {
+ StButtonClass *klass = ST_BUTTON_GET_CLASS (button);
+
+ clutter_ungrab_pointer ();
+
+ button->priv->is_pressed = FALSE;
+
+ if (klass->released)
+ klass->released (button);
+ }
+
+ if (button->priv->is_checked)
+ st_widget_set_style_pseudo_class ((StWidget*) button, "checked");
+ else
+ st_widget_set_style_pseudo_class ((StWidget*) button, NULL);
+
+ return CLUTTER_ACTOR_CLASS (st_button_parent_class)->leave_event (actor, event);
+}
+
+static void
+st_button_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StButton *button = ST_BUTTON (gobject);
+ StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ st_button_set_label (button, g_value_get_string (value));
+ break;
+ case PROP_TOGGLE:
+ st_button_set_toggle_mode (button, g_value_get_boolean (value));
+ break;
+ case PROP_ACTIVE:
+ st_button_set_checked (button, g_value_get_boolean (value));
+ break;
+ case PROP_TRANSITION:
+ priv->transition_duration = g_value_get_int (value);
+ break;
+
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_button_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ g_value_set_string (value, priv->text);
+ break;
+ case PROP_TOGGLE:
+ g_value_set_boolean (value, priv->is_toggle);
+ break;
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, priv->is_checked);
+ break;
+ case PROP_TRANSITION:
+ g_value_set_int (value, priv->transition_duration);
+ break;
+
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_button_finalize (GObject *gobject)
+{
+ StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
+
+ g_free (priv->text);
+
+ G_OBJECT_CLASS (st_button_parent_class)->finalize (gobject);
+}
+
+static void
+st_button_dispose (GObject *gobject)
+{
+ st_button_dispose_old_bg (ST_BUTTON (gobject));
+
+ G_OBJECT_CLASS (st_button_parent_class)->dispose (gobject);
+}
+
+static void
+st_button_map (ClutterActor *self)
+{
+ StButtonPrivate *priv = ST_BUTTON (self)->priv;
+
+ CLUTTER_ACTOR_CLASS (st_button_parent_class)->map (self);
+
+ if (priv->old_bg && priv->old_bg_parented)
+ clutter_actor_map (priv->old_bg);
+}
+
+static void
+st_button_unmap (ClutterActor *self)
+{
+ StButtonPrivate *priv = ST_BUTTON (self)->priv;
+
+ CLUTTER_ACTOR_CLASS (st_button_parent_class)->unmap (self);
+
+ if (priv->old_bg && priv->old_bg_parented)
+ clutter_actor_unmap (priv->old_bg);
+}
+
+static void
+st_button_draw_background (StWidget *widget,
+ ClutterActor *background,
+ const ClutterColor *color)
+{
+ StButtonPrivate *priv;
+
+ ST_WIDGET_CLASS (st_button_parent_class)->draw_background (widget, background, color);
+
+ priv = ST_BUTTON (widget)->priv;
+
+ if (priv->old_bg && priv->old_bg_parented)
+ clutter_actor_paint (priv->old_bg);
+}
+
+static void
+st_button_class_init (StButtonClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (StButtonPrivate));
+
+ klass->pressed = st_button_real_pressed;
+ klass->released = st_button_real_released;
+
+ gobject_class->set_property = st_button_set_property;
+ gobject_class->get_property = st_button_get_property;
+ gobject_class->dispose = st_button_dispose;
+ gobject_class->finalize = st_button_finalize;
+
+ actor_class->button_press_event = st_button_button_press;
+ actor_class->button_release_event = st_button_button_release;
+ actor_class->enter_event = st_button_enter;
+ actor_class->leave_event = st_button_leave;
+
+ actor_class->map = st_button_map;
+ actor_class->unmap = st_button_unmap;
+
+ widget_class->draw_background = st_button_draw_background;
+
+ pspec = g_param_spec_string ("label",
+ "Label",
+ "Label of the button",
+ NULL, G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_LABEL, pspec);
+
+ pspec = g_param_spec_boolean ("toggle-mode",
+ "Toggle Mode",
+ "Enable or disable toggling",
+ FALSE, G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_TOGGLE, pspec);
+
+ pspec = g_param_spec_boolean ("checked",
+ "Checked",
+ "Indicates if a toggle button is \"on\""
+ " or \"off\"",
+ FALSE, G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_ACTIVE, pspec);
+
+ pspec = g_param_spec_int ("transition-duration",
+ "Transition Duration",
+ "Duration of the state transition effect",
+ 0, G_MAXINT, 120, G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_TRANSITION, pspec);
+
+
+ /**
+ * StButton::clicked:
+ * @button: the object that received the signal
+ *
+ * Emitted when the user activates the button, either with a mouse press and
+ * release or with the keyboard.
+ */
+
+ button_signals[CLICKED] =
+ g_signal_new ("clicked",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StButtonClass, clicked),
+ NULL, NULL,
+ _st_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+st_button_init (StButton *button)
+{
+ button->priv = ST_BUTTON_GET_PRIVATE (button);
+ button->priv->transition_duration = 120;
+ button->priv->spacing = 6;
+
+ clutter_actor_set_reactive ((ClutterActor *) button, TRUE);
+
+ g_signal_connect (button, "style-changed",
+ G_CALLBACK (st_button_style_changed), NULL);
+
+ g_signal_connect (button, "stylable-changed",
+ G_CALLBACK (st_button_stylable_changed), NULL);
+}
+
+/**
+ * st_button_new:
+ *
+ * Create a new button
+ *
+ * Returns: a new #StButton
+ */
+StWidget *
+st_button_new (void)
+{
+ return g_object_new (ST_TYPE_BUTTON, NULL);
+}
+
+/**
+ * st_button_new_with_label:
+ * @text: text to set the label to
+ *
+ * Create a new #StButton with the specified label
+ *
+ * Returns: a new #StButton
+ */
+StWidget *
+st_button_new_with_label (const gchar *text)
+{
+ return g_object_new (ST_TYPE_BUTTON, "label", text, NULL);
+}
+
+/**
+ * st_button_get_label:
+ * @button: a #StButton
+ *
+ * Get the text displayed on the button
+ *
+ * Returns: the text for the button. This must not be freed by the application
+ */
+G_CONST_RETURN gchar *
+st_button_get_label (StButton *button)
+{
+ g_return_val_if_fail (ST_IS_BUTTON (button), NULL);
+
+ return button->priv->text;
+}
+
+/**
+ * st_button_set_label:
+ * @button: a #Stbutton
+ * @text: text to set the label to
+ *
+ * Sets the text displayed on the button
+ */
+void
+st_button_set_label (StButton *button,
+ const gchar *text)
+{
+ StButtonPrivate *priv;
+ ClutterActor *label;
+
+ g_return_if_fail (ST_IS_BUTTON (button));
+
+ priv = button->priv;
+
+ g_free (priv->text);
+
+ if (text)
+ priv->text = g_strdup (text);
+ else
+ priv->text = g_strdup ("");
+
+ label = st_bin_get_child ((StBin*) button);
+
+ if (label && CLUTTER_IS_TEXT (label))
+ {
+ clutter_text_set_text (CLUTTER_TEXT (label), priv->text);
+ }
+ else
+ {
+ label = g_object_new (CLUTTER_TYPE_TEXT,
+ "text", priv->text,
+ "line-alignment", PANGO_ALIGN_CENTER,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ "use-markup", TRUE,
+ NULL);
+ st_bin_set_child ((StBin*) button, label);
+ }
+
+ st_stylable_changed ((StStylable*) button);
+
+ g_object_notify (G_OBJECT (button), "label");
+}
+
+/**
+ * st_button_get_toggle_mode:
+ * @button: a #StButton
+ *
+ * Get the toggle mode status of the button.
+ *
+ * Returns: #TRUE if toggle mode is set, otherwise #FALSE
+ */
+gboolean
+st_button_get_toggle_mode (StButton *button)
+{
+ g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
+
+ return button->priv->is_toggle;
+}
+
+/**
+ * st_button_set_toggle_mode:
+ * @button: a #Stbutton
+ * @toggle: #TRUE or #FALSE
+ *
+ * Enables or disables toggle mode for the button. In toggle mode, the active
+ * state will be "toggled" when the user clicks the button.
+ */
+void
+st_button_set_toggle_mode (StButton *button,
+ gboolean toggle)
+{
+ g_return_if_fail (ST_IS_BUTTON (button));
+
+ button->priv->is_toggle = toggle;
+
+ g_object_notify (G_OBJECT (button), "toggle-mode");
+}
+
+/**
+ * st_button_get_checked:
+ * @button: a #StButton
+ *
+ * Get the state of the button that is in toggle mode.
+ *
+ * Returns: #TRUE if the button is checked, or #FALSE if not
+ */
+gboolean
+st_button_get_checked (StButton *button)
+{
+ g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
+
+ return button->priv->is_checked;
+}
+
+/**
+ * st_button_set_checked:
+ * @button: a #Stbutton
+ * @checked: #TRUE or #FALSE
+ *
+ * Sets the pressed state of the button. This is only really useful if the
+ * button has #toggle-mode mode set to #TRUE.
+ */
+void
+st_button_set_checked (StButton *button,
+ gboolean checked)
+{
+ g_return_if_fail (ST_IS_BUTTON (button));
+
+ if (button->priv->is_checked != checked)
+ {
+ button->priv->is_checked = checked;
+
+ if (checked)
+ st_widget_set_style_pseudo_class ((StWidget*) button, "checked");
+ else
+ if (button->priv->is_hover)
+ st_widget_set_style_pseudo_class ((StWidget*) button, "hover");
+ else
+ st_widget_set_style_pseudo_class ((StWidget*) button, NULL);
+ }
+
+ g_object_notify (G_OBJECT (button), "checked");
+}
diff --git a/src/st/st-button.h b/src/st/st-button.h
new file mode 100644
index 0000000..c09a04a
--- /dev/null
+++ b/src/st/st-button.h
@@ -0,0 +1,93 @@
+/*
+ * st-button.h: Plain button actor
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ * Thomas Wood <thomas linux intel com>
+ *
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_BUTTON_H__
+#define __ST_BUTTON_H__
+
+G_BEGIN_DECLS
+
+#include <st/st-bin.h>
+
+#define ST_TYPE_BUTTON (st_button_get_type ())
+#define ST_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_BUTTON, StButton))
+#define ST_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_BUTTON))
+#define ST_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BUTTON, StButtonClass))
+#define ST_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BUTTON))
+#define ST_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BUTTON, StButtonClass))
+
+typedef struct _StButton StButton;
+typedef struct _StButtonPrivate StButtonPrivate;
+typedef struct _StButtonClass StButtonClass;
+
+/**
+ * StButton:
+ *
+ * The contents of this structure is private and should only be accessed using
+ * the provided API.
+ */
+
+struct _StButton
+{
+ /*< private >*/
+ StBin parent_instance;
+
+ StButtonPrivate *priv;
+};
+
+struct _StButtonClass
+{
+ StBinClass parent_class;
+
+ /* vfuncs, not signals */
+ void (* pressed) (StButton *button);
+ void (* released) (StButton *button);
+ void (* transition) (StButton *button,
+ ClutterActor *old_bg);
+
+ /* signals */
+ void (* clicked) (StButton *button);
+};
+
+GType st_button_get_type (void) G_GNUC_CONST;
+
+StWidget * st_button_new (void);
+StWidget * st_button_new_with_label (const gchar *text);
+G_CONST_RETURN gchar *st_button_get_label (StButton *button);
+void st_button_set_label (StButton *button,
+ const gchar *text);
+void st_button_set_toggle_mode (StButton *button,
+ gboolean toggle);
+gboolean st_button_get_toggle_mode (StButton *button);
+void st_button_set_checked (StButton *button,
+ gboolean checked);
+gboolean st_button_get_checked (StButton *button);
+
+G_END_DECLS
+
+#endif /* __ST_BUTTON_H__ */
diff --git a/src/st/st-scroll-bar.c b/src/st/st-scroll-bar.c
new file mode 100644
index 0000000..ad636df
--- /dev/null
+++ b/src/st/st-scroll-bar.c
@@ -0,0 +1,1105 @@
+/*
+ * st-scroll-bar.c: Scroll bar actor
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to St by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+/**
+ * SECTION:st-scroll-bar
+ * @short_description: a user interface element to control scrollable areas.
+ *
+ * The #StScrollBar allows users to scroll scrollable actors, either by
+ * the step or page amount, or by manually dragging the handle.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <clutter/clutter.h>
+
+#include "st-scroll-bar.h"
+#include "st-bin.h"
+#include "st-marshal.h"
+#include "st-stylable.h"
+#include "st-enum-types.h"
+#include "st-private.h"
+#include "st-button.h"
+
+static void st_stylable_iface_init (StStylableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (StScrollBar, st_scroll_bar, ST_TYPE_BIN,
+ G_IMPLEMENT_INTERFACE (ST_TYPE_STYLABLE,
+ st_stylable_iface_init))
+
+#define ST_SCROLL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_SCROLL_BAR, StScrollBarPrivate))
+
+#define PAGING_INITIAL_REPEAT_TIMEOUT 500
+#define PAGING_SUBSEQUENT_REPEAT_TIMEOUT 200
+
+struct _StScrollBarPrivate
+{
+ StAdjustment *adjustment;
+
+ gulong capture_handler;
+ gfloat x_origin;
+ gfloat y_origin;
+
+ ClutterActor *bw_stepper;
+ ClutterActor *fw_stepper;
+ ClutterActor *trough;
+ ClutterActor *handle;
+
+ gfloat move_x;
+ gfloat move_y;
+
+ /* Trough-click handling. */
+ enum { NONE, UP, DOWN } paging_direction;
+ guint paging_source_id;
+ guint paging_event_no;
+
+ gboolean stepper_forward;
+ guint stepper_source_id;
+
+ ClutterAnimation *paging_animation;
+
+ gboolean vertical;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_ADJUSTMENT,
+ PROP_VERTICAL
+};
+
+enum
+{
+ SCROLL_START,
+ SCROLL_STOP,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static gboolean
+handle_button_press_event_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ StScrollBar *bar);
+
+static void
+st_scroll_bar_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StScrollBarPrivate *priv = ST_SCROLL_BAR (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_ADJUSTMENT:
+ g_value_set_object (value, priv->adjustment);
+ break;
+
+ case PROP_VERTICAL:
+ g_value_set_boolean (value, priv->vertical);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_scroll_bar_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StScrollBar *bar = ST_SCROLL_BAR (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_ADJUSTMENT:
+ st_scroll_bar_set_adjustment (bar, g_value_get_object (value));
+ break;
+
+ case PROP_VERTICAL:
+ bar->priv->vertical = g_value_get_boolean (value);
+ if (bar->priv->vertical)
+ {
+ clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->bw_stepper),
+ "up-stepper");
+ clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->fw_stepper),
+ "down-stepper");
+ clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->handle),
+ "vhandle");
+ }
+ else
+ {
+ clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->fw_stepper),
+ "forward-stepper");
+ clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->bw_stepper),
+ "backward-stepper");
+ clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->handle),
+ "hhandle");
+ }
+ clutter_actor_queue_relayout ((ClutterActor*) gobject);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_scroll_bar_dispose (GObject *gobject)
+{
+ StScrollBar *bar = ST_SCROLL_BAR (gobject);
+ StScrollBarPrivate *priv = bar->priv;
+
+ if (priv->adjustment)
+ st_scroll_bar_set_adjustment (bar, NULL);
+
+ if (priv->handle)
+ {
+ g_signal_handlers_disconnect_by_func (priv->handle,
+ G_CALLBACK (handle_button_press_event_cb),
+ bar);
+ clutter_actor_unparent (priv->handle);
+ priv->handle = NULL;
+ }
+
+ clutter_actor_unparent (priv->bw_stepper);
+ priv->bw_stepper = NULL;
+
+ clutter_actor_unparent (priv->fw_stepper);
+ priv->fw_stepper = NULL;
+
+ clutter_actor_unparent (priv->trough);
+ priv->trough = NULL;
+
+ G_OBJECT_CLASS (st_scroll_bar_parent_class)->dispose (gobject);
+}
+
+static void
+st_scroll_bar_paint (ClutterActor *actor)
+{
+ StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
+
+ CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->paint (actor);
+
+ clutter_actor_paint (priv->bw_stepper);
+
+ clutter_actor_paint (priv->fw_stepper);
+
+ clutter_actor_paint (priv->trough);
+
+ if (priv->handle && CLUTTER_ACTOR_IS_VISIBLE (priv->handle))
+ clutter_actor_paint (priv->handle);
+}
+
+static void
+st_scroll_bar_pick (ClutterActor *actor,
+ const ClutterColor *pick_color)
+{
+ StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
+
+ CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->pick (actor, pick_color);
+
+ clutter_actor_paint (priv->bw_stepper);
+ clutter_actor_paint (priv->fw_stepper);
+ clutter_actor_paint (priv->trough);
+
+ if (priv->handle && priv->adjustment)
+ clutter_actor_paint (priv->handle);
+}
+
+static void
+st_scroll_bar_map (ClutterActor *actor)
+{
+ StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
+
+ CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->map (actor);
+
+ clutter_actor_map (priv->bw_stepper);
+ clutter_actor_map (priv->fw_stepper);
+ clutter_actor_map (priv->trough);
+
+ if (priv->handle)
+ clutter_actor_map (priv->handle);
+}
+
+static void
+st_scroll_bar_unmap (ClutterActor *actor)
+{
+ StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
+
+ CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->unmap (actor);
+
+ clutter_actor_unmap (priv->bw_stepper);
+ clutter_actor_unmap (priv->fw_stepper);
+ clutter_actor_unmap (priv->trough);
+
+ if (priv->handle)
+ clutter_actor_unmap (priv->handle);
+}
+
+static void
+st_scroll_bar_allocate (ClutterActor *actor,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags)
+{
+ StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
+ StPadding padding;
+ ClutterActorBox bw_box, fw_box, trough_box;
+ gfloat x, y, width, height, stepper_size;
+
+ /* Chain up */
+ CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->
+ allocate (actor, box, flags);
+
+ st_widget_get_padding (ST_WIDGET (actor), &padding);
+
+ /* calculate the child area */
+ x = padding.left;
+ y = padding.top;
+ width = (box->x2 - box->x1) - padding.left - padding.right;
+ height = (box->y2 - box->y1) - padding.top - padding.bottom;
+
+ if (priv->vertical)
+ {
+ stepper_size = width;
+
+ /* Backward stepper */
+ bw_box.x1 = x;
+ bw_box.y1 = y;
+ bw_box.x2 = bw_box.x1 + stepper_size;
+ bw_box.y2 = bw_box.y1 + stepper_size;
+ clutter_actor_allocate (priv->bw_stepper, &bw_box, flags);
+
+ /* Forward stepper */
+ fw_box.x1 = x;
+ fw_box.y1 = y + height - stepper_size;
+ fw_box.x2 = fw_box.x1 + stepper_size;
+ fw_box.y2 = fw_box.y1 + stepper_size;
+ clutter_actor_allocate (priv->fw_stepper, &fw_box, flags);
+
+ /* Trough */
+ trough_box.x1 = x;
+ trough_box.y1 = y + stepper_size;
+ trough_box.x2 = x + width;
+ trough_box.y2 = y + height - stepper_size;
+ clutter_actor_allocate (priv->trough, &trough_box, flags);
+
+ }
+ else
+ {
+ stepper_size = height;
+
+ /* Backward stepper */
+ bw_box.x1 = x;
+ bw_box.y1 = y;
+ bw_box.x2 = bw_box.x1 + stepper_size;
+ bw_box.y2 = bw_box.y1 + stepper_size;
+ clutter_actor_allocate (priv->bw_stepper, &bw_box, flags);
+
+ /* Forward stepper */
+ fw_box.x1 = x + width - stepper_size;
+ fw_box.y1 = y;
+ fw_box.x2 = fw_box.x1 + stepper_size;
+ fw_box.y2 = fw_box.y1 + stepper_size;
+ clutter_actor_allocate (priv->fw_stepper, &fw_box, flags);
+
+ /* Trough */
+ trough_box.x1 = x + stepper_size;
+ trough_box.y1 = y;
+ trough_box.x2 = x + width - stepper_size;
+ trough_box.y2 = y + height;
+ clutter_actor_allocate (priv->trough, &trough_box, flags);
+ }
+
+
+ if (priv->adjustment)
+ {
+ gfloat handle_size, position, avail_size;
+ gdouble value, lower, upper, page_size, increment;
+ ClutterActorBox handle_box = { 0, };
+ guint min_size, max_size;
+
+ st_adjustment_get_values (priv->adjustment,
+ &value,
+ &lower,
+ &upper,
+ NULL,
+ NULL,
+ &page_size);
+
+ if ((upper == lower)
+ || (page_size >= (upper - lower)))
+ increment = 1.0;
+ else
+ increment = page_size / (upper - lower);
+
+ st_stylable_get (ST_STYLABLE (actor),
+ "min-size", &min_size,
+ "max-size", &max_size,
+ NULL);
+
+ if (upper - lower - page_size <= 0)
+ position = 0;
+ else
+ position = (value - lower) / (upper - lower - page_size);
+
+ if (priv->vertical)
+ {
+ avail_size = height - stepper_size * 2;
+ handle_size = increment * avail_size;
+ handle_size = CLAMP (handle_size, min_size, max_size);
+
+ handle_box.x1 = x;
+ handle_box.y1 = bw_box.y2 + position * (avail_size - handle_size);
+
+ handle_box.x2 = handle_box.x1 + width;
+ handle_box.y2 = handle_box.y1 + handle_size;
+ }
+ else
+ {
+ avail_size = width - stepper_size * 2;
+ handle_size = increment * avail_size;
+ handle_size = CLAMP (handle_size, min_size, max_size);
+
+ handle_box.x1 = bw_box.x2 + position * (avail_size - handle_size);
+ handle_box.y1 = y;
+
+ handle_box.x2 = handle_box.x1 + handle_size;
+ handle_box.y2 = handle_box.y1 + height;
+ }
+
+ /* snap to pixel */
+ handle_box.x1 = (int) handle_box.x1;
+ handle_box.y1 = (int) handle_box.y1;
+ handle_box.x2 = (int) handle_box.x2;
+ handle_box.y2 = (int) handle_box.y2;
+
+ clutter_actor_allocate (priv->handle,
+ &handle_box,
+ flags);
+ }
+}
+
+static void
+st_scroll_bar_style_changed (StWidget *widget)
+{
+ StScrollBarPrivate *priv = ST_SCROLL_BAR (widget)->priv;
+
+ st_stylable_changed ((StStylable *) priv->bw_stepper);
+ st_stylable_changed ((StStylable *) priv->fw_stepper);
+ st_stylable_changed ((StStylable *) priv->trough);
+ st_stylable_changed ((StStylable *) priv->handle);
+
+}
+
+static void
+bar_reactive_notify_cb (GObject *gobject,
+ GParamSpec *arg1,
+ gpointer user_data)
+{
+ StScrollBar *bar = ST_SCROLL_BAR (gobject);
+
+ clutter_actor_set_reactive (bar->priv->handle,
+ clutter_actor_get_reactive (CLUTTER_ACTOR (bar)));
+}
+
+static GObject*
+st_scroll_bar_constructor (GType type,
+ guint n_properties,
+ GObjectConstructParam *properties)
+{
+ GObjectClass *gobject_class;
+ GObject *obj;
+ StScrollBar *bar;
+ StScrollBarPrivate *priv;
+
+ gobject_class = G_OBJECT_CLASS (st_scroll_bar_parent_class);
+ obj = gobject_class->constructor (type, n_properties, properties);
+
+ bar = ST_SCROLL_BAR (obj);
+ priv = ST_SCROLL_BAR_GET_PRIVATE (bar);
+
+ g_signal_connect (bar, "notify::reactive",
+ G_CALLBACK (bar_reactive_notify_cb), NULL);
+
+ return obj;
+}
+
+static gboolean
+st_scroll_bar_scroll_event (ClutterActor *actor,
+ ClutterScrollEvent *event)
+{
+ StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
+ gdouble lower, step, upper, value;
+
+ if (priv->adjustment)
+ {
+ g_object_get (priv->adjustment,
+ "lower", &lower,
+ "step-increment", &step,
+ "upper", &upper,
+ "value", &value,
+ NULL);
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ switch (event->direction)
+ {
+ case CLUTTER_SCROLL_UP:
+ case CLUTTER_SCROLL_LEFT:
+ if (value == lower)
+ return FALSE;
+ else
+ st_adjustment_set_value (priv->adjustment, value - step);
+ break;
+ case CLUTTER_SCROLL_DOWN:
+ case CLUTTER_SCROLL_RIGHT:
+ if (value == upper)
+ return FALSE;
+ else
+ st_adjustment_set_value (priv->adjustment, value + step);
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+st_scroll_bar_class_init (StScrollBarClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (StScrollBarPrivate));
+
+ object_class->get_property = st_scroll_bar_get_property;
+ object_class->set_property = st_scroll_bar_set_property;
+ object_class->dispose = st_scroll_bar_dispose;
+ object_class->constructor = st_scroll_bar_constructor;
+
+ actor_class->allocate = st_scroll_bar_allocate;
+ actor_class->paint = st_scroll_bar_paint;
+ actor_class->pick = st_scroll_bar_pick;
+ actor_class->scroll_event = st_scroll_bar_scroll_event;
+ actor_class->map = st_scroll_bar_map;
+ actor_class->unmap = st_scroll_bar_unmap;
+
+ g_object_class_install_property
+ (object_class,
+ PROP_ADJUSTMENT,
+ g_param_spec_object ("adjustment",
+ "Adjustment",
+ "The adjustment",
+ ST_TYPE_ADJUSTMENT,
+ ST_PARAM_READWRITE));
+
+ pspec = g_param_spec_boolean ("vertical",
+ "Vertical Orientation",
+ "Vertical Orientation",
+ FALSE,
+ ST_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_VERTICAL, pspec);
+
+ signals[SCROLL_START] =
+ g_signal_new ("scroll-start",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StScrollBarClass, scroll_start),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[SCROLL_STOP] =
+ g_signal_new ("scroll-stop",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StScrollBarClass, scroll_stop),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+st_stylable_iface_init (StStylableIface *iface)
+{
+ static gboolean is_initialized = FALSE;
+
+ if (!is_initialized)
+ {
+ GParamSpec *pspec;
+
+ is_initialized = TRUE;
+
+ pspec = g_param_spec_uint ("min-size",
+ "Minimum grabber size",
+ "Minimum size of the scroll grabber, in px",
+ 0, G_MAXUINT, 32,
+ G_PARAM_READWRITE);
+ st_stylable_iface_install_property (iface,
+ ST_TYPE_SCROLL_BAR, pspec);
+
+ pspec = g_param_spec_uint ("max-size",
+ "Maximum grabber size",
+ "Maximum size of the scroll grabber, in px",
+ 0, G_MAXINT16, G_MAXINT16,
+ G_PARAM_READWRITE);
+ st_stylable_iface_install_property (iface,
+ ST_TYPE_SCROLL_BAR, pspec);
+ }
+}
+
+static void
+move_slider (StScrollBar *bar,
+ gfloat x,
+ gfloat y)
+{
+ StScrollBarPrivate *priv = bar->priv;
+ gdouble position, lower, upper, page_size;
+ gfloat ux, uy, pos, size;
+
+ if (!priv->adjustment)
+ return;
+
+ if (!clutter_actor_transform_stage_point (priv->trough, x, y, &ux, &uy))
+ return;
+
+ if (priv->vertical)
+ size = clutter_actor_get_height (priv->trough)
+ - clutter_actor_get_height (priv->handle);
+ else
+ size = clutter_actor_get_width (priv->trough)
+ - clutter_actor_get_width (priv->handle);
+
+ if (size == 0)
+ return;
+
+ if (priv->vertical)
+ pos = uy - priv->y_origin;
+ else
+ pos = ux - priv->x_origin;
+ pos = CLAMP (pos, 0, size);
+
+ st_adjustment_get_values (priv->adjustment,
+ NULL,
+ &lower,
+ &upper,
+ NULL,
+ NULL,
+ &page_size);
+
+ position = ((pos / size)
+ * (upper - lower - page_size))
+ + lower;
+
+ st_adjustment_set_value (priv->adjustment, position);
+}
+
+static gboolean
+handle_capture_event_cb (ClutterActor *trough,
+ ClutterEvent *event,
+ StScrollBar *bar)
+{
+ if (clutter_event_type (event) == CLUTTER_MOTION)
+ {
+ move_slider (bar,
+ ((ClutterMotionEvent*) event)->x,
+ ((ClutterMotionEvent*) event)->y);
+ }
+ else if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE
+ && ((ClutterButtonEvent*) event)->button == 1)
+ {
+ ClutterActor *stage, *target;
+
+ stage = clutter_actor_get_stage(bar->priv->trough);
+
+ if (bar->priv->capture_handler)
+ {
+ g_signal_handler_disconnect (stage, bar->priv->capture_handler);
+ bar->priv->capture_handler = 0;
+ }
+
+ clutter_set_motion_events_enabled (TRUE);
+ g_signal_emit (bar, signals[SCROLL_STOP], 0);
+
+ /* check if the mouse pointer has left the handle during the drag and
+ * remove the hover state if it has */
+ target = clutter_stage_get_actor_at_pos ((ClutterStage*) stage,
+ CLUTTER_PICK_REACTIVE,
+ ((ClutterButtonEvent*) event)->x,
+ ((ClutterButtonEvent*) event)->y);
+ if (target != bar->priv->handle)
+ {
+ st_widget_set_style_pseudo_class ((StWidget*) bar->priv->handle, NULL);
+ }
+
+
+ }
+
+ return TRUE;
+}
+
+static gboolean
+handle_button_press_event_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ StScrollBar *bar)
+{
+ StScrollBarPrivate *priv = bar->priv;
+
+ if (event->button != 1)
+ return FALSE;
+
+ if (!clutter_actor_transform_stage_point (priv->handle,
+ event->x,
+ event->y,
+ &priv->x_origin,
+ &priv->y_origin))
+ return FALSE;
+
+ /* Account for the scrollbar-trough-handle nesting. */
+ priv->x_origin += clutter_actor_get_x (priv->trough);
+ priv->y_origin += clutter_actor_get_y (priv->trough);
+
+ /* Turn off picking for motion events */
+ clutter_set_motion_events_enabled (FALSE);
+
+ priv->capture_handler = g_signal_connect_after (
+ clutter_actor_get_stage (priv->trough),
+ "captured-event",
+ G_CALLBACK (handle_capture_event_cb),
+ bar);
+ g_signal_emit (bar, signals[SCROLL_START], 0);
+
+ return TRUE;
+}
+
+static void
+animation_completed_cb (ClutterAnimation *animation,
+ StScrollBarPrivate *priv)
+{
+ g_object_unref (priv->paging_animation);
+ priv->paging_animation = NULL;
+}
+
+static gboolean
+trough_paging_cb (StScrollBar *self)
+{
+ gfloat handle_pos, event_pos, tx, ty;
+ gdouble value;
+ gdouble page_increment;
+ gboolean ret;
+
+ gulong mode;
+ ClutterAnimation *a;
+ GValue v = { 0, };
+ ClutterTimeline *t;
+
+ if (self->priv->paging_event_no == 0)
+ {
+ /* Scroll on after initial timeout. */
+ mode = CLUTTER_EASE_OUT_CUBIC;
+ ret = FALSE;
+ self->priv->paging_event_no = 1;
+ self->priv->paging_source_id = g_timeout_add (
+ PAGING_INITIAL_REPEAT_TIMEOUT,
+ (GSourceFunc) trough_paging_cb,
+ self);
+ }
+ else if (self->priv->paging_event_no == 1)
+ {
+ /* Scroll on after subsequent timeout. */
+ ret = FALSE;
+ mode = CLUTTER_EASE_IN_CUBIC;
+ self->priv->paging_event_no = 2;
+ self->priv->paging_source_id = g_timeout_add (
+ PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
+ (GSourceFunc) trough_paging_cb,
+ self);
+ }
+ else
+ {
+ /* Keep scrolling. */
+ ret = TRUE;
+ mode = CLUTTER_LINEAR;
+ self->priv->paging_event_no++;
+ }
+
+ /* Do the scrolling */
+ st_adjustment_get_values (self->priv->adjustment,
+ &value, NULL, NULL,
+ NULL, &page_increment, NULL);
+
+ if (self->priv->vertical)
+ handle_pos = clutter_actor_get_y (self->priv->handle);
+ else
+ handle_pos = clutter_actor_get_x (self->priv->handle);
+
+ clutter_actor_transform_stage_point (CLUTTER_ACTOR (self->priv->trough),
+ self->priv->move_x,
+ self->priv->move_y,
+ &tx, &ty);
+
+ if (self->priv->vertical)
+ event_pos = ty;
+ else
+ event_pos = tx;
+
+ if (event_pos > handle_pos)
+ {
+ if (self->priv->paging_direction == NONE)
+ {
+ /* Remember direction. */
+ self->priv->paging_direction = DOWN;
+ }
+ if (self->priv->paging_direction == UP)
+ {
+ /* Scrolled far enough. */
+ return FALSE;
+ }
+ value += page_increment;
+ }
+ else
+ {
+ if (self->priv->paging_direction == NONE)
+ {
+ /* Remember direction. */
+ self->priv->paging_direction = UP;
+ }
+ if (self->priv->paging_direction == DOWN)
+ {
+ /* Scrolled far enough. */
+ return FALSE;
+ }
+ value -= page_increment;
+ }
+
+ if (self->priv->paging_animation)
+ {
+ clutter_animation_completed (self->priv->paging_animation);
+ }
+
+ /* FIXME: Creating a new animation for each scroll is probably not the best
+ * idea, but it's a lot less involved than extenind the current animation */
+ a = self->priv->paging_animation = g_object_new (CLUTTER_TYPE_ANIMATION,
+ "object", self->priv->adjustment,
+ "duration", PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
+ "mode", mode,
+ NULL);
+ g_value_init (&v, G_TYPE_DOUBLE);
+ g_value_set_double (&v, value);
+ clutter_animation_bind (self->priv->paging_animation, "value", &v);
+ t = clutter_animation_get_timeline (self->priv->paging_animation);
+ g_signal_connect (a, "completed", G_CALLBACK (animation_completed_cb),
+ self->priv);
+ clutter_timeline_start (t);
+
+ return ret;
+}
+
+static gboolean
+trough_button_press_event_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ StScrollBar *self)
+{
+ g_return_val_if_fail (self, FALSE);
+
+ if (event->button != 1)
+ return FALSE;
+
+ if (self->priv->adjustment == NULL)
+ return FALSE;
+
+ self->priv->move_x = event->x;
+ self->priv->move_y = event->y;
+ self->priv->paging_direction = NONE;
+ self->priv->paging_event_no = 0;
+ trough_paging_cb (self);
+
+ return TRUE;
+}
+
+static gboolean
+trough_button_release_event_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ StScrollBar *self)
+{
+ if (event->button != 1)
+ return FALSE;
+
+ if (self->priv->paging_source_id)
+ {
+ g_source_remove (self->priv->paging_source_id);
+ self->priv->paging_source_id = 0;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+trough_leave_event_cb (ClutterActor *actor,
+ ClutterEvent *event,
+ StScrollBar *self)
+{
+ if (self->priv->paging_source_id)
+ {
+ g_source_remove (self->priv->paging_source_id);
+ self->priv->paging_source_id = 0;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+stepper_animation_completed_cb (ClutterAnimation *a,
+ gpointer data)
+{
+ g_object_unref (a);
+}
+
+static void
+stepper_move_on (StScrollBarPrivate *priv,
+ gint mode)
+{
+ ClutterAnimation *a;
+ ClutterTimeline *t;
+ GValue v = { 0, };
+ double value, inc;
+
+ a = g_object_new (CLUTTER_TYPE_ANIMATION,
+ "object", priv->adjustment,
+ "duration", PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
+ "mode", mode,
+ NULL);
+
+ g_signal_connect (a, "completed", G_CALLBACK (stepper_animation_completed_cb),
+ NULL);
+
+ g_object_get (priv->adjustment,
+ "step-increment", &inc,
+ "value", &value,
+ NULL);
+
+ if (priv->stepper_forward)
+ value = value + inc;
+ else
+ value = value - inc;
+
+ g_value_init (&v, G_TYPE_DOUBLE);
+ g_value_set_double (&v, value);
+ clutter_animation_bind (a, "value", &v);
+
+ t = clutter_animation_get_timeline (a);
+ clutter_timeline_start (t);
+}
+
+static gboolean
+stepper_button_subsequent_timeout (StScrollBarPrivate *priv)
+{
+ stepper_move_on (priv, CLUTTER_LINEAR);
+
+ return TRUE;
+}
+
+static gboolean
+stepper_button_repeat_timeout (StScrollBarPrivate *priv)
+{
+ priv->stepper_source_id = 0;
+
+ stepper_move_on (priv, CLUTTER_EASE_IN_CUBIC);
+
+ priv->stepper_source_id = g_timeout_add (PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
+ (GSourceFunc)
+ stepper_button_subsequent_timeout,
+ priv);
+ return FALSE;
+}
+
+static gboolean
+stepper_button_press_event_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ StScrollBar *bar)
+{
+ StScrollBarPrivate *priv = bar->priv;
+
+ if (event->button != 1)
+ return FALSE;
+
+ if (bar->priv->adjustment == NULL)
+ return FALSE;
+
+ bar->priv->stepper_forward = (actor == priv->fw_stepper);
+
+ stepper_move_on (priv, CLUTTER_EASE_OUT_CUBIC);
+
+ priv->stepper_source_id = g_timeout_add (PAGING_INITIAL_REPEAT_TIMEOUT,
+ (GSourceFunc)
+ stepper_button_repeat_timeout,
+ priv);
+
+ return TRUE;
+}
+
+static gboolean
+stepper_button_release_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ StScrollBar *self)
+{
+ if (event->button != 1)
+ return FALSE;
+
+ g_source_remove (self->priv->stepper_source_id);
+
+ return FALSE;
+}
+
+static void
+st_scroll_bar_notify_reactive (StScrollBar *self)
+{
+ StScrollBarPrivate *priv = self->priv;
+
+ gboolean reactive = CLUTTER_ACTOR_IS_REACTIVE (self);
+
+ clutter_actor_set_reactive (CLUTTER_ACTOR (priv->bw_stepper), reactive);
+ clutter_actor_set_reactive (CLUTTER_ACTOR (priv->fw_stepper), reactive);
+ clutter_actor_set_reactive (CLUTTER_ACTOR (priv->trough), reactive);
+ clutter_actor_set_reactive (CLUTTER_ACTOR (priv->handle), reactive);
+}
+
+static void
+st_scroll_bar_init (StScrollBar *self)
+{
+ self->priv = ST_SCROLL_BAR_GET_PRIVATE (self);
+
+ self->priv->bw_stepper = (ClutterActor *) st_button_new ();
+ clutter_actor_set_name (CLUTTER_ACTOR (self->priv->bw_stepper),
+ "backward-stepper");
+ clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->bw_stepper),
+ CLUTTER_ACTOR (self));
+ g_signal_connect (self->priv->bw_stepper, "button-press-event",
+ G_CALLBACK (stepper_button_press_event_cb), self);
+ g_signal_connect (self->priv->bw_stepper, "button-release-event",
+ G_CALLBACK (stepper_button_release_cb), self);
+
+ self->priv->fw_stepper = (ClutterActor *) st_button_new ();
+ clutter_actor_set_name (CLUTTER_ACTOR (self->priv->fw_stepper),
+ "forward-stepper");
+ clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->fw_stepper),
+ CLUTTER_ACTOR (self));
+ g_signal_connect (self->priv->fw_stepper, "button-press-event",
+ G_CALLBACK (stepper_button_press_event_cb), self);
+ g_signal_connect (self->priv->fw_stepper, "button-release-event",
+ G_CALLBACK (stepper_button_release_cb), self);
+
+ self->priv->trough = (ClutterActor *) st_bin_new ();
+ clutter_actor_set_reactive ((ClutterActor *) self->priv->trough, TRUE);
+ clutter_actor_set_name (CLUTTER_ACTOR (self->priv->trough), "trough");
+ clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->trough),
+ CLUTTER_ACTOR (self));
+ g_signal_connect (self->priv->trough, "button-press-event",
+ G_CALLBACK (trough_button_press_event_cb), self);
+ g_signal_connect (self->priv->trough, "button-release-event",
+ G_CALLBACK (trough_button_release_event_cb), self);
+ g_signal_connect (self->priv->trough, "leave-event",
+ G_CALLBACK (trough_leave_event_cb), self);
+
+ self->priv->handle = (ClutterActor *) st_button_new ();
+ clutter_actor_set_name (CLUTTER_ACTOR (self->priv->handle), "hhandle");
+ clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->handle),
+ self->priv->trough);
+ g_signal_connect (self->priv->handle, "button-press-event",
+ G_CALLBACK (handle_button_press_event_cb), self);
+
+ clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
+
+ g_signal_connect (self, "style-changed",
+ G_CALLBACK (st_scroll_bar_style_changed), NULL);
+ g_signal_connect (self, "notify::reactive",
+ G_CALLBACK (st_scroll_bar_notify_reactive), NULL);
+}
+
+StWidget *
+st_scroll_bar_new (StAdjustment *adjustment)
+{
+ return g_object_new (ST_TYPE_SCROLL_BAR,
+ "adjustment", adjustment,
+ NULL);
+}
+
+void
+st_scroll_bar_set_adjustment (StScrollBar *bar,
+ StAdjustment *adjustment)
+{
+ StScrollBarPrivate *priv;
+
+ g_return_if_fail (ST_IS_SCROLL_BAR (bar));
+
+ priv = bar->priv;
+ if (priv->adjustment)
+ {
+ g_signal_handlers_disconnect_by_func (priv->adjustment,
+ clutter_actor_queue_relayout,
+ bar);
+ g_signal_handlers_disconnect_by_func (priv->adjustment,
+ clutter_actor_queue_relayout,
+ bar);
+ g_object_unref (priv->adjustment);
+ priv->adjustment = NULL;
+ }
+
+ if (adjustment)
+ {
+ priv->adjustment = g_object_ref (adjustment);
+
+ g_signal_connect_swapped (priv->adjustment, "notify::value",
+ G_CALLBACK (clutter_actor_queue_relayout),
+ bar);
+ g_signal_connect_swapped (priv->adjustment, "changed",
+ G_CALLBACK (clutter_actor_queue_relayout),
+ bar);
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (bar));
+ }
+}
+
+StAdjustment *
+st_scroll_bar_get_adjustment (StScrollBar *bar)
+{
+ g_return_val_if_fail (ST_IS_SCROLL_BAR (bar), NULL);
+
+ return bar->priv->adjustment;
+}
+
diff --git a/src/st/st-scroll-bar.h b/src/st/st-scroll-bar.h
new file mode 100644
index 0000000..33adc03
--- /dev/null
+++ b/src/st/st-scroll-bar.h
@@ -0,0 +1,82 @@
+/*
+ * st-scroll-bar.h: Scroll bar actor
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to St by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_SCROLL_BAR_H__
+#define __ST_SCROLL_BAR_H__
+
+#include <st/st-adjustment.h>
+#include <st/st-bin.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_SCROLL_BAR (st_scroll_bar_get_type())
+#define ST_SCROLL_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLL_BAR, StScrollBar))
+#define ST_IS_SCROLL_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLL_BAR))
+#define ST_SCROLL_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SCROLL_BAR, StScrollBarClass))
+#define ST_IS_SCROLL_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SCROLL_BAR))
+#define ST_SCROLL_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SCROLL_BAR, StScrollBarClass))
+
+typedef struct _StScrollBar StScrollBar;
+typedef struct _StScrollBarPrivate StScrollBarPrivate;
+typedef struct _StScrollBarClass StScrollBarClass;
+
+/**
+ * StScrollBar:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _StScrollBar
+{
+ /*< private >*/
+ StBin parent_instance;
+
+ StScrollBarPrivate *priv;
+};
+
+struct _StScrollBarClass
+{
+ StBinClass parent_class;
+
+ /* signals */
+ void (*scroll_start) (StScrollBar *bar);
+ void (*scroll_stop) (StScrollBar *bar);
+};
+
+GType st_scroll_bar_get_type (void) G_GNUC_CONST;
+
+StWidget *st_scroll_bar_new (StAdjustment *adjustment);
+
+void st_scroll_bar_set_adjustment (StScrollBar *bar,
+ StAdjustment *adjustment);
+StAdjustment *st_scroll_bar_get_adjustment (StScrollBar *bar);
+
+G_END_DECLS
+
+#endif /* __ST_SCROLL_BAR_H__ */
diff --git a/src/st/st-scroll-view.c b/src/st/st-scroll-view.c
new file mode 100644
index 0000000..aba01e6
--- /dev/null
+++ b/src/st/st-scroll-view.c
@@ -0,0 +1,850 @@
+/*
+ * st-scroll-view.h: Container with scroll-bars
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to St by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+/**
+ * SECTION:st-scroll-view
+ * @short_description: a container for scrollable children
+ *
+ * #StScrollView is a single child container for actors that implement
+ * #StScrollable. It provides scrollbars around the edge of the child to
+ * allow the user to move around the scrollable area.
+ */
+
+#include "st-scroll-view.h"
+#include "st-marshal.h"
+#include "st-scroll-bar.h"
+#include "st-scrollable.h"
+#include "st-stylable.h"
+#include <clutter/clutter.h>
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+static void st_stylable_iface_init (StStylableIface *iface);
+
+static ClutterContainerIface *st_scroll_view_parent_iface = NULL;
+
+G_DEFINE_TYPE_WITH_CODE (StScrollView, st_scroll_view, ST_TYPE_BIN,
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+ clutter_container_iface_init)
+ G_IMPLEMENT_INTERFACE (ST_TYPE_STYLABLE,
+ st_stylable_iface_init))
+
+#define SCROLL_VIEW_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+ ST_TYPE_SCROLL_VIEW, \
+ StScrollViewPrivate))
+
+struct _StScrollViewPrivate
+{
+ /* a pointer to the child; this is actually stored
+ * inside StBin:child, but we keep it to avoid
+ * calling st_bin_get_child() every time we need it
+ */
+ ClutterActor *child;
+
+ ClutterActor *hscroll;
+ ClutterActor *vscroll;
+
+ gfloat row_size;
+ gfloat column_size;
+
+ gboolean row_size_set : 1;
+ gboolean column_size_set : 1;
+ guint mouse_scroll : 1;
+};
+
+enum {
+ PROP_0,
+
+ PROP_HSCROLL,
+ PROP_VSCROLL,
+ PROP_MOUSE_SCROLL
+};
+
+static void
+st_scroll_view_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StScrollViewPrivate *priv = ((StScrollView *) object)->priv;
+
+ switch (property_id)
+ {
+ case PROP_HSCROLL:
+ g_value_set_object (value, priv->hscroll);
+ break;
+ case PROP_VSCROLL:
+ g_value_set_object (value, priv->vscroll);
+ break;
+ case PROP_MOUSE_SCROLL:
+ g_value_set_boolean (value, priv->mouse_scroll);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+st_scroll_view_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ case PROP_MOUSE_SCROLL:
+ st_scroll_view_set_mouse_scrolling ((StScrollView *) object,
+ g_value_get_boolean (value));
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+st_scroll_view_dispose (GObject *object)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (object)->priv;
+
+ priv->child = NULL;
+
+ if (priv->vscroll)
+ {
+ clutter_actor_unparent (priv->vscroll);
+ priv->vscroll = NULL;
+ }
+
+ if (priv->hscroll)
+ {
+ clutter_actor_unparent (priv->hscroll);
+ priv->hscroll = NULL;
+ }
+
+ G_OBJECT_CLASS (st_scroll_view_parent_class)->dispose (object);
+}
+
+static void
+st_scroll_view_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (st_scroll_view_parent_class)->finalize (object);
+}
+
+static void
+st_scroll_view_paint (ClutterActor *actor)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+
+ /* StBin will paint the child */
+ CLUTTER_ACTOR_CLASS (st_scroll_view_parent_class)->paint (actor);
+
+ /* paint our custom children */
+ if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
+ clutter_actor_paint (priv->hscroll);
+ if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
+ clutter_actor_paint (priv->vscroll);
+}
+
+static void
+st_scroll_view_pick (ClutterActor *actor,
+ const ClutterColor *color)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+
+ /* Chain up so we get a bounding box pained (if we are reactive) */
+ CLUTTER_ACTOR_CLASS (st_scroll_view_parent_class)->pick (actor, color);
+
+ /* paint our custom children */
+ if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
+ clutter_actor_paint (priv->hscroll);
+ if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
+ clutter_actor_paint (priv->vscroll);
+}
+
+static void
+st_scroll_view_get_preferred_width (ClutterActor *actor,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ StPadding padding;
+ guint xthickness;
+
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+
+ if (!priv->child)
+ return;
+
+ st_widget_get_padding (ST_WIDGET (actor), &padding);
+ st_stylable_get (ST_STYLABLE (actor),
+ "scrollbar-width", &xthickness,
+ NULL);
+
+ /* Our natural width is the natural width of the child */
+ clutter_actor_get_preferred_width (priv->child,
+ for_height,
+ NULL,
+ natural_width_p);
+
+ /* Add space for the scroll-bar if we can determine it will be necessary */
+ if ((for_height >= 0) && natural_width_p)
+ {
+ gfloat natural_height;
+
+ clutter_actor_get_preferred_height (priv->child, -1.0,
+ NULL,
+ &natural_height);
+ if (for_height < natural_height)
+ *natural_width_p += xthickness;
+ }
+
+ /* Add space for padding */
+ if (min_width_p)
+ *min_width_p = padding.left + padding.right;
+
+ if (natural_width_p)
+ *natural_width_p += padding.left + padding.right;
+}
+
+static void
+st_scroll_view_get_preferred_height (ClutterActor *actor,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ StPadding padding;
+ guint ythickness;
+
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+
+ if (!priv->child)
+ return;
+
+ st_widget_get_padding (ST_WIDGET (actor), &padding);
+ st_stylable_get (ST_STYLABLE (actor),
+ "scrollbar-height", &ythickness,
+ NULL);
+
+ /* Our natural height is the natural height of the child */
+ clutter_actor_get_preferred_height (priv->child,
+ for_width,
+ NULL,
+ natural_height_p);
+
+ /* Add space for the scroll-bar if we can determine it will be necessary */
+ if ((for_width >= 0) && natural_height_p)
+ {
+ gfloat natural_width;
+
+ clutter_actor_get_preferred_width (priv->child, -1.0,
+ NULL,
+ &natural_width);
+ if (for_width < natural_width)
+ *natural_height_p += ythickness;
+ }
+
+ /* Add space for padding */
+ if (min_height_p)
+ *min_height_p = padding.top + padding.bottom;
+
+ if (natural_height_p)
+ *natural_height_p += padding.top + padding.bottom;
+}
+
+static void
+st_scroll_view_allocate (ClutterActor *actor,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags)
+{
+ StPadding padding;
+ ClutterActorBox child_box;
+ ClutterActorClass *parent_parent_class;
+ gfloat avail_width, avail_height, sb_width, sb_height;
+
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+
+ /* Chain up to the parent's parent class
+ *
+ * We do this because we do not want StBin to allocate the child, as we
+ * give it a different allocation later, depending on whether the scrollbars
+ * are visible
+ */
+ parent_parent_class
+ = g_type_class_peek_parent (st_scroll_view_parent_class);
+
+ CLUTTER_ACTOR_CLASS (parent_parent_class)->
+ allocate (actor, box, flags);
+
+
+ st_widget_get_padding (ST_WIDGET (actor), &padding);
+
+ avail_width = (box->x2 - box->x1) - padding.left - padding.right;
+ avail_height = (box->y2 - box->y1) - padding.top - padding.bottom;
+
+ st_stylable_get (ST_STYLABLE (actor),
+ "scrollbar-width", &sb_width,
+ "scrollbar-height", &sb_height,
+ NULL);
+ sb_width = 28;
+ sb_height = 28;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
+ sb_width = 0;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
+ sb_height = 0;
+
+ /* Vertical scrollbar */
+ if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
+ {
+ child_box.x1 = avail_width - sb_width;
+ child_box.y1 = padding.top;
+ child_box.x2 = avail_width;
+ child_box.y2 = child_box.y1 + avail_height - sb_height;
+
+ clutter_actor_allocate (priv->vscroll, &child_box, flags);
+ }
+
+ /* Horizontal scrollbar */
+ if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
+ {
+ child_box.x1 = padding.left;
+ child_box.x2 = child_box.x1 + avail_width - sb_width;
+ child_box.y1 = avail_height - sb_height;
+ child_box.y2 = avail_height;
+
+ clutter_actor_allocate (priv->hscroll, &child_box, flags);
+ }
+
+
+ /* Child */
+ child_box.x1 = padding.left;
+ child_box.x2 = avail_width - sb_width;
+ child_box.y1 = padding.top;
+ child_box.y2 = avail_height - sb_height;
+
+ if (priv->child)
+ clutter_actor_allocate (priv->child, &child_box, flags);
+}
+
+static void
+st_scroll_view_style_changed (StWidget *widget)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (widget)->priv;
+
+ st_stylable_changed ((StStylable *) priv->hscroll);
+ st_stylable_changed ((StStylable *) priv->vscroll);
+}
+
+static gboolean
+st_scroll_view_scroll_event (ClutterActor *self,
+ ClutterScrollEvent *event)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv;
+ gdouble lower, value, upper, step;
+ StAdjustment *vadjustment, *hadjustment;
+
+ /* don't handle scroll events if requested not to */
+ if (!priv->mouse_scroll)
+ return FALSE;
+
+ hadjustment = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->hscroll));
+ vadjustment = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->vscroll));
+
+ switch (event->direction)
+ {
+ case CLUTTER_SCROLL_UP:
+ case CLUTTER_SCROLL_DOWN:
+ if (vadjustment)
+ g_object_get (vadjustment,
+ "lower", &lower,
+ "step-increment", &step,
+ "value", &value,
+ "upper", &upper,
+ NULL);
+ else
+ return FALSE;
+ break;
+ case CLUTTER_SCROLL_LEFT:
+ case CLUTTER_SCROLL_RIGHT:
+ if (vadjustment)
+ g_object_get (hadjustment,
+ "lower", &lower,
+ "step-increment", &step,
+ "value", &value,
+ "upper", &upper,
+ NULL);
+ else
+ return FALSE;
+ break;
+ }
+
+ switch (event->direction)
+ {
+ case CLUTTER_SCROLL_UP:
+ if (value == lower)
+ return FALSE;
+ else
+ st_adjustment_set_value (vadjustment, value - step);
+ break;
+ case CLUTTER_SCROLL_DOWN:
+ if (value == upper)
+ return FALSE;
+ else
+ st_adjustment_set_value (vadjustment, value + step);
+ break;
+ case CLUTTER_SCROLL_LEFT:
+ if (value == lower)
+ return FALSE;
+ else
+ st_adjustment_set_value (hadjustment, value - step);
+ break;
+ case CLUTTER_SCROLL_RIGHT:
+ if (value == upper)
+ return FALSE;
+ else
+ st_adjustment_set_value (hadjustment, value + step);
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+st_scroll_view_class_init (StScrollViewClass *klass)
+{
+ GParamSpec *pspec;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (StScrollViewPrivate));
+
+ object_class->get_property = st_scroll_view_get_property;
+ object_class->set_property = st_scroll_view_set_property;
+ object_class->dispose= st_scroll_view_dispose;
+ object_class->finalize = st_scroll_view_finalize;
+
+ actor_class->paint = st_scroll_view_paint;
+ actor_class->pick = st_scroll_view_pick;
+ actor_class->get_preferred_width = st_scroll_view_get_preferred_width;
+ actor_class->get_preferred_height = st_scroll_view_get_preferred_height;
+ actor_class->allocate = st_scroll_view_allocate;
+ actor_class->scroll_event = st_scroll_view_scroll_event;
+
+ g_object_class_install_property (object_class,
+ PROP_HSCROLL,
+ g_param_spec_object ("hscroll",
+ "StScrollBar",
+ "Horizontal scroll indicator",
+ ST_TYPE_SCROLL_BAR,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_VSCROLL,
+ g_param_spec_object ("vscroll",
+ "StScrollBar",
+ "Vertical scroll indicator",
+ ST_TYPE_SCROLL_BAR,
+ G_PARAM_READABLE));
+
+ pspec = g_param_spec_boolean ("enable-mouse-scrolling",
+ "Enable Mouse Scrolling",
+ "Enable automatic mouse wheel scrolling",
+ TRUE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class,
+ PROP_MOUSE_SCROLL,
+ pspec);
+
+}
+
+static void
+st_stylable_iface_init (StStylableIface *iface)
+{
+ static gboolean is_initialized = FALSE;
+
+ if (!is_initialized)
+ {
+ GParamSpec *pspec;
+
+ is_initialized = TRUE;
+
+ pspec = g_param_spec_uint ("scrollbar-width",
+ "Vertical scroll-bar thickness",
+ "Thickness of vertical scrollbar, in px",
+ 0, G_MAXUINT, 24,
+ G_PARAM_READWRITE);
+ st_stylable_iface_install_property (iface, ST_TYPE_SCROLL_VIEW, pspec);
+
+ pspec = g_param_spec_uint ("scrollbar-height",
+ "Horizontal scroll-bar thickness",
+ "Thickness of horizontal scrollbar, in px",
+ 0, G_MAXUINT, 24,
+ G_PARAM_READWRITE);
+ st_stylable_iface_install_property (iface, ST_TYPE_SCROLL_VIEW, pspec);
+ }
+}
+
+static void
+child_adjustment_changed_cb (StAdjustment *adjustment,
+ ClutterActor *bar)
+{
+ StScrollView *scroll;
+ gdouble lower, upper, page_size;
+
+ scroll = ST_SCROLL_VIEW (clutter_actor_get_parent (bar));
+
+ /* Determine if this scroll-bar should be visible */
+ st_adjustment_get_values (adjustment, NULL,
+ &lower, &upper,
+ NULL, NULL,
+ &page_size);
+
+ if ((upper - lower) > page_size)
+ clutter_actor_show (bar);
+ else
+ clutter_actor_hide (bar);
+
+ /* Request a resize */
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (scroll));
+}
+
+static void
+child_hadjustment_notify_cb (GObject *gobject,
+ GParamSpec *arg1,
+ gpointer user_data)
+{
+ StAdjustment *hadjust;
+
+ ClutterActor *actor = CLUTTER_ACTOR (gobject);
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (user_data)->priv;
+
+ hadjust = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->hscroll));
+ if (hadjust)
+ g_signal_handlers_disconnect_by_func (hadjust,
+ child_adjustment_changed_cb,
+ priv->hscroll);
+
+ st_scrollable_get_adjustments (ST_SCROLLABLE (actor), &hadjust, NULL);
+ if (hadjust)
+ {
+ /* Force scroll step if neede. */
+ if (priv->column_size_set)
+ {
+ g_object_set (hadjust,
+ "step-increment", priv->column_size,
+ NULL);
+ }
+
+ st_scroll_bar_set_adjustment (ST_SCROLL_BAR(priv->hscroll), hadjust);
+ g_signal_connect (hadjust, "changed", G_CALLBACK (
+ child_adjustment_changed_cb), priv->hscroll);
+ child_adjustment_changed_cb (hadjust, priv->hscroll);
+ }
+}
+
+static void
+child_vadjustment_notify_cb (GObject *gobject,
+ GParamSpec *arg1,
+ gpointer user_data)
+{
+ StAdjustment *vadjust;
+
+ ClutterActor *actor = CLUTTER_ACTOR (gobject);
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (user_data)->priv;
+
+ vadjust = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->vscroll));
+ if (vadjust)
+ g_signal_handlers_disconnect_by_func (vadjust,
+ child_adjustment_changed_cb,
+ priv->vscroll);
+
+ st_scrollable_get_adjustments (ST_SCROLLABLE(actor), NULL, &vadjust);
+ if (vadjust)
+ {
+ /* Force scroll step if neede. */
+ if (priv->row_size_set)
+ {
+ g_object_set (vadjust,
+ "step-increment", priv->row_size,
+ NULL);
+ }
+
+ st_scroll_bar_set_adjustment (ST_SCROLL_BAR(priv->vscroll), vadjust);
+ g_signal_connect (vadjust, "changed", G_CALLBACK (
+ child_adjustment_changed_cb), priv->vscroll);
+ child_adjustment_changed_cb (vadjust, priv->vscroll);
+ }
+}
+
+static void
+st_scroll_view_init (StScrollView *self)
+{
+ StScrollViewPrivate *priv = self->priv = SCROLL_VIEW_PRIVATE (self);
+
+ priv->hscroll = CLUTTER_ACTOR (st_scroll_bar_new (NULL));
+ priv->vscroll = g_object_new (ST_TYPE_SCROLL_BAR, "vertical", TRUE, NULL);
+
+ clutter_actor_set_parent (priv->hscroll, CLUTTER_ACTOR (self));
+ clutter_actor_set_parent (priv->vscroll, CLUTTER_ACTOR (self));
+
+ /* mouse scroll is enabled by default, so we also need to be reactive */
+ priv->mouse_scroll = TRUE;
+ g_object_set (G_OBJECT (self), "reactive", TRUE, "clip-to-allocation", TRUE,
+ NULL);
+
+ g_signal_connect (self, "style-changed",
+ G_CALLBACK (st_scroll_view_style_changed), NULL);
+}
+
+static void
+st_scroll_view_add (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ StScrollView *self = ST_SCROLL_VIEW (container);
+ StScrollViewPrivate *priv = self->priv;
+
+ if (ST_IS_SCROLLABLE (actor))
+ {
+ priv->child = actor;
+
+ /* chain up to StBin::add() */
+ st_scroll_view_parent_iface->add (container, actor);
+
+ /* Get adjustments for scroll-bars */
+ g_signal_connect (actor, "notify::hadjustment",
+ G_CALLBACK (child_hadjustment_notify_cb),
+ container);
+ g_signal_connect (actor, "notify::vadjustment",
+ G_CALLBACK (child_vadjustment_notify_cb),
+ container);
+ child_hadjustment_notify_cb (G_OBJECT (actor), NULL, container);
+ child_vadjustment_notify_cb (G_OBJECT (actor), NULL, container);
+ }
+ else
+ {
+ g_warning ("Attempting to add an actor of type %s to "
+ "a StScrollView, but the actor does "
+ "not implement StScrollable.",
+ g_type_name (G_OBJECT_TYPE (actor)));
+ }
+}
+
+static void
+st_scroll_view_remove (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (container)->priv;
+
+ if (actor == priv->child)
+ {
+ g_object_ref (priv->child);
+
+ /* chain up to StBin::remove() */
+ st_scroll_view_parent_iface->remove (container, actor);
+
+ g_signal_handlers_disconnect_by_func (priv->child,
+ child_hadjustment_notify_cb,
+ container);
+ g_signal_handlers_disconnect_by_func (priv->child,
+ child_vadjustment_notify_cb,
+ container);
+ st_scrollable_set_adjustments ((StScrollable*) priv->child, NULL, NULL);
+
+ g_object_unref (priv->child);
+ priv->child = NULL;
+ }
+}
+
+static void
+st_scroll_view_foreach_with_internals (ClutterContainer *container,
+ ClutterCallback callback,
+ gpointer user_data)
+{
+ StScrollViewPrivate *priv = ST_SCROLL_VIEW (container)->priv;
+
+ if (priv->child != NULL)
+ callback (priv->child, user_data);
+
+ if (priv->hscroll != NULL)
+ callback (priv->hscroll, user_data);
+
+ if (priv->vscroll != NULL)
+ callback (priv->vscroll, user_data);
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+ /* store a pointer to the StBin implementation of
+ * ClutterContainer so that we can chain up when
+ * overriding the methods
+ */
+ st_scroll_view_parent_iface = g_type_interface_peek_parent (iface);
+
+ iface->add = st_scroll_view_add;
+ iface->remove = st_scroll_view_remove;
+ iface->foreach_with_internals = st_scroll_view_foreach_with_internals;
+}
+
+StWidget *
+st_scroll_view_new (void)
+{
+ return g_object_new (ST_TYPE_SCROLL_VIEW, NULL);
+}
+
+ClutterActor *
+st_scroll_view_get_hscroll_bar (StScrollView *scroll)
+{
+ g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL);
+
+ return scroll->priv->hscroll;
+}
+
+ClutterActor *
+st_scroll_view_get_vscroll_bar (StScrollView *scroll)
+{
+ g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL);
+
+ return scroll->priv->vscroll;
+}
+
+gfloat
+st_scroll_view_get_column_size (StScrollView *scroll)
+{
+ StAdjustment *adjustment;
+ gdouble column_size;
+
+ g_return_val_if_fail (scroll, 0);
+
+ adjustment = st_scroll_bar_get_adjustment (
+ ST_SCROLL_BAR (scroll->priv->hscroll));
+ g_object_get (adjustment,
+ "step-increment", &column_size,
+ NULL);
+
+ return column_size;
+}
+
+void
+st_scroll_view_set_column_size (StScrollView *scroll,
+ gfloat column_size)
+{
+ StAdjustment *adjustment;
+
+ g_return_if_fail (scroll);
+
+ if (column_size < 0)
+ {
+ scroll->priv->column_size_set = FALSE;
+ scroll->priv->column_size = -1;
+ }
+ else
+ {
+ scroll->priv->column_size_set = TRUE;
+ scroll->priv->column_size = column_size;
+
+ adjustment = st_scroll_bar_get_adjustment (
+ ST_SCROLL_BAR (scroll->priv->hscroll));
+
+ if (adjustment)
+ g_object_set (adjustment,
+ "step-increment", (gdouble) scroll->priv->column_size,
+ NULL);
+ }
+}
+
+gfloat
+st_scroll_view_get_row_size (StScrollView *scroll)
+{
+ StAdjustment *adjustment;
+ gdouble row_size;
+
+ g_return_val_if_fail (scroll, 0);
+
+ adjustment = st_scroll_bar_get_adjustment (
+ ST_SCROLL_BAR (scroll->priv->vscroll));
+ g_object_get (adjustment,
+ "step-increment", &row_size,
+ NULL);
+
+ return row_size;
+}
+
+void
+st_scroll_view_set_row_size (StScrollView *scroll,
+ gfloat row_size)
+{
+ StAdjustment *adjustment;
+
+ g_return_if_fail (scroll);
+
+ if (row_size < 0)
+ {
+ scroll->priv->row_size_set = FALSE;
+ scroll->priv->row_size = -1;
+ }
+ else
+ {
+ scroll->priv->row_size_set = TRUE;
+ scroll->priv->row_size = row_size;
+
+ adjustment = st_scroll_bar_get_adjustment (
+ ST_SCROLL_BAR (scroll->priv->vscroll));
+
+ if (adjustment)
+ g_object_set (adjustment,
+ "step-increment", (gdouble) scroll->priv->row_size,
+ NULL);
+ }
+}
+
+void
+st_scroll_view_set_mouse_scrolling (StScrollView *scroll,
+ gboolean enabled)
+{
+ StScrollViewPrivate *priv;
+
+ g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
+
+ priv = ST_SCROLL_VIEW (scroll)->priv;
+
+ if (priv->mouse_scroll != enabled)
+ {
+ priv->mouse_scroll = enabled;
+
+ /* make sure we can receive mouse wheel events */
+ if (enabled)
+ clutter_actor_set_reactive ((ClutterActor *) scroll, TRUE);
+ }
+}
+
+gboolean
+st_scroll_view_get_mouse_scrolling (StScrollView *scroll)
+{
+ StScrollViewPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), FALSE);
+
+ priv = ST_SCROLL_VIEW (scroll)->priv;
+
+ return priv->mouse_scroll;
+}
diff --git a/src/st/st-scroll-view.h b/src/st/st-scroll-view.h
new file mode 100644
index 0000000..c2a3553
--- /dev/null
+++ b/src/st/st-scroll-view.h
@@ -0,0 +1,89 @@
+/*
+ * st-scroll-view.h: Container with scroll-bars
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to St by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_SCROLL_VIEW_H__
+#define __ST_SCROLL_VIEW_H__
+
+#include <st/st-bin.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_SCROLL_VIEW (st_scroll_view_get_type())
+#define ST_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLL_VIEW, StScrollView))
+#define ST_IS_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLL_VIEW))
+#define ST_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SCROLL_VIEW, StScrollViewClass))
+#define ST_IS_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SCROLL_VIEW))
+#define ST_SCROLL_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SCROLL_VIEW, StScrollViewClass))
+
+typedef struct _StScrollView StScrollView;
+typedef struct _StScrollViewPrivate StScrollViewPrivate;
+typedef struct _StScrollViewClass StScrollViewClass;
+
+/**
+ * StScrollView:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _StScrollView
+{
+ /*< private >*/
+ StBin parent_instance;
+
+ StScrollViewPrivate *priv;
+};
+
+struct _StScrollViewClass
+{
+ StBinClass parent_class;
+};
+
+GType st_scroll_view_get_type (void) G_GNUC_CONST;
+
+StWidget *st_scroll_view_new (void);
+
+ClutterActor *st_scroll_view_get_hscroll_bar (StScrollView *scroll);
+ClutterActor *st_scroll_view_get_vscroll_bar (StScrollView *scroll);
+ClutterActor *st_scroll_view_get_child (StScrollView *scroll);
+
+gfloat st_scroll_view_get_column_size (StScrollView *scroll);
+void st_scroll_view_set_column_size (StScrollView *scroll,
+ gfloat column_size);
+
+gfloat st_scroll_view_get_row_size (StScrollView *scroll);
+void st_scroll_view_set_row_size (StScrollView *scroll,
+ gfloat row_size);
+
+void st_scroll_view_set_mouse_scrolling (StScrollView *scroll,
+ gboolean enabled);
+gboolean st_scroll_view_get_mouse_scrolling (StScrollView *scroll);
+
+G_END_DECLS
+
+#endif /* __ST_SCROLL_VIEW_H__ */
diff --git a/src/st/st-scrollable.c b/src/st/st-scrollable.c
new file mode 100644
index 0000000..1c3fc7b
--- /dev/null
+++ b/src/st/st-scrollable.c
@@ -0,0 +1,88 @@
+/*
+ * st-scrollable.c: Scrollable interface
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to St by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#include "st-scrollable.h"
+
+static void
+st_scrollable_base_init (gpointer g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ g_object_interface_install_property (g_iface,
+ g_param_spec_object ("hadjustment",
+ "StAdjustment",
+ "Horizontal adjustment",
+ ST_TYPE_ADJUSTMENT,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property (g_iface,
+ g_param_spec_object ("vadjustment",
+ "StAdjustment",
+ "Vertical adjustment",
+ ST_TYPE_ADJUSTMENT,
+ G_PARAM_READWRITE));
+
+ initialized = TRUE;
+ }
+}
+
+GType
+st_scrollable_get_type (void)
+{
+ static GType type = 0;
+ if (type == 0)
+ {
+ static const GTypeInfo info =
+ {
+ sizeof (StScrollableInterface),
+ st_scrollable_base_init, /* base_init */
+ NULL,
+ };
+ type = g_type_register_static (G_TYPE_INTERFACE,
+ "StScrollable", &info, 0);
+ }
+ return type;
+}
+
+void
+st_scrollable_set_adjustments (StScrollable *scrollable,
+ StAdjustment *hadjustment,
+ StAdjustment *vadjustment)
+{
+ ST_SCROLLABLE_GET_INTERFACE (scrollable)->set_adjustments (scrollable,
+ hadjustment,
+ vadjustment);
+}
+
+void
+st_scrollable_get_adjustments (StScrollable *scrollable,
+ StAdjustment **hadjustment,
+ StAdjustment **vadjustment)
+{
+ ST_SCROLLABLE_GET_INTERFACE (scrollable)->get_adjustments (scrollable,
+ hadjustment,
+ vadjustment);
+}
diff --git a/src/st/st-scrollable.h b/src/st/st-scrollable.h
new file mode 100644
index 0000000..f62d02f
--- /dev/null
+++ b/src/st/st-scrollable.h
@@ -0,0 +1,69 @@
+/*
+ * st-scrollable.h: Scrollable interface
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to St by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_SCROLLABLE_H__
+#define __ST_SCROLLABLE_H__
+
+#include <glib-object.h>
+#include <st/st-adjustment.h>
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_SCROLLABLE (st_scrollable_get_type ())
+#define ST_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLLABLE, StScrollable))
+#define ST_IS_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLLABLE))
+#define ST_SCROLLABLE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), ST_TYPE_SCROLLABLE, StScrollableInterface))
+
+typedef struct _StScrollable StScrollable; /* Dummy object */
+typedef struct _StScrollableInterface StScrollableInterface;
+
+struct _StScrollableInterface
+{
+ GTypeInterface parent;
+
+ void (* set_adjustments) (StScrollable *scrollable,
+ StAdjustment *hadjustment,
+ StAdjustment *vadjustment);
+ void (* get_adjustments) (StScrollable *scrollable,
+ StAdjustment **hadjustment,
+ StAdjustment **vadjustment);
+};
+
+GType st_scrollable_get_type (void) G_GNUC_CONST;
+
+void st_scrollable_set_adjustments (StScrollable *scrollable,
+ StAdjustment *hadjustment,
+ StAdjustment *vadjustment);
+void st_scrollable_get_adjustments (StScrollable *scrollable,
+ StAdjustment **hadjustment,
+ StAdjustment **vadjustment);
+
+G_END_DECLS
+
+#endif /* __ST_SCROLLABLE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]