forked from AuroraMiddleware/gtk
4c150d8eb5
Remove all the old 2.x and 3.x version annotations. GTK+ 4 is a new start, and from the perspective of a GTK+ 4 developer all these APIs have been around since the beginning.
953 lines
27 KiB
C
953 lines
27 KiB
C
/* GDK - The GIMP Drawing Kit
|
|
* Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* 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/>.
|
|
*/
|
|
|
|
/*
|
|
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
|
* file for a list of people on the GTK+ Team. See the ChangeLog
|
|
* files for a list of changes. These files are distributed with
|
|
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gdkdndprivate.h"
|
|
#include "gdkdisplay.h"
|
|
#include "gdkwindow.h"
|
|
#include "gdkintl.h"
|
|
#include "gdkcontentformats.h"
|
|
#include "gdkcontentprovider.h"
|
|
#include "gdkcontentserializer.h"
|
|
#include "gdkcursor.h"
|
|
#include "gdkenumtypes.h"
|
|
#include "gdkeventsprivate.h"
|
|
|
|
static struct {
|
|
GdkDragAction action;
|
|
const gchar *name;
|
|
GdkCursor *cursor;
|
|
} drag_cursors[] = {
|
|
{ GDK_ACTION_DEFAULT, NULL, NULL },
|
|
{ GDK_ACTION_ASK, "dnd-ask", NULL },
|
|
{ GDK_ACTION_COPY, "dnd-copy", NULL },
|
|
{ GDK_ACTION_MOVE, "dnd-move", NULL },
|
|
{ GDK_ACTION_LINK, "dnd-link", NULL },
|
|
{ 0, "dnd-none", NULL },
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_CONTENT,
|
|
PROP_DISPLAY,
|
|
PROP_FORMATS,
|
|
N_PROPERTIES
|
|
};
|
|
|
|
enum {
|
|
CANCEL,
|
|
DROP_PERFORMED,
|
|
DND_FINISHED,
|
|
ACTION_CHANGED,
|
|
N_SIGNALS
|
|
};
|
|
|
|
static GParamSpec *properties[N_PROPERTIES] = { NULL, };
|
|
static guint signals[N_SIGNALS] = { 0 };
|
|
static GList *contexts = NULL;
|
|
|
|
/**
|
|
* SECTION:dnd
|
|
* @title: Drag And Drop
|
|
* @short_description: Functions for controlling drag and drop handling
|
|
*
|
|
* These functions provide a low level interface for drag and drop.
|
|
* The X backend of GDK supports both the Xdnd and Motif drag and drop
|
|
* protocols transparently, the Win32 backend supports the WM_DROPFILES
|
|
* protocol.
|
|
*
|
|
* GTK+ provides a higher level abstraction based on top of these functions,
|
|
* and so they are not normally needed in GTK+ applications.
|
|
* See the [Drag and Drop][gtk3-Drag-and-Drop] section of
|
|
* the GTK+ documentation for more information.
|
|
*/
|
|
|
|
/**
|
|
* GdkDragContext:
|
|
*
|
|
* The GdkDragContext struct contains only private fields and
|
|
* should not be accessed directly.
|
|
*/
|
|
|
|
/**
|
|
* gdk_drag_context_get_display:
|
|
* @context: a #GdkDragContext
|
|
*
|
|
* Gets the #GdkDisplay that the drag context was created for.
|
|
*
|
|
* Returns: (transfer none): a #GdkDisplay
|
|
**/
|
|
GdkDisplay *
|
|
gdk_drag_context_get_display (GdkDragContext *context)
|
|
{
|
|
return context->display;
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_context_get_formats:
|
|
* @context: a #GdkDragContext
|
|
*
|
|
* Retrieves the formats supported by this context.
|
|
*
|
|
* Returns: (transfer none): a #GdkContentFormats
|
|
**/
|
|
GdkContentFormats *
|
|
gdk_drag_context_get_formats (GdkDragContext *context)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
|
|
|
|
return context->formats;
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_context_get_actions:
|
|
* @context: a #GdkDragContext
|
|
*
|
|
* Determines the bitmask of actions proposed by the source if
|
|
* gdk_drag_context_get_suggested_action() returns %GDK_ACTION_ASK.
|
|
*
|
|
* Returns: the #GdkDragAction flags
|
|
**/
|
|
GdkDragAction
|
|
gdk_drag_context_get_actions (GdkDragContext *context)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_ACTION_DEFAULT);
|
|
|
|
return context->actions;
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_context_get_suggested_action:
|
|
* @context: a #GdkDragContext
|
|
*
|
|
* Determines the suggested drag action of the context.
|
|
*
|
|
* Returns: a #GdkDragAction value
|
|
**/
|
|
GdkDragAction
|
|
gdk_drag_context_get_suggested_action (GdkDragContext *context)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), 0);
|
|
|
|
return context->suggested_action;
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_context_get_selected_action:
|
|
* @context: a #GdkDragContext
|
|
*
|
|
* Determines the action chosen by the drag destination.
|
|
*
|
|
* Returns: a #GdkDragAction value
|
|
**/
|
|
GdkDragAction
|
|
gdk_drag_context_get_selected_action (GdkDragContext *context)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), 0);
|
|
|
|
return context->action;
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_context_get_source_window:
|
|
* @context: a #GdkDragContext
|
|
*
|
|
* Returns the #GdkWindow where the DND operation started.
|
|
*
|
|
* Returns: (transfer none): a #GdkWindow
|
|
**/
|
|
GdkWindow *
|
|
gdk_drag_context_get_source_window (GdkDragContext *context)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
|
|
|
|
return context->source_window;
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_context_get_dest_window:
|
|
* @context: a #GdkDragContext
|
|
*
|
|
* Returns the destination window for the DND operation.
|
|
*
|
|
* Returns: (transfer none): a #GdkWindow
|
|
**/
|
|
GdkWindow *
|
|
gdk_drag_context_get_dest_window (GdkDragContext *context)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
|
|
|
|
return context->dest_window;
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_context_set_device:
|
|
* @context: a #GdkDragContext
|
|
* @device: a #GdkDevice
|
|
*
|
|
* Associates a #GdkDevice to @context, so all Drag and Drop events
|
|
* for @context are emitted as if they came from this device.
|
|
*/
|
|
void
|
|
gdk_drag_context_set_device (GdkDragContext *context,
|
|
GdkDevice *device)
|
|
{
|
|
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
|
g_return_if_fail (GDK_IS_DEVICE (device));
|
|
|
|
if (context->device)
|
|
g_object_unref (context->device);
|
|
|
|
context->device = device;
|
|
|
|
if (context->device)
|
|
g_object_ref (context->device);
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_context_get_device:
|
|
* @context: a #GdkDragContext
|
|
*
|
|
* Returns the #GdkDevice associated to the drag context.
|
|
*
|
|
* Returns: (transfer none): The #GdkDevice associated to @context.
|
|
**/
|
|
GdkDevice *
|
|
gdk_drag_context_get_device (GdkDragContext *context)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
|
|
|
|
return context->device;
|
|
}
|
|
|
|
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
|
|
gdk_drag_context_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GdkDragContext *context = GDK_DRAG_CONTEXT (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);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdk_drag_context_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GdkDragContext *context = GDK_DRAG_CONTEXT (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;
|
|
}
|
|
}
|
|
|
|
static void
|
|
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)
|
|
g_object_unref (context->source_window);
|
|
|
|
if (context->dest_window)
|
|
g_object_unref (context->dest_window);
|
|
|
|
G_OBJECT_CLASS (gdk_drag_context_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gdk_drag_context_read_local_async (GdkDragContext *context,
|
|
GdkContentFormats *formats,
|
|
int io_priority,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
|
|
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_read_local_async);
|
|
|
|
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
_("Reading not implemented."));
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static GInputStream *
|
|
gdk_drag_context_read_local_finish (GdkDragContext *context,
|
|
const char **out_mime_type,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
g_return_val_if_fail (g_task_is_valid (result, context), NULL);
|
|
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_drag_context_read_local_async, NULL);
|
|
|
|
if (out_mime_type)
|
|
*out_mime_type = g_task_get_task_data (G_TASK (result));
|
|
|
|
return g_task_propagate_pointer (G_TASK (result), error);
|
|
}
|
|
|
|
static void
|
|
gdk_drag_context_class_init (GdkDragContextClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->get_property = gdk_drag_context_get_property;
|
|
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.
|
|
*/
|
|
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:
|
|
*
|
|
* The #GdkDisplay that the drag context belongs to.
|
|
*/
|
|
properties[PROP_DISPLAY] =
|
|
g_param_spec_object ("display",
|
|
"Display",
|
|
"Display owning this clipboard",
|
|
GDK_TYPE_DISPLAY,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS |
|
|
G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
/**
|
|
* GdkDragContext:formats:
|
|
*
|
|
* The possible formats that the context can provide its data in.
|
|
*/
|
|
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
|
|
* @reason: The reason the context was cancelled
|
|
*
|
|
* The drag and drop operation was cancelled.
|
|
*/
|
|
signals[CANCEL] =
|
|
g_signal_new (g_intern_static_string ("cancel"),
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (GdkDragContextClass, cancel),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__ENUM,
|
|
G_TYPE_NONE, 1, GDK_TYPE_DRAG_CANCEL_REASON);
|
|
|
|
/**
|
|
* GdkDragContext::drop-performed:
|
|
* @context: The object on which the signal is emitted
|
|
* @time: the time at which the drop happened.
|
|
*
|
|
* The drag and drop operation was performed on an accepting client.
|
|
*/
|
|
signals[DROP_PERFORMED] =
|
|
g_signal_new (g_intern_static_string ("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:
|
|
* @context: The object on which the signal is emitted
|
|
*
|
|
* The drag and drop operation was finished, the drag destination
|
|
* finished reading all data. The drag source can now free all
|
|
* miscellaneous data.
|
|
*/
|
|
signals[DND_FINISHED] =
|
|
g_signal_new (g_intern_static_string ("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-changed:
|
|
* @context: The object on which the signal is emitted
|
|
* @action: The action currently chosen
|
|
*
|
|
* A new action is being chosen for the drag and drop operation.
|
|
*/
|
|
signals[ACTION_CHANGED] =
|
|
g_signal_new (g_intern_static_string ("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);
|
|
|
|
g_object_class_install_properties (object_class, N_PROPERTIES, properties);
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_status:
|
|
* @context: a #GdkDragContext
|
|
* @action: the selected action which will be taken when a drop happens,
|
|
* or 0 to indicate that a drop will not be accepted
|
|
* @time_: the timestamp for this operation
|
|
*
|
|
* Selects one of the actions offered by the drag source.
|
|
*
|
|
* This function is called by the drag destination in response to
|
|
* gdk_drag_motion() called by the drag source.
|
|
*/
|
|
void
|
|
gdk_drag_status (GdkDragContext *context,
|
|
GdkDragAction action,
|
|
guint32 time_)
|
|
{
|
|
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
|
|
|
GDK_DRAG_CONTEXT_GET_CLASS (context)->drag_status (context, action, time_);
|
|
}
|
|
|
|
/*
|
|
* gdk_drag_abort:
|
|
* @context: a #GdkDragContext
|
|
* @time_: the timestamp for this operation
|
|
*
|
|
* Aborts a drag without dropping.
|
|
*
|
|
* This function is called by the drag source.
|
|
*/
|
|
void
|
|
gdk_drag_abort (GdkDragContext *context,
|
|
guint32 time_)
|
|
{
|
|
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
|
|
|
GDK_DRAG_CONTEXT_GET_CLASS (context)->drag_abort (context, time_);
|
|
}
|
|
|
|
/*
|
|
* gdk_drag_drop:
|
|
* @context: a #GdkDragContext
|
|
* @time_: the timestamp for this operation
|
|
*
|
|
* Drops on the current destination.
|
|
*
|
|
* This function is called by the drag source.
|
|
*/
|
|
void
|
|
gdk_drag_drop (GdkDragContext *context,
|
|
guint32 time_)
|
|
{
|
|
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
|
|
|
GDK_DRAG_CONTEXT_GET_CLASS (context)->drag_drop (context, time_);
|
|
}
|
|
|
|
/**
|
|
* gdk_drop_reply:
|
|
* @context: a #GdkDragContext
|
|
* @accepted: %TRUE if the drop is accepted
|
|
* @time_: the timestamp for this operation
|
|
*
|
|
* Accepts or rejects a drop.
|
|
*
|
|
* This function is called by the drag destination in response
|
|
* to a drop initiated by the drag source.
|
|
*/
|
|
void
|
|
gdk_drop_reply (GdkDragContext *context,
|
|
gboolean accepted,
|
|
guint32 time_)
|
|
{
|
|
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
|
|
|
GDK_DRAG_CONTEXT_GET_CLASS (context)->drop_reply (context, accepted, time_);
|
|
}
|
|
|
|
/**
|
|
* gdk_drop_finish:
|
|
* @context: a #GdkDragContext
|
|
* @success: %TRUE if the data was successfully received
|
|
* @time_: the timestamp for this operation
|
|
*
|
|
* Ends the drag operation after a drop.
|
|
*
|
|
* This function is called by the drag destination.
|
|
*/
|
|
void
|
|
gdk_drop_finish (GdkDragContext *context,
|
|
gboolean success,
|
|
guint32 time_)
|
|
{
|
|
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
|
|
|
GDK_DRAG_CONTEXT_GET_CLASS (context)->drop_finish (context, success, time_);
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_drop_succeeded:
|
|
* @context: a #GdkDragContext
|
|
*
|
|
* Returns whether the dropped data has been successfully
|
|
* transferred. This function is intended to be used while
|
|
* handling a %GDK_DROP_FINISHED event, its return value is
|
|
* meaningless at other times.
|
|
*
|
|
* Returns: %TRUE if the drop was successful.
|
|
**/
|
|
gboolean
|
|
gdk_drag_drop_succeeded (GdkDragContext *context)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), FALSE);
|
|
|
|
return GDK_DRAG_CONTEXT_GET_CLASS (context)->drop_status (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);
|
|
}
|
|
|
|
/**
|
|
* gdk_drop_read_async:
|
|
* @context: a #GdkDragContext
|
|
* @mime_types: (array zero-terminated=1) (element-type utf8):
|
|
* @io_priority:
|
|
* @cancellable:
|
|
* @callback:
|
|
* @user_data: (closure):
|
|
*/
|
|
void
|
|
gdk_drop_read_async (GdkDragContext *context,
|
|
const char **mime_types,
|
|
int io_priority,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GdkContentFormats *formats;
|
|
|
|
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
|
g_return_if_fail (mime_types != NULL && mime_types[0] != NULL);
|
|
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
|
|
g_return_if_fail (callback != NULL);
|
|
|
|
formats = gdk_content_formats_new (mime_types, g_strv_length ((char **) mime_types));
|
|
|
|
GDK_DRAG_CONTEXT_GET_CLASS (context)->read_async (context,
|
|
formats,
|
|
io_priority,
|
|
cancellable,
|
|
callback,
|
|
user_data);
|
|
|
|
gdk_content_formats_unref (formats);
|
|
}
|
|
|
|
/**
|
|
* gdk_drop_read_finish:
|
|
* @context: a #GdkDragContext
|
|
* @out_mime_type: (out) (type utf8):
|
|
* @result:
|
|
* @error:
|
|
*
|
|
* Returns: (nullable) (transfer full): the #GInputStream, or %NULL
|
|
*/
|
|
GInputStream *
|
|
gdk_drop_read_finish (GdkDragContext *context,
|
|
const char **out_mime_type,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
|
|
if (g_async_result_is_tagged (result, gdk_drag_context_read_local_async))
|
|
{
|
|
return gdk_drag_context_read_local_finish (context, out_mime_type, result, error);
|
|
}
|
|
else
|
|
{
|
|
return GDK_DRAG_CONTEXT_GET_CLASS (context)->read_finish (context, out_mime_type, result, error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_context_get_drag_window:
|
|
* @context: a #GdkDragContext
|
|
*
|
|
* Returns the window on which the drag icon should be rendered
|
|
* during the drag operation. Note that the window may not be
|
|
* available until the drag operation has begun. GDK will move
|
|
* the window in accordance with the ongoing drag operation.
|
|
* The window is owned by @context and will be destroyed when
|
|
* the drag operation is over.
|
|
*
|
|
* Returns: (nullable) (transfer none): the drag window, or %NULL
|
|
*/
|
|
GdkWindow *
|
|
gdk_drag_context_get_drag_window (GdkDragContext *context)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
|
|
|
|
if (GDK_DRAG_CONTEXT_GET_CLASS (context)->get_drag_window)
|
|
return GDK_DRAG_CONTEXT_GET_CLASS (context)->get_drag_window (context);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_context_set_hotspot:
|
|
* @context: a #GdkDragContext
|
|
* @hot_x: x coordinate of the drag window hotspot
|
|
* @hot_y: y coordinate of the drag window hotspot
|
|
*
|
|
* Sets the position of the drag window that will be kept
|
|
* under the cursor hotspot. Initially, the hotspot is at the
|
|
* top left corner of the drag window.
|
|
*/
|
|
void
|
|
gdk_drag_context_set_hotspot (GdkDragContext *context,
|
|
gint hot_x,
|
|
gint hot_y)
|
|
{
|
|
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
|
|
|
if (GDK_DRAG_CONTEXT_GET_CLASS (context)->set_hotspot)
|
|
GDK_DRAG_CONTEXT_GET_CLASS (context)->set_hotspot (context, hot_x, hot_y);
|
|
}
|
|
|
|
/**
|
|
* gdk_drag_drop_done:
|
|
* @context: a #GdkDragContext
|
|
* @success: whether the drag was ultimatively successful
|
|
*
|
|
* Inform GDK if the drop ended successfully. Passing %FALSE
|
|
* for @success may trigger a drag cancellation animation.
|
|
*
|
|
* This function is called by the drag source, and should
|
|
* be the last call before dropping the reference to the
|
|
* @context.
|
|
*
|
|
* The #GdkDragContext will only take the first gdk_drag_drop_done()
|
|
* call as effective, if this function is called multiple times,
|
|
* all subsequent calls will be ignored.
|
|
*/
|
|
void
|
|
gdk_drag_drop_done (GdkDragContext *context,
|
|
gboolean success)
|
|
{
|
|
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
|
|
|
if (context->drop_done)
|
|
return;
|
|
|
|
context->drop_done = TRUE;
|
|
|
|
if (GDK_DRAG_CONTEXT_GET_CLASS (context)->drop_done)
|
|
GDK_DRAG_CONTEXT_GET_CLASS (context)->drop_done (context, success);
|
|
}
|
|
|
|
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,
|
|
GdkDragCancelReason reason)
|
|
{
|
|
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
|
|
|
g_signal_emit (context, signals[CANCEL], 0, reason);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
GdkCursor *
|
|
gdk_drag_get_cursor (GdkDragContext *context,
|
|
GdkDragAction action)
|
|
{
|
|
gint i;
|
|
|
|
for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
|
|
if (drag_cursors[i].action == action)
|
|
break;
|
|
|
|
if (drag_cursors[i].cursor == NULL)
|
|
drag_cursors[i].cursor = gdk_cursor_new_from_name (drag_cursors[i].name, NULL);
|
|
|
|
return drag_cursors[i].cursor;
|
|
}
|
|
|
|
static void
|
|
gdk_drag_context_commit_drag_status (GdkDragContext *context)
|
|
{
|
|
GdkDragContextClass *context_class;
|
|
|
|
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
|
g_return_if_fail (!context->is_source);
|
|
|
|
context_class = GDK_DRAG_CONTEXT_GET_CLASS (context);
|
|
|
|
if (context_class->commit_drag_status)
|
|
context_class->commit_drag_status (context);
|
|
}
|
|
|
|
gboolean
|
|
gdk_drag_context_handle_dest_event (GdkEvent *event)
|
|
{
|
|
GdkDragContext *context = NULL;
|
|
|
|
switch ((guint) event->any.type)
|
|
{
|
|
case GDK_DRAG_MOTION:
|
|
case GDK_DROP_START:
|
|
context = event->dnd.context;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
if (!context)
|
|
return FALSE;
|
|
|
|
gdk_drag_context_commit_drag_status (context);
|
|
return TRUE;;
|
|
}
|