[gtk+/wip/garnacho/dnd-grab: 2/5] gdk: Allow internal management of source-side DnD



commit 8c6dec2f5c48f7952829ad2c3e62ff18f6a03e47
Author: Carlos Garnacho <carlosg gnome org>
Date:   Fri Jan 8 21:03:01 2016 +0100

    gdk: Allow internal management of source-side DnD
    
    We've traditionally left GTK+ to handle the input side of things,
    letting GDK only manage the windowing-specific messaging. This
    way of splitting responsibilities is not compatible however with
    some backends, we must fold then input management at the DnD stage
    into GDK (and backends) domain.
    
    The gdk_drag_context_manage_dnd() call is meant to be the entry
    point for this mode of operation, if the drag and drop operation
    becomes managed, the caller (i.e. gtkdnd.c) doesn't need to perform
    grabs, nor manage input events itself.
    
    As a consequence of this, different aspects now belong to the
    backend GdkDragContext implementation:
    - Because the caller doesn't see keyboard events anymore,
      keyboard navigation must be managed in GDK, so is the decision
      of the current action based on modifiers/button pressed.
    - Because the caller won't see input events in general, the lifetime
      of the drag and drop operation is now communicated through the
      ::drop-performed, ::dnd-finished and ::cancel events
    - Because the caller doesn't participate anymore on the action
      being chosen, the pointer cursor must be set by the backend.
      The caller is rather notified of the final action through the
      ::action signal.
    
    The caller is still responsible of dealing with the corresponding
    GdkSelection, ensuring its ownership and communicating the supported
    mimetypes.

 gdk/gdkdnd.c        |  185 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gdk/gdkdnd.h        |    4 +
 gdk/gdkdndprivate.h |   22 ++++++
 3 files changed, 211 insertions(+), 0 deletions(-)
---
diff --git a/gdk/gdkdnd.c b/gdk/gdkdnd.c
index 3009630..e9cb6a3 100644
--- a/gdk/gdkdnd.c
+++ b/gdk/gdkdnd.c
@@ -27,7 +27,19 @@
 #include "gdkdndprivate.h"
 #include "gdkdisplay.h"
 #include "gdkwindow.h"
+#include "gdkintl.h"
+#include "gdkenumtypes.h"
 
+enum {
+  CANCEL,
+  DROP_PERFORMED,
+  DND_FINISHED,
+  ACTION,
+  N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+static GList *contexts = NULL;
 
 /**
  * SECTION:dnd
@@ -217,6 +229,7 @@ G_DEFINE_TYPE (GdkDragContext, gdk_drag_context, G_TYPE_OBJECT)
 static void
 gdk_drag_context_init (GdkDragContext *context)
 {
+  contexts = g_list_prepend (contexts, context);
 }
 
 static void
@@ -224,6 +237,7 @@ gdk_drag_context_finalize (GObject *object)
 {
   GdkDragContext *context = GDK_DRAG_CONTEXT (object);
 
+  contexts = g_list_remove (contexts, context);
   g_list_free (context->targets);
 
   if (context->source_window)
@@ -241,6 +255,90 @@ gdk_drag_context_class_init (GdkDragContextClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   object_class->finalize = gdk_drag_context_finalize;
+
+  /**
+   * GdkDragContext::cancel:
+   *
+   * The drag and drop operation was cancelled.
+   *
+   * This signal will only be emitted if the #GdkDragContext manages
+   * the drag and drop operation. See gdk_drag_context_manage_dnd()
+   * for more information.
+   *
+   * Since: 3.20
+   */
+  signals[CANCEL] =
+    g_signal_new (P_("cancel"),
+                  G_TYPE_FROM_CLASS (object_class),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (GdkDragContextClass, cancel),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
+  /**
+   * GdkDragContext::drop-performed:
+   * @time: the time at which the drop happened.
+   *
+   * The drag and drop operation was perfomed on an accepting client.
+   *
+   * This signal will only be emitted if the #GdkDragContext manages
+   * the drag and drop operation. See gdk_drag_context_manage_dnd()
+   * for more information.
+   *
+   * Since: 3.20
+   */
+  signals[DROP_PERFORMED] =
+    g_signal_new (P_("drop-performed"),
+                  G_TYPE_FROM_CLASS (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GdkDragContextClass, drop_performed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__INT,
+                  G_TYPE_NONE, 1, G_TYPE_INT);
+
+  /**
+   * GdkDragContext::dnd-finished:
+   *
+   * The drag and drop operation was finished, the drag destination
+   * finished reading all data. The drag source can now free all
+   * miscellaneous data.
+   *
+   * This signal will only be emitted if the #GdkDragContext manages
+   * the drag and drop operation. See gdk_drag_context_manage_dnd()
+   * for more information.
+   *
+   * Since: 3.20
+   */
+  signals[DND_FINISHED] =
+    g_signal_new (P_("dnd-finished"),
+                  G_TYPE_FROM_CLASS (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GdkDragContextClass, dnd_finished),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
+  /**
+   * GdkDragContext::action:
+   * @action: The action currently chosen
+   *
+   * A new action is being chosen for the drag and drop operation.
+   *
+   * This signal will only be emitted if the #GdkDragContext manages
+   * the drag and drop operation. See gdk_drag_context_manage_dnd()
+   * for more information.
+   *
+   * Since: 3.20
+   */
+  signals[ACTION] =
+    g_signal_new (P_("action"),
+                  G_TYPE_FROM_CLASS (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GdkDragContextClass, action),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__FLAGS,
+                  G_TYPE_NONE, 1, GDK_TYPE_DRAG_ACTION);
 }
 
 /**
@@ -527,3 +625,90 @@ gdk_drag_drop_done (GdkDragContext *context,
   if (GDK_DRAG_CONTEXT_GET_CLASS (context)->drop_done)
     GDK_DRAG_CONTEXT_GET_CLASS (context)->drop_done (context, success);
 }
+
+/**
+ * gdk_drag_context_manage_dnd:
+ * @context: a #GdkDragContext
+ * @ipc_window: Window to use for IPC messaging/events
+ * @actions: the actions supported by the drag source
+ *
+ * Requests the drag and drop operation to be managed by @context.
+ * When a drag and drop operation becomes managed, the #GdkDragContext
+ * will internally handle all input and source-side #GdkEventDND events
+ * as required by the windowing system.
+ *
+ * Once the drag and drop operation is managed, the drag context will
+ * emit the following signals:
+ * - The #GdkDragContext::action signal whenever the final action to be
+ *   performed by the drag and drop operation changes.
+ * - The #GdkDragContext::drop-performed signal after the user performs
+ *   the drag and drop gesture (typically by releasing the mouse button).
+ * - The #GdkDragContext::dnd-finished signal after the drag and drop
+ *   operation concludes (after all #GdkSelection transfers happen).
+ * - The #GdkDragContext::cancel signal if the drag and drop operation is
+ *   finished but doesn't happen over an accepting destination, or is
+ *   cancelled through other means.
+ *
+ * Returns: #TRUE if the drag and drop operation is managed.
+ **/
+gboolean
+gdk_drag_context_manage_dnd (GdkDragContext *context,
+                             GdkWindow      *ipc_window,
+                             GdkDragAction   actions)
+{
+  g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), FALSE);
+  g_return_val_if_fail (GDK_IS_WINDOW (ipc_window), FALSE);
+
+  if (GDK_DRAG_CONTEXT_GET_CLASS (context)->manage_dnd)
+    return GDK_DRAG_CONTEXT_GET_CLASS (context)->manage_dnd (context, ipc_window,
+                                                             actions);
+
+  return FALSE;
+}
+
+void
+gdk_drag_context_set_cursor (GdkDragContext *context,
+                             GdkCursor      *cursor)
+{
+  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+
+  if (GDK_DRAG_CONTEXT_GET_CLASS (context)->set_cursor)
+    GDK_DRAG_CONTEXT_GET_CLASS (context)->set_cursor (context, cursor);
+}
+
+void
+gdk_drag_context_cancel (GdkDragContext *context)
+{
+  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+
+  g_signal_emit (context, signals[CANCEL], 0);
+}
+
+GList *
+gdk_drag_context_list (void)
+{
+  return contexts;
+}
+
+gboolean
+gdk_drag_context_handle_source_event (GdkEvent *event)
+{
+  GdkDragContext *context;
+  GList *l;
+
+  for (l = contexts; l; l = l->next)
+    {
+      context = l->data;
+
+      if (!context->is_source)
+        continue;
+
+      if (!GDK_DRAG_CONTEXT_GET_CLASS (context)->handle_event)
+        continue;
+
+      if (GDK_DRAG_CONTEXT_GET_CLASS (context)->handle_event (context, event))
+        return TRUE;
+    }
+
+  return FALSE;
+}
diff --git a/gdk/gdkdnd.h b/gdk/gdkdnd.h
index 1aa5d07..65c8c87 100644
--- a/gdk/gdkdnd.h
+++ b/gdk/gdkdnd.h
@@ -190,6 +190,10 @@ void            gdk_drag_context_set_hotspot (GdkDragContext *context,
                                               gint            hot_x,
                                               gint            hot_y);
 
+GDK_AVAILABLE_IN_3_20
+gboolean        gdk_drag_context_manage_dnd (GdkDragContext *context,
+                                             GdkWindow      *ipc_window,
+                                             GdkDragAction   actions);
 G_END_DECLS
 
 #endif /* __GDK_DND_H__ */
diff --git a/gdk/gdkdndprivate.h b/gdk/gdkdndprivate.h
index fe2a95d..5d31995 100644
--- a/gdk/gdkdndprivate.h
+++ b/gdk/gdkdndprivate.h
@@ -68,6 +68,21 @@ struct _GdkDragContextClass {
                                 gint             hot_y);
   void        (*drop_done)     (GdkDragContext   *context,
                                 gboolean          success);
+
+  gboolean    (*manage_dnd)     (GdkDragContext  *context,
+                                 GdkWindow       *ipc_window,
+                                 GdkDragAction    actions);
+  void        (*set_cursor)     (GdkDragContext  *context,
+                                 GdkCursor       *cursor);
+  void        (*cancel)         (GdkDragContext  *context);
+  void        (*drop_performed) (GdkDragContext  *context,
+                                 guint32          time);
+  void        (*dnd_finished)   (GdkDragContext  *context);
+
+  gboolean    (*handle_event)   (GdkDragContext  *context,
+                                 const GdkEvent  *event);
+  void        (*action)         (GdkDragContext  *context,
+                                 GdkDragAction    action);
 };
 
 struct _GdkDragContext {
@@ -91,6 +106,13 @@ struct _GdkDragContext {
   GdkDevice *device;
 };
 
+GList *  gdk_drag_context_list (void);
+
+void     gdk_drag_context_set_cursor          (GdkDragContext *context,
+                                               GdkCursor      *cursor);
+void     gdk_drag_context_cancel              (GdkDragContext *context);
+gboolean gdk_drag_context_handle_source_event (GdkEvent *event);
+
 G_END_DECLS
 
 #endif


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