From 2f0ba4f1dd3ec8eb4b464d85beed5fa1b47fc4d2 Mon Sep 17 00:00:00 2001 From: Pierre-Luc Beaudoin Date: Fri, 15 Aug 2008 21:16:30 -0400 Subject: [PATCH] Compiling Tidy integration --- Makefile.am | 2 +- config.h.in | 33 ++ configure.ac | 6 +- src/Makefile.am | 4 +- src/champlain_private.h | 5 + src/champlain_widget.c | 214 ++-------- tidy/Makefile.am | 112 +++++ tidy/tidy-adjustment.c | 849 ++++++++++++++++++++++++++++++++++++++ tidy/tidy-adjustment.h | 148 +++++++ tidy/tidy-debug.h | 4 + tidy/tidy-enum-types.c.in | 30 ++ tidy/tidy-enum-types.h.in | 25 ++ tidy/tidy-finger-scroll.c | 670 ++++++++++++++++++++++++++++++ tidy/tidy-finger-scroll.h | 75 ++++ tidy/tidy-marshal.c | 132 ++++++ tidy/tidy-marshal.h | 40 ++ tidy/tidy-marshal.list | 6 + tidy/tidy-private.h | 40 ++ tidy/tidy-scroll-view.c | 348 ++++++++++++++++ tidy/tidy-scroll-view.h | 65 +++ tidy/tidy-scrollable.c | 87 ++++ tidy/tidy-scrollable.h | 63 +++ tidy/tidy-viewport.c | 621 ++++++++++++++++++++++++++++ tidy/tidy-viewport.h | 81 ++++ 24 files changed, 3469 insertions(+), 191 deletions(-) create mode 100644 tidy/Makefile.am create mode 100644 tidy/tidy-adjustment.c create mode 100644 tidy/tidy-adjustment.h create mode 100644 tidy/tidy-debug.h create mode 100644 tidy/tidy-enum-types.c.in create mode 100644 tidy/tidy-enum-types.h.in create mode 100644 tidy/tidy-finger-scroll.c create mode 100644 tidy/tidy-finger-scroll.h create mode 100644 tidy/tidy-marshal.c create mode 100644 tidy/tidy-marshal.h create mode 100644 tidy/tidy-marshal.list create mode 100644 tidy/tidy-private.h create mode 100644 tidy/tidy-scroll-view.c create mode 100644 tidy/tidy-scroll-view.h create mode 100644 tidy/tidy-scrollable.c create mode 100644 tidy/tidy-scrollable.h create mode 100644 tidy/tidy-viewport.c create mode 100644 tidy/tidy-viewport.h diff --git a/Makefile.am b/Makefile.am index af437a6..5c179c2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1 @@ -SUBDIRS = src +SUBDIRS = tidy src diff --git a/config.h.in b/config.h.in index c364eda..09ee04c 100644 --- a/config.h.in +++ b/config.h.in @@ -1,5 +1,35 @@ /* config.h.in. Generated from configure.ac by autoheader. */ +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + /* Name of package */ #undef PACKAGE @@ -18,5 +48,8 @@ /* 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 diff --git a/configure.ac b/configure.ac index 934c87d..4699df4 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,7 @@ AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE AC_LIBTOOL_DLOPEN AC_LIBLTDL_CONVENIENCE +AM_PROG_LIBTOOL # Checks for programs. AC_PROG_CC @@ -20,7 +21,7 @@ AC_SUBST(DEPS_CFLAGS) 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, @@ -35,5 +36,6 @@ PKG_CHECK_MODULES(DEPS, # Checks for library functions. AC_CONFIG_FILES([Makefile - src/Makefile]) + src/Makefile + tidy/Makefile]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index 77d7ce5..db2b1ca 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,8 +21,8 @@ champlain_SOURCES = $(CHAMPLAIN_MARSHAL_LIST) \ 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) diff --git a/src/champlain_private.h b/src/champlain_private.h index 1a0568f..6bf6132 100644 --- a/src/champlain_private.h +++ b/src/champlain_private.h @@ -21,10 +21,15 @@ #ifndef CHAMPLAIN_PRIVATE_H #define CHAMPLAIN_PRIVATE_H +#include +#include + 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 diff --git a/src/champlain_widget.c b/src/champlain_widget.c index a58e37a..9d3416f 100644 --- a/src/champlain_widget.c +++ b/src/champlain_widget.c @@ -25,6 +25,7 @@ #include "champlain_widget.h" #include "champlain-marshal.h" +#include #include #include #include @@ -59,8 +60,6 @@ typedef struct struct _ChamplainWidgetPrivate { GtkWidget *clutterEmbed; - GtkAdjustment *horizontalAdjustment; - GtkAdjustment *verticalAdjustment; ClutterActor *viewport; // Scrolling @@ -73,67 +72,7 @@ struct _ChamplainWidgetPrivate 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) @@ -141,20 +80,6 @@ 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); } @@ -163,18 +88,6 @@ champlain_widget_class_init (ChamplainWidgetClass * champlainWidgetClass) { 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; } @@ -183,86 +96,6 @@ static void 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 @@ -284,17 +117,22 @@ viewport_motion_event_cb (ClutterActor * actor, ClutterMotionEvent * event, Cham 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; } @@ -348,6 +186,9 @@ viewport_captured_event_cb (ClutterActor * actor, ClutterEvent * event, Champlai 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, @@ -355,9 +196,14 @@ viewport_captured_event_cb (ClutterActor * actor, ClutterEvent * event, Champlai (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; @@ -372,6 +218,7 @@ viewport_captured_event_cb (ClutterActor * actor, ClutterEvent * event, Champlai 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; @@ -387,7 +234,7 @@ champlain_widget_load_map (ChamplainWidget * champlainWidget) 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 * @@ -400,19 +247,14 @@ champlain_widget_new () /* 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); } diff --git a/tidy/Makefile.am b/tidy/Makefile.am new file mode 100644 index 0000000..67a5161 --- /dev/null +++ b/tidy/Makefile.am @@ -0,0 +1,112 @@ +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 + diff --git a/tidy/tidy-adjustment.c b/tidy/tidy-adjustment.c new file mode 100644 index 0000000..f681716 --- /dev/null +++ b/tidy/tidy-adjustment.c @@ -0,0 +1,849 @@ +/* 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 , inspired by GtkAdjustment + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#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; +} diff --git a/tidy/tidy-adjustment.h b/tidy/tidy-adjustment.h new file mode 100644 index 0000000..cd51049 --- /dev/null +++ b/tidy/tidy-adjustment.h @@ -0,0 +1,148 @@ +/* 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 , inspired by GtkAdjustment + */ + +#ifndef __TIDY_ADJUSTMENT_H__ +#define __TIDY_ADJUSTMENT_H__ + +#include +#include + +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__ */ + diff --git a/tidy/tidy-debug.h b/tidy/tidy-debug.h new file mode 100644 index 0000000..0efb982 --- /dev/null +++ b/tidy/tidy-debug.h @@ -0,0 +1,4 @@ +#ifndef __TIDY_DEBUG_H__ +#define __TIDY_DEBUG_H__ + +#endif /* __TIDY_DEBUG_H__ */ diff --git a/tidy/tidy-enum-types.c.in b/tidy/tidy-enum-types.c.in new file mode 100644 index 0000000..5f78912 --- /dev/null +++ b/tidy/tidy-enum-types.c.in @@ -0,0 +1,30 @@ +/*** 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 ***/ diff --git a/tidy/tidy-enum-types.h.in b/tidy/tidy-enum-types.h.in new file mode 100644 index 0000000..517cccb --- /dev/null +++ b/tidy/tidy-enum-types.h.in @@ -0,0 +1,25 @@ +/*** BEGIN file-header ***/ +#ifndef __TIDY_ENUM_TYPES_H__ +#define __TIDY_ENUM_TYPES_H__ + +#include + +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 ***/ diff --git a/tidy/tidy-finger-scroll.c b/tidy/tidy-finger-scroll.c new file mode 100644 index 0000000..4bd6ad1 --- /dev/null +++ b/tidy/tidy-finger-scroll.c @@ -0,0 +1,670 @@ +/* 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 + */ + +#include "tidy-finger-scroll.h" +#include "tidy-enum-types.h" +#include "tidy-marshal.h" +#include "tidy-scrollable.h" +#include "tidy-scroll-view.h" +#include +#include + +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; + } +} diff --git a/tidy/tidy-finger-scroll.h b/tidy/tidy-finger-scroll.h new file mode 100644 index 0000000..eabc5f1 --- /dev/null +++ b/tidy/tidy-finger-scroll.h @@ -0,0 +1,75 @@ +/* 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 + */ + +#ifndef __TIDY_FINGER_SCROLL_H__ +#define __TIDY_FINGER_SCROLL_H__ + +#include +#include + +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__ */ diff --git a/tidy/tidy-marshal.c b/tidy/tidy-marshal.c new file mode 100644 index 0000000..9379902 --- /dev/null +++ b/tidy/tidy-marshal.c @@ -0,0 +1,132 @@ +#include "tidy-marshal.h" + +#include + + +#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); +} + diff --git a/tidy/tidy-marshal.h b/tidy/tidy-marshal.h new file mode 100644 index 0000000..9846ed6 --- /dev/null +++ b/tidy/tidy-marshal.h @@ -0,0 +1,40 @@ + +#ifndef ___tidy_marshal_MARSHAL_H__ +#define ___tidy_marshal_MARSHAL_H__ + +#include + +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__ */ + diff --git a/tidy/tidy-marshal.list b/tidy/tidy-marshal.list new file mode 100644 index 0000000..7a60e80 --- /dev/null +++ b/tidy/tidy-marshal.list @@ -0,0 +1,6 @@ +VOID:OBJECT +VOID:VOID +VOID:PARAM +VOID:UINT +VOID:UINT,UINT +VOID:OBJECT,OBJECT diff --git a/tidy/tidy-private.h b/tidy/tidy-private.h new file mode 100644 index 0000000..5f17d93 --- /dev/null +++ b/tidy/tidy-private.h @@ -0,0 +1,40 @@ +/* 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 + +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__ */ diff --git a/tidy/tidy-scroll-view.c b/tidy/tidy-scroll-view.c new file mode 100644 index 0000000..3825d37 --- /dev/null +++ b/tidy/tidy-scroll-view.c @@ -0,0 +1,348 @@ +/* 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 + */ + +#include "tidy-scroll-view.h" +#include "tidy-marshal.h" +#include "tidy-scrollable.h" +#include + +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; +} diff --git a/tidy/tidy-scroll-view.h b/tidy/tidy-scroll-view.h new file mode 100644 index 0000000..e01a72b --- /dev/null +++ b/tidy/tidy-scroll-view.h @@ -0,0 +1,65 @@ +/* tidy-scroll-view.h: Container with scroll-bars + * + * Copyright (C) 2008 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Written by: Chris Lord + */ + +#ifndef __TIDY_SCROLL_VIEW_H__ +#define __TIDY_SCROLL_VIEW_H__ + +#include +#include + +G_BEGIN_DECLS + +#define TIDY_TYPE_SCROLL_VIEW (tidy_scroll_view_get_type()) +#define TIDY_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_SCROLL_VIEW, TidyScrollView)) +#define TIDY_IS_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_SCROLL_VIEW)) +#define TIDY_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_SCROLL_VIEW, TidyScrollViewClass)) +#define TIDY_IS_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_SCROLL_VIEW)) +#define TIDY_SCROLL_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_SCROLL_VIEW, TidyScrollViewClass)) + +typedef struct _TidyScrollView TidyScrollView; +typedef struct _TidyScrollViewPrivate TidyScrollViewPrivate; +typedef struct _TidyScrollViewClass TidyScrollViewClass; + +struct _TidyScrollView +{ + /*< private >*/ + ClutterActor parent; + + TidyScrollViewPrivate *priv; +}; + +struct _TidyScrollViewClass +{ + ClutterActorClass parent_class; +}; + +GType tidy_scroll_view_get_type (void) G_GNUC_CONST; + +ClutterActor *tidy_scroll_view_new (void); + +ClutterActor *tidy_scroll_view_get_hscroll_bar (TidyScrollView *scroll); +ClutterActor *tidy_scroll_view_get_vscroll_bar (TidyScrollView *scroll); +ClutterActor *tidy_scroll_view_get_child (TidyScrollView *scroll); + +G_END_DECLS + +#endif /* __TIDY_SCROLL_VIEW_H__ */ diff --git a/tidy/tidy-scrollable.c b/tidy/tidy-scrollable.c new file mode 100644 index 0000000..6f6545f --- /dev/null +++ b/tidy/tidy-scrollable.c @@ -0,0 +1,87 @@ +/* tidy-scrollable.c: Scrollable interface + * + * Copyright (C) 2008 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Written by: Chris Lord + */ + +#include "tidy-scrollable.h" + +static void +tidy_scrollable_base_init (gpointer g_iface) +{ + static gboolean initialized = FALSE; + + if (!initialized) + { + g_object_interface_install_property (g_iface, + g_param_spec_object ("hadjustment", + "TidyAdjustment", + "Horizontal adjustment", + TIDY_TYPE_ADJUSTMENT, + G_PARAM_READWRITE)); + + g_object_interface_install_property (g_iface, + g_param_spec_object ("vadjustment", + "TidyAdjustment", + "Vertical adjustment", + TIDY_TYPE_ADJUSTMENT, + G_PARAM_READWRITE)); + + initialized = TRUE; + } +} + +GType +tidy_scrollable_get_type (void) +{ + static GType type = 0; + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (TidyScrollableInterface), + tidy_scrollable_base_init, /* base_init */ + NULL, + }; + type = g_type_register_static (G_TYPE_INTERFACE, + "TidyScrollable", &info, 0); + } + return type; +} + +void +tidy_scrollable_set_adjustments (TidyScrollable *scrollable, + TidyAdjustment *hadjustment, + TidyAdjustment *vadjustment) +{ + TIDY_SCROLLABLE_GET_INTERFACE (scrollable)->set_adjustments (scrollable, + hadjustment, + vadjustment); +} + +void +tidy_scrollable_get_adjustments (TidyScrollable *scrollable, + TidyAdjustment **hadjustment, + TidyAdjustment **vadjustment) +{ + TIDY_SCROLLABLE_GET_INTERFACE (scrollable)->get_adjustments (scrollable, + hadjustment, + vadjustment); +} + diff --git a/tidy/tidy-scrollable.h b/tidy/tidy-scrollable.h new file mode 100644 index 0000000..4710897 --- /dev/null +++ b/tidy/tidy-scrollable.h @@ -0,0 +1,63 @@ +/* tidy-scrollable.h: Scrollable interface + * + * Copyright (C) 2008 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Written by: Chris Lord + */ + +#ifndef __TIDY_SCROLLABLE_H__ +#define __TIDY_SCROLLABLE_H__ + +#include +#include + +G_BEGIN_DECLS + +#define TIDY_TYPE_SCROLLABLE (tidy_scrollable_get_type ()) +#define TIDY_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_SCROLLABLE, TidyScrollable)) +#define TIDY_IS_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_SCROLLABLE)) +#define TIDY_SCROLLABLE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), TIDY_TYPE_SCROLLABLE, TidyScrollableInterface)) + +typedef struct _TidyScrollable TidyScrollable; /* Dummy object */ +typedef struct _TidyScrollableInterface TidyScrollableInterface; + +struct _TidyScrollableInterface +{ + GTypeInterface parent; + + void (* set_adjustments) (TidyScrollable *scrollable, + TidyAdjustment *hadjustment, + TidyAdjustment *vadjustment); + void (* get_adjustments) (TidyScrollable *scrollable, + TidyAdjustment **hadjustment, + TidyAdjustment **vadjustment); +}; + +GType tidy_scrollable_get_type (void) G_GNUC_CONST; + +void tidy_scrollable_set_adjustments (TidyScrollable *scrollable, + TidyAdjustment *hadjustment, + TidyAdjustment *vadjustment); +void tidy_scrollable_get_adjustments (TidyScrollable *scrollable, + TidyAdjustment **hadjustment, + TidyAdjustment **vadjustment); + +G_END_DECLS + +#endif /* __TIDY_SCROLLABLE_H__ */ + diff --git a/tidy/tidy-viewport.c b/tidy/tidy-viewport.c new file mode 100644 index 0000000..33602e0 --- /dev/null +++ b/tidy/tidy-viewport.c @@ -0,0 +1,621 @@ +/* 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#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); +} diff --git a/tidy/tidy-viewport.h b/tidy/tidy-viewport.h new file mode 100644 index 0000000..eb457bf --- /dev/null +++ b/tidy/tidy-viewport.h @@ -0,0 +1,81 @@ +/* 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 + */ + +#ifndef __TIDY_VIEWPORT_H__ +#define __TIDY_VIEWPORT_H__ + +#include +#include + +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__ */ + -- 2.39.5