dnd: Pass content to gdk_drag_begin()
Instead of just passing the GdkContentFormats, we are now passing the GdkContentProvider to gdk_drag_begin(). This means that GDK itself can now query the data from the provider directly instead of having to send selection events. Use this to provide the private API gdk_drag_context_write() that allows backends to pass an output stream that this data will be written to. Implement this as the mechanism for providing drag data on Wayland. And to make this all work, implement a content provider named GtkDragContent that is implemented by reverting to the old DND drag-data-get machinery inside GTK, so for widgets everything works just like before.
This commit is contained in:
@@ -84,12 +84,12 @@ gdk_broadway_drag_context_finalize (GObject *object)
|
||||
/* Drag Contexts */
|
||||
|
||||
GdkDragContext *
|
||||
_gdk_broadway_window_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentFormats *formats,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy)
|
||||
_gdk_broadway_window_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentProvider *content,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy)
|
||||
{
|
||||
GdkDragContext *new_context;
|
||||
|
||||
@@ -98,6 +98,7 @@ _gdk_broadway_window_drag_begin (GdkWindow *window,
|
||||
|
||||
new_context = g_object_new (GDK_TYPE_BROADWAY_DRAG_CONTEXT,
|
||||
"display", gdk_window_get_display (window),
|
||||
"content", content,
|
||||
NULL);
|
||||
|
||||
return new_context;
|
||||
|
||||
@@ -47,12 +47,12 @@ void gdk_broadway_window_set_nodes (GdkWindow *window,
|
||||
GPtrArray *node_textures);
|
||||
|
||||
void _gdk_broadway_window_register_dnd (GdkWindow *window);
|
||||
GdkDragContext * _gdk_broadway_window_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentFormats *formats,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy);
|
||||
GdkDragContext * _gdk_broadway_window_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentProvider *content,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy);
|
||||
void _gdk_broadway_window_translate (GdkWindow *window,
|
||||
cairo_region_t *area,
|
||||
gint dx,
|
||||
|
||||
173
gdk/gdkdnd.c
173
gdk/gdkdnd.c
@@ -29,6 +29,8 @@
|
||||
#include "gdkwindow.h"
|
||||
#include "gdkintl.h"
|
||||
#include "gdkcontentformats.h"
|
||||
#include "gdkcontentprovider.h"
|
||||
#include "gdkcontentserializer.h"
|
||||
#include "gdkcursor.h"
|
||||
#include "gdkenumtypes.h"
|
||||
#include "gdkeventsprivate.h"
|
||||
@@ -48,7 +50,9 @@ static struct {
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CONTENT,
|
||||
PROP_DISPLAY,
|
||||
PROP_FORMATS,
|
||||
N_PROPERTIES
|
||||
};
|
||||
|
||||
@@ -261,6 +265,12 @@ gdk_drag_context_set_property (GObject *gobject,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CONTENT:
|
||||
context->content = g_value_dup_object (value);
|
||||
if (context->content)
|
||||
context->formats = gdk_content_provider_ref_formats (context->content);
|
||||
break;
|
||||
|
||||
case PROP_DISPLAY:
|
||||
context->display = g_value_get_object (value);
|
||||
g_assert (context->display != NULL);
|
||||
@@ -282,10 +292,18 @@ gdk_drag_context_get_property (GObject *gobject,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CONTENT:
|
||||
g_value_set_object (value, context->content);
|
||||
break;
|
||||
|
||||
case PROP_DISPLAY:
|
||||
g_value_set_object (value, context->display);
|
||||
break;
|
||||
|
||||
case PROP_FORMATS:
|
||||
g_value_set_boxed (value, context->formats);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
@@ -298,6 +316,8 @@ gdk_drag_context_finalize (GObject *object)
|
||||
GdkDragContext *context = GDK_DRAG_CONTEXT (object);
|
||||
|
||||
contexts = g_list_remove (contexts, context);
|
||||
|
||||
g_clear_object (&context->content);
|
||||
g_clear_pointer (&context->formats, gdk_content_formats_unref);
|
||||
|
||||
if (context->source_window)
|
||||
@@ -352,6 +372,24 @@ gdk_drag_context_class_init (GdkDragContextClass *klass)
|
||||
object_class->set_property = gdk_drag_context_set_property;
|
||||
object_class->finalize = gdk_drag_context_finalize;
|
||||
|
||||
/**
|
||||
* GdkDragContext:content:
|
||||
*
|
||||
* The #GdkContentProvider or %NULL if the context is not a source-side
|
||||
* context.
|
||||
*
|
||||
* Since: 3.94
|
||||
*/
|
||||
properties[PROP_CONTENT] =
|
||||
g_param_spec_object ("content",
|
||||
"Content",
|
||||
"The content being dragged",
|
||||
GDK_TYPE_CONTENT_PROVIDER,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS |
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GdkDragContext:display:
|
||||
*
|
||||
@@ -369,6 +407,22 @@ gdk_drag_context_class_init (GdkDragContextClass *klass)
|
||||
G_PARAM_STATIC_STRINGS |
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GdkDragContext:formats:
|
||||
*
|
||||
* The possible formats that the context can provide its data in.
|
||||
*
|
||||
* Since: 3.94
|
||||
*/
|
||||
properties[PROP_FORMATS] =
|
||||
g_param_spec_boxed ("formats",
|
||||
"Formats",
|
||||
"The possible formats for data",
|
||||
GDK_TYPE_CONTENT_FORMATS,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS |
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GdkDragContext::cancel:
|
||||
* @context: The object on which the signal is emitted
|
||||
@@ -655,6 +709,125 @@ gdk_drag_get_selection (GdkDragContext *context)
|
||||
return GDK_DRAG_CONTEXT_GET_CLASS (context)->get_selection (context);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_drag_context_write_done (GObject *content,
|
||||
GAsyncResult *result,
|
||||
gpointer task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (gdk_content_provider_write_mime_type_finish (GDK_CONTENT_PROVIDER (content), result, &error))
|
||||
g_task_return_boolean (task, TRUE);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_drag_context_write_serialize_done (GObject *content,
|
||||
GAsyncResult *result,
|
||||
gpointer task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (gdk_content_serialize_finish (result, &error))
|
||||
g_task_return_boolean (task, TRUE);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_drag_context_write_async (GdkDragContext *context,
|
||||
const char *mime_type,
|
||||
GOutputStream *stream,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GdkContentFormats *formats, *mime_formats;
|
||||
GTask *task;
|
||||
GType gtype;
|
||||
|
||||
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
||||
g_return_if_fail (context->content);
|
||||
g_return_if_fail (mime_type != NULL);
|
||||
g_return_if_fail (mime_type == g_intern_string (mime_type));
|
||||
g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
|
||||
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
|
||||
g_return_if_fail (callback != NULL);
|
||||
|
||||
task = g_task_new (context, cancellable, callback, user_data);
|
||||
g_task_set_priority (task, io_priority);
|
||||
g_task_set_source_tag (task, gdk_drag_context_write_async);
|
||||
|
||||
formats = gdk_content_provider_ref_formats (context->content);
|
||||
if (gdk_content_formats_contain_mime_type (formats, mime_type))
|
||||
{
|
||||
gdk_content_provider_write_mime_type_async (context->content,
|
||||
mime_type,
|
||||
stream,
|
||||
io_priority,
|
||||
cancellable,
|
||||
gdk_drag_context_write_done,
|
||||
task);
|
||||
gdk_content_formats_unref (formats);
|
||||
return;
|
||||
}
|
||||
|
||||
mime_formats = gdk_content_formats_new ((const gchar *[2]) { mime_type, NULL }, 1);
|
||||
mime_formats = gdk_content_formats_union_serialize_gtypes (mime_formats);
|
||||
gtype = gdk_content_formats_match_gtype (formats, mime_formats);
|
||||
if (gtype != G_TYPE_INVALID)
|
||||
{
|
||||
GValue value = G_VALUE_INIT;
|
||||
GError *error = NULL;
|
||||
|
||||
g_assert (gtype != G_TYPE_INVALID);
|
||||
|
||||
g_value_init (&value, gtype);
|
||||
if (gdk_content_provider_get_value (context->content, &value, &error))
|
||||
{
|
||||
gdk_content_serialize_async (stream,
|
||||
mime_type,
|
||||
&value,
|
||||
io_priority,
|
||||
cancellable,
|
||||
gdk_drag_context_write_serialize_done,
|
||||
g_object_ref (task));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
g_value_unset (&value);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("No compatible formats to transfer clipboard contents."));
|
||||
}
|
||||
|
||||
gdk_content_formats_unref (mime_formats);
|
||||
gdk_content_formats_unref (formats);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gdk_drag_context_write_finish (GdkDragContext *context,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, context), FALSE);
|
||||
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_drag_context_write_async, FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_drop_read_async (GdkDragContext *context,
|
||||
const char **mime_types,
|
||||
|
||||
@@ -136,7 +136,7 @@ GInputStream * gdk_drop_read_finish (GdkDragContext *
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GdkDragContext * gdk_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentFormats *formats,
|
||||
GdkContentProvider *content,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy);
|
||||
|
||||
@@ -134,6 +134,7 @@ struct _GdkDragContext {
|
||||
GdkWindow *dest_window;
|
||||
GdkWindow *drag_window;
|
||||
|
||||
GdkContentProvider *content;
|
||||
GdkContentFormats *formats;
|
||||
GdkDragAction actions;
|
||||
GdkDragAction suggested_action;
|
||||
@@ -177,6 +178,16 @@ void gdk_drag_find_window (GdkDragContext *context,
|
||||
GdkWindow **dest_window,
|
||||
GdkDragProtocol *protocol);
|
||||
|
||||
void gdk_drag_context_write_async (GdkDragContext *context,
|
||||
const char *mime_type,
|
||||
GOutputStream *stream,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean gdk_drag_context_write_finish (GdkDragContext *context,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -6927,7 +6927,7 @@ gdk_window_register_dnd (GdkWindow *window)
|
||||
* gdk_drag_begin:
|
||||
* @window: the source window for this drag
|
||||
* @device: the device that controls this drag
|
||||
* @formats: (transfer none): the offered formats
|
||||
* @content: (transfer none): the offered content
|
||||
* @actions: the actions supported by this drag
|
||||
* @dx: the x offset to @device's position where the drag nominally started
|
||||
* @dy: the y offset to @device's position where the drag nominally started
|
||||
@@ -6940,14 +6940,19 @@ gdk_window_register_dnd (GdkWindow *window)
|
||||
* %NULL on error.
|
||||
*/
|
||||
GdkDragContext *
|
||||
gdk_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentFormats *formats,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy)
|
||||
gdk_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentProvider *content,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy)
|
||||
{
|
||||
return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->drag_begin (window, device, formats, actions, dx, dy);
|
||||
g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
|
||||
g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
|
||||
g_return_val_if_fail (gdk_window_get_display (window) == gdk_device_get_display (device), NULL);
|
||||
g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (content), NULL);
|
||||
|
||||
return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->drag_begin (window, device, content, actions, dx, dy);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -219,7 +219,7 @@ struct _GdkWindowImplClass
|
||||
void (* register_dnd) (GdkWindow *window);
|
||||
GdkDragContext * (*drag_begin) (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentFormats *formats,
|
||||
GdkContentProvider*content,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy);
|
||||
|
||||
@@ -548,12 +548,12 @@ create_dnd_window (GdkDisplay *display)
|
||||
}
|
||||
|
||||
GdkDragContext *
|
||||
_gdk_wayland_window_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentFormats *formats,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy)
|
||||
_gdk_wayland_window_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentProvider *content,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy)
|
||||
{
|
||||
GdkWaylandDragContext *context_wayland;
|
||||
GdkDragContext *context;
|
||||
@@ -566,11 +566,11 @@ _gdk_wayland_window_drag_begin (GdkWindow *window,
|
||||
|
||||
context_wayland = g_object_new (GDK_TYPE_WAYLAND_DRAG_CONTEXT,
|
||||
"display", display_wayland,
|
||||
"content", content,
|
||||
NULL);
|
||||
context = GDK_DRAG_CONTEXT (context_wayland);
|
||||
context->source_window = g_object_ref (window);
|
||||
context->is_source = TRUE;
|
||||
context->formats = gdk_content_formats_ref (formats);
|
||||
|
||||
gdk_drag_context_set_device (context, device);
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ void gdk_wayland_window_sync (GdkWindow *window);
|
||||
void _gdk_wayland_window_register_dnd (GdkWindow *window);
|
||||
GdkDragContext *_gdk_wayland_window_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentFormats *formats,
|
||||
GdkContentProvider *content,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy);
|
||||
|
||||
@@ -753,63 +753,6 @@ gdk_wayland_selection_lookup_requestor_buffer (GdkWindow *requestor)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_wayland_selection_source_handles_target (GdkWaylandSelection *wayland_selection,
|
||||
GdkAtom target)
|
||||
{
|
||||
GdkAtom atom;
|
||||
guint i;
|
||||
|
||||
if (target == NULL)
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < wayland_selection->source_targets->len; i++)
|
||||
{
|
||||
atom = g_array_index (wayland_selection->source_targets, GdkAtom, i);
|
||||
|
||||
if (atom == target)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_wayland_selection_request_target (GdkWaylandSelection *wayland_selection,
|
||||
GdkWindow *window,
|
||||
GdkAtom selection,
|
||||
GdkAtom target,
|
||||
gint fd)
|
||||
{
|
||||
if (wayland_selection->stored_selection.fd == fd &&
|
||||
wayland_selection->requested_target == target)
|
||||
return FALSE;
|
||||
|
||||
/* If we didn't issue gdk_wayland_selection_check_write() yet
|
||||
* on a previous fd, it will still linger here. Just close it,
|
||||
* as we can't have more than one fd on the fly.
|
||||
*/
|
||||
if (wayland_selection->stored_selection.fd >= 0)
|
||||
close (wayland_selection->stored_selection.fd);
|
||||
|
||||
wayland_selection->stored_selection.fd = fd;
|
||||
wayland_selection->requested_target = target;
|
||||
|
||||
if (window &&
|
||||
gdk_wayland_selection_source_handles_target (wayland_selection, target))
|
||||
{
|
||||
gdk_wayland_selection_emit_request (window, selection, target);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
close (fd);
|
||||
wayland_selection->stored_selection.fd = -1;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
data_source_target (void *data,
|
||||
struct wl_data_source *source,
|
||||
@@ -820,45 +763,48 @@ data_source_target (void *data,
|
||||
source, mime_type));
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_wayland_drag_context_write_done (GObject *context,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!gdk_drag_context_write_finish (GDK_DRAG_CONTEXT (context), result, &error))
|
||||
{
|
||||
GDK_NOTE(DND, g_printerr ("%p: failed to write stream: %s\n", context, error->message));
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
data_source_send (void *data,
|
||||
struct wl_data_source *source,
|
||||
const char *mime_type,
|
||||
int32_t fd)
|
||||
{
|
||||
GdkWaylandSelection *wayland_selection = data;
|
||||
GdkWindow *window;
|
||||
GdkAtom selection;
|
||||
GdkDragContext *context;
|
||||
GOutputStream *stream;
|
||||
|
||||
GDK_NOTE (EVENTS,
|
||||
g_message ("data source send, source = %p, mime_type = %s, fd = %d",
|
||||
source, mime_type, fd));
|
||||
|
||||
if (!mime_type)
|
||||
{
|
||||
close (fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source == wayland_selection->dnd_source)
|
||||
{
|
||||
window = wayland_selection->dnd_owner;
|
||||
selection = atoms[ATOM_DND];
|
||||
}
|
||||
else
|
||||
{
|
||||
close (fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window)
|
||||
context = gdk_wayland_drag_context_lookup_by_data_source (source);
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
if (!gdk_wayland_selection_request_target (wayland_selection, window,
|
||||
selection,
|
||||
gdk_atom_intern (mime_type, FALSE),
|
||||
fd))
|
||||
gdk_wayland_selection_check_write (wayland_selection);
|
||||
GDK_NOTE (DND, g_printerr ("%p: data source send request for %s on fd %d\n",
|
||||
source, mime_type, fd));
|
||||
|
||||
//mime_type = gdk_intern_mime_type (mime_type);
|
||||
mime_type = g_intern_string (mime_type);
|
||||
stream = g_unix_output_stream_new (fd, TRUE);
|
||||
|
||||
gdk_drag_context_write_async (context,
|
||||
mime_type,
|
||||
stream,
|
||||
G_PRIORITY_DEFAULT,
|
||||
NULL,
|
||||
gdk_wayland_drag_context_write_done,
|
||||
context);
|
||||
g_object_unref (stream);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -2857,25 +2857,25 @@ drag_context_ungrab (GdkDragContext *context)
|
||||
}
|
||||
|
||||
GdkDragContext *
|
||||
_gdk_x11_window_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentFormats *formats,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy)
|
||||
_gdk_x11_window_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentProvider *content,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy)
|
||||
{
|
||||
GdkDragContext *context;
|
||||
int x_root, y_root;
|
||||
|
||||
context = (GdkDragContext *) g_object_new (GDK_TYPE_X11_DRAG_CONTEXT,
|
||||
"display", gdk_window_get_display (window),
|
||||
"content", content,
|
||||
NULL);
|
||||
|
||||
context->is_source = TRUE;
|
||||
context->source_window = window;
|
||||
g_object_ref (window);
|
||||
|
||||
context->formats = gdk_content_formats_ref (formats);
|
||||
precache_target_list (context);
|
||||
|
||||
gdk_drag_context_set_device (context, device);
|
||||
|
||||
@@ -282,12 +282,12 @@ void _gdk_x11_cursor_display_finalize (GdkDisplay *display);
|
||||
|
||||
void _gdk_x11_window_register_dnd (GdkWindow *window);
|
||||
|
||||
GdkDragContext * _gdk_x11_window_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentFormats *formats,
|
||||
GdkDragAction actions,
|
||||
gint x_root,
|
||||
gint y_root);
|
||||
GdkDragContext * _gdk_x11_window_drag_begin (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkContentProvider *content,
|
||||
GdkDragAction actions,
|
||||
gint dx,
|
||||
gint dy);
|
||||
|
||||
GdkGrabStatus _gdk_x11_convert_grab_status (gint status);
|
||||
|
||||
|
||||
156
gtk/gtkdnd.c
156
gtk/gtkdnd.c
@@ -960,6 +960,150 @@ gtk_drag_dest_drop (GtkWidget *widget,
|
||||
* Source side *
|
||||
***************/
|
||||
|
||||
#define GTK_TYPE_DRAG_CONTENT (gtk_drag_content_get_type ())
|
||||
#define GTK_DRAG_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_DRAG_CONTENT, GtkDragContent))
|
||||
#define GTK_IS_DRAG_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_DRAG_CONTENT))
|
||||
#define GTK_DRAG_CONTENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_DRAG_CONTENT, GtkDragContentClass))
|
||||
#define GTK_IS_DRAG_CONTENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_DRAG_CONTENT))
|
||||
#define GTK_DRAG_CONTENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_DRAG_CONTENT, GtkDragContentClass))
|
||||
|
||||
typedef struct _GtkDragContent GtkDragContent;
|
||||
typedef struct _GtkDragContentClass GtkDragContentClass;
|
||||
|
||||
struct _GtkDragContent
|
||||
{
|
||||
GdkContentProvider parent;
|
||||
|
||||
GtkWidget *widget;
|
||||
GdkDragContext *context;
|
||||
GdkContentFormats *formats;
|
||||
guint32 time;
|
||||
};
|
||||
|
||||
struct _GtkDragContentClass
|
||||
{
|
||||
GdkContentProviderClass parent_class;
|
||||
};
|
||||
|
||||
GType gtk_drag_content_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_DEFINE_TYPE (GtkDragContent, gtk_drag_content, GDK_TYPE_CONTENT_PROVIDER)
|
||||
|
||||
static GdkContentFormats *
|
||||
gtk_drag_content_ref_formats (GdkContentProvider *provider)
|
||||
{
|
||||
GtkDragContent *content = GTK_DRAG_CONTENT (provider);
|
||||
|
||||
return gdk_content_formats_ref (content->formats);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_drag_content_write_mime_type_done (GObject *stream,
|
||||
GAsyncResult *result,
|
||||
gpointer task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream),
|
||||
result,
|
||||
NULL,
|
||||
&error))
|
||||
{
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_task_return_boolean (task, TRUE);
|
||||
}
|
||||
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_drag_content_write_mime_type_async (GdkContentProvider *provider,
|
||||
const char *mime_type,
|
||||
GOutputStream *stream,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkDragContent *content = GTK_DRAG_CONTENT (provider);
|
||||
GtkSelectionData sdata = { 0, };
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (content, cancellable, callback, user_data);
|
||||
g_task_set_priority (task, io_priority);
|
||||
g_task_set_source_tag (task, gtk_drag_content_write_mime_type_async);
|
||||
|
||||
sdata.selection = gdk_drag_get_selection (content->context);
|
||||
sdata.target = gdk_atom_intern (mime_type, FALSE);
|
||||
sdata.length = -1;
|
||||
sdata.display = gtk_widget_get_display (content->widget);
|
||||
|
||||
g_signal_emit_by_name (content->widget, "drag-data-get",
|
||||
content->context,
|
||||
&sdata,
|
||||
content->time);
|
||||
|
||||
if (sdata.length == -1)
|
||||
{
|
||||
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
_("Cannot provide contents as “%s”"), mime_type);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
g_task_set_task_data (task, sdata.data, g_free);
|
||||
|
||||
g_output_stream_write_all_async (stream,
|
||||
sdata.data,
|
||||
sdata.length,
|
||||
io_priority,
|
||||
cancellable,
|
||||
gtk_drag_content_write_mime_type_done,
|
||||
task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_drag_content_write_mime_type_finish (GdkContentProvider *provider,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, provider), FALSE);
|
||||
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gtk_drag_content_write_mime_type_async, FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_drag_content_finalize (GObject *object)
|
||||
{
|
||||
GtkDragContent *content = GTK_DRAG_CONTENT (object);
|
||||
|
||||
g_clear_object (&content->widget);
|
||||
g_clear_pointer (&content->formats, (GDestroyNotify) gdk_content_formats_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_drag_content_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_drag_content_class_init (GtkDragContentClass *class)
|
||||
{
|
||||
GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = gtk_drag_content_finalize;
|
||||
|
||||
provider_class->ref_formats = gtk_drag_content_ref_formats;
|
||||
provider_class->write_mime_type_async = gtk_drag_content_write_mime_type_async;
|
||||
provider_class->write_mime_type_finish = gtk_drag_content_write_mime_type_finish;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_drag_content_init (GtkDragContent *content)
|
||||
{
|
||||
}
|
||||
|
||||
/* Like gtk_drag_begin(), but also takes a GtkIconHelper
|
||||
* so that we can set the icon from the source site information
|
||||
*/
|
||||
@@ -979,6 +1123,7 @@ gtk_drag_begin_internal (GtkWidget *widget,
|
||||
GdkWindow *ipc_window;
|
||||
int dx, dy;
|
||||
GdkAtom selection;
|
||||
GtkDragContent *content;
|
||||
guint32 time;
|
||||
|
||||
ipc_widget = gtk_drag_get_ipc_widget (widget);
|
||||
@@ -1001,13 +1146,22 @@ gtk_drag_begin_internal (GtkWidget *widget,
|
||||
dx -= x;
|
||||
dy -= y;
|
||||
|
||||
context = gdk_drag_begin (ipc_window, device, target_list, actions, dx, dy);
|
||||
content = g_object_new (GTK_TYPE_DRAG_CONTENT, NULL);
|
||||
content->widget = g_object_ref (widget);
|
||||
content->formats = gdk_content_formats_ref (target_list);
|
||||
content->time = time;
|
||||
|
||||
context = gdk_drag_begin (ipc_window, device, GDK_CONTENT_PROVIDER (content), actions, dx, dy);
|
||||
if (context == NULL)
|
||||
{
|
||||
gtk_drag_release_ipc_widget (ipc_widget);
|
||||
g_object_unref (content);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
content->context = context;
|
||||
g_object_unref (content);
|
||||
|
||||
info = gtk_drag_get_source_info (context, TRUE);
|
||||
|
||||
info->ipc_widget = ipc_widget;
|
||||
|
||||
Reference in New Issue
Block a user