diff options
Diffstat (limited to '')
-rw-r--r-- | app/widgets/gimpdnd.c | 2465 |
1 files changed, 2465 insertions, 0 deletions
diff --git a/app/widgets/gimpdnd.c b/app/widgets/gimpdnd.c new file mode 100644 index 0000000..7be8622 --- /dev/null +++ b/app/widgets/gimpdnd.c @@ -0,0 +1,2465 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <gegl.h> +#include <gtk/gtk.h> + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "widgets-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpbrush.h" +#include "core/gimpbuffer.h" +#include "core/gimpchannel.h" +#include "core/gimpcontainer.h" +#include "core/gimpdatafactory.h" +#include "core/gimpdrawable.h" +#include "core/gimpgradient.h" +#include "core/gimpimage.h" +#include "core/gimpimagefile.h" +#include "core/gimplayer.h" +#include "core/gimplayermask.h" +#include "core/gimppalette.h" +#include "core/gimppattern.h" +#include "core/gimptemplate.h" +#include "core/gimptoolitem.h" + +#include "text/gimpfont.h" + +#include "vectors/gimpvectors.h" + +#include "gimpdnd.h" +#include "gimpdnd-xds.h" +#include "gimppixbuf.h" +#include "gimpselectiondata.h" +#include "gimpview.h" +#include "gimpviewrendererimage.h" + +#include "gimp-log.h" +#include "gimp-intl.h" + + +#define DRAG_PREVIEW_SIZE GIMP_VIEW_SIZE_LARGE +#define DRAG_ICON_OFFSET -8 + + +typedef GtkWidget * (* GimpDndGetIconFunc) (GtkWidget *widget, + GdkDragContext *context, + GCallback get_data_func, + gpointer get_data_data); +typedef void (* GimpDndDragDataFunc) (GtkWidget *widget, + GdkDragContext *context, + GCallback get_data_func, + gpointer get_data_data, + GtkSelectionData *selection); +typedef gboolean (* GimpDndDropDataFunc) (GtkWidget *widget, + gint x, + gint y, + GCallback set_data_func, + gpointer set_data_data, + GtkSelectionData *selection); + + +typedef struct _GimpDndDataDef GimpDndDataDef; + +struct _GimpDndDataDef +{ + GtkTargetEntry target_entry; + + const gchar *get_data_func_name; + const gchar *get_data_data_name; + + const gchar *set_data_func_name; + const gchar *set_data_data_name; + + GimpDndGetIconFunc get_icon_func; + GimpDndDragDataFunc get_data_func; + GimpDndDropDataFunc set_data_func; +}; + + +static GtkWidget * gimp_dnd_get_viewable_icon (GtkWidget *widget, + GdkDragContext *context, + GCallback get_viewable_func, + gpointer get_viewable_data); +static GtkWidget * gimp_dnd_get_component_icon (GtkWidget *widget, + GdkDragContext *context, + GCallback get_comp_func, + gpointer get_comp_data); +static GtkWidget * gimp_dnd_get_color_icon (GtkWidget *widget, + GdkDragContext *context, + GCallback get_color_func, + gpointer get_color_data); + +static void gimp_dnd_get_uri_list_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_uri_list_func, + gpointer get_uri_list_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_uri_list_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_uri_list_func, + gpointer set_uri_list_data, + GtkSelectionData *selection); + +static void gimp_dnd_get_xds_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_image_func, + gpointer get_image_data, + GtkSelectionData *selection); + +static void gimp_dnd_get_color_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_color_func, + gpointer get_color_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_color_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_color_func, + gpointer set_color_data, + GtkSelectionData *selection); + +static void gimp_dnd_get_stream_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_stream_func, + gpointer get_stream_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_stream_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_stream_func, + gpointer set_stream_data, + GtkSelectionData *selection); + +static void gimp_dnd_get_pixbuf_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_pixbuf_func, + gpointer get_pixbuf_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_pixbuf_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_pixbuf_func, + gpointer set_pixbuf_data, + GtkSelectionData *selection); +static void gimp_dnd_get_component_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_comp_func, + gpointer get_comp_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_component_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_comp_func, + gpointer set_comp_data, + GtkSelectionData *selection); + +static void gimp_dnd_get_image_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_image_func, + gpointer get_image_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_image_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_image_func, + gpointer set_image_data, + GtkSelectionData *selection); + +static void gimp_dnd_get_item_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_item_func, + gpointer get_item_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_item_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_item_func, + gpointer set_item_data, + GtkSelectionData *selection); + +static void gimp_dnd_get_object_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_object_func, + gpointer get_object_data, + GtkSelectionData *selection); + +static gboolean gimp_dnd_set_brush_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_brush_func, + gpointer set_brush_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_pattern_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_pattern_func, + gpointer set_pattern_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_gradient_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_gradient_func, + gpointer set_gradient_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_palette_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_palette_func, + gpointer set_palette_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_font_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_font_func, + gpointer set_font_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_buffer_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_buffer_func, + gpointer set_buffer_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_imagefile_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_imagefile_func, + gpointer set_imagefile_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_template_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_template_func, + gpointer set_template_data, + GtkSelectionData *selection); +static gboolean gimp_dnd_set_tool_item_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_tool_item_func, + gpointer set_tool_item_data, + GtkSelectionData *selection); + + + +static const GimpDndDataDef dnd_data_defs[] = +{ + { + { NULL, 0, -1 }, + + NULL, + NULL, + + NULL, + NULL, + NULL + }, + + { + GIMP_TARGET_URI_LIST, + + "gimp-dnd-get-uri-list-func", + "gimp-dnd-get-uri-list-data", + + "gimp-dnd-set-uri-list-func", + "gimp-dnd-set-uri-list-data", + + NULL, + gimp_dnd_get_uri_list_data, + gimp_dnd_set_uri_list_data + }, + + { + GIMP_TARGET_TEXT_PLAIN, + + NULL, + NULL, + + "gimp-dnd-set-uri-list-func", + "gimp-dnd-set-uri-list-data", + + NULL, + NULL, + gimp_dnd_set_uri_list_data + }, + + { + GIMP_TARGET_NETSCAPE_URL, + + NULL, + NULL, + + "gimp-dnd-set-uri-list-func", + "gimp-dnd-set-uri-list-data", + + NULL, + NULL, + gimp_dnd_set_uri_list_data + }, + + { + GIMP_TARGET_XDS, + + "gimp-dnd-get-xds-func", + "gimp-dnd-get-xds-data", + + NULL, + NULL, + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_xds_data, + NULL + }, + + { + GIMP_TARGET_COLOR, + + "gimp-dnd-get-color-func", + "gimp-dnd-get-color-data", + + "gimp-dnd-set-color-func", + "gimp-dnd-set-color-data", + + gimp_dnd_get_color_icon, + gimp_dnd_get_color_data, + gimp_dnd_set_color_data + }, + + { + GIMP_TARGET_SVG, + + "gimp-dnd-get-svg-func", + "gimp-dnd-get-svg-data", + + "gimp-dnd-set-svg-func", + "gimp-dnd-set-svg-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_stream_data, + gimp_dnd_set_stream_data + }, + + { + GIMP_TARGET_SVG_XML, + + "gimp-dnd-get-svg-xml-func", + "gimp-dnd-get-svg-xml-data", + + "gimp-dnd-set-svg-xml-func", + "gimp-dnd-set-svg-xml-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_stream_data, + gimp_dnd_set_stream_data + }, + + { + GIMP_TARGET_PIXBUF, + + "gimp-dnd-get-pixbuf-func", + "gimp-dnd-get-pixbuf-data", + + "gimp-dnd-set-pixbuf-func", + "gimp-dnd-set-pixbuf-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_pixbuf_data, + gimp_dnd_set_pixbuf_data + }, + + { + GIMP_TARGET_IMAGE, + + "gimp-dnd-get-image-func", + "gimp-dnd-get-image-data", + + "gimp-dnd-set-image-func", + "gimp-dnd-set-image-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_image_data, + gimp_dnd_set_image_data, + }, + + { + GIMP_TARGET_COMPONENT, + + "gimp-dnd-get-component-func", + "gimp-dnd-get-component-data", + + "gimp-dnd-set-component-func", + "gimp-dnd-set-component-data", + + gimp_dnd_get_component_icon, + gimp_dnd_get_component_data, + gimp_dnd_set_component_data, + }, + + { + GIMP_TARGET_LAYER, + + "gimp-dnd-get-layer-func", + "gimp-dnd-get-layer-data", + + "gimp-dnd-set-layer-func", + "gimp-dnd-set-layer-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_item_data, + gimp_dnd_set_item_data, + }, + + { + GIMP_TARGET_CHANNEL, + + "gimp-dnd-get-channel-func", + "gimp-dnd-get-channel-data", + + "gimp-dnd-set-channel-func", + "gimp-dnd-set-channel-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_item_data, + gimp_dnd_set_item_data, + }, + + { + GIMP_TARGET_LAYER_MASK, + + "gimp-dnd-get-layer-mask-func", + "gimp-dnd-get-layer-mask-data", + + "gimp-dnd-set-layer-mask-func", + "gimp-dnd-set-layer-mask-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_item_data, + gimp_dnd_set_item_data, + }, + + { + GIMP_TARGET_VECTORS, + + "gimp-dnd-get-vectors-func", + "gimp-dnd-get-vectors-data", + + "gimp-dnd-set-vectors-func", + "gimp-dnd-set-vectors-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_item_data, + gimp_dnd_set_item_data, + }, + + { + GIMP_TARGET_BRUSH, + + "gimp-dnd-get-brush-func", + "gimp-dnd-get-brush-data", + + "gimp-dnd-set-brush-func", + "gimp-dnd-set-brush-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_object_data, + gimp_dnd_set_brush_data + }, + + { + GIMP_TARGET_PATTERN, + + "gimp-dnd-get-pattern-func", + "gimp-dnd-get-pattern-data", + + "gimp-dnd-set-pattern-func", + "gimp-dnd-set-pattern-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_object_data, + gimp_dnd_set_pattern_data + }, + + { + GIMP_TARGET_GRADIENT, + + "gimp-dnd-get-gradient-func", + "gimp-dnd-get-gradient-data", + + "gimp-dnd-set-gradient-func", + "gimp-dnd-set-gradient-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_object_data, + gimp_dnd_set_gradient_data + }, + + { + GIMP_TARGET_PALETTE, + + "gimp-dnd-get-palette-func", + "gimp-dnd-get-palette-data", + + "gimp-dnd-set-palette-func", + "gimp-dnd-set-palette-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_object_data, + gimp_dnd_set_palette_data + }, + + { + GIMP_TARGET_FONT, + + "gimp-dnd-get-font-func", + "gimp-dnd-get-font-data", + + "gimp-dnd-set-font-func", + "gimp-dnd-set-font-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_object_data, + gimp_dnd_set_font_data + }, + + { + GIMP_TARGET_BUFFER, + + "gimp-dnd-get-buffer-func", + "gimp-dnd-get-buffer-data", + + "gimp-dnd-set-buffer-func", + "gimp-dnd-set-buffer-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_object_data, + gimp_dnd_set_buffer_data + }, + + { + GIMP_TARGET_IMAGEFILE, + + "gimp-dnd-get-imagefile-func", + "gimp-dnd-get-imagefile-data", + + "gimp-dnd-set-imagefile-func", + "gimp-dnd-set-imagefile-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_object_data, + gimp_dnd_set_imagefile_data + }, + + { + GIMP_TARGET_TEMPLATE, + + "gimp-dnd-get-template-func", + "gimp-dnd-get-template-data", + + "gimp-dnd-set-template-func", + "gimp-dnd-set-template-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_object_data, + gimp_dnd_set_template_data + }, + + { + GIMP_TARGET_TOOL_ITEM, + + "gimp-dnd-get-tool-item-func", + "gimp-dnd-get-tool-item-data", + + "gimp-dnd-set-tool-item-func", + "gimp-dnd-set-tool-item-data", + + gimp_dnd_get_viewable_icon, + gimp_dnd_get_object_data, + gimp_dnd_set_tool_item_data + }, + + { + GIMP_TARGET_DIALOG, + + NULL, + NULL, + + NULL, + NULL, + + NULL, + NULL, + NULL + } +}; + + +static Gimp *the_dnd_gimp = NULL; + + +void +gimp_dnd_init (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (the_dnd_gimp == NULL); + + the_dnd_gimp = gimp; +} + + +/**********************/ +/* helper functions */ +/**********************/ + +static void +gimp_dnd_target_list_add (GtkTargetList *list, + const GtkTargetEntry *entry) +{ + GdkAtom atom = gdk_atom_intern (entry->target, FALSE); + guint info; + + if (! gtk_target_list_find (list, atom, &info) || info != entry->info) + { + gtk_target_list_add (list, atom, entry->flags, entry->info); + } +} + + +/********************************/ +/* general data dnd functions */ +/********************************/ + +static void +gimp_dnd_data_drag_begin (GtkWidget *widget, + GdkDragContext *context, + gpointer data) +{ + const GimpDndDataDef *dnd_data; + GimpDndType data_type; + GCallback get_data_func = NULL; + gpointer get_data_data = NULL; + GtkWidget *icon_widget; + + data_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), + "gimp-dnd-get-data-type")); + + GIMP_LOG (DND, "data type %d", data_type); + + if (! data_type) + return; + + dnd_data = dnd_data_defs + data_type; + + if (dnd_data->get_data_func_name) + get_data_func = g_object_get_data (G_OBJECT (widget), + dnd_data->get_data_func_name); + + if (dnd_data->get_data_data_name) + get_data_data = g_object_get_data (G_OBJECT (widget), + dnd_data->get_data_data_name); + + if (! get_data_func) + return; + + icon_widget = dnd_data->get_icon_func (widget, + context, + get_data_func, + get_data_data); + + if (icon_widget) + { + GtkWidget *frame; + GtkWidget *window; + + window = gtk_window_new (GTK_WINDOW_POPUP); + gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND); + gtk_window_set_screen (GTK_WINDOW (window), + gtk_widget_get_screen (widget)); + + gtk_widget_realize (window); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); + gtk_container_add (GTK_CONTAINER (window), frame); + gtk_widget_show (frame); + + gtk_container_add (GTK_CONTAINER (frame), icon_widget); + gtk_widget_show (icon_widget); + + g_object_set_data_full (G_OBJECT (widget), "gimp-dnd-data-widget", + window, (GDestroyNotify) gtk_widget_destroy); + + gtk_drag_set_icon_widget (context, window, + DRAG_ICON_OFFSET, DRAG_ICON_OFFSET); + + /* remember for which drag context the widget was made */ + g_object_set_data (G_OBJECT (window), "gimp-gdk-drag-context", context); + } +} + +static void +gimp_dnd_data_drag_end (GtkWidget *widget, + GdkDragContext *context) +{ + GimpDndType data_type; + GtkWidget *icon_widget; + + data_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), + "gimp-dnd-get-data-type")); + + GIMP_LOG (DND, "data type %d", data_type); + + icon_widget = g_object_get_data (G_OBJECT (widget), "gimp-dnd-data-widget"); + + if (icon_widget) + { + /* destroy the icon_widget only if it was made for this drag + * context. See bug #139337. + */ + if (g_object_get_data (G_OBJECT (icon_widget), + "gimp-gdk-drag-context") == + (gpointer) context) + { + g_object_set_data (G_OBJECT (widget), "gimp-dnd-data-widget", NULL); + } + } +} + +static void +gimp_dnd_data_drag_handle (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time, + gpointer data) +{ + GCallback get_data_func = NULL; + gpointer get_data_data = NULL; + GimpDndType data_type; + + GIMP_LOG (DND, "data type %d", info); + + for (data_type = GIMP_DND_TYPE_NONE + 1; + data_type <= GIMP_DND_TYPE_LAST; + data_type++) + { + const GimpDndDataDef *dnd_data = dnd_data_defs + data_type; + + if (dnd_data->target_entry.info == info) + { + GIMP_LOG (DND, "target %s", dnd_data->target_entry.target); + + if (dnd_data->get_data_func_name) + get_data_func = g_object_get_data (G_OBJECT (widget), + dnd_data->get_data_func_name); + + if (dnd_data->get_data_data_name) + get_data_data = g_object_get_data (G_OBJECT (widget), + dnd_data->get_data_data_name); + + if (! get_data_func) + return; + + dnd_data->get_data_func (widget, + context, + get_data_func, + get_data_data, + selection_data); + + return; + } + } +} + +static void +gimp_dnd_data_drop_handle (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time, + gpointer data) +{ + GimpDndType data_type; + + GIMP_LOG (DND, "data type %d", info); + + if (gtk_selection_data_get_length (selection_data) <= 0) + { + gtk_drag_finish (context, FALSE, FALSE, time); + return; + } + + for (data_type = GIMP_DND_TYPE_NONE + 1; + data_type <= GIMP_DND_TYPE_LAST; + data_type++) + { + const GimpDndDataDef *dnd_data = dnd_data_defs + data_type; + + if (dnd_data->target_entry.info == info) + { + GCallback set_data_func = NULL; + gpointer set_data_data = NULL; + + GIMP_LOG (DND, "target %s", dnd_data->target_entry.target); + + if (dnd_data->set_data_func_name) + set_data_func = g_object_get_data (G_OBJECT (widget), + dnd_data->set_data_func_name); + + if (dnd_data->set_data_data_name) + set_data_data = g_object_get_data (G_OBJECT (widget), + dnd_data->set_data_data_name); + + if (set_data_func && + dnd_data->set_data_func (widget, x, y, + set_data_func, + set_data_data, + selection_data)) + { + gtk_drag_finish (context, TRUE, FALSE, time); + return; + } + + gtk_drag_finish (context, FALSE, FALSE, time); + return; + } + } +} + +static void +gimp_dnd_data_source_add (GimpDndType data_type, + GtkWidget *widget, + GCallback get_data_func, + gpointer get_data_data) +{ + const GimpDndDataDef *dnd_data; + gboolean drag_connected; + + dnd_data = dnd_data_defs + data_type; + + /* set a default drag source if not already done */ + if (! g_object_get_data (G_OBJECT (widget), "gtk-site-data")) + gtk_drag_source_set (widget, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK, + NULL, 0, + GDK_ACTION_COPY | GDK_ACTION_MOVE); + + drag_connected = + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), + "gimp-dnd-drag-connected")); + + if (! drag_connected) + { + g_signal_connect (widget, "drag-begin", + G_CALLBACK (gimp_dnd_data_drag_begin), + NULL); + g_signal_connect (widget, "drag-end", + G_CALLBACK (gimp_dnd_data_drag_end), + NULL); + g_signal_connect (widget, "drag-data-get", + G_CALLBACK (gimp_dnd_data_drag_handle), + NULL); + + g_object_set_data (G_OBJECT (widget), "gimp-dnd-drag-connected", + GINT_TO_POINTER (TRUE)); + } + + g_object_set_data (G_OBJECT (widget), dnd_data->get_data_func_name, + get_data_func); + g_object_set_data (G_OBJECT (widget), dnd_data->get_data_data_name, + get_data_data); + + /* remember the first set source type for drag view creation */ + if (! g_object_get_data (G_OBJECT (widget), "gimp-dnd-get-data-type")) + g_object_set_data (G_OBJECT (widget), "gimp-dnd-get-data-type", + GINT_TO_POINTER (data_type)); + + if (dnd_data->target_entry.target) + { + GtkTargetList *target_list; + + target_list = gtk_drag_source_get_target_list (widget); + + if (target_list) + { + gimp_dnd_target_list_add (target_list, &dnd_data->target_entry); + } + else + { + target_list = gtk_target_list_new (&dnd_data->target_entry, 1); + + gtk_drag_source_set_target_list (widget, target_list); + gtk_target_list_unref (target_list); + } + } +} + +static gboolean +gimp_dnd_data_source_remove (GimpDndType data_type, + GtkWidget *widget) +{ + const GimpDndDataDef *dnd_data; + gboolean drag_connected; + gboolean list_changed = FALSE; + + drag_connected = + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), + "gimp-dnd-drag-connected")); + + if (! drag_connected) + return FALSE; + + dnd_data = dnd_data_defs + data_type; + + g_object_set_data (G_OBJECT (widget), dnd_data->get_data_func_name, NULL); + g_object_set_data (G_OBJECT (widget), dnd_data->get_data_data_name, NULL); + + /* remove the dnd type remembered for the dnd icon */ + if (data_type == + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), + "gimp-dnd-get-data-type"))) + g_object_set_data (G_OBJECT (widget), "gimp-dnd-get-data-type", NULL); + + if (dnd_data->target_entry.target) + { + /* Don't just remove the target from the existing list, create a + * new list without the target and replace the old list. The + * source's target list is part of a drag operation's state, but + * only by reference, it's not copied. So when we change the + * list, we would change the state of that ongoing drag, making + * it impossible to drop anything. See bug #676522. + */ + GtkTargetList *target_list = gtk_drag_source_get_target_list (widget); + + if (target_list) + { + GtkTargetList *new_list; + GtkTargetEntry *targets; + gint n_targets_old; + gint n_targets_new; + gint i; + + targets = gtk_target_table_new_from_list (target_list, &n_targets_old); + + new_list = gtk_target_list_new (NULL, 0); + + for (i = 0; i < n_targets_old; i++) + { + if (targets[i].info != data_type) + { + gtk_target_list_add (new_list, + gdk_atom_intern (targets[i].target, FALSE), + targets[i].flags, + targets[i].info); + } + } + + gtk_target_table_free (targets, n_targets_old); + + targets = gtk_target_table_new_from_list (new_list, &n_targets_new); + gtk_target_table_free (targets, n_targets_new); + + if (n_targets_old != n_targets_new) + { + list_changed = TRUE; + + if (n_targets_new > 0) + gtk_drag_source_set_target_list (widget, new_list); + else + gtk_drag_source_set_target_list (widget, NULL); + } + + gtk_target_list_unref (new_list); + } + } + + return list_changed; +} + +static void +gimp_dnd_data_dest_add (GimpDndType data_type, + GtkWidget *widget, + gpointer set_data_func, + gpointer set_data_data) +{ + const GimpDndDataDef *dnd_data; + gboolean drop_connected; + + /* set a default drag dest if not already done */ + if (! g_object_get_data (G_OBJECT (widget), "gtk-drag-dest")) + gtk_drag_dest_set (widget, GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY); + + drop_connected = + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), + "gimp-dnd-drop-connected")); + + if (set_data_func && ! drop_connected) + { + g_signal_connect (widget, "drag-data-received", + G_CALLBACK (gimp_dnd_data_drop_handle), + NULL); + + g_object_set_data (G_OBJECT (widget), "gimp-dnd-drop-connected", + GINT_TO_POINTER (TRUE)); + } + + dnd_data = dnd_data_defs + data_type; + + if (set_data_func) + { + g_object_set_data (G_OBJECT (widget), dnd_data->set_data_func_name, + set_data_func); + g_object_set_data (G_OBJECT (widget), dnd_data->set_data_data_name, + set_data_data); + } + + if (dnd_data->target_entry.target) + { + GtkTargetList *target_list; + + target_list = gtk_drag_dest_get_target_list (widget); + + if (target_list) + { + gimp_dnd_target_list_add (target_list, &dnd_data->target_entry); + } + else + { + target_list = gtk_target_list_new (&dnd_data->target_entry, 1); + + gtk_drag_dest_set_target_list (widget, target_list); + gtk_target_list_unref (target_list); + } + } +} + +static void +gimp_dnd_data_dest_remove (GimpDndType data_type, + GtkWidget *widget) +{ + const GimpDndDataDef *dnd_data; + + dnd_data = dnd_data_defs + data_type; + + g_object_set_data (G_OBJECT (widget), dnd_data->set_data_func_name, NULL); + g_object_set_data (G_OBJECT (widget), dnd_data->set_data_data_name, NULL); + + if (dnd_data->target_entry.target) + { + GtkTargetList *target_list; + + target_list = gtk_drag_dest_get_target_list (widget); + + if (target_list) + { + GdkAtom atom = gdk_atom_intern (dnd_data->target_entry.target, TRUE); + + if (atom != GDK_NONE) + gtk_target_list_remove (target_list, atom); + } + } +} + + +/****************************/ +/* uri list dnd functions */ +/****************************/ + +static void +gimp_dnd_get_uri_list_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_uri_list_func, + gpointer get_uri_list_data, + GtkSelectionData *selection) +{ + GList *uri_list; + + uri_list = (* (GimpDndDragUriListFunc) get_uri_list_func) (widget, + get_uri_list_data); + + GIMP_LOG (DND, "uri_list %p", uri_list); + + if (uri_list) + { + gimp_selection_data_set_uri_list (selection, uri_list); + + g_list_free_full (uri_list, (GDestroyNotify) g_free); + } +} + +static gboolean +gimp_dnd_set_uri_list_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_uri_list_func, + gpointer set_uri_list_data, + GtkSelectionData *selection) +{ + GList *uri_list = gimp_selection_data_get_uri_list (selection); + + GIMP_LOG (DND, "uri_list %p", uri_list); + + if (! uri_list) + return FALSE; + + (* (GimpDndDropUriListFunc) set_uri_list_func) (widget, x, y, uri_list, + set_uri_list_data); + + g_list_free_full (uri_list, (GDestroyNotify) g_free); + + return TRUE; +} + +void +gimp_dnd_uri_list_source_add (GtkWidget *widget, + GimpDndDragUriListFunc get_uri_list_func, + gpointer data) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_source_add (GIMP_DND_TYPE_URI_LIST, widget, + G_CALLBACK (get_uri_list_func), + data); +} + +void +gimp_dnd_uri_list_source_remove (GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_source_remove (GIMP_DND_TYPE_URI_LIST, widget); +} + +void +gimp_dnd_uri_list_dest_add (GtkWidget *widget, + GimpDndDropUriListFunc set_uri_list_func, + gpointer data) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + /* Set a default drag dest if not already done. Explicitly set + * COPY and MOVE for file drag destinations. Some file managers + * such as Konqueror only offer MOVE by default. + */ + if (! g_object_get_data (G_OBJECT (widget), "gtk-drag-dest")) + gtk_drag_dest_set (widget, + GTK_DEST_DEFAULT_ALL, NULL, 0, + GDK_ACTION_COPY | GDK_ACTION_MOVE); + + gimp_dnd_data_dest_add (GIMP_DND_TYPE_URI_LIST, widget, + G_CALLBACK (set_uri_list_func), + data); + gimp_dnd_data_dest_add (GIMP_DND_TYPE_TEXT_PLAIN, widget, + G_CALLBACK (set_uri_list_func), + data); + gimp_dnd_data_dest_add (GIMP_DND_TYPE_NETSCAPE_URL, widget, + G_CALLBACK (set_uri_list_func), + data); +} + +void +gimp_dnd_uri_list_dest_remove (GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_dest_remove (GIMP_DND_TYPE_URI_LIST, widget); + gimp_dnd_data_dest_remove (GIMP_DND_TYPE_TEXT_PLAIN, widget); + gimp_dnd_data_dest_remove (GIMP_DND_TYPE_NETSCAPE_URL, widget); +} + + +/******************************/ +/* Direct Save Protocol (XDS) */ +/******************************/ + +static void +gimp_dnd_get_xds_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_image_func, + gpointer get_image_data, + GtkSelectionData *selection) +{ + GimpImage *image; + GimpContext *gimp_context; + + image = g_object_get_data (G_OBJECT (context), "gimp-dnd-viewable"); + + if (! image) + image = (GimpImage *) + (* (GimpDndDragViewableFunc) get_image_func) (widget, &gimp_context, + get_image_data); + + GIMP_LOG (DND, "image %p", image); + + if (image) + gimp_dnd_xds_save_image (context, image, selection); +} + +static void +gimp_dnd_xds_drag_begin (GtkWidget *widget, + GdkDragContext *context) +{ + const GimpDndDataDef *dnd_data = dnd_data_defs + GIMP_DND_TYPE_XDS; + GCallback get_data_func; + gpointer get_data_data; + + get_data_func = g_object_get_data (G_OBJECT (widget), + dnd_data->get_data_func_name); + get_data_data = g_object_get_data (G_OBJECT (widget), + dnd_data->get_data_data_name); + + if (get_data_func) + { + GimpImage *image; + GimpContext *gimp_context; + + image = (GimpImage *) + (* (GimpDndDragViewableFunc) get_data_func) (widget, &gimp_context, + get_data_data); + + GIMP_LOG (DND, "image %p", image); + + gimp_dnd_xds_source_set (context, image); + } +} + +static void +gimp_dnd_xds_drag_end (GtkWidget *widget, + GdkDragContext *context) +{ + gimp_dnd_xds_source_set (context, NULL); +} + +void +gimp_dnd_xds_source_add (GtkWidget *widget, + GimpDndDragViewableFunc get_image_func, + gpointer data) +{ + gulong handler; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_source_add (GIMP_DND_TYPE_XDS, widget, + G_CALLBACK (get_image_func), + data); + + handler = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), + "gimp-dnd-xds-drag-begin")); + + if (! handler) + { + handler = g_signal_connect (widget, "drag-begin", + G_CALLBACK (gimp_dnd_xds_drag_begin), + NULL); + g_object_set_data (G_OBJECT (widget), "gimp-dnd-xds-drag-begin", + GUINT_TO_POINTER (handler)); + } + + handler = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), + "gimp-dnd-xds-drag-end")); + + if (! handler) + { + handler = g_signal_connect (widget, "drag-end", + G_CALLBACK (gimp_dnd_xds_drag_end), + NULL); + g_object_set_data (G_OBJECT (widget), "gimp-dnd-xds-drag-end", + GUINT_TO_POINTER (handler)); + } +} + +void +gimp_dnd_xds_source_remove (GtkWidget *widget) +{ + gulong handler; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + handler = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), + "gimp-dnd-xds-drag-begin")); + if (handler) + { + g_signal_handler_disconnect (widget, handler); + g_object_set_data (G_OBJECT (widget), "gimp-dnd-xds-drag-begin", NULL); + } + + handler = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), + "gimp-dnd-xds-drag-end")); + if (handler) + { + g_signal_handler_disconnect (widget, handler); + g_object_set_data (G_OBJECT (widget), "gimp-dnd-xds-drag-end", NULL); + } + + gimp_dnd_data_source_remove (GIMP_DND_TYPE_XDS, widget); +} + + +/*************************/ +/* color dnd functions */ +/*************************/ + +static GtkWidget * +gimp_dnd_get_color_icon (GtkWidget *widget, + GdkDragContext *context, + GCallback get_color_func, + gpointer get_color_data) +{ + GtkWidget *color_area; + GimpRGB color; + + (* (GimpDndDragColorFunc) get_color_func) (widget, &color, get_color_data); + + GIMP_LOG (DND, "called"); + + g_object_set_data_full (G_OBJECT (context), + "gimp-dnd-color", g_memdup (&color, sizeof (GimpRGB)), + (GDestroyNotify) g_free); + + color_area = gimp_color_area_new (&color, GIMP_COLOR_AREA_SMALL_CHECKS, 0); + gimp_color_area_set_color_config (GIMP_COLOR_AREA (color_area), + the_dnd_gimp->config->color_management); + gtk_widget_set_size_request (color_area, + DRAG_PREVIEW_SIZE, DRAG_PREVIEW_SIZE); + + return color_area; +} + +static void +gimp_dnd_get_color_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_color_func, + gpointer get_color_data, + GtkSelectionData *selection) +{ + GimpRGB *c; + GimpRGB color; + + c = g_object_get_data (G_OBJECT (context), "gimp-dnd-color"); + + if (c) + color = *c; + else + (* (GimpDndDragColorFunc) get_color_func) (widget, &color, get_color_data); + + GIMP_LOG (DND, "called"); + + gimp_selection_data_set_color (selection, &color); +} + +static gboolean +gimp_dnd_set_color_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_color_func, + gpointer set_color_data, + GtkSelectionData *selection) +{ + GimpRGB color; + + GIMP_LOG (DND, "called"); + + if (! gimp_selection_data_get_color (selection, &color)) + return FALSE; + + (* (GimpDndDropColorFunc) set_color_func) (widget, x, y, &color, + set_color_data); + + return TRUE; +} + +void +gimp_dnd_color_source_add (GtkWidget *widget, + GimpDndDragColorFunc get_color_func, + gpointer data) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_source_add (GIMP_DND_TYPE_COLOR, widget, + G_CALLBACK (get_color_func), + data); +} + +void +gimp_dnd_color_source_remove (GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_source_remove (GIMP_DND_TYPE_COLOR, widget); +} + +void +gimp_dnd_color_dest_add (GtkWidget *widget, + GimpDndDropColorFunc set_color_func, + gpointer data) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_dest_add (GIMP_DND_TYPE_COLOR, widget, + G_CALLBACK (set_color_func), + data); +} + +void +gimp_dnd_color_dest_remove (GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_dest_remove (GIMP_DND_TYPE_COLOR, widget); +} + + +/**************************/ +/* stream dnd functions */ +/**************************/ + +static void +gimp_dnd_get_stream_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_stream_func, + gpointer get_stream_data, + GtkSelectionData *selection) +{ + guchar *stream; + gsize stream_length; + + stream = (* (GimpDndDragStreamFunc) get_stream_func) (widget, &stream_length, + get_stream_data); + + GIMP_LOG (DND, "stream %p, length %" G_GSIZE_FORMAT, stream, stream_length); + + if (stream) + { + gimp_selection_data_set_stream (selection, stream, stream_length); + g_free (stream); + } +} + +static gboolean +gimp_dnd_set_stream_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_stream_func, + gpointer set_stream_data, + GtkSelectionData *selection) +{ + const guchar *stream; + gsize stream_length; + + stream = gimp_selection_data_get_stream (selection, &stream_length); + + GIMP_LOG (DND, "stream %p, length %" G_GSIZE_FORMAT, stream, stream_length); + + if (! stream) + return FALSE; + + (* (GimpDndDropStreamFunc) set_stream_func) (widget, x, y, + stream, stream_length, + set_stream_data); + + return TRUE; +} + +void +gimp_dnd_svg_source_add (GtkWidget *widget, + GimpDndDragStreamFunc get_svg_func, + gpointer data) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_source_add (GIMP_DND_TYPE_SVG, widget, + G_CALLBACK (get_svg_func), + data); + gimp_dnd_data_source_add (GIMP_DND_TYPE_SVG_XML, widget, + G_CALLBACK (get_svg_func), + data); +} + +void +gimp_dnd_svg_source_remove (GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_source_remove (GIMP_DND_TYPE_SVG, widget); + gimp_dnd_data_source_remove (GIMP_DND_TYPE_SVG_XML, widget); +} + +void +gimp_dnd_svg_dest_add (GtkWidget *widget, + GimpDndDropStreamFunc set_svg_func, + gpointer data) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_dest_add (GIMP_DND_TYPE_SVG, widget, + G_CALLBACK (set_svg_func), + data); + gimp_dnd_data_dest_add (GIMP_DND_TYPE_SVG_XML, widget, + G_CALLBACK (set_svg_func), + data); +} + +void +gimp_dnd_svg_dest_remove (GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_dest_remove (GIMP_DND_TYPE_SVG, widget); + gimp_dnd_data_dest_remove (GIMP_DND_TYPE_SVG_XML, widget); +} + + +/**************************/ +/* pixbuf dnd functions */ +/**************************/ + +static void +gimp_dnd_get_pixbuf_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_pixbuf_func, + gpointer get_pixbuf_data, + GtkSelectionData *selection) +{ + GdkPixbuf *pixbuf; + + pixbuf = (* (GimpDndDragPixbufFunc) get_pixbuf_func) (widget, + get_pixbuf_data); + + GIMP_LOG (DND, "pixbuf %p", pixbuf); + + if (pixbuf) + { + gimp_set_busy (the_dnd_gimp); + + gtk_selection_data_set_pixbuf (selection, pixbuf); + g_object_unref (pixbuf); + + gimp_unset_busy (the_dnd_gimp); + } +} + +static gboolean +gimp_dnd_set_pixbuf_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_pixbuf_func, + gpointer set_pixbuf_data, + GtkSelectionData *selection) +{ + GdkPixbuf *pixbuf; + + gimp_set_busy (the_dnd_gimp); + + pixbuf = gtk_selection_data_get_pixbuf (selection); + + gimp_unset_busy (the_dnd_gimp); + + GIMP_LOG (DND, "pixbuf %p", pixbuf); + + if (! pixbuf) + return FALSE; + + (* (GimpDndDropPixbufFunc) set_pixbuf_func) (widget, x, y, + pixbuf, + set_pixbuf_data); + + g_object_unref (pixbuf); + + return TRUE; +} + +void +gimp_dnd_pixbuf_source_add (GtkWidget *widget, + GimpDndDragPixbufFunc get_pixbuf_func, + gpointer data) +{ + GtkTargetList *target_list; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_source_add (GIMP_DND_TYPE_PIXBUF, widget, + G_CALLBACK (get_pixbuf_func), + data); + + target_list = gtk_drag_source_get_target_list (widget); + + if (target_list) + gtk_target_list_ref (target_list); + else + target_list = gtk_target_list_new (NULL, 0); + + gimp_pixbuf_targets_add (target_list, GIMP_DND_TYPE_PIXBUF, TRUE); + + gtk_drag_source_set_target_list (widget, target_list); + gtk_target_list_unref (target_list); +} + +void +gimp_dnd_pixbuf_source_remove (GtkWidget *widget) +{ + GtkTargetList *target_list; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_source_remove (GIMP_DND_TYPE_PIXBUF, widget); + + target_list = gtk_drag_source_get_target_list (widget); + + if (target_list) + gimp_pixbuf_targets_remove (target_list); +} + +void +gimp_dnd_pixbuf_dest_add (GtkWidget *widget, + GimpDndDropPixbufFunc set_pixbuf_func, + gpointer data) +{ + GtkTargetList *target_list; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_dest_add (GIMP_DND_TYPE_PIXBUF, widget, + G_CALLBACK (set_pixbuf_func), + data); + + 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); + + gimp_pixbuf_targets_add (target_list, GIMP_DND_TYPE_PIXBUF, FALSE); + + gtk_drag_dest_set_target_list (widget, target_list); + gtk_target_list_unref (target_list); +} + +void +gimp_dnd_pixbuf_dest_remove (GtkWidget *widget) +{ + GtkTargetList *target_list; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_dest_remove (GIMP_DND_TYPE_PIXBUF, widget); + + target_list = gtk_drag_dest_get_target_list (widget); + + if (target_list) + gimp_pixbuf_targets_remove (target_list); +} + + +/*****************************/ +/* component dnd functions */ +/*****************************/ + +static GtkWidget * +gimp_dnd_get_component_icon (GtkWidget *widget, + GdkDragContext *context, + GCallback get_comp_func, + gpointer get_comp_data) +{ + GtkWidget *view; + GimpImage *image; + GimpContext *gimp_context; + GimpChannelType channel; + + image = (* (GimpDndDragComponentFunc) get_comp_func) (widget, &gimp_context, + &channel, + get_comp_data); + + GIMP_LOG (DND, "image %p, component %d", image, channel); + + if (! image) + return NULL; + + g_object_set_data_full (G_OBJECT (context), + "gimp-dnd-viewable", g_object_ref (image), + (GDestroyNotify) g_object_unref); + g_object_set_data (G_OBJECT (context), + "gimp-dnd-component", GINT_TO_POINTER (channel)); + + view = gimp_view_new (gimp_context, GIMP_VIEWABLE (image), + DRAG_PREVIEW_SIZE, 0, TRUE); + + GIMP_VIEW_RENDERER_IMAGE (GIMP_VIEW (view)->renderer)->channel = channel; + + return view; +} + +static void +gimp_dnd_get_component_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_comp_func, + gpointer get_comp_data, + GtkSelectionData *selection) +{ + GimpImage *image; + GimpContext *gimp_context; + GimpChannelType channel = 0; + + image = g_object_get_data (G_OBJECT (context), "gimp-dnd-viewable"); + channel = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context), + "gimp-dnd-component")); + + if (! image) + image = (* (GimpDndDragComponentFunc) get_comp_func) (widget, &gimp_context, + &channel, + get_comp_data); + + GIMP_LOG (DND, "image %p, component %d", image, channel); + + if (image) + gimp_selection_data_set_component (selection, image, channel); +} + +static gboolean +gimp_dnd_set_component_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_comp_func, + gpointer set_comp_data, + GtkSelectionData *selection) +{ + GimpImage *image; + GimpChannelType channel = 0; + + image = gimp_selection_data_get_component (selection, the_dnd_gimp, + &channel); + + GIMP_LOG (DND, "image %p, component %d", image, channel); + + if (! image) + return FALSE; + + (* (GimpDndDropComponentFunc) set_comp_func) (widget, x, y, + image, channel, + set_comp_data); + + return TRUE; +} + +void +gimp_dnd_component_source_add (GtkWidget *widget, + GimpDndDragComponentFunc get_comp_func, + gpointer data) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_source_add (GIMP_DND_TYPE_COMPONENT, widget, + G_CALLBACK (get_comp_func), + data); +} + +void +gimp_dnd_component_source_remove (GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_source_remove (GIMP_DND_TYPE_COMPONENT, widget); +} + +void +gimp_dnd_component_dest_add (GtkWidget *widget, + GimpDndDropComponentFunc set_comp_func, + gpointer data) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_dest_add (GIMP_DND_TYPE_COMPONENT, widget, + G_CALLBACK (set_comp_func), + data); +} + +void +gimp_dnd_component_dest_remove (GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gimp_dnd_data_dest_remove (GIMP_DND_TYPE_COMPONENT, widget); +} + + +/*******************************************/ +/* GimpViewable (by GType) dnd functions */ +/*******************************************/ + +static GtkWidget * +gimp_dnd_get_viewable_icon (GtkWidget *widget, + GdkDragContext *context, + GCallback get_viewable_func, + gpointer get_viewable_data) +{ + GimpViewable *viewable; + GimpContext *gimp_context; + GtkWidget *view; + gchar *desc; + + viewable = (* (GimpDndDragViewableFunc) get_viewable_func) (widget, + &gimp_context, + get_viewable_data); + + GIMP_LOG (DND, "viewable %p", viewable); + + if (! viewable) + return NULL; + + g_object_set_data_full (G_OBJECT (context), + "gimp-dnd-viewable", g_object_ref (viewable), + (GDestroyNotify) g_object_unref); + + view = gimp_view_new (gimp_context, viewable, + DRAG_PREVIEW_SIZE, 0, TRUE); + + desc = gimp_viewable_get_description (viewable, NULL); + + if (desc) + { + GtkWidget *hbox; + GtkWidget *label; + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 3); + gtk_box_pack_start (GTK_BOX (hbox), view, FALSE, FALSE, 0); + gtk_widget_show (view); + + label = g_object_new (GTK_TYPE_LABEL, + "label", desc, + "xpad", 3, + "xalign", 0.0, + "yalign", 0.5, + "max-width-chars", 30, + "ellipsize", PANGO_ELLIPSIZE_END, + NULL); + + g_free (desc); + + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + gtk_widget_show (label); + + return hbox; + } + + return view; +} + +static GimpDndType +gimp_dnd_data_type_get_by_g_type (GType type) +{ + GimpDndType dnd_type = GIMP_DND_TYPE_NONE; + + if (g_type_is_a (type, GIMP_TYPE_IMAGE)) + { + dnd_type = GIMP_DND_TYPE_IMAGE; + } + else if (g_type_is_a (type, GIMP_TYPE_LAYER)) + { + dnd_type = GIMP_DND_TYPE_LAYER; + } + else if (g_type_is_a (type, GIMP_TYPE_LAYER_MASK)) + { + dnd_type = GIMP_DND_TYPE_LAYER_MASK; + } + else if (g_type_is_a (type, GIMP_TYPE_CHANNEL)) + { + dnd_type = GIMP_DND_TYPE_CHANNEL; + } + else if (g_type_is_a (type, GIMP_TYPE_VECTORS)) + { + dnd_type = GIMP_DND_TYPE_VECTORS; + } + else if (g_type_is_a (type, GIMP_TYPE_BRUSH)) + { + dnd_type = GIMP_DND_TYPE_BRUSH; + } + else if (g_type_is_a (type, GIMP_TYPE_PATTERN)) + { + dnd_type = GIMP_DND_TYPE_PATTERN; + } + else if (g_type_is_a (type, GIMP_TYPE_GRADIENT)) + { + dnd_type = GIMP_DND_TYPE_GRADIENT; + } + else if (g_type_is_a (type, GIMP_TYPE_PALETTE)) + { + dnd_type = GIMP_DND_TYPE_PALETTE; + } + else if (g_type_is_a (type, GIMP_TYPE_FONT)) + { + dnd_type = GIMP_DND_TYPE_FONT; + } + else if (g_type_is_a (type, GIMP_TYPE_BUFFER)) + { + dnd_type = GIMP_DND_TYPE_BUFFER; + } + else if (g_type_is_a (type, GIMP_TYPE_IMAGEFILE)) + { + dnd_type = GIMP_DND_TYPE_IMAGEFILE; + } + else if (g_type_is_a (type, GIMP_TYPE_TEMPLATE)) + { + dnd_type = GIMP_DND_TYPE_TEMPLATE; + } + else if (g_type_is_a (type, GIMP_TYPE_TOOL_ITEM)) + { + dnd_type = GIMP_DND_TYPE_TOOL_ITEM; + } + + return dnd_type; +} + +gboolean +gimp_dnd_drag_source_set_by_type (GtkWidget *widget, + GdkModifierType start_button_mask, + GType type, + GdkDragAction actions) +{ + GimpDndType dnd_type; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + dnd_type = gimp_dnd_data_type_get_by_g_type (type); + + if (dnd_type == GIMP_DND_TYPE_NONE) + return FALSE; + + gtk_drag_source_set (widget, start_button_mask, + &dnd_data_defs[dnd_type].target_entry, 1, + actions); + + return TRUE; +} + +gboolean +gimp_dnd_drag_dest_set_by_type (GtkWidget *widget, + GtkDestDefaults flags, + GType type, + GdkDragAction actions) +{ + GimpDndType dnd_type; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + dnd_type = gimp_dnd_data_type_get_by_g_type (type); + + if (dnd_type == GIMP_DND_TYPE_NONE) + return FALSE; + + gtk_drag_dest_set (widget, flags, + &dnd_data_defs[dnd_type].target_entry, 1, + actions); + + return TRUE; +} + +gboolean +gimp_dnd_viewable_source_add (GtkWidget *widget, + GType type, + GimpDndDragViewableFunc get_viewable_func, + gpointer data) +{ + GimpDndType dnd_type; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + g_return_val_if_fail (get_viewable_func != NULL, FALSE); + + dnd_type = gimp_dnd_data_type_get_by_g_type (type); + + if (dnd_type == GIMP_DND_TYPE_NONE) + return FALSE; + + gimp_dnd_data_source_add (dnd_type, widget, + G_CALLBACK (get_viewable_func), + data); + + return TRUE; +} + +gboolean +gimp_dnd_viewable_source_remove (GtkWidget *widget, + GType type) +{ + GimpDndType dnd_type; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + dnd_type = gimp_dnd_data_type_get_by_g_type (type); + + if (dnd_type == GIMP_DND_TYPE_NONE) + return FALSE; + + return gimp_dnd_data_source_remove (dnd_type, widget); +} + +gboolean +gimp_dnd_viewable_dest_add (GtkWidget *widget, + GType type, + GimpDndDropViewableFunc set_viewable_func, + gpointer data) +{ + GimpDndType dnd_type; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + dnd_type = gimp_dnd_data_type_get_by_g_type (type); + + if (dnd_type == GIMP_DND_TYPE_NONE) + return FALSE; + + gimp_dnd_data_dest_add (dnd_type, widget, + G_CALLBACK (set_viewable_func), + data); + + return TRUE; +} + +gboolean +gimp_dnd_viewable_dest_remove (GtkWidget *widget, + GType type) +{ + GimpDndType dnd_type; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + dnd_type = gimp_dnd_data_type_get_by_g_type (type); + + if (dnd_type == GIMP_DND_TYPE_NONE) + return FALSE; + + gimp_dnd_data_dest_remove (dnd_type, widget); + + return TRUE; +} + +GimpViewable * +gimp_dnd_get_drag_data (GtkWidget *widget) +{ + const GimpDndDataDef *dnd_data; + GimpDndType data_type; + GimpDndDragViewableFunc get_data_func = NULL; + gpointer get_data_data = NULL; + GimpContext *context; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + data_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), + "gimp-dnd-get-data-type")); + + if (! data_type) + return NULL; + + dnd_data = dnd_data_defs + data_type; + + if (dnd_data->get_data_func_name) + get_data_func = g_object_get_data (G_OBJECT (widget), + dnd_data->get_data_func_name); + + if (dnd_data->get_data_data_name) + get_data_data = g_object_get_data (G_OBJECT (widget), + dnd_data->get_data_data_name); + + if (! get_data_func) + return NULL; + + return (GimpViewable *) (* get_data_func) (widget, &context, get_data_data); +} + + +/*****************************/ +/* GimpImage dnd functions */ +/*****************************/ + +static void +gimp_dnd_get_image_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_image_func, + gpointer get_image_data, + GtkSelectionData *selection) +{ + GimpImage *image; + GimpContext *gimp_context; + + image = g_object_get_data (G_OBJECT (context), "gimp-dnd-viewable"); + + if (! image) + image = (GimpImage *) + (* (GimpDndDragViewableFunc) get_image_func) (widget, &gimp_context, + get_image_data); + + GIMP_LOG (DND, "image %p", image); + + if (image) + gimp_selection_data_set_image (selection, image); +} + +static gboolean +gimp_dnd_set_image_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_image_func, + gpointer set_image_data, + GtkSelectionData *selection) +{ + GimpImage *image = gimp_selection_data_get_image (selection, the_dnd_gimp); + + GIMP_LOG (DND, "image %p", image); + + if (! image) + return FALSE; + + (* (GimpDndDropViewableFunc) set_image_func) (widget, x, y, + GIMP_VIEWABLE (image), + set_image_data); + + return TRUE; +} + + +/****************************/ +/* GimpItem dnd functions */ +/****************************/ + +static void +gimp_dnd_get_item_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_item_func, + gpointer get_item_data, + GtkSelectionData *selection) +{ + GimpItem *item; + GimpContext *gimp_context; + + item = g_object_get_data (G_OBJECT (context), "gimp-dnd-viewable"); + + if (! item) + item = (GimpItem *) + (* (GimpDndDragViewableFunc) get_item_func) (widget, &gimp_context, + get_item_data); + + GIMP_LOG (DND, "item %p", item); + + if (item) + gimp_selection_data_set_item (selection, item); +} + +static gboolean +gimp_dnd_set_item_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_item_func, + gpointer set_item_data, + GtkSelectionData *selection) +{ + GimpItem *item = gimp_selection_data_get_item (selection, the_dnd_gimp); + + GIMP_LOG (DND, "item %p", item); + + if (! item) + return FALSE; + + (* (GimpDndDropViewableFunc) set_item_func) (widget, x, y, + GIMP_VIEWABLE (item), + set_item_data); + + return TRUE; +} + + +/******************************/ +/* GimpObject dnd functions */ +/******************************/ + +static void +gimp_dnd_get_object_data (GtkWidget *widget, + GdkDragContext *context, + GCallback get_object_func, + gpointer get_object_data, + GtkSelectionData *selection) +{ + GimpObject *object; + GimpContext *gimp_context; + + object = g_object_get_data (G_OBJECT (context), "gimp-dnd-viewable"); + + if (! object) + object = (GimpObject *) + (* (GimpDndDragViewableFunc) get_object_func) (widget, &gimp_context, + get_object_data); + + GIMP_LOG (DND, "object %p", object); + + if (GIMP_IS_OBJECT (object)) + gimp_selection_data_set_object (selection, object); +} + + +/*****************************/ +/* GimpBrush dnd functions */ +/*****************************/ + +static gboolean +gimp_dnd_set_brush_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_brush_func, + gpointer set_brush_data, + GtkSelectionData *selection) +{ + GimpBrush *brush = gimp_selection_data_get_brush (selection, the_dnd_gimp); + + GIMP_LOG (DND, "brush %p", brush); + + if (! brush) + return FALSE; + + (* (GimpDndDropViewableFunc) set_brush_func) (widget, x, y, + GIMP_VIEWABLE (brush), + set_brush_data); + + return TRUE; +} + + +/*******************************/ +/* GimpPattern dnd functions */ +/*******************************/ + +static gboolean +gimp_dnd_set_pattern_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_pattern_func, + gpointer set_pattern_data, + GtkSelectionData *selection) +{ + GimpPattern *pattern = gimp_selection_data_get_pattern (selection, + the_dnd_gimp); + + GIMP_LOG (DND, "pattern %p", pattern); + + if (! pattern) + return FALSE; + + (* (GimpDndDropViewableFunc) set_pattern_func) (widget, x, y, + GIMP_VIEWABLE (pattern), + set_pattern_data); + + return TRUE; +} + + +/********************************/ +/* GimpGradient dnd functions */ +/********************************/ + +static gboolean +gimp_dnd_set_gradient_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_gradient_func, + gpointer set_gradient_data, + GtkSelectionData *selection) +{ + GimpGradient *gradient = gimp_selection_data_get_gradient (selection, + the_dnd_gimp); + + GIMP_LOG (DND, "gradient %p", gradient); + + if (! gradient) + return FALSE; + + (* (GimpDndDropViewableFunc) set_gradient_func) (widget, x, y, + GIMP_VIEWABLE (gradient), + set_gradient_data); + + return TRUE; +} + + +/*******************************/ +/* GimpPalette dnd functions */ +/*******************************/ + +static gboolean +gimp_dnd_set_palette_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_palette_func, + gpointer set_palette_data, + GtkSelectionData *selection) +{ + GimpPalette *palette = gimp_selection_data_get_palette (selection, + the_dnd_gimp); + + GIMP_LOG (DND, "palette %p", palette); + + if (! palette) + return FALSE; + + (* (GimpDndDropViewableFunc) set_palette_func) (widget, x, y, + GIMP_VIEWABLE (palette), + set_palette_data); + + return TRUE; +} + + +/****************************/ +/* GimpFont dnd functions */ +/****************************/ + +static gboolean +gimp_dnd_set_font_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_font_func, + gpointer set_font_data, + GtkSelectionData *selection) +{ + GimpFont *font = gimp_selection_data_get_font (selection, the_dnd_gimp); + + GIMP_LOG (DND, "font %p", font); + + if (! font) + return FALSE; + + (* (GimpDndDropViewableFunc) set_font_func) (widget, x, y, + GIMP_VIEWABLE (font), + set_font_data); + + return TRUE; +} + + +/******************************/ +/* GimpBuffer dnd functions */ +/******************************/ + +static gboolean +gimp_dnd_set_buffer_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_buffer_func, + gpointer set_buffer_data, + GtkSelectionData *selection) +{ + GimpBuffer *buffer = gimp_selection_data_get_buffer (selection, the_dnd_gimp); + + GIMP_LOG (DND, "buffer %p", buffer); + + if (! buffer) + return FALSE; + + (* (GimpDndDropViewableFunc) set_buffer_func) (widget, x, y, + GIMP_VIEWABLE (buffer), + set_buffer_data); + + return TRUE; +} + + +/*********************************/ +/* GimpImagefile dnd functions */ +/*********************************/ + +static gboolean +gimp_dnd_set_imagefile_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_imagefile_func, + gpointer set_imagefile_data, + GtkSelectionData *selection) +{ + GimpImagefile *imagefile = gimp_selection_data_get_imagefile (selection, + the_dnd_gimp); + + GIMP_LOG (DND, "imagefile %p", imagefile); + + if (! imagefile) + return FALSE; + + (* (GimpDndDropViewableFunc) set_imagefile_func) (widget, x, y, + GIMP_VIEWABLE (imagefile), + set_imagefile_data); + + return TRUE; +} + + +/********************************/ +/* GimpTemplate dnd functions */ +/********************************/ + +static gboolean +gimp_dnd_set_template_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_template_func, + gpointer set_template_data, + GtkSelectionData *selection) +{ + GimpTemplate *template = gimp_selection_data_get_template (selection, + the_dnd_gimp); + + GIMP_LOG (DND, "template %p", template); + + if (! template) + return FALSE; + + (* (GimpDndDropViewableFunc) set_template_func) (widget, x, y, + GIMP_VIEWABLE (template), + set_template_data); + + return TRUE; +} + + +/*********************************/ +/* GimpToolEntry dnd functions */ +/*********************************/ + +static gboolean +gimp_dnd_set_tool_item_data (GtkWidget *widget, + gint x, + gint y, + GCallback set_tool_item_func, + gpointer set_tool_item_data, + GtkSelectionData *selection) +{ + GimpToolItem *tool_item = gimp_selection_data_get_tool_item (selection, + the_dnd_gimp); + + GIMP_LOG (DND, "tool_item %p", tool_item); + + if (! tool_item) + return FALSE; + + (* (GimpDndDropViewableFunc) set_tool_item_func) (widget, x, y, + GIMP_VIEWABLE (tool_item), + set_tool_item_data); + + return TRUE; +} |