[gtk+/xi2-playground: 6/8] Add GtkDeviceGroup and ::multidevice-event
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/xi2-playground: 6/8] Add GtkDeviceGroup and ::multidevice-event
- Date: Fri, 2 Jul 2010 12:07:24 +0000 (UTC)
commit 80bf8c3999dfc96952baaad45253cd843d9f4f27
Author: Carlos Garnacho <carlosg gnome org>
Date: Mon Jun 7 15:41:10 2010 +0200
Add GtkDeviceGroup and ::multidevice-event
The former allows to create several per-widget device groups, so the latter
is emitted on these containing motion events for all devices in the reported
group.
gtk/Makefile.am | 2 +
gtk/gtk.symbols | 12 ++
gtk/gtkdevicegroup.c | 215 ++++++++++++++++++++++++++++
gtk/gtkdevicegroup.h | 71 ++++++++++
gtk/gtkwidget.c | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++
gtk/gtkwidget.h | 44 ++++++
6 files changed, 724 insertions(+), 0 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 4c0743b..eadef88 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -200,6 +200,7 @@ gtk_public_h_sources = \
gtkcomboboxentry.h \
gtkcontainer.h \
gtkdebug.h \
+ gtkdevicegroup.h \
gtkdialog.h \
gtkdnd.h \
gtkdrawingarea.h \
@@ -458,6 +459,7 @@ gtk_base_c_sources = \
gtkcombobox.c \
gtkcomboboxentry.c \
gtkcontainer.c \
+ gtkdevicegroup.c \
gtkdialog.c \
gtkdrawingarea.c \
gtkeditable.c \
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index b5125d0..fded2e7 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -4487,10 +4487,22 @@ gtk_widget_set_mapped
gtk_widget_get_mapped
gtk_widget_get_support_multidevice
gtk_widget_set_support_multidevice
+gtk_widget_create_device_group
+gtk_widget_remove_device_group
+gtk_widget_get_group_for_device
gtk_widget_device_is_shadowed
#endif
#endif
+#if IN_HEADER(__GTK_DEVICE_GROUP_H__)
+#if IN_FILE(__GTK_DEVICE_GROUP_C__)
+gtk_device_group_get_type G_GNUC_CONST
+gtk_device_group_add_device
+gtk_device_group_remove_device
+gtk_device_group_get_devices
+#endif
+#endif
+
#if IN_HEADER(__GTK_WINDOW_H__)
#if IN_FILE(__GTK_WINDOW_C__)
gtk_window_activate_default
diff --git a/gtk/gtkdevicegroup.c b/gtk/gtkdevicegroup.c
new file mode 100644
index 0000000..ef9b0ee
--- /dev/null
+++ b/gtk/gtkdevicegroup.c
@@ -0,0 +1,215 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg gnome org>
+ *
+ * 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.
+ */
+
+#include "config.h"
+#include "gtkintl.h"
+#include "gtkdevicegroup.h"
+#include "gtkalias.h"
+
+/**
+ * SECTION:gtkdevicegroup
+ * @Short_description: Group of input devices for multidevice events.
+ * @Title: Device groups
+ * @See_also: #GtkMultiDeviceEvent
+ *
+ * #GtkDeviceGroup defines a group of devices, they are created through
+ * gtk_widget_create_device_group() and destroyed through
+ * gtk_widget_remove_device_group(). Device groups are used by its
+ * corresponding #GtkWidget in order to issue #GtkMultiDeviceEvent<!-- -->s
+ * whenever any of the contained devices emits a #GDK_MOTION_NOTIFY
+ * event, or any device enters or leaves the group.
+ */
+
+typedef struct GtkDeviceGroupPrivate GtkDeviceGroupPrivate;
+
+struct GtkDeviceGroupPrivate
+{
+ GList *devices;
+};
+
+#define GTK_DEVICE_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroupPrivate))
+
+static void gtk_device_group_class_init (GtkDeviceGroupClass *klass);
+static void gtk_device_group_init (GtkDeviceGroup *group);
+
+static void gtk_device_group_finalize (GObject *object);
+
+enum {
+ DEVICE_ADDED,
+ DEVICE_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GtkDeviceGroup, gtk_device_group, G_TYPE_OBJECT)
+
+
+static void
+gtk_device_group_class_init (GtkDeviceGroupClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_device_group_finalize;
+
+ /**
+ * GtkDeviceGroup::device-added:
+ * @device_group: the object that received the signal
+ * @device: the device that was just added
+ *
+ * This signal is emitted right after a #GdkDevice is added
+ * to @device_group.
+ */
+ signals[DEVICE_ADDED] =
+ g_signal_new (I_("device-added"),
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkDeviceGroupClass, device_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, GDK_TYPE_DEVICE);
+ /**
+ * GtkDeviceGroup::device-removed:
+ * @device_group: the object that received the signal
+ * @device: the device that was just removed
+ *
+ * This signal is emitted right after a #GdkDevice is removed
+ * from @device_group.
+ */
+ signals[DEVICE_REMOVED] =
+ g_signal_new (I_("device-removed"),
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkDeviceGroupClass, device_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, GDK_TYPE_DEVICE);
+
+ g_type_class_add_private (object_class, sizeof (GtkDeviceGroupPrivate));
+}
+
+static void
+gtk_device_group_init (GtkDeviceGroup *group)
+{
+}
+
+static void
+gtk_device_group_finalize (GObject *object)
+{
+ GtkDeviceGroupPrivate *priv;
+
+ priv = GTK_DEVICE_GROUP_GET_PRIVATE (object);
+
+ g_list_foreach (priv->devices, (GFunc) g_object_unref, NULL);
+ g_list_free (priv->devices);
+
+ G_OBJECT_CLASS (gtk_device_group_parent_class)->finalize (object);
+}
+
+/**
+ * gtk_device_group_add_device:
+ * @group: a #GtkDeviceGroup
+ * @device: a #GdkDevice
+ *
+ * Adds @device to @group, so events coming from this device will
+ * trigger #GtkWidget::multidevice-event<!-- -->s for @group. Adding
+ * devices with source %GDK_SOURCE_KEYBOARD is not allowed.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_device_group_add_device (GtkDeviceGroup *group,
+ GdkDevice *device)
+{
+ GtkDeviceGroupPrivate *priv;
+
+ g_return_if_fail (GTK_IS_DEVICE_GROUP (group));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+ g_return_if_fail (device->source != GDK_SOURCE_KEYBOARD);
+
+ priv = GTK_DEVICE_GROUP_GET_PRIVATE (group);
+
+ if (g_list_find (priv->devices, device))
+ return;
+
+ priv->devices = g_list_prepend (priv->devices,
+ g_object_ref (device));
+
+ g_signal_emit (group, signals[DEVICE_ADDED], 0, device);
+}
+
+/**
+ * gtk_device_group_remove_device:
+ * @group: a #GtkDeviceGroup
+ * @device: a #GdkDevice
+ *
+ * Removes @device from @group, if it was there.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_device_group_remove_device (GtkDeviceGroup *group,
+ GdkDevice *device)
+{
+ GtkDeviceGroupPrivate *priv;
+ GList *dev;
+
+ g_return_if_fail (GTK_IS_DEVICE_GROUP (group));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+
+ priv = GTK_DEVICE_GROUP_GET_PRIVATE (group);
+
+ dev = g_list_find (priv->devices, device);
+
+ if (!dev)
+ return;
+
+ priv->devices = g_list_remove_link (priv->devices, dev);
+
+ g_signal_emit (group, signals[DEVICE_REMOVED], 0, device);
+
+ g_object_unref (dev->data);
+ g_list_free_1 (dev);
+}
+
+/**
+ * gtk_device_group_get_devices:
+ * @group: a #GtkDeviceGroup
+ *
+ * Returns a #GList of #GdkDevices with the devices contained in @group.
+ *
+ * Returns: a list of #GdkDevices. This list and its elements are owned
+ * by group, and must not be freed or unref'ed.
+ *
+ * Since: 3.0
+ **/
+GList *
+gtk_device_group_get_devices (GtkDeviceGroup *group)
+{
+ GtkDeviceGroupPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_DEVICE_GROUP (group), NULL);
+
+ priv = GTK_DEVICE_GROUP_GET_PRIVATE (group);
+
+ return priv->devices;
+}
+
+#define __GTK_DEVICE_GROUP_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkdevicegroup.h b/gtk/gtkdevicegroup.h
new file mode 100644
index 0000000..c09be77
--- /dev/null
+++ b/gtk/gtkdevicegroup.h
@@ -0,0 +1,71 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg gnome org>
+ *
+ * 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.
+ */
+
+#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_DEVICE_GROUP_H__
+#define __GTK_DEVICE_GROUP_H__
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+typedef struct GtkDeviceGroup GtkPointerGroup;
+
+#define GTK_TYPE_DEVICE_GROUP (gtk_device_group_get_type ())
+#define GTK_DEVICE_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroup))
+#define GTK_DEVICE_GROUP_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroupClass))
+#define GTK_IS_DEVICE_GROUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DEVICE_GROUP))
+#define GTK_IS_DEVICE_GROUP_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GTK_TYPE_DEVICE_GROUP))
+#define GTK_DEVICE_GROUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroupClass))
+
+typedef struct GtkDeviceGroup GtkDeviceGroup;
+typedef struct GtkDeviceGroupClass GtkDeviceGroupClass;
+
+struct GtkDeviceGroup
+{
+ GObject parent_instance;
+};
+
+struct GtkDeviceGroupClass
+{
+ GObjectClass parent_class;
+
+ void (* device_added) (GtkDeviceGroup *group,
+ GdkDevice *device);
+ void (* device_removed) (GtkDeviceGroup *group,
+ GdkDevice *device);
+};
+
+GType gtk_device_group_get_type (void) G_GNUC_CONST;
+
+void gtk_device_group_add_device (GtkDeviceGroup *group,
+ GdkDevice *device);
+
+void gtk_device_group_remove_device (GtkDeviceGroup *group,
+ GdkDevice *device);
+
+GList * gtk_device_group_get_devices (GtkDeviceGroup *group);
+
+
+G_END_DECLS
+
+#endif /* __GTK_DEVICE_GROUP_H__ */
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 351d995..6500de3 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -200,6 +200,7 @@ enum {
KEYNAV_FAILED,
DRAG_FAILED,
DAMAGE_EVENT,
+ MULTIDEVICE_EVENT,
LAST_SIGNAL
};
@@ -231,6 +232,7 @@ enum {
};
typedef struct _GtkStateData GtkStateData;
+typedef struct _GtkMultiDeviceData GtkMultiDeviceData;
struct _GtkStateData
{
@@ -240,6 +242,12 @@ struct _GtkStateData
guint use_forall : 1;
};
+struct _GtkMultiDeviceData
+{
+ GList *groups;
+ GHashTable *by_dev;
+};
+
/* --- prototypes --- */
static void gtk_widget_class_init (GtkWidgetClass *klass);
static void gtk_widget_base_class_finalize (GtkWidgetClass *klass);
@@ -395,6 +403,8 @@ static GQuark quark_mnemonic_labels = 0;
static GQuark quark_tooltip_markup = 0;
static GQuark quark_has_tooltip = 0;
static GQuark quark_tooltip_window = 0;
+static GQuark quark_multidevice_events = 0;
+static GQuark quark_multidevice_data = 0;
GParamSpecPool *_gtk_widget_child_property_pool = NULL;
GObjectNotifyContext *_gtk_widget_child_property_notify_context = NULL;
@@ -492,6 +502,8 @@ gtk_widget_class_init (GtkWidgetClass *klass)
quark_tooltip_markup = g_quark_from_static_string ("gtk-tooltip-markup");
quark_has_tooltip = g_quark_from_static_string ("gtk-has-tooltip");
quark_tooltip_window = g_quark_from_static_string ("gtk-tooltip-window");
+ quark_multidevice_events = g_quark_from_static_string ("gtk-multidevice-events");
+ quark_multidevice_data = g_quark_from_static_string ("gtk-multidevice-data");
style_property_spec_pool = g_param_spec_pool_new (FALSE);
_gtk_widget_child_property_pool = g_param_spec_pool_new (TRUE);
@@ -2423,6 +2435,29 @@ gtk_widget_class_init (GtkWidgetClass *klass)
_gtk_marshal_BOOLEAN__UINT,
G_TYPE_BOOLEAN, 1, G_TYPE_UINT);
+ /**
+ * GtkWidget::multidevice-event:
+ * @widget: the object which received the signal
+ * @device_group: the #GtkDeviceGroup that was updated by a
+ * device event
+ * @event: a #GtkMultiDeviceEvent containing event information
+ * for all devices in @device_group.
+ *
+ * This signal is emitted right after an input device that is
+ * contained in @device_group emits one event, or whenever a
+ * #GdkDevice is added or removed from @device_group.
+ *
+ * Since: 3.0
+ */
+ widget_signals[MULTIDEVICE_EVENT] =
+ g_signal_new (I_("multidevice-event"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ _gtk_marshal_VOID__OBJECT_POINTER,
+ G_TYPE_NONE, 2,
+ GTK_TYPE_DEVICE_GROUP, G_TYPE_POINTER);
+
binding_set = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_signal (binding_set, GDK_F10, GDK_SHIFT_MASK,
"popup-menu", 0);
@@ -4806,6 +4841,85 @@ event_window_is_still_viewable (GdkEvent *event)
}
}
+static void
+compose_multidevice_event (GtkWidget *widget,
+ GdkDevice *device,
+ GdkEventMotion *new_event)
+{
+ GHashTable *multidevice_events;
+ GtkMultiDeviceEventType type;
+ GtkDeviceGroup *group;
+ GtkMultiDeviceData *data;
+ GtkMultiDeviceEvent event;
+ GdkEvent *updated_event;
+ GList *devices;
+ gint i = 0;
+
+ data = g_object_get_qdata ((GObject *) widget,
+ quark_multidevice_data);
+ if (!data)
+ return;
+
+ group = g_hash_table_lookup (data->by_dev, device);
+
+ if (!group)
+ return;
+
+ multidevice_events = g_object_get_qdata ((GObject *) widget,
+ quark_multidevice_events);
+
+ if (G_UNLIKELY (!multidevice_events))
+ {
+ multidevice_events = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) gdk_event_free);
+ g_object_set_qdata_full ((GObject *) widget,
+ quark_multidevice_events,
+ multidevice_events,
+ (GDestroyNotify) g_hash_table_destroy);
+ }
+
+ if (!new_event)
+ {
+ g_hash_table_remove (multidevice_events, device);
+ type = GTK_EVENT_DEVICE_REMOVED;
+ updated_event = NULL;
+ }
+ else
+ {
+ if (g_hash_table_lookup (multidevice_events, device) == NULL)
+ type = GTK_EVENT_DEVICE_ADDED;
+ else
+ type = GTK_EVENT_DEVICE_UPDATED;
+
+ updated_event = gdk_event_copy ((GdkEvent *) new_event);
+
+ g_hash_table_insert (multidevice_events,
+ g_object_ref (device),
+ updated_event);
+ }
+
+ devices = gtk_device_group_get_devices (group);
+
+ /* Compose event */
+ event.type = type;
+ event.n_events = g_list_length (devices);
+ event.events = g_new0 (GdkEventMotion *, event.n_events);
+ event.updated_event = (GdkEventMotion *) updated_event;
+ event.updated_device = device;
+
+ while (devices)
+ {
+ event.events[i] = g_hash_table_lookup (multidevice_events, devices->data);
+ devices = devices->next;
+ i++;
+ }
+
+ g_signal_emit (widget, widget_signals[MULTIDEVICE_EVENT], 0, group, &event);
+ g_free (event.events);
+}
+
static gint
gtk_widget_event_internal (GtkWidget *widget,
GdkEvent *event)
@@ -4928,6 +5042,15 @@ gtk_widget_event_internal (GtkWidget *widget,
}
if (signal_num != -1)
g_signal_emit (widget, widget_signals[signal_num], 0, event, &return_val);
+
+ if (event->type == GDK_MOTION_NOTIFY &&
+ (GTK_WIDGET_FLAGS (widget) & GTK_MULTIDEVICE) != 0)
+ {
+ GdkEventMotion *event_motion;
+
+ event_motion = (GdkEventMotion *) event;
+ compose_multidevice_event (widget, event_motion->device, event_motion);
+ }
}
if (WIDGET_REALIZED_FOR_EVENT (widget, event))
g_signal_emit (widget, widget_signals[EVENT_AFTER], 0, event);
@@ -11553,6 +11676,263 @@ gtk_widget_set_support_multidevice (GtkWidget *widget,
gdk_window_set_support_multidevice (widget->window, support_multidevice);
}
+static GdkEventMotion *
+convert_event_to_motion (GdkEvent *event)
+{
+ GdkEventMotion *new_event;
+
+ new_event = (GdkEventMotion *) gdk_event_new (GDK_MOTION_NOTIFY);
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ new_event->window = g_object_ref (event->button.window);
+ new_event->send_event = TRUE;
+ new_event->time = event->button.time;
+ new_event->x = event->button.x;
+ new_event->y = event->button.y;
+
+ if (event->button.axes)
+ new_event->axes = g_memdup (event->button.axes,
+ sizeof (gdouble) * event->button.device->num_axes);
+
+ new_event->state = 0; /* FIXME */
+ new_event->is_hint = FALSE;
+ new_event->device = event->button.device;
+ new_event->x_root = event->button.x_root;
+ new_event->y_root = event->button.y_root;
+ break;
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ new_event->window = g_object_ref (event->crossing.window);
+ new_event->send_event = TRUE;
+ new_event->time = event->crossing.time;
+ new_event->x = event->crossing.x;
+ new_event->y = event->crossing.y;
+ new_event->axes = NULL; /* FIXME: not ideal for non-mice */
+ new_event->state = 0; /* FIXME */
+ new_event->is_hint = FALSE;
+ new_event->x_root = event->crossing.x_root;
+ new_event->y_root = event->crossing.y_root;
+ gdk_event_set_device ((GdkEvent *) new_event, gdk_event_get_device ((GdkEvent *) event));
+ break;
+ default:
+ g_warning ("Event with type %d can not be transformed to GdkEventMotion", event->type);
+ gdk_event_free ((GdkEvent *) new_event);
+ new_event = NULL;
+ }
+
+ return new_event;
+}
+
+static void
+device_group_device_added (GtkDeviceGroup *group,
+ GdkDevice *device,
+ GtkWidget *widget)
+{
+ GtkMultiDeviceData *data;
+ GtkDeviceGroup *old_group;
+ GdkEventMotion *new_event = NULL;
+ GtkWidget *event_widget;
+ GdkEvent *event;
+
+ data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
+
+ if (G_UNLIKELY (!data))
+ return;
+
+ /* Remove device from old group, if any */
+ old_group = g_hash_table_lookup (data->by_dev, device);
+
+ if (old_group)
+ gtk_device_group_remove_device (old_group, device);
+
+ g_hash_table_insert (data->by_dev,
+ g_object_ref (device),
+ g_object_ref (group));
+
+ event = gtk_get_current_event ();
+
+ if (!event)
+ return;
+
+ if (event->type == GDK_MOTION_NOTIFY)
+ new_event = (GdkEventMotion *) event;
+ else
+ {
+ event_widget = gtk_get_event_widget (event);
+
+ if (widget == event_widget)
+ new_event = convert_event_to_motion (event);
+
+ gdk_event_free (event);
+ }
+
+ if (new_event)
+ {
+ gtk_widget_event_internal (widget, (GdkEvent *) new_event);
+ gdk_event_free ((GdkEvent *) new_event);
+ }
+}
+
+static void
+device_group_device_removed (GtkDeviceGroup *group,
+ GdkDevice *device,
+ GtkWidget *widget)
+{
+ GtkMultiDeviceData *data;
+
+ data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
+
+ g_assert (data != NULL);
+
+ compose_multidevice_event (widget, device, NULL);
+ g_hash_table_remove (data->by_dev, device);
+}
+
+static void
+free_multidevice_data (GtkMultiDeviceData *data)
+{
+ g_list_foreach (data->groups, (GFunc) g_object_unref, NULL);
+ g_list_free (data->groups);
+
+ g_hash_table_destroy (data->by_dev);
+
+ g_slice_free (GtkMultiDeviceData, data);
+}
+
+/**
+ * gtk_widget_create_device_group:
+ * @widget: a #GtkWidget
+ *
+ * Creates a new #GtkDeviceGroup for @widget. devices can be added
+ * later through gtk_device_group_add_device(). Note that
+ * #GdkDevice<!-- -->s can only pertain to one #GtkDeviceGroup for a
+ * given #GtkWidget, so adding it to a new group will remove it from
+ * its previous one.
+ *
+ * Returns: a newly created #GtkDeviceGroup. This object is owned by
+ * @widget and must be destroyed through
+ * gtk_widget_remove_device_group().
+ *
+ * Since: 3.0
+ **/
+GtkDeviceGroup *
+gtk_widget_create_device_group (GtkWidget *widget)
+{
+ GtkMultiDeviceData *data;
+ GtkDeviceGroup *group;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ group = g_object_new (GTK_TYPE_DEVICE_GROUP, NULL);
+
+ g_signal_connect (group, "device-added",
+ G_CALLBACK (device_group_device_added), widget);
+ g_signal_connect (group, "device-removed",
+ G_CALLBACK (device_group_device_removed), widget);
+
+ data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
+
+ if (G_UNLIKELY (!data))
+ {
+ data = g_slice_new0 (GtkMultiDeviceData);
+ data->by_dev = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) g_object_unref);
+
+ g_object_set_qdata_full (G_OBJECT (widget),
+ quark_multidevice_data,
+ data,
+ (GDestroyNotify) free_multidevice_data);
+ }
+
+ data->groups = g_list_prepend (data->groups, group);
+
+ return group;
+}
+
+/**
+ * gtk_widget_remove_device_group:
+ * @widget: a #GtkWidget
+ * @group: a #GtkDeviceGroup
+ *
+ * If @group pertains to @widget, @group will be destroyed so no further
+ * #GtkWidget::multidevice-event<!-- -->s are emitted for it.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_remove_device_group (GtkWidget *widget,
+ GtkDeviceGroup *group)
+{
+ GtkMultiDeviceData *data;
+ GList *devices, *g;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GTK_IS_DEVICE_GROUP (group));
+
+ data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
+
+ if (G_UNLIKELY (!data))
+ return;
+
+ g = g_list_find (data->groups, group);
+
+ if (G_UNLIKELY (!g))
+ return;
+
+ devices = gtk_device_group_get_devices (group);
+
+ /* Free per-device data */
+ while (devices)
+ {
+ g_hash_table_remove (data->by_dev, devices->data);
+ devices = devices->next;
+ }
+
+ /* Free group */
+ data->groups = g_list_remove_link (data->groups, g);
+ g_object_unref (g->data);
+ g_list_free_1 (g);
+}
+
+/**
+ * gtk_widget_get_group_for_device:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ *
+ * Returns the #GtkDeviceGroup containing the #GdkDevice, or %NULL if
+ * there is none.
+ *
+ * Returns: a #GtkDeviceGroup, or %NULL.
+ *
+ * Since: 3.0
+ **/
+GtkDeviceGroup *
+gtk_widget_get_group_for_device (GtkWidget *widget,
+ GdkDevice *device)
+{
+ GtkMultiDeviceData *data;
+ GtkDeviceGroup *group = NULL;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+ g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+
+ data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
+
+ if (!data)
+ return NULL;
+
+ group = g_hash_table_lookup (data->by_dev, device);
+
+ return group;
+}
+
static void
_gtk_widget_set_has_focus (GtkWidget *widget,
gboolean has_focus)
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 15ec198..5320496 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -37,6 +37,7 @@
#include <gtk/gtkadjustment.h>
#include <gtk/gtkstyle.h>
#include <gtk/gtksettings.h>
+#include <gtk/gtkdevicegroup.h>
#include <atk/atk.h>
G_BEGIN_DECLS
@@ -202,6 +203,7 @@ typedef struct _GtkWidgetShapeInfo GtkWidgetShapeInfo;
typedef struct _GtkClipboard GtkClipboard;
typedef struct _GtkTooltip GtkTooltip;
typedef struct _GtkWindow GtkWindow;
+typedef struct _GtkMultiDeviceEvent GtkMultiDeviceEvent;
/**
@@ -533,6 +535,43 @@ struct _GtkWidgetClass
void (*_gtk_reserved7) (void);
};
+/**
+ * GtkMultiDeviceEventType:
+ * @GTK_EVENT_DEVICE_ADDED: A device was added to the device group.
+ * @GTK_EVENT_DEVICE_REMOVED: A device was removed from the device group.
+ * @GTK_EVENT_DEVICE_UPDATED: A device in the device group has updated its
+ * state.
+ *
+ * Provides a hint about the change that initiated the
+ * #GtkWidget::multidevice-event.
+ */
+typedef enum
+{
+ GTK_EVENT_DEVICE_ADDED,
+ GTK_EVENT_DEVICE_REMOVED,
+ GTK_EVENT_DEVICE_UPDATED
+} GtkMultiDeviceEventType;
+
+/**
+ * GtkMultiDeviceEvent:
+ * @type: the event type.
+ * @n_events: number of device events contained in this #GtkMultiDeviceEvent
+ * @events: an array of #GdkEventMotion structs.
+ * @updated_event: latest updated event, or %NULL if @type is %GDK_EVENT_DEVICE_REMOVED.
+ * @updated_device: device that triggered the event.
+ *
+ * The #GtkMultiDeviceEvent struct contains information about latest state of
+ * multiple device pointers. These devices are contained in a #GtkDeviceGroup.
+ */
+struct _GtkMultiDeviceEvent
+{
+ GtkMultiDeviceEventType type;
+ guint n_events;
+ GdkEventMotion **events;
+ GdkEventMotion *updated_event;
+ GdkDevice *updated_device;
+};
+
struct _GtkWidgetAuxInfo
{
gint x;
@@ -766,6 +805,11 @@ GdkPixmap * gtk_widget_get_snapshot (GtkWidget *widget,
gboolean gtk_widget_get_support_multidevice (GtkWidget *widget);
void gtk_widget_set_support_multidevice (GtkWidget *widget,
gboolean support_multidevice);
+GtkDeviceGroup * gtk_widget_get_group_for_device (GtkWidget *widget,
+ GdkDevice *device);
+GtkDeviceGroup * gtk_widget_create_device_group (GtkWidget *widget);
+void gtk_widget_remove_device_group (GtkWidget *widget,
+ GtkDeviceGroup *group);
/* Accessibility support */
AtkObject* gtk_widget_get_accessible (GtkWidget *widget);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]