[gtk+/gestures: 2/202] Add GtkGesture



commit 8f113e07fde88821907d75f6ed7803120a58bd5b
Author: Carlos Garnacho <carlosg gnome org>
Date:   Thu Jan 17 20:59:07 2013 +0100

    Add GtkGesture
    
    This a more specific abstract type that handles one or multiple
    streams of pointer/touch events.

 gtk/Makefile.am        |    2 +
 gtk/gtk.h              |    1 +
 gtk/gtkenums.h         |    7 +
 gtk/gtkgesture.c       |  964 ++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkgesture.h       |  133 +++++++
 gtk/gtkmarshalers.list |    1 +
 6 files changed, 1108 insertions(+), 0 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index a6f73df..b90c934 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -280,6 +280,7 @@ gtk_public_h_sources =              \
        gtkfontchooserdialog.h  \
        gtkfontchooserwidget.h  \
        gtkframe.h              \
+       gtkgesture.h            \
        gtkgrid.h               \
        gtkheaderbar.h          \
        gtkicontheme.h          \
@@ -772,6 +773,7 @@ gtk_base_c_sources =                \
        gtkfontchooserwidget.c  \
        gtkframe.c              \
        gtkgladecatalog.c       \
+       gtkgesture.c            \
        gtkgrid.c               \
        gtkheaderbar.c          \
        gtkhsla.c               \
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 8eb33a6..57e947b 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -106,6 +106,7 @@
 #include <gtk/gtkfontchooserdialog.h>
 #include <gtk/gtkfontchooserwidget.h>
 #include <gtk/gtkframe.h>
+#include <gtk/gtkgesture.h>
 #include <gtk/gtkgrid.h>
 #include <gtk/gtkheaderbar.h>
 #include <gtk/gtkicontheme.h>
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index 2787f2c..95844da 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -1049,4 +1049,11 @@ typedef enum
   GTK_INPUT_HINT_INHIBIT_OSK         = 1 << 7
 } GtkInputHints;
 
+typedef enum
+{
+  GTK_EVENT_SEQUENCE_NONE,
+  GTK_EVENT_SEQUENCE_CLAIMED,
+  GTK_EVENT_SEQUENCE_DENIED
+} GtkEventSequenceState;
+
 #endif /* __GTK_ENUMS_H__ */
diff --git a/gtk/gtkgesture.c b/gtk/gtkgesture.c
new file mode 100644
index 0000000..27cc1f5
--- /dev/null
+++ b/gtk/gtkgesture.c
@@ -0,0 +1,964 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2012, One Laptop Per Child.
+ * Copyright (C) 2014, Red Hat, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg gnome org>
+ */
+#include "config.h"
+#include <gtk/gtkgesture.h>
+#include "gtktypebuiltins.h"
+#include "gtkprivate.h"
+#include "gtkmarshalers.h"
+#include "gtkintl.h"
+
+typedef struct _GtkGesturePrivate GtkGesturePrivate;
+typedef struct _PointData PointData;
+
+enum {
+  PROP_N_POINTS = 1,
+  PROP_TOUCH_ONLY,
+};
+
+enum {
+  CHECK,
+  BEGIN,
+  END,
+  UPDATE,
+  SEQUENCE_STATE_CHANGED,
+  N_SIGNALS
+};
+
+struct _PointData
+{
+  GdkEvent *event;
+  guint state : 2;
+};
+
+struct _GtkGesturePrivate
+{
+  GHashTable *points;
+  GdkEventSequence *last_sequence;
+  GdkWindow *window;
+  GdkDevice *device;
+  guint n_points;
+  guint recognized : 1;
+  guint touch_only : 1;
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+#define BUTTONS_MASK (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkGesture, gtk_gesture, GTK_TYPE_EVENT_CONTROLLER)
+
+static void
+gtk_gesture_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  GtkGesturePrivate *priv = gtk_gesture_get_instance_private (GTK_GESTURE (object));
+
+  switch (prop_id)
+    {
+    case PROP_N_POINTS:
+      g_value_set_uint (value, priv->n_points);
+      break;
+    case PROP_TOUCH_ONLY:
+      g_value_set_boolean (value, priv->touch_only);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_gesture_set_property (GObject      *object,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  GtkGesturePrivate *priv = gtk_gesture_get_instance_private (GTK_GESTURE (object));
+
+  switch (prop_id)
+    {
+    case PROP_N_POINTS:
+      priv->n_points = g_value_get_uint (value);
+      break;
+    case PROP_TOUCH_ONLY:
+      gtk_gesture_set_touch_only (GTK_GESTURE (object),
+                                  g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_gesture_finalize (GObject *object)
+{
+  GtkGesturePrivate *priv = gtk_gesture_get_instance_private (GTK_GESTURE (object));
+
+  g_hash_table_destroy (priv->points);
+
+  G_OBJECT_CLASS (gtk_gesture_parent_class)->finalize (object);
+}
+
+static guint
+_gtk_gesture_effective_n_points (GtkGesture *gesture)
+{
+  GtkGesturePrivate *priv;
+  GHashTableIter iter;
+  guint n_points = 0;
+  PointData *data;
+
+  priv = gtk_gesture_get_instance_private (gesture);
+  g_hash_table_iter_init (&iter, priv->points);
+
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
+    {
+      if (data->state == GTK_EVENT_SEQUENCE_DENIED)
+        continue;
+      if (data->event->type == GDK_TOUCH_END ||
+          data->event->type == GDK_BUTTON_RELEASE)
+        continue;
+
+      n_points++;
+    }
+
+  return n_points;
+}
+
+static gboolean
+gtk_gesture_check_impl (GtkGesture *gesture)
+{
+  GtkGesturePrivate *priv;
+  guint n_points;
+
+  priv = gtk_gesture_get_instance_private (gesture);
+  n_points = _gtk_gesture_effective_n_points (gesture);
+
+  return n_points == priv->n_points;
+}
+
+static void
+_gtk_gesture_set_recognized (GtkGesture       *gesture,
+                             gboolean          recognized,
+                             GdkEventSequence *sequence)
+{
+  GtkGesturePrivate *priv;
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  if (priv->recognized == recognized)
+    return;
+
+  priv->recognized = recognized;
+
+  if (recognized)
+    g_signal_emit (gesture, signals[BEGIN], 0, sequence);
+  else
+    g_signal_emit (gesture, signals[END], 0, sequence);
+}
+
+static gboolean
+_gtk_gesture_do_check (GtkGesture *gesture)
+{
+  gboolean retval;
+
+  g_signal_emit (G_OBJECT (gesture), signals[CHECK], 0, &retval);
+  retval = retval != FALSE;
+
+  return retval;
+}
+
+static gboolean
+_gtk_gesture_check_recognized (GtkGesture       *gesture,
+                               GdkEventSequence *sequence)
+{
+  GtkGesturePrivate *priv = gtk_gesture_get_instance_private (gesture);
+  guint current_n_points;
+
+  current_n_points = _gtk_gesture_effective_n_points (gesture);
+
+  if (priv->recognized && current_n_points != priv->n_points)
+    _gtk_gesture_set_recognized (gesture, FALSE, sequence);
+  else if (!priv->recognized &&
+           current_n_points == priv->n_points &&
+           _gtk_gesture_do_check (gesture))
+    _gtk_gesture_set_recognized (gesture, TRUE, sequence);
+
+  return priv->recognized;
+}
+
+static gboolean
+_gtk_gesture_update_point (GtkGesture     *gesture,
+                           const GdkEvent *event,
+                           gboolean        add)
+{
+  GdkEventSequence *sequence;
+  GtkGesturePrivate *priv;
+  GdkDevice *device;
+  PointData *data;
+  gdouble x, y;
+
+  if (!gdk_event_get_coords (event, &x, &y))
+    return FALSE;
+
+  device = gdk_event_get_device (event);
+
+  if (!device)
+    return FALSE;
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  if (priv->device && priv->device != device)
+    return FALSE;
+
+  sequence = gdk_event_get_event_sequence (event);
+
+  if (!g_hash_table_lookup_extended (priv->points, sequence,
+                                     NULL, (gpointer *) &data))
+    {
+      if (!add)
+        return FALSE;
+
+      if (g_hash_table_size (priv->points) == 0)
+        {
+          priv->window = event->any.window;
+          priv->device = device;
+        }
+
+      data = g_new0 (PointData, 1);
+      g_hash_table_insert (priv->points, sequence, data);
+    }
+
+  if (data->event)
+    gdk_event_free (data->event);
+
+  data->event = gdk_event_copy (event);
+
+  return TRUE;
+}
+
+static void
+_gtk_gesture_remove_point (GtkGesture     *gesture,
+                           const GdkEvent *event)
+{
+  GdkEventSequence *sequence;
+  GtkGesturePrivate *priv;
+  GdkDevice *device;
+
+  sequence = gdk_event_get_event_sequence (event);
+  device = gdk_event_get_device (event);
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  g_hash_table_remove (priv->points, sequence);
+
+  if (device == priv->device &&
+      g_hash_table_size (priv->points) == 0)
+    {
+      priv->window = NULL;
+      priv->device = NULL;
+    }
+}
+
+static gboolean
+gtk_gesture_handle_event (GtkEventController *controller,
+                          const GdkEvent     *event)
+{
+  GtkGesture *gesture = GTK_GESTURE (controller);
+  GdkEventSequence *sequence;
+  GtkGesturePrivate *priv;
+
+  priv = gtk_gesture_get_instance_private (gesture);
+  sequence = gdk_event_get_event_sequence (event);
+  priv->last_sequence = sequence;
+
+#if 0
+  if (event->any.window != priv->window)
+    return FALSE;
+#endif
+
+  switch (event->type)
+    {
+    case GDK_BUTTON_PRESS:
+      if (priv->touch_only)
+        break;
+      /* Fall through */
+    case GDK_TOUCH_BEGIN:
+      if (_gtk_gesture_update_point (gesture, event, TRUE))
+        _gtk_gesture_check_recognized (gesture, sequence);
+      break;
+    case GDK_BUTTON_RELEASE:
+      if (priv->touch_only)
+        break;
+      /* Fall through */
+    case GDK_TOUCH_END:
+      if (_gtk_gesture_update_point (gesture, event, FALSE))
+        {
+          if (_gtk_gesture_check_recognized (gesture, sequence))
+            g_signal_emit (gesture, signals[UPDATE], 0, sequence);
+
+          _gtk_gesture_remove_point (gesture, event);
+        }
+      break;
+    case GDK_MOTION_NOTIFY:
+      if (priv->touch_only)
+        break;
+      if ((event->motion.state & BUTTONS_MASK) == 0)
+        break;
+
+      if (event->motion.is_hint)
+        gdk_event_request_motions (&event->motion);
+
+      /* Fall through */
+    case GDK_TOUCH_UPDATE:
+      if (_gtk_gesture_update_point (gesture, event, TRUE) &&
+          _gtk_gesture_check_recognized (gesture, sequence))
+        g_signal_emit (gesture, signals[UPDATE], 0, sequence);
+      break;
+    default:
+      /* Unhandled event */
+      return FALSE;
+    }
+
+  if (gtk_gesture_get_sequence_state (gesture, sequence) != GTK_EVENT_SEQUENCE_CLAIMED)
+    return FALSE;
+
+  return priv->recognized;
+}
+
+static void
+gtk_gesture_class_init (GtkGestureClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
+
+  object_class->get_property = gtk_gesture_get_property;
+  object_class->set_property = gtk_gesture_set_property;
+  object_class->finalize = gtk_gesture_finalize;
+
+  controller_class->handle_event = gtk_gesture_handle_event;
+
+  klass->check = gtk_gesture_check_impl;
+
+  g_object_class_install_property (object_class,
+                                   PROP_N_POINTS,
+                                   g_param_spec_uint ("n-points",
+                                                      P_("Number of points"),
+                                                      P_("Number of points needed "
+                                                         "to trigger the gesture"),
+                                                      1, G_MAXUINT, 1,
+                                                      GTK_PARAM_READWRITE |
+                                                      G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class,
+                                   PROP_TOUCH_ONLY,
+                                   g_param_spec_boolean ("touch-only",
+                                                         P_("Handle only touch events"),
+                                                         P_("Whether the gesture handles"
+                                                            " only touch events"),
+                                                         TRUE,
+                                                         GTK_PARAM_READWRITE));
+
+  signals[CHECK] =
+    g_signal_new ("check",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkGestureClass, check),
+                  g_signal_accumulator_true_handled,
+                  NULL, NULL,
+                  G_TYPE_BOOLEAN, 0);
+  signals[BEGIN] =
+    g_signal_new ("begin",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkGestureClass, begin),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1, G_TYPE_POINTER);
+  signals[END] =
+    g_signal_new ("end",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkGestureClass, end),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1, G_TYPE_POINTER);
+  signals[UPDATE] =
+    g_signal_new ("update",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkGestureClass, update),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1, G_TYPE_POINTER);
+  signals[SEQUENCE_STATE_CHANGED] =
+    g_signal_new ("sequence-state-changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkGestureClass, sequence_state_changed),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 2, G_TYPE_POINTER,
+                  GTK_TYPE_EVENT_SEQUENCE_STATE);
+}
+
+static void
+_gtk_gesture_update_evmask (GtkGesture *gesture)
+{
+  GtkGesturePrivate *priv;
+  GdkEventMask evmask;
+
+  priv = gtk_gesture_get_instance_private (gesture);
+  evmask = GDK_TOUCH_MASK;
+
+  if (!priv->touch_only)
+    evmask |= GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+      GDK_BUTTON_MOTION_MASK;
+
+  gtk_event_controller_set_event_mask (GTK_EVENT_CONTROLLER (gesture), evmask);
+}
+
+static void
+gtk_gesture_init (GtkGesture *gesture)
+{
+  GtkGesturePrivate *priv;
+
+  priv = gtk_gesture_get_instance_private (gesture);
+  priv->points = g_hash_table_new_full (NULL, NULL, NULL,
+                                        (GDestroyNotify) g_free);
+
+  if (g_getenv ("GTK_TEST_TOUCHSCREEN"))
+    priv->touch_only = FALSE;
+  else
+    priv->touch_only = TRUE;
+
+  _gtk_gesture_update_evmask (gesture);
+}
+
+/**
+ * gtk_gesture_get_device:
+ * @gesture: a #GtkGesture
+ *
+ * Returns the master #GdkDevice that is currently operating
+ * on @gesture, or %NULL if the gesture is not being interacted.
+ *
+ * Returns: a #GdkDevice, or %NULL.
+ *
+ * Since: 3.14
+ **/
+GdkDevice *
+gtk_gesture_get_device (GtkGesture *gesture)
+{
+  GtkGesturePrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  return priv->device;
+}
+
+/**
+ * gtk_gesture_get_sequence_state:
+ * @gesture: a #GtkGesture
+ * @sequence: a #GdkEventSequence
+ *
+ * Returns the @sequence state, as seen by @gesture.
+ *
+ * Returns: The sequence state in @gesture.
+ *
+ * Since: 3.14
+ **/
+GtkEventSequenceState
+gtk_gesture_get_sequence_state (GtkGesture       *gesture,
+                                GdkEventSequence *sequence)
+{
+  GtkGesturePrivate *priv;
+  PointData *data;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture),
+                        GTK_EVENT_SEQUENCE_NONE);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+  data = g_hash_table_lookup (priv->points, sequence);
+
+  if (!data)
+    return GTK_EVENT_SEQUENCE_NONE;
+
+  return data->state;
+}
+
+/**
+ * gtk_gesture_set_sequence_state:
+ * @gesture: a #GtkGesture
+ * @sequence: a #GdkEventSequence
+ * @state: the sequence state
+ *
+ * Sets the state of @sequence in @gesture.
+ *
+ * Returns: #TRUE if @sequence is handled by @gesture.
+ *
+ * Since: 3.14
+ **/
+gboolean
+gtk_gesture_set_sequence_state (GtkGesture            *gesture,
+                                GdkEventSequence      *sequence,
+                                GtkEventSequenceState  state)
+{
+  GtkGesturePrivate *priv;
+  PointData *data;
+  guint old_state;
+
+  g_return_if_fail (GTK_IS_GESTURE (gesture));
+  g_return_if_fail (state >= GTK_EVENT_SEQUENCE_NONE &&
+                    state <= GTK_EVENT_SEQUENCE_DENIED);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+  data = g_hash_table_lookup (priv->points, sequence);
+
+  if (!data || data->state == state)
+    return FALSE;
+
+  old_state = data->state;
+  data->state = state;
+  g_signal_emit (gesture, signals[SEQUENCE_STATE_CHANGED], 0,
+                 sequence, old_state);
+  return TRUE;
+}
+
+/**
+ * gtk_gesture_get_sequences:
+ * @gesture: a #GtkGesture
+ *
+ * Returns the list of #GdkEventSequence<!-- -->s currently being interpreted
+ * by @gesture
+ *
+ * Returns: (transfer container) (element-type:Gdk.EventSequence): A list
+ *          of #GdkEventSequence<!-- -->s, the list elements are owned by GTK+
+ *          and must not be freed or modified, the list itself must be deleted
+ *          through g_list_free()
+ **/
+GList *
+gtk_gesture_get_sequences (GtkGesture *gesture)
+{
+  GdkEventSequence *sequence;
+  GtkGesturePrivate *priv;
+  GList *sequences = NULL;
+  GHashTableIter iter;
+  PointData *data;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+  g_hash_table_iter_init (&iter, priv->points);
+
+  while (g_hash_table_iter_next (&iter, (gpointer *) &sequence, (gpointer *) &data))
+    {
+      if (data->state == GTK_EVENT_SEQUENCE_DENIED)
+        continue;
+      if (data->event->type == GDK_TOUCH_END ||
+          data->event->type == GDK_BUTTON_RELEASE)
+        continue;
+
+      sequences = g_list_prepend (sequences, sequence);
+    }
+
+  return sequences;
+}
+
+/**
+ * gtk_gesture_get_last_updated_sequence:
+ * @gesture: a #GtkGesture
+ *
+ * Returns the #GdkEventSequence that was last updated on @gesture.
+ *
+ * Returns: The last updated sequence.
+ *
+ * Since: 3.14
+ **/
+GdkEventSequence *
+gtk_gesture_get_last_updated_sequence (GtkGesture *gesture)
+{
+  GtkGesturePrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  return priv->last_sequence;
+}
+
+const GdkEvent *
+gtk_gesture_get_last_event (GtkGesture       *gesture,
+                            GdkEventSequence *sequence)
+{
+  GtkGesturePrivate *priv;
+  PointData *data;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+  data = g_hash_table_lookup (priv->points, sequence);
+
+  if (!data)
+    return NULL;
+
+  return data->event;
+}
+
+/**
+ * gtk_gesture_get_point:
+ * @gesture: a #GtkGesture
+ * @sequence: (allow-none): a #GdkEventSequence, or %NULL for pointer events
+ * @x: (out) (allow-none): return location for X axis of the sequence coordinates
+ * @y: (out) (allow-none): return location for Y axis of the sequence coordinates
+ *
+ * If @sequence is currently being interpreted by @gesture, this
+ * function returns %TRUE and fills in @x and @y with the last coordinates
+ * stored for that event sequence.
+ *
+ * Returns: %TRUE if @sequence is currently interpreted
+ *
+ * Since: 3.14
+ **/
+gboolean
+gtk_gesture_get_point (GtkGesture       *gesture,
+                       GdkEventSequence *sequence,
+                       gdouble          *x,
+                       gdouble          *y)
+{
+  GtkGesturePrivate *priv;
+  PointData *data;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  if (!g_hash_table_lookup_extended (priv->points, sequence,
+                                     NULL, (gpointer *) &data))
+    return FALSE;
+
+  return gdk_event_get_coords (data->event, x, y);
+}
+
+/**
+ * gtk_gesture_get_last_update_time:
+ * @gesture: a #GtkGesture
+ * @sequence: (allow-none): a #GdkEventSequence, or %NULL for pointer events
+ * @evtime: (out) (allow-none): return location for last update time
+ *
+ * If @sequence is being interpreted by @gesture, this function
+ * returns %TRUE and fills @evtime with the last event time it
+ * received from that @sequence.
+ *
+ * Returns: %TRUE if @sequence is currently interpreted
+ *
+ * Since: 3.14
+ **/
+gboolean
+gtk_gesture_get_last_update_time (GtkGesture       *gesture,
+                                  GdkEventSequence *sequence,
+                                  guint32          *evtime)
+{
+  GtkGesturePrivate *priv;
+  PointData *data;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  if (!g_hash_table_lookup_extended (priv->points, sequence,
+                                     NULL, (gpointer *) &data))
+    return FALSE;
+
+  if (evtime)
+    *evtime = gdk_event_get_time (data->event);
+
+  return TRUE;
+};
+
+/**
+ * gtk_gesture_get_last_event_type:
+ * @gesture: a #GtkGesture
+ * @sequence: a #GdkEventSequence
+ *
+ * Returns the last event type that was processed for @sequence.
+ *
+ * Returns: the last event type.
+ *
+ * Since: 3.14
+ **/
+GdkEventType
+gtk_gesture_get_last_event_type (GtkGesture       *gesture,
+                                 GdkEventSequence *sequence)
+{
+  GtkGesturePrivate *priv;
+  PointData *data;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), GDK_NOTHING);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  if (!g_hash_table_lookup_extended (priv->points, sequence,
+                                     NULL, (gpointer *) &data))
+    return GDK_NOTHING;
+
+  return data->event->type;
+}
+
+/**
+ * gtk_gesture_get_bounding_box:
+ * @gesture: a #GtkGesture
+ * @rect: (out): bounding box containing all active touches.
+ *
+ * If there are touch sequences being currently handled by @gesture,
+ * this function returns #TRUE and fills in @rect with the bounding
+ * box containing all active touches. Otherwise, #FALSE will be
+ * returned.
+ *
+ * Returns: #TRUE if there are active touches, #FALSE otherwise
+ *
+ * Since: 3.14
+ **/
+gboolean
+gtk_gesture_get_bounding_box (GtkGesture   *gesture,
+                              GdkRectangle *rect)
+{
+  GtkGesturePrivate *priv;
+  gdouble x1, y1, x2, y2;
+  GHashTableIter iter;
+  guint n_points = 0;
+  PointData *data;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+  g_return_val_if_fail (rect != NULL, FALSE);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+  x1 = y1 = G_MAXDOUBLE;
+  x2 = y2 = -G_MAXDOUBLE;
+
+  g_hash_table_iter_init (&iter, priv->points);
+
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
+    {
+      gdouble x, y;
+
+      if (data->state == GTK_EVENT_SEQUENCE_DENIED)
+        continue;
+      if (data->event->type == GDK_TOUCH_END ||
+          data->event->type == GDK_BUTTON_RELEASE)
+        continue;
+
+      gdk_event_get_coords (data->event, &x, &y);
+      n_points++;
+      x1 = MIN (x1, x);
+      y1 = MIN (y1, y);
+      x2 = MAX (x2, x);
+      y2 = MAX (y2, y);
+    }
+
+  if (n_points == 0)
+    return FALSE;
+
+  rect->x = x1;
+  rect->y = y1;
+  rect->width = x2 - x1;
+  rect->height = y2 - y1;
+
+  return TRUE;
+}
+
+
+/**
+ * gtk_gesture_get_bounding_box_center:
+ * @gesture: a #GtkGesture
+ * @x: (out): X coordinate for the bounding box center
+ * @y: (out): Y coordinate for the bounding box center
+ *
+ * If there are touch sequences being currently handled by @gesture,
+ * this function returns #TRUE and fills in @x and @y with the center
+ * of the bounding box containing all active touches. Otherwise, #FALSE
+ * will be returned.
+ *
+ * Returns: #FALSE if no active touches are present, #TRUE otherwise
+ *
+ * Since: 3.14
+ **/
+gboolean
+gtk_gesture_get_bounding_box_center (GtkGesture *gesture,
+                                     gdouble    *x,
+                                     gdouble    *y)
+{
+  GdkRectangle rect;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+  g_return_val_if_fail (x != NULL && y != NULL, FALSE);
+
+  if (!gtk_gesture_get_bounding_box (gesture, &rect))
+    return FALSE;
+
+  *x = rect.x + rect.width / 2;
+  *y = rect.y + rect.height / 2;
+  return TRUE;
+}
+
+/**
+ * gtk_gesture_is_active:
+ * @gesture: a #GtkGesture
+ *
+ * Returns %TRUE if the gesture is currently active.
+ * A gesture is active meanwhile there are touch sequences
+ * interacting with it.
+ *
+ * Returns: %TRUE if gesture is active.
+ *
+ * Since: 3.14
+ **/
+gboolean
+gtk_gesture_is_active (GtkGesture *gesture)
+{
+  GtkGesturePrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  return g_hash_table_size (priv->points) != 0;
+}
+
+/**
+ * gtk_gesture_is_recognized:
+ * @gesture: a #GtkGesture
+ *
+ * Returns %TRUE if the gesture is currently recognized.
+ * A gesture is recognized if there are as many interacting
+ * touch sequences as required by @gesture, and #GtkGesture::check
+ * returned #TRUE for the sequences being currently interpreted.
+ *
+ * Returns: %TRUE if gesture is recognized.
+ *
+ * Since: 3.14
+ **/
+gboolean
+gtk_gesture_is_recognized (GtkGesture *gesture)
+{
+  GtkGesturePrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  return priv->recognized;
+}
+
+/**
+ * gtk_gesture_check:
+ * @gesture: a #GtkGesture
+ *
+ * Triggers a check on the @gesture, this should be rarely needed,
+ * as the gesture will check the state after handling any event.
+ *
+ * Returns: #TRUE if the gesture is recognized.
+ *
+ * Since: 3.14
+ **/
+gboolean
+gtk_gesture_check (GtkGesture *gesture)
+{
+  GtkGesturePrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  return _gtk_gesture_check_recognized (gesture, priv->last_sequence);
+}
+
+/**
+ * gtk_gesture_get_touch_only:
+ * @gesture: a #GtkGesture
+ *
+ * Returns #TRUE if the gesture is only triggered by touch events.
+ *
+ * Returns: #TRUE if the gesture only handles touch events.
+ *
+ * Since: 3.14
+ **/
+gboolean
+gtk_gesture_get_touch_only (GtkGesture *gesture)
+{
+  GtkGesturePrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  return priv->touch_only;
+}
+
+/**
+ * gtk_gesture_set_touch_only:
+ * @gesture: a #GtkGesture
+ * @touch_only: whether @gesture handles only touch events
+ *
+ * If @touch_only is #TRUE, @gesture will only handle events of type
+ * #GDK_TOUCH_BEGIN, #GDK_TOUCH_UPDATE or #GDK_TOUCH_END. If #FALSE,
+ * mouse events will be handled too.
+ *
+ * Since: 3.14
+ **/
+void
+gtk_gesture_set_touch_only (GtkGesture *gesture,
+                            gboolean    touch_only)
+{
+  GtkGesturePrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  touch_only = touch_only != FALSE;
+
+  if (priv->touch_only == touch_only)
+    return;
+
+  priv->touch_only = touch_only;
+  g_object_notify (G_OBJECT (gesture), "touch-only");
+  _gtk_gesture_update_evmask (gesture);
+}
+
+/**
+ * gtk_gesture_handles_sequence:
+ * @gesture: a #GtkGesture
+ * @sequence: a #GdkEventSequence
+ *
+ * Returns #TRUE if @gesture is currently handling events corresponding to
+ * @sequence.
+ *
+ * Returns: #TRUE if @gesture is handling @sequence.
+ *
+ * Since: 3.14
+ **/
+gboolean
+gtk_gesture_handles_sequence (GtkGesture       *gesture,
+                              GdkEventSequence *sequence)
+{
+  GtkGesturePrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
+
+  priv = gtk_gesture_get_instance_private (gesture);
+
+  return g_hash_table_contains (priv->points, sequence);
+}
diff --git a/gtk/gtkgesture.h b/gtk/gtkgesture.h
new file mode 100644
index 0000000..a9296c7
--- /dev/null
+++ b/gtk/gtkgesture.h
@@ -0,0 +1,133 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2012, One Laptop Per Child.
+ * Copyright (C) 2014, Red Hat, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg gnome org>
+ */
+#ifndef __GTK_GESTURE_H__
+#define __GTK_GESTURE_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtkeventcontroller.h>
+#include <gtk/gtkeventcontroller.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_GESTURE         (gtk_gesture_get_type ())
+#define GTK_GESTURE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_GESTURE, GtkGesture))
+#define GTK_GESTURE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_GESTURE, GtkGestureClass))
+#define GTK_IS_GESTURE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_GESTURE))
+#define GTK_IS_GESTURE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_GESTURE))
+#define GTK_GESTURE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_GESTURE, GtkGestureClass))
+
+typedef struct _GtkGesture GtkGesture;
+typedef struct _GtkGestureClass GtkGestureClass;
+
+struct _GtkGesture
+{
+  GtkEventController parent_instance;
+};
+
+struct _GtkGestureClass
+{
+  GtkEventControllerClass parent_class;
+
+  gboolean (* check)  (GtkGesture       *gesture);
+
+  void     (* begin)  (GtkGesture       *gesture,
+                       GdkEventSequence *sequence);
+  void     (* update) (GtkGesture       *gesture,
+                       GdkEventSequence *sequence);
+  void     (* end)    (GtkGesture       *gesture,
+                       GdkEventSequence *sequence);
+
+  void     (* sequence_state_changed) (GtkGesture            *gesture,
+                                       GdkEventSequence      *sequence,
+                                       GtkEventSequenceState  state);
+
+  /*< private >*/
+  gpointer padding[10];
+};
+
+GDK_AVAILABLE_IN_3_14
+GType       gtk_gesture_get_type             (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_14
+GdkDevice * gtk_gesture_get_device           (GtkGesture       *gesture);
+
+GDK_AVAILABLE_IN_3_14
+GtkEventSequenceState
+            gtk_gesture_get_sequence_state   (GtkGesture       *gesture,
+                                              GdkEventSequence *sequence);
+GDK_AVAILABLE_IN_3_14
+gboolean    gtk_gesture_set_sequence_state   (GtkGesture            *gesture,
+                                              GdkEventSequence      *sequence,
+                                              GtkEventSequenceState  state);
+GDK_AVAILABLE_IN_3_14
+GList     * gtk_gesture_get_sequences        (GtkGesture       *gesture);
+
+GDK_AVAILABLE_IN_3_14
+GdkEventSequence * gtk_gesture_get_last_updated_sequence
+                                             (GtkGesture       *gesture);
+
+GDK_AVAILABLE_IN_3_14
+gboolean    gtk_gesture_handles_sequence     (GtkGesture       *gesture,
+                                              GdkEventSequence *sequence);
+
+GDK_AVAILABLE_IN_3_14
+const GdkEvent *
+            gtk_gesture_get_last_event       (GtkGesture       *gesture,
+                                              GdkEventSequence *sequence);
+GDK_AVAILABLE_IN_3_14
+gboolean    gtk_gesture_get_point            (GtkGesture       *gesture,
+                                              GdkEventSequence *sequence,
+                                              gdouble          *x,
+                                              gdouble          *y);
+GDK_AVAILABLE_IN_3_14
+gboolean    gtk_gesture_get_last_update_time (GtkGesture       *gesture,
+                                              GdkEventSequence *sequence,
+                                              guint32          *evtime);
+
+GDK_AVAILABLE_IN_3_14
+gboolean    gtk_gesture_get_bounding_box     (GtkGesture       *gesture,
+                                              GdkRectangle     *rect);
+GDK_AVAILABLE_IN_3_14
+gboolean    gtk_gesture_get_bounding_box_center
+                                             (GtkGesture       *gesture,
+                                              gdouble          *x,
+                                              gdouble          *y);
+GDK_AVAILABLE_IN_3_14
+gboolean    gtk_gesture_is_active            (GtkGesture       *gesture);
+
+GDK_AVAILABLE_IN_3_14
+gboolean    gtk_gesture_is_recognized        (GtkGesture       *gesture);
+
+GDK_AVAILABLE_IN_3_14
+gboolean    gtk_gesture_check                (GtkGesture       *gesture);
+
+GDK_AVAILABLE_IN_3_14
+gboolean    gtk_gesture_get_touch_only       (GtkGesture       *gesture);
+
+GDK_AVAILABLE_IN_3_14
+void        gtk_gesture_set_touch_only       (GtkGesture       *gesture,
+                                              gboolean          touch_only);
+
+G_END_DECLS
+
+#endif /* __GTK_GESTURE_H__ */
diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list
index 31977fe..ab6983d 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -52,6 +52,7 @@ OBJECT:VOID
 STRING:DOUBLE
 STRING:STRING
 VOID:DOUBLE
+VOID:DOUBLE,DOUBLE
 VOID:BOOLEAN
 VOID:BOOLEAN,BOOLEAN,BOOLEAN
 VOID:BOXED


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