[gtk+/xi2-playground: 10/15] Add test applications for multidevice events.
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/xi2-playground: 10/15] Add test applications for multidevice events.
- Date: Tue, 8 Jun 2010 17:42:44 +0000 (UTC)
commit dcb1747ba9114ddf8acaf0e7597faf8df2516bf0
Author: Carlos Garnacho <carlosg gnome org>
Date: Mon Jun 7 15:44:00 2010 +0200
Add test applications for multidevice events.
configure.ac | 1 +
tests/Makefile.am | 2 +
tests/multidevice/Makefile.am | 47 +++
tests/multidevice/testangle.c | 197 ++++++++++++
tests/multidevice/testcoordinates.c | 45 +++
tests/multidevice/testcoordinatewidget.c | 251 +++++++++++++++
tests/multidevice/testcoordinatewidget.h | 57 ++++
tests/multidevice/testphotoalbum.c | 111 +++++++
tests/multidevice/testphotoalbumwidget.c | 508 ++++++++++++++++++++++++++++++
tests/multidevice/testphotoalbumwidget.h | 60 ++++
10 files changed, 1279 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 8827cb0..320f46a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2116,6 +2116,7 @@ demos/Makefile
demos/gtk-demo/Makefile
demos/gtk-demo/geninclude.pl
tests/Makefile
+tests/multidevice/Makefile
docs/Makefile
docs/reference/Makefile
docs/reference/gdk-pixbuf/Makefile
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a7c9705..53046d2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,6 +1,8 @@
## Makefile.am for gtk+/tests
include $(top_srcdir)/Makefile.decl
+SUBDIRS = multidevice
+
INCLUDES = \
-I$(top_srcdir) \
-I$(top_builddir)/gdk \
diff --git a/tests/multidevice/Makefile.am b/tests/multidevice/Makefile.am
new file mode 100644
index 0000000..a26d276
--- /dev/null
+++ b/tests/multidevice/Makefile.am
@@ -0,0 +1,47 @@
+## Makefile.am for gtk+/tests/mpx
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/gdk \
+ -I$(top_srcdir)/gdk \
+ -DGDK_DISABLE_DEPRECATED \
+ -DGTK_DISABLE_DEPRECATED \
+ $(GTK_DEBUG_FLAGS) \
+ $(GTK_DEP_CFLAGS)
+
+DEPS = \
+ $(top_builddir)/gdk-pixbuf/libgdk_pixbuf-$(GTK_API_VERSION).la \
+ $(top_builddir)/gdk/$(gdktargetlib) \
+ $(top_builddir)/gtk/$(gtktargetlib)
+
+LDADDS = \
+ $(top_builddir)/gdk-pixbuf/libgdk_pixbuf-$(GTK_API_VERSION).la \
+ $(top_builddir)/gdk/$(gdktargetlib) \
+ $(top_builddir)/gtk/$(gtktargetlib)
+
+noinst_PROGRAMS = \
+ testangle \
+ testcoordinates \
+ testphotoalbum
+
+testangle_DEPENDENCIES = $(TEST_DEPS)
+testcoordinates_DEPENDENCIES = $(TEST_DEPS)
+testphotoalbum_DEPENDENCIES = $(TEST_DEPS)
+
+testangle_LDADD = $(LDADDS)
+testcoordinates_LDADD = $(LDADDS)
+testphotoalbum_LDADD = $(LDADDS)
+
+testangle_SOURCES = \
+ testangle.c
+
+testcoordinates_SOURCES = \
+ testcoordinatewidget.c \
+ testcoordinatewidget.h \
+ testcoordinates.c
+
+testphotoalbum_SOURCES = \
+ testphotoalbumwidget.c \
+ testphotoalbumwidget.h \
+ testphotoalbum.c
diff --git a/tests/multidevice/testangle.c b/tests/multidevice/testangle.c
new file mode 100644
index 0000000..c87ec46
--- /dev/null
+++ b/tests/multidevice/testangle.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho <carlosg gnome org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work 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.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include <gtk/gtk.h>
+
+typedef struct DeviceData DeviceData;
+typedef struct Data Data;
+
+struct Data
+{
+ GtkDeviceGroup *group;
+
+ gdouble x;
+ gdouble y;
+ gdouble angle;
+ gdouble distance;
+};
+
+static gboolean
+button_press_cb (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ Data *data;
+ GList *devices;
+
+ data = (Data *) user_data;
+ devices = gtk_device_group_get_devices (data->group);
+
+ if (g_list_length (devices) < 2)
+ gtk_device_group_add_device (data->group, gdk_event_get_device (event));
+
+ return FALSE;
+}
+
+static gboolean
+button_release_cb (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ Data *data;
+
+ data = (Data *) user_data;
+
+ gtk_device_group_remove_device (data->group, gdk_event_get_device (event));
+
+ return FALSE;
+}
+
+static void
+multidevice_cb (GtkWidget *widget,
+ GtkDeviceGroup *group,
+ GtkMultiDeviceEvent *event,
+ gpointer user_data)
+{
+ Data *data;
+
+ data = user_data;
+
+ if (event->n_events > 0)
+ {
+ data->x = event->events[0]->x;
+ data->y = event->events[0]->y;
+ }
+
+ if (event->n_events > 1)
+ {
+ gdk_events_get_distance ((GdkEvent *) event->events[0],
+ (GdkEvent *) event->events[1],
+ &data->distance);
+ gdk_events_get_angle ((GdkEvent *) event->events[0],
+ (GdkEvent *) event->events[1],
+ &data->angle);
+ }
+ else
+ {
+ data->distance = 0;
+ data->angle = 0;
+ }
+
+ gtk_widget_queue_draw (widget);
+}
+
+static gboolean
+expose_cb (GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer user_data)
+{
+ Data *data;
+ cairo_t *cr;
+ GList *devices;
+
+ data = (Data *) user_data;
+
+ devices = gtk_device_group_get_devices (data->group);
+
+ if (!devices)
+ return TRUE;
+
+ cr = gdk_cairo_create (widget->window);
+ cairo_translate (cr, data->x, data->y);
+
+ cairo_save (cr);
+
+ cairo_set_source_rgb (cr, 0., 0., 0.);
+ cairo_move_to (cr, 0, 0);
+ cairo_rel_line_to (cr, 1000, 0);
+ cairo_stroke (cr);
+
+ cairo_restore (cr);
+
+ if (data->angle > 0)
+ {
+ cairo_save (cr);
+
+ cairo_set_source_rgb (cr, 0, 0, 1);
+ cairo_move_to (cr, 0, 0);
+
+ cairo_arc (cr,
+ 0.,
+ 0.,
+ MAX (10., data->distance),
+ 0.,
+ data->angle);
+
+ cairo_close_path (cr);
+
+ cairo_fill (cr);
+
+ cairo_restore (cr);
+
+ cairo_set_source_rgb (cr, 1., 0., 0.);
+ cairo_rotate (cr, data->angle);
+ cairo_move_to (cr, 0, 0);
+ cairo_rel_line_to (cr, data->distance, 0);
+ cairo_stroke (cr);
+ }
+
+ cairo_destroy (cr);
+
+ return TRUE;
+}
+
+int
+main (int argc, gchar *argv[])
+{
+ GtkWidget *window;
+ Data data = { 0 };
+
+ gtk_init (&argc, &argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_app_paintable (window, TRUE);
+
+ data.group = gtk_widget_create_device_group (window);
+
+ gtk_widget_add_events (window,
+ (GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK));
+
+ gtk_widget_set_support_multidevice (window, TRUE);
+
+ g_signal_connect (window, "button-press-event",
+ G_CALLBACK (button_press_cb), &data);
+ g_signal_connect (window, "button-release-event",
+ G_CALLBACK (button_release_cb), &data);
+ g_signal_connect (window, "multidevice-event",
+ G_CALLBACK (multidevice_cb), &data);
+ g_signal_connect (window, "expose-event",
+ G_CALLBACK (expose_cb), &data);
+
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
diff --git a/tests/multidevice/testcoordinates.c b/tests/multidevice/testcoordinates.c
new file mode 100644
index 0000000..dc43645
--- /dev/null
+++ b/tests/multidevice/testcoordinates.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho <carlosg gnome org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work 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.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include <gtk/gtk.h>
+#include "testcoordinatewidget.h"
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window, *widget;
+
+ gtk_init (&argc, &argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ widget = test_coordinate_widget_new ();
+ gtk_container_add (GTK_CONTAINER (window), widget);
+
+ g_signal_connect (window, "delete-event",
+ G_CALLBACK (gtk_main_quit), NULL);
+
+ gtk_widget_show_all (window);
+
+ gtk_main ();
+
+ return 0;
+}
diff --git a/tests/multidevice/testcoordinatewidget.c b/tests/multidevice/testcoordinatewidget.c
new file mode 100644
index 0000000..f9f3887
--- /dev/null
+++ b/tests/multidevice/testcoordinatewidget.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho <carlosg gnome org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work 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.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include "testcoordinatewidget.h"
+
+#define TEST_COORDINATE_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidgetPrivate))
+
+typedef struct TestCoordinateWidgetPrivate TestCoordinateWidgetPrivate;
+
+struct TestCoordinateWidgetPrivate
+{
+ GtkDeviceGroup *group;
+ GHashTable *coordinates;
+};
+
+static void test_coordinate_widget_class_init (TestCoordinateWidgetClass *klass);
+static void test_coordinate_widget_init (TestCoordinateWidget *coordinate);
+
+static void test_coordinate_widget_destroy (GtkObject *object);
+
+static gboolean test_coordinate_widget_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean test_coordinate_widget_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean test_coordinate_widget_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+
+static void test_coordinate_widget_multidevice_event (GtkWidget *widget,
+ GtkDeviceGroup *group,
+ GtkMultiDeviceEvent *event);
+
+G_DEFINE_TYPE (TestCoordinateWidget, test_coordinate_widget, GTK_TYPE_DRAWING_AREA)
+
+
+static void
+test_coordinate_widget_class_init (TestCoordinateWidgetClass *klass)
+{
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->destroy = test_coordinate_widget_destroy;
+
+ widget_class->button_press_event = test_coordinate_widget_button_press;
+ widget_class->button_release_event = test_coordinate_widget_button_release;
+ widget_class->expose_event = test_coordinate_widget_expose;
+
+ g_type_class_add_private (klass, sizeof (TestCoordinateWidgetPrivate));
+}
+
+static void
+test_coordinate_widget_init (TestCoordinateWidget *coord)
+{
+ TestCoordinateWidgetPrivate *priv;
+ GtkWidget *widget;
+
+ priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (coord);
+ widget = GTK_WIDGET (coord);
+
+ priv->coordinates = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) g_free);
+
+ gtk_widget_add_events (widget,
+ (GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK));
+
+ priv->group = gtk_widget_create_device_group (GTK_WIDGET (coord));
+ gtk_widget_set_support_multidevice (widget, TRUE);
+
+ /* Multidevice events are not exposed through GtkWidgetClass */
+ g_signal_connect (coord, "multidevice-event",
+ G_CALLBACK (test_coordinate_widget_multidevice_event), NULL);
+}
+
+static void
+test_coordinate_widget_destroy (GtkObject *object)
+{
+ TestCoordinateWidgetPrivate *priv;
+
+ priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (object);
+
+ if (priv->coordinates)
+ {
+ g_hash_table_destroy (priv->coordinates);
+ priv->coordinates = NULL;
+ }
+
+ if (priv->group)
+ {
+ gtk_widget_remove_device_group (GTK_WIDGET (object), priv->group);
+ priv->group = NULL;
+ }
+
+ GTK_OBJECT_CLASS (test_coordinate_widget_parent_class)->destroy (object);
+}
+
+static gboolean
+test_coordinate_widget_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ TestCoordinateWidgetPrivate *priv;
+ GdkDevice *device;
+
+ priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
+ device = gdk_event_get_device ((GdkEvent *) event);
+
+ gtk_device_group_add_device (priv->group, device);
+
+ return FALSE;
+}
+
+static gboolean
+test_coordinate_widget_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ TestCoordinateWidgetPrivate *priv;
+ GdkDevice *device;
+
+ priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
+ device = gdk_event_get_device ((GdkEvent *) event);
+
+ gtk_device_group_remove_device (priv->group, device);
+
+ return FALSE;
+}
+
+static void
+test_coordinate_widget_multidevice_event (GtkWidget *widget,
+ GtkDeviceGroup *group,
+ GtkMultiDeviceEvent *event)
+{
+ TestCoordinateWidgetPrivate *priv;
+ GdkEventMotion *ev;
+ GdkPoint *point;
+
+ priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
+
+ if (event->type == GTK_EVENT_DEVICE_REMOVED)
+ g_hash_table_remove (priv->coordinates, event->updated_device);
+ else
+ {
+ point = g_hash_table_lookup (priv->coordinates, event->updated_device);
+ ev = event->updated_event;
+
+ if (G_UNLIKELY (!point))
+ {
+ point = g_new (GdkPoint, 1);
+ g_hash_table_insert (priv->coordinates,
+ g_object_ref (event->updated_device),
+ point);
+ }
+
+ point->x = ev->x;
+ point->y = ev->y;
+ }
+
+ gtk_widget_queue_draw (widget);
+}
+
+static gboolean
+test_coordinate_widget_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ TestCoordinateWidgetPrivate *priv;
+ GList *coords, *c;
+ cairo_t *cr;
+
+ priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
+ cr = gdk_cairo_create (widget->window);
+
+ coords = g_hash_table_get_values (priv->coordinates);
+
+ cairo_set_source_rgb (cr, 1., 1., 1.);
+ cairo_rectangle (cr,
+ widget->allocation.x,
+ widget->allocation.y,
+ widget->allocation.width,
+ widget->allocation.height);
+ cairo_fill (cr);
+
+ cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
+
+ for (c = coords; c; c = c->next)
+ {
+ GdkPoint *point;
+
+ point = c->data;
+
+ cairo_move_to (cr, point->x, 0);
+ cairo_rel_line_to (cr, 0, widget->allocation.height);
+
+ cairo_move_to (cr, 0, point->y);
+ cairo_rel_line_to (cr, widget->allocation.width, 0);
+ }
+
+ cairo_stroke (cr);
+
+ cairo_set_source_rgba (cr, 0.5, 0., 0., 0.5);
+
+ if (g_list_length (coords) > 1)
+ {
+ for (c = coords; c; c = c->next)
+ {
+ GdkPoint *point;
+
+ point = c->data;
+
+ cairo_line_to (cr, point->x, point->y);
+ }
+
+ cairo_close_path (cr);
+ cairo_fill_preserve (cr);
+
+ cairo_set_source_rgba (cr, 0., 0., 0.5, 0.5);
+ cairo_stroke (cr);
+ }
+
+ cairo_destroy (cr);
+
+ g_list_free (coords);
+
+ return TRUE;
+}
+
+GtkWidget *
+test_coordinate_widget_new (void)
+{
+ return g_object_new (TEST_TYPE_COORDINATE_WIDGET, NULL);
+}
diff --git a/tests/multidevice/testcoordinatewidget.h b/tests/multidevice/testcoordinatewidget.h
new file mode 100644
index 0000000..bfa764d
--- /dev/null
+++ b/tests/multidevice/testcoordinatewidget.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho <carlosg gnome org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work 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.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#ifndef __TEST_COORDINATE_WIDGET_H__
+#define __TEST_COORDINATE_WIDGET_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define TEST_TYPE_COORDINATE_WIDGET (test_coordinate_widget_get_type ())
+#define TEST_COORDINATE_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidget))
+#define TEST_COORDINATE_WIDGET_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidgetClass))
+#define TEST_IS_COORDINATE_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TEST_TYPE_COORDINATE_WIDGET))
+#define TEST_IS_COORDINATE_WIDGET_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), TEST_TYPE_COORDINATE_WIDGET))
+#define TEST_COORDINATE_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidgetClass))
+
+typedef struct TestCoordinateWidget TestCoordinateWidget;
+typedef struct TestCoordinateWidgetClass TestCoordinateWidgetClass;
+
+struct TestCoordinateWidget
+{
+ GtkDrawingArea parent_instance;
+};
+
+struct TestCoordinateWidgetClass
+{
+ GtkDrawingAreaClass parent_class;
+};
+
+
+GType test_coordinate_widget_get_type (void) G_GNUC_CONST;
+
+GtkWidget * test_coordinate_widget_new (void);
+
+
+G_END_DECLS
+
+#endif /* __TEST_COORDINATE_WIDGET_H__ */
diff --git a/tests/multidevice/testphotoalbum.c b/tests/multidevice/testphotoalbum.c
new file mode 100644
index 0000000..008e5e5
--- /dev/null
+++ b/tests/multidevice/testphotoalbum.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho <carlosg gnome org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work 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.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include <gtk/gtk.h>
+#include "testphotoalbumwidget.h"
+
+static void
+add_image (TestPhotoAlbumWidget *album,
+ const gchar *image_path)
+{
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_file_at_size (image_path,
+ 200, -1,
+ NULL);
+ if (pixbuf)
+ {
+ test_photo_album_widget_add_image (album, pixbuf);
+ g_object_unref (pixbuf);
+ }
+}
+
+static void
+read_directory (GFile *directory,
+ TestPhotoAlbumWidget *album)
+{
+ GFileEnumerator *enumerator;
+ GError *error = NULL;
+ GFileInfo *info;
+
+ enumerator = g_file_enumerate_children (directory,
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ &error);
+
+ if (error)
+ {
+ g_warning ("%s\n", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL)
+ {
+ GFile *file;
+ gchar *path;
+
+ file = g_file_get_child (directory, g_file_info_get_name (info));
+
+ path = g_file_get_path (file);
+ add_image (album, path);
+ g_free (path);
+
+ g_object_unref (file);
+ }
+
+ g_file_enumerator_close (enumerator, NULL, NULL);
+ g_object_unref (enumerator);
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window, *widget;
+ GFile *dir;
+
+ if (argc != 2)
+ {
+ g_print ("USAGE: %s <path-to-directory-with-images>\n", argv[0]);
+ return -1;
+ }
+
+ gtk_init (&argc, &argv);
+
+ dir = g_file_new_for_commandline_arg (argv[1]);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ widget = test_photo_album_widget_new ();
+ gtk_container_add (GTK_CONTAINER (window), widget);
+
+ g_signal_connect (window, "delete-event",
+ G_CALLBACK (gtk_main_quit), NULL);
+
+ read_directory (dir, TEST_PHOTO_ALBUM_WIDGET (widget));
+
+ gtk_widget_show_all (window);
+
+ gtk_main ();
+
+ return 0;
+}
diff --git a/tests/multidevice/testphotoalbumwidget.c b/tests/multidevice/testphotoalbumwidget.c
new file mode 100644
index 0000000..b530069
--- /dev/null
+++ b/tests/multidevice/testphotoalbumwidget.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho <carlosg gnome org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work 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.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include "testphotoalbumwidget.h"
+#include <math.h>
+
+#define TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidgetPrivate))
+
+typedef struct TestPhotoAlbumWidgetPrivate TestPhotoAlbumWidgetPrivate;
+typedef struct TestPhoto TestPhoto;
+
+struct TestPhoto
+{
+ GdkPixbuf *pixbuf;
+ GtkDeviceGroup *group;
+ gdouble center_x;
+ gdouble center_y;
+ gdouble x;
+ gdouble y;
+ gdouble angle;
+ gdouble zoom;
+
+ GdkRegion *region;
+
+ gdouble base_zoom;
+ gdouble base_angle;
+ gdouble initial_distance;
+ gdouble initial_angle;
+};
+
+struct TestPhotoAlbumWidgetPrivate
+{
+ GPtrArray *photos;
+};
+
+static GQuark quark_group_photo = 0;
+
+
+static void test_photo_album_widget_class_init (TestPhotoAlbumWidgetClass *klass);
+static void test_photo_album_widget_init (TestPhotoAlbumWidget *album);
+
+static void test_photo_album_widget_destroy (GtkObject *object);
+
+static gboolean test_photo_album_widget_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean test_photo_album_widget_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static void test_photo_album_widget_multidevice_event (GtkWidget *widget,
+ GtkDeviceGroup *group,
+ GtkMultiDeviceEvent *event);
+static gboolean test_photo_album_widget_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+
+G_DEFINE_TYPE (TestPhotoAlbumWidget, test_photo_album_widget, GTK_TYPE_DRAWING_AREA)
+
+
+static void
+test_photo_album_widget_class_init (TestPhotoAlbumWidgetClass *klass)
+{
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->destroy = test_photo_album_widget_destroy;
+
+ widget_class->button_press_event = test_photo_album_widget_button_press;
+ widget_class->button_release_event = test_photo_album_widget_button_release;
+ widget_class->expose_event = test_photo_album_widget_expose;
+
+ g_type_class_add_private (klass, sizeof (TestPhotoAlbumWidgetPrivate));
+
+ quark_group_photo = g_quark_from_static_string ("group-photo");
+}
+
+static void
+test_photo_album_widget_init (TestPhotoAlbumWidget *album)
+{
+ TestPhotoAlbumWidgetPrivate *priv;
+ GtkWidget *widget;
+
+ priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
+ widget = GTK_WIDGET (album);
+
+ priv->photos = g_ptr_array_new ();
+
+ gtk_widget_add_events (widget,
+ (GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK));
+
+ gtk_widget_set_support_multidevice (widget, TRUE);
+
+ /* Multidevice events are not exposed through GtkWidgetClass */
+ g_signal_connect (album, "multidevice-event",
+ G_CALLBACK (test_photo_album_widget_multidevice_event), NULL);
+}
+
+static void
+calculate_rotated_point (gdouble angle,
+ gdouble zoom,
+ gdouble center_x,
+ gdouble center_y,
+ gdouble point_x,
+ gdouble point_y,
+ gdouble *ret_x,
+ gdouble *ret_y)
+{
+ gdouble distance, xd, yd, ang;
+
+ xd = center_x - point_x;
+ yd = center_y - point_y;
+
+ if (xd == 0 && yd == 0)
+ {
+ *ret_x = center_x;
+ *ret_y = center_y;
+ return;
+ }
+
+ distance = sqrt ((xd * xd) + (yd * yd));
+ distance *= zoom;
+
+ ang = atan2 (xd, yd);
+
+ /* Invert angle */
+ ang = (2 * G_PI) - ang;
+
+ /* Shift it 270° */
+ ang += 3 * (G_PI / 2);
+
+ /* And constraint it to 0°-360° */
+ ang = fmod (ang, 2 * G_PI);
+ ang += angle;
+
+ *ret_x = center_x + (distance * cos (ang));
+ *ret_y = center_y + (distance * sin (ang));
+}
+
+static void
+allocate_photo_region (TestPhoto *photo)
+{
+ GdkPoint points[4];
+ gint width, height, i;
+
+ width = gdk_pixbuf_get_width (photo->pixbuf);
+ height = gdk_pixbuf_get_height (photo->pixbuf);
+
+ /* Top/left */
+ points[0].x = photo->x - photo->center_x;
+ points[0].y = photo->y - photo->center_y;
+
+ /* Top/right */
+ points[1].x = photo->x - photo->center_x + width;
+ points[1].y = photo->y - photo->center_y;
+
+ /* Bottom/right */
+ points[2].x = photo->x - photo->center_x + width;
+ points[2].y = photo->y - photo->center_y + height;
+
+ /* Bottom/left */
+ points[3].x = photo->x - photo->center_x;
+ points[3].y = photo->y - photo->center_y + height;
+
+ for (i = 0; i < 4; i++)
+ {
+ gdouble ret_x, ret_y;
+
+ calculate_rotated_point (photo->angle,
+ photo->zoom,
+ photo->x,
+ photo->y,
+ (gdouble) points[i].x,
+ (gdouble) points[i].y,
+ &ret_x,
+ &ret_y);
+
+ points[i].x = (gint) ret_x;
+ points[i].y = (gint) ret_y;
+ }
+
+ if (photo->region)
+ gdk_region_destroy (photo->region);
+
+ photo->region = gdk_region_polygon (points, 4, GDK_WINDING_RULE);
+}
+
+static TestPhoto *
+test_photo_new (TestPhotoAlbumWidget *album,
+ GdkPixbuf *pixbuf)
+{
+ TestPhoto *photo;
+ static gdouble angle = 0;
+
+ photo = g_slice_new0 (TestPhoto);
+ photo->pixbuf = g_object_ref (pixbuf);
+ photo->group = gtk_widget_create_device_group (GTK_WIDGET (album));
+ g_object_set_qdata (G_OBJECT (photo->group), quark_group_photo, photo);
+
+ photo->center_x = 0;
+ photo->center_y = 0;
+ photo->x = 0;
+ photo->y = 0;
+ photo->angle = angle;
+ photo->zoom = 1.0;
+
+ angle += 0.1;
+
+ allocate_photo_region (photo);
+
+ return photo;
+}
+
+static void
+test_photo_free (TestPhoto *photo,
+ TestPhotoAlbumWidget *album)
+{
+ g_object_unref (photo->pixbuf);
+ gtk_widget_remove_device_group (GTK_WIDGET (album), photo->group);
+
+ g_slice_free (TestPhoto, photo);
+}
+
+static void
+test_photo_raise (TestPhoto *photo,
+ TestPhotoAlbumWidget *album)
+{
+ TestPhotoAlbumWidgetPrivate *priv;
+
+ priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
+ g_ptr_array_remove (priv->photos, photo);
+ g_ptr_array_add (priv->photos, photo);
+}
+
+static void
+test_photo_album_widget_destroy (GtkObject *object)
+{
+ TestPhotoAlbumWidgetPrivate *priv;
+
+ priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (object);
+
+ if (priv->photos)
+ {
+ g_ptr_array_foreach (priv->photos, (GFunc) test_photo_free, object);
+ g_ptr_array_free (priv->photos, TRUE);
+ priv->photos = NULL;
+ }
+
+ GTK_OBJECT_CLASS (test_photo_album_widget_parent_class)->destroy (object);
+}
+
+static TestPhoto *
+find_photo_at_position (TestPhotoAlbumWidget *album,
+ gdouble x,
+ gdouble y)
+{
+ TestPhotoAlbumWidgetPrivate *priv;
+ TestPhoto *photo;
+ gint i;
+
+ priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
+
+ for (i = priv->photos->len - 1; i >= 0; i--)
+ {
+ photo = g_ptr_array_index (priv->photos, i);
+
+ if (gdk_region_point_in (photo->region, (gint) x, (gint) y))
+ return photo;
+ }
+
+ return NULL;
+}
+
+static gboolean
+test_photo_album_widget_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ TestPhoto *photo;
+
+ photo = find_photo_at_position (TEST_PHOTO_ALBUM_WIDGET (widget), event->x, event->y);
+
+ if (!photo)
+ return FALSE;
+
+ test_photo_raise (photo, TEST_PHOTO_ALBUM_WIDGET (widget));
+ gtk_device_group_add_device (photo->group, event->device);
+
+ return TRUE;
+}
+
+static gboolean
+test_photo_album_widget_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDeviceGroup *group;
+
+ group = gtk_widget_get_group_for_device (widget, event->device);
+
+ if (group)
+ gtk_device_group_remove_device (group, event->device);
+
+ return TRUE;
+}
+
+static void
+test_photo_album_widget_multidevice_event (GtkWidget *widget,
+ GtkDeviceGroup *group,
+ GtkMultiDeviceEvent *event)
+{
+ TestPhotoAlbumWidgetPrivate *priv;
+ GdkRegion *region;
+ TestPhoto *photo;
+ gboolean new_center = FALSE;
+ gboolean new_position = FALSE;
+ gdouble event_x, event_y;
+
+ priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (widget);
+ photo = g_object_get_qdata (G_OBJECT (group), quark_group_photo);
+
+ region = gdk_region_copy (photo->region);
+
+ if (event->n_events == 1)
+ {
+ if (event->type == GTK_EVENT_DEVICE_REMOVED)
+ {
+ /* Device was just removed, unset zoom/angle info */
+ photo->base_zoom = 0;
+ photo->base_angle = 0;
+ photo->initial_angle = 0;
+ photo->initial_distance = 0;
+ new_center = TRUE;
+ }
+ else if (event->type == GTK_EVENT_DEVICE_ADDED)
+ new_center = TRUE;
+
+ event_x = event->events[0]->x;
+ event_y = event->events[0]->y;
+ new_position = TRUE;
+ }
+ else if (event->n_events == 2)
+ {
+ gdouble distance, angle;
+
+ gdk_events_get_center ((GdkEvent *) event->events[0],
+ (GdkEvent *) event->events[1],
+ &event_x, &event_y);
+
+ gdk_events_get_distance ((GdkEvent *) event->events[0],
+ (GdkEvent *) event->events[1],
+ &distance);
+
+ gdk_events_get_angle ((GdkEvent *) event->events[0],
+ (GdkEvent *) event->events[1],
+ &angle);
+
+ if (event->type == GTK_EVENT_DEVICE_ADDED)
+ {
+ photo->base_zoom = photo->zoom;
+ photo->base_angle = photo->angle;
+ photo->initial_angle = angle;
+ photo->initial_distance = distance;
+ new_center = TRUE;
+ }
+
+ photo->zoom = photo->base_zoom * (distance / photo->initial_distance);
+ photo->angle = photo->base_angle + (angle - photo->initial_angle);
+ new_position = TRUE;
+ }
+
+ if (new_center)
+ {
+ gdouble origin_x, origin_y;
+
+ origin_x = photo->x - photo->center_x;
+ origin_y = photo->y - photo->center_y;
+
+ calculate_rotated_point (- photo->angle,
+ 1 / photo->zoom,
+ photo->x - origin_x,
+ photo->y - origin_y,
+ event_x - origin_x,
+ event_y - origin_y,
+ &photo->center_x,
+ &photo->center_y);
+ }
+
+ if (new_position)
+ {
+ photo->x = event_x;
+ photo->y = event_y;
+ }
+
+ allocate_photo_region (photo);
+
+ gdk_region_union (region, photo->region);
+ gdk_region_shrink (region, -4, -4);
+
+ gdk_window_invalidate_region (widget->window, region, FALSE);
+}
+
+static gboolean
+test_photo_album_widget_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ TestPhotoAlbumWidgetPrivate *priv;
+ cairo_t *cr;
+ gint i;
+
+ priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (widget);
+ cr = gdk_cairo_create (widget->window);
+
+ for (i = 0; i < priv->photos->len; i++)
+ {
+ TestPhoto *photo = g_ptr_array_index (priv->photos, i);
+ GdkRegion *region;
+ gint width, height;
+
+ region = gdk_region_copy (photo->region);
+ gdk_region_shrink (region, -4, -4);
+
+ gdk_region_intersect (region, event->region);
+
+ if (gdk_region_empty (region))
+ {
+ gdk_region_destroy (region);
+ continue;
+ }
+
+ width = gdk_pixbuf_get_width (photo->pixbuf);
+ height = gdk_pixbuf_get_height (photo->pixbuf);
+
+ cairo_save (cr);
+
+ gdk_cairo_region (cr, region);
+ cairo_clip (cr);
+
+ cairo_translate (cr, photo->x, photo->y);
+
+ cairo_scale (cr, photo->zoom, photo->zoom);
+ cairo_rotate (cr, photo->angle);
+ gdk_cairo_set_source_pixbuf (cr,
+ photo->pixbuf,
+ - photo->center_x,
+ - photo->center_y);
+
+ cairo_rectangle (cr,
+ - photo->center_x,
+ - photo->center_y,
+ width, height);
+ cairo_fill_preserve (cr);
+
+ cairo_set_source_rgb (cr, 0., 0., 0.);
+ cairo_stroke (cr);
+
+ cairo_restore (cr);
+
+ gdk_region_destroy (region);
+ }
+
+ cairo_destroy (cr);
+
+ return TRUE;
+}
+
+GtkWidget *
+test_photo_album_widget_new (void)
+{
+ return g_object_new (TEST_TYPE_PHOTO_ALBUM_WIDGET, NULL);
+}
+
+void
+test_photo_album_widget_add_image (TestPhotoAlbumWidget *album,
+ GdkPixbuf *pixbuf)
+{
+ TestPhotoAlbumWidgetPrivate *priv;
+ TestPhoto *photo;
+
+ g_return_if_fail (TEST_IS_PHOTO_ALBUM_WIDGET (album));
+ g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
+
+ priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
+
+ photo = test_photo_new (album, pixbuf);
+ g_ptr_array_add (priv->photos, photo);
+
+ if (gtk_widget_get_realized (album) &&
+ gtk_widget_is_drawable (album))
+ gdk_window_invalidate_region (GTK_WIDGET (album)->window,
+ photo->region,
+ FALSE);
+}
diff --git a/tests/multidevice/testphotoalbumwidget.h b/tests/multidevice/testphotoalbumwidget.h
new file mode 100644
index 0000000..0a36378
--- /dev/null
+++ b/tests/multidevice/testphotoalbumwidget.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho <carlosg gnome org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work 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.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#ifndef __TEST_PHOTO_ALBUM_WIDGET_H__
+#define __TEST_PHOTO_ALBUM_WIDGET_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define TEST_TYPE_PHOTO_ALBUM_WIDGET (test_photo_album_widget_get_type ())
+#define TEST_PHOTO_ALBUM_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidget))
+#define TEST_PHOTO_ALBUM_WIDGET_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidgetClass))
+#define TEST_IS_PHOTO_ALBUM_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET))
+#define TEST_IS_PHOTO_ALBUM_WIDGET_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), TEST_TYPE_PHOTO_ALBUM_WIDGET))
+#define TEST_PHOTO_ALBUM_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidgetClass))
+
+typedef struct TestPhotoAlbumWidget TestPhotoAlbumWidget;
+typedef struct TestPhotoAlbumWidgetClass TestPhotoAlbumWidgetClass;
+
+struct TestPhotoAlbumWidget
+{
+ GtkDrawingArea parent_instance;
+};
+
+struct TestPhotoAlbumWidgetClass
+{
+ GtkDrawingAreaClass parent_class;
+};
+
+
+GType test_photo_album_widget_get_type (void) G_GNUC_CONST;
+
+GtkWidget * test_photo_album_widget_new (void);
+
+void test_photo_album_widget_add_image (TestPhotoAlbumWidget *album,
+ GdkPixbuf *pixbuf);
+
+
+G_END_DECLS
+
+#endif /* __TEST_PHOTO_ALBUM_WIDGET_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]