[libchamplain] Back to tidy



commit 0568e0215b6482903b1880da432d22f02d718c5b
Author: JiÅ?í Techet <techet gmail com>
Date:   Thu Feb 24 20:57:55 2011 +0100

    Back to tidy
    
    Using Mx instead of tidy added too much extra stuff we don't need
    at all. It will be easier to continue using tidy and backport the
    fixes from Mx.

 .gitignore                                         |   12 +-
 Makefile.am                                        |    2 +-
 champlain/Makefile.am                              |    6 +-
 champlain/champlain-view.c                         |   32 +-
 configure.ac                                       |    2 +-
 mx/Makefile.am                                     |   64 -
 mx/mx-adjustment.c                                 | 1233 --------------------
 mx/mx-adjustment.h                                 |  149 ---
 mx/mx-bin.c                                        |  715 ------------
 mx/mx-bin.h                                        |   99 --
 mx/mx-kinetic-scroll-view.c                        | 1232 -------------------
 mx/mx-kinetic-scroll-view.h                        |   97 --
 mx/mx-marshal.list                                 |    8 -
 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 +
 mx/mx-enum-types.c.in => tidy/tidy-enum-types.c.in |    2 +-
 mx/mx-enum-types.h.in => tidy/tidy-enum-types.h.in |    8 +-
 tidy/tidy-finger-scroll.c                          |  694 +++++++++++
 tidy/tidy-finger-scroll.h                          |   64 +
 tidy/tidy-marshal.list                             |    6 +
 mx/mx-private.h => tidy/tidy-private.h             |   14 +-
 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 ++
 34 files changed, 2854 insertions(+), 4655 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index cd22249..6ad77d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -84,12 +84,12 @@ libtool
 ltmain.sh
 missing
 stamp-h1
-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
+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
 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 bd3492e..6b9c72f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = build mx champlain
+SUBDIRS = build tidy champlain
 
 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
 
diff --git a/champlain/Makefile.am b/champlain/Makefile.am
index af457c8..8934354 100644
--- a/champlain/Makefile.am
+++ b/champlain/Makefile.am
@@ -125,7 +125,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)/mx/libchamplainmx-1.0.la
+libchamplain_ CHAMPLAIN_API_VERSION@_la_LIBADD = $(DEPS_LIBS) $(SOUP_LIBS) $(MEMPHIS_LIBS) $(top_builddir)/tidy/libtidy-1.0.la
 
 libchamplain_ CHAMPLAIN_API_VERSION@_la_LDFLAGS = \
 	-version-info $(LIBRARY_VERSION)\
@@ -137,7 +137,7 @@ AM_CPPFLAGS = 				\
 	$(SOUP_CFLAGS)			\
 	$(MEMPHIS_CFLAGS)		\
 	-DDATADIR=\""$(datadir)"\"	\
-	-I$(top_srcdir)/mx		\
+	-I$(top_srcdir)/tidy		\
 	-I$(top_srcdir)			\
 	-DCHAMPLAIN_COMPILATION 	\
 	$(WARN_CFLAGS)
@@ -174,7 +174,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)/mx -I$(top_srcdir) -I$(top_builddir) \
+	-I$(top_srcdir)/tidy -I$(top_srcdir) -I$(top_builddir) \
 	-DCHAMPLAIN_COMPILATION
 
 INTROSPECTION_GIRS += Champlain- CHAMPLAIN_API_VERSION@.gir
diff --git a/champlain/champlain-view.c b/champlain/champlain-view.c
index 5937fba..bfeeb5a 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 <mx-kinetic-scroll-view.h>
-#include <mx-viewport.h>
-#include <mx-adjustment.h>
-#include <mx-scrollable.h>
+#include <tidy-finger-scroll.h>
+#include <tidy-viewport.h>
+#include <tidy-adjustment.h>
+#include <tidy-scrollable.h>
 
 //#define VIEW_LOG 
 #ifdef VIEW_LOG
@@ -265,7 +265,7 @@ update_viewport (ChamplainView *view,
   if (relocate || force_relocate)
     {
       g_signal_handlers_block_by_func (priv->viewport, G_CALLBACK (viewport_pos_changed_cb), view);
-      mx_viewport_set_origin (MX_VIEWPORT (priv->viewport),
+      tidy_viewport_set_origin (TIDY_VIEWPORT (priv->viewport),
           priv->viewport_x,
           priv->viewport_y,
           0);
@@ -285,7 +285,7 @@ update_viewport (ChamplainView *view,
 
 
 static void
-panning_completed (G_GNUC_UNUSED MxKineticScrollView *scroll,
+panning_completed (G_GNUC_UNUSED TidyFingerScroll *scroll,
     ChamplainView *view)
 {
   DEBUG_LOG ()
@@ -294,7 +294,7 @@ panning_completed (G_GNUC_UNUSED MxKineticScrollView *scroll,
   gfloat absolute_x, absolute_y;
   gfloat x, y;
 
-  mx_viewport_get_origin (MX_VIEWPORT (priv->viewport), &x, &y, NULL);
+  tidy_viewport_get_origin (TIDY_VIEWPORT (priv->viewport), &x, &y, NULL);
 
   absolute_x = x + priv->anchor_x + priv->viewport_width / 2.0;
   absolute_y = y + priv->anchor_y + priv->viewport_height / 2.0;
@@ -332,11 +332,11 @@ resize_viewport (ChamplainView *view)
   gdouble lower_y = 0;
   gdouble upper_x = G_MAXINT16;
   gdouble upper_y = G_MAXINT16;
-  MxAdjustment *hadjust, *vadjust;
+  TidyAdjustment *hadjust, *vadjust;
 
   ChamplainViewPrivate *priv = view->priv;
 
-  mx_scrollable_get_adjustments (MX_SCROLLABLE (priv->viewport), &hadjust,
+  tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (priv->viewport), &hadjust,
       &vadjust);
 
   if (priv->zoom_level < 8)
@@ -353,7 +353,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 MxAdjustment, but
+   * callback - the signal can be emitted by updating TidyAdjustment, but
    * calling the callback now would be a disaster since we don't have updated
    * anchor yet
    */
@@ -527,7 +527,7 @@ champlain_view_dispose (GObject *object)
 
   if (priv->kinetic_scroll != NULL)
     {
-      mx_kinetic_scroll_view_stop (MX_KINETIC_SCROLL_VIEW (priv->kinetic_scroll));
+      tidy_finger_scroll_stop (TIDY_FINGER_SCROLL (priv->kinetic_scroll));
       g_object_unref (priv->kinetic_scroll);
       priv->kinetic_scroll = NULL;
     }
@@ -1019,8 +1019,8 @@ champlain_view_init (ChamplainView *view)
   clutter_actor_show (priv->viewport_container);
 
   /* Setup viewport */
-  priv->viewport = g_object_ref (mx_viewport_new ());
-  mx_bin_set_child (MX_BIN (priv->viewport), priv->viewport_container);
+  priv->viewport = g_object_ref (tidy_viewport_new ());
+  clutter_container_add_actor (CLUTTER_CONTAINER (priv->viewport), priv->viewport_container);
   
   g_object_set (G_OBJECT (priv->viewport), "sync-adjustments", FALSE, NULL);
 
@@ -1032,7 +1032,7 @@ champlain_view_init (ChamplainView *view)
   clutter_actor_raise (priv->user_layers, priv->map_layer);
 
   /* Setup kinetic scroll */
-  priv->kinetic_scroll = g_object_ref (mx_kinetic_scroll_view_new ());
+  priv->kinetic_scroll = g_object_ref (tidy_finger_scroll_new (FALSE));
 
   g_signal_connect (priv->kinetic_scroll, "scroll-event",
       G_CALLBACK (scroll_event), view);
@@ -1085,7 +1085,7 @@ viewport_pos_changed_cb (G_GNUC_UNUSED GObject *gobject,
   ChamplainViewPrivate *priv = view->priv;
   gfloat x, y;
 
-  mx_viewport_get_origin (MX_VIEWPORT (priv->viewport), &x, &y, NULL);
+  tidy_viewport_get_origin (TIDY_VIEWPORT (priv->viewport), &x, &y, NULL);
 
   if (fabs (x - priv->viewport_x) > 100 ||
       fabs (y - priv->viewport_y) > 100 ||
@@ -2196,7 +2196,7 @@ champlain_view_set_kinetic_mode (ChamplainView *view,
   ChamplainViewPrivate *priv = view->priv;
 
   priv->kinetic_mode = kinetic;
-  mx_kinetic_scroll_view_set_kinetic_mode (MX_KINETIC_SCROLL_VIEW (priv->kinetic_scroll), kinetic);
+  g_object_set (view->priv->kinetic_scroll, "mode", kinetic, NULL);
 }
 
 
diff --git a/configure.ac b/configure.ac
index 2bf4f26..d8a9c84 100644
--- a/configure.ac
+++ b/configure.ac
@@ -214,7 +214,7 @@ AC_CONFIG_FILES([build/Makefile
                  champlain/Makefile
                  champlain/champlain-version.h
                  demos/Makefile
-                 mx/Makefile
+                 tidy/Makefile
                  docs/Makefile
                  docs/reference/Makefile
                  docs/reference/version.xml
diff --git a/tidy/Makefile.am b/tidy/Makefile.am
new file mode 100644
index 0000000..b2f0592
--- /dev/null
+++ b/tidy/Makefile.am
@@ -0,0 +1,63 @@
+BUILT_SOURCES =
+CLEANFILES =
+DISTCLEANFILES =
+EXTRA_DIST =
+
+
+tidy_headers_public = \
+	$(srcdir)/tidy-adjustment.h		\
+	$(srcdir)/tidy-finger-scroll.h		\
+	$(srcdir)/tidy-scrollable.h		\
+	$(srcdir)/tidy-scroll-view.h		\
+	$(srcdir)/tidy-viewport.h		\
+	$(srcdir)/tidy-private.h		\
+	$(srcdir)/tidy-debug.h
+
+tidy_sources = \
+	$(srcdir)/tidy-adjustment.c \
+	$(srcdir)/tidy-finger-scroll.c \
+	$(srcdir)/tidy-scroll-view.c \
+	$(srcdir)/tidy-scrollable.c \
+	$(srcdir)/tidy-viewport.c
+
+
+# glib-genmarshal rules
+glib_marshal_list = tidy-marshal.list
+glib_marshal_prefix = _tidy_marshal
+include $(top_srcdir)/build/Makefile.am.marshal
+
+# glib-mkenums rules
+glib_enum_h = tidy-enum-types.h
+glib_enum_c = tidy-enum-types.c
+glib_enum_headers = $(tidy_headers_public)
+include $(top_srcdir)/build/Makefile.am.enums
+
+tidy_headers_built = 	\
+	tidy-enum-types.h	\
+	tidy-marshal.h
+
+tidy_sources_built = 	\
+	tidy-enum-types.c	\
+	tidy-marshal.c
+
+
+libtidy_1_0_la_SOURCES = \
+	$(tidy_headers_public)	\
+	$(tidy_sources)
+
+nodist_libtidy_1_0_la_SOURCES = \
+	$(tidy_headers_built)	\
+	$(tidy_sources_built)
+
+libtidy_1_0_la_LIBADD = $(DEPS_LIBS)
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir) \
+	-DPREFIX=\""$(prefix)"\" 	\
+	-DLIBDIR=\""$(libdir)"\" 	\
+	-DG_DISABLE_DEPRECATED 		\
+	-DG_LOG_DOMAIN=\"Tidy\" 	\
+	$(DEPS_CFLAGS) 			\
+	$(TIDY_DEBUG_CFLAGS)
+
+noinst_LTLIBRARIES = libtidy-1.0.la
diff --git a/tidy/tidy-adjustment.c b/tidy/tidy-adjustment.c
new file mode 100644
index 0000000..cd0417e
--- /dev/null
+++ b/tidy/tidy-adjustment.c
@@ -0,0 +1,643 @@
+/* tidy-adjustment.c: Adjustment object
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>, inspired by GtkAdjustment
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+#include "tidy-adjustment.h"
+#include "tidy-marshal.h"
+#include "tidy-private.h"
+
+G_DEFINE_TYPE (TidyAdjustment, tidy_adjustment, G_TYPE_OBJECT)
+
+#define ADJUSTMENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TIDY_TYPE_ADJUSTMENT, TidyAdjustmentPrivate))
+
+struct _TidyAdjustmentPrivate
+{
+  gdouble lower;
+  gdouble upper;
+  gdouble value;
+  gdouble step_increment;
+  gdouble page_increment;
+  gdouble page_size;
+
+  /* For interpolation */
+  ClutterTimeline *interpolation;
+  gdouble     dx;
+  gdouble     old_position;
+  gdouble     new_position;
+
+  /* For elasticity */
+  gboolean      elastic;
+  ClutterAlpha *bounce_alpha;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_LOWER,
+  PROP_UPPER,
+  PROP_VALUE,
+  PROP_STEP_INC,
+  PROP_PAGE_INC,
+  PROP_PAGE_SIZE,
+
+  PROP_ELASTIC,
+};
+
+enum
+{
+  CHANGED,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static void tidy_adjustment_set_lower          (TidyAdjustment *adjustment,
+                                                gdouble         lower);
+static void tidy_adjustment_set_upper          (TidyAdjustment *adjustment,
+                                                gdouble         upper);
+static void tidy_adjustment_set_step_increment (TidyAdjustment *adjustment,
+                                                gdouble         step);
+static void tidy_adjustment_set_page_increment (TidyAdjustment *adjustment,
+                                                gdouble         page);
+static void tidy_adjustment_set_page_size      (TidyAdjustment *adjustment,
+                                                gdouble         size);
+
+static void
+tidy_adjustment_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  TidyAdjustmentPrivate *priv = TIDY_ADJUSTMENT (object)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_LOWER:
+      g_value_set_double (value, priv->lower);
+      break;
+
+    case PROP_UPPER:
+      g_value_set_double (value, priv->upper);
+      break;
+
+    case PROP_VALUE:
+      g_value_set_double (value, priv->value);
+      break;
+
+    case PROP_STEP_INC:
+      g_value_set_double (value, priv->step_increment);
+      break;
+
+    case PROP_PAGE_INC:
+      g_value_set_double (value, priv->page_increment);
+      break;
+
+    case PROP_PAGE_SIZE:
+      g_value_set_double (value, priv->page_size);
+      break;
+
+    case PROP_ELASTIC:
+      g_value_set_boolean (value, priv->elastic);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tidy_adjustment_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  TidyAdjustment *adj = TIDY_ADJUSTMENT (object);
+
+  switch (prop_id)
+    {
+    case PROP_LOWER:
+      tidy_adjustment_set_lower (adj, g_value_get_double (value));
+      break;
+
+    case PROP_UPPER:
+      tidy_adjustment_set_upper (adj, g_value_get_double (value));
+      break;
+
+    case PROP_VALUE:
+      tidy_adjustment_set_value (adj, g_value_get_double (value));
+      break;
+
+    case PROP_STEP_INC:
+      tidy_adjustment_set_step_increment (adj, g_value_get_double (value));
+      break;
+
+    case PROP_PAGE_INC:
+      tidy_adjustment_set_page_increment (adj, g_value_get_double (value));
+      break;
+
+    case PROP_PAGE_SIZE:
+      tidy_adjustment_set_page_size (adj, g_value_get_double (value));
+      break;
+
+    case PROP_ELASTIC:
+      tidy_adjustment_set_elastic (adj, g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+stop_interpolation (TidyAdjustment *adjustment)
+{
+  TidyAdjustmentPrivate *priv = adjustment->priv;
+  if (priv->interpolation)
+    {
+      clutter_timeline_stop (priv->interpolation);
+      g_object_unref (priv->interpolation);
+      priv->interpolation = NULL;
+
+      if (priv->bounce_alpha)
+        {
+          g_object_unref (priv->bounce_alpha);
+          priv->bounce_alpha = NULL;
+        }
+    }
+}
+
+void
+tidy_adjustment_interpolate_stop (TidyAdjustment *adjustment)
+{
+  stop_interpolation (adjustment);
+}
+
+static void
+tidy_adjustment_dispose (GObject *object)
+{
+  stop_interpolation (TIDY_ADJUSTMENT (object));
+  
+  G_OBJECT_CLASS (tidy_adjustment_parent_class)->dispose (object);
+}
+
+static void
+tidy_adjustment_class_init (TidyAdjustmentClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (TidyAdjustmentPrivate));
+
+  object_class->get_property = tidy_adjustment_get_property;
+  object_class->set_property = tidy_adjustment_set_property;
+  object_class->dispose = tidy_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,
+                                                        TIDY_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+                                   PROP_UPPER,
+                                   g_param_spec_double ("upper",
+                                                        "Upper",
+                                                        "Upper bound",
+                                                        -G_MAXDOUBLE,
+                                                        G_MAXDOUBLE,
+                                                        0.0,
+                                                        TIDY_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+                                   PROP_VALUE,
+                                   g_param_spec_double ("value",
+                                                        "Value",
+                                                        "Current value",
+                                                        -G_MAXDOUBLE,
+                                                        G_MAXDOUBLE,
+                                                        0.0,
+                                                        TIDY_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+                                   PROP_STEP_INC,
+                                   g_param_spec_double ("step-increment",
+                                                        "Step Increment",
+                                                        "Step increment",
+                                                        -G_MAXDOUBLE,
+                                                        G_MAXDOUBLE,
+                                                        0.0,
+                                                        TIDY_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+                                   PROP_PAGE_INC,
+                                   g_param_spec_double ("page-increment",
+                                                        "Page Increment",
+                                                        "Page increment",
+                                                        -G_MAXDOUBLE,
+                                                        G_MAXDOUBLE,
+                                                        0.0,
+                                                        TIDY_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+                                   PROP_PAGE_SIZE,
+                                   g_param_spec_double ("page-size",
+                                                        "Page Size",
+                                                        "Page size",
+                                                        -G_MAXDOUBLE,
+                                                        G_MAXDOUBLE,
+                                                        0.0,
+                                                        TIDY_PARAM_READWRITE));
+  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,
+                                                         TIDY_PARAM_READWRITE));
+
+  signals[CHANGED] =
+    g_signal_new ("changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (TidyAdjustmentClass, changed),
+                  NULL, NULL,
+                  _tidy_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+tidy_adjustment_init (TidyAdjustment *self)
+{
+  self->priv = ADJUSTMENT_PRIVATE (self);
+}
+
+TidyAdjustment *
+tidy_adjustment_new (gdouble value,
+                     gdouble lower,
+                     gdouble upper,
+                     gdouble step_increment,
+                     gdouble page_increment,
+                     gdouble page_size)
+{
+  return g_object_new (TIDY_TYPE_ADJUSTMENT,
+                       "value", value,
+                       "lower", lower,
+                       "upper", upper,
+                       "step-increment", step_increment,
+                       "page-increment", page_increment,
+                       "page-size", page_size,
+                       NULL);
+}
+
+gdouble
+tidy_adjustment_get_value (TidyAdjustment *adjustment)
+{
+  g_return_val_if_fail (TIDY_IS_ADJUSTMENT (adjustment), 0.0);
+
+  return adjustment->priv->value;
+}
+
+void
+tidy_adjustment_set_value (TidyAdjustment *adjustment,
+                           double value)
+{
+  TidyAdjustmentPrivate *priv;
+
+  g_return_if_fail (TIDY_IS_ADJUSTMENT (adjustment));
+
+  priv = adjustment->priv;
+
+  stop_interpolation (adjustment);
+
+  if (!priv->elastic)
+    value = CLAMP (value, priv->lower, MAX (priv->lower,
+                                            priv->upper - priv->page_size));
+
+  if (priv->value != value)
+    {
+      priv->value = value;
+      g_object_notify (G_OBJECT (adjustment), "value");
+    }
+}
+
+void
+tidy_adjustment_clamp_page (TidyAdjustment *adjustment,
+                            double lower,
+                            double upper)
+{
+  gboolean changed;
+  TidyAdjustmentPrivate *priv;
+
+  g_return_if_fail (TIDY_IS_ADJUSTMENT (adjustment));
+
+  priv = adjustment->priv;
+
+  stop_interpolation (adjustment);
+
+  lower = CLAMP (lower, priv->lower, priv->upper - priv->page_size);
+  upper = CLAMP (upper, priv->lower + priv->page_size, priv->upper);
+
+  changed = FALSE;
+
+  if (priv->value + priv->page_size > upper)
+    {
+      priv->value = upper - priv->page_size;
+      changed = TRUE;
+    }
+
+  if (priv->value < lower)
+    {
+      priv->value = lower;
+      changed = TRUE;
+    }
+
+  if (changed)
+    g_object_notify (G_OBJECT (adjustment), "value");
+}
+
+static void
+tidy_adjustment_set_lower (TidyAdjustment *adjustment,
+                           gdouble         lower)
+{
+  TidyAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->lower != lower)
+    {
+      priv->lower = lower;
+
+      g_signal_emit (adjustment, signals[CHANGED], 0);
+
+      g_object_notify (G_OBJECT (adjustment), "lower");
+
+      tidy_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+    }
+}
+
+static void
+tidy_adjustment_set_upper (TidyAdjustment *adjustment,
+                           gdouble         upper)
+{
+  TidyAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->upper != upper)
+    {
+      priv->upper = upper;
+
+      g_signal_emit (adjustment, signals[CHANGED], 0);
+
+      g_object_notify (G_OBJECT (adjustment), "upper");
+
+      tidy_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+    }
+}
+
+static void
+tidy_adjustment_set_step_increment (TidyAdjustment *adjustment,
+                                    gdouble         step)
+{
+  TidyAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->step_increment != step)
+    {
+      priv->step_increment = step;
+
+      g_signal_emit (adjustment, signals[CHANGED], 0);
+
+      g_object_notify (G_OBJECT (adjustment), "step-increment");
+    }
+}
+
+static void
+tidy_adjustment_set_page_increment (TidyAdjustment *adjustment,
+                                    gdouble        page)
+{
+  TidyAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->page_increment != page)
+    {
+      priv->page_increment = page;
+
+      g_signal_emit (adjustment, signals[CHANGED], 0);
+
+      g_object_notify (G_OBJECT (adjustment), "page-increment");
+    }
+}
+
+static void
+tidy_adjustment_set_page_size (TidyAdjustment *adjustment,
+                               gdouble         size)
+{
+  TidyAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->page_size != size)
+    {
+      priv->page_size = size;
+
+      g_signal_emit (adjustment, signals[CHANGED], 0);
+
+      g_object_notify (G_OBJECT (adjustment), "page_size");
+
+      tidy_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+    }
+}
+
+void
+tidy_adjustment_get_values (TidyAdjustment *adjustment,
+                            gdouble        *value,
+                            gdouble        *lower,
+                            gdouble        *upper,
+                            gdouble        *step_increment,
+                            gdouble        *page_increment,
+                            gdouble        *page_size)
+{
+  TidyAdjustmentPrivate *priv;
+
+  g_return_if_fail (TIDY_IS_ADJUSTMENT (adjustment));
+
+  priv = adjustment->priv;
+
+  if (lower)
+    *lower = priv->lower;
+
+  if (upper)
+    *upper = priv->upper;
+
+  if (value)
+    *value = tidy_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,
+                            gint             frame_num,
+                            TidyAdjustment  *adjustment)
+{
+  TidyAdjustmentPrivate *priv = adjustment->priv;
+
+  priv->interpolation = NULL;
+  if (priv->elastic && priv->bounce_alpha)
+    {
+      gdouble progress = clutter_alpha_get_alpha (priv->bounce_alpha) / 1;
+      gdouble dx = priv->old_position +
+        (priv->new_position - priv->old_position) *
+        progress;
+      tidy_adjustment_set_value (adjustment, dx);
+    }
+  else
+    tidy_adjustment_set_value (adjustment,
+                                priv->old_position +
+                                frame_num * priv->dx);
+  priv->interpolation = timeline;
+}
+
+static void
+interpolation_completed_cb (ClutterTimeline *timeline,
+                            TidyAdjustment  *adjustment)
+{
+  TidyAdjustmentPrivate *priv = adjustment->priv;
+ 
+  stop_interpolation (adjustment);
+  tidy_adjustment_set_value (adjustment,
+                              priv->new_position);
+}
+
+/* Note, there's super-optimal code that does a similar thing in
+ * clutter-alpha.c
+ *
+ * Tried this instead of CLUTTER_ALPHA_SINE_INC, but I think SINE_INC looks
+ * better. Leaving code here in case this is revisited.
+ */
+/*
+static guint32
+bounce_alpha_func (ClutterAlpha *alpha,
+                   gpointer      user_data)
+{
+  gdouble progress, angle;
+  ClutterTimeline *timeline = clutter_alpha_get_timeline (alpha);
+  
+  progress = clutter_timeline_get_progressx (timeline);
+  angle = clutter_qmulx (CFX_PI_2 + CFX_PI_4/2, progress);
+  
+  return clutter_sinx (angle) +
+    (CFX_ONE - clutter_sinx (CFX_PI_2 + CFX_PI_4/2));
+}
+*/
+
+void
+tidy_adjustment_interpolate (TidyAdjustment *adjustment,
+                              gdouble         value,
+                              guint           n_frames,
+                              guint           fps)
+{
+  TidyAdjustmentPrivate *priv = adjustment->priv;
+
+  stop_interpolation (adjustment);
+
+  if (n_frames <= 1)
+    {
+      tidy_adjustment_set_value (adjustment, value);
+      return;
+    }
+
+  priv->old_position = priv->value;
+  priv->new_position = value;
+
+  priv->dx = (priv->new_position - priv->old_position) * n_frames;
+  priv->interpolation = clutter_timeline_new (((float)n_frames / fps) * 1000);
+
+  if (priv->elastic)
+    priv->bounce_alpha = clutter_alpha_new_full (priv->interpolation,
+                                                 CLUTTER_EASE_OUT_SINE);
+
+  g_signal_connect (priv->interpolation,
+                    "new-frame",
+                    G_CALLBACK (interpolation_new_frame_cb),
+                    adjustment);
+  g_signal_connect (priv->interpolation,
+                    "completed",
+                    G_CALLBACK (interpolation_completed_cb),
+                    adjustment);
+
+  clutter_timeline_start (priv->interpolation);
+}
+
+gboolean
+tidy_adjustment_get_elastic (TidyAdjustment *adjustment)
+{
+  return adjustment->priv->elastic;
+}
+
+void
+tidy_adjustment_set_elastic (TidyAdjustment *adjustment,
+                             gboolean        elastic)
+{
+  adjustment->priv->elastic = elastic;
+}
+
+gboolean
+tidy_adjustment_clamp (TidyAdjustment *adjustment,
+                       gboolean        interpolate,
+                       guint           n_frames,
+                       guint           fps)
+{
+  TidyAdjustmentPrivate *priv = adjustment->priv;
+  gdouble dest = priv->value;
+
+  if (priv->value < priv->lower)
+    dest = priv->lower;
+  if (priv->value > priv->upper - priv->page_size)
+    dest = priv->upper - priv->page_size;
+
+  if (dest != priv->value)
+    {
+      if (interpolate)
+        tidy_adjustment_interpolate (adjustment,
+                                      dest,
+                                      n_frames,
+                                      fps);
+      else
+        tidy_adjustment_set_value (adjustment, dest);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
diff --git a/tidy/tidy-adjustment.h b/tidy/tidy-adjustment.h
new file mode 100644
index 0000000..3215354
--- /dev/null
+++ b/tidy/tidy-adjustment.h
@@ -0,0 +1,119 @@
+/* tidy-adjustment.h: Adjustment object
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>, inspired by GtkAdjustment
+ */
+
+#ifndef __TIDY_ADJUSTMENT_H__
+#define __TIDY_ADJUSTMENT_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_ADJUSTMENT            (tidy_adjustment_get_type())
+#define TIDY_ADJUSTMENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_ADJUSTMENT, TidyAdjustment))
+#define TIDY_IS_ADJUSTMENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_ADJUSTMENT))
+#define TIDY_ADJUSTMENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_ADJUSTMENT, TidyAdjustmentClass))
+#define TIDY_IS_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_ADJUSTMENT))
+#define TIDY_ADJUSTMENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_ADJUSTMENT, TidyAdjustmentClass))
+
+typedef struct _TidyAdjustment          TidyAdjustment;
+typedef struct _TidyAdjustmentPrivate   TidyAdjustmentPrivate;
+typedef struct _TidyAdjustmentClass     TidyAdjustmentClass;
+
+/**
+ * TidyAdjustment:
+ *
+ * Class for handling an interval between to values. The contents of
+ * the #TidyAdjustment are private and should be accessed using the
+ * public API.
+ */
+struct _TidyAdjustment
+{
+  /*< private >*/
+  GObject parent_instance;
+
+  TidyAdjustmentPrivate *priv;
+};
+
+/**
+ * TidyAdjustmentClass
+ * @changed: Class handler for the ::changed signal.
+ *
+ * Base class for #TidyAdjustment.
+ */
+struct _TidyAdjustmentClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+
+  /*< public >*/
+  void (* changed) (TidyAdjustment *adjustment);
+};
+
+GType tidy_adjustment_get_type (void) G_GNUC_CONST;
+
+TidyAdjustment *tidy_adjustment_new          (gdouble         value,
+                                              gdouble         lower,
+                                              gdouble         upper,
+                                              gdouble         step_increment,
+                                              gdouble         page_increment,
+                                              gdouble         page_size);
+gdouble         tidy_adjustment_get_value    (TidyAdjustment *adjustment);
+void            tidy_adjustment_set_value    (TidyAdjustment *adjustment,
+                                              gdouble         value);
+void            tidy_adjustment_clamp_page   (TidyAdjustment *adjustment,
+                                              gdouble         lower,
+                                              gdouble         upper);
+void            tidy_adjustment_set_values   (TidyAdjustment *adjustment,
+                                              gdouble         value,
+                                              gdouble         lower,
+                                              gdouble         upper,
+                                              gdouble         step_increment,
+                                              gdouble         page_increment,
+                                              gdouble         page_size);
+void            tidy_adjustment_get_values   (TidyAdjustment *adjustment,
+                                              gdouble        *value,
+                                              gdouble        *lower,
+                                              gdouble        *upper,
+                                              gdouble        *step_increment,
+                                              gdouble        *page_increment,
+                                              gdouble        *page_size);
+
+void            tidy_adjustment_interpolate  (TidyAdjustment *adjustment,
+                                              gdouble         value,
+                                              guint           n_frames,
+                                              guint           fps);
+
+gboolean        tidy_adjustment_get_elastic  (TidyAdjustment *adjustment);
+void            tidy_adjustment_set_elastic  (TidyAdjustment *adjustment,
+                                              gboolean        elastic);
+
+gboolean        tidy_adjustment_clamp        (TidyAdjustment *adjustment,
+                                              gboolean        interpolate,
+                                              guint           n_frames,
+                                              guint           fps);
+void            tidy_adjustment_interpolate_stop (TidyAdjustment *adjustment);
+
+G_END_DECLS
+
+#endif /* __TIDY_ADJUSTMENT_H__ */
+
diff --git a/tidy/tidy-debug.h b/tidy/tidy-debug.h
new file mode 100644
index 0000000..0efb982
--- /dev/null
+++ b/tidy/tidy-debug.h
@@ -0,0 +1,4 @@
+#ifndef __TIDY_DEBUG_H__
+#define __TIDY_DEBUG_H__
+
+#endif /* __TIDY_DEBUG_H__ */
diff --git a/mx/mx-enum-types.c.in b/tidy/tidy-enum-types.c.in
similarity index 95%
rename from mx/mx-enum-types.c.in
rename to tidy/tidy-enum-types.c.in
index 7af6194..5f78912 100644
--- a/mx/mx-enum-types.c.in
+++ b/tidy/tidy-enum-types.c.in
@@ -1,5 +1,5 @@
 /*** BEGIN file-header ***/
-#include "mx-enum-types.h"
+#include "tidy-enum-types.h"
 /*** END file-header ***/
 
 /*** BEGIN file-production ***/
diff --git a/mx/mx-enum-types.h.in b/tidy/tidy-enum-types.h.in
similarity index 70%
rename from mx/mx-enum-types.h.in
rename to tidy/tidy-enum-types.h.in
index 49bce41..517cccb 100644
--- a/mx/mx-enum-types.h.in
+++ b/tidy/tidy-enum-types.h.in
@@ -1,6 +1,6 @@
 /*** BEGIN file-header ***/
-#ifndef __MX_ENUM_TYPES_H__
-#define __MX_ENUM_TYPES_H__
+#ifndef __TIDY_ENUM_TYPES_H__
+#define __TIDY_ENUM_TYPES_H__
 
 #include <glib-object.h>
 
@@ -15,11 +15,11 @@ G_BEGIN_DECLS
 /*** BEGIN file-tail ***/
 G_END_DECLS
 
-#endif /* !__MX_ENUM_TYPES_H__ */
+#endif /* !__TIDY_ENUM_TYPES_H__ */
 /*** END file-tail ***/
 
 /*** BEGIN value-header ***/
 GType @enum_name _get_type (void) G_GNUC_CONST;
-#define MX_TYPE_ ENUMSHORT@ (@enum_name _get_type())
+#define TIDY_TYPE_ ENUMSHORT@ (@enum_name _get_type())
 
 /*** END value-header ***/
diff --git a/tidy/tidy-finger-scroll.c b/tidy/tidy-finger-scroll.c
new file mode 100644
index 0000000..8f2025f
--- /dev/null
+++ b/tidy/tidy-finger-scroll.c
@@ -0,0 +1,694 @@
+/* tidy-finger-scroll.c: Finger scrolling container actor
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ */
+
+#include "tidy-finger-scroll.h"
+#include "tidy-enum-types.h"
+#include "tidy-marshal.h"
+#include "tidy-scrollable.h"
+#include "tidy-scroll-view.h"
+#include <clutter/clutter.h>
+#include <math.h>
+
+G_DEFINE_TYPE (TidyFingerScroll, tidy_finger_scroll, TIDY_TYPE_SCROLL_VIEW)
+
+#define FINGER_SCROLL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                  TIDY_TYPE_FINGER_SCROLL, \
+                                  TidyFingerScrollPrivate))
+
+typedef struct {
+  /* Units to store the origin of a click when scrolling */
+  gfloat x;
+  gfloat y;
+  GTimeVal    time;
+} TidyFingerScrollMotion;
+
+struct _TidyFingerScrollPrivate
+{
+  /* Scroll mode */
+  gboolean kinetic;
+
+  GArray                *motion_buffer;
+  guint                  last_motion;
+
+  /* Variables for storing acceleration information for kinetic mode */
+  ClutterTimeline       *deceleration_timeline;
+  gdouble                dx;
+  gdouble                dy;
+  gdouble                decel_rate;
+
+};
+
+enum {
+  PROP_MODE = 1,
+  PROP_DECEL_RATE,
+  PROP_BUFFER,
+};
+
+enum
+{
+  /* normal signals */
+  PANNING_COMPLETED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static void
+tidy_finger_scroll_get_property (GObject *object, guint property_id,
+                                 GValue *value, GParamSpec *pspec)
+{
+  TidyFingerScrollPrivate *priv = TIDY_FINGER_SCROLL (object)->priv;
+
+  switch (property_id)
+    {
+    case PROP_MODE :
+      g_value_set_boolean (value, priv->kinetic);
+      break;
+    case PROP_DECEL_RATE :
+      g_value_set_double (value, priv->decel_rate);
+      break;
+    case PROP_BUFFER :
+      g_value_set_uint (value, priv->motion_buffer->len);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+tidy_finger_scroll_set_property (GObject *object, guint property_id,
+                                 const GValue *value, GParamSpec *pspec)
+{
+  TidyFingerScrollPrivate *priv = TIDY_FINGER_SCROLL (object)->priv;
+
+  switch (property_id)
+    {
+    case PROP_MODE :
+      priv->kinetic = g_value_get_boolean (value);
+      g_object_notify (object, "mode");
+      break;
+    case PROP_DECEL_RATE :
+      priv->decel_rate = g_value_get_double (value);
+      g_object_notify (object, "decel-rate");
+      break;
+    case PROP_BUFFER :
+      g_array_set_size (priv->motion_buffer, g_value_get_uint (value));
+      g_object_notify (object, "motion-buffer");
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+tidy_finger_scroll_dispose (GObject *object)
+{
+  TidyFingerScrollPrivate *priv = TIDY_FINGER_SCROLL (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 (tidy_finger_scroll_parent_class)->dispose (object);
+}
+
+static void
+tidy_finger_scroll_finalize (GObject *object)
+{
+  TidyFingerScrollPrivate *priv = TIDY_FINGER_SCROLL (object)->priv;
+
+  g_array_free (priv->motion_buffer, TRUE);
+
+  G_OBJECT_CLASS (tidy_finger_scroll_parent_class)->finalize (object);
+}
+
+
+static void
+tidy_finger_scroll_class_init (TidyFingerScrollClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (TidyFingerScrollPrivate));
+
+  object_class->get_property = tidy_finger_scroll_get_property;
+  object_class->set_property = tidy_finger_scroll_set_property;
+  object_class->dispose = tidy_finger_scroll_dispose;
+  object_class->finalize = tidy_finger_scroll_finalize;
+
+  g_object_class_install_property (object_class,
+                                   PROP_MODE,
+                                   g_param_spec_boolean ("mode",
+                                                      "TidyFingerScrollMode",
+                                                      "Scrolling mode",
+                                                      FALSE,
+                                                      G_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class,
+                                   PROP_DECEL_RATE,
+                                   g_param_spec_double ("decel-rate",
+                                                        "Deceleration rate",
+                                                        "Rate at which the view "
+                                                        "will decelerate in "
+                                                        "kinetic mode.",
+                                                        G_MINFLOAT + 1,
+                                                        G_MAXFLOAT,
+                                                        1.1,
+                                                        G_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class,
+                                   PROP_BUFFER,
+                                   g_param_spec_uint ("motion-buffer",
+                                                      "Motion buffer",
+                                                      "Amount of motion "
+                                                      "events to buffer",
+                                                      1, G_MAXUINT, 3,
+                                                      G_PARAM_READWRITE));
+
+  signals[PANNING_COMPLETED] =
+      g_signal_new ("panning-completed", G_OBJECT_CLASS_TYPE (object_class),
+          G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+          g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+static gboolean
+motion_event_cb (ClutterActor *actor,
+                 ClutterMotionEvent *event,
+                 TidyFingerScroll *scroll)
+{
+  gfloat x, y;
+
+  TidyFingerScrollPrivate *priv = scroll->priv;
+
+  if (clutter_actor_transform_stage_point (actor,
+                                           event->x,
+                                           event->y,
+                                           &x, &y))
+    {
+      TidyFingerScrollMotion *motion;
+      ClutterActor *child =
+        tidy_scroll_view_get_child (TIDY_SCROLL_VIEW(scroll));
+
+      if (child)
+        {
+          gdouble dx, dy;
+          TidyAdjustment *hadjust, *vadjust;
+
+          tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
+                                           &hadjust,
+                                           &vadjust);
+
+          motion = &g_array_index (priv->motion_buffer,
+                                   TidyFingerScrollMotion, priv->last_motion);
+          dx = (motion->x - x) +
+               tidy_adjustment_get_value (hadjust);
+          dy = (motion->y - y) +
+               tidy_adjustment_get_value (vadjust);
+
+          tidy_adjustment_set_value (hadjust, dx);
+          tidy_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,
+                               TidyFingerScrollMotion, priv->last_motion);
+      motion->x = x;
+      motion->y = y;
+      g_get_current_time (&motion->time);
+    }
+
+  return TRUE;
+}
+
+static void
+clamp_adjustments (TidyFingerScroll *scroll)
+{
+  ClutterActor *child = tidy_scroll_view_get_child (TIDY_SCROLL_VIEW (scroll));
+
+  if (child)
+    {
+      guint fps, n_frames;
+      TidyAdjustment *hadj, *vadj;
+      gboolean snap;
+
+      tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
+                                       &hadj, &vadj);
+
+      /* FIXME: Hard-coded value here */
+      fps = clutter_get_default_frame_rate ();
+      n_frames = fps / 6;
+
+      snap = TRUE;
+      if (tidy_adjustment_get_elastic (hadj))
+        snap = !tidy_adjustment_clamp (hadj, TRUE, n_frames, fps);
+
+      /* Snap to the nearest step increment on hadjustment */
+      if (snap)
+        {
+          gdouble d, value, lower, step_increment;
+
+          tidy_adjustment_get_values (hadj, &value, &lower, NULL,
+                                      &step_increment, NULL, NULL);
+          d = (rint ((value - lower) / step_increment) *
+              step_increment) + lower;
+          tidy_adjustment_set_value (hadj, d);
+        }
+
+      snap = TRUE;
+      if (tidy_adjustment_get_elastic (vadj))
+        snap = !tidy_adjustment_clamp (vadj, TRUE, n_frames, fps);
+
+      /* Snap to the nearest step increment on vadjustment */
+      if (snap)
+        {
+          gdouble d, value, lower, step_increment;
+
+          tidy_adjustment_get_values (vadj, &value, &lower, NULL,
+                                      &step_increment, NULL, NULL);
+          d = (rint ((value - lower) / step_increment) *
+              step_increment) + lower;
+          tidy_adjustment_set_value (vadj, d);
+        }
+    }
+}
+
+static void
+deceleration_completed_cb (ClutterTimeline *timeline,
+                           TidyFingerScroll *scroll)
+{
+  clamp_adjustments (scroll);
+  g_object_unref (timeline);
+  scroll->priv->deceleration_timeline = NULL;
+
+  g_signal_emit_by_name (scroll, "panning-completed", NULL);
+}
+
+static void
+deceleration_new_frame_cb (ClutterTimeline *timeline,
+                           gint frame_num,
+                           TidyFingerScroll *scroll)
+{
+  TidyFingerScrollPrivate *priv = scroll->priv;
+  ClutterActor *child = tidy_scroll_view_get_child (TIDY_SCROLL_VIEW(scroll));
+
+  if (child)
+    {
+      gdouble    value, lower, upper, page_size;
+      TidyAdjustment *hadjust, *vadjust;
+      gint i;
+      gboolean stop = TRUE;
+
+      tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
+                                       &hadjust,
+                                       &vadjust);
+
+      for (i = 0; i < clutter_timeline_get_delta (timeline) / 15; i++)
+        {
+          tidy_adjustment_set_value (hadjust,
+                                      priv->dx +
+                                        tidy_adjustment_get_value (hadjust));
+          tidy_adjustment_set_value (vadjust,
+                                      priv->dy +
+                                        tidy_adjustment_get_value (vadjust));
+          priv->dx = (priv->dx / priv->decel_rate);
+          priv->dy = (priv->dy / priv->decel_rate);
+        }
+
+      /* Check if we've hit the upper or lower bounds and stop the timeline */
+      tidy_adjustment_get_values (hadjust, &value, &lower, &upper,
+                                   NULL, NULL, &page_size);
+      if (((priv->dx > 0) && (value < upper - page_size)) ||
+          ((priv->dx < 0) && (value > lower)))
+        stop = FALSE;
+
+      if (stop)
+        {
+          tidy_adjustment_get_values (vadjust, &value, &lower, &upper,
+                                       NULL, NULL, &page_size);
+          if (((priv->dy > 0) && (value < upper - page_size)) ||
+              ((priv->dy < 0) && (value > lower)))
+            stop = FALSE;
+        }
+
+      if (stop)
+        {
+          clutter_timeline_stop (timeline);
+          deceleration_completed_cb (timeline, scroll);
+        }
+    }
+}
+
+static gboolean
+button_release_event_cb (ClutterActor *actor,
+                         ClutterButtonEvent *event,
+                         TidyFingerScroll *scroll)
+{
+  TidyFingerScrollPrivate *priv = scroll->priv;
+  ClutterActor *child = tidy_scroll_view_get_child (TIDY_SCROLL_VIEW(scroll));
+  gboolean decelerating = FALSE;
+  gboolean moved = TRUE;
+
+  if (event->button != 1)
+    return FALSE;
+
+  g_signal_handlers_disconnect_by_func (actor,
+                                        motion_event_cb,
+                                        scroll);
+  g_signal_handlers_disconnect_by_func (actor,
+                                        button_release_event_cb,
+                                        scroll);
+
+  clutter_ungrab_pointer ();
+
+  if (priv->kinetic && child)
+    {
+      gfloat x, y;
+
+      if (clutter_actor_transform_stage_point (actor,
+                                               event->x,
+                                               event->y,
+                                               &x, &y))
+        {
+          double frac, x_origin, y_origin;
+          GTimeVal release_time, motion_time;
+          TidyAdjustment *hadjust, *vadjust;
+          glong time_diff;
+          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++)
+            {
+              TidyFingerScrollMotion *motion =
+                &g_array_index (priv->motion_buffer, TidyFingerScrollMotion, 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 /= priv->last_motion;
+          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);
+
+          /* On a macbook that's running Ubuntu 9.04 sometimes 'time_diff' is 0
+             and this causes a division by 0 when computing 'frac'. This check
+             avoids this error.
+           */
+          if (time_diff != 0)
+            {
+              /* 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 - x) / frac;
+              priv->dy = (y_origin - y) / frac;
+
+              /* Get adjustments to do step-increment snapping */
+              tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
+                                               &hadjust,
+                                               &vadjust);
+
+              if (ABS(priv->dx) > 1 ||
+                  ABS(priv->dy) > 1)
+                {
+                  gdouble value, lower, step_increment, d, a, x, y, n;
+
+                  /* TODO: Convert this all to fixed point? */
+
+                  /* 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)
+                   *
+                   * As z = 1, this will cause stops to be slightly abrupt -
+                   * add a constant 15 frames to compensate.
+                   */
+                  x = MAX(ABS(priv->dx), ABS(priv->dy));
+                  y = priv->decel_rate;
+                  n = logf (x) / logf (y) + 15.0;
+
+                  /* 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 */
+                  a = (1.0 - 1.0 / pow (y, n + 1)) / (1.0 - 1.0 / y);
+
+                  /* Solving for dx */
+                  d = a * priv->dx;
+                  tidy_adjustment_get_values (hadjust, &value, &lower, NULL,
+                                              &step_increment, NULL, NULL);
+                  d = ((rint (((value + d) - lower) / step_increment) *
+                        step_increment) + lower) - value;
+                  priv->dx = (d / a);
+
+                  /* Solving for dy */
+                  d = a * (priv->dy);
+                  tidy_adjustment_get_values (vadjust, &value, &lower, NULL,
+                                              &step_increment, NULL, NULL);
+                  d = ((rint (((value + d) - lower) / step_increment) *
+                        step_increment) + lower) - value;
+                  priv->dy = (d / a);
+
+                  priv->deceleration_timeline = clutter_timeline_new ((n / 60) * 1000.0);
+                }
+              else
+                {
+                  gdouble value, lower, step_increment, d, a, y;
+
+                  /* Start a short effects timeline to snap to the nearest step
+                   * boundary (see equations above)
+                   */
+                  y = priv->decel_rate;
+                  a = (1.0 - 1.0 / pow (y, 4 + 1)) / (1.0 - 1.0 / y);
+
+                  tidy_adjustment_get_values (hadjust, &value, &lower, NULL,
+                                              &step_increment, NULL, NULL);
+                  d = ((rint ((value - lower) / step_increment) *
+                        step_increment) + lower) - value;
+                  priv->dx = (d / a);
+
+                  tidy_adjustment_get_values (vadjust, &value, &lower, NULL,
+                                              &step_increment, NULL, NULL);
+                  d = ((rint ((value - lower) / step_increment) *
+                        step_increment) + lower) - value;
+                  priv->dy = (d / a);
+
+                  priv->deceleration_timeline = clutter_timeline_new (250);
+                }
+
+              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);
+              clutter_timeline_start (priv->deceleration_timeline);
+              decelerating = TRUE;
+            }
+        }
+    }
+
+  /* Reset motion event buffer */
+  if (priv->last_motion <= 1)
+      moved = FALSE;
+  priv->last_motion = 0;
+
+  if (!decelerating)
+    {
+      clamp_adjustments (scroll);
+      g_signal_emit_by_name (scroll, "panning-completed", NULL);
+    }
+
+  /* Pass through events to children.
+   * FIXME: this probably breaks click-count.
+   */
+  if (!moved)
+    clutter_event_put ((ClutterEvent *)event);
+
+  return moved;
+}
+
+static gboolean
+after_event_cb (TidyFingerScroll *scroll)
+{
+  /* Check the pointer grab - if something else has grabbed it - for example,
+   * a scroll-bar or some such, don't do our funky stuff.
+   */
+  if (clutter_get_pointer_grab () != CLUTTER_ACTOR (scroll))
+    {
+      g_signal_handlers_disconnect_by_func (scroll,
+                                            motion_event_cb,
+                                            scroll);
+      g_signal_handlers_disconnect_by_func (scroll,
+                                            button_release_event_cb,
+                                            scroll);
+    }
+
+  return FALSE;
+}
+
+static gboolean
+captured_event_cb (ClutterActor     *actor,
+                   ClutterEvent     *event,
+                   TidyFingerScroll *scroll)
+{
+  TidyFingerScrollPrivate *priv = scroll->priv;
+
+  if (event->type == CLUTTER_BUTTON_PRESS)
+    {
+      TidyFingerScrollMotion *motion;
+      ClutterButtonEvent *bevent = (ClutterButtonEvent *)event;
+
+      if (bevent->source != actor)
+        return FALSE;
+
+      /* Reset motion buffer */
+      priv->last_motion = 0;
+      motion = &g_array_index (priv->motion_buffer, TidyFingerScrollMotion, 0);
+
+      if ((bevent->button == 1) &&
+          (clutter_actor_transform_stage_point (actor,
+                                           bevent->x,
+                                           bevent->y,
+                                           &motion->x, &motion->y)))
+        {
+          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;
+            }
+
+          clutter_grab_pointer (actor);
+
+          /* Add a high priority idle to check the grab after the event
+           * emission is finished.
+           */
+          g_idle_add_full (G_PRIORITY_HIGH_IDLE,
+                           (GSourceFunc)after_event_cb,
+                           scroll,
+                           NULL);
+
+          g_signal_connect (actor,
+                            "motion-event",
+                            G_CALLBACK (motion_event_cb),
+                            scroll);
+          g_signal_connect (actor,
+                            "button-release-event",
+                            G_CALLBACK (button_release_event_cb),
+                            scroll);
+        }
+    }
+
+  return FALSE;
+}
+
+static void
+tidy_finger_scroll_init (TidyFingerScroll *self)
+{
+  ClutterActor *scrollbar;
+  TidyFingerScrollPrivate *priv = self->priv = FINGER_SCROLL_PRIVATE (self);
+
+  priv->motion_buffer = g_array_sized_new (FALSE, TRUE,
+                                           sizeof (TidyFingerScrollMotion), 3);
+  g_array_set_size (priv->motion_buffer, 3);
+  priv->decel_rate = 1.1f;
+
+  clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
+  g_signal_connect (CLUTTER_ACTOR (self),
+                    "captured-event",
+                    G_CALLBACK (captured_event_cb),
+                    self);
+
+
+}
+
+ClutterActor *
+tidy_finger_scroll_new (gboolean kinetic)
+{
+  return CLUTTER_ACTOR (g_object_new (TIDY_TYPE_FINGER_SCROLL,
+                                      "mode", kinetic, NULL));
+}
+
+void
+tidy_finger_scroll_stop (TidyFingerScroll *scroll)
+{
+  TidyFingerScrollPrivate *priv;
+
+  g_return_if_fail (TIDY_IS_FINGER_SCROLL (scroll));
+
+  priv = scroll->priv;
+
+  if (priv->deceleration_timeline)
+    {
+      clutter_timeline_stop (priv->deceleration_timeline);
+      g_object_unref (priv->deceleration_timeline);
+      priv->deceleration_timeline = NULL;
+    }
+}
diff --git a/tidy/tidy-finger-scroll.h b/tidy/tidy-finger-scroll.h
new file mode 100644
index 0000000..6dd3cb9
--- /dev/null
+++ b/tidy/tidy-finger-scroll.h
@@ -0,0 +1,64 @@
+/* tidy-finger-scroll.h: Finger scrolling container actor
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ */
+
+#ifndef __TIDY_FINGER_SCROLL_H__
+#define __TIDY_FINGER_SCROLL_H__
+
+#include <glib-object.h>
+#include <tidy/tidy-scroll-view.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_FINGER_SCROLL            (tidy_finger_scroll_get_type())
+#define TIDY_FINGER_SCROLL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_FINGER_SCROLL, TidyFingerScroll))
+#define TIDY_IS_FINGER_SCROLL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_FINGER_SCROLL))
+#define TIDY_FINGER_SCROLL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_FINGER_SCROLL, TidyFingerScrollClass))
+#define TIDY_IS_FINGER_SCROLL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_FINGER_SCROLL))
+#define TIDY_FINGER_SCROLL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_FINGER_SCROLL, TidyFingerScrollClass))
+
+
+typedef struct _TidyFingerScroll          TidyFingerScroll;
+typedef struct _TidyFingerScrollPrivate   TidyFingerScrollPrivate;
+typedef struct _TidyFingerScrollClass     TidyFingerScrollClass;
+
+struct _TidyFingerScroll
+{
+  /*< private >*/
+  TidyScrollView parent_instance;
+  
+  TidyFingerScrollPrivate *priv;
+};
+
+struct _TidyFingerScrollClass
+{
+  TidyScrollViewClass parent_class;
+};
+
+GType tidy_finger_scroll_get_type (void) G_GNUC_CONST;
+
+ClutterActor *tidy_finger_scroll_new  (gboolean kinetic);
+
+void          tidy_finger_scroll_stop (TidyFingerScroll *scroll);
+
+G_END_DECLS
+
+#endif /* __TIDY_FINGER_SCROLL_H__ */
diff --git a/tidy/tidy-marshal.list b/tidy/tidy-marshal.list
new file mode 100644
index 0000000..7a60e80
--- /dev/null
+++ b/tidy/tidy-marshal.list
@@ -0,0 +1,6 @@
+VOID:OBJECT
+VOID:VOID
+VOID:PARAM
+VOID:UINT
+VOID:UINT,UINT
+VOID:OBJECT,OBJECT
diff --git a/mx/mx-private.h b/tidy/tidy-private.h
similarity index 82%
rename from mx/mx-private.h
rename to tidy/tidy-private.h
index b18eaf3..5f17d93 100644
--- a/mx/mx-private.h
+++ b/tidy/tidy-private.h
@@ -1,4 +1,4 @@
-/* mx-private.h: Private declarations
+/* tidy-private.h: Private declarations
  *
  * Copyright (C) 2007 OpenedHand
  *
@@ -18,8 +18,8 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#ifndef __MX_PRIVATE_H__
-#define __MX_PRIVATE_H__
+#ifndef __TIDY_PRIVATE_H__
+#define __TIDY_PRIVATE_H__
 
 #include <glib.h>
 
@@ -27,14 +27,14 @@ G_BEGIN_DECLS
 
 #define I_(str)         (g_intern_static_string ((str)))
 
-#define MX_PARAM_READWRITE    \
-        (G_PARAM_READABLE | G_PARAM_WRITABLE | \
+#define TIDY_PARAM_READABLE     \
+        (G_PARAM_READABLE |     \
          G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
 
-#define MX_PARAM_READWRITE    \
+#define TIDY_PARAM_READWRITE    \
         (G_PARAM_READABLE | G_PARAM_WRITABLE | \
          G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
 
 G_END_DECLS
 
-#endif /* __MX_PRIVATE_H__ */
+#endif /* __TIDY_PRIVATE_H__ */
diff --git a/tidy/tidy-scroll-view.c b/tidy/tidy-scroll-view.c
new file mode 100644
index 0000000..82487b5
--- /dev/null
+++ b/tidy/tidy-scroll-view.c
@@ -0,0 +1,339 @@
+/* tidy-scroll-view.h: Container with scroll-bars
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ */
+
+#include "tidy-scroll-view.h"
+#include "tidy-marshal.h"
+#include "tidy-scrollable.h"
+#include <clutter/clutter.h>
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (TidyScrollView, tidy_scroll_view, CLUTTER_TYPE_ACTOR,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+                                                clutter_container_iface_init))
+
+#define SCROLL_VIEW_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                TIDY_TYPE_SCROLL_VIEW, \
+                                TidyScrollViewPrivate))
+
+struct _TidyScrollViewPrivate
+{
+  ClutterActor   *child;
+};
+
+enum {
+  PROP_0,
+  PROP_CHILD,
+};
+
+static void
+tidy_scroll_view_get_property (GObject *object, guint property_id,
+                                 GValue *value, GParamSpec *pspec)
+{
+  TidyScrollViewPrivate *priv = ((TidyScrollView *)object)->priv;
+
+  switch (property_id)
+    {
+    case PROP_CHILD :
+      g_value_set_object (value, priv->child);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+tidy_scroll_view_set_property (GObject *object, guint property_id,
+                                 const GValue *value, GParamSpec *pspec)
+{
+  switch (property_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+tidy_scroll_view_dispose (GObject *object)
+{
+  TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (object)->priv;
+
+  if (priv->child)
+    clutter_container_remove_actor (CLUTTER_CONTAINER (object), priv->child);
+
+  G_OBJECT_CLASS (tidy_scroll_view_parent_class)->dispose (object);
+}
+
+static void
+tidy_scroll_view_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (tidy_scroll_view_parent_class)->finalize (object);
+}
+
+static void
+tidy_scroll_view_paint (ClutterActor *actor)
+{
+  TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (actor)->priv;
+
+  if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
+    clutter_actor_paint (priv->child);
+}
+
+static void
+tidy_scroll_view_pick (ClutterActor *actor, const ClutterColor *color)
+{
+  /* Chain up so we get a bounding box pained (if we are reactive) */
+  CLUTTER_ACTOR_CLASS (tidy_scroll_view_parent_class)->pick (actor, color);
+
+  /* Trigger pick on children */
+  tidy_scroll_view_paint (actor);
+}
+
+static void
+tidy_scroll_view_get_preferred_width (ClutterActor *actor,
+                                      gfloat   for_height,
+                                      gfloat  *min_width_p,
+                                      gfloat  *natural_width_p)
+{
+
+  TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (actor)->priv;
+
+  if (!priv->child)
+    return;
+
+
+  /* Our natural width is the natural width of the child */
+  clutter_actor_get_preferred_width (priv->child,
+                                     for_height,
+                                     NULL,
+                                     natural_width_p);
+
+}
+
+static void
+tidy_scroll_view_get_preferred_height (ClutterActor *actor,
+                                       gfloat   for_width,
+                                       gfloat  *min_height_p,
+                                       gfloat  *natural_height_p)
+{
+
+  TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (actor)->priv;
+
+  if (!priv->child)
+    return;
+
+
+  /* Our natural height is the natural height of the child */
+  clutter_actor_get_preferred_height (priv->child,
+                                      for_width,
+                                      NULL,
+                                      natural_height_p);
+}
+
+static void
+tidy_scroll_view_allocate (ClutterActor          *actor,
+                           const ClutterActorBox *box,
+                           ClutterAllocationFlags flags)
+{
+  ClutterActorBox child_box;
+
+  TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (actor)->priv;
+
+  /* Chain up */
+  CLUTTER_ACTOR_CLASS (tidy_scroll_view_parent_class)->
+    allocate (actor, box, flags);
+
+  /* Child */
+  child_box.x1 = 0;
+  child_box.x2 = box->x2 - box->x1;
+  child_box.y1 = 0;
+  child_box.y2 = box->y2 - box->y1;
+
+  if (priv->child)
+    {
+      clutter_actor_allocate (priv->child, &child_box, flags);
+      clutter_actor_set_clip (priv->child,
+                              child_box.x1,
+                              child_box.y1,
+                              child_box.x2 - child_box.x1,
+                              child_box.y2 - child_box.y1);
+    }
+
+}
+
+static void
+tidy_scroll_view_class_init (TidyScrollViewClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (TidyScrollViewPrivate));
+
+  object_class->get_property = tidy_scroll_view_get_property;
+  object_class->set_property = tidy_scroll_view_set_property;
+  object_class->dispose= tidy_scroll_view_dispose;
+  object_class->finalize = tidy_scroll_view_finalize;
+
+  actor_class->paint = tidy_scroll_view_paint;
+  actor_class->pick = tidy_scroll_view_pick;
+  actor_class->get_preferred_width = tidy_scroll_view_get_preferred_width;
+  actor_class->get_preferred_height = tidy_scroll_view_get_preferred_height;
+  actor_class->allocate = tidy_scroll_view_allocate;
+
+  g_object_class_install_property (object_class,
+                                   PROP_CHILD,
+                                   g_param_spec_object ("child",
+                                                        "ClutterActor",
+                                                        "Child actor",
+                                                        CLUTTER_TYPE_ACTOR,
+                                                        G_PARAM_READABLE));
+}
+
+static void
+tidy_scroll_view_init (TidyScrollView *self)
+{
+  self->priv = SCROLL_VIEW_PRIVATE (self);
+}
+
+static void
+tidy_scroll_view_add_actor (ClutterContainer *container,
+                            ClutterActor     *actor)
+{
+  TidyScrollView *self = TIDY_SCROLL_VIEW (container);
+  TidyScrollViewPrivate *priv = self->priv;
+
+  if (priv->child)
+    {
+      g_warning ("Attempting to add an actor of type %s to "
+                 "a TidyScrollView that already contains "
+                 "an actor of type %s.",
+                 g_type_name (G_OBJECT_TYPE (actor)),
+                 g_type_name (G_OBJECT_TYPE (priv->child)));
+    }
+  else
+    {
+      if (TIDY_IS_SCROLLABLE(actor))
+        {
+          priv->child = actor;
+          clutter_actor_set_parent (actor, CLUTTER_ACTOR (container));
+
+          /* Notify that child has been set */
+          g_signal_emit_by_name (container, "actor-added", priv->child);
+          g_object_notify (G_OBJECT (container), "child");
+
+          clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
+        }
+      else
+        {
+          g_warning ("Attempting to add an actor to "
+                     "a TidyScrollView, but the actor does "
+                     "not implement TidyScrollable.");
+        }
+    }
+}
+
+static void
+tidy_scroll_view_remove_actor (ClutterContainer *container,
+                               ClutterActor     *actor)
+{
+  TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (container)->priv;
+
+  if (actor == priv->child)
+    {
+      g_object_ref (priv->child);
+
+
+      clutter_actor_unparent (priv->child);
+
+      g_signal_emit_by_name (container, "actor-removed", priv->child);
+
+      g_object_unref (priv->child);
+      priv->child = NULL;
+
+      g_object_notify (G_OBJECT (container), "child");
+
+      if (CLUTTER_ACTOR_IS_VISIBLE (container))
+        clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
+    }
+}
+
+static void
+tidy_scroll_view_foreach (ClutterContainer *container,
+                          ClutterCallback   callback,
+                          gpointer          callback_data)
+{
+  TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (container)->priv;
+
+  if (priv->child)
+    callback (priv->child, callback_data);
+}
+
+static void
+tidy_scroll_view_lower (ClutterContainer *container,
+                        ClutterActor     *actor,
+                        ClutterActor     *sibling)
+{
+  /* single child */
+}
+
+static void
+tidy_scroll_view_raise (ClutterContainer *container,
+                        ClutterActor     *actor,
+                        ClutterActor     *sibling)
+{
+  /* single child */
+}
+
+static void
+tidy_scroll_view_sort_depth_order (ClutterContainer *container)
+{
+  /* single child */
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+  iface->add = tidy_scroll_view_add_actor;
+  iface->remove = tidy_scroll_view_remove_actor;
+  iface->foreach = tidy_scroll_view_foreach;
+  iface->lower = tidy_scroll_view_lower;
+  iface->raise = tidy_scroll_view_raise;
+  iface->sort_depth_order = tidy_scroll_view_sort_depth_order;
+}
+
+ClutterActor *
+tidy_scroll_view_new (void)
+{
+  return CLUTTER_ACTOR (g_object_new (TIDY_TYPE_SCROLL_VIEW, NULL));
+}
+
+ClutterActor *
+tidy_scroll_view_get_child (TidyScrollView *scroll)
+{
+  TidyScrollViewPrivate *priv;
+
+  g_return_val_if_fail (TIDY_IS_SCROLL_VIEW (scroll), NULL);
+
+  priv = scroll->priv;
+
+  return priv->child;
+}
diff --git a/tidy/tidy-scroll-view.h b/tidy/tidy-scroll-view.h
new file mode 100644
index 0000000..aabc4d2
--- /dev/null
+++ b/tidy/tidy-scroll-view.h
@@ -0,0 +1,65 @@
+/* tidy-scroll-view.h: Container with scroll-bars
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ */
+
+#ifndef __TIDY_SCROLL_VIEW_H__
+#define __TIDY_SCROLL_VIEW_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_SCROLL_VIEW            (tidy_scroll_view_get_type())
+#define TIDY_SCROLL_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_SCROLL_VIEW, TidyScrollView))
+#define TIDY_IS_SCROLL_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_SCROLL_VIEW))
+#define TIDY_SCROLL_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_SCROLL_VIEW, TidyScrollViewClass))
+#define TIDY_IS_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_SCROLL_VIEW))
+#define TIDY_SCROLL_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_SCROLL_VIEW, TidyScrollViewClass))
+
+typedef struct _TidyScrollView          TidyScrollView;
+typedef struct _TidyScrollViewPrivate   TidyScrollViewPrivate;
+typedef struct _TidyScrollViewClass     TidyScrollViewClass;
+
+struct _TidyScrollView
+{
+  /*< private >*/
+  ClutterActor parent;
+  
+  TidyScrollViewPrivate *priv;
+};
+
+struct _TidyScrollViewClass
+{
+  ClutterActorClass parent_class;
+};
+
+GType tidy_scroll_view_get_type (void) G_GNUC_CONST;
+
+ClutterActor *tidy_scroll_view_new             (void);
+
+ClutterActor *tidy_scroll_view_get_hscroll_bar (TidyScrollView *scroll);
+ClutterActor *tidy_scroll_view_get_vscroll_bar (TidyScrollView *scroll);
+ClutterActor *tidy_scroll_view_get_child       (TidyScrollView *scroll);
+
+G_END_DECLS
+
+#endif /* __TIDY_SCROLL_VIEW_H__ */
diff --git a/tidy/tidy-scrollable.c b/tidy/tidy-scrollable.c
new file mode 100644
index 0000000..6f6545f
--- /dev/null
+++ b/tidy/tidy-scrollable.c
@@ -0,0 +1,87 @@
+/* tidy-scrollable.c: Scrollable interface
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ */
+
+#include "tidy-scrollable.h"
+
+static void
+tidy_scrollable_base_init (gpointer g_iface)
+{
+  static gboolean initialized = FALSE;
+
+  if (!initialized)
+    {
+      g_object_interface_install_property (g_iface,
+                                   g_param_spec_object ("hadjustment",
+                                                        "TidyAdjustment",
+                                                        "Horizontal adjustment",
+                                                        TIDY_TYPE_ADJUSTMENT,
+                                                        G_PARAM_READWRITE));
+
+      g_object_interface_install_property (g_iface,
+                                   g_param_spec_object ("vadjustment",
+                                                        "TidyAdjustment",
+                                                        "Vertical adjustment",
+                                                        TIDY_TYPE_ADJUSTMENT,
+                                                        G_PARAM_READWRITE));
+
+      initialized = TRUE;
+    }
+}
+
+GType
+tidy_scrollable_get_type (void)
+{
+  static GType type = 0;
+  if (type == 0)
+    {
+      static const GTypeInfo info =
+        {
+          sizeof (TidyScrollableInterface),
+          tidy_scrollable_base_init,        /* base_init */
+          NULL,
+        };
+      type = g_type_register_static (G_TYPE_INTERFACE,
+                                     "TidyScrollable", &info, 0);
+    }
+  return type;
+}
+
+void
+tidy_scrollable_set_adjustments (TidyScrollable *scrollable,
+                                 TidyAdjustment *hadjustment,
+                                 TidyAdjustment *vadjustment)
+{
+  TIDY_SCROLLABLE_GET_INTERFACE (scrollable)->set_adjustments (scrollable,
+                                                               hadjustment,
+                                                               vadjustment);
+}
+
+void
+tidy_scrollable_get_adjustments (TidyScrollable *scrollable,
+                                 TidyAdjustment **hadjustment,
+                                 TidyAdjustment **vadjustment)
+{
+  TIDY_SCROLLABLE_GET_INTERFACE (scrollable)->get_adjustments (scrollable,
+                                                               hadjustment,
+                                                               vadjustment);
+}
+
diff --git a/tidy/tidy-scrollable.h b/tidy/tidy-scrollable.h
new file mode 100644
index 0000000..367e589
--- /dev/null
+++ b/tidy/tidy-scrollable.h
@@ -0,0 +1,63 @@
+/* tidy-scrollable.h: Scrollable interface
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ */
+
+#ifndef __TIDY_SCROLLABLE_H__
+#define __TIDY_SCROLLABLE_H__
+
+#include <glib-object.h>
+#include "tidy-adjustment.h"
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_SCROLLABLE                (tidy_scrollable_get_type ())
+#define TIDY_SCROLLABLE(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_SCROLLABLE, TidyScrollable))
+#define TIDY_IS_SCROLLABLE(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_SCROLLABLE))
+#define TIDY_SCROLLABLE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), TIDY_TYPE_SCROLLABLE, TidyScrollableInterface))
+
+typedef struct _TidyScrollable TidyScrollable; /* Dummy object */
+typedef struct _TidyScrollableInterface TidyScrollableInterface;
+
+struct _TidyScrollableInterface
+{
+  GTypeInterface parent;
+
+  void (* set_adjustments) (TidyScrollable  *scrollable,
+                            TidyAdjustment  *hadjustment,
+                            TidyAdjustment  *vadjustment);
+  void (* get_adjustments) (TidyScrollable  *scrollable,
+                            TidyAdjustment **hadjustment,
+                            TidyAdjustment **vadjustment);
+};
+
+GType tidy_scrollable_get_type (void) G_GNUC_CONST;
+
+void tidy_scrollable_set_adjustments (TidyScrollable  *scrollable,
+                                      TidyAdjustment  *hadjustment,
+                                      TidyAdjustment  *vadjustment);
+void tidy_scrollable_get_adjustments (TidyScrollable  *scrollable,
+                                      TidyAdjustment **hadjustment,
+                                      TidyAdjustment **vadjustment);
+
+G_END_DECLS
+
+#endif /* __TIDY_SCROLLABLE_H__ */
+
diff --git a/tidy/tidy-viewport.c b/tidy/tidy-viewport.c
new file mode 100644
index 0000000..d30d1c5
--- /dev/null
+++ b/tidy/tidy-viewport.c
@@ -0,0 +1,596 @@
+/* tidy-viewport.c: Viewport actor
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <clutter/clutter.h>
+
+#include "tidy-viewport.h"
+#include "tidy-adjustment.h"
+#include "tidy-scrollable.h"
+#include "tidy-private.h"
+
+static void scrollable_interface_init (TidyScrollableInterface *iface);
+
+static void scrollable_set_adjustments (TidyScrollable *scrollable,
+                                        TidyAdjustment *hadjustment,
+                                        TidyAdjustment *vadjustment);
+
+static void scrollable_get_adjustments (TidyScrollable  *scrollable,
+                                        TidyAdjustment **hadjustment,
+                                        TidyAdjustment **vadjustment);
+
+G_DEFINE_TYPE_WITH_CODE (TidyViewport, tidy_viewport, CLUTTER_TYPE_GROUP,
+                         G_IMPLEMENT_INTERFACE (TIDY_TYPE_SCROLLABLE,
+                                                scrollable_interface_init))
+
+#define VIEWPORT_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), TIDY_TYPE_VIEWPORT, \
+  TidyViewportPrivate))
+
+struct _TidyViewportPrivate
+{
+  gfloat x;
+  gfloat y;
+  gfloat z;
+
+  TidyAdjustment *hadjustment;
+  TidyAdjustment *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
+tidy_viewport_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  TidyAdjustment *adjustment;
+
+  TidyViewportPrivate *priv = TIDY_VIEWPORT (object)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_X_ORIGIN:
+      g_value_set_int (value, priv->x);
+      break;
+
+    case PROP_Y_ORIGIN:
+      g_value_set_int (value, priv->y);
+      break;
+
+    case PROP_Z_ORIGIN:
+      g_value_set_int (value, priv->z);
+      break;
+
+    case PROP_HADJUST :
+      scrollable_get_adjustments (TIDY_SCROLLABLE (object), &adjustment, NULL);
+      g_value_set_object (value, adjustment);
+      break;
+
+    case PROP_VADJUST :
+      scrollable_get_adjustments (TIDY_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
+tidy_viewport_set_property (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+  TidyViewport *viewport = TIDY_VIEWPORT (object);
+  TidyViewportPrivate *priv = viewport->priv;
+
+  switch (prop_id)
+    {
+    case PROP_X_ORIGIN:
+      tidy_viewport_set_origin (viewport,
+                                 g_value_get_int (value),
+                                 priv->y,
+                                 priv->z);
+      break;
+
+    case PROP_Y_ORIGIN:
+      tidy_viewport_set_origin (viewport,
+                                 priv->x,
+                                 g_value_get_int (value),
+                                 priv->z);
+      break;
+
+    case PROP_Z_ORIGIN:
+      tidy_viewport_set_origin (viewport,
+                                 priv->x,
+                                 priv->y,
+                                 g_value_get_int (value));
+      break;
+
+    case PROP_HADJUST :
+      scrollable_set_adjustments (TIDY_SCROLLABLE (object),
+                                  g_value_get_object (value),
+                                  priv->vadjustment);
+      break;
+
+    case PROP_VADJUST :
+      scrollable_set_adjustments (TIDY_SCROLLABLE (object),
+                                  priv->hadjustment,
+                                  g_value_get_object (value));
+      break;
+
+    case PROP_SYNC_ADJUST :
+      priv->sync_adjustments = g_value_get_boolean (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+void
+tidy_viewport_stop (TidyViewport *viewport)
+{
+  TidyViewportPrivate *priv = TIDY_VIEWPORT (viewport)->priv;
+
+  tidy_adjustment_interpolate_stop (priv->hadjustment);
+  tidy_adjustment_interpolate_stop (priv->vadjustment);
+}
+
+static void
+tidy_viewport_dispose (GObject *gobject)
+{
+  TidyViewportPrivate *priv = TIDY_VIEWPORT (gobject)->priv;
+
+  if (priv->hadjustment)
+    {
+      tidy_adjustment_interpolate_stop (priv->hadjustment);
+      g_object_unref (priv->hadjustment);
+      priv->hadjustment = NULL;
+    }
+
+  if (priv->vadjustment)
+    {
+      tidy_adjustment_interpolate_stop (priv->vadjustment);
+      g_object_unref (priv->vadjustment);
+      priv->vadjustment = NULL;
+    }
+
+  G_OBJECT_CLASS (tidy_viewport_parent_class)->dispose (gobject);
+}
+
+static void
+tidy_viewport_paint (ClutterActor *self)
+{
+  TidyViewportPrivate *priv = TIDY_VIEWPORT (self)->priv;
+
+  cogl_push_matrix ();
+
+  cogl_translate ((priv->x) * -1.0,
+                  (priv->y) * -1.0,
+                  (priv->z) * -1.0);
+
+  CLUTTER_ACTOR_CLASS (tidy_viewport_parent_class)->paint (self);
+
+  cogl_pop_matrix ();
+}
+
+static void
+tidy_viewport_pick (ClutterActor       *self,
+                    const ClutterColor *color)
+{
+  tidy_viewport_paint (self);
+}
+
+static void
+tidy_viewport_allocate (ClutterActor          *self,
+                        const ClutterActorBox *box,
+                        ClutterAllocationFlags flags)
+{
+  CoglFixed prev_value;
+
+  TidyViewportPrivate *priv = TIDY_VIEWPORT (self)->priv;
+
+  /* Chain up */
+  CLUTTER_ACTOR_CLASS (tidy_viewport_parent_class)->
+    allocate (self, box, flags);
+
+  /* Refresh adjustments */
+  if (priv->sync_adjustments)
+    {
+      if (priv->hadjustment)
+        {
+          g_object_set (G_OBJECT (priv->hadjustment),
+                       "lower", 0.0,
+                       "upper", (box->x2 - box->x1),
+                       NULL);
+
+          /* Make sure value is clamped */
+          prev_value = tidy_adjustment_get_value (priv->hadjustment);
+          tidy_adjustment_set_value (priv->hadjustment, prev_value);
+        }
+
+      if (priv->vadjustment)
+        {
+          g_object_set (G_OBJECT (priv->vadjustment),
+                       "lower", 0.0,
+                       "upper", (box->y2 - box->y1),
+                       NULL);
+
+          prev_value = tidy_adjustment_get_value (priv->vadjustment);
+          tidy_adjustment_set_value (priv->vadjustment, prev_value);
+        }
+    }
+}
+
+static void
+tidy_viewport_class_init (TidyViewportClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (TidyViewportPrivate));
+
+  gobject_class->get_property = tidy_viewport_get_property;
+  gobject_class->set_property = tidy_viewport_set_property;
+  gobject_class->dispose = tidy_viewport_dispose;
+
+  actor_class->paint = tidy_viewport_paint;
+  actor_class->pick = tidy_viewport_pick;
+  actor_class->allocate = tidy_viewport_allocate;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_X_ORIGIN,
+                                   g_param_spec_int ("x-origin",
+                                                     "X Origin",
+                                                     "Origin's X coordinate in pixels",
+                                                     -G_MAXINT, G_MAXINT,
+                                                     0,
+                                                     G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_Y_ORIGIN,
+                                   g_param_spec_int ("y-origin",
+                                                     "Y Origin",
+                                                     "Origin's Y coordinate in pixels",
+                                                     -G_MAXINT, G_MAXINT,
+                                                     0,
+                                                     G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_Z_ORIGIN,
+                                   g_param_spec_int ("z-origin",
+                                                     "Z Origin",
+                                                     "Origin's Z coordinate in pixels",
+                                                     -G_MAXINT, G_MAXINT,
+                                                     0,
+                                                     G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_SYNC_ADJUST,
+                                   g_param_spec_boolean ("sync-adjustments",
+                                                         "Synchronise "
+                                                         "adjustments",
+                                                         "Whether to "
+                                                         "synchronise "
+                                                         "adjustments with "
+                                                         "viewport size",
+                                                         TRUE,
+                                                         G_PARAM_READWRITE));
+
+  g_object_class_override_property (gobject_class,
+                                    PROP_HADJUST,
+                                    "hadjustment");
+
+  g_object_class_override_property (gobject_class,
+                                    PROP_VADJUST,
+                                    "vadjustment");
+}
+
+static void
+hadjustment_value_notify_cb (TidyAdjustment *adjustment,
+                             GParamSpec     *pspec,
+                             TidyViewport   *viewport)
+{
+  TidyViewportPrivate *priv = viewport->priv;
+  gdouble value;
+
+  value = tidy_adjustment_get_value (adjustment);
+
+  tidy_viewport_set_origin (viewport,
+                             value,
+                             priv->y,
+                             priv->z);
+}
+
+static void
+vadjustment_value_notify_cb (TidyAdjustment *adjustment, GParamSpec *arg1,
+                             TidyViewport *viewport)
+{
+  TidyViewportPrivate *priv = viewport->priv;
+  gdouble value;
+
+  value = tidy_adjustment_get_value (adjustment);
+
+  tidy_viewport_set_origin (viewport,
+                             priv->x,
+                             value,
+                             priv->z);
+}
+
+static void
+scrollable_set_adjustments (TidyScrollable *scrollable,
+                            TidyAdjustment *hadjustment,
+                            TidyAdjustment *vadjustment)
+{
+  TidyViewportPrivate *priv = TIDY_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 (TidyScrollable *scrollable,
+                            TidyAdjustment **hadjustment,
+                            TidyAdjustment **vadjustment)
+{
+  TidyViewportPrivate *priv;
+
+  g_return_if_fail (TIDY_IS_VIEWPORT (scrollable));
+
+  priv = ((TidyViewport *)scrollable)->priv;
+
+  if (hadjustment)
+    {
+      if (priv->hadjustment)
+        *hadjustment = priv->hadjustment;
+      else
+        {
+          TidyAdjustment *adjustment;
+          guint width, stage_width, increment;
+
+          width = clutter_actor_get_width (CLUTTER_ACTOR(scrollable));
+          stage_width = clutter_actor_get_width (clutter_stage_get_default ());
+          increment = MAX (1, MIN(stage_width, width));
+
+          adjustment = tidy_adjustment_new (priv->x,
+                                            0,
+                                            width,
+                                            1,
+                                            increment,
+                                            increment);
+          scrollable_set_adjustments (scrollable,
+                                      adjustment,
+                                      priv->vadjustment);
+          *hadjustment = adjustment;
+        }
+    }
+
+  if (vadjustment)
+    {
+      if (priv->vadjustment)
+        *vadjustment = priv->vadjustment;
+      else
+        {
+          TidyAdjustment *adjustment;
+          guint height, stage_height, increment;
+
+          height = clutter_actor_get_height (CLUTTER_ACTOR(scrollable));
+          stage_height = clutter_actor_get_height (clutter_stage_get_default ());
+          increment = MAX (1, MIN(stage_height, height));
+
+          adjustment = tidy_adjustment_new (priv->y,
+                                            0,
+                                            height,
+                                            1,
+                                            increment,
+                                            increment);
+          scrollable_set_adjustments (scrollable,
+                                      priv->hadjustment,
+                                      adjustment);
+          *vadjustment = adjustment;
+        }
+    }
+}
+
+static void
+scrollable_interface_init (TidyScrollableInterface *iface)
+{
+  iface->set_adjustments = scrollable_set_adjustments;
+  iface->get_adjustments = scrollable_get_adjustments;
+}
+
+static void
+clip_notify_cb (ClutterActor *actor,
+                GParamSpec   *pspec,
+                TidyViewport *self)
+{
+  gfloat width, height;
+  TidyViewportPrivate *priv = self->priv;
+
+  if (!priv->sync_adjustments)
+    return;
+
+  if (!clutter_actor_has_clip (actor))
+    {
+      if (priv->hadjustment)
+        g_object_set (priv->hadjustment, "page-size", (gdouble)1.0, NULL);
+      if (priv->vadjustment)
+        g_object_set (priv->vadjustment, "page-size", (gdouble)1.0, NULL);
+      return;
+    }
+
+  clutter_actor_get_clip (actor, NULL, NULL, &width, &height);
+
+  if (priv->hadjustment)
+    g_object_set (priv->hadjustment, "page-size", (gdouble)width, NULL);
+
+  if (priv->vadjustment)
+    g_object_set (priv->vadjustment, "page-size", (gdouble)height, NULL);
+}
+
+static void
+tidy_viewport_init (TidyViewport *self)
+{
+  self->priv = VIEWPORT_PRIVATE (self);
+
+  self->priv->sync_adjustments = TRUE;
+
+  g_signal_connect (self, "notify::clip",
+                    G_CALLBACK (clip_notify_cb), self);
+}
+
+ClutterActor *
+tidy_viewport_new (void)
+{
+  return g_object_new (TIDY_TYPE_VIEWPORT, NULL);
+}
+
+void
+tidy_viewport_set_origin (TidyViewport *viewport,
+                          float x,
+                          float y,
+                          float z)
+{
+  TidyViewportPrivate *priv;
+
+  g_return_if_fail (TIDY_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)
+        tidy_adjustment_set_value (priv->hadjustment,
+                                    x);
+    }
+
+  if (y != priv->y)
+    {
+      priv->y = y;
+      g_object_notify (G_OBJECT (viewport), "y-origin");
+
+      if (priv->vadjustment)
+        tidy_adjustment_set_value (priv->vadjustment,
+                                    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
+tidy_viewport_get_origin (TidyViewport *viewport,
+                          float *x,
+                          float *y,
+                          float *z)
+{
+  TidyViewportPrivate *priv;
+
+  g_return_if_fail (TIDY_IS_VIEWPORT (viewport));
+
+  priv = viewport->priv;
+
+  if (x)
+    *x = priv->x;
+
+  if (y)
+    *y = priv->y;
+
+  if (z)
+    *z = priv->z;
+}
diff --git a/tidy/tidy-viewport.h b/tidy/tidy-viewport.h
new file mode 100644
index 0000000..1ee9b35
--- /dev/null
+++ b/tidy/tidy-viewport.h
@@ -0,0 +1,72 @@
+/* tidy-viewport.h: Viewport actor
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ */
+
+#ifndef __TIDY_VIEWPORT_H__
+#define __TIDY_VIEWPORT_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_VIEWPORT            (tidy_viewport_get_type())
+#define TIDY_VIEWPORT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_VIEWPORT, TidyViewport))
+#define TIDY_IS_VIEWPORT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_VIEWPORT))
+#define TIDY_VIEWPORT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_VIEWPORT, TidyViewportClass))
+#define TIDY_IS_VIEWPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_VIEWPORT))
+#define TIDY_VIEWPORT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_VIEWPORT, TidyViewportClass))
+
+typedef struct _TidyViewport          TidyViewport;
+typedef struct _TidyViewportPrivate   TidyViewportPrivate;
+typedef struct _TidyViewportClass     TidyViewportClass;
+
+struct _TidyViewport
+{
+  ClutterGroup parent;
+
+  TidyViewportPrivate *priv;
+};
+
+struct _TidyViewportClass
+{
+  ClutterGroupClass parent_class;
+};
+
+GType tidy_viewport_get_type (void) G_GNUC_CONST;
+
+ClutterActor * tidy_viewport_new         (void);
+
+void           tidy_viewport_set_origin  (TidyViewport *viewport,
+                                          float          x,
+                                          float          y,
+                                          float          z);
+
+void           tidy_viewport_get_origin  (TidyViewport *viewport,
+                                          gfloat         *x,
+                                          gfloat         *y,
+                                          gfloat         *z);
+void           tidy_viewport_stop        (TidyViewport *viewport);
+
+G_END_DECLS
+
+#endif /* __TIDY_VIEWPORT_H__ */
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]