[libchamplain] Replace tidy with mx
- From: Jiří Techet <jiritechet src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libchamplain] Replace tidy with mx
- Date: Wed, 9 Feb 2011 08:37:49 +0000 (UTC)
commit dcd152746c36e1be8141cb30231d406b280bae63
Author: JiÅ?Ã Techet <techet gmail com>
Date: Sun Jan 23 11:01:12 2011 +0100
Replace tidy with mx
Replace the outdated tidy library with its successor - mx. This will
help us to use fixes and updates from the mainline.
.gitignore | 12 +-
Makefile.am | 2 +-
champlain/Makefile.am | 6 +-
champlain/champlain-layer.c | 2 +-
champlain/champlain-view.c | 61 +-
configure.ac | 2 +-
mx/Makefile.am | 66 ++
mx/mx-adjustment.c | 1233 ++++++++++++++++++++
mx/mx-adjustment.h | 149 +++
mx/mx-bin.c | 715 ++++++++++++
mx/mx-bin.h | 99 ++
mx/mx-draggable.c | 706 +++++++++++
mx/mx-draggable.h | 132 +++
tidy/tidy-enum-types.c.in => mx/mx-enum-types.c.in | 2 +-
tidy/tidy-enum-types.h.in => mx/mx-enum-types.h.in | 8 +-
mx/mx-kinetic-scroll-view.c | 1195 +++++++++++++++++++
mx/mx-kinetic-scroll-view.h | 92 ++
mx/mx-marshal.list | 8 +
tidy/tidy-private.h => mx/mx-private.h | 14 +-
mx/mx-scrollable.c | 99 ++
mx/mx-scrollable.h | 71 ++
mx/mx-types.h | 80 ++
mx/mx-viewport.c | 678 +++++++++++
mx/mx-viewport.h | 91 ++
tidy/Makefile.am | 63 -
tidy/tidy-adjustment.c | 643 ----------
tidy/tidy-adjustment.h | 119 --
tidy/tidy-debug.h | 4 -
tidy/tidy-finger-scroll.c | 695 -----------
tidy/tidy-finger-scroll.h | 75 --
tidy/tidy-marshal.list | 6 -
tidy/tidy-scroll-view.c | 339 ------
tidy/tidy-scroll-view.h | 65 -
tidy/tidy-scrollable.c | 87 --
tidy/tidy-scrollable.h | 63 -
tidy/tidy-viewport.c | 596 ----------
tidy/tidy-viewport.h | 72 --
37 files changed, 5472 insertions(+), 2878 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index f292cda..ac7cd39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,12 +86,12 @@ libtool
ltmain.sh
missing
stamp-h1
-tidy/tidy-enum-types.c
-tidy/tidy-enum-types.h
-tidy/stamp-enum-types
-tidy/tidy-marshal.c
-tidy/tidy-marshal.h
-tidy/stamp-marshal
+mx/mx-enum-types.c
+mx/mx-enum-types.h
+mx/stamp-enum-types
+mx/mx-marshal.c
+mx/mx-marshal.h
+mx/stamp-marshal
bindings/python/update-binding.sh
bindings/python/champlain-gtk/pychamplaingtk.c
bindings/python/champlain/pychamplain.c
diff --git a/Makefile.am b/Makefile.am
index 6b9c72f..bd3492e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = build tidy champlain
+SUBDIRS = build mx champlain
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
diff --git a/champlain/Makefile.am b/champlain/Makefile.am
index 442bbd2..e82c64a 100644
--- a/champlain/Makefile.am
+++ b/champlain/Makefile.am
@@ -111,7 +111,7 @@ nodist_libchamplain_ CHAMPLAIN_API_VERSION@_la_SOURCES = \
$(libchamplain_headers_built) \
$(libchamplain_sources_built)
-libchamplain_ CHAMPLAIN_API_VERSION@_la_LIBADD = $(DEPS_LIBS) $(SOUP_LIBS) $(MEMPHIS_LIBS) $(top_builddir)/tidy/libtidy-1.0.la
+libchamplain_ CHAMPLAIN_API_VERSION@_la_LIBADD = $(DEPS_LIBS) $(SOUP_LIBS) $(MEMPHIS_LIBS) $(top_builddir)/mx/libchamplainmx-1.0.la
libchamplain_ CHAMPLAIN_API_VERSION@_la_LDFLAGS = \
-version-info $(LIBRARY_VERSION)\
@@ -123,7 +123,7 @@ AM_CPPFLAGS = \
$(SOUP_CFLAGS) \
$(MEMPHIS_CFLAGS) \
-DDATADIR=\""$(datadir)"\" \
- -I$(top_srcdir)/tidy \
+ -I$(top_srcdir)/mx \
-I$(top_srcdir) \
-DCHAMPLAIN_COMPILATION \
$(WARN_CFLAGS)
@@ -160,7 +160,7 @@ Champlain_ CHAMPLAIN_API_VERSION_NORM@_gir_FILES = $(introspection_sources)
Champlain_ CHAMPLAIN_API_VERSION_NORM@_gir_INCLUDES = Clutter-1.0 Gtk-3.0 $(memphis_gir_include)
Champlain_ CHAMPLAIN_API_VERSION_NORM@_gir_CFLAGS = \
$(DEPS_CFLAGS) $(SOUP_CFLAGS) $(MEMPHIS_CFLAGS) \
- -I$(top_srcdir)/tidy -I$(top_srcdir) -I$(top_builddir) \
+ -I$(top_srcdir)/mx -I$(top_srcdir) -I$(top_builddir) \
-DCHAMPLAIN_COMPILATION
INTROSPECTION_GIRS += Champlain- CHAMPLAIN_API_VERSION@.gir
diff --git a/champlain/champlain-layer.c b/champlain/champlain-layer.c
index 3e3e3a0..c966136 100644
--- a/champlain/champlain-layer.c
+++ b/champlain/champlain-layer.c
@@ -657,7 +657,7 @@ button_release_cb (G_GNUC_UNUSED ClutterActor *actor,
ChamplainLayer *layer)
{
gboolean found = FALSE;
-printf("FOOOOOO\n");
+
if (clutter_event_get_button (event) != 1)
return FALSE;
diff --git a/champlain/champlain-view.c b/champlain/champlain-view.c
index 6979e8e..275af73 100644
--- a/champlain/champlain-view.c
+++ b/champlain/champlain-view.c
@@ -67,10 +67,10 @@
#include <glib.h>
#include <glib-object.h>
#include <math.h>
-#include <tidy-adjustment.h>
-#include <tidy-finger-scroll.h>
-#include <tidy-scrollable.h>
-#include <tidy-viewport.h>
+#include <mx-kinetic-scroll-view.h>
+#include <mx-viewport.h>
+#include <mx-adjustment.h>
+#include <mx-scrollable.h>
//#define VIEW_LOG
#ifdef VIEW_LOG
@@ -176,6 +176,7 @@ struct _ChamplainViewPrivate
ClutterActor *viewport; /* Contains the map_layer, license and markers */
ClutterActor *map_layer; /* Contains tiles actors (grouped by zoom level) */
ChamplainRectangle viewport_size;
+ ClutterActor *viewport_container;
ClutterActor *user_layers; /* Contains the markers */
@@ -280,7 +281,7 @@ update_viewport (ChamplainView *view,
if (relocate || force)
{
g_signal_handlers_block_by_func (priv->viewport, G_CALLBACK (viewport_pos_changed_cb), view);
- tidy_viewport_set_origin (TIDY_VIEWPORT (priv->viewport),
+ mx_viewport_set_origin (MX_VIEWPORT (priv->viewport),
priv->viewport_size.x,
priv->viewport_size.y,
0);
@@ -305,7 +306,7 @@ update_viewport (ChamplainView *view,
static void
-panning_completed (G_GNUC_UNUSED TidyFingerScroll *scroll,
+panning_completed (G_GNUC_UNUSED MxKineticScrollView *scroll,
ChamplainView *view)
{
DEBUG_LOG ()
@@ -314,7 +315,7 @@ panning_completed (G_GNUC_UNUSED TidyFingerScroll *scroll,
ChamplainFloatPoint absolute;
gfloat x, y;
- tidy_viewport_get_origin (TIDY_VIEWPORT (priv->viewport), &x, &y, NULL);
+ mx_viewport_get_origin (MX_VIEWPORT (priv->viewport), &x, &y, NULL);
absolute.x = x + priv->anchor.x + priv->viewport_size.width / 2.0;
absolute.y = y + priv->anchor.y + priv->viewport_size.height / 2.0;
@@ -393,11 +394,11 @@ resize_viewport (ChamplainView *view)
gdouble lower_y = 0;
gdouble upper_x = G_MAXINT16;
gdouble upper_y = G_MAXINT16;
- TidyAdjustment *hadjust, *vadjust;
+ MxAdjustment *hadjust, *vadjust;
ChamplainViewPrivate *priv = view->priv;
- tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (priv->viewport), &hadjust,
+ mx_scrollable_get_adjustments (MX_SCROLLABLE (priv->viewport), &hadjust,
&vadjust);
if (priv->zoom_level < 8)
@@ -414,7 +415,7 @@ resize_viewport (ChamplainView *view)
/*
* block emmision of signal by priv->viewport with viewport_pos_changed_cb()
- * callback - the signal can be emitted by updating TidyAdjustment, but
+ * callback - the signal can be emitted by updating MxAdjustment, but
* calling the callback now would be a disaster since we don't have updated
* anchor yet
*/
@@ -488,7 +489,7 @@ champlain_view_get_property (GObject *object,
case PROP_DECEL_RATE:
{
gdouble decel = 0.0;
- g_object_get (priv->finger_scroll, "decel-rate", &decel, NULL);
+ g_object_get (priv->finger_scroll, "deceleration", &decel, NULL);
g_value_set_double (value, decel);
break;
}
@@ -640,14 +641,14 @@ champlain_view_dispose (GObject *object)
if (priv->finger_scroll != NULL)
{
- tidy_finger_scroll_stop (TIDY_FINGER_SCROLL (priv->finger_scroll));
+ mx_kinetic_scroll_view_stop (MX_KINETIC_SCROLL_VIEW (priv->finger_scroll));
g_object_unref (priv->finger_scroll);
priv->finger_scroll = NULL;
}
if (priv->viewport != NULL)
{
- tidy_viewport_stop (TIDY_VIEWPORT (priv->viewport));
+// mx_viewport_stop (MX_VIEWPORT (priv->viewport));
g_object_unref (priv->viewport);
priv->viewport = NULL;
}
@@ -1385,8 +1386,21 @@ champlain_view_init (ChamplainView *view)
priv->user_layers = g_object_ref (clutter_group_new ());
clutter_actor_show (priv->user_layers);
+ priv->viewport_container = g_object_ref (clutter_group_new ());
+
+ clutter_container_add_actor (CLUTTER_CONTAINER (priv->viewport_container),
+ priv->map_layer);
+ clutter_container_add_actor (CLUTTER_CONTAINER (priv->viewport_container),
+ priv->polygon_layer);
+ clutter_container_add_actor (CLUTTER_CONTAINER (priv->viewport_container),
+ priv->user_layers);
+
+ clutter_actor_show (priv->viewport_container);
+
/* Setup viewport */
- priv->viewport = g_object_ref (tidy_viewport_new ());
+ priv->viewport = g_object_ref (mx_viewport_new ());
+ mx_bin_set_child (MX_BIN (priv->viewport), priv->viewport_container);
+
g_object_set (G_OBJECT (priv->viewport), "sync-adjustments", FALSE, NULL);
g_signal_connect (priv->viewport, "notify::x-origin",
@@ -1394,18 +1408,11 @@ champlain_view_init (ChamplainView *view)
g_signal_connect (priv->viewport, "notify::y-origin",
G_CALLBACK (viewport_pos_changed_cb), view);
- clutter_container_add_actor (CLUTTER_CONTAINER (priv->viewport),
- priv->map_layer);
- clutter_container_add_actor (CLUTTER_CONTAINER (priv->viewport),
- priv->polygon_layer);
- clutter_container_add_actor (CLUTTER_CONTAINER (priv->viewport),
- priv->user_layers);
-
clutter_actor_raise (priv->polygon_layer, priv->map_layer);
clutter_actor_raise (priv->user_layers, priv->map_layer);
/* Setup finger scroll */
- priv->finger_scroll = g_object_ref (tidy_finger_scroll_new (priv->scroll_mode));
+ priv->finger_scroll = g_object_ref (mx_kinetic_scroll_view_new ());
g_signal_connect (priv->finger_scroll, "scroll-event",
G_CALLBACK (scroll_event), view);
@@ -1455,7 +1462,7 @@ viewport_pos_changed_cb (G_GNUC_UNUSED GObject *gobject,
ChamplainViewPrivate *priv = view->priv;
gfloat x, y;
- tidy_viewport_get_origin (TIDY_VIEWPORT (priv->viewport), &x, &y, NULL);
+ mx_viewport_get_origin (MX_VIEWPORT (priv->viewport), &x, &y, NULL);
if (fabs (x - priv->viewport_size.x) > 100 ||
fabs (y - priv->viewport_size.y) > 100 ||
@@ -2624,7 +2631,7 @@ champlain_view_set_decel_rate (ChamplainView *view,
g_return_if_fail (CHAMPLAIN_IS_VIEW (view) &&
rate < 2.0 && rate > 1.0001);
- g_object_set (view->priv->finger_scroll, "decel-rate", rate, NULL);
+ g_object_set (view->priv->finger_scroll, "deceleration", rate, NULL);
}
@@ -2649,8 +2656,8 @@ champlain_view_set_scroll_mode (ChamplainView *view,
priv->scroll_mode = mode;
- g_object_set (G_OBJECT (priv->finger_scroll), "mode",
- priv->scroll_mode, NULL);
+// g_object_set (G_OBJECT (priv->finger_scroll), "mode",
+// priv->scroll_mode, NULL);
}
@@ -3062,7 +3069,7 @@ champlain_view_get_decel_rate (ChamplainView *view)
g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), 0.0);
gdouble decel = 0.0;
- g_object_get (view->priv->finger_scroll, "decel-rate", &decel, NULL);
+ g_object_get (view->priv->finger_scroll, "deceleration", &decel, NULL);
return decel;
}
diff --git a/configure.ac b/configure.ac
index ca1ea75..d4331c6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -214,7 +214,7 @@ AC_CONFIG_FILES([build/Makefile
champlain/Makefile
champlain/champlain-version.h
demos/Makefile
- tidy/Makefile
+ mx/Makefile
docs/Makefile
docs/reference/Makefile
docs/reference/version.xml
diff --git a/mx/Makefile.am b/mx/Makefile.am
new file mode 100644
index 0000000..1e02d40
--- /dev/null
+++ b/mx/Makefile.am
@@ -0,0 +1,66 @@
+BUILT_SOURCES =
+CLEANFILES =
+DISTCLEANFILES =
+EXTRA_DIST =
+
+
+mx_headers_public = \
+ $(srcdir)/mx-draggable.h \
+ $(srcdir)/mx-viewport.h \
+ $(srcdir)/mx-kinetic-scroll-view.h \
+ $(srcdir)/mx-types.h \
+ $(srcdir)/mx-adjustment.h \
+ $(srcdir)/mx-bin.h \
+ $(srcdir)/mx-scrollable.h \
+ $(srcdir)/mx-private.h
+
+mx_sources = \
+ $(srcdir)/mx-draggable.c \
+ $(srcdir)/mx-viewport.c \
+ $(srcdir)/mx-kinetic-scroll-view.c \
+ $(srcdir)/mx-adjustment.c \
+ $(srcdir)/mx-bin.c \
+ $(srcdir)/mx-scrollable.c
+
+
+# glib-genmarshal rules
+glib_marshal_list = mx-marshal.list
+glib_marshal_prefix = _mx_marshal
+include $(top_srcdir)/build/Makefile.am.marshal
+
+# glib-mkenums rules
+glib_enum_h = mx-enum-types.h
+glib_enum_c = mx-enum-types.c
+glib_enum_headers = $(mx_headers_public)
+include $(top_srcdir)/build/Makefile.am.enums
+
+mx_headers_built = \
+ mx-enum-types.h \
+ mx-marshal.h
+
+mx_sources_built = \
+ mx-enum-types.c \
+ mx-marshal.c
+
+
+libchamplainmx_1_0_la_SOURCES = \
+ $(mx_headers_public) \
+ $(mx_sources)
+
+nodist_libchamplainmx_1_0_la_SOURCES = \
+ $(mx_headers_built) \
+ $(mx_sources_built)
+
+libchamplainmx_1_0_la_LIBADD = $(DEPS_LIBS)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -DPREFIX=\""$(prefix)"\" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DG_DISABLE_DEPRECATED \
+ -DG_LOG_DOMAIN=\"Mx\" \
+ $(DEPS_CFLAGS) \
+ $(MX_DEBUG_CFLAGS) \
+ $(WARN_CFLAGS)
+
+noinst_LTLIBRARIES = libchamplainmx-1.0.la
diff --git a/mx/mx-adjustment.c b/mx/mx-adjustment.c
new file mode 100644
index 0000000..2718d60
--- /dev/null
+++ b/mx/mx-adjustment.c
@@ -0,0 +1,1233 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * mx-adjustment.c: Adjustment object
+ *
+ * Copyright (C) 2008 OpenedHand
+ * Copyright (c) 2009, 2010 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 Mx by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+/**
+ * SECTION:mx-adjustment
+ * @short_description: A GObject representing an adjustable bounded value
+ *
+ * The #MxAdjustment 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 <math.h>
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+#include "mx-adjustment.h"
+#include "mx-marshal.h"
+#include "mx-private.h"
+
+G_DEFINE_TYPE (MxAdjustment, mx_adjustment, G_TYPE_OBJECT)
+
+#define ADJUSTMENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_ADJUSTMENT, MxAdjustmentPrivate))
+
+struct _MxAdjustmentPrivate
+{
+ /* Do not sanity-check values while constructing,
+ * not all properties may be set yet. */
+ guint is_constructing : 1;
+ guint clamp_value : 1;
+ guint elastic : 1;
+
+ gdouble lower;
+ gdouble upper;
+ gdouble value;
+ gdouble step_increment;
+ gdouble page_increment;
+ gdouble page_size;
+
+ /* For signal emission/notification */
+ guint lower_source;
+ guint upper_source;
+ guint value_source;
+ guint step_inc_source;
+ guint page_inc_source;
+ guint page_size_source;
+ guint changed_source;
+
+ /* For interpolation */
+ ClutterTimeline *interpolation;
+ gdouble old_position;
+ gdouble new_position;
+ ClutterAlpha *interpolate_alpha;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_LOWER,
+ PROP_UPPER,
+ PROP_VALUE,
+ PROP_STEP_INC,
+ PROP_PAGE_INC,
+ PROP_PAGE_SIZE,
+
+ PROP_ELASTIC,
+ PROP_CLAMP_VALUE,
+};
+
+enum
+{
+ CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static gboolean _mx_adjustment_set_lower (MxAdjustment *adjustment,
+ gdouble lower);
+static gboolean _mx_adjustment_set_upper (MxAdjustment *adjustment,
+ gdouble upper);
+static gboolean _mx_adjustment_set_step_increment (MxAdjustment *adjustment,
+ gdouble step);
+static gboolean _mx_adjustment_set_page_increment (MxAdjustment *adjustment,
+ gdouble page);
+static gboolean _mx_adjustment_set_page_size (MxAdjustment *adjustment,
+ gdouble size);
+static void mx_adjustment_clamp_page (MxAdjustment *adjustment,
+ gdouble lower,
+ gdouble upper);
+
+static void
+mx_adjustment_constructed (GObject *object)
+{
+ GObjectClass *g_class;
+ MxAdjustment *self = MX_ADJUSTMENT (object);
+
+ g_class = G_OBJECT_CLASS (mx_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 != mx_adjustment_constructed)
+ {
+ g_class->constructed (object);
+ }
+
+ MX_ADJUSTMENT (self)->priv->is_constructing = FALSE;
+ mx_adjustment_clamp_page (self, self->priv->lower, self->priv->upper);
+}
+
+static void
+mx_adjustment_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MxAdjustmentPrivate *priv = MX_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;
+
+ case PROP_CLAMP_VALUE:
+ g_value_set_boolean (value, priv->clamp_value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+mx_adjustment_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MxAdjustment *adj = MX_ADJUSTMENT (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_LOWER:
+ mx_adjustment_set_lower (adj, g_value_get_double (value));
+ break;
+
+ case PROP_UPPER:
+ mx_adjustment_set_upper (adj, g_value_get_double (value));
+ break;
+
+ case PROP_VALUE:
+ mx_adjustment_set_value (adj, g_value_get_double (value));
+ break;
+
+ case PROP_STEP_INC:
+ mx_adjustment_set_step_increment (adj, g_value_get_double (value));
+ break;
+
+ case PROP_PAGE_INC:
+ mx_adjustment_set_page_increment (adj, g_value_get_double (value));
+ break;
+
+ case PROP_PAGE_SIZE:
+ mx_adjustment_set_page_size (adj, g_value_get_double (value));
+ break;
+
+ case PROP_ELASTIC:
+ mx_adjustment_set_elastic (adj, g_value_get_boolean (value));
+ break;
+
+ case PROP_CLAMP_VALUE:
+ mx_adjustment_set_clamp_value (adj, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+stop_interpolation (MxAdjustment *adjustment)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->interpolation)
+ {
+ clutter_timeline_stop (priv->interpolation);
+ g_object_unref (priv->interpolation);
+ priv->interpolation = NULL;
+ }
+}
+
+static void
+mx_adjustment_remove_idle (guint *source)
+{
+ if (*source)
+ {
+ g_source_remove (*source);
+ *source = 0;
+ }
+}
+
+static void
+mx_adjustment_dispose (GObject *object)
+{
+ MxAdjustmentPrivate *priv = MX_ADJUSTMENT (object)->priv;
+
+ stop_interpolation (MX_ADJUSTMENT (object));
+
+ /* Remove idle handlers */
+ mx_adjustment_remove_idle (&priv->value_source);
+ mx_adjustment_remove_idle (&priv->lower_source);
+ mx_adjustment_remove_idle (&priv->upper_source);
+ mx_adjustment_remove_idle (&priv->page_inc_source);
+ mx_adjustment_remove_idle (&priv->step_inc_source);
+ mx_adjustment_remove_idle (&priv->page_size_source);
+ mx_adjustment_remove_idle (&priv->changed_source);
+
+ if (priv->interpolate_alpha)
+ {
+ g_object_unref (priv->interpolate_alpha);
+ priv->interpolate_alpha = NULL;
+ }
+
+ G_OBJECT_CLASS (mx_adjustment_parent_class)->dispose (object);
+}
+
+static void
+mx_adjustment_class_init (MxAdjustmentClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (MxAdjustmentPrivate));
+
+ object_class->constructed = mx_adjustment_constructed;
+ object_class->get_property = mx_adjustment_get_property;
+ object_class->set_property = mx_adjustment_set_property;
+ object_class->dispose = mx_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,
+ MX_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,
+ MX_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,
+ MX_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,
+ MX_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,
+ MX_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,
+ MX_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,
+ MX_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_CLAMP_VALUE,
+ g_param_spec_boolean ("clamp-value",
+ "Clamp value",
+ "Clamp the adjustment "
+ "value between the "
+ "lower and upper "
+ "values, respecting "
+ "the page-size.",
+ TRUE,
+ MX_PARAM_READWRITE));
+
+ /**
+ * MxAdjustment::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 (MxAdjustmentClass, changed),
+ NULL, NULL,
+ _mx_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+mx_adjustment_init (MxAdjustment *self)
+{
+ self->priv = ADJUSTMENT_PRIVATE (self);
+
+ self->priv->is_constructing = TRUE;
+ self->priv->clamp_value = TRUE;
+}
+
+/**
+ * mx_adjustment_new:
+ *
+ * Create a new MxAdjustment
+ *
+ * Returns: a newly allocated MxAdjustment
+ */
+MxAdjustment *
+mx_adjustment_new (void)
+{
+ return g_object_new (MX_TYPE_ADJUSTMENT, NULL);
+}
+
+/**
+ * mx_adjustment_new_with_values:
+ * @value: A #gdouble
+ * @lower: A #gdouble
+ * @upper: A #gdouble
+ * @step_increment: A #gdouble
+ * @page_increment: A #gdouble
+ * @page_size: A #gdouble
+ *
+ * Create a new MxAdjustment with the properties set to the values specified.
+ *
+ * Returns: a newly allocated MxAdjustment
+ */
+MxAdjustment *
+mx_adjustment_new_with_values (gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size)
+{
+ return g_object_new (MX_TYPE_ADJUSTMENT,
+ "value", value,
+ "lower", lower,
+ "upper", upper,
+ "step-increment", step_increment,
+ "page-increment", page_increment,
+ "page-size", page_size,
+ NULL);
+}
+
+/**
+ * mx_adjustment_get_value:
+ * @adjustment: An #MxAdjustment
+ *
+ * Get the current value of the #MxAdjustment:value property
+ *
+ * Returns: the current value of the "value" property
+ */
+gdouble
+mx_adjustment_get_value (MxAdjustment *adjustment)
+{
+ MxAdjustmentPrivate *priv;
+
+ g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0);
+
+ priv = adjustment->priv;
+
+ return priv->value;
+}
+
+static gboolean
+mx_adjustment_value_notify_cb (MxAdjustment *adjustment)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ priv->value_source = 0;
+
+ g_object_notify (G_OBJECT (adjustment), "value");
+
+ return FALSE;
+}
+
+static gboolean
+mx_adjustment_lower_notify_cb (MxAdjustment *adjustment)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ priv->lower_source = 0;
+ g_object_notify (G_OBJECT (adjustment), "lower");
+
+ return FALSE;
+}
+
+static gboolean
+mx_adjustment_upper_notify_cb (MxAdjustment *adjustment)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ priv->upper_source = 0;
+ g_object_notify (G_OBJECT (adjustment), "upper");
+
+ return FALSE;
+}
+
+static gboolean
+mx_adjustment_step_inc_notify_cb (MxAdjustment *adjustment)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ priv->step_inc_source = 0;
+ g_object_notify (G_OBJECT (adjustment), "step-increment");
+
+ return FALSE;
+}
+
+static gboolean
+mx_adjustment_page_inc_notify_cb (MxAdjustment *adjustment)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ priv->page_inc_source = 0;
+ g_object_notify (G_OBJECT (adjustment), "page-increment");
+
+ return FALSE;
+}
+
+static gboolean
+mx_adjustment_page_size_notify_cb (MxAdjustment *adjustment)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ priv->page_size_source = 0;
+ g_object_notify (G_OBJECT (adjustment), "page-size");
+
+ return FALSE;
+}
+
+static gboolean
+mx_adjustment_emit_changed_cb (MxAdjustment *adjustment)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ priv->changed_source = 0;
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ return FALSE;
+}
+
+/**
+ * mx_adjustment_set_value:
+ * @adjustment: An #MxAdjustment
+ * @value: A #gdouble
+ *
+ * Set the value of the #MxAdjustment:value property.
+ *
+ */
+void
+mx_adjustment_set_value (MxAdjustment *adjustment,
+ gdouble value)
+{
+ MxAdjustmentPrivate *priv;
+
+ g_return_if_fail (MX_IS_ADJUSTMENT (adjustment));
+
+ priv = adjustment->priv;
+
+ /* Defer clamp until after construction. */
+ if (!priv->is_constructing)
+ {
+ if (!priv->elastic && priv->clamp_value)
+ value = CLAMP (value,
+ priv->lower,
+ MAX (priv->lower, priv->upper - priv->page_size));
+ }
+
+ if (priv->value != value)
+ {
+ stop_interpolation (adjustment);
+
+ priv->value = value;
+
+ g_object_notify (G_OBJECT (adjustment), "value");
+ }
+}
+
+static void
+mx_adjustment_clamp_page (MxAdjustment *adjustment,
+ gdouble lower,
+ gdouble upper)
+{
+ MxAdjustmentPrivate *priv;
+ gboolean changed;
+
+ g_return_if_fail (MX_IS_ADJUSTMENT (adjustment));
+
+ priv = adjustment->priv;
+
+ 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 && !priv->value_source)
+ priv->value_source =
+ g_idle_add_full (CLUTTER_PRIORITY_REDRAW,
+ (GSourceFunc)mx_adjustment_value_notify_cb,
+ adjustment,
+ NULL);
+}
+
+static void
+mx_adjustment_emit_changed (MxAdjustment *adjustment)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ if (!priv->changed_source)
+ priv->changed_source =
+ g_idle_add_full (CLUTTER_PRIORITY_REDRAW,
+ (GSourceFunc)mx_adjustment_emit_changed_cb,
+ adjustment,
+ NULL);
+}
+
+static gboolean
+_mx_adjustment_set_lower (MxAdjustment *adjustment,
+ gdouble lower)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->lower != lower)
+ {
+ priv->lower = lower;
+
+ mx_adjustment_emit_changed (adjustment);
+
+ if (!priv->lower_source)
+ priv->lower_source =
+ g_idle_add_full (CLUTTER_PRIORITY_REDRAW,
+ (GSourceFunc)mx_adjustment_lower_notify_cb,
+ adjustment,
+ NULL);
+
+ /* Defer clamp until after construction. */
+ if (!priv->is_constructing && priv->clamp_value)
+ mx_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * mx_adjustment_set_lower:
+ * @adjustment: A #MxAdjustment
+ * @lower: A #gdouble
+ *
+ * Set the value of the #MxAdjustment:lower property.
+ *
+ */
+void
+mx_adjustment_set_lower (MxAdjustment *adjustment,
+ gdouble lower)
+{
+ _mx_adjustment_set_lower (adjustment, lower);
+}
+
+/**
+ * mx_adjustment_get_lower:
+ * @adjustment: A #MxAdjustment
+ *
+ * Get the value of the #MxAdjustment:lower property.
+ *
+ * Returns: the current value of the "lower" property.
+ */
+gdouble
+mx_adjustment_get_lower (MxAdjustment *adjustment)
+{
+ g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0.0);
+ return adjustment->priv->lower;
+}
+
+static gboolean
+_mx_adjustment_set_upper (MxAdjustment *adjustment,
+ gdouble upper)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->upper != upper)
+ {
+ priv->upper = upper;
+
+ mx_adjustment_emit_changed (adjustment);
+
+ if (!priv->upper_source)
+ priv->upper_source =
+ g_idle_add_full (CLUTTER_PRIORITY_REDRAW,
+ (GSourceFunc)mx_adjustment_upper_notify_cb,
+ adjustment,
+ NULL);
+
+ /* Defer clamp until after construction. */
+ if (!priv->is_constructing && priv->clamp_value)
+ mx_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * mx_adjustment_set_upper:
+ * @adjustment: A #MxAdjustment
+ * @upper: A #gdouble
+ *
+ * Set the value of the #MxAdjustment:upper property.
+ *
+ */
+void
+mx_adjustment_set_upper (MxAdjustment *adjustment,
+ gdouble upper)
+{
+ _mx_adjustment_set_upper (adjustment, upper);
+}
+
+/**
+ * mx_adjustment_get_upper:
+ * @adjustment: A #MxAdjustment
+ *
+ * Get the value of the #MxAdjustment:upper property.
+ *
+ * Returns: the current value of the "upper" property.
+ */
+gdouble
+mx_adjustment_get_upper (MxAdjustment *adjustment)
+{
+ g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0.0);
+ return adjustment->priv->upper;
+}
+
+static gboolean
+_mx_adjustment_set_step_increment (MxAdjustment *adjustment,
+ gdouble step)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->step_increment != step)
+ {
+ priv->step_increment = step;
+
+ mx_adjustment_emit_changed (adjustment);
+
+ if (!priv->step_inc_source)
+ priv->step_inc_source =
+ g_idle_add_full (CLUTTER_PRIORITY_REDRAW,
+ (GSourceFunc)mx_adjustment_step_inc_notify_cb,
+ adjustment,
+ NULL);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * mx_adjustment_set_step_increment:
+ * @adjustment: A #MxAdjustment
+ * @increment: A #gdouble
+ *
+ * Set the value of the #MxAdjustment:step-increment property.
+ *
+ */
+void
+mx_adjustment_set_step_increment (MxAdjustment *adjustment,
+ gdouble increment)
+{
+ _mx_adjustment_set_step_increment (adjustment, increment);
+}
+
+/**
+ * mx_adjustment_get_step_increment:
+ * @adjustment: A #MxAdjustment
+ *
+ * Get the value of the MxAdjustment:step-increment property.
+ *
+ * Returns: the current value of the "step-increment" property.
+ */
+gdouble
+mx_adjustment_get_step_increment (MxAdjustment *adjustment)
+{
+ g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0.0);
+ return adjustment->priv->step_increment;
+}
+
+static gboolean
+_mx_adjustment_set_page_increment (MxAdjustment *adjustment,
+ gdouble page)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->page_increment != page)
+ {
+ priv->page_increment = page;
+
+ mx_adjustment_emit_changed (adjustment);
+
+ if (!priv->page_inc_source)
+ priv->page_inc_source =
+ g_idle_add_full (CLUTTER_PRIORITY_REDRAW,
+ (GSourceFunc)mx_adjustment_page_inc_notify_cb,
+ adjustment,
+ NULL);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * mx_adjustment_set_page_increment:
+ * @adjustment: A #MxAdjustment
+ * @increment: A #gdouble
+ *
+ * Set the value of the #MxAdjustment:page-increment property.
+ *
+ */
+void
+mx_adjustment_set_page_increment (MxAdjustment *adjustment,
+ gdouble increment)
+{
+ _mx_adjustment_set_page_increment (adjustment, increment);
+}
+
+/**
+ * mx_adjustment_get_page_increment:
+ * @adjustment: A #MxAdjustment
+ *
+ * Get the value of the MxAdjustment:page-increment property.
+ *
+ * Returns: the current value of the "page-increment" property.
+ */
+gdouble
+mx_adjustment_get_page_increment (MxAdjustment *adjustment)
+{
+ g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0.0);
+ return adjustment->priv->page_increment;
+}
+
+static gboolean
+_mx_adjustment_set_page_size (MxAdjustment *adjustment,
+ gdouble size)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->page_size != size)
+ {
+ priv->page_size = size;
+
+ mx_adjustment_emit_changed (adjustment);
+
+ if (!priv->page_size_source)
+ priv->page_size_source =
+ g_idle_add_full (CLUTTER_PRIORITY_REDRAW,
+ (GSourceFunc)mx_adjustment_page_size_notify_cb,
+ adjustment,
+ NULL);
+
+ /* Well explicitely clamp after construction. */
+ if (!priv->is_constructing && priv->clamp_value)
+ mx_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * mx_adjustment_set_page_size:
+ * @adjustment: A #MxAdjustment
+ * @page_size: A #gdouble
+ *
+ * Set the #MxAdjustment:page-size property.
+ *
+ */
+void
+mx_adjustment_set_page_size (MxAdjustment *adjustment,
+ gdouble page_size)
+{
+ _mx_adjustment_set_page_size (adjustment, page_size);
+}
+
+/**
+ * mx_adjustment_get_page_size:
+ * @adjustment: A #MxAdjustment
+ *
+ * Get the value of the #MxAdjustment:page-size property.
+ *
+ * Returns: the current value of the "page-size" property.
+ */
+gdouble
+mx_adjustment_get_page_size (MxAdjustment *adjustment)
+{
+ g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0.0);
+ return adjustment->priv->page_size;
+}
+
+/**
+ * mx_adjustment_set_values:
+ * @adjustment: A #MxAdjustment
+ * @value: A #gdouble
+ * @lower: A #gdouble
+ * @upper: A #gdouble
+ * @step_increment: A #gdouble
+ * @page_increment: A #gdouble
+ * @page_size: A #gdouble
+ *
+ * Set the various properties of MxAdjustment.
+ *
+ */
+void
+mx_adjustment_set_values (MxAdjustment *adjustment,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size)
+{
+ MxAdjustmentPrivate *priv;
+ gboolean emit_changed = FALSE;
+
+ g_return_if_fail (MX_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;
+
+ emit_changed = FALSE;
+
+ g_object_freeze_notify (G_OBJECT (adjustment));
+
+ emit_changed |= _mx_adjustment_set_lower (adjustment, lower);
+ emit_changed |= _mx_adjustment_set_upper (adjustment, upper);
+ emit_changed |= _mx_adjustment_set_step_increment (adjustment,
+ step_increment);
+ emit_changed |= _mx_adjustment_set_page_increment (adjustment,
+ page_increment);
+ emit_changed |= _mx_adjustment_set_page_size (adjustment, page_size);
+
+ if (value != priv->value)
+ {
+ mx_adjustment_set_value (adjustment, value);
+ emit_changed = TRUE;
+ }
+
+ if (emit_changed)
+ mx_adjustment_emit_changed (adjustment);
+
+ g_object_thaw_notify (G_OBJECT (adjustment));
+}
+
+/**
+ * mx_adjustment_get_values:
+ * @adjustment: A #MxAdjustment
+ * @value: A #gdouble
+ * @lower: A #gdouble
+ * @upper: A #gdouble
+ * @step_increment: A #gdouble
+ * @page_increment: A #gdouble
+ * @page_size: A #gdouble
+ *
+ * Get the various properties of MxAdjustment.
+ *
+ */
+void
+mx_adjustment_get_values (MxAdjustment *adjustment,
+ gdouble *value,
+ gdouble *lower,
+ gdouble *upper,
+ gdouble *step_increment,
+ gdouble *page_increment,
+ gdouble *page_size)
+{
+ MxAdjustmentPrivate *priv;
+
+ g_return_if_fail (MX_IS_ADJUSTMENT (adjustment));
+
+ priv = adjustment->priv;
+
+ if (lower)
+ *lower = priv->lower;
+
+ if (upper)
+ *upper = priv->upper;
+
+ if (value)
+ *value = mx_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,
+ MxAdjustment *adjustment)
+{
+ gdouble new_value;
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ priv->interpolation = NULL;
+
+ new_value = priv->old_position +
+ (priv->new_position - priv->old_position) *
+ clutter_alpha_get_alpha (priv->interpolate_alpha);
+
+ mx_adjustment_set_value (adjustment, new_value);
+ priv->interpolation = timeline;
+
+ /* Stop the interpolation if we've reached the end of the adjustment */
+ if (!priv->elastic && priv->clamp_value &&
+ ((new_value < priv->lower) ||
+ (new_value >= (priv->upper - priv->page_size))))
+ stop_interpolation (adjustment);
+}
+
+static void
+interpolation_completed_cb (ClutterTimeline *timeline,
+ MxAdjustment *adjustment)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->elastic && priv->clamp_value)
+ {
+ if (clutter_timeline_get_direction (priv->interpolation) ==
+ CLUTTER_TIMELINE_FORWARD)
+ {
+ clutter_timeline_set_direction (priv->interpolation,
+ CLUTTER_TIMELINE_BACKWARD);
+ clutter_timeline_set_duration (priv->interpolation, 250);
+ clutter_timeline_rewind (priv->interpolation);
+
+ if (priv->new_position < priv->lower)
+ {
+ priv->old_position = priv->lower;
+ clutter_timeline_start (priv->interpolation);
+ }
+ else if (priv->new_position > (priv->upper - priv->page_size))
+ {
+ priv->old_position = priv->upper - priv->page_size;
+ clutter_timeline_start (priv->interpolation);
+ }
+ }
+ else
+ {
+ stop_interpolation (adjustment);
+ mx_adjustment_set_value (adjustment, priv->old_position);
+ }
+ }
+ else
+ {
+ stop_interpolation (adjustment);
+ mx_adjustment_set_value (adjustment, priv->new_position);
+ }
+}
+
+/**
+ * mx_adjustment_interpolate:
+ * @adjustment: A #MxAdjustment
+ * @value: A #gdouble
+ * @duration: duration in milliseconds
+ * @mode: A #ClutterAnimationMode
+ *
+ * Interpolate #MxAdjustment:value to the new value specified by @value, using
+ * the mode and duration given.
+ */
+void
+mx_adjustment_interpolate (MxAdjustment *adjustment,
+ gdouble value,
+ guint duration,
+ gulong mode)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ g_return_if_fail (isfinite (value));
+
+ if (duration <= 1)
+ {
+ stop_interpolation (adjustment);
+ mx_adjustment_set_value (adjustment, value);
+ return;
+ }
+
+ priv->old_position = priv->value;
+ priv->new_position = value;
+
+ if (!priv->interpolation)
+ {
+ priv->interpolation = clutter_timeline_new (duration);
+
+ 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);
+ }
+ else
+ {
+ /* Extend the animation if it gets interrupted, otherwise frequent calls
+ * to this function will end up with no advancements until the calls
+ * finish (as the animation never gets a chance to start).
+ */
+ clutter_timeline_set_direction (priv->interpolation,
+ CLUTTER_TIMELINE_FORWARD);
+ clutter_timeline_rewind (priv->interpolation);
+ clutter_timeline_set_duration (priv->interpolation, duration);
+ }
+
+ if (priv->interpolate_alpha)
+ g_object_unref (priv->interpolate_alpha);
+
+ priv->interpolate_alpha = clutter_alpha_new_full (priv->interpolation,
+ mode);
+
+ clutter_timeline_start (priv->interpolation);
+}
+
+/**
+ * mx_adjustment_interpolate_relative:
+ * @adjustment: A #MxAdjustment
+ * @offset: A #gdouble
+ * @duration: duration in milliseconds
+ * @mode: A #ClutterAnimationMode
+ *
+ * Interpolate the value of #MxAdjustment:value to a new value calculated from
+ * @offset.
+ *
+ */
+void
+mx_adjustment_interpolate_relative (MxAdjustment *adjustment,
+ gdouble offset,
+ guint duration,
+ gulong mode)
+{
+ MxAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->interpolation)
+ offset += priv->new_position;
+ else
+ offset += priv->value;
+
+ mx_adjustment_interpolate (adjustment,
+ offset,
+ duration,
+ mode);
+}
+
+/**
+ * mx_adjustment_get_elastic:
+ * @adjustment: A #MxAdjustment
+ *
+ * Get the value of the #MxAdjustment:elastic property.
+ *
+ * Returns: the current value of the "elastic" property.
+ */
+gboolean
+mx_adjustment_get_elastic (MxAdjustment *adjustment)
+{
+ return adjustment->priv->elastic;
+}
+
+/**
+ * mx_adjustment_set_elastic:
+ * @adjustment: A #MxAdjustment
+ * @elastic: A #gboolean
+ *
+ * Set the value of the #MxAdjustment:elastic property.
+ *
+ */
+void
+mx_adjustment_set_elastic (MxAdjustment *adjustment,
+ gboolean elastic)
+{
+ adjustment->priv->elastic = elastic;
+}
+
+/**
+ * mx_adjustment_get_clamp_value:
+ * @adjustment: A #MxAdjustment
+ *
+ * Get the value of the #MxAdjustment:clamp-value property.
+ *
+ * Returns: the current value of the "clamp-value" property.
+ */
+gboolean
+mx_adjustment_get_clamp_value (MxAdjustment *adjustment)
+{
+ return adjustment->priv->clamp_value;
+}
+
+/**
+ * mx_adjustment_set_clamp_value:
+ * @adjustment: A #MxAdjustment
+ * @clamp: a #gboolean
+ *
+ * Set the value of the #MxAdjustment:clamp-value property.
+ */
+void
+mx_adjustment_set_clamp_value (MxAdjustment *adjustment,
+ gboolean clamp)
+{
+ adjustment->priv->clamp_value = clamp;
+}
+
diff --git a/mx/mx-adjustment.h b/mx/mx-adjustment.h
new file mode 100644
index 0000000..0ae7153
--- /dev/null
+++ b/mx/mx-adjustment.h
@@ -0,0 +1,149 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * mx-adjustment.h: Adjustment object
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009, 2010 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 Mx by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#ifndef __MX_ADJUSTMENT_H__
+#define __MX_ADJUSTMENT_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define MX_TYPE_ADJUSTMENT (mx_adjustment_get_type())
+#define MX_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_ADJUSTMENT, MxAdjustment))
+#define MX_IS_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_ADJUSTMENT))
+#define MX_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_ADJUSTMENT, MxAdjustmentClass))
+#define MX_IS_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_ADJUSTMENT))
+#define MX_ADJUSTMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_ADJUSTMENT, MxAdjustmentClass))
+
+typedef struct _MxAdjustment MxAdjustment;
+typedef struct _MxAdjustmentPrivate MxAdjustmentPrivate;
+typedef struct _MxAdjustmentClass MxAdjustmentClass;
+
+/**
+ * MxAdjustment:
+ *
+ * Class for handling an interval between to values. The contents of
+ * the #MxAdjustment are private and should be accessed using the
+ * public API.
+ */
+struct _MxAdjustment
+{
+ /*< private >*/
+ GObject parent_instance;
+
+ MxAdjustmentPrivate *priv;
+};
+
+/**
+ * MxAdjustmentClass
+ * @changed: Class handler for the ::changed signal.
+ *
+ * Base class for #MxAdjustment.
+ */
+struct _MxAdjustmentClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ /*< public >*/
+ void (* changed) (MxAdjustment *adjustment);
+
+ /* padding for future expansion */
+ void (*_padding_0) (void);
+ void (*_padding_1) (void);
+ void (*_padding_2) (void);
+ void (*_padding_3) (void);
+ void (*_padding_4) (void);
+};
+
+GType mx_adjustment_get_type (void) G_GNUC_CONST;
+
+MxAdjustment *mx_adjustment_new (void);
+MxAdjustment *mx_adjustment_new_with_values (gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size);
+gdouble mx_adjustment_get_value (MxAdjustment *adjustment);
+void mx_adjustment_set_value (MxAdjustment *adjustment,
+ gdouble value);
+
+gdouble mx_adjustment_get_lower (MxAdjustment *adjustment);
+void mx_adjustment_set_lower (MxAdjustment *adjustment,
+ gdouble lower);
+
+gdouble mx_adjustment_get_upper (MxAdjustment *adjustment);
+void mx_adjustment_set_upper (MxAdjustment *adjustment,
+ gdouble upper);
+
+gdouble mx_adjustment_get_step_increment (MxAdjustment *adjustment);
+void mx_adjustment_set_step_increment (MxAdjustment *adjustment,
+ gdouble increment);
+
+gdouble mx_adjustment_get_page_increment (MxAdjustment *adjustment);
+void mx_adjustment_set_page_increment (MxAdjustment *adjustment,
+ gdouble increment);
+
+gdouble mx_adjustment_get_page_size (MxAdjustment *adjustment);
+void mx_adjustment_set_page_size (MxAdjustment *adjustment,
+ gdouble page_size);
+
+void mx_adjustment_set_values (MxAdjustment *adjustment,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size);
+void mx_adjustment_get_values (MxAdjustment *adjustment,
+ gdouble *value,
+ gdouble *lower,
+ gdouble *upper,
+ gdouble *step_increment,
+ gdouble *page_increment,
+ gdouble *page_size);
+
+void mx_adjustment_interpolate (MxAdjustment *adjustment,
+ gdouble value,
+ guint duration,
+ gulong mode);
+void mx_adjustment_interpolate_relative (MxAdjustment *adjustment,
+ gdouble offset,
+ guint duration,
+ gulong mode);
+
+gboolean mx_adjustment_get_elastic (MxAdjustment *adjustment);
+void mx_adjustment_set_elastic (MxAdjustment *adjustment,
+ gboolean elastic);
+
+gboolean mx_adjustment_get_clamp_value (MxAdjustment *adjustment);
+void mx_adjustment_set_clamp_value (MxAdjustment *adjustment,
+ gboolean clamp);
+
+G_END_DECLS
+
+#endif /* __MX_ADJUSTMENT_H__ */
diff --git a/mx/mx-bin.c b/mx/mx-bin.c
new file mode 100644
index 0000000..3d16631
--- /dev/null
+++ b/mx/mx-bin.c
@@ -0,0 +1,715 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * mx-bin.c: Basic container actor
+ *
+ * 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: Emmanuele Bassi <ebassi linux intel com>
+ *
+ */
+
+/**
+ * SECTION:mx-bin
+ * @short_description: a simple container with one actor.
+ *
+ * #MxBin is a simple abstract container capable of having only one
+ * #ClutterActor as a child. #MxBin does not allocate the child itself,
+ * therefore any subclasses are required to implement the
+ * #ClutterActorClass.allocate function.
+ * #mx_bin_allocate_child() can be used if no special allocation requirements
+ * are needed.
+ *
+ * #MxFrame is a simple implementation of #MxBin that can be used as a single
+ * actor container that implements alignment and padding.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <clutter/clutter.h>
+
+#include "mx-bin.h"
+#include "mx-enum-types.h"
+#include "mx-private.h"
+//#include "mx-focusable.h"
+
+
+#define MX_BIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_BIN, MxBinPrivate))
+
+struct _MxBinPrivate
+{
+ ClutterActor *child;
+ gboolean child_has_space;
+
+ MxAlign x_align;
+ MxAlign y_align;
+
+ guint x_fill : 1;
+ guint y_fill : 1;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_CHILD,
+ PROP_X_ALIGN,
+ PROP_Y_ALIGN,
+ PROP_X_FILL,
+ PROP_Y_FILL
+};
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MxBin, mx_bin, CLUTTER_TYPE_GROUP,
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+ clutter_container_iface_init));
+
+/*
+void
+_mx_bin_get_align_factors (MxBin *bin,
+ gdouble *x_align,
+ gdouble *y_align)
+{
+ MxBinPrivate *priv = bin->priv;
+ gdouble factor;
+
+ switch (priv->x_align)
+ {
+ case MX_ALIGN_START:
+ factor = 0.0;
+ break;
+
+ case MX_ALIGN_MIDDLE:
+ factor = 0.5;
+ break;
+
+ case MX_ALIGN_END:
+ factor = 1.0;
+ break;
+
+ default:
+ factor = 0.0;
+ break;
+ }
+
+ if (x_align)
+ *x_align = factor;
+
+ switch (priv->y_align)
+ {
+ case MX_ALIGN_START:
+ factor = 0.0;
+ break;
+
+ case MX_ALIGN_MIDDLE:
+ factor = 0.5;
+ break;
+
+ case MX_ALIGN_END:
+ factor = 1.0;
+ break;
+
+ default:
+ factor = 0.0;
+ break;
+ }
+
+ if (y_align)
+ *y_align = factor;
+}
+*/
+
+static void
+mx_bin_add (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ mx_bin_set_child (MX_BIN (container), actor);
+}
+
+static void
+mx_bin_remove (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ MxBinPrivate *priv = MX_BIN (container)->priv;
+
+ if (priv->child == actor)
+ mx_bin_set_child (MX_BIN (container), NULL);
+}
+
+static void
+mx_bin_foreach (ClutterContainer *container,
+ ClutterCallback callback,
+ gpointer user_data)
+{
+ MxBinPrivate *priv = MX_BIN (container)->priv;
+
+ if (priv->child)
+ callback (priv->child, user_data);
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+ iface->add = mx_bin_add;
+ iface->remove = mx_bin_remove;
+ iface->foreach = mx_bin_foreach;
+}
+
+static void
+mx_bin_paint (ClutterActor *self)
+{
+ MxBinPrivate *priv = MX_BIN (self)->priv;
+
+ /* allow MxWidget to paint the background */
+ CLUTTER_ACTOR_CLASS (mx_bin_parent_class)->paint (self);
+
+ /* then paint our child */
+ if (priv->child && priv->child_has_space)
+ clutter_actor_paint (priv->child);
+}
+
+static void
+mx_bin_pick (ClutterActor *self,
+ const ClutterColor *pick_color)
+{
+ MxBinPrivate *priv = MX_BIN (self)->priv;
+
+ /* get the default pick implementation */
+ CLUTTER_ACTOR_CLASS (mx_bin_parent_class)->pick (self, pick_color);
+
+ if (priv->child)
+ clutter_actor_paint (priv->child);
+}
+
+/**
+ * mx_bin_allocate_child:
+ * @bin: An #MxBin
+ * @box: The box to allocate the child within
+ * @flags: #ClutterAllocationFlags, usually provided by the.
+ * clutter_actor_allocate function.
+ *
+ * Allocates the child of an #MxBin inside the given box. This function should
+ * usually only be called by subclasses of #MxBin.
+ *
+ * This function can be used to allocate the child of an #MxBin if no special
+ * allocation requirements are needed. It is similar to
+ * #mx_allocate_align_fill, except that it reads the alignment, padding and
+ * fill values from the #MxBin, and will call #clutter_actor_allocate on the
+ * child.
+ *
+ */
+void
+mx_bin_allocate_child (MxBin *bin,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags)
+{
+ MxBinPrivate *priv;
+
+ g_return_if_fail (MX_IS_BIN (bin));
+
+ priv = bin->priv;
+
+ if (priv->child)
+ {
+ MxPadding padding = { 0, };
+ ClutterActorBox allocation = { 0, };
+
+// mx_widget_get_padding (MX_WIDGET (bin), &padding);
+
+ allocation.x1 = padding.left;
+ allocation.x2 = box->x2 - box->x1 - padding.right;
+ allocation.y1 = padding.top;
+ allocation.y2 = box->y2 - box->y1 - padding.bottom;
+
+/* mx_allocate_align_fill (priv->child,
+ &allocation,
+ priv->x_align,
+ priv->y_align,
+ priv->x_fill,
+ priv->y_fill);*/
+
+ clutter_actor_allocate (priv->child, &allocation, flags);
+ }
+}
+
+static void
+mx_bin_get_preferred_width (ClutterActor *self,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ MxBinPrivate *priv = MX_BIN (self)->priv;
+ gfloat min_width, natural_width;
+ gfloat available_height;
+ MxPadding padding = { 0, };
+
+// mx_widget_get_padding (MX_WIDGET (self), &padding);
+
+ available_height = for_height - padding.top - padding.bottom;
+
+ min_width = natural_width = padding.left + padding.right;
+
+ if (priv->child == NULL)
+ {
+ if (min_width_p)
+ *min_width_p = min_width;
+
+ if (natural_width_p)
+ *natural_width_p = natural_width;
+ }
+ else
+ {
+ clutter_actor_get_preferred_width (priv->child, available_height,
+ min_width_p,
+ natural_width_p);
+
+ if (min_width_p)
+ *min_width_p += min_width;
+
+ if (natural_width_p)
+ *natural_width_p += natural_width;
+ }
+}
+
+static void
+mx_bin_get_preferred_height (ClutterActor *self,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ MxBinPrivate *priv = MX_BIN (self)->priv;
+ gfloat min_height, natural_height;
+ gfloat available_width;
+ MxPadding padding = { 0, };
+
+// mx_widget_get_padding (MX_WIDGET (self), &padding);
+
+ available_width = for_width - padding.left - padding.right;
+
+ min_height = natural_height = padding.top + padding.bottom;
+
+ if (priv->child == NULL)
+ {
+ if (min_height_p)
+ *min_height_p = min_height;
+
+ if (natural_height_p)
+ *natural_height_p = natural_height;
+ }
+ else
+ {
+ clutter_actor_get_preferred_height (priv->child, available_width,
+ min_height_p,
+ natural_height_p);
+
+ if (min_height_p)
+ *min_height_p += min_height;
+
+ if (natural_height_p)
+ *natural_height_p += natural_height;
+ }
+}
+
+static void
+mx_bin_dispose (GObject *gobject)
+{
+ MxBinPrivate *priv = MX_BIN (gobject)->priv;
+
+ if (priv->child)
+ {
+ clutter_actor_destroy (priv->child);
+ priv->child = NULL;
+ }
+
+ G_OBJECT_CLASS (mx_bin_parent_class)->dispose (gobject);
+}
+
+static void
+mx_bin_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MxBin *bin = MX_BIN (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD:
+ mx_bin_set_child (bin, g_value_get_object (value));
+ break;
+
+ case PROP_X_ALIGN:
+ mx_bin_set_alignment (bin,
+ g_value_get_enum (value),
+ bin->priv->y_align);
+ break;
+
+ case PROP_Y_ALIGN:
+ mx_bin_set_alignment (bin,
+ bin->priv->x_align,
+ g_value_get_enum (value));
+ break;
+
+ case PROP_X_FILL:
+ mx_bin_set_fill (bin,
+ g_value_get_boolean (value),
+ bin->priv->y_fill);
+ break;
+
+ case PROP_Y_FILL:
+ mx_bin_set_fill (bin,
+ bin->priv->x_fill,
+ g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+mx_bin_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MxBinPrivate *priv = MX_BIN (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_CHILD:
+ g_value_set_object (value, priv->child);
+ break;
+
+ case PROP_X_FILL:
+ g_value_set_boolean (value, priv->x_fill);
+ break;
+
+ case PROP_Y_FILL:
+ g_value_set_boolean (value, priv->y_fill);
+ break;
+
+ case PROP_X_ALIGN:
+ g_value_set_enum (value, priv->x_align);
+ break;
+
+ case PROP_Y_ALIGN:
+ g_value_set_enum (value, priv->y_align);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+mx_bin_class_init (MxBinClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (MxBinPrivate));
+
+ gobject_class->set_property = mx_bin_set_property;
+ gobject_class->get_property = mx_bin_get_property;
+ gobject_class->dispose = mx_bin_dispose;
+
+ actor_class->get_preferred_width = mx_bin_get_preferred_width;
+ actor_class->get_preferred_height = mx_bin_get_preferred_height;
+ actor_class->paint = mx_bin_paint;
+ actor_class->pick = mx_bin_pick;
+
+ /**
+ * MxBin:child:
+ *
+ * The child #ClutterActor of the #MxBin container.
+ */
+ pspec = g_param_spec_object ("child",
+ "Child",
+ "The child of the Bin",
+ CLUTTER_TYPE_ACTOR,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD, pspec);
+
+ /**
+ * MxBin:x-align:
+ *
+ * The horizontal alignment of the #MxBin child.
+ */
+ pspec = g_param_spec_enum ("x-align",
+ "X Align",
+ "The horizontal alignment",
+ MX_TYPE_ALIGN,
+ MX_ALIGN_MIDDLE,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec);
+
+ /**
+ * MxBin:y-align:
+ *
+ * The vertical alignment of the #MxBin child.
+ */
+ pspec = g_param_spec_enum ("y-align",
+ "Y Align",
+ "The vertical alignment",
+ MX_TYPE_ALIGN,
+ MX_ALIGN_MIDDLE,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec);
+
+ /**
+ * MxBin:x-fill:
+ *
+ * Whether the child should fill the horizontal allocation
+ */
+ pspec = g_param_spec_boolean ("x-fill",
+ "X Fill",
+ "Whether the child should fill the "
+ "horizontal allocation",
+ FALSE,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_X_FILL, pspec);
+
+ /**
+ * MxBin:y-fill:
+ *
+ * Whether the child should fill the vertical allocation
+ */
+ pspec = g_param_spec_boolean ("y-fill",
+ "Y Fill",
+ "Whether the child should fill the "
+ "vertical allocation",
+ FALSE,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_Y_FILL, pspec);
+}
+
+static void
+mx_bin_init (MxBin *bin)
+{
+ bin->priv = MX_BIN_GET_PRIVATE (bin);
+
+ bin->priv->x_align = MX_ALIGN_MIDDLE;
+ bin->priv->y_align = MX_ALIGN_MIDDLE;
+ bin->priv->child_has_space = TRUE;
+}
+
+/**
+ * mx_bin_set_child:
+ * @bin: a #MxBin
+ * @child: a #ClutterActor, or %NULL
+ *
+ * Sets @child as the child of @bin.
+ *
+ * If @bin already has a child, the previous child is removed.
+ */
+void
+mx_bin_set_child (MxBin *bin,
+ ClutterActor *child)
+{
+ MxBinPrivate *priv;
+
+ g_return_if_fail (MX_IS_BIN (bin));
+ g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child));
+
+ priv = bin->priv;
+
+ if (priv->child == child)
+ return;
+
+ if (priv->child)
+ {
+ ClutterActor *old_child = priv->child;
+
+ g_object_ref (old_child);
+
+ priv->child = NULL;
+ clutter_actor_unparent (old_child);
+
+ g_signal_emit_by_name (bin, "actor-removed", old_child);
+
+ g_object_unref (old_child);
+ }
+
+ if (child)
+ {
+ priv->child = child;
+ clutter_actor_set_parent (child, CLUTTER_ACTOR (bin));
+
+ g_signal_emit_by_name (bin, "actor-added", priv->child);
+ }
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
+
+ g_object_notify (G_OBJECT (bin), "child");
+}
+
+/**
+ * mx_bin_get_child:
+ * @bin: a #MxBin
+ *
+ * Retrieves a pointer to the child of @bin.
+ *
+ * Return value: (transfer none): a #ClutterActor, or %NULL
+ */
+ClutterActor *
+mx_bin_get_child (MxBin *bin)
+{
+ g_return_val_if_fail (MX_IS_BIN (bin), NULL);
+
+ return bin->priv->child;
+}
+
+/**
+ * mx_bin_set_alignment:
+ * @bin: a #MxBin
+ * @x_align: horizontal alignment
+ * @y_align: vertical alignment
+ *
+ * Sets the horizontal and vertical alignment of the child
+ * inside a #MxBin.
+ */
+void
+mx_bin_set_alignment (MxBin *bin,
+ MxAlign x_align,
+ MxAlign y_align)
+{
+ MxBinPrivate *priv;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (MX_IS_BIN (bin));
+
+ priv = bin->priv;
+
+ g_object_freeze_notify (G_OBJECT (bin));
+
+ if (priv->x_align != x_align)
+ {
+ priv->x_align = x_align;
+ g_object_notify (G_OBJECT (bin), "x-align");
+ changed = TRUE;
+ }
+
+ if (priv->y_align != y_align)
+ {
+ priv->y_align = y_align;
+ g_object_notify (G_OBJECT (bin), "y-align");
+ changed = TRUE;
+ }
+
+ if (changed)
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
+
+ g_object_thaw_notify (G_OBJECT (bin));
+}
+
+/**
+ * mx_bin_get_alignment:
+ * @bin: a #MxBin
+ * @x_align: return location for the horizontal alignment, or %NULL
+ * @y_align: return location for the vertical alignment, or %NULL
+ *
+ * Retrieves the horizontal and vertical alignment of the child
+ * inside a #MxBin, as set by mx_bin_set_alignment().
+ */
+void
+mx_bin_get_alignment (MxBin *bin,
+ MxAlign *x_align,
+ MxAlign *y_align)
+{
+ MxBinPrivate *priv;
+
+ g_return_if_fail (MX_IS_BIN (bin));
+
+ priv = bin->priv;
+
+ if (x_align)
+ *x_align = priv->x_align;
+
+ if (y_align)
+ *y_align = priv->y_align;
+}
+
+/**
+ * mx_bin_set_fill:
+ * @bin: a #MxBin
+ * @x_fill: %TRUE if the child should fill horizontally the @bin
+ * @y_fill: %TRUE if the child should fill vertically the @bin
+ *
+ * Sets whether the child of @bin should fill out the horizontal
+ * and/or vertical allocation of the parent
+ */
+void
+mx_bin_set_fill (MxBin *bin,
+ gboolean x_fill,
+ gboolean y_fill)
+{
+ MxBinPrivate *priv;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (MX_IS_BIN (bin));
+
+ priv = bin->priv;
+
+ g_object_freeze_notify (G_OBJECT (bin));
+
+ if (priv->x_fill != x_fill)
+ {
+ priv->x_fill = x_fill;
+ changed = TRUE;
+
+ g_object_notify (G_OBJECT (bin), "x-fill");
+ }
+
+ if (priv->y_fill != y_fill)
+ {
+ priv->y_fill = y_fill;
+ changed = TRUE;
+
+ g_object_notify (G_OBJECT (bin), "y-fill");
+ }
+
+ if (changed)
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
+
+ g_object_thaw_notify (G_OBJECT (bin));
+}
+
+/**
+ * mx_bin_get_fill:
+ * @bin: a #MxBin
+ * @x_fill: (out): return location for the horizontal fill, or %NULL
+ * @y_fill: (out): return location for the vertical fill, or %NULL
+ *
+ * Retrieves the horizontal and vertical fill settings
+ */
+void
+mx_bin_get_fill (MxBin *bin,
+ gboolean *x_fill,
+ gboolean *y_fill)
+{
+ g_return_if_fail (MX_IS_BIN (bin));
+
+ if (x_fill)
+ *x_fill = bin->priv->x_fill;
+
+ if (y_fill)
+ *y_fill = bin->priv->y_fill;
+}
diff --git a/mx/mx-bin.h b/mx/mx-bin.h
new file mode 100644
index 0000000..666e809
--- /dev/null
+++ b/mx/mx-bin.h
@@ -0,0 +1,99 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * mx-bin.h: Basic container actor
+ *
+ * Copyright 2009, 2008 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 linux intel com>
+ *
+ */
+
+#ifndef __MX_BIN_H__
+#define __MX_BIN_H__
+
+#include "mx-types.h"
+#include <clutter/clutter.h>
+
+
+G_BEGIN_DECLS
+
+#define MX_TYPE_BIN (mx_bin_get_type ())
+#define MX_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_BIN, MxBin))
+#define MX_IS_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_BIN))
+#define MX_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_BIN, MxBinClass))
+#define MX_IS_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_BIN))
+#define MX_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_BIN, MxBinClass))
+
+typedef struct _MxBin MxBin;
+typedef struct _MxBinPrivate MxBinPrivate;
+typedef struct _MxBinClass MxBinClass;
+
+/**
+ * MxBin:
+ *
+ * The #MxBin struct contains only private data
+ */
+struct _MxBin
+{
+ /*< private >*/
+ ClutterGroup parent_instance;
+
+ MxBinPrivate *priv;
+};
+
+/**
+ * MxBinClass:
+ *
+ * The #MxBinClass struct contains only private data
+ */
+struct _MxBinClass
+{
+ /*< private >*/
+ ClutterGroupClass parent_class;
+
+ /* padding for future expansion */
+ void (*_padding_0) (void);
+ void (*_padding_1) (void);
+ void (*_padding_2) (void);
+ void (*_padding_3) (void);
+ void (*_padding_4) (void);
+};
+
+GType mx_bin_get_type (void) G_GNUC_CONST;
+
+void mx_bin_allocate_child (MxBin *bin,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags);
+void mx_bin_set_child (MxBin *bin,
+ ClutterActor *child);
+ClutterActor *mx_bin_get_child (MxBin *bin);
+void mx_bin_set_alignment (MxBin *bin,
+ MxAlign x_align,
+ MxAlign y_align);
+void mx_bin_get_alignment (MxBin *bin,
+ MxAlign *x_align,
+ MxAlign *y_align);
+void mx_bin_set_fill (MxBin *bin,
+ gboolean x_fill,
+ gboolean y_fill);
+void mx_bin_get_fill (MxBin *bin,
+ gboolean *x_fill,
+ gboolean *y_fill);
+
+G_END_DECLS
+
+#endif /* __MX_BIN_H__ */
diff --git a/mx/mx-draggable.c b/mx/mx-draggable.c
new file mode 100644
index 0000000..eced211
--- /dev/null
+++ b/mx/mx-draggable.c
@@ -0,0 +1,706 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * mx-draggable.c: draggable interface
+ *
+ * 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: Emmanuele Bassi <ebassi linux intel com>
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mx-draggable.h"
+#include "mx-enum-types.h"
+#include "mx-marshal.h"
+#include "mx-private.h"
+
+typedef struct _DragContext DragContext;
+
+struct _DragContext
+{
+ MxDraggable *draggable;
+ ClutterActor *stage;
+ ClutterActor *actor;
+
+ guint threshold;
+
+ MxDragAxis axis;
+
+#if 0
+ MxDragContainment containment;
+ ClutterActorBox *containment_area;
+#endif
+
+ gfloat press_x;
+ gfloat press_y;
+ guint press_button;
+ ClutterModifierType press_modifiers;
+
+ gfloat last_x;
+ gfloat last_y;
+
+ guint emit_delayed_press : 1;
+ guint in_drag : 1;
+};
+
+enum
+{
+ DRAG_BEGIN,
+ DRAG_MOTION,
+ DRAG_END,
+
+ LAST_SIGNAL
+};
+
+static GQuark quark_draggable_context = 0;
+static guint draggable_signals[LAST_SIGNAL] = { 0, };
+
+static gboolean on_stage_capture (ClutterActor *stage,
+ ClutterEvent *event,
+ DragContext *context);
+
+static gboolean
+draggable_release (DragContext *context,
+ ClutterButtonEvent *event)
+{
+ ClutterActor *stage, *actor;
+ gfloat event_x, event_y;
+ gfloat actor_x, actor_y;
+ gboolean res;
+
+ if (!context->in_drag)
+ return FALSE;
+
+ event_x = event->x;
+ event_y = event->y;
+ actor_x = 0;
+ actor_y = 0;
+
+ if (context->actor && !context->emit_delayed_press)
+ actor = context->actor;
+ else
+ actor = CLUTTER_ACTOR (context->draggable);
+
+ res = clutter_actor_transform_stage_point (actor,
+ event_x, event_y,
+ &actor_x, &actor_y);
+ if (!res)
+ return FALSE;
+
+ stage = clutter_actor_get_stage (CLUTTER_ACTOR (context->draggable));
+
+ context->last_x = actor_x;
+ context->last_y = actor_y;
+
+ context->in_drag = FALSE;
+
+ g_signal_handlers_disconnect_by_func (stage,
+ G_CALLBACK (on_stage_capture),
+ context);
+
+ if (!context->emit_delayed_press)
+ g_signal_emit (context->draggable, draggable_signals[DRAG_END], 0,
+ context->last_x,
+ context->last_y);
+
+ g_object_set_data (G_OBJECT (stage), "mx-drag-actor", NULL);
+
+ return FALSE;
+}
+
+static gboolean
+draggable_motion (DragContext *context,
+ ClutterMotionEvent *event)
+{
+ gfloat event_x, event_y;
+ gfloat actor_x, actor_y;
+ gfloat delta_x, delta_y;
+ ClutterActor *actor;
+ gboolean res;
+
+ if (!context->in_drag)
+ return FALSE;
+
+ event_x = event->x;
+ event_y = event->y;
+ actor_x = 0;
+ actor_y = 0;
+
+ if (context->actor && !context->emit_delayed_press)
+ actor = context->actor;
+ else
+ actor = CLUTTER_ACTOR (context->draggable);
+
+ res = clutter_actor_transform_stage_point (actor,
+ event_x, event_y,
+ &actor_x, &actor_y);
+ if (!res)
+ return FALSE;
+
+ context->last_x = actor_x;
+ context->last_y = actor_y;
+
+ delta_x = delta_y = 0;
+
+ if (context->axis == 0)
+ {
+ delta_x = context->last_x - context->press_x;
+ delta_y = context->last_y - context->press_y;
+ }
+ else
+ {
+ if (context->axis == MX_DRAG_AXIS_X)
+ delta_x = context->last_x - context->press_x;
+ else
+ delta_y = context->last_y - context->press_y;
+ }
+
+ if (context->emit_delayed_press)
+ {
+ if (ABS (delta_x) >= context->threshold
+ || ABS (delta_y) >= context->threshold)
+ {
+ ClutterActor *stage;
+
+ context->emit_delayed_press = FALSE;
+
+ g_signal_emit (context->draggable, draggable_signals[DRAG_BEGIN], 0,
+ context->press_x,
+ context->press_y,
+ context->press_button,
+ context->press_modifiers);
+
+ actor = CLUTTER_ACTOR (context->draggable);
+ stage = clutter_actor_get_stage (actor);
+ g_object_set_data (G_OBJECT (stage), "mx-drag-actor", actor);
+ }
+ else
+ return FALSE;
+ }
+
+ g_signal_emit (context->draggable, draggable_signals[DRAG_MOTION], 0,
+ delta_x,
+ delta_y);
+
+ return FALSE;
+}
+
+static gboolean
+on_stage_capture (ClutterActor *stage,
+ ClutterEvent *event,
+ DragContext *context)
+{
+ switch (event->type)
+ {
+ case CLUTTER_MOTION:
+ if (context->in_drag)
+ {
+ ClutterMotionEvent *mevent = (ClutterMotionEvent *) event;
+
+ /* We can miss release events in the case of grabs, so check that
+ * the button is still down here.
+ */
+ if (!(mevent->modifier_state & CLUTTER_BUTTON1_MASK))
+ return draggable_release (context, (ClutterButtonEvent *) event);
+ else
+ return draggable_motion (context, (ClutterMotionEvent *) event);
+ }
+ break;
+
+ case CLUTTER_BUTTON_RELEASE:
+ if (context->in_drag)
+ return draggable_release (context, (ClutterButtonEvent *) event);
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+on_draggable_press (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ DragContext *context)
+{
+ MxDraggable *draggable = context->draggable;
+ ClutterActor *stage;
+ gfloat event_x, event_y;
+ gfloat actor_x, actor_y;
+ gboolean res;
+
+ event_x = event->x;
+ event_y = event->y;
+ actor_x = 0;
+ actor_y = 0;
+
+ res = clutter_actor_transform_stage_point (actor,
+ event_x, event_y,
+ &actor_x, &actor_y);
+ if (!res)
+ return FALSE;
+
+ stage = clutter_actor_get_stage (actor);
+
+ context->press_x = actor_x;
+ context->press_y = actor_y;
+ context->last_x = context->press_x;
+ context->last_y = context->press_y;
+ context->press_button = event->button;
+ context->press_modifiers = event->modifier_state;
+ context->emit_delayed_press = FALSE;
+
+ g_object_get (G_OBJECT (draggable),
+ "drag-threshold", &context->threshold,
+ "axis", &context->axis,
+#if 0
+ "containment-type", &context->containment,
+ "containment-area", &context->containment_area,
+#endif
+ "drag-actor", &context->actor,
+ NULL);
+
+ if (context->threshold == 0)
+ {
+ g_signal_emit (draggable, draggable_signals[DRAG_BEGIN], 0,
+ context->press_x,
+ context->press_y,
+ context->press_button,
+ context->press_modifiers);
+
+ g_object_set_data (G_OBJECT (stage), "mx-drag-actor", actor);
+ }
+ else
+ context->emit_delayed_press = TRUE;
+
+ context->in_drag = TRUE;
+
+ context->stage = stage;
+ g_signal_connect_after (stage,
+ "captured-event", G_CALLBACK (on_stage_capture),
+ context);
+
+ return FALSE;
+}
+
+static void
+drag_context_free (gpointer data)
+{
+ if (G_LIKELY (data))
+ {
+ DragContext *context = data;
+
+ /* disconnect any signal handlers we may have installed */
+ g_signal_handlers_disconnect_by_func (context->draggable,
+ G_CALLBACK (on_draggable_press),
+ context);
+ if (context->stage)
+ {
+ g_signal_handlers_disconnect_by_func (context->stage,
+ G_CALLBACK (on_stage_capture),
+ context);
+ context->stage = NULL;
+ }
+
+ if (context->actor)
+ {
+ g_object_unref (G_OBJECT (context->actor));
+ context->actor = NULL;
+ }
+#if 0
+ if (context->containment_area)
+ g_boxed_free (CLUTTER_TYPE_ACTOR_BOX, context->containment_area);
+#endif
+
+ g_slice_free (DragContext, context);
+ }
+}
+
+static DragContext *
+drag_context_create (MxDraggable *draggable)
+{
+ DragContext *context;
+
+ context = g_slice_new (DragContext);
+
+ context->draggable = draggable;
+ context->threshold = 0;
+ context->axis = 0;
+#if 0
+ context->containment = MX_DISABLE_CONTAINMENT;
+ context->containment_area = NULL;
+#endif
+ context->in_drag = FALSE;
+ context->emit_delayed_press = FALSE;
+ context->stage = NULL;
+ context->actor = NULL;
+
+ /* attach the context to the draggable */
+ g_object_set_qdata_full (G_OBJECT (draggable), quark_draggable_context,
+ context,
+ drag_context_free);
+
+ return context;
+}
+
+static void
+mx_draggable_real_enable (MxDraggable *draggable)
+{
+ DragContext *context;
+ ClutterActor *stage;
+
+ context = g_object_get_qdata (G_OBJECT (draggable), quark_draggable_context);
+ if (G_UNLIKELY (context != NULL))
+ return;
+
+ stage = clutter_actor_get_stage (CLUTTER_ACTOR (draggable));
+ if (G_UNLIKELY (stage == NULL))
+ {
+ g_warning ("Draggable actors can only be enabled when they "
+ "on the stage");
+ return;
+ }
+
+ context = drag_context_create (draggable);
+ g_signal_connect (draggable,
+ "button-press-event", G_CALLBACK (on_draggable_press),
+ context);
+
+ g_object_notify (G_OBJECT (draggable), "drag-enabled");
+}
+
+static void
+mx_draggable_real_disable (MxDraggable *draggable)
+{
+ DragContext *context;
+ ClutterActor *stage;
+
+ context = g_object_get_qdata (G_OBJECT (draggable), quark_draggable_context);
+ if (G_UNLIKELY (context == NULL))
+ return;
+
+ stage = clutter_actor_get_stage (CLUTTER_ACTOR (draggable));
+
+ g_signal_handlers_disconnect_by_func (draggable,
+ G_CALLBACK (on_draggable_press),
+ context);
+ g_signal_handlers_disconnect_by_func (stage,
+ G_CALLBACK (on_stage_capture),
+ context);
+ context->stage = NULL;
+
+ g_object_set_qdata (G_OBJECT (draggable), quark_draggable_context, NULL);
+
+ g_object_notify (G_OBJECT (draggable), "drag-enabled");
+}
+
+static void
+mx_draggable_base_init (gpointer g_iface)
+{
+ static gboolean is_initialized = FALSE;
+
+ if (G_UNLIKELY (!is_initialized))
+ {
+ MxDraggableIface *iface = g_iface;
+ GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+ GParamSpec *pspec;
+
+ is_initialized = TRUE;
+
+ quark_draggable_context =
+ g_quark_from_static_string ("mx-draggable-context");
+
+ pspec = g_param_spec_boolean ("drag-enabled",
+ "Drag Enabled",
+ "Whether the Draggable is enabled",
+ TRUE,
+ MX_PARAM_READWRITE);
+ g_object_interface_install_property (g_iface, pspec);
+
+ pspec = g_param_spec_uint ("drag-threshold",
+ "Drag Threshold",
+ "The amount of pixels required to "
+ "start dragging",
+ 0, G_MAXUINT,
+ 0,
+ MX_PARAM_READWRITE);
+ g_object_interface_install_property (g_iface, pspec);
+
+#if 0
+ pspec = g_param_spec_enum ("containment-type",
+ "Containment Type",
+ "The type of containment to be used",
+ MX_TYPE_DRAG_CONTAINMENT,
+ MX_DISABLE_CONTAINMENT,
+ MX_PARAM_READWRITE);
+ g_object_interface_install_property (g_iface, pspec);
+
+ pspec = g_param_spec_boxed ("containment-area",
+ "Containment Area",
+ "The area to which the draggable is "
+ "contained",
+ CLUTTER_TYPE_ACTOR_BOX,
+ MX_PARAM_READWRITE);
+ g_object_interface_install_property (g_iface, pspec);
+#endif
+
+ pspec = g_param_spec_enum ("axis",
+ "Axis",
+ "The axis along which the dragging "
+ "should be performed",
+ MX_TYPE_DRAG_AXIS,
+ MX_DRAG_AXIS_NONE,
+ MX_PARAM_READWRITE);
+ g_object_interface_install_property (g_iface, pspec);
+
+ pspec = g_param_spec_object ("drag-actor",
+ "Drag Actor",
+ "An actor to use in place of the "
+ "draggable while dragging.",
+ CLUTTER_TYPE_ACTOR,
+ MX_PARAM_READWRITE);
+ g_object_interface_install_property (g_iface, pspec);
+
+ draggable_signals[DRAG_BEGIN] =
+ g_signal_new (g_intern_static_string ("drag-begin"),
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MxDraggableIface, drag_begin),
+ NULL, NULL,
+ _mx_marshal_VOID__FLOAT_FLOAT_INT_ENUM,
+ G_TYPE_NONE, 4,
+ G_TYPE_FLOAT,
+ G_TYPE_FLOAT,
+ G_TYPE_INT,
+ CLUTTER_TYPE_MODIFIER_TYPE);
+
+ draggable_signals[DRAG_MOTION] =
+ g_signal_new (g_intern_static_string ("drag-motion"),
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MxDraggableIface, drag_motion),
+ NULL, NULL,
+ _mx_marshal_VOID__FLOAT_FLOAT,
+ G_TYPE_NONE, 2,
+ G_TYPE_FLOAT,
+ G_TYPE_FLOAT);
+
+ draggable_signals[DRAG_END] =
+ g_signal_new (g_intern_static_string ("drag-end"),
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MxDraggableIface, drag_end),
+ NULL, NULL,
+ _mx_marshal_VOID__FLOAT_FLOAT,
+ G_TYPE_NONE, 2,
+ G_TYPE_FLOAT,
+ G_TYPE_FLOAT);
+
+ iface->enable = mx_draggable_real_enable;
+ iface->disable = mx_draggable_real_disable;
+ }
+}
+
+GType
+mx_draggable_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (G_UNLIKELY (our_type == 0))
+ {
+ const GTypeInfo draggable_info = {
+ sizeof (MxDraggableIface),
+ mx_draggable_base_init,
+ NULL, /* base_finalize */
+ };
+
+ our_type = g_type_register_static (G_TYPE_INTERFACE,
+ g_intern_static_string ("MxDraggable"),
+ &draggable_info, 0);
+
+ g_type_interface_add_prerequisite (our_type, CLUTTER_TYPE_ACTOR);
+ }
+
+ return our_type;
+}
+
+void
+mx_draggable_set_axis (MxDraggable *draggable,
+ MxDragAxis axis)
+{
+ g_return_if_fail (MX_IS_DRAGGABLE (draggable));
+
+ g_object_set (G_OBJECT (draggable), "axis", axis, NULL);
+}
+
+MxDragAxis
+mx_draggable_get_axis (MxDraggable *draggable)
+{
+ MxDragAxis retval = 0;
+
+ g_return_val_if_fail (MX_IS_DRAGGABLE (draggable), 0);
+
+ g_object_get (G_OBJECT (draggable), "axis", &retval, NULL);
+
+ return retval;
+}
+
+void
+mx_draggable_set_drag_threshold (MxDraggable *draggable,
+ guint threshold)
+{
+ g_return_if_fail (MX_IS_DRAGGABLE (draggable));
+
+ g_object_set (G_OBJECT (draggable), "drag-threshold", threshold, NULL);
+}
+
+guint
+mx_draggable_get_drag_threshold (MxDraggable *draggable)
+{
+ guint retval = 0;
+
+ g_return_val_if_fail (MX_IS_DRAGGABLE (draggable), 0);
+
+ g_object_get (G_OBJECT (draggable), "drag-threshold", &retval, NULL);
+
+ return retval;
+}
+#if 0
+void
+mx_draggable_set_containment_type (MxDraggable *draggable,
+ MxDragContainment containment)
+{
+ g_return_if_fail (MX_IS_DRAGGABLE (draggable));
+
+ g_object_set (G_OBJECT (draggable), "containment-type", containment, NULL);
+}
+
+MxDragContainment
+mx_draggable_get_containment_type (MxDraggable *draggable)
+{
+ MxDragContainment retval = MX_DISABLE_CONTAINMENT;
+
+ g_return_val_if_fail (MX_IS_DRAGGABLE (draggable), 0);
+
+ g_object_get (G_OBJECT (draggable), "containment-type", &retval, NULL);
+
+ return retval;
+}
+
+void
+mx_draggable_set_containment_area (MxDraggable *draggable,
+ gfloat x_1,
+ gfloat y_1,
+ gfloat x_2,
+ gfloat y_2)
+{
+ ClutterActorBox box;
+
+ g_return_if_fail (MX_IS_DRAGGABLE (draggable));
+
+ box.x1 = x_1;
+ box.y1 = y_1;
+ box.x2 = x_2;
+ box.y2 = y_2;
+
+ g_object_set (G_OBJECT (draggable), "containment-area", &box, NULL);
+}
+
+void
+mx_draggable_get_containment_area (MxDraggable *draggable,
+ gfloat *x_1,
+ gfloat *y_1,
+ gfloat *x_2,
+ gfloat *y_2)
+{
+ ClutterActorBox *box = NULL;
+
+ g_return_if_fail (MX_IS_DRAGGABLE (draggable));
+
+ g_object_get (G_OBJECT (draggable), "containment-area", &box, NULL);
+
+ if (box == NULL)
+ return;
+
+ if (x_1)
+ *x_1 = box->x1;
+
+ if (y_1)
+ *y_1 = box->y1;
+
+ if (x_2)
+ *x_2 = box->x2;
+
+ if (y_2)
+ *y_2 = box->y2;
+
+ g_boxed_free (CLUTTER_TYPE_ACTOR_BOX, box);
+}
+#endif
+
+void
+mx_draggable_set_drag_actor (MxDraggable *draggable,
+ ClutterActor *actor)
+{
+ g_return_if_fail (MX_IS_DRAGGABLE (draggable));
+
+ g_object_set (G_OBJECT (draggable), "drag-actor", actor, NULL);
+}
+
+ClutterActor *
+mx_draggable_get_drag_actor (MxDraggable *draggable)
+{
+ ClutterActor *actor = NULL;
+
+ g_return_val_if_fail (MX_IS_DRAGGABLE (draggable), NULL);
+
+ g_object_get (G_OBJECT (draggable), "drag-actor", &actor, NULL);
+
+ return actor;
+}
+
+void
+mx_draggable_enable (MxDraggable *draggable)
+{
+ g_return_if_fail (MX_IS_DRAGGABLE (draggable));
+
+ MX_DRAGGABLE_GET_IFACE (draggable)->enable (draggable);
+}
+
+void
+mx_draggable_disable (MxDraggable *draggable)
+{
+ g_return_if_fail (MX_IS_DRAGGABLE (draggable));
+
+ MX_DRAGGABLE_GET_IFACE (draggable)->disable (draggable);
+}
+
+gboolean
+mx_draggable_is_enabled (MxDraggable *draggable)
+{
+ gboolean retval = FALSE;
+
+ g_return_val_if_fail (MX_IS_DRAGGABLE (draggable), FALSE);
+
+ g_object_get (G_OBJECT (draggable), "drag-enabled", &retval, NULL);
+
+ return retval;
+}
diff --git a/mx/mx-draggable.h b/mx/mx-draggable.h
new file mode 100644
index 0000000..0718c86
--- /dev/null
+++ b/mx/mx-draggable.h
@@ -0,0 +1,132 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * mx-draggable.h: draggable interface
+ *
+ * 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: Emmanuele Bassi <ebassi linux intel com>
+ *
+ */
+
+#ifndef __MX_DRAGGABLE_H__
+#define __MX_DRAGGABLE_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define MX_TYPE_DRAGGABLE (mx_draggable_get_type ())
+#define MX_DRAGGABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_DRAGGABLE, MxDraggable))
+#define MX_IS_DRAGGABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_DRAGGABLE))
+#define MX_DRAGGABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MX_TYPE_DRAGGABLE, MxDraggableIface))
+
+/**
+ * MxDraggable:
+ *
+ * This is an opaque structure whose members cannot be directly accessed.
+ */
+typedef struct _MxDraggable MxDraggable; /* dummy typedef */
+typedef struct _MxDraggableIface MxDraggableIface;
+
+typedef enum {
+ MX_DRAG_AXIS_NONE,
+ MX_DRAG_AXIS_X,
+ MX_DRAG_AXIS_Y
+} MxDragAxis;
+
+#if 0
+/*
+typedef enum {
+ MX_DISABLE_CONTAINMENT,
+ MX_CONTAIN_IN_STAGE,
+ MX_CONTAIN_IN_PARENT,
+ MX_CONTAIN_IN_AREA
+} MxDragContainment;
+*/
+#endif
+
+/**
+ * MxDraggableIface:
+ * @enable: virtual function called when enabling a #MxDraggable; MX
+ * already provides a default implementation
+ * @disable: virtual function called when disabling a #MxDraggable; MX
+ * already provides a default implementation
+ * @drag_begin: class handler for the #MxDraggable::drag-begin signal
+ * @drag_motion: class handler for the #MxDraggable::drag-motion signal
+ * @drag_end: class handler for the #MxDraggable::drag-end signal
+ *
+ * Interface for draggable #ClutterActor<!-- -->s.
+ */
+struct _MxDraggableIface
+{
+ /*< private >*/
+ GTypeInterface g_iface;
+
+ /*< public >*/
+ /* vfuncs, not signals */
+ void (* enable) (MxDraggable *draggable);
+ void (* disable) (MxDraggable *draggable);
+
+ /* signals */
+ void (* drag_begin) (MxDraggable *draggable,
+ gfloat event_x,
+ gfloat event_y,
+ gint event_button,
+ ClutterModifierType modifiers);
+ void (* drag_motion) (MxDraggable *draggable,
+ gfloat delta_x,
+ gfloat delta_y);
+ void (* drag_end) (MxDraggable *draggable,
+ gfloat event_x,
+ gfloat event_y);
+};
+
+GType mx_draggable_get_type (void) G_GNUC_CONST;
+
+void mx_draggable_set_axis (MxDraggable *draggable,
+ MxDragAxis axis);
+MxDragAxis mx_draggable_get_axis (MxDraggable *draggable);
+
+void mx_draggable_set_drag_threshold (MxDraggable *draggable,
+ guint threshold);
+guint mx_draggable_get_drag_threshold (MxDraggable *draggable);
+#if 0
+void mx_draggable_set_containment_type (MxDraggable *draggable,
+ MxDragContainment containment);
+MxDragContainment mx_draggable_get_containment_type (MxDraggable *draggable);
+void mx_draggable_set_containment_area (MxDraggable *draggable,
+ gfloat x_1,
+ gfloat y_1,
+ gfloat x_2,
+ gfloat y_2);
+void mx_draggable_get_containment_area (MxDraggable *draggable,
+ gfloat *x_1,
+ gfloat *y_1,
+ gfloat *x_2,
+ gfloat *y_2);
+#endif
+void mx_draggable_set_drag_actor (MxDraggable *draggable,
+ ClutterActor *actor);
+ClutterActor * mx_draggable_get_drag_actor (MxDraggable *draggable);
+
+void mx_draggable_disable (MxDraggable *draggable);
+void mx_draggable_enable (MxDraggable *draggable);
+gboolean mx_draggable_is_enabled (MxDraggable *draggable);
+
+G_END_DECLS
+
+#endif /* __MX_DRAGGABLE_H__ */
diff --git a/tidy/tidy-enum-types.c.in b/mx/mx-enum-types.c.in
similarity index 95%
rename from tidy/tidy-enum-types.c.in
rename to mx/mx-enum-types.c.in
index 5f78912..7af6194 100644
--- a/tidy/tidy-enum-types.c.in
+++ b/mx/mx-enum-types.c.in
@@ -1,5 +1,5 @@
/*** BEGIN file-header ***/
-#include "tidy-enum-types.h"
+#include "mx-enum-types.h"
/*** END file-header ***/
/*** BEGIN file-production ***/
diff --git a/tidy/tidy-enum-types.h.in b/mx/mx-enum-types.h.in
similarity index 70%
rename from tidy/tidy-enum-types.h.in
rename to mx/mx-enum-types.h.in
index 517cccb..49bce41 100644
--- a/tidy/tidy-enum-types.h.in
+++ b/mx/mx-enum-types.h.in
@@ -1,6 +1,6 @@
/*** BEGIN file-header ***/
-#ifndef __TIDY_ENUM_TYPES_H__
-#define __TIDY_ENUM_TYPES_H__
+#ifndef __MX_ENUM_TYPES_H__
+#define __MX_ENUM_TYPES_H__
#include <glib-object.h>
@@ -15,11 +15,11 @@ G_BEGIN_DECLS
/*** BEGIN file-tail ***/
G_END_DECLS
-#endif /* !__TIDY_ENUM_TYPES_H__ */
+#endif /* !__MX_ENUM_TYPES_H__ */
/*** END file-tail ***/
/*** BEGIN value-header ***/
GType @enum_name _get_type (void) G_GNUC_CONST;
-#define TIDY_TYPE_ ENUMSHORT@ (@enum_name _get_type())
+#define MX_TYPE_ ENUMSHORT@ (@enum_name _get_type())
/*** END value-header ***/
diff --git a/mx/mx-kinetic-scroll-view.c b/mx/mx-kinetic-scroll-view.c
new file mode 100644
index 0000000..ebc5fd2
--- /dev/null
+++ b/mx/mx-kinetic-scroll-view.c
@@ -0,0 +1,1195 @@
+/* mx-kinetic-scroll-view.c: Kinetic scrolling container actor
+ *
+ * Copyright (C) 2008 OpenedHand
+ * Copyright (C) 2010 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that 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 linux intel com>
+ */
+
+/**
+ * SECTION:mx-kinetic-scroll-view
+ * @short_description: A kinetic scrolling container widget
+ *
+ * #MxKineticScrollView is a single child container for actors that implements
+ * #MxScrollable. It allows the contained child to be dragged to scroll, and
+ * maintains the momentum once the drag is complete. Deceleration after
+ * dragging is configurable, and it will always snap to the
+ * #MxAdjustment:step-increment boundary.
+ *
+ * #MxKineticScrollView also implements #MxScrollable itself, allowing it to
+ * be embedded in an #MxScrollView to provide scroll-bars.
+ */
+
+#include "mx-kinetic-scroll-view.h"
+#include "mx-enum-types.h"
+#include "mx-marshal.h"
+#include "mx-private.h"
+#include "mx-scrollable.h"
+#include <math.h>
+
+static void mx_scrollable_iface_init (MxScrollableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (MxKineticScrollView,
+ mx_kinetic_scroll_view, MX_TYPE_BIN,
+ G_IMPLEMENT_INTERFACE (MX_TYPE_SCROLLABLE,
+ mx_scrollable_iface_init))
+
+#define KINETIC_SCROLL_VIEW_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+ MX_TYPE_KINETIC_SCROLL_VIEW, \
+ MxKineticScrollViewPrivate))
+
+typedef struct {
+ /* Units to store the origin of a click when scrolling */
+ gfloat x;
+ gfloat y;
+ GTimeVal time;
+} MxKineticScrollViewMotion;
+
+struct _MxKineticScrollViewPrivate
+{
+ ClutterActor *child;
+
+ guint use_captured : 1;
+ guint in_drag : 1;
+ guint hmoving : 1;
+ guint vmoving : 1;
+ guint32 button;
+
+ /* Mouse motion event information */
+ GArray *motion_buffer;
+ guint last_motion;
+
+ /* Variables for storing acceleration information */
+ ClutterTimeline *deceleration_timeline;
+ gfloat dx;
+ gfloat dy;
+ gdouble decel_rate;
+ gdouble overshoot;
+ gdouble accumulated_delta;
+};
+
+enum {
+ PROP_0,
+
+ PROP_DECELERATION,
+/* PROP_BUFFER_SIZE,*/
+ PROP_HADJUST,
+ PROP_VADJUST,
+ PROP_BUTTON,
+ PROP_USE_CAPTURED,
+ PROP_OVERSHOOT
+};
+
+enum
+{
+ /* normal signals */
+ PANNING_COMPLETED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+/* MxScrollableIface implementation */
+
+static void
+mx_kinetic_scroll_view_set_adjustments (MxScrollable *scrollable,
+ MxAdjustment *hadjustment,
+ MxAdjustment *vadjustment)
+{
+ MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (scrollable)->priv;
+
+ if (priv->child)
+ mx_scrollable_set_adjustments (MX_SCROLLABLE (priv->child),
+ hadjustment,
+ vadjustment);
+}
+
+static void
+mx_kinetic_scroll_view_get_adjustments (MxScrollable *scrollable,
+ MxAdjustment **hadjustment,
+ MxAdjustment **vadjustment)
+{
+ MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (scrollable)->priv;
+
+ if (priv->child)
+ {
+ mx_scrollable_get_adjustments (MX_SCROLLABLE (priv->child),
+ hadjustment,
+ vadjustment);
+ }
+ else
+ {
+ if (hadjustment)
+ *hadjustment = NULL;
+ if (vadjustment)
+ *vadjustment = NULL;
+ }
+}
+
+static void
+mx_scrollable_iface_init (MxScrollableIface *iface)
+{
+ iface->set_adjustments = mx_kinetic_scroll_view_set_adjustments;
+ iface->get_adjustments = mx_kinetic_scroll_view_get_adjustments;
+}
+
+/* Object implementation */
+
+static void
+mx_kinetic_scroll_view_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MxAdjustment *adjustment;
+ MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (object)->priv;
+
+ switch (property_id)
+ {
+ case PROP_DECELERATION :
+ g_value_set_double (value, priv->decel_rate);
+ break;
+
+/*
+ case PROP_BUFFER_SIZE :
+ g_value_set_uint (value, priv->motion_buffer->len);
+ break;
+*/
+
+ case PROP_HADJUST:
+ mx_kinetic_scroll_view_get_adjustments (MX_SCROLLABLE (object),
+ &adjustment, NULL);
+ g_value_set_object (value, adjustment);
+ break;
+
+ case PROP_VADJUST:
+ mx_kinetic_scroll_view_get_adjustments (MX_SCROLLABLE (object),
+ NULL, &adjustment);
+ g_value_set_object (value, adjustment);
+ break;
+
+ case PROP_BUTTON:
+ g_value_set_uint (value, priv->button);
+ break;
+
+ case PROP_USE_CAPTURED:
+ g_value_set_boolean (value, priv->use_captured);
+ break;
+
+ case PROP_OVERSHOOT:
+ g_value_set_double (value, priv->overshoot);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+mx_kinetic_scroll_view_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MxAdjustment *adjustment;
+ MxScrollable *scrollable;
+ MxKineticScrollView *self = MX_KINETIC_SCROLL_VIEW (object);
+
+ switch (property_id)
+ {
+ case PROP_DECELERATION :
+ mx_kinetic_scroll_view_set_deceleration (self,
+ g_value_get_double (value));
+ break;
+
+/*
+ case PROP_BUFFER_SIZE :
+ mx_kinetic_scroll_view_set_buffer_size (self, g_value_get_uint (value));
+ break;
+*/
+
+ case PROP_HADJUST:
+ scrollable = MX_SCROLLABLE (object);
+ mx_kinetic_scroll_view_get_adjustments (scrollable, NULL, &adjustment);
+ mx_kinetic_scroll_view_set_adjustments (scrollable,
+ g_value_get_object (value),
+ adjustment);
+ break;
+
+ case PROP_VADJUST:
+ scrollable = MX_SCROLLABLE (object);
+ mx_kinetic_scroll_view_get_adjustments (scrollable, &adjustment, NULL);
+ mx_kinetic_scroll_view_set_adjustments (scrollable,
+ adjustment,
+ g_value_get_object (value));
+ break;
+
+ case PROP_BUTTON:
+ mx_kinetic_scroll_view_set_mouse_button (self, g_value_get_uint (value));
+ break;
+
+ case PROP_USE_CAPTURED:
+ mx_kinetic_scroll_view_set_use_captured (self,
+ g_value_get_boolean (value));
+ break;
+
+ case PROP_OVERSHOOT:
+ mx_kinetic_scroll_view_set_overshoot (self, g_value_get_double (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+mx_kinetic_scroll_view_dispose (GObject *object)
+{
+ MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (object)->priv;
+
+ if (priv->deceleration_timeline)
+ {
+ clutter_timeline_stop (priv->deceleration_timeline);
+ g_object_unref (priv->deceleration_timeline);
+ priv->deceleration_timeline = NULL;
+ }
+
+ G_OBJECT_CLASS (mx_kinetic_scroll_view_parent_class)->dispose (object);
+}
+
+static void
+mx_kinetic_scroll_view_finalize (GObject *object)
+{
+ MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (object)->priv;
+
+ g_array_free (priv->motion_buffer, TRUE);
+
+ G_OBJECT_CLASS (mx_kinetic_scroll_view_parent_class)->finalize (object);
+}
+
+static void
+mx_kinetic_scroll_view_get_preferred_width (ClutterActor *actor,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *nat_width_p)
+{
+ CLUTTER_ACTOR_CLASS (mx_kinetic_scroll_view_parent_class)->
+ get_preferred_width (actor, for_height, NULL, nat_width_p);
+
+ if (min_width_p)
+ {
+ MxPadding padding = { 0, };
+
+// mx_widget_get_padding (MX_WIDGET (actor), &padding);
+ *min_width_p = padding.left + padding.right;
+ }
+}
+
+static void
+mx_kinetic_scroll_view_get_preferred_height (ClutterActor *actor,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *nat_height_p)
+{
+ CLUTTER_ACTOR_CLASS (mx_kinetic_scroll_view_parent_class)->
+ get_preferred_height (actor, for_width, NULL, nat_height_p);
+
+ if (min_height_p)
+ {
+ MxPadding padding = { 0, };
+
+// mx_widget_get_padding (MX_WIDGET (actor), &padding);
+ *min_height_p = padding.top + padding.bottom;
+ }
+}
+
+static void
+mx_kinetic_scroll_view_allocate (ClutterActor *actor,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags)
+{
+ CLUTTER_ACTOR_CLASS (mx_kinetic_scroll_view_parent_class)->
+ allocate (actor, box, flags);
+
+ mx_bin_allocate_child (MX_BIN (actor), box, flags);
+}
+
+static void
+mx_kinetic_scroll_view_class_init (MxKineticScrollViewClass *klass)
+{
+ GParamSpec *pspec;
+
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (MxKineticScrollViewPrivate));
+
+ object_class->get_property = mx_kinetic_scroll_view_get_property;
+ object_class->set_property = mx_kinetic_scroll_view_set_property;
+ object_class->dispose = mx_kinetic_scroll_view_dispose;
+ object_class->finalize = mx_kinetic_scroll_view_finalize;
+
+ actor_class->get_preferred_width =
+ mx_kinetic_scroll_view_get_preferred_width;
+ actor_class->get_preferred_height =
+ mx_kinetic_scroll_view_get_preferred_height;
+ actor_class->allocate =
+ mx_kinetic_scroll_view_allocate;
+
+ pspec = g_param_spec_double ("deceleration",
+ "Deceleration",
+ "Rate at which the view will decelerate in.",
+ 1.1, G_MAXDOUBLE, 1.1,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_DECELERATION, pspec);
+
+ /*
+ pspec = g_param_spec_uint ("buffer-size",
+ "Buffer size",
+ "Amount of motion events to buffer",
+ 1, G_MAXUINT, 3,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_BUFFER_SIZE, pspec);
+ */
+
+ pspec = g_param_spec_uint ("mouse-button",
+ "Mouse button",
+ "The mouse button used to control scrolling",
+ 0, G_MAXUINT, 1,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_BUTTON, pspec);
+
+ pspec = g_param_spec_boolean ("use-captured",
+ "Use captured",
+ "Use captured events to initiate scrolling",
+ FALSE,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_USE_CAPTURED, pspec);
+
+ pspec = g_param_spec_double ("overshoot",
+ "Overshoot",
+ "The rate at which the view will decelerate "
+ "when scrolled beyond its boundaries.",
+ 0.0, 1.0, 0.0,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_OVERSHOOT, pspec);
+
+ /* MxScrollable properties */
+ g_object_class_override_property (object_class,
+ PROP_HADJUST,
+ "horizontal-adjustment");
+
+ g_object_class_override_property (object_class,
+ PROP_VADJUST,
+ "vertical-adjustment");
+
+ signals[PANNING_COMPLETED] =
+ g_signal_new ("panning-completed", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+ _mx_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+static gboolean
+motion_event_cb (ClutterActor *stage,
+ ClutterMotionEvent *event,
+ MxKineticScrollView *scroll)
+{
+ gfloat x, y;
+
+ MxKineticScrollViewPrivate *priv = scroll->priv;
+ ClutterActor *actor = CLUTTER_ACTOR (scroll);
+
+ if (event->type != CLUTTER_MOTION)
+ return FALSE;
+
+ if (clutter_actor_transform_stage_point (actor,
+ event->x,
+ event->y,
+ &x, &y))
+ {
+ MxKineticScrollViewMotion *motion;
+ ClutterActor *child = mx_bin_get_child (MX_BIN (scroll));
+
+ /* Check if we've passed the drag threshold */
+ if (!priv->in_drag)
+ {
+ guint threshold = 8; // default value
+
+ motion = &g_array_index (priv->motion_buffer,
+ MxKineticScrollViewMotion, 0);
+
+ if ((ABS (motion->x - x) >= threshold) ||
+ (ABS (motion->y - y) >= threshold))
+ {
+ clutter_set_motion_events_enabled (TRUE);
+ priv->in_drag = TRUE;
+ }
+ else
+ return FALSE;
+ }
+
+ if (child)
+ {
+ gdouble dx, dy;
+ MxAdjustment *hadjust, *vadjust;
+
+ mx_scrollable_get_adjustments (MX_SCROLLABLE (child),
+ &hadjust, &vadjust);
+
+ motion = &g_array_index (priv->motion_buffer,
+ MxKineticScrollViewMotion,
+ priv->last_motion);
+
+ if (hadjust)
+ {
+ dx = (motion->x - x) + mx_adjustment_get_value (hadjust);
+ mx_adjustment_set_value (hadjust, dx);
+ }
+
+ if (vadjust)
+ {
+ dy = (motion->y - y) + mx_adjustment_get_value (vadjust);
+ mx_adjustment_set_value (vadjust, dy);
+ }
+ }
+
+ priv->last_motion ++;
+ if (priv->last_motion == priv->motion_buffer->len)
+ {
+ priv->motion_buffer = g_array_remove_index (priv->motion_buffer, 0);
+ g_array_set_size (priv->motion_buffer, priv->last_motion);
+ priv->last_motion --;
+ }
+
+ motion = &g_array_index (priv->motion_buffer,
+ MxKineticScrollViewMotion, priv->last_motion);
+ motion->x = x;
+ motion->y = y;
+ g_get_current_time (&motion->time);
+ }
+
+ return TRUE;
+}
+
+static void
+clamp_adjustments (MxKineticScrollView *scroll,
+ guint duration,
+ gboolean horizontal,
+ gboolean vertical)
+{
+ ClutterActor *child = mx_bin_get_child (MX_BIN (scroll));
+
+ if (child)
+ {
+ gdouble d, value, lower, upper, step_increment, page_size;
+ MxAdjustment *hadj, *vadj;
+
+ mx_scrollable_get_adjustments (MX_SCROLLABLE (child),
+ &hadj, &vadj);
+
+ if (horizontal && hadj)
+ {
+ /* Snap to the nearest step increment on hadjustment */
+ mx_adjustment_get_values (hadj, &value, &lower, &upper,
+ &step_increment, NULL, &page_size);
+ d = (rint ((value - lower) / step_increment) *
+ step_increment) + lower;
+ d = CLAMP (d, lower, upper - page_size);
+ mx_adjustment_interpolate (hadj, d, duration, CLUTTER_EASE_OUT_QUAD);
+ }
+
+ if (vertical && vadj)
+ {
+ /* Snap to the nearest step increment on vadjustment */
+ mx_adjustment_get_values (vadj, &value, &lower, &upper,
+ &step_increment, NULL, &page_size);
+ d = (rint ((value - lower) / step_increment) *
+ step_increment) + lower;
+ d = CLAMP (d, lower, upper - page_size);
+ mx_adjustment_interpolate (vadj, d, duration, CLUTTER_EASE_OUT_QUAD);
+ }
+ }
+}
+
+static void
+deceleration_completed_cb (ClutterTimeline *timeline,
+ MxKineticScrollView *scroll)
+{
+ MxKineticScrollViewPrivate *priv = scroll->priv;
+
+ clamp_adjustments (scroll, (priv->overshoot > 0.0) ? 250 : 10,
+ priv->hmoving, priv->vmoving);
+
+ g_object_unref (timeline);
+ priv->deceleration_timeline = NULL;
+
+ g_signal_emit_by_name (scroll, "panning-completed", NULL);
+}
+
+static void
+deceleration_new_frame_cb (ClutterTimeline *timeline,
+ gint frame_num,
+ MxKineticScrollView *scroll)
+{
+ MxKineticScrollViewPrivate *priv = scroll->priv;
+ ClutterActor *child = mx_bin_get_child (MX_BIN (scroll));
+
+ if (child)
+ {
+ MxAdjustment *hadjust, *vadjust;
+
+ gboolean stop = TRUE;
+
+ mx_scrollable_get_adjustments (MX_SCROLLABLE (child),
+ &hadjust, &vadjust);
+
+ priv->accumulated_delta += clutter_timeline_get_delta (timeline);
+
+ if (priv->accumulated_delta <= 1000.0/60.0)
+ stop = FALSE;
+
+ while (priv->accumulated_delta > 1000.0/60.0)
+ {
+ gdouble hvalue, vvalue;
+
+ if (hadjust)
+ {
+ if (ABS (priv->dx) > 0.1)
+ {
+ hvalue = priv->dx + mx_adjustment_get_value (hadjust);
+ mx_adjustment_set_value (hadjust, hvalue);
+
+ if (priv->overshoot > 0.0)
+ {
+ if ((hvalue > mx_adjustment_get_upper (hadjust) -
+ mx_adjustment_get_page_size (hadjust)) ||
+ (hvalue < mx_adjustment_get_lower (hadjust)))
+ priv->dx *= priv->overshoot;
+ }
+
+ priv->dx = priv->dx / priv->decel_rate;
+
+ stop = FALSE;
+ }
+ else if (priv->hmoving)
+ {
+ priv->hmoving = FALSE;
+ clamp_adjustments (scroll,
+ (priv->overshoot > 0.0) ? 250 : 10,
+ TRUE, FALSE);
+ }
+ }
+
+ if (vadjust)
+ {
+ if (ABS (priv->dy) > 0.1)
+ {
+ vvalue = priv->dy + mx_adjustment_get_value (vadjust);
+ mx_adjustment_set_value (vadjust, vvalue);
+
+ if (priv->overshoot > 0.0)
+ {
+ if ((vvalue > mx_adjustment_get_upper (vadjust) -
+ mx_adjustment_get_page_size (vadjust)) ||
+ (vvalue < mx_adjustment_get_lower (vadjust)))
+ priv->dy *= priv->overshoot;
+ }
+
+ priv->dy = priv->dy / priv->decel_rate;
+
+ stop = FALSE;
+ }
+ else if (priv->vmoving)
+ {
+ priv->vmoving = FALSE;
+ clamp_adjustments (scroll,
+ (priv->overshoot > 0.0) ? 250 : 10,
+ FALSE, TRUE);
+ }
+ }
+
+ priv->accumulated_delta -= 1000.0/60.0;
+ }
+
+ if (stop)
+ {
+ clutter_timeline_stop (timeline);
+ deceleration_completed_cb (timeline, scroll);
+ }
+ }
+}
+
+static gboolean
+button_release_event_cb (ClutterActor *stage,
+ ClutterButtonEvent *event,
+ MxKineticScrollView *scroll)
+{
+ MxKineticScrollViewPrivate *priv = scroll->priv;
+ ClutterActor *actor = CLUTTER_ACTOR (scroll);
+ ClutterActor *child = mx_bin_get_child (MX_BIN (scroll));
+ gboolean decelerating = FALSE;
+
+ if ((event->type != CLUTTER_BUTTON_RELEASE) ||
+ (event->button != priv->button))
+ return FALSE;
+
+ g_signal_handlers_disconnect_by_func (stage,
+ motion_event_cb,
+ scroll);
+ g_signal_handlers_disconnect_by_func (stage,
+ button_release_event_cb,
+ scroll);
+
+ if (!priv->in_drag)
+ return FALSE;
+
+ clutter_set_motion_events_enabled (TRUE);
+
+ if (child)
+ {
+ gfloat event_x, event_y;
+
+ if (clutter_actor_transform_stage_point (actor, event->x, event->y,
+ &event_x, &event_y))
+ {
+ gdouble value, lower, upper, step_increment, page_size,
+ d, ax, ay, y, nx, ny, n;
+ gfloat frac, x_origin, y_origin;
+ GTimeVal release_time, motion_time;
+ MxAdjustment *hadjust, *vadjust;
+ glong time_diff;
+ guint duration;
+ gint i;
+
+ /* Get time delta */
+ g_get_current_time (&release_time);
+
+ /* Get average position/time of last x mouse events */
+ priv->last_motion ++;
+ x_origin = y_origin = 0;
+ motion_time = (GTimeVal){ 0, 0 };
+ for (i = 0; i < priv->last_motion; i++)
+ {
+ MxKineticScrollViewMotion *motion =
+ &g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, i);
+
+ /* FIXME: This doesn't guard against overflows - Should
+ * either fix that, or calculate the correct maximum
+ * value for the buffer size
+ */
+ x_origin += motion->x;
+ y_origin += motion->y;
+ motion_time.tv_sec += motion->time.tv_sec;
+ motion_time.tv_usec += motion->time.tv_usec;
+ }
+ x_origin = x_origin / priv->last_motion;
+ y_origin = y_origin / priv->last_motion;
+ motion_time.tv_sec /= priv->last_motion;
+ motion_time.tv_usec /= priv->last_motion;
+
+ if (motion_time.tv_sec == release_time.tv_sec)
+ time_diff = release_time.tv_usec - motion_time.tv_usec;
+ else
+ time_diff = release_time.tv_usec +
+ (G_USEC_PER_SEC - motion_time.tv_usec);
+
+ /* Work out the fraction of 1/60th of a second that has elapsed */
+ frac = (time_diff/1000.0) / (1000.0/60.0);
+
+ /* See how many units to move in 1/60th of a second */
+ priv->dx = (x_origin - event_x) / frac;
+ priv->dy = (y_origin - event_y) / frac;
+
+ /* If the delta is too low for the equations to work,
+ * bump the values up a bit.
+ */
+ if (ABS (priv->dx) < 1)
+ priv->dx = (priv->dx > 0) ? 1 : -1;
+ if (ABS (priv->dy) < 1)
+ priv->dy = (priv->dy > 0) ? 1 : -1;
+
+ /* We want n, where x / y^n < z,
+ * x = Distance to move per frame
+ * y = Deceleration rate
+ * z = maximum distance from target
+ *
+ * Rearrange to n = log (x / z) / log (y)
+ * To simplify, z = 1, so n = log (x) / log (y)
+ */
+ y = priv->decel_rate;
+ nx = logf (ABS (priv->dx)) / logf (y);
+ ny = logf (ABS (priv->dy)) / logf (y);
+ n = MAX (nx, ny);
+
+ duration = MAX (1, (gint)(MAX (nx, ny) * (1000/60.0)));
+
+ if (duration > 250)
+ {
+ /* Now we have n, adjust dx/dy so that we finish on a step
+ * boundary.
+ *
+ * Distance moved, using the above variable names:
+ *
+ * d = x + x/y + x/y^2 + ... + x/y^n
+ *
+ * Using geometric series,
+ *
+ * d = (1 - 1/y^(n+1))/(1 - 1/y)*x
+ *
+ * Let a = (1 - 1/y^(n+1))/(1 - 1/y),
+ *
+ * d = a * x
+ *
+ * Find d and find its nearest page boundary, then solve for x
+ *
+ * x = d / a
+ */
+
+ /* Get adjustments, work out y^n */
+ mx_scrollable_get_adjustments (MX_SCROLLABLE (child),
+ &hadjust, &vadjust);
+ ax = (1.0 - 1.0 / pow (y, n + 1)) / (1.0 - 1.0 / y);
+ ay = (1.0 - 1.0 / pow (y, n + 1)) / (1.0 - 1.0 / y);
+
+ /* Solving for dx */
+ if (hadjust)
+ {
+ mx_adjustment_get_values (hadjust, &value, &lower, &upper,
+ &step_increment, NULL, &page_size);
+
+ /* Make sure we pick the next nearest step increment in the
+ * same direction as the push.
+ */
+ priv->dx *= n;
+ if (ABS (priv->dx) < step_increment / 2)
+ d = round ((value + priv->dx - lower) / step_increment);
+ else if (priv->dx > 0)
+ d = ceil ((value + priv->dx - lower) / step_increment);
+ else
+ d = floor ((value + priv->dx - lower) / step_increment);
+
+ if (priv->overshoot <= 0.0)
+ d = CLAMP ((d * step_increment) + lower,
+ lower, upper - page_size) - value;
+ else
+ d = ((d * step_increment) + lower) - value;
+
+ priv->dx = d / ax;
+ }
+
+ /* Solving for dy */
+ if (vadjust)
+ {
+ mx_adjustment_get_values (vadjust, &value, &lower, &upper,
+ &step_increment, NULL, &page_size);
+
+ priv->dy *= n;
+ if (ABS (priv->dy) < step_increment / 2)
+ d = round ((value + priv->dy - lower) / step_increment);
+ else if (priv->dy > 0)
+ d = ceil ((value + priv->dy - lower) / step_increment);
+ else
+ d = floor ((value + priv->dy - lower) / step_increment);
+
+ if (priv->overshoot <= 0.0)
+ d = CLAMP ((d * step_increment) + lower,
+ lower, upper - page_size) - value;
+ else
+ d = ((d * step_increment) + lower) - value;
+
+ priv->dy = d / ay;
+ }
+
+ priv->deceleration_timeline = clutter_timeline_new (duration);
+
+ g_signal_connect (priv->deceleration_timeline, "new_frame",
+ G_CALLBACK (deceleration_new_frame_cb), scroll);
+ g_signal_connect (priv->deceleration_timeline, "completed",
+ G_CALLBACK (deceleration_completed_cb), scroll);
+ priv->accumulated_delta = 0;
+ priv->hmoving = priv->vmoving = TRUE;
+ clutter_timeline_start (priv->deceleration_timeline);
+ decelerating = TRUE;
+ }
+ }
+ }
+
+ /* Reset motion event buffer */
+ priv->last_motion = 0;
+
+ if (!decelerating)
+ {
+ clamp_adjustments (scroll, 250, TRUE, TRUE);
+ g_signal_emit_by_name (scroll, "panning-completed", NULL);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+button_press_event_cb (ClutterActor *actor,
+ ClutterEvent *event,
+ MxKineticScrollView *scroll)
+{
+ MxKineticScrollViewPrivate *priv = scroll->priv;
+ ClutterButtonEvent *bevent = (ClutterButtonEvent *)event;
+ ClutterActor *stage = clutter_actor_get_stage (actor);
+
+ if ((event->type == CLUTTER_BUTTON_PRESS) &&
+ (bevent->button == priv->button) &&
+ stage)
+ {
+ MxKineticScrollViewMotion *motion;
+
+ /* Reset motion buffer */
+ priv->last_motion = 0;
+ motion = &g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, 0);
+
+ if (clutter_actor_transform_stage_point (actor, bevent->x, bevent->y,
+ &motion->x, &motion->y))
+ {
+ guint threshold = 8; //default value
+
+ g_get_current_time (&motion->time);
+
+ if (priv->deceleration_timeline)
+ {
+ clutter_timeline_stop (priv->deceleration_timeline);
+ g_object_unref (priv->deceleration_timeline);
+ priv->deceleration_timeline = NULL;
+ }
+
+ g_signal_connect (stage,
+ "captured-event",
+ G_CALLBACK (motion_event_cb),
+ scroll);
+ g_signal_connect (stage,
+ "captured-event",
+ G_CALLBACK (button_release_event_cb),
+ scroll);
+
+ /* If there's a zero drag threshold, start the drag immediately */
+ if (threshold == 0)
+ {
+ priv->in_drag = TRUE;
+ clutter_set_motion_events_enabled (FALSE);
+
+ /* Swallow the press event */
+ return TRUE;
+ }
+ else
+ priv->in_drag = FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+mx_kinetic_scroll_view_actor_added_cb (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (container)->priv;
+
+ if (MX_IS_SCROLLABLE (actor))
+ {
+ MxAdjustment *hadjust, *vadjust;
+
+ priv->child = actor;
+
+ /* Make sure the adjustments have been created so the child
+ * will initialise them during its allocation (necessary for
+ * MxBoxLayout, for example)
+ */
+ mx_scrollable_get_adjustments (MX_SCROLLABLE (actor), &hadjust, &vadjust);
+ }
+ else
+ g_warning ("Attempting to add an actor of type %s to "
+ "a MxKineticScrollView, but the actor does "
+ "not implement MxScrollable.",
+ g_type_name (G_OBJECT_TYPE (actor)));
+}
+
+static void
+mx_kinetic_scroll_view_actor_removed_cb (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (container)->priv;
+ priv->child = NULL;
+}
+
+static void
+mx_kinetic_scroll_view_init (MxKineticScrollView *self)
+{
+ MxKineticScrollViewPrivate *priv = self->priv =
+ KINETIC_SCROLL_VIEW_PRIVATE (self);
+
+ priv->motion_buffer =
+ g_array_sized_new (FALSE, TRUE, sizeof (MxKineticScrollViewMotion), 3);
+ g_array_set_size (priv->motion_buffer, 3);
+ priv->decel_rate = 1.1f;
+ priv->button = 1;
+
+ clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
+ g_signal_connect (self, "button-press-event",
+ G_CALLBACK (button_press_event_cb), self);
+ g_signal_connect (self, "actor-added",
+ G_CALLBACK (mx_kinetic_scroll_view_actor_added_cb), self);
+ g_signal_connect (self, "actor-removed",
+ G_CALLBACK (mx_kinetic_scroll_view_actor_removed_cb), self);
+
+ mx_bin_set_alignment (MX_BIN (self), MX_ALIGN_START, MX_ALIGN_START);
+}
+
+/**
+ * mx_kinetic_scroll_view_new:
+ *
+ * Creates a new #MxKineticScrollView.
+ *
+ * Returns: a newly allocated #MxKineticScrollView
+ */
+ClutterActor *
+mx_kinetic_scroll_view_new ()
+{
+ return g_object_new (MX_TYPE_KINETIC_SCROLL_VIEW, NULL);
+}
+
+/**
+ * mx_kinetic_scroll_view_stop:
+ * @scroll: A #MxKineticScrollView
+ *
+ * Stops any current movement due to kinetic scrolling.
+ */
+void
+mx_kinetic_scroll_view_stop (MxKineticScrollView *scroll)
+{
+ MxKineticScrollViewPrivate *priv;
+
+ g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll));
+
+ priv = scroll->priv;
+
+ if (priv->deceleration_timeline)
+ {
+ clutter_timeline_stop (priv->deceleration_timeline);
+ g_object_unref (priv->deceleration_timeline);
+ priv->deceleration_timeline = NULL;
+ }
+}
+
+/**
+ * mx_kinetic_scroll_view_set_deceleration:
+ * @scroll: A #MxKineticScrollView
+ * @rate: The deceleration rate
+ *
+ * Sets the deceleration rate when a drag is finished on the kinetic
+ * scroll-view. This is the value that the momentum is divided by
+ * every 60th of a second.
+ */
+void
+mx_kinetic_scroll_view_set_deceleration (MxKineticScrollView *scroll,
+ gdouble rate)
+{
+ MxKineticScrollViewPrivate *priv;
+
+ g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll));
+ g_return_if_fail (rate >= 1.1);
+
+ priv = scroll->priv;
+
+ if (priv->decel_rate != rate)
+ {
+ priv->decel_rate = rate;
+ g_object_notify (G_OBJECT (scroll), "deceleration");
+ }
+}
+
+/**
+ * mx_kinetic_scroll_view_get_deceleration:
+ * @scroll: A #MxKineticScrollView
+ *
+ * Retrieves the deceleration rate of the kinetic scroll-view.
+ *
+ * Returns: The deceleration rate of the kinetic scroll-view
+ */
+gdouble
+mx_kinetic_scroll_view_get_deceleration (MxKineticScrollView *scroll)
+{
+ g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 0.0);
+ return scroll->priv->decel_rate;
+}
+
+/*
+void
+mx_kinetic_scroll_view_set_buffer_size (MxKineticScrollView *scroll,
+ guint size)
+{
+ MxKineticScrollViewPrivate *priv;
+
+ g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll));
+ g_return_if_fail (size > 0);
+
+ priv = scroll->priv;
+ if (priv->motion_buffer->len != size)
+ {
+ g_array_set_size (priv->motion_buffer, size);
+ g_object_notify (G_OBJECT (scroll), "buffer-size");
+ }
+}
+
+guint
+mx_kinetic_scroll_view_get_buffer_size (MxKineticScrollView *scroll)
+{
+ g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 0);
+ return scroll->priv->motion_buffer->len;
+}
+*/
+
+/**
+ * mx_kinetic_scroll_view_set_mouse_button:
+ * @scroll: A #MxKineticScrollView
+ * @button: A mouse button number
+ *
+ * Sets the mouse button number used to initiate drag events on the kinetic
+ * scroll-view.
+ */
+void
+mx_kinetic_scroll_view_set_mouse_button (MxKineticScrollView *scroll,
+ guint32 button)
+{
+ MxKineticScrollViewPrivate *priv;
+
+ g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll));
+
+ priv = scroll->priv;
+
+ if (priv->button != button)
+ {
+ priv->button = button;
+ g_object_notify (G_OBJECT (scroll), "mouse-button");
+ }
+}
+
+/**
+ * mx_kinetic_scroll_view_get_mouse_button:
+ * @scroll: A #MxKineticScrollView
+ *
+ * Gets the #MxKineticScrollView:mouse-button property
+ *
+ * Returns: The mouse button number used to initiate drag events on the
+ * kinetic scroll-view
+ */
+guint32
+mx_kinetic_scroll_view_get_mouse_button (MxKineticScrollView *scroll)
+{
+ g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 0);
+ return scroll->priv->button;
+}
+
+/**
+ * mx_kinetic_scroll_view_set_use_captured:
+ * @scroll: A #MxKineticScrollView
+ * @use_captured: %TRUE to use captured events
+ *
+ * Sets whether to use captured events to initiate drag events. This can be
+ * used to block events that would initiate scrolling from reaching the child
+ * actor.
+ */
+void
+mx_kinetic_scroll_view_set_use_captured (MxKineticScrollView *scroll,
+ gboolean use_captured)
+{
+ MxKineticScrollViewPrivate *priv;
+
+ g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll));
+
+ priv = scroll->priv;
+ if (priv->use_captured != use_captured)
+ {
+ priv->use_captured = use_captured;
+
+ g_signal_handlers_disconnect_by_func (scroll,
+ button_press_event_cb,
+ scroll);
+
+ g_signal_connect (scroll,
+ use_captured ? "captured-event" : "button-press-event",
+ G_CALLBACK (button_press_event_cb),
+ scroll);
+
+ g_object_notify (G_OBJECT (scroll), "use-captured");
+ }
+}
+
+/**
+ * mx_kinetic_scroll_view_get_use_captured:
+ * @scroll: A #MxKineticScrollView
+ *
+ * Gets the #MxKineticScrollView:use-captured property.
+ *
+ * Returns: %TRUE if captured-events should be used to initiate scrolling
+ */
+gboolean
+mx_kinetic_scroll_view_get_use_captured (MxKineticScrollView *scroll)
+{
+ g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), FALSE);
+ return scroll->priv->use_captured;
+}
+
+/**
+ * mx_kinetic_scroll_view_set_overshoot:
+ * @scroll: A #MxKineticScrollView
+ * @overshoot: The rate at which the view will decelerate when scrolling beyond
+ * its boundaries.
+ *
+ * Sets the rate at which the view will decelerate when scrolling beyond its
+ * boundaries. The deceleration rate will be multiplied by this value every
+ * 60th of a second when the view is scrolling outside of the range set by its
+ * adjustments.
+ *
+ * See mx_kinetic_scroll_view_set_deceleration()
+ */
+void
+mx_kinetic_scroll_view_set_overshoot (MxKineticScrollView *scroll,
+ gdouble overshoot)
+{
+ MxKineticScrollViewPrivate *priv;
+
+ g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll));
+
+ priv = scroll->priv;
+ if (priv->overshoot != overshoot)
+ {
+ priv->overshoot = overshoot;
+ g_object_notify (G_OBJECT (scroll), "overshoot");
+ }
+}
+
+/**
+ * mx_kinetic_scroll_view_get_overshoot:
+ * @scroll: A #MxKineticScrollView
+ *
+ * Retrieves the deceleration rate multiplier used when the scroll-view is
+ * scrolling beyond its boundaries.
+ */
+gdouble
+mx_kinetic_scroll_view_get_overshoot (MxKineticScrollView *scroll)
+{
+ g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 0.0);
+ return scroll->priv->overshoot;
+}
diff --git a/mx/mx-kinetic-scroll-view.h b/mx/mx-kinetic-scroll-view.h
new file mode 100644
index 0000000..b8efe28
--- /dev/null
+++ b/mx/mx-kinetic-scroll-view.h
@@ -0,0 +1,92 @@
+/* mx-kinetic-scroll-view.h: Kinetic scrolling container actor
+ *
+ * Copyright (C) 2008 OpenedHand
+ * Copyright (C) 2010 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that 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 linux intel com>
+ */
+
+#ifndef __MX_KINETIC_SCROLL_VIEW_H__
+#define __MX_KINETIC_SCROLL_VIEW_H__
+
+#include <glib-object.h>
+#include "mx-bin.h"
+
+G_BEGIN_DECLS
+
+#define MX_TYPE_KINETIC_SCROLL_VIEW (mx_kinetic_scroll_view_get_type())
+#define MX_KINETIC_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_KINETIC_SCROLL_VIEW, MxKineticScrollView))
+#define MX_IS_KINETIC_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_KINETIC_SCROLL_VIEW))
+#define MX_KINETIC_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_KINETIC_SCROLL_VIEW, MxKineticScrollViewClass))
+#define MX_IS_KINETIC_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_KINETIC_SCROLL_VIEW))
+#define MX_KINETIC_SCROLL_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_KINETIC_SCROLL_VIEW, MxKineticScrollViewClass))
+
+/**
+ * MxKineticScrollView:
+ *
+ * The contents of this structure is private and should only be accessed using
+ * the provided API.
+ */
+typedef struct _MxKineticScrollView MxKineticScrollView;
+typedef struct _MxKineticScrollViewPrivate MxKineticScrollViewPrivate;
+typedef struct _MxKineticScrollViewClass MxKineticScrollViewClass;
+
+struct _MxKineticScrollView
+{
+ /*< private >*/
+ MxBin parent_instance;
+
+ MxKineticScrollViewPrivate *priv;
+};
+
+struct _MxKineticScrollViewClass
+{
+ MxBinClass parent_class;
+};
+
+GType mx_kinetic_scroll_view_get_type (void) G_GNUC_CONST;
+
+ClutterActor *mx_kinetic_scroll_view_new (void);
+
+void mx_kinetic_scroll_view_stop (MxKineticScrollView *scroll);
+
+void mx_kinetic_scroll_view_set_deceleration (MxKineticScrollView *scroll,
+ gdouble rate);
+gdouble mx_kinetic_scroll_view_get_deceleration (MxKineticScrollView *scroll);
+
+/*
+void mx_kinetic_scroll_view_set_buffer_size (MxKineticScrollView *scroll,
+ guint size);
+guint mx_kinetic_scroll_view_get_buffer_size (MxKineticScrollView *scroll);
+*/
+
+void mx_kinetic_scroll_view_set_use_captured (MxKineticScrollView *scroll,
+ gboolean use_captured);
+gboolean mx_kinetic_scroll_view_get_use_captured (MxKineticScrollView *scroll);
+
+void mx_kinetic_scroll_view_set_mouse_button (MxKineticScrollView *scroll,
+ guint32 button);
+guint32 mx_kinetic_scroll_view_get_mouse_button (MxKineticScrollView *scroll);
+
+void mx_kinetic_scroll_view_set_overshoot (MxKineticScrollView *scroll,
+ gdouble overshoot);
+gdouble mx_kinetic_scroll_view_get_overshoot (MxKineticScrollView *scroll);
+
+G_END_DECLS
+
+#endif /* __MX_KINETIC_SCROLL_VIEW_H__ */
diff --git a/mx/mx-marshal.list b/mx/mx-marshal.list
new file mode 100644
index 0000000..bd59b23
--- /dev/null
+++ b/mx/mx-marshal.list
@@ -0,0 +1,8 @@
+VOID:OBJECT
+VOID:VOID
+VOID:PARAM
+VOID:UINT
+VOID:UINT,UINT
+VOID:OBJECT,OBJECT
+VOID:FLOAT,FLOAT,INT,ENUM
+VOID:FLOAT,FLOAT
diff --git a/tidy/tidy-private.h b/mx/mx-private.h
similarity index 82%
rename from tidy/tidy-private.h
rename to mx/mx-private.h
index 5f17d93..b18eaf3 100644
--- a/tidy/tidy-private.h
+++ b/mx/mx-private.h
@@ -1,4 +1,4 @@
-/* tidy-private.h: Private declarations
+/* mx-private.h: Private declarations
*
* Copyright (C) 2007 OpenedHand
*
@@ -18,8 +18,8 @@
* Boston, MA 02111-1307, USA.
*/
-#ifndef __TIDY_PRIVATE_H__
-#define __TIDY_PRIVATE_H__
+#ifndef __MX_PRIVATE_H__
+#define __MX_PRIVATE_H__
#include <glib.h>
@@ -27,14 +27,14 @@ G_BEGIN_DECLS
#define I_(str) (g_intern_static_string ((str)))
-#define TIDY_PARAM_READABLE \
- (G_PARAM_READABLE | \
+#define MX_PARAM_READWRITE \
+ (G_PARAM_READABLE | G_PARAM_WRITABLE | \
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
-#define TIDY_PARAM_READWRITE \
+#define MX_PARAM_READWRITE \
(G_PARAM_READABLE | G_PARAM_WRITABLE | \
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
G_END_DECLS
-#endif /* __TIDY_PRIVATE_H__ */
+#endif /* __MX_PRIVATE_H__ */
diff --git a/mx/mx-scrollable.c b/mx/mx-scrollable.c
new file mode 100644
index 0000000..52912b4
--- /dev/null
+++ b/mx/mx-scrollable.c
@@ -0,0 +1,99 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * mx-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 Mx by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#include "mx-scrollable.h"
+#include "mx-private.h"
+
+static void
+mx_scrollable_base_init (gpointer g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ GParamSpec *pspec;
+ pspec = g_param_spec_object ("horizontal-adjustment",
+ "Horizontal adjustment",
+ "The MxAdjustment for horizontal scrolling.",
+ MX_TYPE_ADJUSTMENT,
+ MX_PARAM_READWRITE);
+ g_object_interface_install_property (g_iface, pspec);
+
+ pspec = g_param_spec_object ("vertical-adjustment",
+ "Vertical adjustment",
+ "The MxAdjustment for vertical scrolling.",
+ MX_TYPE_ADJUSTMENT,
+ MX_PARAM_READWRITE);
+ g_object_interface_install_property (g_iface, pspec);
+
+ initialized = TRUE;
+ }
+}
+
+GType
+mx_scrollable_get_type (void)
+{
+ static GType type = 0;
+ if (type == 0)
+ {
+ static const GTypeInfo info =
+ {
+ sizeof (MxScrollableIface),
+ mx_scrollable_base_init, /* base_init */
+ NULL,
+ };
+ type = g_type_register_static (G_TYPE_INTERFACE,
+ "MxScrollable", &info, 0);
+ }
+ return type;
+}
+
+void
+mx_scrollable_set_adjustments (MxScrollable *scrollable,
+ MxAdjustment *hadjustment,
+ MxAdjustment *vadjustment)
+{
+ MX_SCROLLABLE_GET_IFACE (scrollable)->set_adjustments (scrollable,
+ hadjustment,
+ vadjustment);
+}
+
+/**
+ * mx_scroll_bar_get_adjustments:
+ * @hadjustment: (transfer none) (out) (allow-none): location to store the horizontal adjustment, or %NULL
+ * @vadjustment: (transfer none) (out) (allow-none): location to store the vertical adjustment, or %NULL
+ *
+ * Gets the adjustment objects that store the offsets of the scrollable widget
+ * into its possible scrolling area.
+ */
+void
+mx_scrollable_get_adjustments (MxScrollable *scrollable,
+ MxAdjustment **hadjustment,
+ MxAdjustment **vadjustment)
+{
+ MX_SCROLLABLE_GET_IFACE (scrollable)->get_adjustments (scrollable,
+ hadjustment,
+ vadjustment);
+}
diff --git a/mx/mx-scrollable.h b/mx/mx-scrollable.h
new file mode 100644
index 0000000..493f3ae
--- /dev/null
+++ b/mx/mx-scrollable.h
@@ -0,0 +1,71 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * mx-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 Mx by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#ifndef __MX_SCROLLABLE_H__
+#define __MX_SCROLLABLE_H__
+
+#include <glib-object.h>
+#include "mx-adjustment.h"
+
+G_BEGIN_DECLS
+
+#define MX_TYPE_SCROLLABLE (mx_scrollable_get_type ())
+#define MX_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_SCROLLABLE, MxScrollable))
+#define MX_IS_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_SCROLLABLE))
+#define MX_SCROLLABLE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MX_TYPE_SCROLLABLE, MxScrollableIface))
+
+/**
+ * MxScrollable:
+ *
+ * This is an opaque structure whose members cannot be directly accessed.
+ */
+typedef struct _MxScrollable MxScrollable; /* Dummy object */
+typedef struct _MxScrollableIface MxScrollableIface;
+
+struct _MxScrollableIface
+{
+ GTypeInterface parent;
+
+ void (* set_adjustments) (MxScrollable *scrollable,
+ MxAdjustment *hadjustment,
+ MxAdjustment *vadjustment);
+ void (* get_adjustments) (MxScrollable *scrollable,
+ MxAdjustment **hadjustment,
+ MxAdjustment **vadjustment);
+};
+
+GType mx_scrollable_get_type (void) G_GNUC_CONST;
+
+void mx_scrollable_set_adjustments (MxScrollable *scrollable,
+ MxAdjustment *hadjustment,
+ MxAdjustment *vadjustment);
+void mx_scrollable_get_adjustments (MxScrollable *scrollable,
+ MxAdjustment **hadjustment,
+ MxAdjustment **vadjustment);
+
+G_END_DECLS
+
+#endif /* __MX_SCROLLABLE_H__ */
diff --git a/mx/mx-types.h b/mx/mx-types.h
new file mode 100644
index 0000000..11eebbc
--- /dev/null
+++ b/mx/mx-types.h
@@ -0,0 +1,80 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2009, 2010 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.
+ *
+ */
+
+/**
+ * SECTION:mx-types
+ * @short_description: type definitions used throughout Mx
+ *
+ * Common types for MxWidgets.
+ */
+
+
+#ifndef __MX_TYPES_H__
+#define __MX_TYPES_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define MX_TYPE_PADDING (mx_padding_get_type ())
+
+#define MX_PARAM_TRANSLATEABLE 1 << 8
+
+typedef struct _MxPadding MxPadding;
+
+/**
+ * MxPadding:
+ * @top: padding from the top
+ * @right: padding from the right
+ * @bottom: padding from the bottom
+ * @left: padding from the left
+ *
+ * The padding from the internal border of the parent container.
+ */
+struct _MxPadding
+{
+ gfloat top;
+ gfloat right;
+ gfloat bottom;
+ gfloat left;
+};
+
+GType mx_padding_get_type (void) G_GNUC_CONST;
+
+
+
+/**
+ * MxAlign:
+ * @MX_ALIGN_START: Align at the beginning of the axis
+ * @MX_ALIGN_MIDDLE: Align in the middle of the axis
+ * @MX_ALIGN_END: Align at the end of the axis
+ *
+ * Set the alignment of the item
+ */
+typedef enum { /*< prefix=MX_ALIGN >*/
+ MX_ALIGN_START,
+ MX_ALIGN_MIDDLE,
+ MX_ALIGN_END
+} MxAlign;
+
+
+G_END_DECLS
+
+#endif /* __MX_TYPES_H__ */
diff --git a/mx/mx-viewport.c b/mx/mx-viewport.c
new file mode 100644
index 0000000..001614f
--- /dev/null
+++ b/mx/mx-viewport.c
@@ -0,0 +1,678 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * mx-viewport.c: Viewport actor
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009, 2010 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 Mx by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+/**
+ * SECTION:mx-viewport
+ * @short_description: single child scrollable container
+ *
+ * #MxViewport allows non-scrollable children (like images or text)
+ * to be scrollable by implementing the #MxScrollable and #ClutterContainer
+ * interface.
+ *
+ * To use it, add the non-scrollable child to an #MxViewport; then sit the
+ * viewport inside an #MxScrollView to get the scrollbars.
+ *
+ * <figure id="mx-viewport">
+ * <title>#MxViewport around an #MxLabel</title>
+ * <para>An example of a large label (which isn't normally scrollable),
+ * placed inside an #MxViewport, which is in turn inside an #MxScrollView.
+ * </para>
+ * <graphic fileref="MxViewport.png" format="PNG"/>
+ * </figure>
+ *
+ * Do not use #MxViewport if you need good performance as it does can not
+ * be selective about the area of its child that is painted/picked. Therefore
+ * if the child is very large or contains a lot of children, you will experience
+ * poor performance.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <clutter/clutter.h>
+
+#include "mx-viewport.h"
+#include "mx-adjustment.h"
+#include "mx-scrollable.h"
+#include "mx-private.h"
+#include "mx-bin.h"
+
+static void scrollable_interface_init (MxScrollableIface *iface);
+
+static void scrollable_set_adjustments (MxScrollable *scrollable,
+ MxAdjustment *hadjustment,
+ MxAdjustment *vadjustment);
+
+static void scrollable_get_adjustments (MxScrollable *scrollable,
+ MxAdjustment **hadjustment,
+ MxAdjustment **vadjustment);
+
+G_DEFINE_TYPE_WITH_CODE (MxViewport, mx_viewport, MX_TYPE_BIN,
+ G_IMPLEMENT_INTERFACE (MX_TYPE_SCROLLABLE,
+ scrollable_interface_init))
+
+#define VIEWPORT_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_VIEWPORT, \
+ MxViewportPrivate))
+
+struct _MxViewportPrivate
+{
+ gfloat x;
+ gfloat y;
+ gfloat z;
+
+ MxAdjustment *hadjustment;
+ MxAdjustment *vadjustment;
+
+ gboolean sync_adjustments;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_X_ORIGIN,
+ PROP_Y_ORIGIN,
+ PROP_Z_ORIGIN,
+ PROP_HADJUST,
+ PROP_VADJUST,
+ PROP_SYNC_ADJUST
+};
+
+static void
+mx_viewport_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MxAdjustment *adjustment;
+
+ MxViewportPrivate *priv = MX_VIEWPORT (object)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_X_ORIGIN:
+ g_value_set_float (value, priv->x);
+ break;
+
+ case PROP_Y_ORIGIN:
+ g_value_set_float (value, priv->y);
+ break;
+
+ case PROP_Z_ORIGIN:
+ g_value_set_float (value, priv->z);
+ break;
+
+ case PROP_HADJUST:
+ scrollable_get_adjustments (MX_SCROLLABLE (object), &adjustment, NULL);
+ g_value_set_object (value, adjustment);
+ break;
+
+ case PROP_VADJUST:
+ scrollable_get_adjustments (MX_SCROLLABLE (object), NULL, &adjustment);
+ g_value_set_object (value, adjustment);
+ break;
+
+ case PROP_SYNC_ADJUST:
+ g_value_set_boolean (value, priv->sync_adjustments);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+mx_viewport_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MxViewport *viewport = MX_VIEWPORT (object);
+ MxViewportPrivate *priv = viewport->priv;
+
+ switch (prop_id)
+ {
+ case PROP_X_ORIGIN:
+ mx_viewport_set_origin (viewport,
+ g_value_get_float (value),
+ priv->y,
+ priv->z);
+ break;
+
+ case PROP_Y_ORIGIN:
+ mx_viewport_set_origin (viewport,
+ priv->x,
+ g_value_get_float (value),
+ priv->z);
+ break;
+
+ case PROP_Z_ORIGIN:
+ mx_viewport_set_origin (viewport,
+ priv->x,
+ priv->y,
+ g_value_get_float (value));
+ break;
+
+ case PROP_HADJUST:
+ scrollable_set_adjustments (MX_SCROLLABLE (object),
+ g_value_get_object (value),
+ priv->vadjustment);
+ break;
+
+ case PROP_VADJUST:
+ scrollable_set_adjustments (MX_SCROLLABLE (object),
+ priv->hadjustment,
+ g_value_get_object (value));
+ break;
+
+ case PROP_SYNC_ADJUST:
+ mx_viewport_set_sync_adjustments (viewport, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+mx_viewport_dispose (GObject *gobject)
+{
+ MxViewportPrivate *priv = MX_VIEWPORT (gobject)->priv;
+
+ if (priv->hadjustment)
+ {
+ g_object_unref (priv->hadjustment);
+ priv->hadjustment = NULL;
+ }
+
+ if (priv->vadjustment)
+ {
+ g_object_unref (priv->vadjustment);
+ priv->vadjustment = NULL;
+ }
+
+ G_OBJECT_CLASS (mx_viewport_parent_class)->dispose (gobject);
+}
+
+static void
+mx_viewport_paint (ClutterActor *self)
+{
+ MxViewportPrivate *priv = MX_VIEWPORT (self)->priv;
+
+ cogl_push_matrix ();
+
+ cogl_translate ((int) priv->x * -1,
+ (int) priv->y * -1,
+ (int) priv->z * -1);
+
+ CLUTTER_ACTOR_CLASS (mx_viewport_parent_class)->paint (self);
+
+ cogl_pop_matrix ();
+}
+
+static void
+mx_viewport_pick (ClutterActor *self,
+ const ClutterColor *color)
+{
+ mx_viewport_paint (self);
+}
+
+static void
+mx_viewport_allocate (ClutterActor *self,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags)
+{
+ MxViewportPrivate *priv = MX_VIEWPORT (self)->priv;
+ MxPadding padding = { 0, };
+ ClutterActor *child;
+ gfloat width, height;
+ gfloat available_width, available_height;
+
+ /* Chain up. */
+ CLUTTER_ACTOR_CLASS (mx_viewport_parent_class)-> allocate (self, box, flags);
+
+// mx_widget_get_padding (MX_WIDGET (self), &padding);
+ available_width = box->x2 - box->x1 - padding.left - padding.right;
+ available_height = box->y2 - box->y1 - padding.top - padding.bottom;
+
+ child = mx_bin_get_child (MX_BIN (self));
+
+ if (child)
+ {
+ gfloat natural_width, natural_height;
+ ClutterActorBox child_box;
+ MxAlign x_align, y_align;
+ gboolean x_fill, y_fill;
+
+ clutter_actor_get_preferred_size (child, NULL, NULL, &natural_width,
+ &natural_height);
+ mx_bin_get_fill (MX_BIN (self), &x_fill, &y_fill);
+
+ if (x_fill && (available_width > natural_width))
+ width = available_width;
+ else
+ width = natural_width;
+
+ if (y_fill && (available_height > natural_height))
+ height = available_height;
+ else
+ height = natural_height;
+
+ mx_bin_get_alignment (MX_BIN (self), &x_align, &y_align);
+
+ if (!x_fill && width < available_width)
+ {
+ switch (x_align)
+ {
+ case MX_ALIGN_START:
+ child_box.x1 = padding.left;
+ break;
+ case MX_ALIGN_MIDDLE:
+ child_box.x1 = padding.left + (available_width - width) / 2.f;
+ break;
+ case MX_ALIGN_END:
+ child_box.x1 = box->x2 - box->x1 - padding.right - width;
+ break;
+ }
+ }
+ else
+ child_box.x1 = padding.left;
+
+ if (!y_fill && height < available_height)
+ {
+ switch (y_align)
+ {
+ case MX_ALIGN_START:
+ child_box.y1 = padding.top;
+ break;
+ case MX_ALIGN_MIDDLE:
+ child_box.y1 = padding.top + (available_height - height) / 2.f;
+ break;
+ case MX_ALIGN_END:
+ child_box.y1 = box->y2 - box->y1 - padding.bottom - height;
+ break;
+ }
+ }
+ else
+ child_box.y1 = padding.top;
+
+ child_box.x2 = child_box.x1 + width;
+ child_box.y2 = child_box.y1 + height;
+
+ clutter_actor_allocate (child, &child_box, flags);
+ }
+ else
+ {
+ width = 0;
+ height = 0;
+ }
+
+
+
+ /* Refresh adjustments */
+ if (priv->sync_adjustments)
+ {
+ if (priv->hadjustment)
+ {
+ g_object_set (G_OBJECT (priv->hadjustment),
+ "lower", 0.0,
+ "page-size", available_width,
+ "upper", width,
+ "page-increment", available_width / 3,
+ "step-increment", available_width / 12,
+ NULL);
+ }
+
+ if (priv->vadjustment)
+ {
+ g_object_set (G_OBJECT (priv->vadjustment),
+ "lower", 0.0,
+ "page-size", available_height,
+ "upper", height,
+ "page-increment", available_height / 3,
+ "step-increment", available_height / 12,
+ NULL);
+ }
+ }
+}
+
+static void
+mx_viewport_class_init (MxViewportClass *klass)
+{
+ GParamSpec *pspec;
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (MxViewportPrivate));
+
+ gobject_class->get_property = mx_viewport_get_property;
+ gobject_class->set_property = mx_viewport_set_property;
+ gobject_class->dispose = mx_viewport_dispose;
+
+ actor_class->paint = mx_viewport_paint;
+ actor_class->pick = mx_viewport_pick;
+ actor_class->allocate = mx_viewport_allocate;
+
+
+ pspec = g_param_spec_float ("x-origin",
+ "X Origin",
+ "Origin's X coordinate in pixels",
+ -G_MAXFLOAT, G_MAXFLOAT, 0,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_X_ORIGIN, pspec);
+
+ pspec = g_param_spec_float ("y-origin",
+ "Y Origin",
+ "Origin's Y coordinate in pixels",
+ -G_MAXFLOAT, G_MAXFLOAT, 0,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_Y_ORIGIN, pspec);
+
+ pspec = g_param_spec_float ("z-origin",
+ "Z Origin",
+ "Origin's Z coordinate in pixels",
+ -G_MAXFLOAT, G_MAXFLOAT, 0,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_Z_ORIGIN, pspec);
+
+ pspec = g_param_spec_boolean ("sync-adjustments",
+ "Synchronise adjustments",
+ "Whether to synchronise adjustments with "
+ "viewport size",
+ TRUE,
+ MX_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_SYNC_ADJUST, pspec);
+
+ g_object_class_override_property (gobject_class,
+ PROP_HADJUST,
+ "horizontal-adjustment");
+
+ g_object_class_override_property (gobject_class,
+ PROP_VADJUST,
+ "vertical-adjustment");
+}
+
+static void
+hadjustment_value_notify_cb (MxAdjustment *adjustment,
+ GParamSpec *pspec,
+ MxViewport *viewport)
+{
+ MxViewportPrivate *priv = viewport->priv;
+ gdouble value;
+
+ value = mx_adjustment_get_value (adjustment);
+
+ mx_viewport_set_origin (viewport,
+ (float)(value),
+ priv->y,
+ priv->z);
+}
+
+static void
+vadjustment_value_notify_cb (MxAdjustment *adjustment,
+ GParamSpec *arg1,
+ MxViewport *viewport)
+{
+ MxViewportPrivate *priv = viewport->priv;
+ gdouble value;
+
+ value = mx_adjustment_get_value (adjustment);
+
+ mx_viewport_set_origin (viewport,
+ priv->x,
+ (float)(value),
+ priv->z);
+}
+
+static void
+scrollable_set_adjustments (MxScrollable *scrollable,
+ MxAdjustment *hadjustment,
+ MxAdjustment *vadjustment)
+{
+ MxViewportPrivate *priv = MX_VIEWPORT (scrollable)->priv;
+
+ if (hadjustment != priv->hadjustment)
+ {
+ if (priv->hadjustment)
+ {
+ g_signal_handlers_disconnect_by_func (priv->hadjustment,
+ hadjustment_value_notify_cb,
+ scrollable);
+ g_object_unref (priv->hadjustment);
+ }
+
+ if (hadjustment)
+ {
+ g_object_ref (hadjustment);
+ g_signal_connect (hadjustment, "notify::value",
+ G_CALLBACK (hadjustment_value_notify_cb),
+ scrollable);
+ }
+
+ priv->hadjustment = hadjustment;
+ }
+
+ if (vadjustment != priv->vadjustment)
+ {
+ if (priv->vadjustment)
+ {
+ g_signal_handlers_disconnect_by_func (priv->vadjustment,
+ vadjustment_value_notify_cb,
+ scrollable);
+ g_object_unref (priv->vadjustment);
+ }
+
+ if (vadjustment)
+ {
+ g_object_ref (vadjustment);
+ g_signal_connect (vadjustment, "notify::value",
+ G_CALLBACK (vadjustment_value_notify_cb),
+ scrollable);
+ }
+
+ priv->vadjustment = vadjustment;
+ }
+}
+
+static void
+scrollable_get_adjustments (MxScrollable *scrollable,
+ MxAdjustment **hadjustment,
+ MxAdjustment **vadjustment)
+{
+ MxViewportPrivate *priv;
+
+ g_return_if_fail (MX_IS_VIEWPORT (scrollable));
+
+ priv = ((MxViewport *) scrollable)->priv;
+
+ if (hadjustment)
+ {
+ if (priv->hadjustment)
+ *hadjustment = priv->hadjustment;
+ else
+ {
+ MxAdjustment *adjustment;
+
+ /* create an initial adjustment. this is filled with correct values
+ * as soon as allocate() is called */
+
+ adjustment = mx_adjustment_new ();
+
+ scrollable_set_adjustments (scrollable,
+ adjustment,
+ priv->vadjustment);
+
+ g_object_unref (adjustment);
+
+ *hadjustment = adjustment;
+ }
+ }
+
+ if (vadjustment)
+ {
+ if (priv->vadjustment)
+ *vadjustment = priv->vadjustment;
+ else
+ {
+ MxAdjustment *adjustment;
+
+ /* create an initial adjustment. this is filled with correct values
+ * as soon as allocate() is called */
+
+ adjustment = mx_adjustment_new ();
+
+ scrollable_set_adjustments (scrollable,
+ priv->hadjustment,
+ adjustment);
+
+ g_object_unref (adjustment);
+
+ *vadjustment = adjustment;
+ }
+ }
+}
+
+static void
+scrollable_interface_init (MxScrollableIface *iface)
+{
+ iface->set_adjustments = scrollable_set_adjustments;
+ iface->get_adjustments = scrollable_get_adjustments;
+}
+
+static void
+mx_viewport_init (MxViewport *self)
+{
+ self->priv = VIEWPORT_PRIVATE (self);
+
+ self->priv->sync_adjustments = TRUE;
+
+ g_object_set (G_OBJECT (self),
+ "reactive", FALSE,
+ "clip-to-allocation", TRUE,
+ "x-align", MX_ALIGN_START,
+ "y-align", MX_ALIGN_START,
+ NULL);
+}
+
+ClutterActor *
+mx_viewport_new (void)
+{
+ return g_object_new (MX_TYPE_VIEWPORT, NULL);
+}
+
+void
+mx_viewport_set_origin (MxViewport *viewport,
+ gfloat x,
+ gfloat y,
+ gfloat z)
+{
+ MxViewportPrivate *priv;
+
+ g_return_if_fail (MX_IS_VIEWPORT (viewport));
+
+ priv = viewport->priv;
+
+ g_object_freeze_notify (G_OBJECT (viewport));
+
+ if (x != priv->x)
+ {
+ priv->x = x;
+ g_object_notify (G_OBJECT (viewport), "x-origin");
+
+ if (priv->hadjustment)
+ mx_adjustment_set_value (priv->hadjustment,
+ (float)(x));
+ }
+
+ if (y != priv->y)
+ {
+ priv->y = y;
+ g_object_notify (G_OBJECT (viewport), "y-origin");
+
+ if (priv->vadjustment)
+ mx_adjustment_set_value (priv->vadjustment,
+ (float)(y));
+ }
+
+ if (z != priv->z)
+ {
+ priv->z = z;
+ g_object_notify (G_OBJECT (viewport), "z-origin");
+ }
+
+ g_object_thaw_notify (G_OBJECT (viewport));
+
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (viewport));
+}
+
+void
+mx_viewport_get_origin (MxViewport *viewport,
+ gfloat *x,
+ gfloat *y,
+ gfloat *z)
+{
+ MxViewportPrivate *priv;
+
+ g_return_if_fail (MX_IS_VIEWPORT (viewport));
+
+ priv = viewport->priv;
+
+ if (x)
+ *x = priv->x;
+
+ if (y)
+ *y = priv->y;
+
+ if (z)
+ *z = priv->z;
+}
+
+void
+mx_viewport_set_sync_adjustments (MxViewport *viewport,
+ gboolean sync_adjustments)
+{
+ MxViewportPrivate *priv;
+
+ g_return_if_fail (MX_IS_VIEWPORT (viewport));
+
+ priv = viewport->priv;
+ if (priv->sync_adjustments != sync_adjustments)
+ {
+ priv->sync_adjustments = sync_adjustments;
+ g_object_notify (G_OBJECT (viewport), "sync-adjustments");
+ }
+}
+
+gboolean
+mx_viewport_get_sync_adjustments (MxViewport *viewport)
+{
+ g_return_val_if_fail (MX_IS_VIEWPORT (viewport), FALSE);
+ return viewport->priv->sync_adjustments;
+}
diff --git a/mx/mx-viewport.h b/mx/mx-viewport.h
new file mode 100644
index 0000000..7b4042b
--- /dev/null
+++ b/mx/mx-viewport.h
@@ -0,0 +1,91 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * mx-viewport.h: Viewport 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 Mx by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#ifndef __MX_VIEWPORT_H__
+#define __MX_VIEWPORT_H__
+
+#include <clutter/clutter.h>
+#include "mx-bin.h"
+
+G_BEGIN_DECLS
+
+#define MX_TYPE_VIEWPORT (mx_viewport_get_type())
+#define MX_VIEWPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_VIEWPORT, MxViewport))
+#define MX_IS_VIEWPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_VIEWPORT))
+#define MX_VIEWPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_VIEWPORT, MxViewportClass))
+#define MX_IS_VIEWPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_VIEWPORT))
+#define MX_VIEWPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_VIEWPORT, MxViewportClass))
+
+typedef struct _MxViewport MxViewport;
+typedef struct _MxViewportPrivate MxViewportPrivate;
+typedef struct _MxViewportClass MxViewportClass;
+
+/**
+ * MxViewport:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _MxViewport
+{
+ /*< private >*/
+ MxBin parent;
+
+ MxViewportPrivate *priv;
+};
+
+struct _MxViewportClass
+{
+ MxBinClass parent_class;
+
+ /* padding for future expansion */
+ void (*_padding_0) (void);
+ void (*_padding_1) (void);
+ void (*_padding_2) (void);
+ void (*_padding_3) (void);
+ void (*_padding_4) (void);
+};
+
+GType mx_viewport_get_type (void) G_GNUC_CONST;
+
+ClutterActor *mx_viewport_new (void);
+
+void mx_viewport_set_origin (MxViewport *viewport,
+ gfloat x,
+ gfloat y,
+ gfloat z);
+void mx_viewport_get_origin (MxViewport *viewport,
+ gfloat *x,
+ gfloat *y,
+ gfloat *z);
+
+void mx_viewport_set_sync_adjustments (MxViewport *viewport,
+ gboolean sync);
+gboolean mx_viewport_get_sync_adjustments (MxViewport *viewport);
+
+G_END_DECLS
+
+#endif /* __MX_VIEWPORT_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]