gtk2/gtk/gtkdragdest.c
2016-10-20 13:48:05 -04:00

503 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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 "gtkdragdest.h"
#include "gtkdnd.h"
#include "gtkdndprivate.h"
#include "gtkselectionprivate.h"
#include "gtkintl.h"
static void
gtk_drag_dest_realized (GtkWidget *widget)
{
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
if (gtk_widget_is_toplevel (toplevel))
gdk_window_register_dnd (gtk_widget_get_window (toplevel));
}
static void
gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
GtkWidget *previous_toplevel)
{
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
if (gtk_widget_is_toplevel (toplevel) && gtk_widget_get_realized (toplevel))
gdk_window_register_dnd (gtk_widget_get_window (toplevel));
}
static void
gtk_drag_dest_site_destroy (gpointer data)
{
GtkDragDestSite *site = data;
if (site->proxy_window)
g_object_unref (site->proxy_window);
if (site->target_list)
gtk_target_list_unref (site->target_list);
g_slice_free (GtkDragDestSite, site);
}
static void
gtk_drag_dest_set_internal (GtkWidget *widget,
GtkDragDestSite *site)
{
GtkDragDestSite *old_site;
old_site = g_object_get_data (G_OBJECT (widget), I_("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);
site->track_motion = old_site->track_motion;
}
if (gtk_widget_get_realized (widget))
gtk_drag_dest_realized (widget);
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: (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.
*
* Sets a widget as a potential drop destination, and adds default behaviors.
*
* The default behaviors listed in @flags have an effect similar
* to installing default handlers for the widgets drag-and-drop signals
* (#GtkWidget::drag-motion, #GtkWidget::drag-drop, ...). They all exist
* for convenience. When passing #GTK_DEST_DEFAULT_ALL for instance it is
* sufficient to connect to the widgets #GtkWidget::drag-data-received
* signal to get primitive, but consistent drag-and-drop support.
*
* Things become more complicated when you try to preview the dragged data,
* as described in the documentation for #GtkWidget::drag-motion. The default
* behaviors described by @flags make some assumptions, that can conflict
* with your own signal handlers. For instance #GTK_DEST_DEFAULT_DROP causes
* invokations of gdk_drag_status() in the context of #GtkWidget::drag-motion,
* and invokations of gtk_drag_finish() in #GtkWidget::drag-data-received.
* Especially the later is dramatic, when your own #GtkWidget::drag-motion
* handler calls gtk_drag_get_data() to inspect the dragged data.
*
* Theres no way to set a default action here, you can use the
* #GtkWidget::drag-motion callback for that. Heres an example which selects
* the action to use depending on whether the control key is pressed or not:
* |[<!-- language="C" -->
* static void
* drag_motion (GtkWidget *widget,
* GdkDragContext *context,
* gint x,
* gint y,
* guint time)
* {
* GdkModifierType mask;
*
* gdk_window_get_pointer (gtk_widget_get_window (widget),
* NULL, NULL, &mask);
* if (mask & GDK_CONTROL_MASK)
* gdk_drag_status (context, GDK_ACTION_COPY, time);
* else
* gdk_drag_status (context, GDK_ACTION_MOVE, time);
* }
* ]|
*/
void
gtk_drag_dest_set (GtkWidget *widget,
GtkDestDefaults flags,
const GtkTargetEntry *targets,
gint n_targets,
GdkDragAction actions)
{
GtkDragDestSite *site;
g_return_if_fail (GTK_IS_WIDGET (widget));
site = g_slice_new0 (GtkDragDestSite);
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;
site->do_proxy = FALSE;
site->proxy_window = NULL;
site->track_motion = FALSE;
gtk_drag_dest_set_internal (widget, site);
}
/**
* 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.
*
* Sets this widget as a proxy for drops to another window.
*
* Deprecated: 3.22
*/
void
gtk_drag_dest_set_proxy (GtkWidget *widget,
GdkWindow *proxy_window,
GdkDragProtocol protocol,
gboolean use_coordinates)
{
GtkDragDestSite *site;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
site = g_slice_new (GtkDragDestSite);
site->flags = 0;
site->have_drag = FALSE;
site->target_list = NULL;
site->actions = 0;
site->proxy_window = proxy_window;
if (proxy_window)
g_object_ref (proxy_window);
site->do_proxy = TRUE;
site->proxy_protocol = protocol;
site->proxy_coords = use_coordinates;
site->track_motion = FALSE;
gtk_drag_dest_set_internal (widget, site);
}
/**
* gtk_drag_dest_unset: (method)
* @widget: a #GtkWidget
*
* Clears information about a drop destination set with
* gtk_drag_dest_set(). The widget will no longer receive
* notification of drags.
*/
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), I_("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 the list of targets this widget can accept from
* drag-and-drop.
*
* 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), I_("gtk-drag-dest"));
return site ? site->target_list : NULL;
}
/**
* gtk_drag_dest_set_target_list: (method)
* @widget: a #GtkWidget thats a drag destination
* @target_list: (allow-none): list of droppable targets, or %NULL for none
*
* Sets the target types that this widget can accept from drag-and-drop.
* The widget must first be made into a drag destination with
* gtk_drag_dest_set().
*/
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), I_("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;
}
/**
* gtk_drag_dest_add_text_targets: (method)
* @widget: a #GtkWidget thats a drag destination
*
* Add the text targets supported by #GtkSelectionData to
* the target list of the drag destination. The targets
* are added with @info = 0. If you need another value,
* use gtk_target_list_add_text_targets() and
* gtk_drag_dest_set_target_list().
*
* Since: 2.6
*/
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 thats a drag destination
*
* Add the image targets supported by #GtkSelectionData to
* the target list of the drag destination. The targets
* are added with @info = 0. If you need another value,
* use gtk_target_list_add_image_targets() and
* gtk_drag_dest_set_target_list().
*
* Since: 2.6
*/
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 thats a drag destination
*
* Add the URI targets supported by #GtkSelectionData to
* the target list of the drag destination. The targets
* are added with @info = 0. If you need another value,
* use gtk_target_list_add_uri_targets() and
* gtk_drag_dest_set_target_list().
*
* Since: 2.6
*/
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);
}
/**
* gtk_drag_dest_set_track_motion: (method)
* @widget: a #GtkWidget thats a drag destination
* @track_motion: whether to accept all targets
*
* Tells the widget to emit #GtkWidget::drag-motion and
* #GtkWidget::drag-leave events regardless of the targets and the
* %GTK_DEST_DEFAULT_MOTION flag.
*
* This may be used when a widget wants to do generic
* actions regardless of the targets that the source offers.
*
* Since: 2.10
*/
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), I_("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 thats a drag destination
*
* Returns whether the widget has been configured to always
* emit #GtkWidget::drag-motion signals.
*
* Returns: %TRUE if the widget always emits
* #GtkWidget::drag-motion events
*
* Since: 2.10
*/
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), I_("gtk-drag-dest"));
if (site)
return site->track_motion;
return FALSE;
}
/**
* 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).
*
* Looks for a match between the supported targets of @context and the
* @dest_target_list, returning the first matching target, otherwise
* returning %GDK_NONE. @dest_target_list should usually be the return
* value from gtk_drag_dest_get_target_list(), but some widgets may
* have different valid targets for different parts of the widget; in
* that case, they will have to implement a drag_motion handler that
* passes the correct target list to this function.
*
* Returns: (transfer none): first target that the source offers
* and the dest can accept, or %GDK_NONE
*/
GdkAtom
gtk_drag_dest_find_target (GtkWidget *widget,
GdkDragContext *context,
GtkTargetList *target_list)
{
GList *tmp_target;
GList *tmp_source = NULL;
GtkWidget *source_widget;
g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
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;
tmp_target = target_list->list;
while (tmp_target)
{
GtkTargetPair *pair = tmp_target->data;
tmp_source = gdk_drag_context_list_targets (context);
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)) &&
(!(pair->flags & GTK_TARGET_OTHER_APP) || !source_widget) &&
(!(pair->flags & GTK_TARGET_OTHER_WIDGET) || (source_widget != widget)))
return pair->target;
else
break;
}
tmp_source = tmp_source->next;
}
tmp_target = tmp_target->next;
}
return GDK_NONE;
}