-SUBDIRS = src
+SUBDIRS = tidy src
/* config.h.in. Generated from configure.ac by autoheader. */
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
/* Name of package */
#undef PACKAGE
/* Define to the version of this package. */
#undef PACKAGE_VERSION
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
/* Version number of package */
#undef VERSION
AM_INIT_AUTOMAKE
AC_LIBTOOL_DLOPEN
AC_LIBLTDL_CONVENIENCE
+AM_PROG_LIBTOOL
# Checks for programs.
AC_PROG_CC
AC_SUBST(DEPS_LIBS)
PKG_CHECK_MODULES(DEPS,
- [ glib-2.0 >= 2.2,
+ [ glib-2.0 >= 2.2,
gobject-2.0 >= 2.10,
gtk+-2.0 >= 2.2,
clutter-0.8 >= 0.8,
# Checks for library functions.
AC_CONFIG_FILES([Makefile
- src/Makefile])
+ src/Makefile
+ tidy/Makefile])
AC_OUTPUT
launcher.c
-champlain_LDADD = $(DEPS_LIBS)
-AM_CPPFLAGS = $(DEPS_CFLAGS)
+champlain_LDADD = $(DEPS_LIBS) ../tidy/libtidy-1.0.la
+AM_CPPFLAGS = $(DEPS_CFLAGS) -I../tidy
EXTRA_DIST = $(CHAMPLAIN_MARSHAL_LIST)
#ifndef CHAMPLAIN_PRIVATE_H
#define CHAMPLAIN_PRIVATE_H
+#include <clutter/clutter.h>
+#include <clutter/clutter.h>
+
void champlain_map_create_tiles(gint zoom_level);
ChamplainMapZoomLevel* champlain_map_zoom_level_new(gint zoom_level, gint row, gint column, gint tile_size);
+//gboolean tile_is_visible(ClutterUnit viewport_w, ClutterUnit viewport_h, ChamplainPoint position, ChamplainMapTile* tile);
+
ChamplainMapTile* champlain_map_tile_new(gint x, gint y, gint tile_size);
#endif
#include "champlain_widget.h"
#include "champlain-marshal.h"
+#include <tidy-finger-scroll.h>
#include <math.h>
#include <glib.h>
#include <glib-object.h>
struct _ChamplainWidgetPrivate
{
GtkWidget *clutterEmbed;
- GtkAdjustment *horizontalAdjustment;
- GtkAdjustment *verticalAdjustment;
ClutterActor *viewport;
// Scrolling
G_DEFINE_TYPE (ChamplainWidget, champlain_widget, GTK_TYPE_ALIGNMENT);
-static void
-adjustement_changed_cb (GtkAdjustment * adjustment, ChamplainWidgetPrivate * champlainWidget)
-{
- ChamplainWidgetPrivate *priv = CHAMPLAIN_WIDGET_GET_PRIVATE (champlainWidget);
- /*
- if (adjustment == priv->horizontalAdjustment)
- priv->scrollOffset.x = (int)gtk_adjustment_get_value(adjustment);
- else if (adjustment == priv->verticalAdjustment)
- priv->scrollOffset.y = (int)gtk_adjustment_get_value(adjustment);
- */
- // Check if the offset is empty
-}
-
-static void
-champlain_widget_set_scroll_adjustments (ChamplainWidget * champlainWidget,
- GtkAdjustment * hadjustment, GtkAdjustment * vadjustment)
-{
- ChamplainWidgetPrivate *priv = CHAMPLAIN_WIDGET_GET_PRIVATE (champlainWidget);
-
- if (priv->horizontalAdjustment)
- {
- g_signal_handlers_disconnect_by_func (G_OBJECT
- (priv->horizontalAdjustment),
- (gpointer) adjustement_changed_cb, champlainWidget);
- g_signal_handlers_disconnect_by_func (G_OBJECT
- (priv->verticalAdjustment),
- (gpointer) adjustement_changed_cb, champlainWidget);
-
- g_object_unref (priv->horizontalAdjustment);
- g_object_unref (priv->verticalAdjustment);
- }
-
- priv->horizontalAdjustment = hadjustment;
- priv->verticalAdjustment = vadjustment;
-
- if (hadjustment)
- {
- g_object_ref_sink (priv->horizontalAdjustment);
- g_object_ref_sink (priv->verticalAdjustment);
-
- gdouble val = gtk_adjustment_get_value (hadjustment);
- val = gtk_adjustment_get_value (vadjustment);
- // Connect the signals
-
- g_object_set (G_OBJECT (priv->horizontalAdjustment), "lower", -180.0, NULL);
- g_object_set (G_OBJECT (priv->horizontalAdjustment), "upper", 180.0, NULL);
- g_object_set (G_OBJECT (priv->horizontalAdjustment), "page-size", 20.0, NULL);
- g_object_set (G_OBJECT (priv->horizontalAdjustment), "step-increment", 5.0, NULL);
- g_object_set (G_OBJECT (priv->horizontalAdjustment), "page-increment", 15.0, NULL);
-
- g_object_set (G_OBJECT (priv->verticalAdjustment), "lower", -90.0, NULL);
- g_object_set (G_OBJECT (priv->verticalAdjustment), "upper", 90.0, NULL);
- g_object_set (G_OBJECT (priv->verticalAdjustment), "page-size", 20.0, NULL);
- g_object_set (G_OBJECT (priv->verticalAdjustment), "step-increment", 5.0, NULL);
- g_object_set (G_OBJECT (priv->verticalAdjustment), "page-increment", 15.0, NULL);
-
- //g_signal_connect(G_OBJECT(priv->horizontalAdjustment), "value-changed", (gpointer)adjustement_changed_cb, champlainWidget);
- //g_signal_connect(G_OBJECT(priv->verticalAdjustment), "value-changed", (gpointer)adjustement_changed_cb, champlainWidget);
- }
-}
static void
champlain_widget_finalize (GObject * object)
ChamplainWidget *widget = CHAMPLAIN_WIDGET (object);
ChamplainWidgetPrivate *priv = CHAMPLAIN_WIDGET_GET_PRIVATE (widget);
- if (priv->horizontalAdjustment)
- {
- g_object_unref (priv->horizontalAdjustment);
- //g_signal_handlers_disconnect_by_func (G_OBJECT
- // (priv->horizontalAdjustment), (gpointer) adjustement_changed_cb, widget);
- }
-
- if (priv->verticalAdjustment)
- {
- g_object_unref (priv->verticalAdjustment);
- //g_signal_handlers_disconnect_by_func (G_OBJECT
- // (priv->verticalAdjustment), (gpointer) adjustement_changed_cb, widget);
- }
-
G_OBJECT_CLASS (champlain_widget_parent_class)->finalize (object);
}
{
g_type_class_add_private (champlainWidgetClass, sizeof (ChamplainWidgetPrivate));
- /*
- * make us scrollable (e.g. addable to a GtkScrolledWindow)
- */
- champlainWidgetClass->set_scroll_adjustments = champlain_widget_set_scroll_adjustments;
- GTK_WIDGET_CLASS (champlainWidgetClass)->set_scroll_adjustments_signal =
- g_signal_new ("set-scroll-adjustments",
- G_TYPE_FROM_CLASS (champlainWidgetClass),
- (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
- G_STRUCT_OFFSET (ChamplainWidgetClass,
- set_scroll_adjustments), NULL, NULL,
- champlain_marshal_VOID__OBJECT_OBJECT, G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
-
GObjectClass *objectClass = G_OBJECT_CLASS (champlainWidgetClass);
objectClass->finalize = champlain_widget_finalize;
}
champlain_widget_init (ChamplainWidget * champlainWidget)
{
ChamplainWidgetPrivate *priv = CHAMPLAIN_WIDGET_GET_PRIVATE (champlainWidget);
-
- priv->horizontalAdjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
- priv->verticalAdjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
-
- g_object_ref_sink (priv->horizontalAdjustment);
- g_object_ref_sink (priv->verticalAdjustment);
-
-
-}
-
-static gboolean
-tile_is_visible(ClutterUnit viewport_w, ClutterUnit viewport_h, ChamplainPoint position, ChamplainMapTile* tile)
-{
- ClutterUnit size = CLUTTER_UNITS_FROM_INT(tile->size);
-
-
- if( ((tile->x + 1)* size + position.x < 0 || tile->x* size + position.x > viewport_w) ||
- ((tile->y + 1)* size + position.y < 0 || tile->y* size + position.y > viewport_h))
- {
- g_print ("Tile I: %d, %d\t p: %d, %d \n",
- tile->x, tile->y,
- CLUTTER_UNITS_TO_INT (position.x),
- CLUTTER_UNITS_TO_INT (position.y));
- return FALSE;
- }
- g_print ("Tile V: %d, %d\t p: %d, %d \n",
- tile->x, tile->y,
- CLUTTER_UNITS_TO_INT (position.x),
- CLUTTER_UNITS_TO_INT (position.y));
- return TRUE;
-
-}
-
-static void
-champlain_widget_verify_tiles (ChamplainWidget * champlainWidget)
-{
- ChamplainWidgetPrivate *priv = CHAMPLAIN_WIDGET_GET_PRIVATE (champlainWidget);
- ClutterActor *stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (priv->clutterEmbed));
- ClutterUnit stage_w, stage_h;
- clutter_actor_get_sizeu(stage, &stage_w, &stage_h);
- g_print("Stage: %d, %d\n", CLUTTER_UNITS_TO_INT(stage_w), CLUTTER_UNITS_TO_INT(stage_h));
- // Check for tiles that left the viewport
-
- int i;
- int tile_count = priv->map->current_level->tiles->len;
- for (i = 0; i < tile_count; i++)
- {
- ChamplainMapTile* tile = g_ptr_array_index (priv->map->current_level->tiles, i);
- if (tile_is_visible(stage_w, stage_h, priv->position, tile))
- {
- if(!tile->visible)
- {
- tile->visible = TRUE;
- clutter_container_add (CLUTTER_CONTAINER (priv->map->current_level->group), tile->actor, NULL);
- }
- }
- else
- {
- if (tile->visible)
- {
- clutter_container_remove_actor (CLUTTER_CONTAINER (priv->map->current_level->group), tile->actor);
- tile->visible = FALSE;
- }
- }
- }
-
-
- // Check for missing tiles in the viewport
- /*if ( priv->position.x > 0 )
- {
- gboolean exist = FALSE;
- for (i = 0; i < 20; i++)
- {
- ClutterActor * tile = CLUTTER_ACTOR(g_ptr_array_index (priv->tiles, i));
- ClutterUnit actor_x, actor_y;
- clutter_actor_get_positionu(tile, &actor_x, &actor_y);
- //if (actor_x < 0
- }
-
- }*/
}
static gboolean
dx = x - priv->position.x;
dy = y - priv->position.y;
- //g_print ("Motion n: %d, %d\t c: %d, %d \t d: %d, %d\n",
- // CLUTTER_UNITS_TO_INT (x), CLUTTER_UNITS_TO_INT (y),
- // CLUTTER_UNITS_TO_INT (priv->position.x),
- // CLUTTER_UNITS_TO_INT (priv->position.y), CLUTTER_UNITS_TO_INT (dx), CLUTTER_UNITS_TO_INT (dy));
+ /*g_print ("Motion n: %d, %d\t c: %d, %d \t d: %d, %d\t h: %d, %d\n",
+ CLUTTER_UNITS_TO_INT (x),
+ CLUTTER_UNITS_TO_INT (y),
+ CLUTTER_UNITS_TO_INT (priv->position.x),
+ CLUTTER_UNITS_TO_INT (priv->position.y),
+ CLUTTER_UNITS_TO_INT (dx),
+ CLUTTER_UNITS_TO_INT (dy),
+ CLUTTER_UNITS_TO_INT (priv->hitPoint.x),
+ CLUTTER_UNITS_TO_INT (priv->hitPoint.y));*/
priv->position.x += dx - priv->hitPoint.x;
priv->position.y += dy - priv->hitPoint.y;
clutter_actor_set_positionu (priv->viewport, priv->position.x, priv->position.y);
- champlain_widget_verify_tiles(champlainWidget);
- }
+
+ } else g_print("Couldn't convert point");
return TRUE;
}
if (event->type == CLUTTER_BUTTON_PRESS)
{
ClutterButtonEvent *bevent = (ClutterButtonEvent *) event;
+ g_print ("d: %d, %d \n",
+ bevent->x,
+ bevent->y);
ClutterUnit x, y;
if ((bevent->button == 1) &&
(clutter_actor_transform_stage_point (stage,
(bevent->x), CLUTTER_UNITS_FROM_DEVICE (bevent->y), &x, &y)))
{
- //g_print ("Hit h: %d, %d\t c: %d, %d \n", CLUTTER_UNITS_TO_INT (x),
- // CLUTTER_UNITS_TO_INT (y),
- // CLUTTER_UNITS_TO_INT (priv->position.x), CLUTTER_UNITS_TO_INT (priv->position.y));
+ g_print ("Hit h: %d, %d\t c: %d, %d \t d: %d, %d \n",
+ CLUTTER_UNITS_TO_INT (x),
+ CLUTTER_UNITS_TO_INT (y),
+ CLUTTER_UNITS_TO_INT (priv->position.x),
+ CLUTTER_UNITS_TO_INT (priv->position.y),
+ bevent->x,
+ bevent->y);
+
priv->hitPoint.x = x - priv->position.x;
priv->hitPoint.y = y - priv->position.y;
g_signal_connect (priv->viewport,
"button-release-event", G_CALLBACK (viewport_button_release_event_cb), champlainWidget);
}
+ else g_print("Couldn't convert point");
}
return FALSE;
champlain_map_load(priv->map, 1);
clutter_container_add_actor (CLUTTER_CONTAINER (priv->viewport), priv->map->current_level->group);
- champlain_widget_verify_tiles (champlainWidget);
+
}
GtkWidget *
/* Setup stage */
ClutterActor *stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (priv->clutterEmbed));
+
ClutterColor black;
clutter_color_parse ("black", &black);
clutter_stage_set_color (CLUTTER_STAGE (stage), &black);
gtk_container_add (GTK_CONTAINER (widget), priv->clutterEmbed);
-
- /* Setup viewport */
- priv->viewport = clutter_group_new ();
- clutter_actor_set_reactive (CLUTTER_ACTOR (priv->viewport), TRUE);
- g_signal_connect (CLUTTER_ACTOR (priv->viewport),
- "captured-event", G_CALLBACK (viewport_captured_event_cb), widget);
- clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->viewport);
- champlain_widget_load_map (widget);
+ ClutterActor* finger_scroll = tidy_finger_scroll_new(TIDY_FINGER_SCROLL_MODE_PUSH);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), finger_scroll);
return GTK_WIDGET (widget);
}
--- /dev/null
+NULL =
+
+GLIB_GENMARSHAL = `pkg-config --variable=glib_genmarshal glib-2.0`
+GLIB_MKENUMS = `pkg-config --variable=glib_mkenums glib-2.0`
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ -DPREFIX=\""$(prefix)"\" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DG_DISABLE_DEPRECATED \
+ -DG_LOG_DOMAIN=\"Tidy\" \
+ $(DEPS_CFLAGS) \
+ $(TIDY_DEBUG_CFLAGS) \
+ $(NULL)
+
+LDADD = $(TIDY_LT_LDFLAGS) -export-dynamic -rpath $(libdir)
+
+BUILT_SOURCES = \
+ tidy-enum-types.h \
+ tidy-enum-types.c \
+ tidy-marshal.h \
+ tidy-marshal.c
+
+STAMP_FILES = stamp-tidy-marshal.h stamp-tidy-enum-types.h
+
+# please, keep this sorted alphabetically
+source_h = \
+ $(top_srcdir)/tidy/tidy-adjustment.h \
+ $(top_srcdir)/tidy/tidy-finger-scroll.h \
+ $(top_srcdir)/tidy/tidy-scrollable.h \
+ $(top_srcdir)/tidy/tidy-scroll-view.h \
+ $(top_srcdir)/tidy/tidy-viewport.h \
+ $(NULL)
+
+source_h_private = \
+ tidy-debug.h \
+ $(NULL)
+
+# please, keep this sorted alphabetically
+source_c = \
+ tidy-adjustment.c \
+ tidy-finger-scroll.c \
+ tidy-scroll-view.c \
+ tidy-scrollable.c \
+ tidy-viewport.c \
+ $(NULL)
+
+tidy-marshal.h: stamp-tidy-marshal.h
+ @true
+stamp-tidy-marshal.h: Makefile tidy-marshal.list
+ $(GLIB_GENMARSHAL) \
+ --prefix=_tidy_marshal \
+ --header \
+ $(srcdir)/tidy-marshal.list > xgen-tmh && \
+ (cmp -s xgen-tmh tidy-marshal.h || cp -f xgen-tmh tidy-marshal.h) && \
+ rm -f xgen-tmh && \
+ echo timestamp > $(@F)
+
+tidy-marshal.c: Makefile tidy-marshal.list
+ (echo "#include \"tidy-marshal.h\"" ; \
+ $(GLIB_GENMARSHAL) \
+ --prefix=_tidy_marshal \
+ --body \
+ $(srcdir)/tidy-marshal.list ) > xgen-tmc && \
+ cp -f xgen-tmc tidy-marshal.c && \
+ rm -f xgen-tmc
+
+tidy-enum-types.h: stamp-tidy-enum-types.h Makefile
+ @true
+stamp-tidy-enum-types.h: $(source_h) tidy-enum-types.h.in
+ ( cd $(srcdir) && \
+ $(GLIB_MKENUMS) \
+ --template $(srcdir)/tidy-enum-types.h.in \
+ $(source_h) ) >> xgen-teth && \
+ (cmp xgen-teth tidy-enum-types.h || cp xgen-teth tidy-enum-types.h) && \
+ rm -f xgen-teth && \
+ echo timestamp > $(@F)
+
+tidy-enum-types.c: stamp-tidy-enum-types.h tidy-enum-types.c.in
+ ( cd $(srcdir) && \
+ $(GLIB_MKENUMS) \
+ --template $(srcdir)/tidy-enum-types.c.in \
+ $(source_h) ) >> xgen-tetc && \
+ cp xgen-tetc tidy-enum-types.c && \
+ rm -f xgen-tetc
+
+lib_LTLIBRARIES = libtidy-1.0.la
+
+libtidy_1_0_la_LIBADD = $(DEPS_LIBS)
+libtidy_1_0_la_SOURCES = \
+ $(source_c) \
+ $(source_h) \
+ $(source_h_priv) \
+ $(BUILT_SOURCES) \
+ $(NULL)
+libtidy_1_0_la_LDFLAGS = $(TIDY_LT_LDFLAGS)
+
+tidyincludedir = $(includedir)/tidy-1.0/tidy
+tidyinclude_DATA = \
+ $(source_h) \
+ $(top_srcdir)/tidy/tidy-enum-types.h \
+ $(NULL)
+
+CLEANFILES = $(STAMP_FILES) $(BUILT_SOURCES)
+
+DISTCLEANFILES = tidy-version.h
+
+EXTRA_DIST = \
+ tidy-enum-types.h.in \
+ tidy-enum-types.c.in \
+ tidy-marshal.list
+
--- /dev/null
+/* tidy-adjustment.c: Adjustment object
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris@openedhand.com>, inspired by GtkAdjustment
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib-object.h>
+#include <clutter/clutter-alpha.h>
+#include <clutter/clutter-fixed.h>
+#include <clutter/clutter-timeline.h>
+
+#include "tidy-adjustment.h"
+#include "tidy-marshal.h"
+#include "tidy-private.h"
+
+G_DEFINE_TYPE (TidyAdjustment, tidy_adjustment, G_TYPE_OBJECT)
+
+#define ADJUSTMENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TIDY_TYPE_ADJUSTMENT, TidyAdjustmentPrivate))
+
+struct _TidyAdjustmentPrivate
+{
+ ClutterFixed lower;
+ ClutterFixed upper;
+ ClutterFixed value;
+ ClutterFixed step_increment;
+ ClutterFixed page_increment;
+ ClutterFixed page_size;
+
+ /* For interpolation */
+ ClutterTimeline *interpolation;
+ ClutterFixed dx;
+ ClutterFixed old_position;
+ ClutterFixed 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 void tidy_adjustment_set_lower (TidyAdjustment *adjustment,
+ gdouble lower);
+static void tidy_adjustment_set_upper (TidyAdjustment *adjustment,
+ gdouble upper);
+static void tidy_adjustment_set_step_increment (TidyAdjustment *adjustment,
+ gdouble step);
+static void tidy_adjustment_set_page_increment (TidyAdjustment *adjustment,
+ gdouble page);
+static void tidy_adjustment_set_page_size (TidyAdjustment *adjustment,
+ gdouble size);
+
+static void
+tidy_adjustment_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TidyAdjustmentPrivate *priv = TIDY_ADJUSTMENT (object)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_LOWER:
+ g_value_set_double (value, CLUTTER_FIXED_TO_DOUBLE (priv->lower));
+ break;
+
+ case PROP_UPPER:
+ g_value_set_double (value, CLUTTER_FIXED_TO_DOUBLE (priv->upper));
+ break;
+
+ case PROP_VALUE:
+ g_value_set_double (value, CLUTTER_FIXED_TO_DOUBLE (priv->value));
+ break;
+
+ case PROP_STEP_INC:
+ g_value_set_double (value, CLUTTER_FIXED_TO_DOUBLE (priv->step_increment));
+ break;
+
+ case PROP_PAGE_INC:
+ g_value_set_double (value, CLUTTER_FIXED_TO_DOUBLE (priv->page_increment));
+ break;
+
+ case PROP_PAGE_SIZE:
+ g_value_set_double (value, CLUTTER_FIXED_TO_DOUBLE (priv->page_size));
+ break;
+
+ case PROP_ELASTIC:
+ g_value_set_boolean (value, priv->elastic);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tidy_adjustment_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TidyAdjustment *adj = TIDY_ADJUSTMENT (object);
+
+ switch (prop_id)
+ {
+ case PROP_LOWER:
+ tidy_adjustment_set_lower (adj, g_value_get_double (value));
+ break;
+
+ case PROP_UPPER:
+ tidy_adjustment_set_upper (adj, g_value_get_double (value));
+ break;
+
+ case PROP_VALUE:
+ tidy_adjustment_set_value (adj, g_value_get_double (value));
+ break;
+
+ case PROP_STEP_INC:
+ tidy_adjustment_set_step_increment (adj, g_value_get_double (value));
+ break;
+
+ case PROP_PAGE_INC:
+ tidy_adjustment_set_page_increment (adj, g_value_get_double (value));
+ break;
+
+ case PROP_PAGE_SIZE:
+ tidy_adjustment_set_page_size (adj, g_value_get_double (value));
+ break;
+
+ case PROP_ELASTIC:
+ tidy_adjustment_set_elastic (adj, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+stop_interpolation (TidyAdjustment *adjustment)
+{
+ TidyAdjustmentPrivate *priv = adjustment->priv;
+
+ if (priv->interpolation)
+ {
+ clutter_timeline_stop (priv->interpolation);
+ g_object_unref (priv->interpolation);
+ priv->interpolation = NULL;
+
+ if (priv->bounce_alpha)
+ {
+ g_object_unref (priv->bounce_alpha);
+ priv->bounce_alpha = NULL;
+ }
+ }
+
+ if (priv->bounce_source)
+ {
+ g_source_remove (priv->bounce_source);
+ priv->bounce_source = 0;
+ }
+}
+
+static void
+tidy_adjustment_dispose (GObject *object)
+{
+ stop_interpolation (TIDY_ADJUSTMENT (object));
+
+ G_OBJECT_CLASS (tidy_adjustment_parent_class)->dispose (object);
+}
+
+static void
+tidy_adjustment_class_init (TidyAdjustmentClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (TidyAdjustmentPrivate));
+
+ object_class->get_property = tidy_adjustment_get_property;
+ object_class->set_property = tidy_adjustment_set_property;
+ object_class->dispose = tidy_adjustment_dispose;
+
+ g_object_class_install_property (object_class,
+ PROP_LOWER,
+ g_param_spec_double ("lower",
+ "Lower",
+ "Lower bound",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ TIDY_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_UPPER,
+ g_param_spec_double ("upper",
+ "Upper",
+ "Upper bound",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ TIDY_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_VALUE,
+ g_param_spec_double ("value",
+ "Value",
+ "Current value",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ TIDY_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_STEP_INC,
+ g_param_spec_double ("step-increment",
+ "Step Increment",
+ "Step increment",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ TIDY_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_PAGE_INC,
+ g_param_spec_double ("page-increment",
+ "Page Increment",
+ "Page increment",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ TIDY_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_PAGE_SIZE,
+ g_param_spec_double ("page-size",
+ "Page Size",
+ "Page size",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ TIDY_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_ELASTIC,
+ g_param_spec_boolean ("elastic",
+ "Elastic",
+ "Make interpolation "
+ "behave in an "
+ "'elastic' way and "
+ "stop clamping value.",
+ FALSE,
+ TIDY_PARAM_READWRITE));
+
+ signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TidyAdjustmentClass, changed),
+ NULL, NULL,
+ _tidy_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+tidy_adjustment_init (TidyAdjustment *self)
+{
+ self->priv = ADJUSTMENT_PRIVATE (self);
+}
+
+TidyAdjustment *
+tidy_adjustment_new (gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size)
+{
+ return g_object_new (TIDY_TYPE_ADJUSTMENT,
+ "value", value,
+ "lower", lower,
+ "upper", upper,
+ "step-increment", step_increment,
+ "page-increment", page_increment,
+ "page-size", page_size,
+ NULL);
+}
+
+TidyAdjustment *
+tidy_adjustment_newx (ClutterFixed value,
+ ClutterFixed lower,
+ ClutterFixed upper,
+ ClutterFixed step_increment,
+ ClutterFixed page_increment,
+ ClutterFixed page_size)
+{
+ TidyAdjustment *retval;
+ TidyAdjustmentPrivate *priv;
+
+ retval = g_object_new (TIDY_TYPE_ADJUSTMENT, NULL);
+ priv = retval->priv;
+
+ priv->value = value;
+ priv->lower = lower;
+ priv->upper = upper;
+ priv->step_increment = step_increment;
+ priv->page_increment = page_increment;
+ priv->page_size = page_size;
+
+ return retval;
+}
+
+ClutterFixed
+tidy_adjustment_get_valuex (TidyAdjustment *adjustment)
+{
+ TidyAdjustmentPrivate *priv;
+
+ g_return_val_if_fail (TIDY_IS_ADJUSTMENT (adjustment), 0);
+
+ priv = adjustment->priv;
+
+ if (adjustment->priv->interpolation)
+ {
+ return MAX (priv->lower, MIN (priv->upper - priv->page_size,
+ adjustment->priv->new_position));
+ }
+ else
+ return adjustment->priv->value;
+}
+
+gdouble
+tidy_adjustment_get_value (TidyAdjustment *adjustment)
+{
+ g_return_val_if_fail (TIDY_IS_ADJUSTMENT (adjustment), 0.0);
+
+ return CLUTTER_FIXED_TO_FLOAT (adjustment->priv->value);
+}
+
+void
+tidy_adjustment_set_valuex (TidyAdjustment *adjustment,
+ ClutterFixed value)
+{
+ TidyAdjustmentPrivate *priv;
+
+ g_return_if_fail (TIDY_IS_ADJUSTMENT (adjustment));
+
+ priv = adjustment->priv;
+
+ stop_interpolation (adjustment);
+
+ if (!priv->elastic)
+ value = CLAMP (value, priv->lower, MAX (priv->lower,
+ priv->upper - priv->page_size));
+
+ if (priv->value != value)
+ {
+ priv->value = value;
+ g_object_notify (G_OBJECT (adjustment), "value");
+ }
+}
+
+void
+tidy_adjustment_set_value (TidyAdjustment *adjustment,
+ gdouble value)
+{
+ tidy_adjustment_set_valuex (adjustment, CLUTTER_FLOAT_TO_FIXED (value));
+}
+
+void
+tidy_adjustment_clamp_pagex (TidyAdjustment *adjustment,
+ ClutterFixed lower,
+ ClutterFixed upper)
+{
+ gboolean changed;
+ TidyAdjustmentPrivate *priv;
+
+ g_return_if_fail (TIDY_IS_ADJUSTMENT (adjustment));
+
+ priv = adjustment->priv;
+
+ stop_interpolation (adjustment);
+
+ lower = CLAMP (lower, priv->lower, priv->upper - priv->page_size);
+ upper = CLAMP (upper, priv->lower + priv->page_size, priv->upper);
+
+ changed = FALSE;
+
+ if (priv->value + priv->page_size > upper)
+ {
+ priv->value = upper - priv->page_size;
+ changed = TRUE;
+ }
+
+ if (priv->value < lower)
+ {
+ priv->value = lower;
+ changed = TRUE;
+ }
+
+ if (changed)
+ g_object_notify (G_OBJECT (adjustment), "value");
+}
+
+void
+tidy_adjustment_clamp_page (TidyAdjustment *adjustment,
+ gdouble lower,
+ gdouble upper)
+{
+ tidy_adjustment_clamp_pagex (adjustment,
+ CLUTTER_FLOAT_TO_FIXED (lower),
+ CLUTTER_FLOAT_TO_FIXED (upper));
+}
+
+static void
+tidy_adjustment_set_lower (TidyAdjustment *adjustment,
+ gdouble lower)
+{
+ TidyAdjustmentPrivate *priv = adjustment->priv;
+ ClutterFixed value = CLUTTER_FLOAT_TO_FIXED (lower);
+
+ if (priv->lower != value)
+ {
+ priv->lower = value;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify (G_OBJECT (adjustment), "lower");
+
+ tidy_adjustment_clamp_pagex (adjustment, priv->lower, priv->upper);
+ }
+}
+
+static void
+tidy_adjustment_set_upper (TidyAdjustment *adjustment,
+ gdouble upper)
+{
+ TidyAdjustmentPrivate *priv = adjustment->priv;
+ ClutterFixed value = CLUTTER_FLOAT_TO_FIXED (upper);
+
+ if (priv->upper != value)
+ {
+ priv->upper = value;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify (G_OBJECT (adjustment), "upper");
+
+ tidy_adjustment_clamp_pagex (adjustment, priv->lower, priv->upper);
+ }
+}
+
+static void
+tidy_adjustment_set_step_increment (TidyAdjustment *adjustment,
+ gdouble step)
+{
+ TidyAdjustmentPrivate *priv = adjustment->priv;
+ ClutterFixed value = CLUTTER_FLOAT_TO_FIXED (step);
+
+ if (priv->step_increment != value)
+ {
+ priv->step_increment = value;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify (G_OBJECT (adjustment), "step-increment");
+ }
+}
+
+static void
+tidy_adjustment_set_page_increment (TidyAdjustment *adjustment,
+ gdouble page)
+{
+ TidyAdjustmentPrivate *priv = adjustment->priv;
+ ClutterFixed value = CLUTTER_FLOAT_TO_FIXED (page);
+
+ if (priv->page_increment != value)
+ {
+ priv->page_increment = value;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify (G_OBJECT (adjustment), "page-increment");
+ }
+}
+
+static void
+tidy_adjustment_set_page_size (TidyAdjustment *adjustment,
+ gdouble size)
+{
+ TidyAdjustmentPrivate *priv = adjustment->priv;
+ ClutterFixed value = CLUTTER_FLOAT_TO_FIXED (size);
+
+ if (priv->page_size != value)
+ {
+ priv->page_size = value;
+
+ g_signal_emit (adjustment, signals[CHANGED], 0);
+
+ g_object_notify (G_OBJECT (adjustment), "page_size");
+
+ tidy_adjustment_clamp_pagex (adjustment, priv->lower, priv->upper);
+ }
+}
+
+void
+tidy_adjustment_set_valuesx (TidyAdjustment *adjustment,
+ ClutterFixed value,
+ ClutterFixed lower,
+ ClutterFixed upper,
+ ClutterFixed step_increment,
+ ClutterFixed page_increment,
+ ClutterFixed page_size)
+{
+ TidyAdjustmentPrivate *priv;
+ gboolean emit_changed = FALSE;
+
+ g_return_if_fail (TIDY_IS_ADJUSTMENT (adjustment));
+
+ priv = adjustment->priv;
+
+ stop_interpolation (adjustment);
+
+ emit_changed = FALSE;
+
+ g_object_freeze_notify (G_OBJECT (adjustment));
+
+ if (priv->lower != lower)
+ {
+ priv->lower = lower;
+ emit_changed = TRUE;
+
+ g_object_notify (G_OBJECT (adjustment), "lower");
+ }
+
+ if (priv->upper != upper)
+ {
+ priv->upper = upper;
+ emit_changed = TRUE;
+
+ g_object_notify (G_OBJECT (adjustment), "upper");
+ }
+
+ if (priv->step_increment != step_increment)
+ {
+ priv->step_increment = step_increment;
+ emit_changed = TRUE;
+
+ g_object_notify (G_OBJECT (adjustment), "step-increment");
+ }
+
+ if (priv->page_increment != page_increment)
+ {
+ priv->page_increment = page_increment;
+ emit_changed = TRUE;
+
+ g_object_notify (G_OBJECT (adjustment), "page-increment");
+ }
+
+ if (priv->page_size != page_size)
+ {
+ priv->page_size = page_size;
+ emit_changed = TRUE;
+
+ g_object_notify (G_OBJECT (adjustment), "page-size");
+ }
+
+ tidy_adjustment_set_valuex (adjustment, value);
+
+ if (emit_changed)
+ g_signal_emit (G_OBJECT (adjustment), signals[CHANGED], 0);
+
+ g_object_thaw_notify (G_OBJECT (adjustment));
+}
+
+void
+tidy_adjustment_set_values (TidyAdjustment *adjustment,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size)
+{
+ tidy_adjustment_set_valuesx (adjustment,
+ CLUTTER_FLOAT_TO_FIXED (value),
+ CLUTTER_FLOAT_TO_FIXED (lower),
+ CLUTTER_FLOAT_TO_FIXED (upper),
+ CLUTTER_FLOAT_TO_FIXED (step_increment),
+ CLUTTER_FLOAT_TO_FIXED (page_increment),
+ CLUTTER_FLOAT_TO_FIXED (page_size));
+}
+
+void
+tidy_adjustment_get_valuesx (TidyAdjustment *adjustment,
+ ClutterFixed *value,
+ ClutterFixed *lower,
+ ClutterFixed *upper,
+ ClutterFixed *step_increment,
+ ClutterFixed *page_increment,
+ ClutterFixed *page_size)
+{
+ TidyAdjustmentPrivate *priv;
+
+ g_return_if_fail (TIDY_IS_ADJUSTMENT (adjustment));
+
+ priv = adjustment->priv;
+
+ if (lower)
+ *lower = priv->lower;
+
+ if (upper)
+ *upper = priv->upper;
+
+ if (value)
+ *value = tidy_adjustment_get_valuex (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;
+}
+
+void
+tidy_adjustment_get_values (TidyAdjustment *adjustment,
+ gdouble *value,
+ gdouble *lower,
+ gdouble *upper,
+ gdouble *step_increment,
+ gdouble *page_increment,
+ gdouble *page_size)
+{
+ TidyAdjustmentPrivate *priv;
+
+ g_return_if_fail (TIDY_IS_ADJUSTMENT (adjustment));
+
+ priv = adjustment->priv;
+
+ if (lower)
+ *lower = CLUTTER_FIXED_TO_DOUBLE (priv->lower);
+
+ if (upper)
+ *upper = CLUTTER_FIXED_TO_DOUBLE (priv->upper);
+
+ if (value)
+ *value = CLUTTER_FIXED_TO_DOUBLE (tidy_adjustment_get_valuex (adjustment));
+
+ if (step_increment)
+ *step_increment = CLUTTER_FIXED_TO_DOUBLE (priv->step_increment);
+
+ if (page_increment)
+ *page_increment = CLUTTER_FIXED_TO_DOUBLE (priv->page_increment);
+
+ if (page_size)
+ *page_size = CLUTTER_FIXED_TO_DOUBLE (priv->page_size);
+}
+
+static void
+interpolation_new_frame_cb (ClutterTimeline *timeline,
+ gint frame_num,
+ TidyAdjustment *adjustment)
+{
+ TidyAdjustmentPrivate *priv = adjustment->priv;
+
+ priv->interpolation = NULL;
+ if (priv->elastic)
+ {
+ gdouble progress = clutter_alpha_get_alpha (priv->bounce_alpha) /
+ (gdouble)CLUTTER_ALPHA_MAX_ALPHA;
+ gdouble dx = CLUTTER_FIXED_TO_FLOAT (priv->old_position) +
+ CLUTTER_FIXED_TO_FLOAT (priv->new_position - priv->old_position) *
+ progress;
+ tidy_adjustment_set_value (adjustment, dx);
+ }
+ else
+ tidy_adjustment_set_valuex (adjustment,
+ priv->old_position +
+ clutter_qmulx (CLUTTER_INT_TO_FIXED (frame_num),
+ priv->dx));
+ priv->interpolation = timeline;
+}
+
+static void
+interpolation_completed_cb (ClutterTimeline *timeline,
+ TidyAdjustment *adjustment)
+{
+ TidyAdjustmentPrivate *priv = adjustment->priv;
+
+ stop_interpolation (adjustment);
+ tidy_adjustment_set_valuex (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
+tidy_adjustment_interpolatex (TidyAdjustment *adjustment,
+ ClutterFixed value,
+ guint n_frames,
+ guint fps)
+{
+ TidyAdjustmentPrivate *priv = adjustment->priv;
+
+ stop_interpolation (adjustment);
+
+ if (n_frames <= 1)
+ {
+ tidy_adjustment_set_valuex (adjustment, value);
+ return;
+ }
+
+ priv->old_position = priv->value;
+ priv->new_position = value;
+
+ priv->dx = clutter_qdivx (priv->new_position - priv->old_position,
+ CLUTTER_INT_TO_FIXED (n_frames));
+ priv->interpolation = clutter_timeline_new (n_frames, fps);
+
+ if (priv->elastic)
+ priv->bounce_alpha = clutter_alpha_new_full (priv->interpolation,
+ CLUTTER_ALPHA_SINE_INC,
+ NULL, NULL);
+
+ 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);
+}
+
+void
+tidy_adjustment_interpolate (TidyAdjustment *adjustment,
+ gdouble value,
+ guint n_frames,
+ guint fps)
+{
+ tidy_adjustment_interpolatex (adjustment,
+ CLUTTER_FLOAT_TO_FIXED (value),
+ n_frames,
+ fps);
+}
+
+gboolean
+tidy_adjustment_get_elastic (TidyAdjustment *adjustment)
+{
+ return adjustment->priv->elastic;
+}
+
+void
+tidy_adjustment_set_elastic (TidyAdjustment *adjustment,
+ gboolean elastic)
+{
+ adjustment->priv->elastic = elastic;
+}
+
+gboolean
+tidy_adjustment_clamp (TidyAdjustment *adjustment,
+ gboolean interpolate,
+ guint n_frames,
+ guint fps)
+{
+ TidyAdjustmentPrivate *priv = adjustment->priv;
+ ClutterFixed dest = priv->value;
+
+ if (priv->value < priv->lower)
+ dest = priv->lower;
+ if (priv->value > priv->upper - priv->page_size)
+ dest = priv->upper - priv->page_size;
+
+ if (dest != priv->value)
+ {
+ if (interpolate)
+ tidy_adjustment_interpolatex (adjustment,
+ dest,
+ n_frames,
+ fps);
+ else
+ tidy_adjustment_set_valuex (adjustment, dest);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
--- /dev/null
+/* tidy-adjustment.h: Adjustment object
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris@openedhand.com>, inspired by GtkAdjustment
+ */
+
+#ifndef __TIDY_ADJUSTMENT_H__
+#define __TIDY_ADJUSTMENT_H__
+
+#include <glib-object.h>
+#include <clutter/clutter-fixed.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_ADJUSTMENT (tidy_adjustment_get_type())
+#define TIDY_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_ADJUSTMENT, TidyAdjustment))
+#define TIDY_IS_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_ADJUSTMENT))
+#define TIDY_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_ADJUSTMENT, TidyAdjustmentClass))
+#define TIDY_IS_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_ADJUSTMENT))
+#define TIDY_ADJUSTMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_ADJUSTMENT, TidyAdjustmentClass))
+
+typedef struct _TidyAdjustment TidyAdjustment;
+typedef struct _TidyAdjustmentPrivate TidyAdjustmentPrivate;
+typedef struct _TidyAdjustmentClass TidyAdjustmentClass;
+
+/**
+ * TidyAdjustment:
+ *
+ * Class for handling an interval between to values. The contents of
+ * the #TidyAdjustment are private and should be accessed using the
+ * public API.
+ */
+struct _TidyAdjustment
+{
+ /*< private >*/
+ GObject parent_instance;
+
+ TidyAdjustmentPrivate *priv;
+};
+
+/**
+ * TidyAdjustmentClass
+ * @changed: Class handler for the ::changed signal.
+ *
+ * Base class for #TidyAdjustment.
+ */
+struct _TidyAdjustmentClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ /*< public >*/
+ void (* changed) (TidyAdjustment *adjustment);
+};
+
+GType tidy_adjustment_get_type (void) G_GNUC_CONST;
+
+TidyAdjustment *tidy_adjustment_new (gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size);
+TidyAdjustment *tidy_adjustment_newx (ClutterFixed value,
+ ClutterFixed lower,
+ ClutterFixed upper,
+ ClutterFixed step_increment,
+ ClutterFixed page_increment,
+ ClutterFixed page_size);
+gdouble tidy_adjustment_get_value (TidyAdjustment *adjustment);
+ClutterFixed tidy_adjustment_get_valuex (TidyAdjustment *adjustment);
+void tidy_adjustment_set_value (TidyAdjustment *adjustment,
+ gdouble value);
+void tidy_adjustment_set_valuex (TidyAdjustment *adjustment,
+ ClutterFixed value);
+void tidy_adjustment_clamp_page (TidyAdjustment *adjustment,
+ gdouble lower,
+ gdouble upper);
+void tidy_adjustment_clamp_pagex (TidyAdjustment *adjustment,
+ ClutterFixed lower,
+ ClutterFixed upper);
+void tidy_adjustment_set_values (TidyAdjustment *adjustment,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size);
+void tidy_adjustment_set_valuesx (TidyAdjustment *adjustment,
+ ClutterFixed value,
+ ClutterFixed lower,
+ ClutterFixed upper,
+ ClutterFixed step_increment,
+ ClutterFixed page_increment,
+ ClutterFixed page_size);
+void tidy_adjustment_get_values (TidyAdjustment *adjustment,
+ gdouble *value,
+ gdouble *lower,
+ gdouble *upper,
+ gdouble *step_increment,
+ gdouble *page_increment,
+ gdouble *page_size);
+void tidy_adjustment_get_valuesx (TidyAdjustment *adjustment,
+ ClutterFixed *value,
+ ClutterFixed *lower,
+ ClutterFixed *upper,
+ ClutterFixed *step_increment,
+ ClutterFixed *page_increment,
+ ClutterFixed *page_size);
+
+void tidy_adjustment_interpolate (TidyAdjustment *adjustment,
+ gdouble value,
+ guint n_frames,
+ guint fps);
+void tidy_adjustment_interpolatex (TidyAdjustment *adjustment,
+ ClutterFixed value,
+ guint n_frames,
+ guint fps);
+
+gboolean tidy_adjustment_get_elastic (TidyAdjustment *adjustment);
+void tidy_adjustment_set_elastic (TidyAdjustment *adjustment,
+ gboolean elastic);
+
+gboolean tidy_adjustment_clamp (TidyAdjustment *adjustment,
+ gboolean interpolate,
+ guint n_frames,
+ guint fps);
+
+G_END_DECLS
+
+#endif /* __TIDY_ADJUSTMENT_H__ */
+
--- /dev/null
+#ifndef __TIDY_DEBUG_H__
+#define __TIDY_DEBUG_H__
+
+#endif /* __TIDY_DEBUG_H__ */
--- /dev/null
+/*** BEGIN file-header ***/
+#include "tidy-enum-types.h"
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+#include "@filename@"
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type(void) {
+ static GType enum_type_id = 0;
+ if (G_UNLIKELY (!enum_type_id))
+ {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+ enum_type_id = g_@type@_register_static("@EnumName@", values);
+ }
+ return enum_type_id;
+}
+/*** END value-tail ***/
--- /dev/null
+/*** BEGIN file-header ***/
+#ifndef __TIDY_ENUM_TYPES_H__
+#define __TIDY_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* !__TIDY_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define TIDY_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+
+/*** END value-header ***/
--- /dev/null
+/* tidy-finger-scroll.c: Finger scrolling container actor
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris@openedhand.com>
+ */
+
+#include "tidy-finger-scroll.h"
+#include "tidy-enum-types.h"
+#include "tidy-marshal.h"
+#include "tidy-scrollable.h"
+#include "tidy-scroll-view.h"
+#include <clutter/clutter.h>
+#include <math.h>
+
+G_DEFINE_TYPE (TidyFingerScroll, tidy_finger_scroll, TIDY_TYPE_SCROLL_VIEW)
+
+#define FINGER_SCROLL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+ TIDY_TYPE_FINGER_SCROLL, \
+ TidyFingerScrollPrivate))
+
+typedef struct {
+ /* Units to store the origin of a click when scrolling */
+ ClutterUnit x;
+ ClutterUnit y;
+ GTimeVal time;
+} TidyFingerScrollMotion;
+
+struct _TidyFingerScrollPrivate
+{
+ /* Scroll mode */
+ TidyFingerScrollMode mode;
+
+ GArray *motion_buffer;
+ guint last_motion;
+
+ /* Variables for storing acceleration information for kinetic mode */
+ ClutterTimeline *deceleration_timeline;
+ ClutterUnit dx;
+ ClutterUnit dy;
+ ClutterFixed decel_rate;
+
+};
+
+enum {
+ PROP_MODE = 1,
+ PROP_DECEL_RATE,
+ PROP_BUFFER,
+};
+
+static void
+tidy_finger_scroll_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ TidyFingerScrollPrivate *priv = TIDY_FINGER_SCROLL (object)->priv;
+
+ switch (property_id)
+ {
+ case PROP_MODE :
+ g_value_set_enum (value, priv->mode);
+ break;
+ case PROP_DECEL_RATE :
+ g_value_set_double (value, CLUTTER_FIXED_TO_FLOAT (priv->decel_rate));
+ break;
+ case PROP_BUFFER :
+ g_value_set_uint (value, priv->motion_buffer->len);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+tidy_finger_scroll_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ TidyFingerScrollPrivate *priv = TIDY_FINGER_SCROLL (object)->priv;
+
+ switch (property_id)
+ {
+ case PROP_MODE :
+ priv->mode = g_value_get_enum (value);
+ g_object_notify (object, "mode");
+ break;
+ case PROP_DECEL_RATE :
+ priv->decel_rate = CLUTTER_FLOAT_TO_FIXED (g_value_get_double (value));
+ g_object_notify (object, "decel-rate");
+ break;
+ case PROP_BUFFER :
+ g_array_set_size (priv->motion_buffer, g_value_get_uint (value));
+ g_object_notify (object, "motion-buffer");
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+tidy_finger_scroll_dispose (GObject *object)
+{
+ TidyFingerScrollPrivate *priv = TIDY_FINGER_SCROLL (object)->priv;
+
+ if (priv->deceleration_timeline)
+ {
+ clutter_timeline_stop (priv->deceleration_timeline);
+ g_object_unref (priv->deceleration_timeline);
+ priv->deceleration_timeline = NULL;
+ }
+
+ G_OBJECT_CLASS (tidy_finger_scroll_parent_class)->dispose (object);
+}
+
+static void
+tidy_finger_scroll_finalize (GObject *object)
+{
+ TidyFingerScrollPrivate *priv = TIDY_FINGER_SCROLL (object)->priv;
+
+ g_array_free (priv->motion_buffer, TRUE);
+
+ G_OBJECT_CLASS (tidy_finger_scroll_parent_class)->finalize (object);
+}
+
+
+static void
+tidy_finger_scroll_class_init (TidyFingerScrollClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (TidyFingerScrollPrivate));
+
+ object_class->get_property = tidy_finger_scroll_get_property;
+ object_class->set_property = tidy_finger_scroll_set_property;
+ object_class->dispose = tidy_finger_scroll_dispose;
+ object_class->finalize = tidy_finger_scroll_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_MODE,
+ g_param_spec_enum ("mode",
+ "TidyFingerScrollMode",
+ "Scrolling mode",
+ TIDY_TYPE_FINGER_SCROLL_MODE,
+ TIDY_FINGER_SCROLL_MODE_PUSH,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_DECEL_RATE,
+ g_param_spec_double ("decel-rate",
+ "Deceleration rate",
+ "Rate at which the view "
+ "will decelerate in "
+ "kinetic mode.",
+ CLUTTER_FIXED_TO_FLOAT (CFX_ONE + CFX_MIN),
+ CLUTTER_FIXED_TO_FLOAT (CFX_MAX),
+ 1.1,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_BUFFER,
+ g_param_spec_uint ("motion-buffer",
+ "Motion buffer",
+ "Amount of motion "
+ "events to buffer",
+ 1, G_MAXUINT, 3,
+ G_PARAM_READWRITE));
+}
+
+static gboolean
+motion_event_cb (ClutterActor *actor,
+ ClutterMotionEvent *event,
+ TidyFingerScroll *scroll)
+{
+ ClutterUnit x, y;
+
+ TidyFingerScrollPrivate *priv = scroll->priv;
+
+ if (clutter_actor_transform_stage_point (actor,
+ CLUTTER_UNITS_FROM_DEVICE(event->x),
+ CLUTTER_UNITS_FROM_DEVICE(event->y),
+ &x, &y))
+ {
+ TidyFingerScrollMotion *motion;
+ ClutterActor *child =
+ tidy_scroll_view_get_child (TIDY_SCROLL_VIEW(scroll));
+
+ if (child)
+ {
+ ClutterFixed dx, dy;
+ TidyAdjustment *hadjust, *vadjust;
+
+ tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
+ &hadjust,
+ &vadjust);
+
+ motion = &g_array_index (priv->motion_buffer,
+ TidyFingerScrollMotion, priv->last_motion);
+ dx = CLUTTER_UNITS_TO_FIXED(motion->x - x) +
+ tidy_adjustment_get_valuex (hadjust);
+ dy = CLUTTER_UNITS_TO_FIXED(motion->y - y) +
+ tidy_adjustment_get_valuex (vadjust);
+
+ tidy_adjustment_set_valuex (hadjust, dx);
+ tidy_adjustment_set_valuex (vadjust, dy);
+ }
+
+ priv->last_motion ++;
+ if (priv->last_motion == priv->motion_buffer->len)
+ {
+ priv->motion_buffer = g_array_remove_index (priv->motion_buffer, 0);
+ g_array_set_size (priv->motion_buffer, priv->last_motion);
+ priv->last_motion --;
+ }
+
+ motion = &g_array_index (priv->motion_buffer,
+ TidyFingerScrollMotion, priv->last_motion);
+ motion->x = x;
+ motion->y = y;
+ g_get_current_time (&motion->time);
+ }
+
+ return TRUE;
+}
+
+static void
+clamp_adjustments (TidyFingerScroll *scroll)
+{
+ ClutterActor *child = tidy_scroll_view_get_child (TIDY_SCROLL_VIEW (scroll));
+
+ if (child)
+ {
+ guint fps, n_frames;
+ TidyAdjustment *hadj, *vadj;
+ gboolean snap;
+
+ tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
+ &hadj, &vadj);
+
+ /* FIXME: Hard-coded value here */
+ fps = clutter_get_default_frame_rate ();
+ n_frames = fps / 6;
+
+ snap = TRUE;
+ if (tidy_adjustment_get_elastic (hadj))
+ snap = !tidy_adjustment_clamp (hadj, TRUE, n_frames, fps);
+
+ /* Snap to the nearest step increment on hadjustment */
+ if (snap)
+ {
+ gdouble d, value, lower, step_increment;
+
+ tidy_adjustment_get_values (hadj, &value, &lower, NULL,
+ &step_increment, NULL, NULL);
+ d = (rint ((value - lower) / step_increment) *
+ step_increment) + lower;
+ tidy_adjustment_set_value (hadj, d);
+ }
+
+ snap = TRUE;
+ if (tidy_adjustment_get_elastic (vadj))
+ snap = !tidy_adjustment_clamp (vadj, TRUE, n_frames, fps);
+
+ /* Snap to the nearest step increment on vadjustment */
+ if (snap)
+ {
+ gdouble d, value, lower, step_increment;
+
+ tidy_adjustment_get_values (vadj, &value, &lower, NULL,
+ &step_increment, NULL, NULL);
+ d = (rint ((value - lower) / step_increment) *
+ step_increment) + lower;
+ tidy_adjustment_set_value (vadj, d);
+ }
+ }
+}
+
+static void
+deceleration_completed_cb (ClutterTimeline *timeline,
+ TidyFingerScroll *scroll)
+{
+ clamp_adjustments (scroll);
+ g_object_unref (timeline);
+ scroll->priv->deceleration_timeline = NULL;
+}
+
+static void
+deceleration_new_frame_cb (ClutterTimeline *timeline,
+ gint frame_num,
+ TidyFingerScroll *scroll)
+{
+ TidyFingerScrollPrivate *priv = scroll->priv;
+ ClutterActor *child = tidy_scroll_view_get_child (TIDY_SCROLL_VIEW(scroll));
+
+ if (child)
+ {
+ ClutterFixed value, lower, upper, page_size;
+ TidyAdjustment *hadjust, *vadjust;
+ gint i;
+ gboolean stop = TRUE;
+
+ tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
+ &hadjust,
+ &vadjust);
+
+ for (i = 0; i < clutter_timeline_get_delta (timeline, NULL); i++)
+ {
+ tidy_adjustment_set_valuex (hadjust,
+ priv->dx +
+ tidy_adjustment_get_valuex (hadjust));
+ tidy_adjustment_set_valuex (vadjust,
+ priv->dy +
+ tidy_adjustment_get_valuex (vadjust));
+ priv->dx = clutter_qdivx (priv->dx, priv->decel_rate);
+ priv->dy = clutter_qdivx (priv->dy, priv->decel_rate);
+ }
+
+ /* Check if we've hit the upper or lower bounds and stop the timeline */
+ tidy_adjustment_get_valuesx (hadjust, &value, &lower, &upper,
+ NULL, NULL, &page_size);
+ if (((priv->dx > 0) && (value < upper - page_size)) ||
+ ((priv->dx < 0) && (value > lower)))
+ stop = FALSE;
+
+ if (stop)
+ {
+ tidy_adjustment_get_valuesx (vadjust, &value, &lower, &upper,
+ NULL, NULL, &page_size);
+ if (((priv->dy > 0) && (value < upper - page_size)) ||
+ ((priv->dy < 0) && (value > lower)))
+ stop = FALSE;
+ }
+
+ if (stop)
+ {
+ clutter_timeline_stop (timeline);
+ deceleration_completed_cb (timeline, scroll);
+ }
+ }
+}
+
+static gboolean
+button_release_event_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ TidyFingerScroll *scroll)
+{
+ TidyFingerScrollPrivate *priv = scroll->priv;
+ ClutterActor *child = tidy_scroll_view_get_child (TIDY_SCROLL_VIEW(scroll));
+ gboolean decelerating = FALSE;
+
+ if (event->button != 1)
+ return FALSE;
+
+ g_signal_handlers_disconnect_by_func (actor,
+ motion_event_cb,
+ scroll);
+ g_signal_handlers_disconnect_by_func (actor,
+ button_release_event_cb,
+ scroll);
+
+ clutter_ungrab_pointer ();
+
+ if ((priv->mode == TIDY_FINGER_SCROLL_MODE_KINETIC) && (child))
+ {
+ ClutterUnit x, y;
+
+ if (clutter_actor_transform_stage_point (actor,
+ CLUTTER_UNITS_FROM_DEVICE(event->x),
+ CLUTTER_UNITS_FROM_DEVICE(event->y),
+ &x, &y))
+ {
+ ClutterUnit frac, x_origin, y_origin;
+ GTimeVal release_time, motion_time;
+ TidyAdjustment *hadjust, *vadjust;
+ glong time_diff;
+ gint i;
+
+ /* Get time delta */
+ g_get_current_time (&release_time);
+
+ /* Get average position/time of last x mouse events */
+ priv->last_motion ++;
+ x_origin = y_origin = 0;
+ motion_time = (GTimeVal){ 0, 0 };
+ for (i = 0; i < priv->last_motion; i++)
+ {
+ TidyFingerScrollMotion *motion =
+ &g_array_index (priv->motion_buffer, TidyFingerScrollMotion, i);
+
+ /* FIXME: This doesn't guard against overflows - Should
+ * either fix that, or calculate the correct maximum
+ * value for the buffer size
+ */
+ x_origin += motion->x;
+ y_origin += motion->y;
+ motion_time.tv_sec += motion->time.tv_sec;
+ motion_time.tv_usec += motion->time.tv_usec;
+ }
+ x_origin = CLUTTER_UNITS_FROM_FIXED (
+ clutter_qdivx (CLUTTER_UNITS_TO_FIXED (x_origin),
+ CLUTTER_INT_TO_FIXED (priv->last_motion)));
+ y_origin = CLUTTER_UNITS_FROM_FIXED (
+ clutter_qdivx (CLUTTER_UNITS_TO_FIXED (y_origin),
+ CLUTTER_INT_TO_FIXED (priv->last_motion)));
+ motion_time.tv_sec /= priv->last_motion;
+ motion_time.tv_usec /= priv->last_motion;
+
+ if (motion_time.tv_sec == release_time.tv_sec)
+ time_diff = release_time.tv_usec - motion_time.tv_usec;
+ else
+ time_diff = release_time.tv_usec +
+ (G_USEC_PER_SEC - motion_time.tv_usec);
+
+ /* Work out the fraction of 1/60th of a second that has elapsed */
+ frac = clutter_qdivx (CLUTTER_FLOAT_TO_FIXED (time_diff/1000.0),
+ CLUTTER_FLOAT_TO_FIXED (1000.0/60.0));
+
+ /* See how many units to move in 1/60th of a second */
+ priv->dx = CLUTTER_UNITS_FROM_FIXED(clutter_qdivx (
+ CLUTTER_UNITS_TO_FIXED(x_origin - x), frac));
+ priv->dy = CLUTTER_UNITS_FROM_FIXED(clutter_qdivx (
+ CLUTTER_UNITS_TO_FIXED(y_origin - y), frac));
+
+ /* Get adjustments to do step-increment snapping */
+ tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
+ &hadjust,
+ &vadjust);
+
+ if (ABS(CLUTTER_UNITS_TO_INT(priv->dx)) > 1 ||
+ ABS(CLUTTER_UNITS_TO_INT(priv->dy)) > 1)
+ {
+ gdouble value, lower, step_increment, d, a, x, y, n;
+
+ /* TODO: Convert this all to fixed point? */
+
+ /* We want n, where x / y^n < z,
+ * x = Distance to move per frame
+ * y = Deceleration rate
+ * z = maximum distance from target
+ *
+ * Rearrange to n = log (x / z) / log (y)
+ * To simplify, z = 1, so n = log (x) / log (y)
+ *
+ * As z = 1, this will cause stops to be slightly abrupt -
+ * add a constant 15 frames to compensate.
+ */
+ x = CLUTTER_FIXED_TO_FLOAT (MAX(ABS(priv->dx), ABS(priv->dy)));
+ y = CLUTTER_FIXED_TO_FLOAT (priv->decel_rate);
+ n = logf (x) / logf (y) + 15.0;
+
+ /* Now we have n, adjust dx/dy so that we finish on a step
+ * boundary.
+ *
+ * Distance moved, using the above variable names:
+ *
+ * d = x + x/y + x/y^2 + ... + x/y^n
+ *
+ * Using geometric series,
+ *
+ * d = (1 - 1/y^(n+1))/(1 - 1/y)*x
+ *
+ * Let a = (1 - 1/y^(n+1))/(1 - 1/y),
+ *
+ * d = a * x
+ *
+ * Find d and find its nearest page boundary, then solve for x
+ *
+ * x = d / a
+ */
+
+ /* Get adjustments, work out y^n */
+ a = (1.0 - 1.0 / pow (y, n + 1)) / (1.0 - 1.0 / y);
+
+ /* Solving for dx */
+ d = a * CLUTTER_UNITS_TO_FLOAT (priv->dx);
+ tidy_adjustment_get_values (hadjust, &value, &lower, NULL,
+ &step_increment, NULL, NULL);
+ d = ((rint (((value + d) - lower) / step_increment) *
+ step_increment) + lower) - value;
+ priv->dx = CLUTTER_UNITS_FROM_FLOAT (d / a);
+
+ /* Solving for dy */
+ d = a * CLUTTER_UNITS_TO_FLOAT (priv->dy);
+ tidy_adjustment_get_values (vadjust, &value, &lower, NULL,
+ &step_increment, NULL, NULL);
+ d = ((rint (((value + d) - lower) / step_increment) *
+ step_increment) + lower) - value;
+ priv->dy = CLUTTER_UNITS_FROM_FLOAT (d / a);
+
+ priv->deceleration_timeline = clutter_timeline_new ((gint)n, 60);
+ }
+ else
+ {
+ gdouble value, lower, step_increment, d, a, y;
+
+ /* Start a short effects timeline to snap to the nearest step
+ * boundary (see equations above)
+ */
+ y = CLUTTER_FIXED_TO_FLOAT (priv->decel_rate);
+ a = (1.0 - 1.0 / pow (y, 4 + 1)) / (1.0 - 1.0 / y);
+
+ tidy_adjustment_get_values (hadjust, &value, &lower, NULL,
+ &step_increment, NULL, NULL);
+ d = ((rint ((value - lower) / step_increment) *
+ step_increment) + lower) - value;
+ priv->dx = CLUTTER_UNITS_FROM_FLOAT (d / a);
+
+ tidy_adjustment_get_values (vadjust, &value, &lower, NULL,
+ &step_increment, NULL, NULL);
+ d = ((rint ((value - lower) / step_increment) *
+ step_increment) + lower) - value;
+ priv->dy = CLUTTER_UNITS_FROM_FLOAT (d / a);
+
+ priv->deceleration_timeline = clutter_timeline_new (4, 60);
+ }
+
+ g_signal_connect (priv->deceleration_timeline, "new_frame",
+ G_CALLBACK (deceleration_new_frame_cb), scroll);
+ g_signal_connect (priv->deceleration_timeline, "completed",
+ G_CALLBACK (deceleration_completed_cb), scroll);
+ clutter_timeline_start (priv->deceleration_timeline);
+ decelerating = TRUE;
+ }
+ }
+
+ /* Reset motion event buffer */
+ priv->last_motion = 0;
+
+ if (!decelerating)
+ {
+ clamp_adjustments (scroll);
+ }
+
+ /* Pass through events to children.
+ * FIXME: this probably breaks click-count.
+ */
+ clutter_event_put ((ClutterEvent *)event);
+
+ return TRUE;
+}
+
+static gboolean
+after_event_cb (TidyFingerScroll *scroll)
+{
+ /* Check the pointer grab - if something else has grabbed it - for example,
+ * a scroll-bar or some such, don't do our funky stuff.
+ */
+ if (clutter_get_pointer_grab () != CLUTTER_ACTOR (scroll))
+ {
+ g_signal_handlers_disconnect_by_func (scroll,
+ motion_event_cb,
+ scroll);
+ g_signal_handlers_disconnect_by_func (scroll,
+ button_release_event_cb,
+ scroll);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+captured_event_cb (ClutterActor *actor,
+ ClutterEvent *event,
+ TidyFingerScroll *scroll)
+{
+ TidyFingerScrollPrivate *priv = scroll->priv;
+
+ if (event->type == CLUTTER_BUTTON_PRESS)
+ {
+ TidyFingerScrollMotion *motion;
+ ClutterButtonEvent *bevent = (ClutterButtonEvent *)event;
+
+ /* Reset motion buffer */
+ priv->last_motion = 0;
+ motion = &g_array_index (priv->motion_buffer, TidyFingerScrollMotion, 0);
+
+ if ((bevent->button == 1) &&
+ (clutter_actor_transform_stage_point (actor,
+ CLUTTER_UNITS_FROM_DEVICE(bevent->x),
+ CLUTTER_UNITS_FROM_DEVICE(bevent->y),
+ &motion->x, &motion->y)))
+ {
+ g_get_current_time (&motion->time);
+
+ if (priv->deceleration_timeline)
+ {
+ clutter_timeline_stop (priv->deceleration_timeline);
+ g_object_unref (priv->deceleration_timeline);
+ priv->deceleration_timeline = NULL;
+ }
+
+ clutter_grab_pointer (actor);
+
+ /* Add a high priority idle to check the grab after the event
+ * emission is finished.
+ */
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE,
+ (GSourceFunc)after_event_cb,
+ scroll,
+ NULL);
+
+ g_signal_connect (actor,
+ "motion-event",
+ G_CALLBACK (motion_event_cb),
+ scroll);
+ g_signal_connect (actor,
+ "button-release-event",
+ G_CALLBACK (button_release_event_cb),
+ scroll);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+tidy_finger_scroll_init (TidyFingerScroll *self)
+{
+ ClutterActor *scrollbar;
+ TidyFingerScrollPrivate *priv = self->priv = FINGER_SCROLL_PRIVATE (self);
+
+ priv->motion_buffer = g_array_sized_new (FALSE, TRUE,
+ sizeof (TidyFingerScrollMotion), 3);
+ g_array_set_size (priv->motion_buffer, 3);
+ priv->decel_rate = CLUTTER_FLOAT_TO_FIXED (1.1f);
+
+ clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
+ g_signal_connect (CLUTTER_ACTOR (self),
+ "captured-event",
+ G_CALLBACK (captured_event_cb),
+ self);
+
+
+}
+
+ClutterActor *
+tidy_finger_scroll_new (TidyFingerScrollMode mode)
+{
+ return CLUTTER_ACTOR (g_object_new (TIDY_TYPE_FINGER_SCROLL,
+ "mode", mode, NULL));
+}
+
+void
+tidy_finger_scroll_stop (TidyFingerScroll *scroll)
+{
+ TidyFingerScrollPrivate *priv;
+
+ g_return_if_fail (TIDY_IS_FINGER_SCROLL (scroll));
+
+ priv = scroll->priv;
+
+ if (priv->deceleration_timeline)
+ {
+ clutter_timeline_stop (priv->deceleration_timeline);
+ g_object_unref (priv->deceleration_timeline);
+ priv->deceleration_timeline = NULL;
+ }
+}
--- /dev/null
+/* tidy-finger-scroll.h: Finger scrolling container actor
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris@openedhand.com>
+ */
+
+#ifndef __TIDY_FINGER_SCROLL_H__
+#define __TIDY_FINGER_SCROLL_H__
+
+#include <glib-object.h>
+#include <tidy/tidy-scroll-view.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_FINGER_SCROLL (tidy_finger_scroll_get_type())
+#define TIDY_FINGER_SCROLL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_FINGER_SCROLL, TidyFingerScroll))
+#define TIDY_IS_FINGER_SCROLL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_FINGER_SCROLL))
+#define TIDY_FINGER_SCROLL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_FINGER_SCROLL, TidyFingerScrollClass))
+#define TIDY_IS_FINGER_SCROLL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_FINGER_SCROLL))
+#define TIDY_FINGER_SCROLL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_FINGER_SCROLL, TidyFingerScrollClass))
+
+/**
+ * TidyFingerScrollMode:
+ * @TIDY_FINGER_SCROLL_MODE_PUSH: Non-kinetic scrolling
+ * @TIDY_FINGER_SCROLL_MODE_KINETIC: Kinetic scrolling
+ *
+ * Type of scrolling.
+ */
+typedef enum {
+ TIDY_FINGER_SCROLL_MODE_PUSH,
+ TIDY_FINGER_SCROLL_MODE_KINETIC
+} TidyFingerScrollMode;
+
+typedef struct _TidyFingerScroll TidyFingerScroll;
+typedef struct _TidyFingerScrollPrivate TidyFingerScrollPrivate;
+typedef struct _TidyFingerScrollClass TidyFingerScrollClass;
+
+struct _TidyFingerScroll
+{
+ /*< private >*/
+ TidyScrollView parent_instance;
+
+ TidyFingerScrollPrivate *priv;
+};
+
+struct _TidyFingerScrollClass
+{
+ TidyScrollViewClass parent_class;
+};
+
+GType tidy_finger_scroll_get_type (void) G_GNUC_CONST;
+
+ClutterActor *tidy_finger_scroll_new (TidyFingerScrollMode mode);
+
+void tidy_finger_scroll_stop (TidyFingerScroll *scroll);
+
+G_END_DECLS
+
+#endif /* __TIDY_FINGER_SCROLL_H__ */
--- /dev/null
+#include "tidy-marshal.h"
+
+#include <glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:OBJECT (./tidy-marshal.list:1) */
+
+/* VOID:VOID (./tidy-marshal.list:2) */
+
+/* VOID:PARAM (./tidy-marshal.list:3) */
+
+/* VOID:UINT (./tidy-marshal.list:4) */
+
+/* VOID:UINT,UINT (./tidy-marshal.list:5) */
+void
+_tidy_marshal_VOID__UINT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__UINT_UINT) (gpointer data1,
+ guint arg_1,
+ guint arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__UINT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_uint (param_values + 1),
+ g_marshal_value_peek_uint (param_values + 2),
+ data2);
+}
+
+/* VOID:OBJECT,OBJECT (./tidy-marshal.list:6) */
+void
+_tidy_marshal_VOID__OBJECT_OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__OBJECT_OBJECT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_object (param_values + 2),
+ data2);
+}
+
--- /dev/null
+
+#ifndef ___tidy_marshal_MARSHAL_H__
+#define ___tidy_marshal_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID:OBJECT (./tidy-marshal.list:1) */
+#define _tidy_marshal_VOID__OBJECT g_cclosure_marshal_VOID__OBJECT
+
+/* VOID:VOID (./tidy-marshal.list:2) */
+#define _tidy_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID
+
+/* VOID:PARAM (./tidy-marshal.list:3) */
+#define _tidy_marshal_VOID__PARAM g_cclosure_marshal_VOID__PARAM
+
+/* VOID:UINT (./tidy-marshal.list:4) */
+#define _tidy_marshal_VOID__UINT g_cclosure_marshal_VOID__UINT
+
+/* VOID:UINT,UINT (./tidy-marshal.list:5) */
+extern void _tidy_marshal_VOID__UINT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID:OBJECT,OBJECT (./tidy-marshal.list:6) */
+extern void _tidy_marshal_VOID__OBJECT_OBJECT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+G_END_DECLS
+
+#endif /* ___tidy_marshal_MARSHAL_H__ */
+
--- /dev/null
+VOID:OBJECT
+VOID:VOID
+VOID:PARAM
+VOID:UINT
+VOID:UINT,UINT
+VOID:OBJECT,OBJECT
--- /dev/null
+/* tidy-private.h: Private declarations
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TIDY_PRIVATE_H__
+#define __TIDY_PRIVATE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define I_(str) (g_intern_static_string ((str)))
+
+#define TIDY_PARAM_READABLE \
+ (G_PARAM_READABLE | \
+ G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
+
+#define TIDY_PARAM_READWRITE \
+ (G_PARAM_READABLE | G_PARAM_WRITABLE | \
+ G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
+
+G_END_DECLS
+
+#endif /* __TIDY_PRIVATE_H__ */
--- /dev/null
+/* tidy-scroll-view.h: Container with scroll-bars
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris@openedhand.com>
+ */
+
+#include "tidy-scroll-view.h"
+#include "tidy-marshal.h"
+#include "tidy-scrollable.h"
+#include <clutter/clutter.h>
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (TidyScrollView, tidy_scroll_view, CLUTTER_TYPE_ACTOR,
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+ clutter_container_iface_init))
+
+#define SCROLL_VIEW_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+ TIDY_TYPE_SCROLL_VIEW, \
+ TidyScrollViewPrivate))
+
+struct _TidyScrollViewPrivate
+{
+ ClutterActor *child;
+
+ TidyAdjustment *hadjustment;
+ TidyAdjustment *vadjustment;
+};
+
+enum {
+ PROP_0,
+ PROP_CHILD,
+};
+
+static void
+tidy_scroll_view_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ TidyScrollViewPrivate *priv = ((TidyScrollView *)object)->priv;
+
+ switch (property_id)
+ {
+ case PROP_CHILD :
+ g_value_set_object (value, priv->child);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+tidy_scroll_view_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+tidy_scroll_view_dispose (GObject *object)
+{
+ TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (object)->priv;
+
+ if (priv->child)
+ clutter_container_remove_actor (CLUTTER_CONTAINER (object), priv->child);
+
+ G_OBJECT_CLASS (tidy_scroll_view_parent_class)->dispose (object);
+}
+
+static void
+tidy_scroll_view_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (tidy_scroll_view_parent_class)->finalize (object);
+}
+
+static void
+tidy_scroll_view_paint (ClutterActor *actor)
+{
+ TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (actor)->priv;
+
+ if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
+ clutter_actor_paint (priv->child);
+}
+
+static void
+tidy_scroll_view_pick (ClutterActor *actor, const ClutterColor *color)
+{
+ /* Chain up so we get a bounding box pained (if we are reactive) */
+ CLUTTER_ACTOR_CLASS (tidy_scroll_view_parent_class)->pick (actor, color);
+
+ /* Trigger pick on children */
+ tidy_scroll_view_paint (actor);
+}
+
+static void
+tidy_scroll_view_get_preferred_width (ClutterActor *actor,
+ ClutterUnit for_height,
+ ClutterUnit *min_width_p,
+ ClutterUnit *natural_width_p)
+{
+
+ TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (actor)->priv;
+
+ if (!priv->child)
+ return;
+
+
+ /* Our natural width is the natural width of the child */
+ clutter_actor_get_preferred_width (priv->child,
+ for_height,
+ NULL,
+ natural_width_p);
+
+}
+
+static void
+tidy_scroll_view_get_preferred_height (ClutterActor *actor,
+ ClutterUnit for_width,
+ ClutterUnit *min_height_p,
+ ClutterUnit *natural_height_p)
+{
+
+ TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (actor)->priv;
+
+ if (!priv->child)
+ return;
+
+
+ /* Our natural height is the natural height of the child */
+ clutter_actor_get_preferred_height (priv->child,
+ for_width,
+ NULL,
+ natural_height_p);
+}
+
+static void
+tidy_scroll_view_allocate (ClutterActor *actor,
+ const ClutterActorBox *box,
+ gboolean absolute_origin_changed)
+{
+ ClutterActorBox child_box;
+
+ TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (actor)->priv;
+
+ /* Chain up */
+ CLUTTER_ACTOR_CLASS (tidy_scroll_view_parent_class)->
+ allocate (actor, box, absolute_origin_changed);
+
+
+
+
+ /* Child */
+ child_box.x1 = 0;
+ child_box.x2 = box->x2 - box->x1;
+ child_box.y1 = 0;
+ child_box.y2 = box->y2 - box->y1;
+
+
+ if (priv->child)
+ {
+ clutter_actor_allocate (priv->child, &child_box, absolute_origin_changed);
+ clutter_actor_set_clipu (priv->child,
+ child_box.x1,
+ child_box.y1,
+ child_box.x2 - child_box.x1,
+ child_box.y2 - child_box.y1);
+ }
+
+}
+
+static void
+tidy_scroll_view_class_init (TidyScrollViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (TidyScrollViewPrivate));
+
+ object_class->get_property = tidy_scroll_view_get_property;
+ object_class->set_property = tidy_scroll_view_set_property;
+ object_class->dispose= tidy_scroll_view_dispose;
+ object_class->finalize = tidy_scroll_view_finalize;
+
+ actor_class->paint = tidy_scroll_view_paint;
+ actor_class->pick = tidy_scroll_view_pick;
+ actor_class->get_preferred_width = tidy_scroll_view_get_preferred_width;
+ actor_class->get_preferred_height = tidy_scroll_view_get_preferred_height;
+ actor_class->allocate = tidy_scroll_view_allocate;
+
+ g_object_class_install_property (object_class,
+ PROP_CHILD,
+ g_param_spec_object ("child",
+ "ClutterActor",
+ "Child actor",
+ CLUTTER_TYPE_ACTOR,
+ G_PARAM_READABLE));
+}
+
+static void
+tidy_scroll_view_init (TidyScrollView *self)
+{
+ TidyScrollViewPrivate *priv = self->priv = SCROLL_VIEW_PRIVATE (self);
+
+}
+
+static void
+tidy_scroll_view_add_actor (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ TidyScrollView *self = TIDY_SCROLL_VIEW (container);
+ TidyScrollViewPrivate *priv = self->priv;
+
+ if (priv->child)
+ {
+ g_warning ("Attempting to add an actor of type %s to "
+ "a TidyScrollView that already contains "
+ "an actor of type %s.",
+ g_type_name (G_OBJECT_TYPE (actor)),
+ g_type_name (G_OBJECT_TYPE (priv->child)));
+ }
+ else
+ {
+ if (TIDY_IS_SCROLLABLE(actor))
+ {
+ priv->child = actor;
+ clutter_actor_set_parent (actor, CLUTTER_ACTOR (container));
+
+ /* Notify that child has been set */
+ g_signal_emit_by_name (container, "actor-added", priv->child);
+ g_object_notify (G_OBJECT (container), "child");
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
+ }
+ else
+ {
+ g_warning ("Attempting to add an actor of type %s to "
+ "a TidyScrollView, but the actor does "
+ "not implement TidyScrollable.",
+ g_type_name (G_OBJECT_TYPE (actor)));
+ }
+ }
+}
+
+static void
+tidy_scroll_view_remove_actor (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (container)->priv;
+
+ if (actor == priv->child)
+ {
+ g_object_ref (priv->child);
+
+
+ clutter_actor_unparent (priv->child);
+
+ g_signal_emit_by_name (container, "actor-removed", priv->child);
+
+ g_object_unref (priv->child);
+ priv->child = NULL;
+
+ g_object_notify (G_OBJECT (container), "child");
+
+ if (CLUTTER_ACTOR_IS_VISIBLE (container))
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
+ }
+}
+
+static void
+tidy_scroll_view_foreach (ClutterContainer *container,
+ ClutterCallback callback,
+ gpointer callback_data)
+{
+ TidyScrollViewPrivate *priv = TIDY_SCROLL_VIEW (container)->priv;
+
+ if (priv->child)
+ callback (priv->child, callback_data);
+}
+
+static void
+tidy_scroll_view_lower (ClutterContainer *container,
+ ClutterActor *actor,
+ ClutterActor *sibling)
+{
+ /* single child */
+}
+
+static void
+tidy_scroll_view_raise (ClutterContainer *container,
+ ClutterActor *actor,
+ ClutterActor *sibling)
+{
+ /* single child */
+}
+
+static void
+tidy_scroll_view_sort_depth_order (ClutterContainer *container)
+{
+ /* single child */
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+ iface->add = tidy_scroll_view_add_actor;
+ iface->remove = tidy_scroll_view_remove_actor;
+ iface->foreach = tidy_scroll_view_foreach;
+ iface->lower = tidy_scroll_view_lower;
+ iface->raise = tidy_scroll_view_raise;
+ iface->sort_depth_order = tidy_scroll_view_sort_depth_order;
+}
+
+ClutterActor *
+tidy_scroll_view_new (void)
+{
+ return CLUTTER_ACTOR (g_object_new (TIDY_TYPE_SCROLL_VIEW, NULL));
+}
+
+ClutterActor *
+tidy_scroll_view_get_child (TidyScrollView *scroll)
+{
+ TidyScrollViewPrivate *priv;
+
+ g_return_val_if_fail (TIDY_IS_SCROLL_VIEW (scroll), NULL);
+
+ priv = scroll->priv;
+
+ return priv->child;
+}
--- /dev/null
+/* tidy-scroll-view.h: Container with scroll-bars
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris@openedhand.com>
+ */
+
+#ifndef __TIDY_SCROLL_VIEW_H__
+#define __TIDY_SCROLL_VIEW_H__
+
+#include <glib-object.h>
+#include <clutter/clutter-actor.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_SCROLL_VIEW (tidy_scroll_view_get_type())
+#define TIDY_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_SCROLL_VIEW, TidyScrollView))
+#define TIDY_IS_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_SCROLL_VIEW))
+#define TIDY_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_SCROLL_VIEW, TidyScrollViewClass))
+#define TIDY_IS_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_SCROLL_VIEW))
+#define TIDY_SCROLL_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_SCROLL_VIEW, TidyScrollViewClass))
+
+typedef struct _TidyScrollView TidyScrollView;
+typedef struct _TidyScrollViewPrivate TidyScrollViewPrivate;
+typedef struct _TidyScrollViewClass TidyScrollViewClass;
+
+struct _TidyScrollView
+{
+ /*< private >*/
+ ClutterActor parent;
+
+ TidyScrollViewPrivate *priv;
+};
+
+struct _TidyScrollViewClass
+{
+ ClutterActorClass parent_class;
+};
+
+GType tidy_scroll_view_get_type (void) G_GNUC_CONST;
+
+ClutterActor *tidy_scroll_view_new (void);
+
+ClutterActor *tidy_scroll_view_get_hscroll_bar (TidyScrollView *scroll);
+ClutterActor *tidy_scroll_view_get_vscroll_bar (TidyScrollView *scroll);
+ClutterActor *tidy_scroll_view_get_child (TidyScrollView *scroll);
+
+G_END_DECLS
+
+#endif /* __TIDY_SCROLL_VIEW_H__ */
--- /dev/null
+/* tidy-scrollable.c: Scrollable interface
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris@openedhand.com>
+ */
+
+#include "tidy-scrollable.h"
+
+static void
+tidy_scrollable_base_init (gpointer g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ g_object_interface_install_property (g_iface,
+ g_param_spec_object ("hadjustment",
+ "TidyAdjustment",
+ "Horizontal adjustment",
+ TIDY_TYPE_ADJUSTMENT,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property (g_iface,
+ g_param_spec_object ("vadjustment",
+ "TidyAdjustment",
+ "Vertical adjustment",
+ TIDY_TYPE_ADJUSTMENT,
+ G_PARAM_READWRITE));
+
+ initialized = TRUE;
+ }
+}
+
+GType
+tidy_scrollable_get_type (void)
+{
+ static GType type = 0;
+ if (type == 0)
+ {
+ static const GTypeInfo info =
+ {
+ sizeof (TidyScrollableInterface),
+ tidy_scrollable_base_init, /* base_init */
+ NULL,
+ };
+ type = g_type_register_static (G_TYPE_INTERFACE,
+ "TidyScrollable", &info, 0);
+ }
+ return type;
+}
+
+void
+tidy_scrollable_set_adjustments (TidyScrollable *scrollable,
+ TidyAdjustment *hadjustment,
+ TidyAdjustment *vadjustment)
+{
+ TIDY_SCROLLABLE_GET_INTERFACE (scrollable)->set_adjustments (scrollable,
+ hadjustment,
+ vadjustment);
+}
+
+void
+tidy_scrollable_get_adjustments (TidyScrollable *scrollable,
+ TidyAdjustment **hadjustment,
+ TidyAdjustment **vadjustment)
+{
+ TIDY_SCROLLABLE_GET_INTERFACE (scrollable)->get_adjustments (scrollable,
+ hadjustment,
+ vadjustment);
+}
+
--- /dev/null
+/* tidy-scrollable.h: Scrollable interface
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris@openedhand.com>
+ */
+
+#ifndef __TIDY_SCROLLABLE_H__
+#define __TIDY_SCROLLABLE_H__
+
+#include <glib-object.h>
+#include <tidy/tidy-adjustment.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_SCROLLABLE (tidy_scrollable_get_type ())
+#define TIDY_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_SCROLLABLE, TidyScrollable))
+#define TIDY_IS_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_SCROLLABLE))
+#define TIDY_SCROLLABLE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), TIDY_TYPE_SCROLLABLE, TidyScrollableInterface))
+
+typedef struct _TidyScrollable TidyScrollable; /* Dummy object */
+typedef struct _TidyScrollableInterface TidyScrollableInterface;
+
+struct _TidyScrollableInterface
+{
+ GTypeInterface parent;
+
+ void (* set_adjustments) (TidyScrollable *scrollable,
+ TidyAdjustment *hadjustment,
+ TidyAdjustment *vadjustment);
+ void (* get_adjustments) (TidyScrollable *scrollable,
+ TidyAdjustment **hadjustment,
+ TidyAdjustment **vadjustment);
+};
+
+GType tidy_scrollable_get_type (void) G_GNUC_CONST;
+
+void tidy_scrollable_set_adjustments (TidyScrollable *scrollable,
+ TidyAdjustment *hadjustment,
+ TidyAdjustment *vadjustment);
+void tidy_scrollable_get_adjustments (TidyScrollable *scrollable,
+ TidyAdjustment **hadjustment,
+ TidyAdjustment **vadjustment);
+
+G_END_DECLS
+
+#endif /* __TIDY_SCROLLABLE_H__ */
+
--- /dev/null
+/* tidy-viewport.c: Viewport actor
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris@openedhand.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <clutter/clutter.h>
+
+#include "tidy-viewport.h"
+#include "tidy-adjustment.h"
+#include "tidy-scrollable.h"
+#include "tidy-private.h"
+
+static void scrollable_interface_init (TidyScrollableInterface *iface);
+
+static void scrollable_set_adjustments (TidyScrollable *scrollable,
+ TidyAdjustment *hadjustment,
+ TidyAdjustment *vadjustment);
+
+static void scrollable_get_adjustments (TidyScrollable *scrollable,
+ TidyAdjustment **hadjustment,
+ TidyAdjustment **vadjustment);
+
+G_DEFINE_TYPE_WITH_CODE (TidyViewport, tidy_viewport, CLUTTER_TYPE_GROUP,
+ G_IMPLEMENT_INTERFACE (TIDY_TYPE_SCROLLABLE,
+ scrollable_interface_init))
+
+#define VIEWPORT_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TIDY_TYPE_VIEWPORT, \
+ TidyViewportPrivate))
+
+struct _TidyViewportPrivate
+{
+ ClutterUnit x;
+ ClutterUnit y;
+ ClutterUnit z;
+
+ TidyAdjustment *hadjustment;
+ TidyAdjustment *vadjustment;
+
+ gboolean sync_adjustments;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_X_ORIGIN,
+ PROP_Y_ORIGIN,
+ PROP_Z_ORIGIN,
+ PROP_HADJUST,
+ PROP_VADJUST,
+ PROP_SYNC_ADJUST
+};
+
+static void
+tidy_viewport_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TidyAdjustment *adjustment;
+
+ TidyViewportPrivate *priv = TIDY_VIEWPORT (object)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_X_ORIGIN:
+ g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE (priv->x));
+ break;
+
+ case PROP_Y_ORIGIN:
+ g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE (priv->y));
+ break;
+
+ case PROP_Z_ORIGIN:
+ g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE (priv->z));
+ break;
+
+ case PROP_HADJUST :
+ scrollable_get_adjustments (TIDY_SCROLLABLE (object), &adjustment, NULL);
+ g_value_set_object (value, adjustment);
+ break;
+
+ case PROP_VADJUST :
+ scrollable_get_adjustments (TIDY_SCROLLABLE (object), NULL, &adjustment);
+ g_value_set_object (value, adjustment);
+ break;
+
+ case PROP_SYNC_ADJUST :
+ g_value_set_boolean (value, priv->sync_adjustments);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tidy_viewport_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TidyViewport *viewport = TIDY_VIEWPORT (object);
+ TidyViewportPrivate *priv = viewport->priv;
+
+ switch (prop_id)
+ {
+ case PROP_X_ORIGIN:
+ tidy_viewport_set_originu (viewport,
+ g_value_get_int (value),
+ priv->y,
+ priv->z);
+ break;
+
+ case PROP_Y_ORIGIN:
+ tidy_viewport_set_originu (viewport,
+ priv->x,
+ g_value_get_int (value),
+ priv->z);
+ break;
+
+ case PROP_Z_ORIGIN:
+ tidy_viewport_set_originu (viewport,
+ priv->x,
+ priv->y,
+ g_value_get_int (value));
+ break;
+
+ case PROP_HADJUST :
+ scrollable_set_adjustments (TIDY_SCROLLABLE (object),
+ g_value_get_object (value),
+ priv->vadjustment);
+ break;
+
+ case PROP_VADJUST :
+ scrollable_set_adjustments (TIDY_SCROLLABLE (object),
+ priv->hadjustment,
+ g_value_get_object (value));
+ break;
+
+ case PROP_SYNC_ADJUST :
+ priv->sync_adjustments = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tidy_viewport_dispose (GObject *gobject)
+{
+ TidyViewportPrivate *priv = TIDY_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 (tidy_viewport_parent_class)->dispose (gobject);
+}
+
+static void
+tidy_viewport_paint (ClutterActor *self)
+{
+ TidyViewportPrivate *priv = TIDY_VIEWPORT (self)->priv;
+
+ cogl_push_matrix ();
+
+ cogl_translatex (CLUTTER_UNITS_TO_FIXED (priv->x) * -1,
+ CLUTTER_UNITS_TO_FIXED (priv->y) * -1,
+ CLUTTER_UNITS_TO_FIXED (priv->z) * -1);
+
+ CLUTTER_ACTOR_CLASS (tidy_viewport_parent_class)->paint (self);
+
+ cogl_pop_matrix ();
+}
+
+static void
+tidy_viewport_pick (ClutterActor *self,
+ const ClutterColor *color)
+{
+ tidy_viewport_paint (self);
+}
+
+static void
+tidy_viewport_allocate (ClutterActor *self,
+ const ClutterActorBox *box,
+ gboolean absolute_origin_changed)
+{
+ ClutterFixed prev_value;
+
+ TidyViewportPrivate *priv = TIDY_VIEWPORT (self)->priv;
+
+ /* Chain up */
+ CLUTTER_ACTOR_CLASS (tidy_viewport_parent_class)->
+ allocate (self, box, absolute_origin_changed);
+
+ /* Refresh adjustments */
+ if (priv->sync_adjustments)
+ {
+ if (priv->hadjustment)
+ {
+ g_object_set (G_OBJECT (priv->hadjustment),
+ "lower", 0.0,
+ "upper", CLUTTER_UNITS_TO_FLOAT (box->x2 - box->x1),
+ NULL);
+
+ /* Make sure value is clamped */
+ prev_value = tidy_adjustment_get_valuex (priv->hadjustment);
+ tidy_adjustment_set_valuex (priv->hadjustment, prev_value);
+ }
+
+ if (priv->vadjustment)
+ {
+ g_object_set (G_OBJECT (priv->vadjustment),
+ "lower", 0.0,
+ "upper", CLUTTER_UNITS_TO_FLOAT (box->y2 - box->y1),
+ NULL);
+
+ prev_value = tidy_adjustment_get_valuex (priv->vadjustment);
+ tidy_adjustment_set_valuex (priv->vadjustment, prev_value);
+ }
+ }
+}
+
+static void
+tidy_viewport_class_init (TidyViewportClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (TidyViewportPrivate));
+
+ gobject_class->get_property = tidy_viewport_get_property;
+ gobject_class->set_property = tidy_viewport_set_property;
+ gobject_class->dispose = tidy_viewport_dispose;
+
+ actor_class->paint = tidy_viewport_paint;
+ actor_class->pick = tidy_viewport_pick;
+ actor_class->allocate = tidy_viewport_allocate;
+
+ g_object_class_install_property (gobject_class,
+ PROP_X_ORIGIN,
+ g_param_spec_int ("x-origin",
+ "X Origin",
+ "Origin's X coordinate in pixels",
+ -G_MAXINT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_Y_ORIGIN,
+ g_param_spec_int ("y-origin",
+ "Y Origin",
+ "Origin's Y coordinate in pixels",
+ -G_MAXINT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_Z_ORIGIN,
+ g_param_spec_int ("z-origin",
+ "Z Origin",
+ "Origin's Z coordinate in pixels",
+ -G_MAXINT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_SYNC_ADJUST,
+ g_param_spec_boolean ("sync-adjustments",
+ "Synchronise "
+ "adjustments",
+ "Whether to "
+ "synchronise "
+ "adjustments with "
+ "viewport size",
+ TRUE,
+ G_PARAM_READWRITE));
+
+ g_object_class_override_property (gobject_class,
+ PROP_HADJUST,
+ "hadjustment");
+
+ g_object_class_override_property (gobject_class,
+ PROP_VADJUST,
+ "vadjustment");
+}
+
+static void
+hadjustment_value_notify_cb (TidyAdjustment *adjustment,
+ GParamSpec *pspec,
+ TidyViewport *viewport)
+{
+ TidyViewportPrivate *priv = viewport->priv;
+ ClutterFixed value;
+
+ value = tidy_adjustment_get_valuex (adjustment);
+
+ tidy_viewport_set_originu (viewport,
+ CLUTTER_UNITS_FROM_FIXED (value),
+ priv->y,
+ priv->z);
+}
+
+static void
+vadjustment_value_notify_cb (TidyAdjustment *adjustment, GParamSpec *arg1,
+ TidyViewport *viewport)
+{
+ TidyViewportPrivate *priv = viewport->priv;
+ ClutterFixed value;
+
+ value = tidy_adjustment_get_valuex (adjustment);
+
+ tidy_viewport_set_originu (viewport,
+ priv->x,
+ CLUTTER_UNITS_FROM_FIXED (value),
+ priv->z);
+}
+
+static void
+scrollable_set_adjustments (TidyScrollable *scrollable,
+ TidyAdjustment *hadjustment,
+ TidyAdjustment *vadjustment)
+{
+ TidyViewportPrivate *priv = TIDY_VIEWPORT (scrollable)->priv;
+
+ if (hadjustment != priv->hadjustment)
+ {
+ if (priv->hadjustment)
+ {
+ g_signal_handlers_disconnect_by_func (priv->hadjustment,
+ hadjustment_value_notify_cb,
+ scrollable);
+ g_object_unref (priv->hadjustment);
+ }
+
+ if (hadjustment)
+ {
+ g_object_ref (hadjustment);
+ g_signal_connect (hadjustment, "notify::value",
+ G_CALLBACK (hadjustment_value_notify_cb),
+ scrollable);
+ }
+
+ priv->hadjustment = hadjustment;
+ }
+
+ if (vadjustment != priv->vadjustment)
+ {
+ if (priv->vadjustment)
+ {
+ g_signal_handlers_disconnect_by_func (priv->vadjustment,
+ vadjustment_value_notify_cb,
+ scrollable);
+ g_object_unref (priv->vadjustment);
+ }
+
+ if (vadjustment)
+ {
+ g_object_ref (vadjustment);
+ g_signal_connect (vadjustment, "notify::value",
+ G_CALLBACK (vadjustment_value_notify_cb),
+ scrollable);
+ }
+
+ priv->vadjustment = vadjustment;
+ }
+}
+
+static void
+scrollable_get_adjustments (TidyScrollable *scrollable,
+ TidyAdjustment **hadjustment,
+ TidyAdjustment **vadjustment)
+{
+ TidyViewportPrivate *priv;
+
+ g_return_if_fail (TIDY_IS_VIEWPORT (scrollable));
+
+ priv = ((TidyViewport *)scrollable)->priv;
+
+ if (hadjustment)
+ {
+ if (priv->hadjustment)
+ *hadjustment = priv->hadjustment;
+ else
+ {
+ TidyAdjustment *adjustment;
+ ClutterFixed width, stage_width, increment;
+
+ width = CLUTTER_UNITS_TO_FIXED(clutter_actor_get_widthu (CLUTTER_ACTOR(scrollable)));
+ stage_width = CLUTTER_UNITS_TO_FIXED(clutter_actor_get_widthu (clutter_stage_get_default ()));
+ increment = MAX (CFX_ONE, MIN(stage_width, width));
+
+ adjustment = tidy_adjustment_newx (CLUTTER_UNITS_TO_FIXED(priv->x),
+ 0,
+ width,
+ CFX_ONE,
+ increment,
+ increment);
+ scrollable_set_adjustments (scrollable,
+ adjustment,
+ priv->vadjustment);
+ *hadjustment = adjustment;
+ }
+ }
+
+ if (vadjustment)
+ {
+ if (priv->vadjustment)
+ *vadjustment = priv->vadjustment;
+ else
+ {
+ TidyAdjustment *adjustment;
+ ClutterFixed height, stage_height, increment;
+
+ height = CLUTTER_UNITS_TO_FIXED(clutter_actor_get_heightu (CLUTTER_ACTOR(scrollable)));
+ stage_height = CLUTTER_UNITS_TO_FIXED(clutter_actor_get_heightu (clutter_stage_get_default ()));
+ increment = MAX (CFX_ONE, MIN(stage_height, height));
+
+ adjustment = tidy_adjustment_newx (CLUTTER_UNITS_TO_FIXED(priv->y),
+ 0,
+ height,
+ CFX_ONE,
+ increment,
+ increment);
+ scrollable_set_adjustments (scrollable,
+ priv->hadjustment,
+ adjustment);
+ *vadjustment = adjustment;
+ }
+ }
+}
+
+static void
+scrollable_interface_init (TidyScrollableInterface *iface)
+{
+ iface->set_adjustments = scrollable_set_adjustments;
+ iface->get_adjustments = scrollable_get_adjustments;
+}
+
+static void
+clip_notify_cb (ClutterActor *actor,
+ GParamSpec *pspec,
+ TidyViewport *self)
+{
+ gint width, height;
+ TidyViewportPrivate *priv = self->priv;
+
+ if (!priv->sync_adjustments)
+ return;
+
+ if (!clutter_actor_has_clip (actor))
+ {
+ if (priv->hadjustment)
+ g_object_set (priv->hadjustment, "page-size", (gdouble)1.0, NULL);
+ if (priv->vadjustment)
+ g_object_set (priv->vadjustment, "page-size", (gdouble)1.0, NULL);
+ return;
+ }
+
+ clutter_actor_get_clip (actor, NULL, NULL, &width, &height);
+
+ if (priv->hadjustment)
+ g_object_set (priv->hadjustment, "page-size", (gdouble)width, NULL);
+
+ if (priv->vadjustment)
+ g_object_set (priv->vadjustment, "page-size", (gdouble)height, NULL);
+}
+
+static void
+tidy_viewport_init (TidyViewport *self)
+{
+ self->priv = VIEWPORT_PRIVATE (self);
+
+ self->priv->sync_adjustments = TRUE;
+
+ g_signal_connect (self, "notify::clip",
+ G_CALLBACK (clip_notify_cb), self);
+}
+
+ClutterActor *
+tidy_viewport_new (void)
+{
+ return g_object_new (TIDY_TYPE_VIEWPORT, NULL);
+}
+
+void
+tidy_viewport_set_originu (TidyViewport *viewport,
+ ClutterUnit x,
+ ClutterUnit y,
+ ClutterUnit z)
+{
+ TidyViewportPrivate *priv;
+
+ g_return_if_fail (TIDY_IS_VIEWPORT (viewport));
+
+ priv = viewport->priv;
+
+ g_object_freeze_notify (G_OBJECT (viewport));
+
+ if (x != priv->x)
+ {
+ priv->x = x;
+ g_object_notify (G_OBJECT (viewport), "x-origin");
+
+ if (priv->hadjustment)
+ tidy_adjustment_set_valuex (priv->hadjustment,
+ CLUTTER_UNITS_TO_FIXED (x));
+ }
+
+ if (y != priv->y)
+ {
+ priv->y = y;
+ g_object_notify (G_OBJECT (viewport), "y-origin");
+
+ if (priv->vadjustment)
+ tidy_adjustment_set_valuex (priv->vadjustment,
+ CLUTTER_UNITS_TO_FIXED (y));
+ }
+
+ if (z != priv->z)
+ {
+ priv->z = z;
+ g_object_notify (G_OBJECT (viewport), "z-origin");
+ }
+
+ g_object_thaw_notify (G_OBJECT (viewport));
+
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (viewport));
+}
+
+void
+tidy_viewport_set_origin (TidyViewport *viewport,
+ gint x,
+ gint y,
+ gint z)
+{
+ g_return_if_fail (TIDY_IS_VIEWPORT (viewport));
+
+ tidy_viewport_set_originu (viewport,
+ CLUTTER_UNITS_FROM_DEVICE (x),
+ CLUTTER_UNITS_FROM_DEVICE (y),
+ CLUTTER_UNITS_FROM_DEVICE (z));
+}
+
+void
+tidy_viewport_get_originu (TidyViewport *viewport,
+ ClutterUnit *x,
+ ClutterUnit *y,
+ ClutterUnit *z)
+{
+ TidyViewportPrivate *priv;
+
+ g_return_if_fail (TIDY_IS_VIEWPORT (viewport));
+
+ priv = viewport->priv;
+
+ if (x)
+ *x = priv->x;
+
+ if (y)
+ *y = priv->y;
+
+ if (z)
+ *z = priv->z;
+}
+
+void
+tidy_viewport_get_origin (TidyViewport *viewport,
+ gint *x,
+ gint *y,
+ gint *z)
+{
+ TidyViewportPrivate *priv;
+
+ g_return_if_fail (TIDY_IS_VIEWPORT (viewport));
+
+ priv = viewport->priv;
+
+ if (x)
+ *x = CLUTTER_UNITS_TO_DEVICE (priv->x);
+
+ if (y)
+ *y = CLUTTER_UNITS_TO_DEVICE (priv->y);
+
+ if (z)
+ *z = CLUTTER_UNITS_TO_DEVICE (priv->z);
+}
--- /dev/null
+/* tidy-viewport.h: Viewport actor
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Chris Lord <chris@openedhand.com>
+ */
+
+#ifndef __TIDY_VIEWPORT_H__
+#define __TIDY_VIEWPORT_H__
+
+#include <glib-object.h>
+#include <clutter/clutter-group.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_VIEWPORT (tidy_viewport_get_type())
+#define TIDY_VIEWPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_VIEWPORT, TidyViewport))
+#define TIDY_IS_VIEWPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_VIEWPORT))
+#define TIDY_VIEWPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_VIEWPORT, TidyViewportClass))
+#define TIDY_IS_VIEWPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_VIEWPORT))
+#define TIDY_VIEWPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_VIEWPORT, TidyViewportClass))
+
+typedef struct _TidyViewport TidyViewport;
+typedef struct _TidyViewportPrivate TidyViewportPrivate;
+typedef struct _TidyViewportClass TidyViewportClass;
+
+struct _TidyViewport
+{
+ ClutterGroup parent;
+
+ TidyViewportPrivate *priv;
+};
+
+struct _TidyViewportClass
+{
+ ClutterGroupClass parent_class;
+};
+
+GType tidy_viewport_get_type (void) G_GNUC_CONST;
+
+ClutterActor * tidy_viewport_new (void);
+
+void tidy_viewport_set_originu (TidyViewport *viewport,
+ ClutterUnit x,
+ ClutterUnit y,
+ ClutterUnit z);
+
+void tidy_viewport_set_origin (TidyViewport *viewport,
+ gint x,
+ gint y,
+ gint z);
+
+void tidy_viewport_get_originu (TidyViewport *viewport,
+ ClutterUnit *x,
+ ClutterUnit *y,
+ ClutterUnit *z);
+
+void tidy_viewport_get_origin (TidyViewport *viewport,
+ gint *x,
+ gint *y,
+ gint *z);
+
+G_END_DECLS
+
+#endif /* __TIDY_VIEWPORT_H__ */
+