[gnome-shell/nbtk-introduction: 2/8] Import NbtkScrollView and dependencies



commit 271e4ca07e00d82c056f9d8b72332d811fb50d3c
Author: Colin Walters <walters verbum org>
Date:   Wed Sep 9 23:13:35 2009 -0400

    Import NbtkScrollView and dependencies
    
    https://bugzilla.gnome.org/show_bug.cgi?id=591245

 src/Makefile-nbtk.am        |   12 +
 src/nbtk/nbtk-adjustment.c  |  768 ++++++++++++++++++++++++++++++
 src/nbtk/nbtk-adjustment.h  |  121 +++++
 src/nbtk/nbtk-button.c      |  759 ++++++++++++++++++++++++++++++
 src/nbtk/nbtk-button.h      |   90 ++++
 src/nbtk/nbtk-scroll-bar.c  | 1094 +++++++++++++++++++++++++++++++++++++++++++
 src/nbtk/nbtk-scroll-bar.h  |   81 ++++
 src/nbtk/nbtk-scroll-view.c |  836 +++++++++++++++++++++++++++++++++
 src/nbtk/nbtk-scroll-view.h |   88 ++++
 src/nbtk/nbtk-scrollable.c  |   88 ++++
 src/nbtk/nbtk-scrollable.h  |   69 +++
 src/nbtk/nbtk-viewport.c    |  668 ++++++++++++++++++++++++++
 src/nbtk/nbtk-viewport.h    |   94 ++++
 13 files changed, 4768 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile-nbtk.am b/src/Makefile-nbtk.am
index adfec21..19c21e0 100644
--- a/src/Makefile-nbtk.am
+++ b/src/Makefile-nbtk.am
@@ -64,8 +64,13 @@ nbtk-enum-types.c: stamp-nbtk-enum-types.h nbtk/nbtk-enum-types.c.in
 
 # please, keep this sorted alphabetically
 nbtk_source_h =                  \
+    nbtk/nbtk-adjustment.h            \
     nbtk/nbtk-bin.h                   \
+    nbtk/nbtk-button.h                \
     nbtk/nbtk-private.h               \
+    nbtk/nbtk-scrollable.h            \
+    nbtk/nbtk-scroll-bar.h            \
+    nbtk/nbtk-scroll-view.h           \
     nbtk/nbtk-stylable.h              \
     nbtk/nbtk-style.h                 \
     nbtk/nbtk-subtexture.h            \
@@ -73,19 +78,26 @@ nbtk_source_h =                  \
     nbtk/nbtk-texture-frame.h         \
     nbtk/nbtk-tooltip.h               \
     nbtk/nbtk-types.h                 \
+    nbtk/nbtk-viewport.h              \
     nbtk/nbtk-widget.h                \
     $(NULL)
 
 # please, keep this sorted alphabetically
 nbtk_source_c =                  \
+    nbtk/nbtk-adjustment.c           \
     nbtk/nbtk-bin.c                   \
+    nbtk/nbtk-button.c                \
     nbtk/nbtk-private.c               \
+    nbtk/nbtk-scrollable.c            \
+    nbtk/nbtk-scroll-bar.c            \
+    nbtk/nbtk-scroll-view.c           \
     nbtk/nbtk-stylable.c              \
     nbtk/nbtk-style.c                 \
     nbtk/nbtk-subtexture.c            \
     nbtk/nbtk-texture-cache.c         \
     nbtk/nbtk-texture-frame.c         \
     nbtk/nbtk-tooltip.c               \
+    nbtk/nbtk-viewport.c              \
     nbtk/nbtk-widget.c                \
     $(NULL)
 
diff --git a/src/nbtk/nbtk-adjustment.c b/src/nbtk/nbtk-adjustment.c
new file mode 100644
index 0000000..4a60472
--- /dev/null
+++ b/src/nbtk/nbtk-adjustment.c
@@ -0,0 +1,768 @@
+/*
+ * nbtk-adjustment.c: Adjustment object
+ *
+ * Copyright (C) 2008 OpenedHand
+ * Copyright (c) 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>, inspired by GtkAdjustment
+ * Port to Nbtk by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+#include "nbtk-adjustment.h"
+#include "nbtk-marshal.h"
+#include "nbtk-private.h"
+
+G_DEFINE_TYPE (NbtkAdjustment, nbtk_adjustment, G_TYPE_OBJECT)
+
+#define ADJUSTMENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NBTK_TYPE_ADJUSTMENT, NbtkAdjustmentPrivate))
+
+struct _NbtkAdjustmentPrivate
+{
+  /* Do not sanity-check values while constructing,
+   * not all properties may be set yet. */
+  gboolean is_constructing : 1;
+
+  gdouble lower;
+  gdouble upper;
+  gdouble value;
+  gdouble step_increment;
+  gdouble page_increment;
+  gdouble page_size;
+
+  /* For interpolation */
+  ClutterTimeline *interpolation;
+  gdouble old_position;
+  gdouble new_position;
+
+  /* For elasticity */
+  gboolean      elastic;
+  guint         bounce_source;
+  ClutterAlpha *bounce_alpha;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_LOWER,
+  PROP_UPPER,
+  PROP_VALUE,
+  PROP_STEP_INC,
+  PROP_PAGE_INC,
+  PROP_PAGE_SIZE,
+
+  PROP_ELASTIC,
+};
+
+enum
+{
+  CHANGED,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static gboolean nbtk_adjustment_set_lower          (NbtkAdjustment *adjustment,
+                                                    gdouble         lower);
+static gboolean nbtk_adjustment_set_upper          (NbtkAdjustment *adjustment,
+                                                    gdouble         upper);
+static gboolean nbtk_adjustment_set_step_increment (NbtkAdjustment *adjustment,
+                                                    gdouble         step);
+static gboolean nbtk_adjustment_set_page_increment (NbtkAdjustment *adjustment,
+                                                    gdouble         page);
+static gboolean nbtk_adjustment_set_page_size      (NbtkAdjustment *adjustment,
+                                                    gdouble         size);
+
+static void
+nbtk_adjustment_constructed (GObject *object)
+{
+  GObjectClass *g_class;
+  NbtkAdjustment *self = NBTK_ADJUSTMENT (object);
+
+  g_class = G_OBJECT_CLASS (nbtk_adjustment_parent_class);
+  /* The docs say we're suppose to chain up, but would crash without
+   * some extra care. */
+  if (g_class && g_class->constructed &&
+      g_class->constructed != nbtk_adjustment_constructed)
+    {
+      g_class->constructed (object);
+    }
+
+  NBTK_ADJUSTMENT (self)->priv->is_constructing = FALSE;
+  nbtk_adjustment_clamp_page (self, self->priv->lower, self->priv->upper);
+}
+
+static void
+nbtk_adjustment_get_property (GObject    *gobject,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  NbtkAdjustmentPrivate *priv = NBTK_ADJUSTMENT (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_LOWER:
+      g_value_set_double (value, priv->lower);
+      break;
+
+    case PROP_UPPER:
+      g_value_set_double (value, priv->upper);
+      break;
+
+    case PROP_VALUE:
+      g_value_set_double (value, priv->value);
+      break;
+
+    case PROP_STEP_INC:
+      g_value_set_double (value, priv->step_increment);
+      break;
+
+    case PROP_PAGE_INC:
+      g_value_set_double (value, priv->page_increment);
+      break;
+
+    case PROP_PAGE_SIZE:
+      g_value_set_double (value, priv->page_size);
+      break;
+
+    case PROP_ELASTIC:
+      g_value_set_boolean (value, priv->elastic);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_adjustment_set_property (GObject      *gobject,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  NbtkAdjustment *adj = NBTK_ADJUSTMENT (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_LOWER:
+      nbtk_adjustment_set_lower (adj, g_value_get_double (value));
+      break;
+
+    case PROP_UPPER:
+      nbtk_adjustment_set_upper (adj, g_value_get_double (value));
+      break;
+
+    case PROP_VALUE:
+      nbtk_adjustment_set_value (adj, g_value_get_double (value));
+      break;
+
+    case PROP_STEP_INC:
+      nbtk_adjustment_set_step_increment (adj, g_value_get_double (value));
+      break;
+
+    case PROP_PAGE_INC:
+      nbtk_adjustment_set_page_increment (adj, g_value_get_double (value));
+      break;
+
+    case PROP_PAGE_SIZE:
+      nbtk_adjustment_set_page_size (adj, g_value_get_double (value));
+      break;
+
+    case PROP_ELASTIC:
+      nbtk_adjustment_set_elastic (adj, g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+stop_interpolation (NbtkAdjustment *adjustment)
+{
+  NbtkAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->interpolation)
+    {
+      clutter_timeline_stop (priv->interpolation);
+      g_object_unref (priv->interpolation);
+      priv->interpolation = NULL;
+
+      if (priv->bounce_alpha)
+        {
+          g_object_unref (priv->bounce_alpha);
+          priv->bounce_alpha = NULL;
+        }
+    }
+
+  if (priv->bounce_source)
+    {
+      g_source_remove (priv->bounce_source);
+      priv->bounce_source = 0;
+    }
+}
+
+static void
+nbtk_adjustment_dispose (GObject *object)
+{
+  stop_interpolation (NBTK_ADJUSTMENT (object));
+
+  G_OBJECT_CLASS (nbtk_adjustment_parent_class)->dispose (object);
+}
+
+static void
+nbtk_adjustment_class_init (NbtkAdjustmentClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (NbtkAdjustmentPrivate));
+
+  object_class->constructed = nbtk_adjustment_constructed;
+  object_class->get_property = nbtk_adjustment_get_property;
+  object_class->set_property = nbtk_adjustment_set_property;
+  object_class->dispose = nbtk_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,
+                                                        NBTK_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class,
+                                   PROP_UPPER,
+                                   g_param_spec_double ("upper",
+                                                        "Upper",
+                                                        "Upper bound",
+                                                        -G_MAXDOUBLE,
+                                                        G_MAXDOUBLE,
+                                                        0.0,
+                                                        NBTK_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class,
+                                   PROP_VALUE,
+                                   g_param_spec_double ("value",
+                                                        "Value",
+                                                        "Current value",
+                                                        -G_MAXDOUBLE,
+                                                        G_MAXDOUBLE,
+                                                        0.0,
+                                                        NBTK_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class,
+                                   PROP_STEP_INC,
+                                   g_param_spec_double ("step-increment",
+                                                        "Step Increment",
+                                                        "Step increment",
+                                                        0.0,
+                                                        G_MAXDOUBLE,
+                                                        0.0,
+                                                        NBTK_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class,
+                                   PROP_PAGE_INC,
+                                   g_param_spec_double ("page-increment",
+                                                        "Page Increment",
+                                                        "Page increment",
+                                                        0.0,
+                                                        G_MAXDOUBLE,
+                                                        0.0,
+                                                        NBTK_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class,
+                                   PROP_PAGE_SIZE,
+                                   g_param_spec_double ("page-size",
+                                                        "Page Size",
+                                                        "Page size",
+                                                        0.0,
+                                                        G_MAXDOUBLE,
+                                                        0.0,
+                                                        NBTK_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class,
+                                   PROP_ELASTIC,
+                                   g_param_spec_boolean ("elastic",
+                                                         "Elastic",
+                                                         "Make interpolation "
+                                                         "behave in an "
+                                                         "'elastic' way and "
+                                                         "stop clamping value.",
+                                                         FALSE,
+                                                        NBTK_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  /**
+   * NbtkAdjustment::changed:
+   *
+   * Emitted when any of the adjustment values have changed
+   */
+  signals[CHANGED] =
+    g_signal_new ("changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (NbtkAdjustmentClass, changed),
+                  NULL, NULL,
+                  _nbtk_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+nbtk_adjustment_init (NbtkAdjustment *self)
+{
+  self->priv = ADJUSTMENT_PRIVATE (self);
+
+  self->priv->is_constructing = TRUE;
+}
+
+NbtkAdjustment *
+nbtk_adjustment_new (gdouble value,
+                     gdouble lower,
+                     gdouble upper,
+                     gdouble step_increment,
+                     gdouble page_increment,
+                     gdouble page_size)
+{
+  return g_object_new (NBTK_TYPE_ADJUSTMENT,
+                       "value", value,
+                       "lower", lower,
+                       "upper", upper,
+                       "step-increment", step_increment,
+                       "page-increment", page_increment,
+                       "page-size", page_size,
+                       NULL);
+}
+
+gdouble
+nbtk_adjustment_get_value (NbtkAdjustment *adjustment)
+{
+  NbtkAdjustmentPrivate *priv;
+
+  g_return_val_if_fail (NBTK_IS_ADJUSTMENT (adjustment), 0);
+
+  priv = adjustment->priv;
+
+  if (priv->interpolation)
+    {
+      return MAX (priv->lower,
+                  MIN (priv->upper - priv->page_size,
+                       priv->new_position));
+    }
+  else
+    return priv->value;
+}
+
+void
+nbtk_adjustment_set_value (NbtkAdjustment *adjustment,
+                           gdouble         value)
+{
+  NbtkAdjustmentPrivate *priv;
+
+  g_return_if_fail (NBTK_IS_ADJUSTMENT (adjustment));
+
+  priv = adjustment->priv;
+
+  stop_interpolation (adjustment);
+
+  /* Defer clamp until after construction. */
+  if (!priv->is_constructing)
+    {
+      if (!priv->elastic)
+        value = CLAMP (value,
+                       priv->lower,
+                       MAX (priv->lower, priv->upper - priv->page_size));
+    }
+
+  if (priv->value != value)
+    {
+      priv->value = value;
+
+      g_object_notify (G_OBJECT (adjustment), "value");
+    }
+}
+
+void
+nbtk_adjustment_clamp_page (NbtkAdjustment *adjustment,
+                            gdouble         lower,
+                            gdouble         upper)
+{
+  NbtkAdjustmentPrivate *priv;
+  gboolean changed;
+
+  g_return_if_fail (NBTK_IS_ADJUSTMENT (adjustment));
+
+  priv = adjustment->priv;
+
+  stop_interpolation (adjustment);
+
+  lower = CLAMP (lower, priv->lower, priv->upper - priv->page_size);
+  upper = CLAMP (upper, priv->lower + priv->page_size, priv->upper);
+
+  changed = FALSE;
+
+  if (priv->value + priv->page_size > upper)
+    {
+      priv->value = upper - priv->page_size;
+      changed = TRUE;
+    }
+
+  if (priv->value < lower)
+    {
+      priv->value = lower;
+      changed = TRUE;
+    }
+
+  if (changed)
+    g_object_notify (G_OBJECT (adjustment), "value");
+}
+
+static gboolean
+nbtk_adjustment_set_lower (NbtkAdjustment *adjustment,
+                           gdouble         lower)
+{
+  NbtkAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->lower != lower)
+    {
+      priv->lower = lower;
+
+      g_signal_emit (adjustment, signals[CHANGED], 0);
+
+      g_object_notify (G_OBJECT (adjustment), "lower");
+
+      /* Defer clamp until after construction. */
+      if (!priv->is_constructing)
+        nbtk_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+nbtk_adjustment_set_upper (NbtkAdjustment *adjustment,
+                           gdouble         upper)
+{
+  NbtkAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->upper != upper)
+    {
+      priv->upper = upper;
+
+      g_signal_emit (adjustment, signals[CHANGED], 0);
+
+      g_object_notify (G_OBJECT (adjustment), "upper");
+
+      /* Defer clamp until after construction. */
+      if (!priv->is_constructing)
+        nbtk_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+nbtk_adjustment_set_step_increment (NbtkAdjustment *adjustment,
+                                    gdouble         step)
+{
+  NbtkAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->step_increment != step)
+    {
+      priv->step_increment = step;
+
+      g_signal_emit (adjustment, signals[CHANGED], 0);
+
+      g_object_notify (G_OBJECT (adjustment), "step-increment");
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+nbtk_adjustment_set_page_increment (NbtkAdjustment *adjustment,
+                                    gdouble         page)
+{
+  NbtkAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->page_increment != page)
+    {
+      priv->page_increment = page;
+
+      g_signal_emit (adjustment, signals[CHANGED], 0);
+
+      g_object_notify (G_OBJECT (adjustment), "page-increment");
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+nbtk_adjustment_set_page_size (NbtkAdjustment *adjustment,
+                               gdouble         size)
+{
+  NbtkAdjustmentPrivate *priv = adjustment->priv;
+
+  if (priv->page_size != size)
+    {
+      priv->page_size = size;
+
+      g_signal_emit (adjustment, signals[CHANGED], 0);
+
+      g_object_notify (G_OBJECT (adjustment), "page_size");
+
+      /* Well explicitely clamp after construction. */
+      if (!priv->is_constructing)
+        nbtk_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+void
+nbtk_adjustment_set_values (NbtkAdjustment *adjustment,
+                            gdouble         value,
+                            gdouble         lower,
+                            gdouble         upper,
+                            gdouble         step_increment,
+                            gdouble         page_increment,
+                            gdouble         page_size)
+{
+  NbtkAdjustmentPrivate *priv;
+  gboolean emit_changed = FALSE;
+
+  g_return_if_fail (NBTK_IS_ADJUSTMENT (adjustment));
+  g_return_if_fail (page_size >= 0 && page_size <= G_MAXDOUBLE);
+  g_return_if_fail (step_increment >= 0 && step_increment <= G_MAXDOUBLE);
+  g_return_if_fail (page_increment >= 0 && page_increment <= G_MAXDOUBLE);
+
+  priv = adjustment->priv;
+
+  stop_interpolation (adjustment);
+
+  emit_changed = FALSE;
+
+  g_object_freeze_notify (G_OBJECT (adjustment));
+
+  emit_changed |= nbtk_adjustment_set_lower (adjustment, lower);
+  emit_changed |= nbtk_adjustment_set_upper (adjustment, upper);
+  emit_changed |= nbtk_adjustment_set_step_increment (adjustment, step_increment);
+  emit_changed |= nbtk_adjustment_set_page_increment (adjustment, page_increment);
+  emit_changed |= nbtk_adjustment_set_page_size (adjustment, page_size);
+
+  if (value != priv->value)
+    {
+      nbtk_adjustment_set_value (adjustment, value);
+      emit_changed = TRUE;
+    }
+
+  if (emit_changed)
+    g_signal_emit (G_OBJECT (adjustment), signals[CHANGED], 0);
+
+  g_object_thaw_notify (G_OBJECT (adjustment));
+}
+
+void
+nbtk_adjustment_get_values (NbtkAdjustment *adjustment,
+                            gdouble        *value,
+                            gdouble        *lower,
+                            gdouble        *upper,
+                            gdouble        *step_increment,
+                            gdouble        *page_increment,
+                            gdouble        *page_size)
+{
+  NbtkAdjustmentPrivate *priv;
+
+  g_return_if_fail (NBTK_IS_ADJUSTMENT (adjustment));
+
+  priv = adjustment->priv;
+
+  if (lower)
+    *lower = priv->lower;
+
+  if (upper)
+    *upper = priv->upper;
+
+  if (value)
+    *value = nbtk_adjustment_get_value (adjustment);
+
+  if (step_increment)
+    *step_increment = priv->step_increment;
+
+  if (page_increment)
+    *page_increment = priv->page_increment;
+
+  if (page_size)
+    *page_size = priv->page_size;
+}
+
+static void
+interpolation_new_frame_cb (ClutterTimeline *timeline,
+                            guint            msecs,
+                            NbtkAdjustment  *adjustment)
+{
+  NbtkAdjustmentPrivate *priv = adjustment->priv;
+
+  priv->interpolation = NULL;
+
+  if (priv->elastic)
+    {
+      gdouble progress = clutter_alpha_get_alpha (priv->bounce_alpha) / 1.0;
+      gdouble dx = priv->old_position
+                 + (priv->new_position - priv->old_position)
+                 * progress;
+
+      nbtk_adjustment_set_value (adjustment, dx);
+    }
+  else
+    nbtk_adjustment_set_value (adjustment,
+                               priv->old_position +
+                               (priv->new_position - priv->old_position) *
+                               clutter_timeline_get_progress (timeline));
+
+  priv->interpolation = timeline;
+}
+
+static void
+interpolation_completed_cb (ClutterTimeline *timeline,
+                            NbtkAdjustment  *adjustment)
+{
+  NbtkAdjustmentPrivate *priv = adjustment->priv;
+
+  stop_interpolation (adjustment);
+  nbtk_adjustment_set_value (adjustment, priv->new_position);
+}
+
+/* Note, there's super-optimal code that does a similar thing in
+ * clutter-alpha.c
+ *
+ * Tried this instead of CLUTTER_ALPHA_SINE_INC, but I think SINE_INC looks
+ * better. Leaving code here in case this is revisited.
+ */
+/*
+static guint32
+bounce_alpha_func (ClutterAlpha *alpha,
+                   gpointer      user_data)
+{
+  ClutterFixed progress, angle;
+  ClutterTimeline *timeline = clutter_alpha_get_timeline (alpha);
+
+  progress = clutter_timeline_get_progressx (timeline);
+  angle = clutter_qmulx (CFX_PI_2 + CFX_PI_4/2, progress);
+
+  return clutter_sinx (angle) +
+    (CFX_ONE - clutter_sinx (CFX_PI_2 + CFX_PI_4/2));
+}
+*/
+
+void
+nbtk_adjustment_interpolate (NbtkAdjustment *adjustment,
+                             gdouble         value,
+                             guint           duration)
+{
+  NbtkAdjustmentPrivate *priv = adjustment->priv;
+
+  stop_interpolation (adjustment);
+
+  if (duration <= 1)
+    {
+      nbtk_adjustment_set_value (adjustment, value);
+      return;
+    }
+
+  priv->old_position = priv->value;
+  priv->new_position = value;
+
+  priv->interpolation = clutter_timeline_new (duration);
+
+  if (priv->elastic)
+    priv->bounce_alpha = clutter_alpha_new_full (priv->interpolation,
+                                                 CLUTTER_LINEAR);
+
+  g_signal_connect (priv->interpolation,
+                    "new-frame",
+                    G_CALLBACK (interpolation_new_frame_cb),
+                    adjustment);
+  g_signal_connect (priv->interpolation,
+                    "completed",
+                    G_CALLBACK (interpolation_completed_cb),
+                    adjustment);
+
+  clutter_timeline_start (priv->interpolation);
+}
+
+gboolean
+nbtk_adjustment_get_elastic (NbtkAdjustment *adjustment)
+{
+  return adjustment->priv->elastic;
+}
+
+void
+nbtk_adjustment_set_elastic (NbtkAdjustment *adjustment,
+                             gboolean        elastic)
+{
+  adjustment->priv->elastic = elastic;
+}
+
+gboolean
+nbtk_adjustment_clamp (NbtkAdjustment *adjustment,
+                       gboolean        interpolate,
+                       guint           duration)
+{
+  NbtkAdjustmentPrivate *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)
+        nbtk_adjustment_interpolate (adjustment, dest, duration);
+      else
+        nbtk_adjustment_set_value (adjustment, dest);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
diff --git a/src/nbtk/nbtk-adjustment.h b/src/nbtk/nbtk-adjustment.h
new file mode 100644
index 0000000..1fdfb04
--- /dev/null
+++ b/src/nbtk/nbtk-adjustment.h
@@ -0,0 +1,121 @@
+/*
+ * nbtk-adjustment.h: Adjustment object
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>, inspired by GtkAdjustment
+ * Port to Nbtk by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_ADJUSTMENT_H__
+#define __NBTK_ADJUSTMENT_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_ADJUSTMENT            (nbtk_adjustment_get_type())
+#define NBTK_ADJUSTMENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_ADJUSTMENT, NbtkAdjustment))
+#define NBTK_IS_ADJUSTMENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_ADJUSTMENT))
+#define NBTK_ADJUSTMENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_ADJUSTMENT, NbtkAdjustmentClass))
+#define NBTK_IS_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_ADJUSTMENT))
+#define NBTK_ADJUSTMENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_ADJUSTMENT, NbtkAdjustmentClass))
+
+typedef struct _NbtkAdjustment          NbtkAdjustment;
+typedef struct _NbtkAdjustmentPrivate   NbtkAdjustmentPrivate;
+typedef struct _NbtkAdjustmentClass     NbtkAdjustmentClass;
+
+/**
+ * NbtkAdjustment:
+ *
+ * Class for handling an interval between to values. The contents of
+ * the #NbtkAdjustment are private and should be accessed using the
+ * public API.
+ */
+struct _NbtkAdjustment
+{
+  /*< private >*/
+  GObject parent_instance;
+
+  NbtkAdjustmentPrivate *priv;
+};
+
+/**
+ * NbtkAdjustmentClass
+ * @changed: Class handler for the ::changed signal.
+ *
+ * Base class for #NbtkAdjustment.
+ */
+struct _NbtkAdjustmentClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+
+  /*< public >*/
+  void (* changed) (NbtkAdjustment *adjustment);
+};
+
+GType nbtk_adjustment_get_type (void) G_GNUC_CONST;
+
+NbtkAdjustment *nbtk_adjustment_new          (gdouble         value,
+                                              gdouble         lower,
+                                              gdouble         upper,
+                                              gdouble         step_increment,
+                                              gdouble         page_increment,
+                                              gdouble         page_size);
+gdouble         nbtk_adjustment_get_value    (NbtkAdjustment *adjustment);
+void            nbtk_adjustment_set_value    (NbtkAdjustment *adjustment,
+                                              gdouble         value);
+void            nbtk_adjustment_clamp_page   (NbtkAdjustment *adjustment,
+                                              gdouble         lower,
+                                              gdouble         upper);
+void            nbtk_adjustment_set_values   (NbtkAdjustment *adjustment,
+                                              gdouble         value,
+                                              gdouble         lower,
+                                              gdouble         upper,
+                                              gdouble         step_increment,
+                                              gdouble         page_increment,
+                                              gdouble         page_size);
+void            nbtk_adjustment_get_values   (NbtkAdjustment *adjustment,
+                                              gdouble        *value,
+                                              gdouble        *lower,
+                                              gdouble        *upper,
+                                              gdouble        *step_increment,
+                                              gdouble        *page_increment,
+                                              gdouble        *page_size);
+
+void            nbtk_adjustment_interpolate  (NbtkAdjustment *adjustment,
+                                              gdouble         value,
+                                              guint           duration);
+
+gboolean        nbtk_adjustment_get_elastic  (NbtkAdjustment *adjustment);
+void            nbtk_adjustment_set_elastic  (NbtkAdjustment *adjustment,
+                                              gboolean        elastic);
+
+gboolean        nbtk_adjustment_clamp        (NbtkAdjustment *adjustment,
+                                              gboolean        interpolate,
+                                              guint           duration);
+
+G_END_DECLS
+
+#endif /* __NBTK_ADJUSTMENT_H__ */
diff --git a/src/nbtk/nbtk-button.c b/src/nbtk/nbtk-button.c
new file mode 100644
index 0000000..558852d
--- /dev/null
+++ b/src/nbtk/nbtk-button.c
@@ -0,0 +1,759 @@
+/*
+ * nbtk-button.c: Plain button actor
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ *             Thomas Wood <thomas linux intel com>
+ *
+ */
+
+/**
+ * SECTION:nbtk-button
+ * @short_description: Button widget
+ *
+ * A button widget with support for either a text label or icon, toggle mode
+ * and transitions effects between states.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <clutter/clutter.h>
+
+#include "nbtk-button.h"
+
+#include "nbtk-marshal.h"
+#include "nbtk-stylable.h"
+#include "nbtk-style.h"
+#include "nbtk-texture-frame.h"
+#include "nbtk-texture-cache.h"
+#include "nbtk-private.h"
+
+enum
+{
+  PROP_0,
+
+  PROP_LABEL,
+  PROP_TOGGLE,
+  PROP_ACTIVE,
+  PROP_TRANSITION
+};
+
+enum
+{
+  CLICKED,
+
+  LAST_SIGNAL
+};
+
+#define NBTK_BUTTON_GET_PRIVATE(obj)    \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_BUTTON, NbtkButtonPrivate))
+
+struct _NbtkButtonPrivate
+{
+  gchar *text;
+
+  ClutterActor *old_bg;
+  gboolean old_bg_parented; /* TRUE if we have adopted old_bg */
+
+  guint8 old_opacity;
+
+  guint is_pressed : 1;
+  guint is_hover : 1;
+  guint is_checked : 1;
+  guint is_toggle : 1;
+
+  gint transition_duration;
+
+  ClutterAnimation *animation;
+
+  gint spacing;
+};
+
+static guint button_signals[LAST_SIGNAL] = { 0, };
+
+static void nbtk_stylable_iface_init (NbtkStylableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (NbtkButton, nbtk_button, NBTK_TYPE_BIN,
+                         G_IMPLEMENT_INTERFACE (NBTK_TYPE_STYLABLE,
+                                                nbtk_stylable_iface_init));
+
+static void
+nbtk_stylable_iface_init (NbtkStylableIface *iface)
+{
+  static gboolean is_initialized = FALSE;
+
+  if (G_UNLIKELY (!is_initialized))
+    {
+      ClutterColor bg_color = { 0xcc, 0xcc, 0xcc, 0x00 };
+      GParamSpec *pspec;
+
+      is_initialized = TRUE;
+
+      pspec = g_param_spec_int ("border-spacing",
+                                "Border Spacing",
+                                "Spacing between internal elements",
+                                0, G_MAXINT, 6,
+                                G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface, NBTK_TYPE_BUTTON, pspec);
+
+
+      is_initialized = TRUE;
+
+      pspec = clutter_param_spec_color ("background-color",
+                                        "Background Color",
+                                        "The background color of an actor",
+                                        &bg_color,
+                                        G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface, NBTK_TYPE_BUTTON, pspec);
+    }
+}
+
+static void
+nbtk_button_update_label_style (NbtkButton *button)
+{
+  ClutterColor *real_color = NULL;
+  gchar *font_string = NULL;
+  gchar *font_name = NULL;
+  gint font_size = 0;
+  ClutterActor *label;
+
+  label = nbtk_bin_get_child ((NbtkBin*) button);
+
+  /* check the child is really a label */
+  if (!CLUTTER_IS_TEXT (label))
+    return;
+
+  nbtk_stylable_get (NBTK_STYLABLE (button),
+                     "color", &real_color,
+                     "font-family", &font_name,
+                     "font-size", &font_size,
+                     NULL);
+
+  if (font_name || font_size)
+    {
+      if (font_name && font_size)
+        font_string = g_strdup_printf ("%s %dpx", font_name, font_size);
+      else
+        {
+          if (font_size)
+            font_string = g_strdup_printf ("%dpx", font_size);
+          else
+            font_string = font_name;
+        }
+
+      clutter_text_set_font_name (CLUTTER_TEXT (label), font_string);
+
+      if (font_string != font_name)
+        g_free (font_string);
+    }
+
+  g_free (font_name);
+
+  if (real_color)
+    {
+      clutter_text_set_color (CLUTTER_TEXT (label), real_color);
+      clutter_color_free (real_color);
+    }
+}
+
+static void
+nbtk_button_dispose_old_bg (NbtkButton *button)
+{
+  NbtkButtonPrivate *priv = button->priv;
+
+  if (priv->old_bg)
+    {
+      if (priv->old_bg_parented)
+        {
+          clutter_actor_unparent (priv->old_bg);
+          priv->old_bg_parented = FALSE;
+        }
+      g_object_unref (priv->old_bg);
+      priv->old_bg = NULL;
+    }
+}
+
+static void
+nbtk_button_stylable_changed (NbtkStylable *stylable)
+{
+  NbtkButton *button = NBTK_BUTTON (stylable);
+  ClutterActor *bg_image;
+
+  nbtk_button_dispose_old_bg (button);
+
+  bg_image = nbtk_widget_get_border_image ((NbtkWidget*) button);
+  if (bg_image)
+    button->priv->old_bg = g_object_ref (bg_image);
+}
+
+static void
+nbtk_animation_completed (ClutterAnimation  *animation,
+                          NbtkButton        *button)
+{
+  nbtk_button_dispose_old_bg (button);
+}
+
+static void
+nbtk_button_style_changed (NbtkWidget *widget)
+{
+  NbtkButton *button = NBTK_BUTTON (widget);
+  NbtkButtonPrivate *priv = button->priv;
+  NbtkButtonClass *button_class = NBTK_BUTTON_GET_CLASS (button);
+
+  /* get the spacing value */
+  nbtk_stylable_get (NBTK_STYLABLE (widget),
+                     "border-spacing", &priv->spacing,
+                     NULL);
+
+  /* update the label styling */
+  nbtk_button_update_label_style (button);
+
+  /* run a transition if applicable */
+  if (button_class->transition)
+    {
+      button_class->transition (button, priv->old_bg);
+    }
+  else
+    {
+      if (priv->old_bg &&
+          (!nbtk_widget_get_style_pseudo_class (widget)))
+        {
+          ClutterAnimation *animation;
+          if (!clutter_actor_get_parent (priv->old_bg))
+            {
+              clutter_actor_set_parent (priv->old_bg, (ClutterActor*) widget);
+              priv->old_bg_parented = TRUE;
+            }
+          if (priv->transition_duration > 0)
+            {
+              animation = clutter_actor_animate (priv->old_bg,
+                                                 CLUTTER_LINEAR,
+                                                 priv->transition_duration,
+                                                 "opacity", 0,
+                                                 NULL);
+              g_signal_connect (animation, "completed",
+                                G_CALLBACK (nbtk_animation_completed), button);
+            }
+          else
+            {
+              nbtk_button_dispose_old_bg (button);
+            }
+
+        }
+    }
+}
+
+static void
+nbtk_button_real_pressed (NbtkButton *button)
+{
+  nbtk_widget_set_style_pseudo_class ((NbtkWidget*) button, "active");
+}
+
+static void
+nbtk_button_real_released (NbtkButton *button)
+{
+  NbtkButtonPrivate *priv = button->priv;
+
+  if (priv->is_checked)
+    nbtk_widget_set_style_pseudo_class ((NbtkWidget*) button, "checked");
+  else if (!priv->is_hover)
+    nbtk_widget_set_style_pseudo_class ((NbtkWidget*) button, NULL);
+  else
+    nbtk_widget_set_style_pseudo_class ((NbtkWidget*) button, "hover");
+
+}
+
+static gboolean
+nbtk_button_button_press (ClutterActor       *actor,
+                          ClutterButtonEvent *event)
+{
+  nbtk_widget_hide_tooltip (NBTK_WIDGET (actor));
+
+  if (event->button == 1)
+    {
+      NbtkButton *button = NBTK_BUTTON (actor);
+      NbtkButtonClass *klass = NBTK_BUTTON_GET_CLASS (button);
+
+      button->priv->is_pressed = TRUE;
+
+      clutter_grab_pointer (actor);
+
+      if (klass->pressed)
+        klass->pressed (button);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+nbtk_button_button_release (ClutterActor       *actor,
+                            ClutterButtonEvent *event)
+{
+  if (event->button == 1)
+    {
+      NbtkButton *button = NBTK_BUTTON (actor);
+      NbtkButtonClass *klass = NBTK_BUTTON_GET_CLASS (button);
+
+      if (!button->priv->is_pressed)
+        return FALSE;
+
+      clutter_ungrab_pointer ();
+
+      if (button->priv->is_toggle)
+        {
+          nbtk_button_set_checked (button, !button->priv->is_checked);
+        }
+
+      button->priv->is_pressed = FALSE;
+
+      if (klass->released)
+        klass->released (button);
+
+      g_signal_emit (button, button_signals[CLICKED], 0);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+nbtk_button_enter (ClutterActor         *actor,
+                   ClutterCrossingEvent *event)
+{
+  NbtkButton *button = NBTK_BUTTON (actor);
+
+  if (!button->priv->is_checked)
+    nbtk_widget_set_style_pseudo_class ((NbtkWidget*) button, "hover");
+
+  button->priv->is_hover = 1;
+
+  return CLUTTER_ACTOR_CLASS (nbtk_button_parent_class)->enter_event (actor, event);
+}
+
+static gboolean
+nbtk_button_leave (ClutterActor         *actor,
+                   ClutterCrossingEvent *event)
+{
+  NbtkButton *button = NBTK_BUTTON (actor);
+
+  button->priv->is_hover = 0;
+
+  if (button->priv->is_pressed)
+    {
+      NbtkButtonClass *klass = NBTK_BUTTON_GET_CLASS (button);
+
+      clutter_ungrab_pointer ();
+
+      button->priv->is_pressed = FALSE;
+
+      if (klass->released)
+        klass->released (button);
+    }
+
+  if (button->priv->is_checked)
+    nbtk_widget_set_style_pseudo_class ((NbtkWidget*) button, "checked");
+  else
+    nbtk_widget_set_style_pseudo_class ((NbtkWidget*) button, NULL);
+
+  return CLUTTER_ACTOR_CLASS (nbtk_button_parent_class)->leave_event (actor, event);
+}
+
+static void
+nbtk_button_set_property (GObject      *gobject,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  NbtkButton *button = NBTK_BUTTON (gobject);
+  NbtkButtonPrivate *priv = NBTK_BUTTON (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_LABEL:
+      nbtk_button_set_label (button, g_value_get_string (value));
+      break;
+    case PROP_TOGGLE:
+      nbtk_button_set_toggle_mode (button, g_value_get_boolean (value));
+      break;
+    case PROP_ACTIVE:
+      nbtk_button_set_checked (button, g_value_get_boolean (value));
+      break;
+    case PROP_TRANSITION:
+      priv->transition_duration = g_value_get_int (value);
+      break;
+
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_button_get_property (GObject    *gobject,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  NbtkButtonPrivate *priv = NBTK_BUTTON (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_LABEL:
+      g_value_set_string (value, priv->text);
+      break;
+    case PROP_TOGGLE:
+      g_value_set_boolean (value, priv->is_toggle);
+      break;
+    case PROP_ACTIVE:
+      g_value_set_boolean (value, priv->is_checked);
+      break;
+    case PROP_TRANSITION:
+      g_value_set_int (value, priv->transition_duration);
+      break;
+
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_button_finalize (GObject *gobject)
+{
+  NbtkButtonPrivate *priv = NBTK_BUTTON (gobject)->priv;
+
+  g_free (priv->text);
+
+  G_OBJECT_CLASS (nbtk_button_parent_class)->finalize (gobject);
+}
+
+static void
+nbtk_button_dispose (GObject *gobject)
+{
+  nbtk_button_dispose_old_bg (NBTK_BUTTON (gobject));
+
+  G_OBJECT_CLASS (nbtk_button_parent_class)->dispose (gobject);
+}
+
+static void
+nbtk_button_map (ClutterActor *self)
+{
+  NbtkButtonPrivate *priv = NBTK_BUTTON (self)->priv;
+
+  CLUTTER_ACTOR_CLASS (nbtk_button_parent_class)->map (self);
+
+  if (priv->old_bg && priv->old_bg_parented)
+    clutter_actor_map (priv->old_bg);
+}
+
+static void
+nbtk_button_unmap (ClutterActor *self)
+{
+  NbtkButtonPrivate *priv = NBTK_BUTTON (self)->priv;
+
+  CLUTTER_ACTOR_CLASS (nbtk_button_parent_class)->unmap (self);
+
+  if (priv->old_bg && priv->old_bg_parented)
+    clutter_actor_unmap (priv->old_bg);
+}
+
+static void
+nbtk_button_draw_background (NbtkWidget         *widget,
+                             ClutterActor       *background,
+                             const ClutterColor *color)
+{
+  NbtkButtonPrivate *priv;
+
+  NBTK_WIDGET_CLASS (nbtk_button_parent_class)->draw_background (widget, background, color);
+
+  priv = NBTK_BUTTON (widget)->priv;
+
+  if (priv->old_bg && priv->old_bg_parented)
+      clutter_actor_paint (priv->old_bg);
+}
+
+static void
+nbtk_button_class_init (NbtkButtonClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+  NbtkWidgetClass *widget_class = NBTK_WIDGET_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (NbtkButtonPrivate));
+
+  klass->pressed = nbtk_button_real_pressed;
+  klass->released = nbtk_button_real_released;
+
+  gobject_class->set_property = nbtk_button_set_property;
+  gobject_class->get_property = nbtk_button_get_property;
+  gobject_class->dispose = nbtk_button_dispose;
+  gobject_class->finalize = nbtk_button_finalize;
+
+  actor_class->button_press_event = nbtk_button_button_press;
+  actor_class->button_release_event = nbtk_button_button_release;
+  actor_class->enter_event = nbtk_button_enter;
+  actor_class->leave_event = nbtk_button_leave;
+
+  actor_class->map = nbtk_button_map;
+  actor_class->unmap = nbtk_button_unmap;
+
+  widget_class->draw_background = nbtk_button_draw_background;
+
+  pspec = g_param_spec_string ("label",
+                               "Label",
+                               "Label of the button",
+                               NULL, G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_LABEL, pspec);
+
+  pspec = g_param_spec_boolean ("toggle-mode",
+                                "Toggle Mode",
+                                "Enable or disable toggling",
+                                FALSE, G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_TOGGLE, pspec);
+
+  pspec = g_param_spec_boolean ("checked",
+                                "Checked",
+                                "Indicates if a toggle button is \"on\""
+                                " or \"off\"",
+                                FALSE, G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_ACTIVE, pspec);
+
+  pspec = g_param_spec_int ("transition-duration",
+                            "Transition Duration",
+                            "Duration of the state transition effect",
+                            0, G_MAXINT, 120, G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_TRANSITION, pspec);
+
+
+  /**
+   * NbtkButton::clicked:
+   * @button: the object that received the signal
+   *
+   * Emitted when the user activates the button, either with a mouse press and
+   * release or with the keyboard.
+   */
+
+  button_signals[CLICKED] =
+    g_signal_new ("clicked",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (NbtkButtonClass, clicked),
+                  NULL, NULL,
+                  _nbtk_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+nbtk_button_init (NbtkButton *button)
+{
+  button->priv = NBTK_BUTTON_GET_PRIVATE (button);
+  button->priv->transition_duration = 120;
+  button->priv->spacing = 6;
+
+  clutter_actor_set_reactive ((ClutterActor *) button, TRUE);
+
+  g_signal_connect (button, "style-changed",
+                    G_CALLBACK (nbtk_button_style_changed), NULL);
+
+  g_signal_connect (button, "stylable-changed",
+                    G_CALLBACK (nbtk_button_stylable_changed), NULL);
+}
+
+/**
+ * nbtk_button_new:
+ *
+ * Create a new button
+ *
+ * Returns: a new #NbtkButton
+ */
+NbtkWidget *
+nbtk_button_new (void)
+{
+  return g_object_new (NBTK_TYPE_BUTTON, NULL);
+}
+
+/**
+ * nbtk_button_new_with_label:
+ * @text: text to set the label to
+ *
+ * Create a new #NbtkButton with the specified label
+ *
+ * Returns: a new #NbtkButton
+ */
+NbtkWidget *
+nbtk_button_new_with_label (const gchar *text)
+{
+  return g_object_new (NBTK_TYPE_BUTTON, "label", text, NULL);
+}
+
+/**
+ * nbtk_button_get_label:
+ * @button: a #NbtkButton
+ *
+ * Get the text displayed on the button
+ *
+ * Returns: the text for the button. This must not be freed by the application
+ */
+G_CONST_RETURN gchar *
+nbtk_button_get_label (NbtkButton *button)
+{
+  g_return_val_if_fail (NBTK_IS_BUTTON (button), NULL);
+
+  return button->priv->text;
+}
+
+/**
+ * nbtk_button_set_label:
+ * @button: a #Nbtkbutton
+ * @text: text to set the label to
+ *
+ * Sets the text displayed on the button
+ */
+void
+nbtk_button_set_label (NbtkButton  *button,
+                       const gchar *text)
+{
+  NbtkButtonPrivate *priv;
+  ClutterActor *label;
+
+  g_return_if_fail (NBTK_IS_BUTTON (button));
+
+  priv = button->priv;
+
+  g_free (priv->text);
+
+  if (text)
+    priv->text = g_strdup (text);
+  else
+    priv->text = g_strdup ("");
+
+  label = nbtk_bin_get_child ((NbtkBin*) button);
+
+  if (label && CLUTTER_IS_TEXT (label))
+    {
+      clutter_text_set_text (CLUTTER_TEXT (label), priv->text);
+    }
+  else
+    {
+      label = g_object_new (CLUTTER_TYPE_TEXT,
+                            "text", priv->text,
+                            "line-alignment", PANGO_ALIGN_CENTER,
+                            "ellipsize", PANGO_ELLIPSIZE_END,
+                            "use-markup", TRUE,
+                            NULL);
+      nbtk_bin_set_child ((NbtkBin*) button, label);
+    }
+
+  nbtk_stylable_changed ((NbtkStylable*) button);
+
+  g_object_notify (G_OBJECT (button), "label");
+}
+
+/**
+ * nbtk_button_get_toggle_mode:
+ * @button: a #NbtkButton
+ *
+ * Get the toggle mode status of the button.
+ *
+ * Returns: #TRUE if toggle mode is set, otherwise #FALSE
+ */
+gboolean
+nbtk_button_get_toggle_mode (NbtkButton *button)
+{
+  g_return_val_if_fail (NBTK_IS_BUTTON (button), FALSE);
+
+  return button->priv->is_toggle;
+}
+
+/**
+ * nbtk_button_set_toggle_mode:
+ * @button: a #Nbtkbutton
+ * @toggle: #TRUE or #FALSE
+ *
+ * Enables or disables toggle mode for the button. In toggle mode, the active
+ * state will be "toggled" when the user clicks the button.
+ */
+void
+nbtk_button_set_toggle_mode (NbtkButton  *button,
+                             gboolean     toggle)
+{
+  g_return_if_fail (NBTK_IS_BUTTON (button));
+
+  button->priv->is_toggle = toggle;
+
+  g_object_notify (G_OBJECT (button), "toggle-mode");
+}
+
+/**
+ * nbtk_button_get_checked:
+ * @button: a #NbtkButton
+ *
+ * Get the state of the button that is in toggle mode.
+ *
+ * Returns: #TRUE if the button is checked, or #FALSE if not
+ */
+gboolean
+nbtk_button_get_checked (NbtkButton *button)
+{
+  g_return_val_if_fail (NBTK_IS_BUTTON (button), FALSE);
+
+  return button->priv->is_checked;
+}
+
+/**
+ * nbtk_button_set_checked:
+ * @button: a #Nbtkbutton
+ * @checked: #TRUE or #FALSE
+ *
+ * Sets the pressed state of the button. This is only really useful if the
+ * button has #toggle-mode mode set to #TRUE.
+ */
+void
+nbtk_button_set_checked (NbtkButton  *button,
+                         gboolean     checked)
+{
+  g_return_if_fail (NBTK_IS_BUTTON (button));
+
+  if (button->priv->is_checked != checked)
+    {
+      button->priv->is_checked = checked;
+
+      if (checked)
+        nbtk_widget_set_style_pseudo_class ((NbtkWidget*) button, "checked");
+      else
+        if (button->priv->is_hover)
+          nbtk_widget_set_style_pseudo_class ((NbtkWidget*) button, "hover");
+        else
+          nbtk_widget_set_style_pseudo_class ((NbtkWidget*) button, NULL);
+    }
+
+  g_object_notify (G_OBJECT (button), "checked");
+}
diff --git a/src/nbtk/nbtk-button.h b/src/nbtk/nbtk-button.h
new file mode 100644
index 0000000..711417c
--- /dev/null
+++ b/src/nbtk/nbtk-button.h
@@ -0,0 +1,90 @@
+/*
+ * nbtk-button.h: Plain button actor
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ *             Thomas Wood <thomas linux intel com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_BUTTON_H__
+#define __NBTK_BUTTON_H__
+
+G_BEGIN_DECLS
+
+#include <nbtk/nbtk-bin.h>
+
+#define NBTK_TYPE_BUTTON                (nbtk_button_get_type ())
+#define NBTK_BUTTON(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_BUTTON, NbtkButton))
+#define NBTK_IS_BUTTON(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_BUTTON))
+#define NBTK_BUTTON_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_BUTTON, NbtkButtonClass))
+#define NBTK_IS_BUTTON_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_BUTTON))
+#define NBTK_BUTTON_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_BUTTON, NbtkButtonClass))
+
+typedef struct _NbtkButton              NbtkButton;
+typedef struct _NbtkButtonPrivate       NbtkButtonPrivate;
+typedef struct _NbtkButtonClass         NbtkButtonClass;
+
+/**
+ * NbtkButton:
+ *
+ * The contents of this structure is private and should only be accessed using
+ * the provided API.
+ */
+
+struct _NbtkButton
+{
+  /*< private >*/
+  NbtkBin parent_instance;
+
+  NbtkButtonPrivate *priv;
+};
+
+struct _NbtkButtonClass
+{
+  NbtkBinClass parent_class;
+
+  /* vfuncs, not signals */
+  void (* pressed)  (NbtkButton *button);
+  void (* released) (NbtkButton *button);
+  void (* transition) (NbtkButton *button, ClutterActor *old_bg);
+
+  /* signals */
+  void (* clicked) (NbtkButton *button);
+};
+
+GType nbtk_button_get_type (void) G_GNUC_CONST;
+
+NbtkWidget *          nbtk_button_new            (void);
+NbtkWidget *          nbtk_button_new_with_label (const gchar  *text);
+G_CONST_RETURN gchar *nbtk_button_get_label      (NbtkButton   *button);
+void                  nbtk_button_set_label      (NbtkButton   *button,
+                                                  const gchar  *text);
+void                  nbtk_button_set_toggle_mode    (NbtkButton *button, gboolean toggle);
+gboolean              nbtk_button_get_toggle_mode    (NbtkButton *button);
+void                  nbtk_button_set_checked        (NbtkButton *button, gboolean checked);
+gboolean              nbtk_button_get_checked        (NbtkButton *button);
+
+G_END_DECLS
+
+#endif /* __NBTK_BUTTON_H__ */
diff --git a/src/nbtk/nbtk-scroll-bar.c b/src/nbtk/nbtk-scroll-bar.c
new file mode 100644
index 0000000..2c67833
--- /dev/null
+++ b/src/nbtk/nbtk-scroll-bar.c
@@ -0,0 +1,1094 @@
+/*
+ * nbtk-scroll-bar.c: Scroll bar actor
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to Nbtk by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <clutter/clutter.h>
+
+#include "nbtk-scroll-bar.h"
+#include "nbtk-bin.h"
+#include "nbtk-marshal.h"
+#include "nbtk-stylable.h"
+#include "nbtk-enum-types.h"
+#include "nbtk-private.h"
+#include "nbtk-button.h"
+
+static void nbtk_stylable_iface_init (NbtkStylableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (NbtkScrollBar, nbtk_scroll_bar, NBTK_TYPE_BIN,
+                         G_IMPLEMENT_INTERFACE (NBTK_TYPE_STYLABLE,
+                                                nbtk_stylable_iface_init))
+
+#define NBTK_SCROLL_BAR_GET_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), NBTK_TYPE_SCROLL_BAR, NbtkScrollBarPrivate))
+
+#define PAGING_INITIAL_REPEAT_TIMEOUT 500
+#define PAGING_SUBSEQUENT_REPEAT_TIMEOUT 200
+
+struct _NbtkScrollBarPrivate
+{
+  NbtkAdjustment    *adjustment;
+
+  gulong             capture_handler;
+  gfloat             x_origin;
+  gfloat             y_origin;
+
+  ClutterActor      *bw_stepper;
+  ClutterActor      *fw_stepper;
+  ClutterActor      *trough;
+  ClutterActor      *handle;
+
+  gfloat             move_x;
+  gfloat             move_y;
+
+  /* Trough-click handling. */
+  enum { NONE, UP, DOWN }  paging_direction;
+  guint              paging_source_id;
+  guint              paging_event_no;
+
+  gboolean           stepper_forward;
+  guint              stepper_source_id;
+
+  ClutterAnimation  *paging_animation;
+
+  gboolean           vertical;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_ADJUSTMENT,
+  PROP_VERTICAL
+};
+
+enum
+{
+  SCROLL_START,
+  SCROLL_STOP,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static gboolean
+handle_button_press_event_cb (ClutterActor       *actor,
+                              ClutterButtonEvent *event,
+                              NbtkScrollBar      *bar);
+
+static void
+nbtk_scroll_bar_get_property (GObject    *gobject,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  NbtkScrollBarPrivate *priv = NBTK_SCROLL_BAR (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_ADJUSTMENT:
+      g_value_set_object (value, priv->adjustment);
+      break;
+
+    case PROP_VERTICAL:
+      g_value_set_boolean (value, priv->vertical);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_scroll_bar_set_property (GObject      *gobject,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  NbtkScrollBar *bar = NBTK_SCROLL_BAR (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_ADJUSTMENT:
+      nbtk_scroll_bar_set_adjustment (bar, g_value_get_object (value));
+      break;
+
+    case PROP_VERTICAL:
+      bar->priv->vertical = g_value_get_boolean (value);
+      if (bar->priv->vertical)
+        {
+          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->bw_stepper),
+                                  "up-stepper");
+          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->fw_stepper),
+                                  "down-stepper");
+          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->handle),
+                                  "vhandle");
+        }
+      else
+        {
+          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->fw_stepper),
+                                  "forward-stepper");
+          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->bw_stepper),
+                                  "backward-stepper");
+          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->handle),
+                                  "hhandle");
+        }
+      clutter_actor_queue_relayout ((ClutterActor*) gobject);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_scroll_bar_dispose (GObject *gobject)
+{
+  NbtkScrollBar *bar = NBTK_SCROLL_BAR (gobject);
+  NbtkScrollBarPrivate *priv = bar->priv;
+
+  if (priv->adjustment)
+    nbtk_scroll_bar_set_adjustment (bar, NULL);
+
+  if (priv->handle)
+    {
+      g_signal_handlers_disconnect_by_func (priv->handle,
+                                            G_CALLBACK (handle_button_press_event_cb),
+                                            bar);
+      clutter_actor_unparent (priv->handle);
+      priv->handle = NULL;
+    }
+
+  clutter_actor_unparent (priv->bw_stepper);
+  priv->bw_stepper = NULL;
+
+  clutter_actor_unparent (priv->fw_stepper);
+  priv->fw_stepper = NULL;
+
+  clutter_actor_unparent (priv->trough);
+  priv->trough = NULL;
+
+  G_OBJECT_CLASS (nbtk_scroll_bar_parent_class)->dispose (gobject);
+}
+
+static void
+nbtk_scroll_bar_paint (ClutterActor *actor)
+{
+  NbtkScrollBarPrivate *priv = NBTK_SCROLL_BAR (actor)->priv;
+
+  CLUTTER_ACTOR_CLASS (nbtk_scroll_bar_parent_class)->paint (actor);
+
+  clutter_actor_paint (priv->bw_stepper);
+
+  clutter_actor_paint (priv->fw_stepper);
+
+  clutter_actor_paint (priv->trough);
+
+  if (priv->handle && CLUTTER_ACTOR_IS_VISIBLE (priv->handle))
+    clutter_actor_paint (priv->handle);
+}
+
+static void
+nbtk_scroll_bar_pick (ClutterActor       *actor,
+                      const ClutterColor *pick_color)
+{
+  NbtkScrollBarPrivate *priv = NBTK_SCROLL_BAR (actor)->priv;
+
+  CLUTTER_ACTOR_CLASS (nbtk_scroll_bar_parent_class)->pick (actor, pick_color);
+
+  clutter_actor_paint (priv->bw_stepper);
+  clutter_actor_paint (priv->fw_stepper);
+  clutter_actor_paint (priv->trough);
+
+  if (priv->handle && priv->adjustment)
+    clutter_actor_paint (priv->handle);
+}
+
+static void
+nbtk_scroll_bar_map (ClutterActor *actor)
+{
+  NbtkScrollBarPrivate *priv = NBTK_SCROLL_BAR (actor)->priv;
+
+  CLUTTER_ACTOR_CLASS (nbtk_scroll_bar_parent_class)->map (actor);
+
+  clutter_actor_map (priv->bw_stepper);
+  clutter_actor_map (priv->fw_stepper);
+  clutter_actor_map (priv->trough);
+
+  if (priv->handle)
+    clutter_actor_map (priv->handle);
+}
+
+static void
+nbtk_scroll_bar_unmap (ClutterActor *actor)
+{
+  NbtkScrollBarPrivate *priv = NBTK_SCROLL_BAR (actor)->priv;
+
+  CLUTTER_ACTOR_CLASS (nbtk_scroll_bar_parent_class)->unmap (actor);
+
+  clutter_actor_unmap (priv->bw_stepper);
+  clutter_actor_unmap (priv->fw_stepper);
+  clutter_actor_unmap (priv->trough);
+
+  if (priv->handle)
+    clutter_actor_unmap (priv->handle);
+}
+
+static void
+nbtk_scroll_bar_allocate (ClutterActor          *actor,
+                          const ClutterActorBox *box,
+                          ClutterAllocationFlags flags)
+{
+  NbtkScrollBarPrivate *priv = NBTK_SCROLL_BAR (actor)->priv;
+  NbtkPadding padding;
+  ClutterActorBox bw_box, fw_box, trough_box;
+  gfloat x, y, width, height, stepper_size;
+
+  /* Chain up */
+  CLUTTER_ACTOR_CLASS (nbtk_scroll_bar_parent_class)->
+    allocate (actor, box, flags);
+
+  nbtk_widget_get_padding (NBTK_WIDGET (actor), &padding);
+
+  /* calculate the child area */
+  x = padding.left;
+  y = padding.top;
+  width = (box->x2 - box->x1) - padding.left - padding.right;
+  height = (box->y2 - box->y1) - padding.top - padding.bottom;
+
+  if (priv->vertical)
+    {
+      stepper_size = width;
+
+      /* Backward stepper */
+      bw_box.x1 = x;
+      bw_box.y1 = y;
+      bw_box.x2 = bw_box.x1 + stepper_size;
+      bw_box.y2 = bw_box.y1 + stepper_size;
+      clutter_actor_allocate (priv->bw_stepper, &bw_box, flags);
+
+      /* Forward stepper */
+      fw_box.x1 = x;
+      fw_box.y1 = y + height - stepper_size;
+      fw_box.x2 = fw_box.x1 + stepper_size;
+      fw_box.y2 = fw_box.y1 + stepper_size;
+      clutter_actor_allocate (priv->fw_stepper, &fw_box, flags);
+
+      /* Trough */
+      trough_box.x1 = x;
+      trough_box.y1 = y + stepper_size;
+      trough_box.x2 = x + width;
+      trough_box.y2 = y + height - stepper_size;
+      clutter_actor_allocate (priv->trough, &trough_box, flags);
+
+    }
+  else
+    {
+      stepper_size = height;
+
+      /* Backward stepper */
+      bw_box.x1 = x;
+      bw_box.y1 = y;
+      bw_box.x2 = bw_box.x1 + stepper_size;
+      bw_box.y2 = bw_box.y1 + stepper_size;
+      clutter_actor_allocate (priv->bw_stepper, &bw_box, flags);
+
+      /* Forward stepper */
+      fw_box.x1 = x + width - stepper_size;
+      fw_box.y1 = y;
+      fw_box.x2 = fw_box.x1 + stepper_size;
+      fw_box.y2 = fw_box.y1 + stepper_size;
+      clutter_actor_allocate (priv->fw_stepper, &fw_box, flags);
+
+      /* Trough */
+      trough_box.x1 = x + stepper_size;
+      trough_box.y1 = y;
+      trough_box.x2 = x + width - stepper_size;
+      trough_box.y2 = y + height;
+      clutter_actor_allocate (priv->trough, &trough_box, flags);
+    }
+
+
+  if (priv->adjustment)
+    {
+      gfloat handle_size, position, avail_size;
+      gdouble value, lower, upper, page_size, increment;
+      ClutterActorBox handle_box = { 0, };
+      guint min_size, max_size;
+
+      nbtk_adjustment_get_values (priv->adjustment,
+                                  &value,
+                                  &lower,
+                                  &upper,
+                                  NULL,
+                                  NULL,
+                                  &page_size);
+
+      if ((upper == lower)
+          || (page_size >= (upper - lower)))
+        increment = 1.0;
+      else
+        increment = page_size / (upper - lower);
+
+      nbtk_stylable_get (NBTK_STYLABLE (actor),
+                         "min-size", &min_size,
+                         "max-size", &max_size,
+                         NULL);
+
+      if (upper - lower - page_size <= 0)
+        position = 0;
+      else
+        position = (value - lower) / (upper - lower - page_size);
+
+      if (priv->vertical)
+        {
+          avail_size = height - stepper_size * 2;
+          handle_size = increment * avail_size;
+          handle_size = CLAMP (handle_size, min_size, max_size);
+
+          handle_box.x1 = x;
+          handle_box.y1 = bw_box.y2 + position * (avail_size - handle_size);
+
+          handle_box.x2 = handle_box.x1 + width;
+          handle_box.y2 = handle_box.y1 + handle_size;
+        }
+      else
+        {
+          avail_size = width - stepper_size * 2;
+          handle_size = increment * avail_size;
+          handle_size = CLAMP (handle_size, min_size, max_size);
+
+          handle_box.x1 = bw_box.x2 + position * (avail_size - handle_size);
+          handle_box.y1 = y;
+
+          handle_box.x2 = handle_box.x1 + handle_size;
+          handle_box.y2 = handle_box.y1 + height;
+        }
+
+      /* snap to pixel */
+      handle_box.x1 = (int) handle_box.x1;
+      handle_box.y1 = (int) handle_box.y1;
+      handle_box.x2 = (int) handle_box.x2;
+      handle_box.y2 = (int) handle_box.y2;
+
+      clutter_actor_allocate (priv->handle,
+                              &handle_box,
+                              flags);
+    }
+}
+
+static void
+nbtk_scroll_bar_style_changed (NbtkWidget *widget)
+{
+  NbtkScrollBarPrivate *priv = NBTK_SCROLL_BAR (widget)->priv;
+
+  nbtk_stylable_changed ((NbtkStylable *) priv->bw_stepper);
+  nbtk_stylable_changed ((NbtkStylable *) priv->fw_stepper);
+  nbtk_stylable_changed ((NbtkStylable *) priv->trough);
+  nbtk_stylable_changed ((NbtkStylable *) priv->handle);
+
+}
+
+static void
+bar_reactive_notify_cb (GObject *gobject,
+                        GParamSpec *arg1,
+                        gpointer user_data)
+{
+  NbtkScrollBar *bar = NBTK_SCROLL_BAR (gobject);
+
+  clutter_actor_set_reactive (bar->priv->handle,
+                              clutter_actor_get_reactive (CLUTTER_ACTOR (bar)));
+}
+
+static GObject*
+nbtk_scroll_bar_constructor (GType                  type,
+                             guint                  n_properties,
+                             GObjectConstructParam *properties)
+{
+  GObjectClass         *gobject_class;
+  GObject              *obj;
+  NbtkScrollBar        *bar;
+  NbtkScrollBarPrivate *priv;
+
+  gobject_class = G_OBJECT_CLASS (nbtk_scroll_bar_parent_class);
+  obj = gobject_class->constructor (type, n_properties, properties);
+
+  bar  = NBTK_SCROLL_BAR (obj);
+  priv = NBTK_SCROLL_BAR_GET_PRIVATE (bar);
+
+  g_signal_connect (bar, "notify::reactive",
+                    G_CALLBACK (bar_reactive_notify_cb), NULL);
+
+  return obj;
+}
+
+static gboolean
+nbtk_scroll_bar_scroll_event (ClutterActor         *actor,
+                              ClutterScrollEvent   *event)
+{
+  NbtkScrollBarPrivate *priv = NBTK_SCROLL_BAR (actor)->priv;
+  gdouble lower, step, upper, value;
+
+  if (priv->adjustment)
+    {
+      g_object_get (priv->adjustment,
+                    "lower", &lower,
+                    "step-increment", &step,
+                    "upper", &upper,
+                    "value", &value,
+                    NULL);
+    }
+  else
+    {
+      return FALSE;
+    }
+
+  switch (event->direction)
+    {
+      case CLUTTER_SCROLL_UP:
+      case CLUTTER_SCROLL_LEFT:
+        if (value == lower)
+          return FALSE;
+        else
+          nbtk_adjustment_set_value (priv->adjustment, value - step);
+        break;
+      case CLUTTER_SCROLL_DOWN:
+      case CLUTTER_SCROLL_RIGHT:
+        if (value == upper)
+          return FALSE;
+        else
+          nbtk_adjustment_set_value (priv->adjustment, value + step);
+        break;
+    }
+
+  return TRUE;
+}
+
+static void
+nbtk_scroll_bar_class_init (NbtkScrollBarClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (NbtkScrollBarPrivate));
+
+  object_class->get_property = nbtk_scroll_bar_get_property;
+  object_class->set_property = nbtk_scroll_bar_set_property;
+  object_class->dispose      = nbtk_scroll_bar_dispose;
+  object_class->constructor  = nbtk_scroll_bar_constructor;
+
+  actor_class->allocate       = nbtk_scroll_bar_allocate;
+  actor_class->paint          = nbtk_scroll_bar_paint;
+  actor_class->pick           = nbtk_scroll_bar_pick;
+  actor_class->scroll_event   = nbtk_scroll_bar_scroll_event;
+  actor_class->map            = nbtk_scroll_bar_map;
+  actor_class->unmap          = nbtk_scroll_bar_unmap;
+
+  g_object_class_install_property
+           (object_class,
+            PROP_ADJUSTMENT,
+            g_param_spec_object ("adjustment",
+                                 "Adjustment",
+                                 "The adjustment",
+                                 NBTK_TYPE_ADJUSTMENT,
+                                 NBTK_PARAM_READWRITE));
+
+  pspec = g_param_spec_boolean ("vertical",
+                                "Vertical Orientation",
+                                "Vertical Orientation",
+                                FALSE,
+                                NBTK_PARAM_READWRITE);
+  g_object_class_install_property (object_class, PROP_VERTICAL, pspec);
+
+  signals[SCROLL_START] =
+    g_signal_new ("scroll-start",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (NbtkScrollBarClass, scroll_start),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
+  signals[SCROLL_STOP] =
+    g_signal_new ("scroll-stop",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (NbtkScrollBarClass, scroll_stop),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+nbtk_stylable_iface_init (NbtkStylableIface *iface)
+{
+  static gboolean is_initialized = FALSE;
+
+  if (!is_initialized)
+    {
+      GParamSpec *pspec;
+
+      is_initialized = TRUE;
+
+      pspec = g_param_spec_uint ("min-size",
+                                 "Minimum grabber size",
+                                 "Minimum size of the scroll grabber, in px",
+                                 0, G_MAXUINT, 32,
+                                 G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface,
+                                            NBTK_TYPE_SCROLL_BAR, pspec);
+
+      pspec = g_param_spec_uint ("max-size",
+                                 "Maximum grabber size",
+                                 "Maximum size of the scroll grabber, in px",
+                                 0, G_MAXINT16, G_MAXINT16,
+                                 G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface,
+                                            NBTK_TYPE_SCROLL_BAR, pspec);
+    }
+}
+
+static void
+move_slider (NbtkScrollBar *bar, gfloat x, gfloat y)
+{
+  NbtkScrollBarPrivate *priv = bar->priv;
+  gdouble position, lower, upper, page_size;
+  gfloat ux, uy, pos, size;
+
+  if (!priv->adjustment)
+    return;
+
+  if (!clutter_actor_transform_stage_point (priv->trough, x, y, &ux, &uy))
+    return;
+
+  if (priv->vertical)
+      size = clutter_actor_get_height (priv->trough)
+        - clutter_actor_get_height (priv->handle);
+  else
+      size = clutter_actor_get_width (priv->trough)
+        - clutter_actor_get_width (priv->handle);
+
+  if (size == 0)
+    return;
+
+  if (priv->vertical)
+    pos = uy - priv->y_origin;
+  else
+    pos = ux - priv->x_origin;
+  pos = CLAMP (pos, 0, size);
+
+  nbtk_adjustment_get_values (priv->adjustment,
+                              NULL,
+                              &lower,
+                              &upper,
+                              NULL,
+                              NULL,
+                              &page_size);
+
+  position = ((pos / size)
+           * (upper - lower - page_size))
+           + lower;
+
+  nbtk_adjustment_set_value (priv->adjustment, position);
+}
+
+static gboolean
+handle_capture_event_cb (ClutterActor       *trough,
+			 ClutterEvent       *event,
+			 NbtkScrollBar      *bar)
+{
+  if (clutter_event_type (event) == CLUTTER_MOTION)
+    {
+      move_slider (bar,
+                   ((ClutterMotionEvent*)event)->x,
+                   ((ClutterMotionEvent*)event)->y);
+    }
+  else if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE
+	   && ((ClutterButtonEvent*)event)->button == 1)
+    {
+      ClutterActor *stage, *target;
+
+      stage = clutter_actor_get_stage(bar->priv->trough);
+
+      if (bar->priv->capture_handler)
+	{
+	  g_signal_handler_disconnect (stage, bar->priv->capture_handler);
+	  bar->priv->capture_handler = 0;
+	}
+
+      clutter_set_motion_events_enabled (TRUE);
+      g_signal_emit (bar, signals[SCROLL_STOP], 0);
+
+      /* check if the mouse pointer has left the handle during the drag and
+       * remove the hover state if it has */
+      target = clutter_stage_get_actor_at_pos ((ClutterStage*) stage,
+                                               CLUTTER_PICK_REACTIVE,
+                                               ((ClutterButtonEvent*) event)->x,
+                                               ((ClutterButtonEvent*) event)->y);
+      if (target != bar->priv->handle)
+        {
+          nbtk_widget_set_style_pseudo_class ((NbtkWidget*) bar->priv->handle, NULL);
+        }
+
+
+    }
+
+  return TRUE;
+}
+
+static gboolean
+handle_button_press_event_cb (ClutterActor       *actor,
+                              ClutterButtonEvent *event,
+                              NbtkScrollBar      *bar)
+{
+  NbtkScrollBarPrivate *priv = bar->priv;
+
+  if (event->button != 1)
+    return FALSE;
+
+  if (!clutter_actor_transform_stage_point (priv->handle,
+                                            event->x,
+                                            event->y,
+                                            &priv->x_origin,
+                                            &priv->y_origin))
+    return FALSE;
+
+  /* Account for the scrollbar-trough-handle nesting. */
+  priv->x_origin += clutter_actor_get_x (priv->trough);
+  priv->y_origin += clutter_actor_get_y (priv->trough);
+
+  /* Turn off picking for motion events */
+  clutter_set_motion_events_enabled (FALSE);
+
+  priv->capture_handler = g_signal_connect_after (
+				  clutter_actor_get_stage (priv->trough),
+				  "captured-event",
+				  G_CALLBACK (handle_capture_event_cb),
+				  bar);
+  g_signal_emit (bar, signals[SCROLL_START], 0);
+
+  return TRUE;
+}
+
+static void
+animation_completed_cb (ClutterAnimation     *animation,
+                        NbtkScrollBarPrivate *priv)
+{
+  g_object_unref (priv->paging_animation);
+  priv->paging_animation = NULL;
+}
+
+static gboolean
+trough_paging_cb (NbtkScrollBar *self)
+{
+  gfloat handle_pos, event_pos, tx, ty;
+  gdouble value;
+  gdouble page_increment;
+  gboolean ret;
+
+  gulong mode;
+  ClutterAnimation *a;
+  GValue v = { 0, };
+  ClutterTimeline *t;
+
+  if (self->priv->paging_event_no == 0)
+    {
+      /* Scroll on after initial timeout. */
+      mode = CLUTTER_EASE_OUT_CUBIC;
+      ret = FALSE;
+      self->priv->paging_event_no = 1;
+      self->priv->paging_source_id = g_timeout_add (
+                                      PAGING_INITIAL_REPEAT_TIMEOUT,
+                                      (GSourceFunc) trough_paging_cb,
+                                      self);
+    }
+  else if (self->priv->paging_event_no == 1)
+    {
+      /* Scroll on after subsequent timeout. */
+      ret = FALSE;
+      mode = CLUTTER_EASE_IN_CUBIC;
+      self->priv->paging_event_no = 2;
+      self->priv->paging_source_id = g_timeout_add (
+                                      PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
+                                      (GSourceFunc) trough_paging_cb,
+                                      self);
+    }
+  else
+    {
+      /* Keep scrolling. */
+      ret = TRUE;
+      mode = CLUTTER_LINEAR;
+      self->priv->paging_event_no++;
+    }
+
+  /* Do the scrolling */
+  nbtk_adjustment_get_values (self->priv->adjustment,
+                              &value, NULL, NULL,
+                              NULL, &page_increment, NULL);
+
+  if (self->priv->vertical)
+    handle_pos = clutter_actor_get_y (self->priv->handle);
+  else
+    handle_pos = clutter_actor_get_x (self->priv->handle);
+
+  clutter_actor_transform_stage_point (CLUTTER_ACTOR (self->priv->trough),
+                                       self->priv->move_x,
+                                       self->priv->move_y,
+                                       &tx, &ty);
+
+  if (self->priv->vertical)
+    event_pos = ty;
+  else
+    event_pos = tx;
+
+  if (event_pos > handle_pos)
+    {
+      if (self->priv->paging_direction == NONE)
+        {
+          /* Remember direction. */
+          self->priv->paging_direction = DOWN;
+        }
+      if (self->priv->paging_direction == UP)
+        {
+          /* Scrolled far enough. */
+          return FALSE;
+        }
+      value += page_increment;
+    }
+  else
+    {
+      if (self->priv->paging_direction == NONE)
+        {
+          /* Remember direction. */
+          self->priv->paging_direction = UP;
+        }
+      if (self->priv->paging_direction == DOWN)
+        {
+          /* Scrolled far enough. */
+          return FALSE;
+        }
+      value -= page_increment;
+    }
+
+  if (self->priv->paging_animation)
+    {
+      clutter_animation_completed (self->priv->paging_animation);
+    }
+
+  /* FIXME: Creating a new animation for each scroll is probably not the best
+   * idea, but it's a lot less involved than extenind the current animation */
+  a = self->priv->paging_animation = g_object_new (CLUTTER_TYPE_ANIMATION,
+                                                   "object", self->priv->adjustment,
+                                                   "duration", PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
+                                                   "mode", mode,
+                                                   NULL);
+  g_value_init (&v, G_TYPE_DOUBLE);
+  g_value_set_double (&v, value);
+  clutter_animation_bind (self->priv->paging_animation, "value", &v);
+  t = clutter_animation_get_timeline (self->priv->paging_animation);
+  g_signal_connect (a, "completed", G_CALLBACK (animation_completed_cb),
+                    self->priv);
+  clutter_timeline_start (t);
+
+  return ret;
+}
+
+static gboolean
+trough_button_press_event_cb (ClutterActor       *actor,
+                              ClutterButtonEvent *event,
+                              NbtkScrollBar      *self)
+{
+  g_return_val_if_fail (self, FALSE);
+
+  if (event->button != 1)
+    return FALSE;
+
+  if (self->priv->adjustment == NULL)
+    return FALSE;
+
+  self->priv->move_x = event->x;
+  self->priv->move_y = event->y;
+  self->priv->paging_direction = NONE;
+  self->priv->paging_event_no = 0;
+  trough_paging_cb (self);
+
+  return TRUE;
+}
+
+static gboolean
+trough_button_release_event_cb (ClutterActor       *actor,
+                                ClutterButtonEvent *event,
+                                NbtkScrollBar      *self)
+{
+  if (event->button != 1)
+    return FALSE;
+
+  if (self->priv->paging_source_id)
+    {
+      g_source_remove (self->priv->paging_source_id);
+      self->priv->paging_source_id = 0;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+trough_leave_event_cb (ClutterActor   *actor,
+                       ClutterEvent   *event,
+                       NbtkScrollBar  *self)
+{
+  if (self->priv->paging_source_id)
+    {
+      g_source_remove (self->priv->paging_source_id);
+      self->priv->paging_source_id = 0;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+stepper_animation_completed_cb (ClutterAnimation *a,
+                                gpointer          data)
+{
+  g_object_unref (a);
+}
+
+static void
+stepper_move_on (NbtkScrollBarPrivate *priv,
+                 gint                  mode)
+{
+  ClutterAnimation *a;
+  ClutterTimeline *t;
+  GValue v = { 0, };
+  double value, inc;
+
+  a = g_object_new (CLUTTER_TYPE_ANIMATION,
+                    "object", priv->adjustment,
+                    "duration", PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
+                    "mode", mode,
+                    NULL);
+
+  g_signal_connect (a, "completed", G_CALLBACK (stepper_animation_completed_cb),
+                    NULL);
+
+  g_object_get (priv->adjustment,
+                "step-increment", &inc,
+                "value", &value,
+                NULL);
+
+  if (priv->stepper_forward)
+    value = value + inc;
+  else
+    value = value - inc;
+
+  g_value_init (&v, G_TYPE_DOUBLE);
+  g_value_set_double (&v, value);
+  clutter_animation_bind (a, "value", &v);
+
+  t = clutter_animation_get_timeline (a);
+  clutter_timeline_start (t);
+}
+
+static gboolean
+stepper_button_subsequent_timeout (NbtkScrollBarPrivate *priv)
+{
+  stepper_move_on (priv, CLUTTER_LINEAR);
+
+  return TRUE;
+}
+
+static gboolean
+stepper_button_repeat_timeout (NbtkScrollBarPrivate *priv)
+{
+  priv->stepper_source_id = 0;
+
+  stepper_move_on (priv, CLUTTER_EASE_IN_CUBIC);
+
+  priv->stepper_source_id = g_timeout_add (PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
+                                           (GSourceFunc)
+                                           stepper_button_subsequent_timeout,
+                                           priv);
+  return FALSE;
+}
+
+static gboolean
+stepper_button_press_event_cb (ClutterActor       *actor,
+                               ClutterButtonEvent *event,
+                               NbtkScrollBar      *bar)
+{
+  NbtkScrollBarPrivate *priv = bar->priv;
+
+  if (event->button != 1)
+    return FALSE;
+
+  if (bar->priv->adjustment == NULL)
+    return FALSE;
+
+  bar->priv->stepper_forward = (actor == priv->fw_stepper);
+
+  stepper_move_on (priv, CLUTTER_EASE_OUT_CUBIC);
+
+  priv->stepper_source_id = g_timeout_add (PAGING_INITIAL_REPEAT_TIMEOUT,
+                                           (GSourceFunc)
+                                           stepper_button_repeat_timeout,
+                                           priv);
+
+  return TRUE;
+}
+
+static gboolean
+stepper_button_release_cb (ClutterActor        *actor,
+                           ClutterButtonEvent  *event,
+                           NbtkScrollBar       *self)
+{
+  if (event->button != 1)
+    return FALSE;
+
+  g_source_remove (self->priv->stepper_source_id);
+
+  return FALSE;
+}
+
+static void
+nbtk_scroll_bar_notify_reactive (NbtkScrollBar *self)
+{
+  NbtkScrollBarPrivate *priv = self->priv;
+
+  gboolean reactive = CLUTTER_ACTOR_IS_REACTIVE (self);
+
+  clutter_actor_set_reactive (CLUTTER_ACTOR (priv->bw_stepper), reactive);
+  clutter_actor_set_reactive (CLUTTER_ACTOR (priv->fw_stepper), reactive);
+  clutter_actor_set_reactive (CLUTTER_ACTOR (priv->trough), reactive);
+  clutter_actor_set_reactive (CLUTTER_ACTOR (priv->handle), reactive);
+}
+
+static void
+nbtk_scroll_bar_init (NbtkScrollBar *self)
+{
+  self->priv = NBTK_SCROLL_BAR_GET_PRIVATE (self);
+
+  self->priv->bw_stepper = (ClutterActor *) nbtk_button_new ();
+  clutter_actor_set_name (CLUTTER_ACTOR (self->priv->bw_stepper),
+                          "backward-stepper");
+  clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->bw_stepper),
+                            CLUTTER_ACTOR (self));
+  g_signal_connect (self->priv->bw_stepper, "button-press-event",
+                    G_CALLBACK (stepper_button_press_event_cb), self);
+  g_signal_connect (self->priv->bw_stepper, "button-release-event",
+                    G_CALLBACK (stepper_button_release_cb), self);
+
+  self->priv->fw_stepper = (ClutterActor *) nbtk_button_new ();
+  clutter_actor_set_name (CLUTTER_ACTOR (self->priv->fw_stepper),
+                          "forward-stepper");
+  clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->fw_stepper),
+                            CLUTTER_ACTOR (self));
+  g_signal_connect (self->priv->fw_stepper, "button-press-event",
+                    G_CALLBACK (stepper_button_press_event_cb), self);
+  g_signal_connect (self->priv->fw_stepper, "button-release-event",
+                    G_CALLBACK (stepper_button_release_cb), self);
+
+  self->priv->trough = (ClutterActor *) nbtk_bin_new ();
+  clutter_actor_set_reactive ((ClutterActor *) self->priv->trough, TRUE);
+  clutter_actor_set_name (CLUTTER_ACTOR (self->priv->trough), "trough");
+  clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->trough),
+                            CLUTTER_ACTOR (self));
+  g_signal_connect (self->priv->trough, "button-press-event",
+                    G_CALLBACK (trough_button_press_event_cb), self);
+  g_signal_connect (self->priv->trough, "button-release-event",
+                    G_CALLBACK (trough_button_release_event_cb), self);
+  g_signal_connect (self->priv->trough, "leave-event",
+                    G_CALLBACK (trough_leave_event_cb), self);
+
+  self->priv->handle = (ClutterActor *) nbtk_button_new ();
+  clutter_actor_set_name (CLUTTER_ACTOR (self->priv->handle), "hhandle");
+  clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->handle),
+                            self->priv->trough);
+  g_signal_connect (self->priv->handle, "button-press-event",
+                    G_CALLBACK (handle_button_press_event_cb), self);
+
+  clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
+
+  g_signal_connect (self, "style-changed",
+                    G_CALLBACK (nbtk_scroll_bar_style_changed), NULL);
+  g_signal_connect (self, "notify::reactive",
+                    G_CALLBACK (nbtk_scroll_bar_notify_reactive), NULL);
+}
+
+NbtkWidget *
+nbtk_scroll_bar_new (NbtkAdjustment *adjustment)
+{
+  return g_object_new (NBTK_TYPE_SCROLL_BAR,
+                       "adjustment", adjustment,
+                       NULL);
+}
+
+void
+nbtk_scroll_bar_set_adjustment (NbtkScrollBar *bar,
+                                NbtkAdjustment *adjustment)
+{
+  NbtkScrollBarPrivate *priv;
+
+  g_return_if_fail (NBTK_IS_SCROLL_BAR (bar));
+
+  priv = bar->priv;
+  if (priv->adjustment)
+    {
+      g_signal_handlers_disconnect_by_func (priv->adjustment,
+                                            clutter_actor_queue_relayout,
+                                            bar);
+      g_signal_handlers_disconnect_by_func (priv->adjustment,
+                                            clutter_actor_queue_relayout,
+                                            bar);
+      g_object_unref (priv->adjustment);
+      priv->adjustment = NULL;
+    }
+
+  if (adjustment)
+    {
+      priv->adjustment = g_object_ref (adjustment);
+
+      g_signal_connect_swapped (priv->adjustment, "notify::value",
+                                G_CALLBACK (clutter_actor_queue_relayout),
+                                bar);
+      g_signal_connect_swapped (priv->adjustment, "changed",
+				G_CALLBACK (clutter_actor_queue_relayout),
+				bar);
+
+      clutter_actor_queue_relayout (CLUTTER_ACTOR (bar));
+    }
+}
+
+NbtkAdjustment *
+nbtk_scroll_bar_get_adjustment (NbtkScrollBar *bar)
+{
+  g_return_val_if_fail (NBTK_IS_SCROLL_BAR (bar), NULL);
+
+  return bar->priv->adjustment;
+}
diff --git a/src/nbtk/nbtk-scroll-bar.h b/src/nbtk/nbtk-scroll-bar.h
new file mode 100644
index 0000000..5dfd24d
--- /dev/null
+++ b/src/nbtk/nbtk-scroll-bar.h
@@ -0,0 +1,81 @@
+/*
+ * nbtk-scroll-bar.h: Scroll bar actor
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to Nbtk by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_SCROLL_BAR_H__
+#define __NBTK_SCROLL_BAR_H__
+
+#include <nbtk/nbtk-adjustment.h>
+#include <nbtk/nbtk-bin.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_SCROLL_BAR            (nbtk_scroll_bar_get_type())
+#define NBTK_SCROLL_BAR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_SCROLL_BAR, NbtkScrollBar))
+#define NBTK_IS_SCROLL_BAR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_SCROLL_BAR))
+#define NBTK_SCROLL_BAR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_SCROLL_BAR, NbtkScrollBarClass))
+#define NBTK_IS_SCROLL_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_SCROLL_BAR))
+#define NBTK_SCROLL_BAR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_SCROLL_BAR, NbtkScrollBarClass))
+
+typedef struct _NbtkScrollBar          NbtkScrollBar;
+typedef struct _NbtkScrollBarPrivate   NbtkScrollBarPrivate;
+typedef struct _NbtkScrollBarClass     NbtkScrollBarClass;
+
+/**
+ * NbtkScrollBar:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _NbtkScrollBar
+{
+  /*< private >*/
+  NbtkBin parent_instance;
+
+  NbtkScrollBarPrivate *priv;
+};
+
+struct _NbtkScrollBarClass
+{
+  NbtkBinClass parent_class;
+
+  /* signals */
+  void (*scroll_start) (NbtkScrollBar *bar);
+  void (*scroll_stop)  (NbtkScrollBar *bar);
+};
+
+GType nbtk_scroll_bar_get_type (void) G_GNUC_CONST;
+
+NbtkWidget *    nbtk_scroll_bar_new            (NbtkAdjustment *adjustment);
+void            nbtk_scroll_bar_set_adjustment (NbtkScrollBar  *bar,
+                                                NbtkAdjustment *adjustment);
+NbtkAdjustment *nbtk_scroll_bar_get_adjustment (NbtkScrollBar  *bar);
+
+G_END_DECLS
+
+#endif /* __NBTK_SCROLL_BAR_H__ */
diff --git a/src/nbtk/nbtk-scroll-view.c b/src/nbtk/nbtk-scroll-view.c
new file mode 100644
index 0000000..7ea0e66
--- /dev/null
+++ b/src/nbtk/nbtk-scroll-view.c
@@ -0,0 +1,836 @@
+/*
+ * nbtk-scroll-view.h: Container with scroll-bars
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to Nbtk by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#include "nbtk-scroll-view.h"
+#include "nbtk-marshal.h"
+#include "nbtk-scroll-bar.h"
+#include "nbtk-scrollable.h"
+#include "nbtk-stylable.h"
+#include <clutter/clutter.h>
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+static void nbtk_stylable_iface_init (NbtkStylableIface *iface);
+
+static ClutterContainerIface *nbtk_scroll_view_parent_iface = NULL;
+
+G_DEFINE_TYPE_WITH_CODE (NbtkScrollView, nbtk_scroll_view, NBTK_TYPE_BIN,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+                                                clutter_container_iface_init)
+                         G_IMPLEMENT_INTERFACE (NBTK_TYPE_STYLABLE,
+                                                nbtk_stylable_iface_init))
+
+#define SCROLL_VIEW_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                NBTK_TYPE_SCROLL_VIEW, \
+                                NbtkScrollViewPrivate))
+
+struct _NbtkScrollViewPrivate
+{
+  /* a pointer to the child; this is actually stored
+   * inside NbtkBin:child, but we keep it to avoid
+   * calling nbtk_bin_get_child() every time we need it
+   */
+  ClutterActor   *child;
+
+  ClutterActor   *hscroll;
+  ClutterActor   *vscroll;
+
+  gfloat          row_size;
+  gfloat          column_size;
+
+  gboolean        row_size_set : 1;
+  gboolean        column_size_set : 1;
+  guint           mouse_scroll : 1;
+};
+
+enum {
+  PROP_0,
+
+  PROP_HSCROLL,
+  PROP_VSCROLL,
+  PROP_MOUSE_SCROLL
+};
+
+static void
+nbtk_scroll_view_get_property (GObject *object, guint property_id,
+                                 GValue *value, GParamSpec *pspec)
+{
+  NbtkScrollViewPrivate *priv = ((NbtkScrollView *)object)->priv;
+
+  switch (property_id)
+    {
+    case PROP_HSCROLL :
+      g_value_set_object (value, priv->hscroll);
+      break;
+    case PROP_VSCROLL :
+      g_value_set_object (value, priv->vscroll);
+      break;
+    case PROP_MOUSE_SCROLL:
+      g_value_set_boolean (value, priv->mouse_scroll);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+nbtk_scroll_view_set_property (GObject *object, guint property_id,
+                                 const GValue *value, GParamSpec *pspec)
+{
+  switch (property_id)
+    {
+    case PROP_MOUSE_SCROLL:
+      nbtk_scroll_view_set_mouse_scrolling ((NbtkScrollView *) object,
+                                         g_value_get_boolean (value));
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+nbtk_scroll_view_dispose (GObject *object)
+{
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (object)->priv;
+
+  priv->child = NULL;
+
+  if (priv->vscroll)
+    {
+      clutter_actor_unparent (priv->vscroll);
+      priv->vscroll = NULL;
+    }
+
+  if (priv->hscroll)
+    {
+      clutter_actor_unparent (priv->hscroll);
+      priv->hscroll = NULL;
+    }
+
+  G_OBJECT_CLASS (nbtk_scroll_view_parent_class)->dispose (object);
+}
+
+static void
+nbtk_scroll_view_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (nbtk_scroll_view_parent_class)->finalize (object);
+}
+
+static void
+nbtk_scroll_view_paint (ClutterActor *actor)
+{
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (actor)->priv;
+
+  /* NbtkBin will paint the child */
+  CLUTTER_ACTOR_CLASS (nbtk_scroll_view_parent_class)->paint (actor);
+
+  /* paint our custom children */
+  if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
+    clutter_actor_paint (priv->hscroll);
+  if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
+    clutter_actor_paint (priv->vscroll);
+}
+
+static void
+nbtk_scroll_view_pick (ClutterActor *actor, const ClutterColor *color)
+{
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (actor)->priv;
+
+  /* Chain up so we get a bounding box pained (if we are reactive) */
+  CLUTTER_ACTOR_CLASS (nbtk_scroll_view_parent_class)->pick (actor, color);
+
+  /* paint our custom children */
+  if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
+    clutter_actor_paint (priv->hscroll);
+  if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
+    clutter_actor_paint (priv->vscroll);
+}
+
+static void
+nbtk_scroll_view_get_preferred_width (ClutterActor *actor,
+                                      gfloat        for_height,
+                                      gfloat       *min_width_p,
+                                      gfloat       *natural_width_p)
+{
+  NbtkPadding padding;
+  guint xthickness;
+
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (actor)->priv;
+
+  if (!priv->child)
+    return;
+
+  nbtk_widget_get_padding (NBTK_WIDGET (actor), &padding);
+  nbtk_stylable_get (NBTK_STYLABLE (actor),
+                     "scrollbar-width", &xthickness,
+                     NULL);
+
+  /* Our natural width is the natural width of the child */
+  clutter_actor_get_preferred_width (priv->child,
+                                     for_height,
+                                     NULL,
+                                     natural_width_p);
+
+  /* Add space for the scroll-bar if we can determine it will be necessary */
+  if ((for_height >= 0) && natural_width_p)
+    {
+      gfloat natural_height;
+
+      clutter_actor_get_preferred_height (priv->child, -1.0,
+                                          NULL,
+                                          &natural_height);
+      if (for_height < natural_height)
+        *natural_width_p += xthickness;
+    }
+
+  /* Add space for padding */
+  if (min_width_p)
+    *min_width_p = padding.left + padding.right;
+
+  if (natural_width_p)
+    *natural_width_p += padding.left + padding.right;
+}
+
+static void
+nbtk_scroll_view_get_preferred_height (ClutterActor *actor,
+                                       gfloat        for_width,
+                                       gfloat       *min_height_p,
+                                       gfloat       *natural_height_p)
+{
+  NbtkPadding padding;
+  guint ythickness;
+
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (actor)->priv;
+
+  if (!priv->child)
+    return;
+
+  nbtk_widget_get_padding (NBTK_WIDGET (actor), &padding);
+  nbtk_stylable_get (NBTK_STYLABLE (actor),
+                     "scrollbar-height", &ythickness,
+                     NULL);
+
+  /* Our natural height is the natural height of the child */
+  clutter_actor_get_preferred_height (priv->child,
+                                      for_width,
+                                      NULL,
+                                      natural_height_p);
+
+  /* Add space for the scroll-bar if we can determine it will be necessary */
+  if ((for_width >= 0) && natural_height_p)
+    {
+      gfloat natural_width;
+
+      clutter_actor_get_preferred_width (priv->child, -1.0,
+                                         NULL,
+                                         &natural_width);
+      if (for_width < natural_width)
+        *natural_height_p += ythickness;
+    }
+
+  /* Add space for padding */
+  if (min_height_p)
+    *min_height_p = padding.top + padding.bottom;
+
+  if (natural_height_p)
+    *natural_height_p += padding.top + padding.bottom;
+}
+
+static void
+nbtk_scroll_view_allocate (ClutterActor          *actor,
+                           const ClutterActorBox *box,
+                           ClutterAllocationFlags flags)
+{
+  NbtkPadding padding;
+  ClutterActorBox child_box;
+  ClutterActorClass *parent_parent_class;
+  gfloat avail_width, avail_height, sb_width, sb_height;
+
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (actor)->priv;
+
+  /* Chain up to the parent's parent class
+   *
+   * We do this because we do not want NbtkBin to allocate the child, as we
+   * give it a different allocation later, depending on whether the scrollbars
+   * are visible
+   */
+  parent_parent_class
+    = g_type_class_peek_parent (nbtk_scroll_view_parent_class);
+
+  CLUTTER_ACTOR_CLASS (parent_parent_class)->
+    allocate (actor, box, flags);
+
+
+  nbtk_widget_get_padding (NBTK_WIDGET (actor), &padding);
+
+  avail_width = (box->x2 - box->x1) - padding.left - padding.right;
+  avail_height = (box->y2 - box->y1) - padding.top - padding.bottom;
+
+  nbtk_stylable_get (NBTK_STYLABLE (actor),
+                     "scrollbar-width", &sb_width,
+                     "scrollbar-height", &sb_height,
+                     NULL);
+  sb_width = 28;
+  sb_height = 28;
+
+  if (!CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
+    sb_width = 0;
+
+  if (!CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
+    sb_height = 0;
+
+  /* Vertical scrollbar */
+  if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
+    {
+      child_box.x1 = avail_width - sb_width;
+      child_box.y1 = padding.top;
+      child_box.x2 = avail_width;
+      child_box.y2 = child_box.y1 + avail_height - sb_height;
+
+      clutter_actor_allocate (priv->vscroll, &child_box, flags);
+    }
+
+  /* Horizontal scrollbar */
+  if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
+    {
+      child_box.x1 = padding.left;
+      child_box.x2 = child_box.x1 + avail_width - sb_width;
+      child_box.y1 = avail_height - sb_height;
+      child_box.y2 = avail_height;
+
+      clutter_actor_allocate (priv->hscroll, &child_box, flags);
+    }
+
+
+  /* Child */
+  child_box.x1 = padding.left;
+  child_box.x2 = avail_width - sb_width;
+  child_box.y1 = padding.top;
+  child_box.y2 = avail_height - sb_height;
+
+  if (priv->child)
+      clutter_actor_allocate (priv->child, &child_box, flags);
+}
+
+static void
+nbtk_scroll_view_style_changed (NbtkWidget *widget)
+{
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (widget)->priv;
+
+  nbtk_stylable_changed ((NbtkStylable *) priv->hscroll);
+  nbtk_stylable_changed ((NbtkStylable *) priv->vscroll);
+}
+
+static gboolean
+nbtk_scroll_view_scroll_event (ClutterActor        *self,
+                               ClutterScrollEvent  *event)
+{
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (self)->priv;
+  gdouble lower, value, upper, step;
+  NbtkAdjustment *vadjustment, *hadjustment;
+
+  /* don't handle scroll events if requested not to */
+  if (!priv->mouse_scroll)
+    return FALSE;
+
+  hadjustment = nbtk_scroll_bar_get_adjustment (NBTK_SCROLL_BAR(priv->hscroll));
+  vadjustment = nbtk_scroll_bar_get_adjustment (NBTK_SCROLL_BAR(priv->vscroll));
+
+  switch (event->direction)
+    {
+      case CLUTTER_SCROLL_UP:
+      case CLUTTER_SCROLL_DOWN:
+        if (vadjustment)
+          g_object_get (vadjustment,
+                        "lower", &lower,
+                        "step-increment", &step,
+                        "value", &value,
+                        "upper", &upper,
+                        NULL);
+        else
+          return FALSE;
+        break;
+      case CLUTTER_SCROLL_LEFT:
+      case CLUTTER_SCROLL_RIGHT:
+        if (vadjustment)
+          g_object_get (hadjustment,
+                        "lower", &lower,
+                        "step-increment", &step,
+                        "value", &value,
+                        "upper", &upper,
+                        NULL);
+          else
+            return FALSE;
+        break;
+    }
+
+  switch (event->direction)
+    {
+      case CLUTTER_SCROLL_UP:
+        if (value == lower)
+          return FALSE;
+        else
+          nbtk_adjustment_set_value (vadjustment, value - step);
+        break;
+      case CLUTTER_SCROLL_DOWN:
+        if (value == upper)
+          return FALSE;
+        else
+          nbtk_adjustment_set_value (vadjustment, value + step);
+        break;
+      case CLUTTER_SCROLL_LEFT:
+        if (value == lower)
+          return FALSE;
+        else
+          nbtk_adjustment_set_value (hadjustment, value - step);
+        break;
+      case CLUTTER_SCROLL_RIGHT:
+        if (value == upper)
+          return FALSE;
+        else
+          nbtk_adjustment_set_value (hadjustment, value + step);
+        break;
+    }
+
+  return TRUE;
+}
+
+static void
+nbtk_scroll_view_class_init (NbtkScrollViewClass *klass)
+{
+  GParamSpec *pspec;
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (NbtkScrollViewPrivate));
+
+  object_class->get_property = nbtk_scroll_view_get_property;
+  object_class->set_property = nbtk_scroll_view_set_property;
+  object_class->dispose= nbtk_scroll_view_dispose;
+  object_class->finalize = nbtk_scroll_view_finalize;
+
+  actor_class->paint = nbtk_scroll_view_paint;
+  actor_class->pick = nbtk_scroll_view_pick;
+  actor_class->get_preferred_width = nbtk_scroll_view_get_preferred_width;
+  actor_class->get_preferred_height = nbtk_scroll_view_get_preferred_height;
+  actor_class->allocate = nbtk_scroll_view_allocate;
+  actor_class->scroll_event = nbtk_scroll_view_scroll_event;
+
+  g_object_class_install_property (object_class,
+                                   PROP_HSCROLL,
+                                   g_param_spec_object ("hscroll",
+                                                        "NbtkScrollBar",
+                                                        "Horizontal scroll indicator",
+                                                        NBTK_TYPE_SCROLL_BAR,
+                                                        G_PARAM_READABLE));
+
+  g_object_class_install_property (object_class,
+                                   PROP_VSCROLL,
+                                   g_param_spec_object ("vscroll",
+                                                       "NbtkScrollBar",
+                                                       "Vertical scroll indicator",
+                                                       NBTK_TYPE_SCROLL_BAR,
+                                                       G_PARAM_READABLE));
+
+  pspec = g_param_spec_boolean ("enable-mouse-scrolling",
+                                "Enable Mouse Scrolling",
+                                "Enable automatic mouse wheel scrolling",
+                                TRUE,
+                                G_PARAM_READWRITE);
+  g_object_class_install_property (object_class,
+                                   PROP_MOUSE_SCROLL,
+                                   pspec);
+
+}
+
+static void
+nbtk_stylable_iface_init (NbtkStylableIface *iface)
+{
+  static gboolean is_initialized = FALSE;
+
+  if (!is_initialized)
+    {
+      GParamSpec *pspec;
+
+      is_initialized = TRUE;
+
+      pspec = g_param_spec_uint ("scrollbar-width",
+                                  "Vertical scroll-bar thickness",
+                                  "Thickness of vertical scrollbar, in px",
+                                  0, G_MAXUINT, 24,
+                                  G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface, NBTK_TYPE_SCROLL_VIEW, pspec);
+
+      pspec = g_param_spec_uint ("scrollbar-height",
+                                  "Horizontal scroll-bar thickness",
+                                  "Thickness of horizontal scrollbar, in px",
+                                  0, G_MAXUINT, 24,
+                                  G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface, NBTK_TYPE_SCROLL_VIEW, pspec);
+    }
+}
+
+static void
+child_adjustment_changed_cb (NbtkAdjustment *adjustment,
+                             ClutterActor   *bar)
+{
+  NbtkScrollView *scroll;
+  gdouble lower, upper, page_size;
+
+  scroll = NBTK_SCROLL_VIEW (clutter_actor_get_parent (bar));
+
+  /* Determine if this scroll-bar should be visible */
+  nbtk_adjustment_get_values (adjustment, NULL,
+                              &lower, &upper,
+                              NULL, NULL,
+                              &page_size);
+
+  if ((upper - lower) > page_size)
+    clutter_actor_show (bar);
+  else
+    clutter_actor_hide (bar);
+
+  /* Request a resize */
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (scroll));
+}
+
+static void
+child_hadjustment_notify_cb (GObject *gobject,
+                             GParamSpec *arg1,
+                             gpointer user_data)
+{
+  NbtkAdjustment *hadjust;
+
+  ClutterActor *actor = CLUTTER_ACTOR (gobject);
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (user_data)->priv;
+
+  hadjust = nbtk_scroll_bar_get_adjustment (NBTK_SCROLL_BAR(priv->hscroll));
+  if (hadjust)
+    g_signal_handlers_disconnect_by_func (hadjust,
+                                          child_adjustment_changed_cb,
+                                          priv->hscroll);
+
+  nbtk_scrollable_get_adjustments (NBTK_SCROLLABLE (actor), &hadjust, NULL);
+  if (hadjust)
+    {
+      /* Force scroll step if neede. */
+      if (priv->column_size_set)
+        {
+          g_object_set (hadjust,
+                        "step-increment", priv->column_size,
+                        NULL);
+        }
+
+      nbtk_scroll_bar_set_adjustment (NBTK_SCROLL_BAR(priv->hscroll), hadjust);
+      g_signal_connect (hadjust, "changed", G_CALLBACK (
+                        child_adjustment_changed_cb), priv->hscroll);
+      child_adjustment_changed_cb (hadjust, priv->hscroll);
+    }
+}
+
+static void
+child_vadjustment_notify_cb (GObject *gobject,
+                             GParamSpec *arg1,
+                             gpointer user_data)
+{
+  NbtkAdjustment *vadjust;
+
+  ClutterActor *actor = CLUTTER_ACTOR (gobject);
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (user_data)->priv;
+
+  vadjust = nbtk_scroll_bar_get_adjustment (NBTK_SCROLL_BAR(priv->vscroll));
+  if (vadjust)
+    g_signal_handlers_disconnect_by_func (vadjust,
+                                          child_adjustment_changed_cb,
+                                          priv->vscroll);
+
+  nbtk_scrollable_get_adjustments (NBTK_SCROLLABLE(actor), NULL, &vadjust);
+  if (vadjust)
+    {
+      /* Force scroll step if neede. */
+      if (priv->row_size_set)
+        {
+          g_object_set (vadjust,
+                        "step-increment", priv->row_size,
+                        NULL);
+        }
+
+      nbtk_scroll_bar_set_adjustment (NBTK_SCROLL_BAR(priv->vscroll), vadjust);
+      g_signal_connect (vadjust, "changed", G_CALLBACK (
+                        child_adjustment_changed_cb), priv->vscroll);
+      child_adjustment_changed_cb (vadjust, priv->vscroll);
+    }
+}
+
+static void
+nbtk_scroll_view_init (NbtkScrollView *self)
+{
+  NbtkScrollViewPrivate *priv = self->priv = SCROLL_VIEW_PRIVATE (self);
+
+  priv->hscroll = CLUTTER_ACTOR (nbtk_scroll_bar_new (NULL));
+  priv->vscroll = g_object_new (NBTK_TYPE_SCROLL_BAR, "vertical", TRUE, NULL);
+
+  clutter_actor_set_parent (priv->hscroll, CLUTTER_ACTOR (self));
+  clutter_actor_set_parent (priv->vscroll, CLUTTER_ACTOR (self));
+
+  /* mouse scroll is enabled by default, so we also need to be reactive */
+  priv->mouse_scroll = TRUE;
+  g_object_set (G_OBJECT (self), "reactive", TRUE, "clip-to-allocation", TRUE,
+                NULL);
+
+  g_signal_connect (self, "style-changed",
+                    G_CALLBACK (nbtk_scroll_view_style_changed), NULL);
+}
+
+static void
+nbtk_scroll_view_add (ClutterContainer *container,
+                      ClutterActor     *actor)
+{
+  NbtkScrollView *self = NBTK_SCROLL_VIEW (container);
+  NbtkScrollViewPrivate *priv = self->priv;
+
+  if (NBTK_IS_SCROLLABLE (actor))
+    {
+      priv->child = actor;
+
+      /* chain up to NbtkBin::add() */
+      nbtk_scroll_view_parent_iface->add (container, actor);
+
+      /* Get adjustments for scroll-bars */
+      g_signal_connect (actor, "notify::hadjustment",
+                        G_CALLBACK (child_hadjustment_notify_cb),
+                        container);
+      g_signal_connect (actor, "notify::vadjustment",
+                        G_CALLBACK (child_vadjustment_notify_cb),
+                        container);
+      child_hadjustment_notify_cb (G_OBJECT (actor), NULL, container);
+      child_vadjustment_notify_cb (G_OBJECT (actor), NULL, container);
+    }
+  else
+    {
+      g_warning ("Attempting to add an actor of type %s to "
+                 "a NbtkScrollView, but the actor does "
+                 "not implement NbtkScrollable.",
+                 g_type_name (G_OBJECT_TYPE (actor)));
+    }
+}
+
+static void
+nbtk_scroll_view_remove (ClutterContainer *container,
+                         ClutterActor     *actor)
+{
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (container)->priv;
+
+  if (actor == priv->child)
+    {
+      g_object_ref (priv->child);
+
+      /* chain up to NbtkBin::remove() */
+      nbtk_scroll_view_parent_iface->remove (container, actor);
+
+      g_signal_handlers_disconnect_by_func (priv->child,
+                                            child_hadjustment_notify_cb,
+                                            container);
+      g_signal_handlers_disconnect_by_func (priv->child,
+                                            child_vadjustment_notify_cb,
+                                            container);
+      nbtk_scrollable_set_adjustments ((NbtkScrollable*) priv->child, NULL, NULL);
+
+      g_object_unref (priv->child);
+      priv->child = NULL;
+    }
+}
+
+static void
+nbtk_scroll_view_foreach_with_internals (ClutterContainer *container,
+                                         ClutterCallback   callback,
+                                         gpointer          user_data)
+{
+  NbtkScrollViewPrivate *priv = NBTK_SCROLL_VIEW (container)->priv;
+
+  if (priv->child != NULL)
+    callback (priv->child, user_data);
+
+  if (priv->hscroll != NULL)
+    callback (priv->hscroll, user_data);
+
+  if (priv->vscroll != NULL)
+    callback (priv->vscroll, user_data);
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+  /* store a pointer to the NbtkBin implementation of
+   * ClutterContainer so that we can chain up when
+   * overriding the methods
+   */
+  nbtk_scroll_view_parent_iface = g_type_interface_peek_parent (iface);
+
+  iface->add = nbtk_scroll_view_add;
+  iface->remove = nbtk_scroll_view_remove;
+  iface->foreach_with_internals = nbtk_scroll_view_foreach_with_internals;
+}
+
+NbtkWidget *
+nbtk_scroll_view_new (void)
+{
+  return g_object_new (NBTK_TYPE_SCROLL_VIEW, NULL);
+}
+
+ClutterActor *
+nbtk_scroll_view_get_hscroll_bar (NbtkScrollView *scroll)
+{
+  g_return_val_if_fail (NBTK_IS_SCROLL_VIEW (scroll), NULL);
+
+  return scroll->priv->hscroll;
+}
+
+ClutterActor *
+nbtk_scroll_view_get_vscroll_bar (NbtkScrollView *scroll)
+{
+  g_return_val_if_fail (NBTK_IS_SCROLL_VIEW (scroll), NULL);
+
+  return scroll->priv->vscroll;
+}
+
+gfloat
+nbtk_scroll_view_get_column_size (NbtkScrollView *scroll)
+{
+  NbtkAdjustment  *adjustment;
+  gdouble          column_size;
+
+  g_return_val_if_fail (scroll, 0);
+
+  adjustment = nbtk_scroll_bar_get_adjustment (
+                NBTK_SCROLL_BAR (scroll->priv->hscroll));
+  g_object_get (adjustment,
+                "step-increment", &column_size,
+                NULL);
+
+  return column_size;
+}
+
+void
+nbtk_scroll_view_set_column_size (NbtkScrollView *scroll,
+                                  gfloat          column_size)
+{
+  NbtkAdjustment  *adjustment;
+
+  g_return_if_fail (scroll);
+
+  if (column_size < 0)
+    {
+      scroll->priv->column_size_set = FALSE;
+      scroll->priv->column_size = -1;
+    }
+  else
+    {
+      scroll->priv->column_size_set = TRUE;
+      scroll->priv->column_size = column_size;
+
+      adjustment = nbtk_scroll_bar_get_adjustment (
+                    NBTK_SCROLL_BAR (scroll->priv->hscroll));
+
+      if (adjustment)
+        g_object_set (adjustment,
+                      "step-increment", (gdouble) scroll->priv->column_size,
+                      NULL);
+     }
+}
+
+gfloat
+nbtk_scroll_view_get_row_size (NbtkScrollView *scroll)
+{
+  NbtkAdjustment  *adjustment;
+  gdouble row_size;
+
+  g_return_val_if_fail (scroll, 0);
+
+  adjustment = nbtk_scroll_bar_get_adjustment (
+                NBTK_SCROLL_BAR (scroll->priv->vscroll));
+  g_object_get (adjustment,
+                "step-increment", &row_size,
+                NULL);
+
+  return row_size;
+}
+
+void
+nbtk_scroll_view_set_row_size (NbtkScrollView *scroll,
+                               gfloat          row_size)
+{
+  NbtkAdjustment  *adjustment;
+
+  g_return_if_fail (scroll);
+
+  if (row_size < 0)
+    {
+      scroll->priv->row_size_set = FALSE;
+      scroll->priv->row_size = -1;
+    }
+  else
+    {
+      scroll->priv->row_size_set = TRUE;
+      scroll->priv->row_size = row_size;
+
+      adjustment = nbtk_scroll_bar_get_adjustment (
+                    NBTK_SCROLL_BAR (scroll->priv->vscroll));
+
+      if (adjustment)
+        g_object_set (adjustment,
+                      "step-increment", (gdouble) scroll->priv->row_size,
+                      NULL);
+    }
+}
+
+void
+nbtk_scroll_view_set_mouse_scrolling (NbtkScrollView *scroll,
+                                      gboolean        enabled)
+{
+  NbtkScrollViewPrivate *priv;
+
+  g_return_if_fail (NBTK_IS_SCROLL_VIEW (scroll));
+
+  priv = NBTK_SCROLL_VIEW (scroll)->priv;
+
+  if (priv->mouse_scroll != enabled)
+    {
+      priv->mouse_scroll = enabled;
+
+      /* make sure we can receive mouse wheel events */
+      if (enabled)
+        clutter_actor_set_reactive ((ClutterActor *) scroll, TRUE);
+    }
+}
+
+gboolean
+nbtk_scroll_view_get_mouse_scrolling (NbtkScrollView *scroll)
+{
+  NbtkScrollViewPrivate *priv;
+
+  g_return_val_if_fail (NBTK_IS_SCROLL_VIEW (scroll), FALSE);
+
+  priv = NBTK_SCROLL_VIEW (scroll)->priv;
+
+  return priv->mouse_scroll;
+}
diff --git a/src/nbtk/nbtk-scroll-view.h b/src/nbtk/nbtk-scroll-view.h
new file mode 100644
index 0000000..0c07398
--- /dev/null
+++ b/src/nbtk/nbtk-scroll-view.h
@@ -0,0 +1,88 @@
+/*
+ * nbtk-scroll-view.h: Container with scroll-bars
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to Nbtk by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_SCROLL_VIEW_H__
+#define __NBTK_SCROLL_VIEW_H__
+
+#include <nbtk/nbtk-bin.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_SCROLL_VIEW            (nbtk_scroll_view_get_type())
+#define NBTK_SCROLL_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_SCROLL_VIEW, NbtkScrollView))
+#define NBTK_IS_SCROLL_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_SCROLL_VIEW))
+#define NBTK_SCROLL_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_SCROLL_VIEW, NbtkScrollViewClass))
+#define NBTK_IS_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_SCROLL_VIEW))
+#define NBTK_SCROLL_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_SCROLL_VIEW, NbtkScrollViewClass))
+
+typedef struct _NbtkScrollView          NbtkScrollView;
+typedef struct _NbtkScrollViewPrivate   NbtkScrollViewPrivate;
+typedef struct _NbtkScrollViewClass     NbtkScrollViewClass;
+
+/**
+ * NbtkScrollView:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _NbtkScrollView
+{
+  /*< private >*/
+  NbtkBin parent_instance;
+
+  NbtkScrollViewPrivate *priv;
+};
+
+struct _NbtkScrollViewClass
+{
+  NbtkBinClass parent_class;
+};
+
+GType nbtk_scroll_view_get_type (void) G_GNUC_CONST;
+
+NbtkWidget *  nbtk_scroll_view_new             (void);
+
+ClutterActor *  nbtk_scroll_view_get_hscroll_bar (NbtkScrollView *scroll);
+ClutterActor *  nbtk_scroll_view_get_vscroll_bar (NbtkScrollView *scroll);
+ClutterActor *  nbtk_scroll_view_get_child       (NbtkScrollView *scroll);
+
+gfloat     nbtk_scroll_view_get_column_size (NbtkScrollView *scroll);
+void            nbtk_scroll_view_set_column_size (NbtkScrollView *scroll,
+                                                  gfloat          column_size);
+
+gfloat     nbtk_scroll_view_get_row_size    (NbtkScrollView *scroll);
+void            nbtk_scroll_view_set_row_size    (NbtkScrollView *scroll,
+                                                  gfloat          row_size);
+
+void nbtk_scroll_view_set_mouse_scrolling (NbtkScrollView *scroll, gboolean enabled);
+gboolean nbtk_scroll_view_get_mouse_scrolling (NbtkScrollView *scroll);
+
+G_END_DECLS
+
+#endif /* __NBTK_SCROLL_VIEW_H__ */
diff --git a/src/nbtk/nbtk-scrollable.c b/src/nbtk/nbtk-scrollable.c
new file mode 100644
index 0000000..aed1e9e
--- /dev/null
+++ b/src/nbtk/nbtk-scrollable.c
@@ -0,0 +1,88 @@
+/*
+ * nbtk-scrollable.c: Scrollable interface
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to Nbtk by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#include "nbtk-scrollable.h"
+
+static void
+nbtk_scrollable_base_init (gpointer g_iface)
+{
+  static gboolean initialized = FALSE;
+
+  if (!initialized)
+    {
+      g_object_interface_install_property (g_iface,
+                                   g_param_spec_object ("hadjustment",
+                                                        "NbtkAdjustment",
+                                                        "Horizontal adjustment",
+                                                        NBTK_TYPE_ADJUSTMENT,
+                                                        G_PARAM_READWRITE));
+
+      g_object_interface_install_property (g_iface,
+                                   g_param_spec_object ("vadjustment",
+                                                        "NbtkAdjustment",
+                                                        "Vertical adjustment",
+                                                        NBTK_TYPE_ADJUSTMENT,
+                                                        G_PARAM_READWRITE));
+
+      initialized = TRUE;
+    }
+}
+
+GType
+nbtk_scrollable_get_type (void)
+{
+  static GType type = 0;
+  if (type == 0)
+    {
+      static const GTypeInfo info =
+        {
+          sizeof (NbtkScrollableInterface),
+          nbtk_scrollable_base_init,        /* base_init */
+          NULL,
+        };
+      type = g_type_register_static (G_TYPE_INTERFACE,
+                                     "NbtkScrollable", &info, 0);
+    }
+  return type;
+}
+
+void
+nbtk_scrollable_set_adjustments (NbtkScrollable *scrollable,
+                                 NbtkAdjustment *hadjustment,
+                                 NbtkAdjustment *vadjustment)
+{
+  NBTK_SCROLLABLE_GET_INTERFACE (scrollable)->set_adjustments (scrollable,
+                                                               hadjustment,
+                                                               vadjustment);
+}
+
+void
+nbtk_scrollable_get_adjustments (NbtkScrollable *scrollable,
+                                 NbtkAdjustment **hadjustment,
+                                 NbtkAdjustment **vadjustment)
+{
+  NBTK_SCROLLABLE_GET_INTERFACE (scrollable)->get_adjustments (scrollable,
+                                                               hadjustment,
+                                                               vadjustment);
+}
diff --git a/src/nbtk/nbtk-scrollable.h b/src/nbtk/nbtk-scrollable.h
new file mode 100644
index 0000000..d8a6234
--- /dev/null
+++ b/src/nbtk/nbtk-scrollable.h
@@ -0,0 +1,69 @@
+/*
+ * nbtk-scrollable.h: Scrollable interface
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to Nbtk by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_SCROLLABLE_H__
+#define __NBTK_SCROLLABLE_H__
+
+#include <glib-object.h>
+#include <nbtk/nbtk-adjustment.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_SCROLLABLE                (nbtk_scrollable_get_type ())
+#define NBTK_SCROLLABLE(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_SCROLLABLE, NbtkScrollable))
+#define NBTK_IS_SCROLLABLE(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_SCROLLABLE))
+#define NBTK_SCROLLABLE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), NBTK_TYPE_SCROLLABLE, NbtkScrollableInterface))
+
+typedef struct _NbtkScrollable NbtkScrollable; /* Dummy object */
+typedef struct _NbtkScrollableInterface NbtkScrollableInterface;
+
+struct _NbtkScrollableInterface
+{
+  GTypeInterface parent;
+
+  void (* set_adjustments) (NbtkScrollable  *scrollable,
+                            NbtkAdjustment  *hadjustment,
+                            NbtkAdjustment  *vadjustment);
+  void (* get_adjustments) (NbtkScrollable  *scrollable,
+                            NbtkAdjustment **hadjustment,
+                            NbtkAdjustment **vadjustment);
+};
+
+GType nbtk_scrollable_get_type (void) G_GNUC_CONST;
+
+void nbtk_scrollable_set_adjustments (NbtkScrollable  *scrollable,
+                                      NbtkAdjustment  *hadjustment,
+                                      NbtkAdjustment  *vadjustment);
+void nbtk_scrollable_get_adjustments (NbtkScrollable  *scrollable,
+                                      NbtkAdjustment **hadjustment,
+                                      NbtkAdjustment **vadjustment);
+
+G_END_DECLS
+
+#endif /* __NBTK_SCROLLABLE_H__ */
diff --git a/src/nbtk/nbtk-viewport.c b/src/nbtk/nbtk-viewport.c
new file mode 100644
index 0000000..c7de348
--- /dev/null
+++ b/src/nbtk/nbtk-viewport.c
@@ -0,0 +1,668 @@
+/*
+ * nbtk-viewport.c: Viewport actor
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to Nbtk by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <clutter/clutter.h>
+
+#include "nbtk-viewport.h"
+#include "nbtk-adjustment.h"
+#include "nbtk-scrollable.h"
+#include "nbtk-private.h"
+#include "nbtk-bin.h"
+
+static void scrollable_interface_init (NbtkScrollableInterface *iface);
+
+static void scrollable_set_adjustments (NbtkScrollable *scrollable,
+                                        NbtkAdjustment *hadjustment,
+                                        NbtkAdjustment *vadjustment);
+
+static void scrollable_get_adjustments (NbtkScrollable  *scrollable,
+                                        NbtkAdjustment **hadjustment,
+                                        NbtkAdjustment **vadjustment);
+
+G_DEFINE_TYPE_WITH_CODE (NbtkViewport, nbtk_viewport, NBTK_TYPE_BIN,
+                         G_IMPLEMENT_INTERFACE (NBTK_TYPE_SCROLLABLE,
+                                                scrollable_interface_init))
+
+#define VIEWPORT_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), NBTK_TYPE_VIEWPORT, \
+  NbtkViewportPrivate))
+
+struct _NbtkViewportPrivate
+{
+  gfloat x;
+  gfloat y;
+  gfloat z;
+
+  NbtkAdjustment *hadjustment;
+  NbtkAdjustment *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
+nbtk_viewport_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  NbtkAdjustment *adjustment;
+
+  NbtkViewportPrivate *priv = NBTK_VIEWPORT (object)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_X_ORIGIN:
+      g_value_set_int (value, (int) priv->x);
+      break;
+
+    case PROP_Y_ORIGIN:
+      g_value_set_int (value, (int) priv->y);
+      break;
+
+    case PROP_Z_ORIGIN:
+      g_value_set_int (value, (int) priv->z);
+      break;
+
+    case PROP_HADJUST :
+      scrollable_get_adjustments (NBTK_SCROLLABLE (object), &adjustment, NULL);
+      g_value_set_object (value, adjustment);
+      break;
+
+    case PROP_VADJUST :
+      scrollable_get_adjustments (NBTK_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
+nbtk_viewport_set_property (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+  NbtkViewport *viewport = NBTK_VIEWPORT (object);
+  NbtkViewportPrivate *priv = viewport->priv;
+
+  switch (prop_id)
+    {
+    case PROP_X_ORIGIN:
+      nbtk_viewport_set_originu (viewport,
+                                 g_value_get_int (value),
+                                 priv->y,
+                                 priv->z);
+      break;
+
+    case PROP_Y_ORIGIN:
+      nbtk_viewport_set_originu (viewport,
+                                 priv->x,
+                                 g_value_get_int (value),
+                                 priv->z);
+      break;
+
+    case PROP_Z_ORIGIN:
+      nbtk_viewport_set_originu (viewport,
+                                 priv->x,
+                                 priv->y,
+                                 g_value_get_int (value));
+      break;
+
+    case PROP_HADJUST :
+      scrollable_set_adjustments (NBTK_SCROLLABLE (object),
+                                  g_value_get_object (value),
+                                  priv->vadjustment);
+      break;
+
+    case PROP_VADJUST :
+      scrollable_set_adjustments (NBTK_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;
+    }
+}
+
+static void
+nbtk_viewport_dispose (GObject *gobject)
+{
+  NbtkViewportPrivate *priv = NBTK_VIEWPORT (gobject)->priv;
+
+  if (priv->hadjustment)
+    {
+      g_object_unref (priv->hadjustment);
+      priv->hadjustment = NULL;
+    }
+
+  if (priv->vadjustment)
+    {
+      g_object_unref (priv->vadjustment);
+      priv->vadjustment = NULL;
+    }
+
+  G_OBJECT_CLASS (nbtk_viewport_parent_class)->dispose (gobject);
+}
+
+static ClutterActor *
+get_child_and_natural_size (NbtkViewport  *self,
+                            gfloat   *natural_width,
+                            gfloat   *natural_height)
+{
+  /* NbtkBin is a single-child container,
+    * let it grow as big as it wants. */
+  ClutterActor        *child;
+  ClutterRequestMode   mode;
+
+  child = nbtk_bin_get_child (NBTK_BIN (self));
+  if (child)
+    {
+
+      g_object_get (G_OBJECT (child), "request-mode", &mode, NULL);
+      if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
+        {
+          clutter_actor_get_preferred_width (child, -1, NULL,
+                                             natural_width);
+          clutter_actor_get_preferred_height (child, *natural_width, NULL,
+                                              natural_height);
+        }
+      else
+        {
+          clutter_actor_get_preferred_height (child, -1, NULL,
+                                              natural_height);
+          clutter_actor_get_preferred_width (child, *natural_height, NULL,
+                                             natural_width);
+        }
+
+      return child;
+    }
+
+    return NULL;
+}
+
+static void
+nbtk_viewport_paint (ClutterActor *self)
+{
+  NbtkViewportPrivate *priv = NBTK_VIEWPORT (self)->priv;
+
+  cogl_push_matrix ();
+
+  cogl_translate ((int) priv->x * -1,
+                  (int) priv->y * -1,
+                  (int) priv->z * -1);
+
+  CLUTTER_ACTOR_CLASS (nbtk_viewport_parent_class)->paint (self);
+
+  cogl_pop_matrix ();
+}
+
+static void
+nbtk_viewport_pick (ClutterActor       *self,
+                    const ClutterColor *color)
+{
+  nbtk_viewport_paint (self);
+}
+
+static void
+nbtk_viewport_allocate (ClutterActor          *self,
+                        const ClutterActorBox *box,
+                        ClutterAllocationFlags flags)
+{
+  NbtkViewportPrivate   *priv = NBTK_VIEWPORT (self)->priv;
+  ClutterActor          *child;
+  ClutterActorBox natural_box;
+  gfloat     natural_width, natural_height;
+  gfloat     available_width, available_height;
+
+  /* Chain up. */
+  CLUTTER_ACTOR_CLASS (nbtk_viewport_parent_class)->
+    allocate (self, box, flags);
+
+  available_width = box->x2 - box->x1;
+  available_height = box->y2 - box->y1;
+
+  natural_box.x1 = 0;
+  natural_box.y1 = 0;
+
+  if (NULL != (child = get_child_and_natural_size (NBTK_VIEWPORT (self),
+                                                   &natural_width,
+                                                   &natural_height)))
+    {
+      natural_box.x2 = natural_width;
+      natural_box.y2 = natural_height;
+      clutter_actor_allocate (child, &natural_box, flags);
+    }
+  else
+    {
+      natural_box.x2 = available_width;
+      natural_box.y2 = available_height;
+    }
+
+  /* Refresh adjustments */
+  if (priv->sync_adjustments)
+    {
+      gdouble prev_value;
+
+      if (priv->hadjustment)
+        {
+          g_object_set (G_OBJECT (priv->hadjustment),
+                       "lower", 0.0,
+                       "page-size", available_width,
+                       "upper", natural_width,
+                       NULL);
+
+          /* Make sure value is clamped */
+          prev_value = nbtk_adjustment_get_value (priv->hadjustment);
+          nbtk_adjustment_set_value (priv->hadjustment, prev_value);
+        }
+
+      if (priv->vadjustment)
+        {
+          g_object_set (G_OBJECT (priv->vadjustment),
+                       "lower", 0.0,
+                       "page-size", available_height,
+                       "upper", natural_height,
+                       NULL);
+
+          prev_value = nbtk_adjustment_get_value (priv->vadjustment);
+          nbtk_adjustment_set_value (priv->vadjustment, prev_value);
+        }
+    }
+}
+
+static void
+nbtk_viewport_class_init (NbtkViewportClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (NbtkViewportPrivate));
+
+  gobject_class->get_property = nbtk_viewport_get_property;
+  gobject_class->set_property = nbtk_viewport_set_property;
+  gobject_class->dispose = nbtk_viewport_dispose;
+
+  actor_class->paint = nbtk_viewport_paint;
+  actor_class->pick = nbtk_viewport_pick;
+  actor_class->allocate = nbtk_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 (NbtkAdjustment *adjustment,
+                             GParamSpec     *pspec,
+                             NbtkViewport   *viewport)
+{
+  NbtkViewportPrivate *priv = viewport->priv;
+  gdouble value;
+
+  value = nbtk_adjustment_get_value (adjustment);
+
+  nbtk_viewport_set_originu (viewport,
+                             (float) (value),
+                             priv->y,
+                             priv->z);
+}
+
+static void
+vadjustment_value_notify_cb (NbtkAdjustment *adjustment, GParamSpec *arg1,
+                             NbtkViewport *viewport)
+{
+  NbtkViewportPrivate *priv = viewport->priv;
+  gdouble value;
+
+  value = nbtk_adjustment_get_value (adjustment);
+
+  nbtk_viewport_set_originu (viewport,
+                             priv->x,
+                             (float) (value),
+                             priv->z);
+}
+
+static void
+scrollable_set_adjustments (NbtkScrollable *scrollable,
+                            NbtkAdjustment *hadjustment,
+                            NbtkAdjustment *vadjustment)
+{
+  NbtkViewportPrivate *priv = NBTK_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 (NbtkScrollable *scrollable,
+                            NbtkAdjustment **hadjustment,
+                            NbtkAdjustment **vadjustment)
+{
+  NbtkViewportPrivate *priv;
+  ClutterActor *actor, *stage;
+
+  g_return_if_fail (NBTK_IS_VIEWPORT (scrollable));
+
+  priv = ((NbtkViewport *) scrollable)->priv;
+
+  actor = CLUTTER_ACTOR (scrollable);
+  stage = clutter_actor_get_stage (actor);
+  if (G_UNLIKELY (stage == NULL))
+    stage = clutter_stage_get_default ();
+
+  if (hadjustment)
+    {
+      if (priv->hadjustment)
+        *hadjustment = priv->hadjustment;
+      else
+        {
+          NbtkAdjustment *adjustment;
+          gdouble width, stage_width, increment;
+
+          width = clutter_actor_get_width (actor);
+          stage_width = clutter_actor_get_width (stage);
+          increment = MAX (1.0, MIN (stage_width, width));
+
+          adjustment = nbtk_adjustment_new (priv->x,
+                                            0,
+                                            width,
+                                            1.0,
+                                            increment,
+                                            increment);
+
+          scrollable_set_adjustments (scrollable,
+                                      adjustment,
+                                      priv->vadjustment);
+
+          *hadjustment = adjustment;
+        }
+    }
+
+  if (vadjustment)
+    {
+      if (priv->vadjustment)
+        *vadjustment = priv->vadjustment;
+      else
+        {
+          NbtkAdjustment *adjustment;
+          gdouble height, stage_height, increment;
+
+          height = clutter_actor_get_height (actor);
+          stage_height = clutter_actor_get_height (stage);
+          increment = MAX (1.0, MIN (stage_height, height));
+
+          adjustment = nbtk_adjustment_new (priv->y,
+                                            0,
+                                            height,
+                                            1.0,
+                                            increment,
+                                            increment);
+
+          scrollable_set_adjustments (scrollable,
+                                      priv->hadjustment,
+                                      adjustment);
+
+          *vadjustment = adjustment;
+        }
+    }
+}
+
+static void
+scrollable_interface_init (NbtkScrollableInterface *iface)
+{
+  iface->set_adjustments = scrollable_set_adjustments;
+  iface->get_adjustments = scrollable_get_adjustments;
+}
+
+static void
+nbtk_viewport_init (NbtkViewport *self)
+{
+  self->priv = VIEWPORT_PRIVATE (self);
+
+  self->priv->sync_adjustments = TRUE;
+
+  g_object_set (G_OBJECT (self), "reactive", FALSE, NULL);
+}
+
+NbtkWidget *
+nbtk_viewport_new (void)
+{
+  return g_object_new (NBTK_TYPE_VIEWPORT, NULL);
+}
+
+void
+nbtk_viewport_set_originu (NbtkViewport *viewport,
+                           gfloat   x,
+                           gfloat   y,
+                           gfloat   z)
+{
+  NbtkViewportPrivate *priv;
+
+  g_return_if_fail (NBTK_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)
+        nbtk_adjustment_set_value (priv->hadjustment,
+                                   (float) (x));
+    }
+
+  if (y != priv->y)
+    {
+      priv->y = y;
+      g_object_notify (G_OBJECT (viewport), "y-origin");
+
+      if (priv->vadjustment)
+        nbtk_adjustment_set_value (priv->vadjustment,
+                                   (float) (y));
+    }
+
+  if (z != priv->z)
+    {
+      priv->z = z;
+      g_object_notify (G_OBJECT (viewport), "z-origin");
+    }
+
+  g_object_thaw_notify (G_OBJECT (viewport));
+
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (viewport));
+}
+
+void
+nbtk_viewport_set_origin (NbtkViewport *viewport,
+                          gint          x,
+                          gint          y,
+                          gint          z)
+{
+  g_return_if_fail (NBTK_IS_VIEWPORT (viewport));
+
+  nbtk_viewport_set_originu (viewport,
+                             (float) (x),
+                             (float) (y),
+                             (float) (z));
+}
+
+void
+nbtk_viewport_get_originu (NbtkViewport *viewport,
+                           gfloat  *x,
+                           gfloat  *y,
+                           gfloat  *z)
+{
+  NbtkViewportPrivate *priv;
+
+  g_return_if_fail (NBTK_IS_VIEWPORT (viewport));
+
+  priv = viewport->priv;
+
+  if (x)
+    *x = priv->x;
+
+  if (y)
+    *y = priv->y;
+
+  if (z)
+    *z = priv->z;
+}
+
+void
+nbtk_viewport_get_origin (NbtkViewport *viewport,
+                          gint         *x,
+                          gint         *y,
+                          gint         *z)
+{
+  NbtkViewportPrivate *priv;
+
+  g_return_if_fail (NBTK_IS_VIEWPORT (viewport));
+
+  priv = viewport->priv;
+
+  if (x)
+    *x = (int) priv->x;
+
+  if (y)
+    *y = (int) priv->y;
+
+  if (z)
+    *z = (int) priv->z;
+}
diff --git a/src/nbtk/nbtk-viewport.h b/src/nbtk/nbtk-viewport.h
new file mode 100644
index 0000000..8dadda8
--- /dev/null
+++ b/src/nbtk/nbtk-viewport.h
@@ -0,0 +1,94 @@
+/*
+ * nbtk-viewport.h: Viewport actor
+ *
+ * Copyright 2008 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris openedhand com>
+ * Port to Nbtk by: Robert Staudinger <robsta openedhand com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_VIEWPORT_H__
+#define __NBTK_VIEWPORT_H__
+
+#include <clutter/clutter.h>
+#include <nbtk/nbtk-bin.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_VIEWPORT            (nbtk_viewport_get_type())
+#define NBTK_VIEWPORT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_VIEWPORT, NbtkViewport))
+#define NBTK_IS_VIEWPORT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_VIEWPORT))
+#define NBTK_VIEWPORT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_VIEWPORT, NbtkViewportClass))
+#define NBTK_IS_VIEWPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_VIEWPORT))
+#define NBTK_VIEWPORT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_VIEWPORT, NbtkViewportClass))
+
+typedef struct _NbtkViewport          NbtkViewport;
+typedef struct _NbtkViewportPrivate   NbtkViewportPrivate;
+typedef struct _NbtkViewportClass     NbtkViewportClass;
+
+/**
+ * NbtkViewport:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _NbtkViewport
+{
+  /*< private >*/
+  NbtkBin parent;
+
+  NbtkViewportPrivate *priv;
+};
+
+struct _NbtkViewportClass
+{
+  NbtkBinClass parent_class;
+};
+
+GType nbtk_viewport_get_type (void) G_GNUC_CONST;
+
+NbtkWidget *   nbtk_viewport_new         (void);
+
+void           nbtk_viewport_set_originu (NbtkViewport *viewport,
+                                          gfloat        x,
+                                          gfloat        y,
+                                          gfloat        z);
+
+void           nbtk_viewport_set_origin  (NbtkViewport *viewport,
+                                          gint          x,
+                                          gint          y,
+                                          gint          z);
+
+void           nbtk_viewport_get_originu (NbtkViewport *viewport,
+                                          gfloat       *x,
+                                          gfloat       *y,
+                                          gfloat       *z);
+
+void           nbtk_viewport_get_origin  (NbtkViewport *viewport,
+                                          gint         *x,
+                                          gint         *y,
+                                          gint         *z);
+
+G_END_DECLS
+
+#endif /* __NBTK_VIEWPORT_H__ */



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