forked from AuroraMiddleware/gtk
242b76a771
Some applications need to access gdk_quartz_window_get_nsview, gdk_quartz_window_get_nswindow, and gdk_quartz_event_get_nsevent so move these from the private gdkquartz-gtk-only.h to a new header gdkquartz-cocoa-access.h. Don't include this header in gdkquartz.h so that user code that doesn't need to access these functins isn't required to compile with Objective C/C++. Closes https://gitlab.gnome.org/GNOME/gtk/-/issues/1737
1701 lines
46 KiB
C
1701 lines
46 KiB
C
/* GTK - The GIMP Toolkit
|
||
* 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 <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#include "gdk/gdk.h"
|
||
|
||
#include "gtkdnd.h"
|
||
#include "gtkdndprivate.h"
|
||
#include "deprecated/gtkiconfactory.h"
|
||
#include "gtkicontheme.h"
|
||
#include "gtkimageprivate.h"
|
||
#include "gtkinvisible.h"
|
||
#include "gtkmain.h"
|
||
#include "gtkoffscreenwindow.h"
|
||
#include "deprecated/gtkstock.h"
|
||
#include "gtkwindow.h"
|
||
#include "gtkintl.h"
|
||
#include "gtkquartz.h"
|
||
#include "gdk/quartz/gdkquartz.h"
|
||
#include "gdk/quartz/gdkquartz-cocoa-access.h"
|
||
#include "gdk/quartz/gdkquartz-gtk-only.h"
|
||
#include "gdk/quartz/gdkquartzdnd.h"
|
||
#include "gtkselectionprivate.h"
|
||
#include "gtksettings.h"
|
||
#include "gtkiconhelperprivate.h"
|
||
|
||
typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
|
||
typedef struct _GtkDragDestInfo GtkDragDestInfo;
|
||
typedef struct _GtkDragFindData GtkDragFindData;
|
||
|
||
static void gtk_drag_find_widget (GtkWidget *widget,
|
||
GtkDragFindData *data);
|
||
static void gtk_drag_dest_site_destroy (gpointer data);
|
||
static void gtk_drag_dest_leave (GtkWidget *widget,
|
||
GdkDragContext *context,
|
||
guint time);
|
||
static GtkDragDestInfo *gtk_drag_get_dest_info (GdkDragContext *context,
|
||
gboolean create);
|
||
static void gtk_drag_source_site_destroy (gpointer data);
|
||
|
||
static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
|
||
gboolean create);
|
||
|
||
static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
|
||
GtkDragResult result);
|
||
|
||
struct _GtkDragSourceInfo
|
||
{
|
||
GtkWidget *source_widget;
|
||
GtkWidget *widget;
|
||
GtkTargetList *target_list; /* Targets for drag data */
|
||
GdkDragAction possible_actions; /* Actions allowed by source */
|
||
GdkDragContext *context; /* drag context */
|
||
NSEvent *nsevent; /* what started it */
|
||
gint hot_x, hot_y; /* Hot spot for drag */
|
||
cairo_surface_t *icon_surface;
|
||
gboolean success;
|
||
gboolean delete;
|
||
};
|
||
|
||
struct _GtkDragDestInfo
|
||
{
|
||
GtkWidget *widget; /* Widget in which drag is in */
|
||
GdkDragContext *context; /* Drag context */
|
||
guint dropped : 1; /* Set after we receive a drop */
|
||
gint drop_x, drop_y; /* Position of drop */
|
||
};
|
||
|
||
struct _GtkDragFindData
|
||
{
|
||
gint x;
|
||
gint y;
|
||
GdkDragContext *context;
|
||
GtkDragDestInfo *info;
|
||
gboolean found;
|
||
gboolean toplevel;
|
||
gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
|
||
gint x, gint y, guint32 time);
|
||
guint32 time;
|
||
};
|
||
|
||
|
||
@interface GtkDragSourceOwner : NSObject {
|
||
GtkDragSourceInfo *info;
|
||
}
|
||
|
||
@end
|
||
|
||
@implementation GtkDragSourceOwner
|
||
-(void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
|
||
{
|
||
guint target_info;
|
||
GtkSelectionData selection_data;
|
||
|
||
selection_data.selection = GDK_NONE;
|
||
selection_data.data = NULL;
|
||
selection_data.length = -1;
|
||
selection_data.target = gdk_quartz_pasteboard_type_to_atom_libgtk_only (type);
|
||
selection_data.display = gdk_display_get_default ();
|
||
|
||
if (gtk_target_list_find (info->target_list,
|
||
selection_data.target,
|
||
&target_info))
|
||
{
|
||
g_signal_emit_by_name (info->widget, "drag-data-get",
|
||
info->context,
|
||
&selection_data,
|
||
target_info,
|
||
time);
|
||
|
||
if (selection_data.length >= 0)
|
||
_gtk_quartz_set_selection_data_for_pasteboard (sender, &selection_data);
|
||
|
||
g_free (selection_data.data);
|
||
}
|
||
}
|
||
|
||
- (id)initWithInfo:(GtkDragSourceInfo *)anInfo
|
||
{
|
||
self = [super init];
|
||
|
||
if (self)
|
||
{
|
||
info = anInfo;
|
||
}
|
||
|
||
return self;
|
||
}
|
||
|
||
@end
|
||
|
||
/**
|
||
* gtk_drag_get_data: (method)
|
||
* @widget: the widget that will receive the
|
||
* #GtkWidget::drag-data-received signal.
|
||
* @context: the drag context
|
||
* @target: the target (form of the data) to retrieve.
|
||
* @time_: a timestamp for retrieving the data. This will
|
||
* generally be the time received in a #GtkWidget::drag-motion"
|
||
* or #GtkWidget::drag-drop" signal.
|
||
*/
|
||
void
|
||
gtk_drag_get_data (GtkWidget *widget,
|
||
GdkDragContext *context,
|
||
GdkAtom target,
|
||
guint32 time)
|
||
{
|
||
id <NSDraggingInfo> dragging_info;
|
||
NSPasteboard *pasteboard;
|
||
GtkSelectionData *selection_data;
|
||
GtkDragDestInfo *info;
|
||
GtkDragDestSite *site;
|
||
|
||
dragging_info = gdk_quartz_drag_context_get_dragging_info_libgtk_only (context);
|
||
pasteboard = [dragging_info draggingPasteboard];
|
||
|
||
info = gtk_drag_get_dest_info (context, FALSE);
|
||
site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
|
||
|
||
selection_data = _gtk_quartz_get_selection_data_from_pasteboard (pasteboard,
|
||
target, 0);
|
||
|
||
if (site && site->target_list)
|
||
{
|
||
guint target_info;
|
||
|
||
if (gtk_target_list_find (site->target_list,
|
||
selection_data->target,
|
||
&target_info))
|
||
{
|
||
if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
|
||
selection_data->length >= 0)
|
||
g_signal_emit_by_name (widget,
|
||
"drag-data-received",
|
||
context, info->drop_x, info->drop_y,
|
||
selection_data,
|
||
target_info, time);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
g_signal_emit_by_name (widget,
|
||
"drag-data-received",
|
||
context, info->drop_x, info->drop_y,
|
||
selection_data,
|
||
0, time);
|
||
}
|
||
|
||
if (site && site->flags & GTK_DEST_DEFAULT_DROP)
|
||
{
|
||
gtk_drag_finish (context,
|
||
(selection_data->length >= 0),
|
||
(gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
|
||
time);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_finish: (method)
|
||
* @context: the drag context.
|
||
* @success: a flag indicating whether the drop was successful
|
||
* @del: a flag indicating whether the source should delete the
|
||
* original data. (This should be %TRUE for a move)
|
||
* @time_: the timestamp from the #GtkWidget::drag-drop signal.
|
||
*/
|
||
void
|
||
gtk_drag_finish (GdkDragContext *context,
|
||
gboolean success,
|
||
gboolean del,
|
||
guint32 time)
|
||
{
|
||
GtkDragSourceInfo *info;
|
||
GdkDragContext* source_context = gdk_quartz_drag_source_context_libgtk_only ();
|
||
|
||
if (source_context)
|
||
{
|
||
info = gtk_drag_get_source_info (source_context, FALSE);
|
||
if (info)
|
||
{
|
||
info->success = success;
|
||
info->delete = del;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_drag_dest_info_destroy (gpointer data)
|
||
{
|
||
GtkDragDestInfo *info = data;
|
||
|
||
g_free (info);
|
||
}
|
||
|
||
static GtkDragDestInfo *
|
||
gtk_drag_get_dest_info (GdkDragContext *context,
|
||
gboolean create)
|
||
{
|
||
GtkDragDestInfo *info;
|
||
static GQuark info_quark = 0;
|
||
if (!info_quark)
|
||
info_quark = g_quark_from_static_string ("gtk-dest-info");
|
||
|
||
info = g_object_get_qdata (G_OBJECT (context), info_quark);
|
||
if (!info && create)
|
||
{
|
||
info = g_new (GtkDragDestInfo, 1);
|
||
info->widget = NULL;
|
||
info->context = context;
|
||
info->dropped = FALSE;
|
||
g_object_set_qdata_full (G_OBJECT (context), info_quark,
|
||
info, gtk_drag_dest_info_destroy);
|
||
}
|
||
|
||
return info;
|
||
}
|
||
|
||
static GQuark dest_info_quark = 0;
|
||
|
||
static GtkDragSourceInfo *
|
||
gtk_drag_get_source_info (GdkDragContext *context,
|
||
gboolean create)
|
||
{
|
||
GtkDragSourceInfo *info;
|
||
|
||
if (!dest_info_quark)
|
||
dest_info_quark = g_quark_from_static_string ("gtk-source-info");
|
||
|
||
info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
|
||
if (!info && create)
|
||
{
|
||
info = g_new0 (GtkDragSourceInfo, 1);
|
||
info->context = context;
|
||
g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
|
||
}
|
||
|
||
return info;
|
||
}
|
||
|
||
static void
|
||
gtk_drag_clear_source_info (GdkDragContext *context)
|
||
{
|
||
g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_get_source_widget: (method)
|
||
* @context: a (destination side) drag context
|
||
*
|
||
* Returns: (transfer none):
|
||
*/
|
||
GtkWidget *
|
||
gtk_drag_get_source_widget (GdkDragContext *context)
|
||
{
|
||
GtkDragSourceInfo *info;
|
||
GdkDragContext* real_source_context = gdk_quartz_drag_source_context_libgtk_only ();
|
||
|
||
if (!real_source_context)
|
||
return NULL;
|
||
|
||
info = gtk_drag_get_source_info (real_source_context, FALSE);
|
||
if (!info)
|
||
return NULL;
|
||
|
||
return info->source_widget;
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_highlight: (method)
|
||
* @widget: a widget to highlight
|
||
*/
|
||
void
|
||
gtk_drag_highlight (GtkWidget *widget)
|
||
{
|
||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||
|
||
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_unhighlight: (method)
|
||
* @widget: a widget to remove the highlight from.
|
||
*/
|
||
void
|
||
gtk_drag_unhighlight (GtkWidget *widget)
|
||
{
|
||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||
|
||
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
|
||
}
|
||
|
||
static NSWindow *
|
||
get_toplevel_nswindow (GtkWidget *widget)
|
||
{
|
||
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
|
||
GdkWindow *window = gtk_widget_get_window (toplevel);
|
||
|
||
/* Offscreen windows don't support drag and drop */
|
||
if (GTK_IS_OFFSCREEN_WINDOW (toplevel))
|
||
return NULL;
|
||
|
||
if (gtk_widget_is_toplevel (toplevel) && window)
|
||
return [gdk_quartz_window_get_nsview (window) window];
|
||
else
|
||
return NULL;
|
||
}
|
||
|
||
static void
|
||
register_types (GtkWidget *widget, GtkDragDestSite *site)
|
||
{
|
||
if (site->target_list)
|
||
{
|
||
NSWindow *nswindow = get_toplevel_nswindow (widget);
|
||
NSSet *types;
|
||
NSAutoreleasePool *pool;
|
||
|
||
if (!nswindow)
|
||
return;
|
||
|
||
pool = [[NSAutoreleasePool alloc] init];
|
||
types = _gtk_quartz_target_list_to_pasteboard_types (site->target_list);
|
||
|
||
[nswindow registerForDraggedTypes:[types allObjects]];
|
||
|
||
[types release];
|
||
[pool release];
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_drag_dest_realized (GtkWidget *widget,
|
||
gpointer user_data)
|
||
{
|
||
GtkDragDestSite *site = user_data;
|
||
|
||
register_types (widget, site);
|
||
}
|
||
|
||
static void
|
||
gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
|
||
GtkWidget *previous_toplevel,
|
||
gpointer user_data)
|
||
{
|
||
GtkDragDestSite *site = user_data;
|
||
|
||
register_types (widget, site);
|
||
}
|
||
|
||
static void
|
||
gtk_drag_dest_site_destroy (gpointer data)
|
||
{
|
||
GtkDragDestSite *site = data;
|
||
|
||
if (site->target_list)
|
||
gtk_target_list_unref (site->target_list);
|
||
|
||
g_free (site);
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_dest_set: (method)
|
||
* @widget: a #GtkWidget
|
||
* @flags: which types of default drag behavior to use
|
||
* @targets: (allow-none) (array length=n_targets): a pointer to an array of #GtkTargetEntrys
|
||
* indicating the drop types that this @widget will accept, or %NULL.
|
||
* Later you can access the list with gtk_drag_dest_get_target_list()
|
||
* and gtk_drag_dest_find_target().
|
||
* @n_targets: the number of entries in @targets
|
||
* @actions: a bitmask of possible actions for a drop onto this @widget.
|
||
*/
|
||
void
|
||
gtk_drag_dest_set (GtkWidget *widget,
|
||
GtkDestDefaults flags,
|
||
const GtkTargetEntry *targets,
|
||
gint n_targets,
|
||
GdkDragAction actions)
|
||
{
|
||
GtkDragDestSite *old_site, *site;
|
||
|
||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||
|
||
old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
|
||
|
||
site = g_new0 (GtkDragDestSite, 1);
|
||
site->flags = flags;
|
||
site->have_drag = FALSE;
|
||
if (targets)
|
||
site->target_list = gtk_target_list_new (targets, n_targets);
|
||
else
|
||
site->target_list = NULL;
|
||
site->actions = actions;
|
||
|
||
if (old_site)
|
||
site->track_motion = old_site->track_motion;
|
||
else
|
||
site->track_motion = FALSE;
|
||
|
||
gtk_drag_dest_unset (widget);
|
||
|
||
if (gtk_widget_get_realized (widget))
|
||
gtk_drag_dest_realized (widget, site);
|
||
|
||
g_signal_connect (widget, "realize",
|
||
G_CALLBACK (gtk_drag_dest_realized), site);
|
||
g_signal_connect (widget, "hierarchy-changed",
|
||
G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
|
||
|
||
g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
|
||
site, gtk_drag_dest_site_destroy);
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_dest_set_proxy: (method)
|
||
* @widget: a #GtkWidget
|
||
* @proxy_window: the window to which to forward drag events
|
||
* @protocol: the drag protocol which the @proxy_window accepts
|
||
* (You can use gdk_drag_get_protocol() to determine this)
|
||
* @use_coordinates: If %TRUE, send the same coordinates to the
|
||
* destination, because it is an embedded
|
||
* subwindow.
|
||
*/
|
||
void
|
||
gtk_drag_dest_set_proxy (GtkWidget *widget,
|
||
GdkWindow *proxy_window,
|
||
GdkDragProtocol protocol,
|
||
gboolean use_coordinates)
|
||
{
|
||
g_warning ("gtk_drag_dest_set_proxy is not supported on Mac OS X.");
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_dest_unset: (method)
|
||
* @widget: a #GtkWidget
|
||
*/
|
||
void
|
||
gtk_drag_dest_unset (GtkWidget *widget)
|
||
{
|
||
GtkDragDestSite *old_site;
|
||
|
||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||
|
||
old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
|
||
if (old_site)
|
||
{
|
||
g_signal_handlers_disconnect_by_func (widget,
|
||
gtk_drag_dest_realized,
|
||
old_site);
|
||
g_signal_handlers_disconnect_by_func (widget,
|
||
gtk_drag_dest_hierarchy_changed,
|
||
old_site);
|
||
}
|
||
|
||
g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_dest_get_target_list: (method)
|
||
* @widget: a #GtkWidget
|
||
* Returns: (nullable) (transfer none): the #GtkTargetList, or %NULL if none
|
||
*/
|
||
GtkTargetList*
|
||
gtk_drag_dest_get_target_list (GtkWidget *widget)
|
||
{
|
||
GtkDragDestSite *site;
|
||
|
||
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
|
||
|
||
site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
|
||
|
||
return site ? site->target_list : NULL;
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_dest_set_target_list: (method)
|
||
* @widget: a #GtkWidget that’s a drag destination
|
||
* @target_list: (allow-none): list of droppable targets, or %NULL for none
|
||
*/
|
||
void
|
||
gtk_drag_dest_set_target_list (GtkWidget *widget,
|
||
GtkTargetList *target_list)
|
||
{
|
||
GtkDragDestSite *site;
|
||
|
||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||
|
||
site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
|
||
|
||
if (!site)
|
||
{
|
||
g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
|
||
"to make the widget into a drag destination");
|
||
return;
|
||
}
|
||
|
||
if (target_list)
|
||
gtk_target_list_ref (target_list);
|
||
|
||
if (site->target_list)
|
||
gtk_target_list_unref (site->target_list);
|
||
|
||
site->target_list = target_list;
|
||
|
||
register_types (widget, site);
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_dest_add_text_targets: (method)
|
||
* @widget: a #GtkWidget that’s a drag destination
|
||
*/
|
||
void
|
||
gtk_drag_dest_add_text_targets (GtkWidget *widget)
|
||
{
|
||
GtkTargetList *target_list;
|
||
|
||
target_list = gtk_drag_dest_get_target_list (widget);
|
||
if (target_list)
|
||
gtk_target_list_ref (target_list);
|
||
else
|
||
target_list = gtk_target_list_new (NULL, 0);
|
||
gtk_target_list_add_text_targets (target_list, 0);
|
||
gtk_drag_dest_set_target_list (widget, target_list);
|
||
gtk_target_list_unref (target_list);
|
||
}
|
||
|
||
|
||
/**
|
||
* gtk_drag_dest_add_image_targets: (method)
|
||
* @widget: a #GtkWidget that’s a drag destination
|
||
*/
|
||
void
|
||
gtk_drag_dest_add_image_targets (GtkWidget *widget)
|
||
{
|
||
GtkTargetList *target_list;
|
||
|
||
target_list = gtk_drag_dest_get_target_list (widget);
|
||
if (target_list)
|
||
gtk_target_list_ref (target_list);
|
||
else
|
||
target_list = gtk_target_list_new (NULL, 0);
|
||
gtk_target_list_add_image_targets (target_list, 0, FALSE);
|
||
gtk_drag_dest_set_target_list (widget, target_list);
|
||
gtk_target_list_unref (target_list);
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_dest_add_uri_targets: (method)
|
||
* @widget: a #GtkWidget that’s a drag destination
|
||
*/
|
||
void
|
||
gtk_drag_dest_add_uri_targets (GtkWidget *widget)
|
||
{
|
||
GtkTargetList *target_list;
|
||
|
||
target_list = gtk_drag_dest_get_target_list (widget);
|
||
if (target_list)
|
||
gtk_target_list_ref (target_list);
|
||
else
|
||
target_list = gtk_target_list_new (NULL, 0);
|
||
gtk_target_list_add_uri_targets (target_list, 0);
|
||
gtk_drag_dest_set_target_list (widget, target_list);
|
||
gtk_target_list_unref (target_list);
|
||
}
|
||
|
||
static void
|
||
prepend_and_ref_widget (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
GSList **slist_p = data;
|
||
|
||
*slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
|
||
}
|
||
|
||
static void
|
||
gtk_drag_find_widget (GtkWidget *widget,
|
||
GtkDragFindData *data)
|
||
{
|
||
GtkAllocation new_allocation;
|
||
gint allocation_to_window_x = 0;
|
||
gint allocation_to_window_y = 0;
|
||
gint x_offset = 0;
|
||
gint y_offset = 0;
|
||
|
||
if (data->found || !gtk_widget_get_mapped (widget) || !gtk_widget_get_sensitive (widget))
|
||
return;
|
||
|
||
/* Note that in the following code, we only count the
|
||
* position as being inside a WINDOW widget if it is inside
|
||
* widget->window; points that are outside of widget->window
|
||
* but within the allocation are not counted. This is consistent
|
||
* with the way we highlight drag targets.
|
||
*
|
||
* data->x,y are relative to widget->parent->window (if
|
||
* widget is not a toplevel, widget->window otherwise).
|
||
* We compute the allocation of widget in the same coordinates,
|
||
* clipping to widget->window, and all intermediate
|
||
* windows. If data->x,y is inside that, then we translate
|
||
* our coordinates to be relative to widget->window and
|
||
* recurse.
|
||
*/
|
||
gtk_widget_get_allocation (widget, &new_allocation);
|
||
|
||
if (gtk_widget_get_parent (widget))
|
||
{
|
||
gint tx, ty;
|
||
GdkWindow *window = gtk_widget_get_window (widget);
|
||
GdkWindow *parent_window;
|
||
GtkAllocation allocation;
|
||
|
||
parent_window = gtk_widget_get_window (gtk_widget_get_parent (widget));
|
||
|
||
/* Compute the offset from allocation-relative to
|
||
* window-relative coordinates.
|
||
*/
|
||
gtk_widget_get_allocation (widget, &allocation);
|
||
allocation_to_window_x = allocation.x;
|
||
allocation_to_window_y = allocation.y;
|
||
|
||
if (gtk_widget_get_has_window (widget))
|
||
{
|
||
/* The allocation is relative to the parent window for
|
||
* window widgets, not to widget->window.
|
||
*/
|
||
gdk_window_get_position (window, &tx, &ty);
|
||
|
||
allocation_to_window_x -= tx;
|
||
allocation_to_window_y -= ty;
|
||
}
|
||
|
||
new_allocation.x = 0 + allocation_to_window_x;
|
||
new_allocation.y = 0 + allocation_to_window_y;
|
||
|
||
while (window && window != parent_window)
|
||
{
|
||
GdkRectangle window_rect = { 0, 0, 0, 0 };
|
||
|
||
window_rect.width = gdk_window_get_width (window);
|
||
window_rect.height = gdk_window_get_height (window);
|
||
|
||
gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
|
||
|
||
gdk_window_get_position (window, &tx, &ty);
|
||
new_allocation.x += tx;
|
||
x_offset += tx;
|
||
new_allocation.y += ty;
|
||
y_offset += ty;
|
||
|
||
window = gdk_window_get_parent (window);
|
||
}
|
||
|
||
if (!window) /* Window and widget heirarchies didn't match. */
|
||
return;
|
||
}
|
||
|
||
if (data->toplevel ||
|
||
((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
|
||
(data->x < new_allocation.x + new_allocation.width) &&
|
||
(data->y < new_allocation.y + new_allocation.height)))
|
||
{
|
||
/* First, check if the drag is in a valid drop site in
|
||
* one of our children
|
||
*/
|
||
if (GTK_IS_CONTAINER (widget))
|
||
{
|
||
GtkDragFindData new_data = *data;
|
||
GSList *children = NULL;
|
||
GSList *tmp_list;
|
||
|
||
new_data.x -= x_offset;
|
||
new_data.y -= y_offset;
|
||
new_data.found = FALSE;
|
||
new_data.toplevel = FALSE;
|
||
|
||
/* need to reference children temporarily in case the
|
||
* ::drag-motion/::drag-drop callbacks change the widget hierarchy.
|
||
*/
|
||
gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
|
||
for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
|
||
{
|
||
if (!new_data.found && gtk_widget_is_drawable (tmp_list->data))
|
||
gtk_drag_find_widget (tmp_list->data, &new_data);
|
||
g_object_unref (tmp_list->data);
|
||
}
|
||
g_slist_free (children);
|
||
|
||
data->found = new_data.found;
|
||
}
|
||
|
||
/* If not, and this widget is registered as a drop site, check to
|
||
* emit "drag-motion" to check if we are actually in
|
||
* a drop site.
|
||
*/
|
||
if (!data->found &&
|
||
g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
|
||
{
|
||
data->found = data->callback (widget,
|
||
data->context,
|
||
data->x - x_offset - allocation_to_window_x,
|
||
data->y - y_offset - allocation_to_window_y,
|
||
data->time);
|
||
/* If so, send a "drag-leave" to the last widget */
|
||
if (data->found)
|
||
{
|
||
if (data->info->widget && data->info->widget != widget)
|
||
{
|
||
gtk_drag_dest_leave (data->info->widget, data->context, data->time);
|
||
}
|
||
data->info->widget = widget;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_drag_dest_leave (GtkWidget *widget,
|
||
GdkDragContext *context,
|
||
guint time)
|
||
{
|
||
GtkDragDestSite *site;
|
||
|
||
site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
|
||
g_return_if_fail (site != NULL);
|
||
|
||
if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
|
||
gtk_drag_unhighlight (widget);
|
||
|
||
if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
|
||
site->track_motion)
|
||
g_signal_emit_by_name (widget, "drag-leave", context, time);
|
||
|
||
site->have_drag = FALSE;
|
||
}
|
||
|
||
static gboolean
|
||
gtk_drag_dest_motion (GtkWidget *widget,
|
||
GdkDragContext *context,
|
||
gint x,
|
||
gint y,
|
||
guint time)
|
||
{
|
||
GtkDragDestSite *site;
|
||
GdkDragAction action = 0;
|
||
gboolean retval;
|
||
|
||
site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
|
||
g_return_val_if_fail (site != NULL, FALSE);
|
||
|
||
if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
|
||
{
|
||
if (gdk_drag_context_get_suggested_action (context) & site->actions)
|
||
action = gdk_drag_context_get_suggested_action (context);
|
||
|
||
if (action && gtk_drag_dest_find_target (widget, context, NULL))
|
||
{
|
||
if (!site->have_drag)
|
||
{
|
||
site->have_drag = TRUE;
|
||
if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
|
||
gtk_drag_highlight (widget);
|
||
}
|
||
|
||
gdk_drag_status (context, action, time);
|
||
}
|
||
else
|
||
{
|
||
gdk_drag_status (context, 0, time);
|
||
if (!site->track_motion)
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
g_signal_emit_by_name (widget, "drag-motion",
|
||
context, x, y, time, &retval);
|
||
|
||
return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
|
||
}
|
||
|
||
static gboolean
|
||
gtk_drag_dest_drop (GtkWidget *widget,
|
||
GdkDragContext *context,
|
||
gint x,
|
||
gint y,
|
||
guint time)
|
||
{
|
||
GtkDragDestSite *site;
|
||
GtkDragDestInfo *info;
|
||
gboolean retval;
|
||
|
||
site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
|
||
g_return_val_if_fail (site != NULL, FALSE);
|
||
|
||
info = gtk_drag_get_dest_info (context, FALSE);
|
||
g_return_val_if_fail (info != NULL, FALSE);
|
||
|
||
info->drop_x = x;
|
||
info->drop_y = y;
|
||
|
||
if (site->flags & GTK_DEST_DEFAULT_DROP)
|
||
{
|
||
GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
|
||
|
||
if (target == GDK_NONE)
|
||
{
|
||
gtk_drag_finish (context, FALSE, FALSE, time);
|
||
return TRUE;
|
||
}
|
||
else
|
||
gtk_drag_get_data (widget, context, target, time);
|
||
}
|
||
|
||
g_signal_emit_by_name (widget, "drag-drop",
|
||
context, x, y, time, &retval);
|
||
|
||
return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_dest_set_track_motion: (method)
|
||
* @widget: a #GtkWidget that’s a drag destination
|
||
* @track_motion: whether to accept all targets
|
||
*/
|
||
void
|
||
gtk_drag_dest_set_track_motion (GtkWidget *widget,
|
||
gboolean track_motion)
|
||
{
|
||
GtkDragDestSite *site;
|
||
|
||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||
|
||
site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
|
||
|
||
g_return_if_fail (site != NULL);
|
||
|
||
site->track_motion = track_motion != FALSE;
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_dest_get_track_motion: (method)
|
||
* @widget: a #GtkWidget that’s a drag destination
|
||
*/
|
||
gboolean
|
||
gtk_drag_dest_get_track_motion (GtkWidget *widget)
|
||
{
|
||
GtkDragDestSite *site;
|
||
|
||
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
|
||
|
||
site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
|
||
|
||
if (site)
|
||
return site->track_motion;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
void
|
||
_gtk_drag_dest_handle_event (GtkWidget *toplevel,
|
||
GdkEvent *event)
|
||
{
|
||
GtkDragDestInfo *info;
|
||
GdkDragContext *context;
|
||
|
||
g_return_if_fail (toplevel != NULL);
|
||
g_return_if_fail (event != NULL);
|
||
|
||
context = event->dnd.context;
|
||
|
||
info = gtk_drag_get_dest_info (context, TRUE);
|
||
|
||
/* Find the widget for the event */
|
||
switch (event->type)
|
||
{
|
||
case GDK_DRAG_ENTER:
|
||
break;
|
||
|
||
case GDK_DRAG_LEAVE:
|
||
if (info->widget)
|
||
{
|
||
gtk_drag_dest_leave (info->widget, context, event->dnd.time);
|
||
info->widget = NULL;
|
||
}
|
||
break;
|
||
|
||
case GDK_DRAG_MOTION:
|
||
case GDK_DROP_START:
|
||
{
|
||
GtkDragFindData data;
|
||
gint tx, ty;
|
||
|
||
if (event->type == GDK_DROP_START)
|
||
{
|
||
info->dropped = TRUE;
|
||
/* We send a leave here so that the widget unhighlights
|
||
* properly.
|
||
*/
|
||
if (info->widget)
|
||
{
|
||
gtk_drag_dest_leave (info->widget, context, event->dnd.time);
|
||
info->widget = NULL;
|
||
}
|
||
}
|
||
|
||
gdk_window_get_position (gtk_widget_get_window (toplevel), &tx, &ty);
|
||
|
||
data.x = event->dnd.x_root - tx;
|
||
data.y = event->dnd.y_root - ty;
|
||
data.context = context;
|
||
data.info = info;
|
||
data.found = FALSE;
|
||
data.toplevel = TRUE;
|
||
data.callback = (event->type == GDK_DRAG_MOTION) ?
|
||
gtk_drag_dest_motion : gtk_drag_dest_drop;
|
||
data.time = event->dnd.time;
|
||
|
||
gtk_drag_find_widget (toplevel, &data);
|
||
|
||
if (info->widget && !data.found)
|
||
{
|
||
gtk_drag_dest_leave (info->widget, context, event->dnd.time);
|
||
info->widget = NULL;
|
||
}
|
||
|
||
/* Send a reply.
|
||
*/
|
||
if (event->type == GDK_DRAG_MOTION)
|
||
{
|
||
if (!data.found)
|
||
gdk_drag_status (context, 0, event->dnd.time);
|
||
}
|
||
|
||
break;
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* gtk_drag_dest_find_target: (method)
|
||
* @widget: drag destination widget
|
||
* @context: drag context
|
||
* @target_list: (allow-none): list of droppable targets, or %NULL to use
|
||
* gtk_drag_dest_get_target_list (@widget).
|
||
*
|
||
* Returns: (transfer none):
|
||
*/
|
||
GdkAtom
|
||
gtk_drag_dest_find_target (GtkWidget *widget,
|
||
GdkDragContext *context,
|
||
GtkTargetList *target_list)
|
||
{
|
||
id <NSDraggingInfo> dragging_info;
|
||
NSPasteboard *pasteboard;
|
||
GtkWidget *source_widget;
|
||
GList *tmp_target;
|
||
GList *tmp_source = NULL;
|
||
GList *source_targets;
|
||
|
||
g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
|
||
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
|
||
|
||
dragging_info = gdk_quartz_drag_context_get_dragging_info_libgtk_only (context);
|
||
pasteboard = [dragging_info draggingPasteboard];
|
||
|
||
source_widget = gtk_drag_get_source_widget (context);
|
||
|
||
if (target_list == NULL)
|
||
target_list = gtk_drag_dest_get_target_list (widget);
|
||
|
||
if (target_list == NULL)
|
||
return GDK_NONE;
|
||
|
||
source_targets = _gtk_quartz_pasteboard_types_to_atom_list ([pasteboard types]);
|
||
tmp_target = target_list->list;
|
||
while (tmp_target)
|
||
{
|
||
GtkTargetPair *pair = tmp_target->data;
|
||
tmp_source = source_targets;
|
||
while (tmp_source)
|
||
{
|
||
if (tmp_source->data == GUINT_TO_POINTER (pair->target))
|
||
{
|
||
if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
|
||
(!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
|
||
{
|
||
g_list_free (source_targets);
|
||
return pair->target;
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
tmp_source = tmp_source->next;
|
||
}
|
||
tmp_target = tmp_target->next;
|
||
}
|
||
|
||
g_list_free (source_targets);
|
||
return GDK_NONE;
|
||
}
|
||
|
||
static gboolean
|
||
gtk_drag_begin_idle (gpointer arg)
|
||
{
|
||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||
GdkDragContext* context = (GdkDragContext*) arg;
|
||
GtkDragSourceInfo* info = gtk_drag_get_source_info (context, FALSE);
|
||
NSWindow *nswindow;
|
||
NSPasteboard *pasteboard;
|
||
GtkDragSourceOwner *owner;
|
||
NSPoint point;
|
||
NSSet *types;
|
||
NSImage *drag_image;
|
||
|
||
g_assert (info != NULL);
|
||
|
||
pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
|
||
owner = [[GtkDragSourceOwner alloc] initWithInfo:info];
|
||
|
||
types = _gtk_quartz_target_list_to_pasteboard_types (info->target_list);
|
||
|
||
[pasteboard declareTypes:[types allObjects] owner:owner];
|
||
|
||
[owner release];
|
||
[types release];
|
||
|
||
if ((nswindow = get_toplevel_nswindow (info->source_widget)) == NULL)
|
||
return G_SOURCE_REMOVE;
|
||
|
||
/* Ref the context. It's unreffed when the drag has been aborted */
|
||
g_object_ref (info->context);
|
||
|
||
/* FIXME: If the event isn't a mouse event, use the global cursor position instead */
|
||
point = [info->nsevent locationInWindow];
|
||
|
||
drag_image = _gtk_quartz_create_image_from_surface (info->icon_surface);
|
||
|
||
if (drag_image == NULL)
|
||
{
|
||
g_object_unref (info->context);
|
||
return G_SOURCE_REMOVE;
|
||
}
|
||
|
||
point.x -= info->hot_x;
|
||
point.y -= [drag_image size].height - info->hot_y;
|
||
|
||
[nswindow dragImage:drag_image
|
||
at:point
|
||
offset:NSZeroSize
|
||
event:info->nsevent
|
||
pasteboard:pasteboard
|
||
source:nswindow
|
||
slideBack:YES];
|
||
|
||
[info->nsevent release];
|
||
[drag_image release];
|
||
|
||
[pool release];
|
||
|
||
return G_SOURCE_REMOVE;
|
||
}
|
||
/* Fake protocol to let us call GdkNSView gdkWindow without including
|
||
* gdk/GdkNSView.h (which we can’t because it pulls in the internal-only
|
||
* gdkwindow.h).
|
||
*/
|
||
@protocol GdkNSView
|
||
- (GdkWindow *)gdkWindow;
|
||
@end
|
||
|
||
GdkDragContext *
|
||
gtk_drag_begin_internal (GtkWidget *widget,
|
||
gboolean *out_needs_icon,
|
||
GtkTargetList *target_list,
|
||
GdkDragAction actions,
|
||
gint button,
|
||
const GdkEvent *event,
|
||
int x,
|
||
int y)
|
||
{
|
||
GtkDragSourceInfo *info;
|
||
GdkDevice *pointer;
|
||
GdkWindow *window;
|
||
GdkDragContext *context;
|
||
NSWindow *nswindow = get_toplevel_nswindow (widget);
|
||
NSPoint point = {0, 0};
|
||
double time = (double)g_get_real_time ();
|
||
NSEvent *nsevent;
|
||
NSTimeInterval nstime;
|
||
|
||
if ((x != -1 && y != -1) || event)
|
||
{
|
||
GdkWindow *window;
|
||
gdouble dx, dy;
|
||
if (x != -1 && y != -1)
|
||
{
|
||
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
|
||
window = gtk_widget_get_window (toplevel);
|
||
gtk_widget_translate_coordinates (widget, toplevel, x, y, &x, &y);
|
||
gdk_window_get_root_coords (gtk_widget_get_window (toplevel), x, y,
|
||
&x, &y);
|
||
dx = (gdouble)x;
|
||
dy = (gdouble)y;
|
||
}
|
||
else if (event)
|
||
{
|
||
if (gdk_event_get_coords (event, &dx, &dy))
|
||
{
|
||
/* We need to translate (x, y) to coordinates relative to the
|
||
* toplevel GdkWindow, which should be the GdkWindow backing
|
||
* nswindow. Then, we convert to the NSWindow coordinate system.
|
||
*/
|
||
window = event->any.window;
|
||
GdkWindow *toplevel = gdk_window_get_effective_toplevel (window);
|
||
|
||
while (window != toplevel)
|
||
{
|
||
double old_x = dx;
|
||
double old_y = dy;
|
||
|
||
gdk_window_coords_to_parent (window, old_x, old_y,
|
||
&dx, &dy);
|
||
window = gdk_window_get_effective_parent (window);
|
||
}
|
||
}
|
||
time = (double)gdk_event_get_time (event);
|
||
}
|
||
point.x = dx;
|
||
point.y = gdk_window_get_height (window) - dy;
|
||
}
|
||
|
||
nstime = [[NSDate dateWithTimeIntervalSince1970: time / 1000] timeIntervalSinceReferenceDate];
|
||
nsevent = [NSEvent mouseEventWithType: NSLeftMouseDown
|
||
location: point
|
||
modifierFlags: 0
|
||
timestamp: nstime
|
||
windowNumber: [nswindow windowNumber]
|
||
context: [nswindow graphicsContext]
|
||
eventNumber: 0
|
||
clickCount: 1
|
||
pressure: 0.0 ];
|
||
|
||
window = [(id<GdkNSView>)[nswindow contentView] gdkWindow];
|
||
g_return_val_if_fail (nsevent != NULL, NULL);
|
||
g_return_val_if_fail (target_list != NULL, NULL);
|
||
|
||
context = gdk_drag_begin (window, g_list_copy (target_list->list));
|
||
g_return_val_if_fail (context != NULL, NULL);
|
||
|
||
info = gtk_drag_get_source_info (context, TRUE);
|
||
info->nsevent = nsevent;
|
||
[info->nsevent retain];
|
||
|
||
info->source_widget = g_object_ref (widget);
|
||
info->widget = g_object_ref (widget);
|
||
info->target_list = target_list;
|
||
gtk_target_list_ref (target_list);
|
||
|
||
info->possible_actions = actions;
|
||
|
||
g_signal_emit_by_name (widget, "drag-begin", info->context);
|
||
|
||
/* Ensure that we have an icon before we start the drag; the
|
||
* application may have set one in ::drag_begin, or it may
|
||
* not have set one.
|
||
*/
|
||
if (info->icon_surface == NULL && out_needs_icon == NULL)
|
||
gtk_drag_set_icon_default (context);
|
||
|
||
if (out_needs_icon != NULL)
|
||
*out_needs_icon = (info->icon_surface == NULL);
|
||
|
||
/* no image def or no supported type -> set the default */
|
||
if (!info->icon_surface)
|
||
gtk_drag_set_icon_default (context);
|
||
|
||
/* drag will begin in an idle handler to avoid nested run loops */
|
||
|
||
g_idle_add_full (G_PRIORITY_HIGH_IDLE, gtk_drag_begin_idle, context, NULL);
|
||
|
||
pointer = gdk_drag_context_get_device (info->context);
|
||
gdk_device_ungrab (pointer, 0);
|
||
|
||
return context;
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_begin_with_coordinates: (method)
|
||
* @widget:
|
||
* @targets:
|
||
* @actions:
|
||
* @button:
|
||
* @event:
|
||
* @x:
|
||
* @y:
|
||
*
|
||
* Returns: (transfer none):
|
||
*/
|
||
GdkDragContext *
|
||
gtk_drag_begin_with_coordinates (GtkWidget *widget,
|
||
GtkTargetList *targets,
|
||
GdkDragAction actions,
|
||
gint button,
|
||
GdkEvent *event,
|
||
gint x,
|
||
gint y)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
|
||
g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
|
||
g_return_val_if_fail (targets != NULL, NULL);
|
||
|
||
return gtk_drag_begin_internal (widget, NULL, targets,
|
||
actions, button, event, x, y);
|
||
}
|
||
/**
|
||
* gtk_drag_begin: (method)
|
||
* @widget: the source widget.
|
||
* @targets: The targets (data formats) in which the
|
||
* source can provide the data.
|
||
* @actions: A bitmask of the allowed drag actions for this drag.
|
||
* @button: The button the user clicked to start the drag.
|
||
* @event: The event that triggered the start of the drag.
|
||
*
|
||
* Returns: (transfer none):
|
||
*/
|
||
GdkDragContext *
|
||
gtk_drag_begin (GtkWidget *widget,
|
||
GtkTargetList *targets,
|
||
GdkDragAction actions,
|
||
gint button,
|
||
GdkEvent *event)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
|
||
g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
|
||
g_return_val_if_fail (targets != NULL, NULL);
|
||
|
||
return gtk_drag_begin_internal (widget, NULL, targets,
|
||
actions, button, event, -1, -1);
|
||
}
|
||
|
||
|
||
/**
|
||
* gtk_drag_cancel:
|
||
* @context: a #GdkDragContext, as e.g. returned by gtk_drag_begin_with_coordinates()
|
||
*
|
||
*/
|
||
void
|
||
gtk_drag_cancel (GdkDragContext *context)
|
||
{
|
||
GtkDragSourceInfo *info;
|
||
|
||
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
||
|
||
info = gtk_drag_get_source_info (context, FALSE);
|
||
if (info != NULL)
|
||
gtk_drag_drop_finished (info, GTK_DRAG_RESULT_ERROR);
|
||
}
|
||
|
||
|
||
/**
|
||
* gtk_drag_set_icon_widget: (method)
|
||
* @context: the context for a drag. (This must be called
|
||
with a context for the source side of a drag)
|
||
* @widget: a toplevel window to use as an icon.
|
||
* @hot_x: the X offset within @widget of the hotspot.
|
||
* @hot_y: the Y offset within @widget of the hotspot.
|
||
*
|
||
* Changes the icon for a widget to a given widget. GTK+
|
||
* will not destroy the icon, so if you don’t want
|
||
* it to persist, you should connect to the “drag-end”
|
||
* signal and destroy it yourself.
|
||
**/
|
||
void
|
||
gtk_drag_set_icon_widget (GdkDragContext *context,
|
||
GtkWidget *widget,
|
||
gint hot_x,
|
||
gint hot_y)
|
||
{
|
||
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||
|
||
g_warning ("gtk_drag_set_icon_widget is not supported on Mac OS X");
|
||
}
|
||
|
||
static void
|
||
set_icon_stock_pixbuf (GdkDragContext *context,
|
||
const gchar *stock_id,
|
||
GdkPixbuf *pixbuf,
|
||
gint hot_x,
|
||
gint hot_y)
|
||
{
|
||
GtkDragSourceInfo *info;
|
||
cairo_surface_t *surface;
|
||
cairo_t *cr;
|
||
|
||
info = gtk_drag_get_source_info (context, FALSE);
|
||
|
||
if (stock_id)
|
||
{
|
||
pixbuf = gtk_widget_render_icon_pixbuf (info->widget, stock_id,
|
||
GTK_ICON_SIZE_DND);
|
||
|
||
if (!pixbuf)
|
||
{
|
||
g_warning ("Cannot load drag icon from stock_id %s", stock_id);
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
g_object_ref (pixbuf);
|
||
|
||
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
||
gdk_pixbuf_get_width (pixbuf),
|
||
gdk_pixbuf_get_height (pixbuf));
|
||
|
||
cr = cairo_create (surface);
|
||
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
|
||
cairo_paint (cr);
|
||
cairo_destroy (cr);
|
||
g_object_unref (pixbuf);
|
||
|
||
cairo_surface_set_device_offset (surface, -hot_x, -hot_y);
|
||
gtk_drag_set_icon_surface (context, surface);
|
||
cairo_surface_destroy (surface);
|
||
}
|
||
|
||
void
|
||
gtk_drag_set_icon_definition (GdkDragContext *context,
|
||
GtkImageDefinition *def,
|
||
gint hot_x,
|
||
gint hot_y)
|
||
{
|
||
switch (gtk_image_definition_get_storage_type (def))
|
||
{
|
||
case GTK_IMAGE_EMPTY:
|
||
gtk_drag_set_icon_default (context);
|
||
break;
|
||
|
||
case GTK_IMAGE_PIXBUF:
|
||
gtk_drag_set_icon_pixbuf (context,
|
||
gtk_image_definition_get_pixbuf (def),
|
||
hot_x, hot_y);
|
||
break;
|
||
|
||
case GTK_IMAGE_STOCK:
|
||
gtk_drag_set_icon_stock (context,
|
||
gtk_image_definition_get_stock (def),
|
||
hot_x, hot_y);
|
||
break;
|
||
|
||
case GTK_IMAGE_ICON_NAME:
|
||
gtk_drag_set_icon_name (context,
|
||
gtk_image_definition_get_icon_name (def),
|
||
hot_x, hot_y);
|
||
break;
|
||
|
||
default:
|
||
g_warning ("FIXME: setting drag icon of type %u not implemented, using default.", gtk_image_definition_get_storage_type (def));
|
||
gtk_drag_set_icon_default (context);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_set_icon_pixbuf:
|
||
* @context: the context for a drag. (This must be called
|
||
* with a context for the source side of a drag)
|
||
* @pixbuf: the #GdkPixbuf to use as the drag icon.
|
||
* @hot_x: the X offset within @widget of the hotspot.
|
||
* @hot_y: the Y offset within @widget of the hotspot.
|
||
*
|
||
* Sets @pixbuf as the icon for a given drag.
|
||
**/
|
||
void
|
||
gtk_drag_set_icon_pixbuf (GdkDragContext *context,
|
||
GdkPixbuf *pixbuf,
|
||
gint hot_x,
|
||
gint hot_y)
|
||
{
|
||
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
||
g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
|
||
|
||
set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_set_icon_stock:
|
||
* @context: the context for a drag. (This must be called
|
||
* with a context for the source side of a drag)
|
||
* @stock_id: the ID of the stock icon to use for the drag.
|
||
* @hot_x: the X offset within the icon of the hotspot.
|
||
* @hot_y: the Y offset within the icon of the hotspot.
|
||
*
|
||
* Sets the icon for a given drag from a stock ID.
|
||
**/
|
||
void
|
||
gtk_drag_set_icon_stock (GdkDragContext *context,
|
||
const gchar *stock_id,
|
||
gint hot_x,
|
||
gint hot_y)
|
||
{
|
||
|
||
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
||
g_return_if_fail (stock_id != NULL);
|
||
|
||
set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_set_icon_surface:
|
||
* @context: the context for a drag. (This must be called
|
||
* with a context for the source side of a drag)
|
||
* @surface: the surface to use as icon
|
||
*
|
||
* Sets @surface as the icon for a given drag. GTK+ retains
|
||
* references for the arguments, and will release them when
|
||
* they are no longer needed.
|
||
*
|
||
* To position the surface relative to the mouse, use
|
||
* cairo_surface_set_device_offset() on @surface. The mouse
|
||
* cursor will be positioned at the (0,0) coordinate of the
|
||
* surface.
|
||
**/
|
||
void
|
||
gtk_drag_set_icon_surface (GdkDragContext *context,
|
||
cairo_surface_t *surface)
|
||
{
|
||
double x_offset, y_offset;
|
||
GtkDragSourceInfo *info;
|
||
|
||
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
||
g_return_if_fail (surface != NULL);
|
||
|
||
cairo_surface_get_device_offset (surface, &x_offset, &y_offset);
|
||
info = gtk_drag_get_source_info (context, FALSE);
|
||
cairo_surface_reference (surface);
|
||
|
||
if (info->icon_surface)
|
||
cairo_surface_destroy (info->icon_surface);
|
||
|
||
info->icon_surface = surface;
|
||
info->hot_x = -x_offset;
|
||
info->hot_y = -y_offset;
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_set_icon_name:
|
||
* @context: the context for a drag. (This must be called
|
||
* with a context for the source side of a drag)
|
||
* @icon_name: name of icon to use
|
||
* @hot_x: the X offset of the hotspot within the icon
|
||
* @hot_y: the Y offset of the hotspot within the icon
|
||
*
|
||
* Sets the icon for a given drag from a named themed icon. See
|
||
* the docs for #GtkIconTheme for more details. Note that the
|
||
* size of the icon depends on the icon theme (the icon is
|
||
* loaded at the symbolic size #GTK_ICON_SIZE_DND), thus
|
||
* @hot_x and @hot_y have to be used with care.
|
||
*
|
||
* Since: 2.8
|
||
**/
|
||
void
|
||
gtk_drag_set_icon_name (GdkDragContext *context,
|
||
const gchar *icon_name,
|
||
gint hot_x,
|
||
gint hot_y)
|
||
{
|
||
GdkScreen *screen;
|
||
GtkIconTheme *icon_theme;
|
||
GdkPixbuf *pixbuf;
|
||
gint width, height, icon_size;
|
||
|
||
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
||
g_return_if_fail (icon_name != NULL);
|
||
|
||
screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
|
||
g_return_if_fail (screen != NULL);
|
||
|
||
gtk_icon_size_lookup (GTK_ICON_SIZE_DND, &width, &height);
|
||
icon_size = MAX (width, height);
|
||
|
||
icon_theme = gtk_icon_theme_get_for_screen (screen);
|
||
|
||
pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name,
|
||
icon_size, 0, NULL);
|
||
if (pixbuf)
|
||
set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
|
||
else
|
||
g_warning ("Cannot load drag icon from icon name %s", icon_name);
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_set_icon_default: (method)
|
||
* @context: the context for a drag. (This must be called
|
||
with a context for the source side of a drag)
|
||
*
|
||
* Sets the icon for a particular drag to the default
|
||
* icon.
|
||
**/
|
||
void
|
||
gtk_drag_set_icon_default (GdkDragContext *context)
|
||
{
|
||
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
|
||
|
||
gtk_drag_set_icon_name (context, "text-x-generic", -2, -2);
|
||
}
|
||
|
||
static void
|
||
gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
|
||
{
|
||
NSPasteboard *pasteboard;
|
||
NSAutoreleasePool *pool;
|
||
|
||
if (info->icon_surface)
|
||
cairo_surface_destroy (info->icon_surface);
|
||
|
||
g_signal_emit_by_name (info->widget, "drag-end",
|
||
info->context);
|
||
|
||
if (info->source_widget)
|
||
g_object_unref (info->source_widget);
|
||
|
||
if (info->widget)
|
||
g_object_unref (info->widget);
|
||
|
||
gtk_target_list_unref (info->target_list);
|
||
|
||
pool = [[NSAutoreleasePool alloc] init];
|
||
|
||
/* Empty the pasteboard, so that it will not accidentally access
|
||
* info->context after it has been destroyed.
|
||
*/
|
||
pasteboard = [NSPasteboard pasteboardWithName: NSDragPboard];
|
||
[pasteboard clearContents];
|
||
|
||
[pool release];
|
||
|
||
gtk_drag_clear_source_info (info->context);
|
||
g_object_unref (info->context);
|
||
|
||
g_free (info);
|
||
info = NULL;
|
||
}
|
||
|
||
static gboolean
|
||
drag_drop_finished_idle_cb (gpointer data)
|
||
{
|
||
GtkDragSourceInfo* info = (GtkDragSourceInfo*) data;
|
||
if (info->success)
|
||
gtk_drag_source_info_destroy (data);
|
||
return G_SOURCE_REMOVE;
|
||
}
|
||
|
||
static void
|
||
gtk_drag_drop_finished (GtkDragSourceInfo *info,
|
||
GtkDragResult result)
|
||
{
|
||
gboolean success = (result == GTK_DRAG_RESULT_SUCCESS);
|
||
|
||
if (!success)
|
||
g_signal_emit_by_name (info->source_widget, "drag-failed",
|
||
info->context, result, &success);
|
||
|
||
if (success && info->delete)
|
||
g_signal_emit_by_name (info->source_widget, "drag-data-delete",
|
||
info->context);
|
||
|
||
/* Workaround for the fact that the NS API blocks until the drag is
|
||
* over. This way the context is still valid when returning from
|
||
* drag_begin, even if it will still be quite useless. See bug #501588.
|
||
*/
|
||
g_idle_add (drag_drop_finished_idle_cb, info);
|
||
}
|
||
|
||
/*************************************************************
|
||
* _gtk_drag_source_handle_event:
|
||
* Called from widget event handling code on Drag events
|
||
* for drag sources.
|
||
*
|
||
* arguments:
|
||
* toplevel: Toplevel widget that received the event
|
||
* event:
|
||
* results:
|
||
*************************************************************/
|
||
|
||
void
|
||
_gtk_drag_source_handle_event (GtkWidget *widget,
|
||
GdkEvent *event)
|
||
{
|
||
GtkDragSourceInfo *info;
|
||
GdkDragContext *context;
|
||
GtkDragResult result;
|
||
|
||
g_return_if_fail (widget != NULL);
|
||
g_return_if_fail (event != NULL);
|
||
|
||
context = event->dnd.context;
|
||
info = gtk_drag_get_source_info (context, FALSE);
|
||
if (!info)
|
||
return;
|
||
|
||
switch (event->type)
|
||
{
|
||
case GDK_DROP_FINISHED:
|
||
result = (gdk_drag_context_get_dest_window (context) != NULL) ? GTK_DRAG_RESULT_SUCCESS : GTK_DRAG_RESULT_NO_TARGET;
|
||
gtk_drag_drop_finished (info, result);
|
||
break;
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gtk_drag_check_threshold: (method)
|
||
* @widget: a #GtkWidget
|
||
* @start_x: X coordinate of start of drag
|
||
* @start_y: Y coordinate of start of drag
|
||
* @current_x: current X coordinate
|
||
* @current_y: current Y coordinate
|
||
*/
|
||
gboolean
|
||
gtk_drag_check_threshold (GtkWidget *widget,
|
||
gint start_x,
|
||
gint start_y,
|
||
gint current_x,
|
||
gint current_y)
|
||
{
|
||
gint drag_threshold;
|
||
|
||
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
|
||
|
||
g_object_get (gtk_widget_get_settings (widget),
|
||
"gtk-dnd-drag-threshold", &drag_threshold,
|
||
NULL);
|
||
|
||
return (ABS (current_x - start_x) > drag_threshold ||
|
||
ABS (current_y - start_y) > drag_threshold);
|
||
}
|