summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2016-01-08 21:03:01 +0100
committerCarlos Garnacho <carlosg@gnome.org>2016-01-19 00:00:05 +0100
commit5aee1b5e5ad3d50fbc9c6bd27594b5fdde8fd53d (patch)
tree0e96d28af0e46154412478a9a7cae60328e2f3da
parent1821c2df609b2877d732ad662a399e297ff56a41 (diff)
downloadgtk+-5aee1b5e5ad3d50fbc9c6bd27594b5fdde8fd53d.tar.gz
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.
-rw-r--r--gdk/gdkdnd.c185
-rw-r--r--gdk/gdkdnd.h4
-rw-r--r--gdk/gdkdndprivate.h22
3 files changed, 211 insertions, 0 deletions
diff --git a/gdk/gdkdnd.c b/gdk/gdkdnd.c
index 30096302f3..32b4528a5c 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_CHANGED,
+ 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_CHANGED] =
+ g_signal_new (P_("action-changed"),
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdkDragContextClass, action_changed),
+ 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 1aa5d07092..65c8c873f2 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 fe2a95dfc7..2404293b55 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_changed) (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