PIKApp/app/widgets/pikadnd.c

2817 lines
86 KiB
C

/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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 <math.h>
#include "libpikacolor/pikacolor.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "config/pikacoreconfig.h"
#include "core/pika.h"
#include "core/pikabrush.h"
#include "core/pikabuffer.h"
#include "core/pikachannel.h"
#include "core/pikacontainer.h"
#include "core/pikadatafactory.h"
#include "core/pikadrawable.h"
#include "core/pikagradient.h"
#include "core/pikaimage.h"
#include "core/pikaimagefile.h"
#include "core/pikalayer.h"
#include "core/pikalayermask.h"
#include "core/pikapalette.h"
#include "core/pikapattern.h"
#include "core/pikatemplate.h"
#include "core/pikatoolitem.h"
#include "text/pikafont.h"
#include "vectors/pikavectors.h"
#include "pikadnd.h"
#include "pikadnd-xds.h"
#include "pikapixbuf.h"
#include "pikaselectiondata.h"
#include "pikaview.h"
#include "pikaviewrendererimage.h"
#include "pika-log.h"
#include "pika-intl.h"
#define DRAG_PREVIEW_SIZE PIKA_VIEW_SIZE_LARGE
#define DRAG_ICON_OFFSET -8
typedef GtkWidget * (* PikaDndGetIconFunc) (GtkWidget *widget,
GdkDragContext *context,
GCallback get_data_func,
gpointer get_data_data);
typedef void (* PikaDndDragDataFunc) (GtkWidget *widget,
GdkDragContext *context,
GCallback get_data_func,
gpointer get_data_data,
GtkSelectionData *selection);
typedef gboolean (* PikaDndDropDataFunc) (GtkWidget *widget,
gint x,
gint y,
GCallback set_data_func,
gpointer set_data_data,
GtkSelectionData *selection);
typedef struct _PikaDndDataDef PikaDndDataDef;
struct _PikaDndDataDef
{
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;
PikaDndGetIconFunc get_icon_func;
PikaDndDragDataFunc get_data_func;
PikaDndDropDataFunc set_data_func;
};
static GtkWidget * pika_dnd_get_viewable_list_icon (GtkWidget *widget,
GdkDragContext *context,
GCallback get_viewable_list_func,
gpointer get_viewable_list_data);
static GtkWidget * pika_dnd_get_viewable_icon (GtkWidget *widget,
GdkDragContext *context,
GCallback get_viewable_func,
gpointer get_viewable_data);
static GtkWidget * pika_dnd_get_component_icon (GtkWidget *widget,
GdkDragContext *context,
GCallback get_comp_func,
gpointer get_comp_data);
static GtkWidget * pika_dnd_get_color_icon (GtkWidget *widget,
GdkDragContext *context,
GCallback get_color_func,
gpointer get_color_data);
static void pika_dnd_get_uri_list_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_uri_list_func,
gpointer get_uri_list_data,
GtkSelectionData *selection);
static gboolean pika_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 pika_dnd_get_xds_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_image_func,
gpointer get_image_data,
GtkSelectionData *selection);
static void pika_dnd_get_color_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_color_func,
gpointer get_color_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_color_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_color_func,
gpointer set_color_data,
GtkSelectionData *selection);
static void pika_dnd_get_stream_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_stream_func,
gpointer get_stream_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_stream_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_stream_func,
gpointer set_stream_data,
GtkSelectionData *selection);
static void pika_dnd_get_pixbuf_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_pixbuf_func,
gpointer get_pixbuf_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_pixbuf_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_pixbuf_func,
gpointer set_pixbuf_data,
GtkSelectionData *selection);
static void pika_dnd_get_component_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_comp_func,
gpointer get_comp_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_component_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_comp_func,
gpointer set_comp_data,
GtkSelectionData *selection);
static void pika_dnd_get_image_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_image_func,
gpointer get_image_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_image_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_image_func,
gpointer set_image_data,
GtkSelectionData *selection);
static void pika_dnd_get_item_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_item_func,
gpointer get_item_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_item_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_item_func,
gpointer set_item_data,
GtkSelectionData *selection);
static void pika_dnd_get_item_list_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_item_func,
gpointer get_item_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_item_list_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_item_func,
gpointer set_item_data,
GtkSelectionData *selection);
static void pika_dnd_get_object_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_object_func,
gpointer get_object_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_brush_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_brush_func,
gpointer set_brush_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_pattern_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_pattern_func,
gpointer set_pattern_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_gradient_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_gradient_func,
gpointer set_gradient_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_palette_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_palette_func,
gpointer set_palette_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_font_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_font_func,
gpointer set_font_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_buffer_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_buffer_func,
gpointer set_buffer_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_imagefile_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_imagefile_func,
gpointer set_imagefile_data,
GtkSelectionData *selection);
static gboolean pika_dnd_set_template_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_template_func,
gpointer set_template_data,
GtkSelectionData *selection);
static gboolean pika_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 PikaDndDataDef dnd_data_defs[] =
{
{
{ NULL, 0, -1 },
NULL,
NULL,
NULL,
NULL,
NULL
},
{
PIKA_TARGET_URI_LIST,
"pika-dnd-get-uri-list-func",
"pika-dnd-get-uri-list-data",
"pika-dnd-set-uri-list-func",
"pika-dnd-set-uri-list-data",
NULL,
pika_dnd_get_uri_list_data,
pika_dnd_set_uri_list_data
},
{
PIKA_TARGET_TEXT_PLAIN,
NULL,
NULL,
"pika-dnd-set-uri-list-func",
"pika-dnd-set-uri-list-data",
NULL,
NULL,
pika_dnd_set_uri_list_data
},
{
PIKA_TARGET_NETSCAPE_URL,
NULL,
NULL,
"pika-dnd-set-uri-list-func",
"pika-dnd-set-uri-list-data",
NULL,
NULL,
pika_dnd_set_uri_list_data
},
{
PIKA_TARGET_XDS,
"pika-dnd-get-xds-func",
"pika-dnd-get-xds-data",
NULL,
NULL,
pika_dnd_get_viewable_icon,
pika_dnd_get_xds_data,
NULL
},
{
PIKA_TARGET_COLOR,
"pika-dnd-get-color-func",
"pika-dnd-get-color-data",
"pika-dnd-set-color-func",
"pika-dnd-set-color-data",
pika_dnd_get_color_icon,
pika_dnd_get_color_data,
pika_dnd_set_color_data
},
{
PIKA_TARGET_SVG,
"pika-dnd-get-svg-func",
"pika-dnd-get-svg-data",
"pika-dnd-set-svg-func",
"pika-dnd-set-svg-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_stream_data,
pika_dnd_set_stream_data
},
{
PIKA_TARGET_SVG_XML,
"pika-dnd-get-svg-xml-func",
"pika-dnd-get-svg-xml-data",
"pika-dnd-set-svg-xml-func",
"pika-dnd-set-svg-xml-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_stream_data,
pika_dnd_set_stream_data
},
{
PIKA_TARGET_PIXBUF,
"pika-dnd-get-pixbuf-func",
"pika-dnd-get-pixbuf-data",
"pika-dnd-set-pixbuf-func",
"pika-dnd-set-pixbuf-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_pixbuf_data,
pika_dnd_set_pixbuf_data
},
{
PIKA_TARGET_IMAGE,
"pika-dnd-get-image-func",
"pika-dnd-get-image-data",
"pika-dnd-set-image-func",
"pika-dnd-set-image-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_image_data,
pika_dnd_set_image_data,
},
{
PIKA_TARGET_COMPONENT,
"pika-dnd-get-component-func",
"pika-dnd-get-component-data",
"pika-dnd-set-component-func",
"pika-dnd-set-component-data",
pika_dnd_get_component_icon,
pika_dnd_get_component_data,
pika_dnd_set_component_data,
},
{
PIKA_TARGET_LAYER,
"pika-dnd-get-layer-func",
"pika-dnd-get-layer-data",
"pika-dnd-set-layer-func",
"pika-dnd-set-layer-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_item_data,
pika_dnd_set_item_data,
},
{
PIKA_TARGET_CHANNEL,
"pika-dnd-get-channel-func",
"pika-dnd-get-channel-data",
"pika-dnd-set-channel-func",
"pika-dnd-set-channel-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_item_data,
pika_dnd_set_item_data,
},
{
PIKA_TARGET_LAYER_MASK,
"pika-dnd-get-layer-mask-func",
"pika-dnd-get-layer-mask-data",
"pika-dnd-set-layer-mask-func",
"pika-dnd-set-layer-mask-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_item_data,
pika_dnd_set_item_data,
},
{
PIKA_TARGET_VECTORS,
"pika-dnd-get-vectors-func",
"pika-dnd-get-vectors-data",
"pika-dnd-set-vectors-func",
"pika-dnd-set-vectors-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_item_data,
pika_dnd_set_item_data,
},
{
PIKA_TARGET_BRUSH,
"pika-dnd-get-brush-func",
"pika-dnd-get-brush-data",
"pika-dnd-set-brush-func",
"pika-dnd-set-brush-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_object_data,
pika_dnd_set_brush_data
},
{
PIKA_TARGET_PATTERN,
"pika-dnd-get-pattern-func",
"pika-dnd-get-pattern-data",
"pika-dnd-set-pattern-func",
"pika-dnd-set-pattern-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_object_data,
pika_dnd_set_pattern_data
},
{
PIKA_TARGET_GRADIENT,
"pika-dnd-get-gradient-func",
"pika-dnd-get-gradient-data",
"pika-dnd-set-gradient-func",
"pika-dnd-set-gradient-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_object_data,
pika_dnd_set_gradient_data
},
{
PIKA_TARGET_PALETTE,
"pika-dnd-get-palette-func",
"pika-dnd-get-palette-data",
"pika-dnd-set-palette-func",
"pika-dnd-set-palette-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_object_data,
pika_dnd_set_palette_data
},
{
PIKA_TARGET_FONT,
"pika-dnd-get-font-func",
"pika-dnd-get-font-data",
"pika-dnd-set-font-func",
"pika-dnd-set-font-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_object_data,
pika_dnd_set_font_data
},
{
PIKA_TARGET_BUFFER,
"pika-dnd-get-buffer-func",
"pika-dnd-get-buffer-data",
"pika-dnd-set-buffer-func",
"pika-dnd-set-buffer-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_object_data,
pika_dnd_set_buffer_data
},
{
PIKA_TARGET_IMAGEFILE,
"pika-dnd-get-imagefile-func",
"pika-dnd-get-imagefile-data",
"pika-dnd-set-imagefile-func",
"pika-dnd-set-imagefile-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_object_data,
pika_dnd_set_imagefile_data
},
{
PIKA_TARGET_TEMPLATE,
"pika-dnd-get-template-func",
"pika-dnd-get-template-data",
"pika-dnd-set-template-func",
"pika-dnd-set-template-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_object_data,
pika_dnd_set_template_data
},
{
PIKA_TARGET_TOOL_ITEM,
"pika-dnd-get-tool-item-func",
"pika-dnd-get-tool-item-data",
"pika-dnd-set-tool-item-func",
"pika-dnd-set-tool-item-data",
pika_dnd_get_viewable_icon,
pika_dnd_get_object_data,
pika_dnd_set_tool_item_data
},
{
PIKA_TARGET_NOTEBOOK_TAB,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
},
{
PIKA_TARGET_LAYER_LIST,
"pika-dnd-get-layer-list-func",
"pika-dnd-get-layer-list-data",
"pika-dnd-set-layer-list-func",
"pika-dnd-set-layer-list-data",
pika_dnd_get_viewable_list_icon,
pika_dnd_get_item_list_data,
pika_dnd_set_item_list_data,
},
{
PIKA_TARGET_CHANNEL_LIST,
"pika-dnd-get-channel-list-func",
"pika-dnd-get-channel-list-data",
"pika-dnd-set-channel-list-func",
"pika-dnd-set-channel-list-data",
pika_dnd_get_viewable_list_icon,
pika_dnd_get_item_list_data,
pika_dnd_set_item_list_data,
},
{
PIKA_TARGET_VECTORS_LIST,
"pika-dnd-get-vectors-list-func",
"pika-dnd-get-vectors-list-data",
"pika-dnd-set-vectors-list-func",
"pika-dnd-set-vectors-list-data",
pika_dnd_get_viewable_list_icon,
pika_dnd_get_item_list_data,
pika_dnd_set_item_list_data,
},
};
static Pika *the_dnd_pika = NULL;
void
pika_dnd_init (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (the_dnd_pika == NULL);
the_dnd_pika = pika;
}
/**********************/
/* helper functions */
/**********************/
static void
pika_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
pika_dnd_data_drag_begin (GtkWidget *widget,
GdkDragContext *context,
gpointer data)
{
const PikaDndDataDef *dnd_data;
PikaDndType 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),
"pika-dnd-get-data-type"));
PIKA_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), "pika-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), "pika-gdk-drag-context", context);
}
}
static void
pika_dnd_data_drag_end (GtkWidget *widget,
GdkDragContext *context)
{
PikaDndType data_type;
GtkWidget *icon_widget;
data_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
"pika-dnd-get-data-type"));
PIKA_LOG (DND, "data type %d", data_type);
icon_widget = g_object_get_data (G_OBJECT (widget), "pika-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),
"pika-gdk-drag-context") ==
(gpointer) context)
{
g_object_set_data (G_OBJECT (widget), "pika-dnd-data-widget", NULL);
}
}
}
static void
pika_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;
PikaDndType data_type;
PIKA_LOG (DND, "data type %d", info);
for (data_type = PIKA_DND_TYPE_NONE + 1;
data_type <= PIKA_DND_TYPE_LAST;
data_type++)
{
const PikaDndDataDef *dnd_data = dnd_data_defs + data_type;
if (dnd_data->target_entry.info == info)
{
PIKA_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
pika_dnd_data_drop_handle (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint time,
gpointer data)
{
PikaDndType data_type;
PIKA_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 = PIKA_DND_TYPE_NONE + 1;
data_type <= PIKA_DND_TYPE_LAST;
data_type++)
{
const PikaDndDataDef *dnd_data = dnd_data_defs + data_type;
if (dnd_data->target_entry.info == info)
{
GCallback set_data_func = NULL;
gpointer set_data_data = NULL;
PIKA_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
pika_dnd_data_source_add (PikaDndType data_type,
GtkWidget *widget,
GCallback get_data_func,
gpointer get_data_data)
{
const PikaDndDataDef *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),
"pika-dnd-drag-connected"));
if (! drag_connected)
{
g_signal_connect (widget, "drag-begin",
G_CALLBACK (pika_dnd_data_drag_begin),
NULL);
g_signal_connect (widget, "drag-end",
G_CALLBACK (pika_dnd_data_drag_end),
NULL);
g_signal_connect (widget, "drag-data-get",
G_CALLBACK (pika_dnd_data_drag_handle),
NULL);
g_object_set_data (G_OBJECT (widget), "pika-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), "pika-dnd-get-data-type"))
g_object_set_data (G_OBJECT (widget), "pika-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)
{
pika_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
pika_dnd_data_source_remove (PikaDndType data_type,
GtkWidget *widget)
{
const PikaDndDataDef *dnd_data;
gboolean drag_connected;
gboolean list_changed = FALSE;
drag_connected =
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
"pika-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),
"pika-dnd-get-data-type")))
g_object_set_data (G_OBJECT (widget), "pika-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
pika_dnd_data_dest_add (PikaDndType data_type,
GtkWidget *widget,
gpointer set_data_func,
gpointer set_data_data)
{
const PikaDndDataDef *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),
"pika-dnd-drop-connected"));
if (set_data_func && ! drop_connected)
{
g_signal_connect (widget, "drag-data-received",
G_CALLBACK (pika_dnd_data_drop_handle),
NULL);
g_object_set_data (G_OBJECT (widget), "pika-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)
{
pika_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
pika_dnd_data_dest_remove (PikaDndType data_type,
GtkWidget *widget)
{
const PikaDndDataDef *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
pika_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 = (* (PikaDndDragUriListFunc) get_uri_list_func) (widget,
get_uri_list_data);
PIKA_LOG (DND, "uri_list %p", uri_list);
if (uri_list)
{
pika_selection_data_set_uri_list (selection, uri_list);
g_list_free_full (uri_list, (GDestroyNotify) g_free);
}
}
static gboolean
pika_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 = pika_selection_data_get_uri_list (selection);
PIKA_LOG (DND, "uri_list %p", uri_list);
if (! uri_list)
return FALSE;
(* (PikaDndDropUriListFunc) 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
pika_dnd_uri_list_source_add (GtkWidget *widget,
PikaDndDragUriListFunc get_uri_list_func,
gpointer data)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_source_add (PIKA_DND_TYPE_URI_LIST, widget,
G_CALLBACK (get_uri_list_func),
data);
}
void
pika_dnd_uri_list_source_remove (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_source_remove (PIKA_DND_TYPE_URI_LIST, widget);
}
void
pika_dnd_uri_list_dest_add (GtkWidget *widget,
PikaDndDropUriListFunc 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);
pika_dnd_data_dest_add (PIKA_DND_TYPE_URI_LIST, widget,
G_CALLBACK (set_uri_list_func),
data);
pika_dnd_data_dest_add (PIKA_DND_TYPE_TEXT_PLAIN, widget,
G_CALLBACK (set_uri_list_func),
data);
pika_dnd_data_dest_add (PIKA_DND_TYPE_NETSCAPE_URL, widget,
G_CALLBACK (set_uri_list_func),
data);
}
void
pika_dnd_uri_list_dest_remove (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_dest_remove (PIKA_DND_TYPE_URI_LIST, widget);
pika_dnd_data_dest_remove (PIKA_DND_TYPE_TEXT_PLAIN, widget);
pika_dnd_data_dest_remove (PIKA_DND_TYPE_NETSCAPE_URL, widget);
}
/******************************/
/* Direct Save Protocol (XDS) */
/******************************/
static void
pika_dnd_get_xds_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_image_func,
gpointer get_image_data,
GtkSelectionData *selection)
{
PikaImage *image;
PikaContext *pika_context;
image = g_object_get_data (G_OBJECT (context), "pika-dnd-viewable");
if (! image)
image = (PikaImage *)
(* (PikaDndDragViewableFunc) get_image_func) (widget, &pika_context,
get_image_data);
PIKA_LOG (DND, "image %p", image);
if (image)
pika_dnd_xds_save_image (context, image, selection);
}
static void
pika_dnd_xds_drag_begin (GtkWidget *widget,
GdkDragContext *context)
{
const PikaDndDataDef *dnd_data = dnd_data_defs + PIKA_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)
{
PikaImage *image;
PikaContext *pika_context;
image = (PikaImage *)
(* (PikaDndDragViewableFunc) get_data_func) (widget, &pika_context,
get_data_data);
PIKA_LOG (DND, "image %p", image);
pika_dnd_xds_source_set (context, image);
}
}
static void
pika_dnd_xds_drag_end (GtkWidget *widget,
GdkDragContext *context)
{
pika_dnd_xds_source_set (context, NULL);
}
void
pika_dnd_xds_source_add (GtkWidget *widget,
PikaDndDragViewableFunc get_image_func,
gpointer data)
{
gulong handler;
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_source_add (PIKA_DND_TYPE_XDS, widget,
G_CALLBACK (get_image_func),
data);
handler = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget),
"pika-dnd-xds-drag-begin"));
if (! handler)
{
handler = g_signal_connect (widget, "drag-begin",
G_CALLBACK (pika_dnd_xds_drag_begin),
NULL);
g_object_set_data (G_OBJECT (widget), "pika-dnd-xds-drag-begin",
GUINT_TO_POINTER (handler));
}
handler = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget),
"pika-dnd-xds-drag-end"));
if (! handler)
{
handler = g_signal_connect (widget, "drag-end",
G_CALLBACK (pika_dnd_xds_drag_end),
NULL);
g_object_set_data (G_OBJECT (widget), "pika-dnd-xds-drag-end",
GUINT_TO_POINTER (handler));
}
}
void
pika_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),
"pika-dnd-xds-drag-begin"));
if (handler)
{
g_signal_handler_disconnect (widget, handler);
g_object_set_data (G_OBJECT (widget), "pika-dnd-xds-drag-begin", NULL);
}
handler = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget),
"pika-dnd-xds-drag-end"));
if (handler)
{
g_signal_handler_disconnect (widget, handler);
g_object_set_data (G_OBJECT (widget), "pika-dnd-xds-drag-end", NULL);
}
pika_dnd_data_source_remove (PIKA_DND_TYPE_XDS, widget);
}
/*************************/
/* color dnd functions */
/*************************/
static GtkWidget *
pika_dnd_get_color_icon (GtkWidget *widget,
GdkDragContext *context,
GCallback get_color_func,
gpointer get_color_data)
{
GtkWidget *color_area;
PikaRGB color;
(* (PikaDndDragColorFunc) get_color_func) (widget, &color, get_color_data);
PIKA_LOG (DND, "called");
g_object_set_data_full (G_OBJECT (context),
"pika-dnd-color", g_memdup2 (&color, sizeof (PikaRGB)),
(GDestroyNotify) g_free);
color_area = pika_color_area_new (&color, PIKA_COLOR_AREA_SMALL_CHECKS, 0);
pika_color_area_set_color_config (PIKA_COLOR_AREA (color_area),
the_dnd_pika->config->color_management);
gtk_widget_set_size_request (color_area,
DRAG_PREVIEW_SIZE, DRAG_PREVIEW_SIZE);
return color_area;
}
static void
pika_dnd_get_color_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_color_func,
gpointer get_color_data,
GtkSelectionData *selection)
{
PikaRGB *c;
PikaRGB color;
c = g_object_get_data (G_OBJECT (context), "pika-dnd-color");
if (c)
color = *c;
else
(* (PikaDndDragColorFunc) get_color_func) (widget, &color, get_color_data);
PIKA_LOG (DND, "called");
pika_selection_data_set_color (selection, &color);
}
static gboolean
pika_dnd_set_color_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_color_func,
gpointer set_color_data,
GtkSelectionData *selection)
{
PikaRGB color;
PIKA_LOG (DND, "called");
if (! pika_selection_data_get_color (selection, &color))
return FALSE;
(* (PikaDndDropColorFunc) set_color_func) (widget, x, y, &color,
set_color_data);
return TRUE;
}
void
pika_dnd_color_source_add (GtkWidget *widget,
PikaDndDragColorFunc get_color_func,
gpointer data)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_source_add (PIKA_DND_TYPE_COLOR, widget,
G_CALLBACK (get_color_func),
data);
}
void
pika_dnd_color_source_remove (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_source_remove (PIKA_DND_TYPE_COLOR, widget);
}
void
pika_dnd_color_dest_add (GtkWidget *widget,
PikaDndDropColorFunc set_color_func,
gpointer data)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_dest_add (PIKA_DND_TYPE_COLOR, widget,
G_CALLBACK (set_color_func),
data);
}
void
pika_dnd_color_dest_remove (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_dest_remove (PIKA_DND_TYPE_COLOR, widget);
}
/**************************/
/* stream dnd functions */
/**************************/
static void
pika_dnd_get_stream_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_stream_func,
gpointer get_stream_data,
GtkSelectionData *selection)
{
guchar *stream;
gsize stream_length;
stream = (* (PikaDndDragStreamFunc) get_stream_func) (widget, &stream_length,
get_stream_data);
PIKA_LOG (DND, "stream %p, length %" G_GSIZE_FORMAT, stream, stream_length);
if (stream)
{
pika_selection_data_set_stream (selection, stream, stream_length);
g_free (stream);
}
}
static gboolean
pika_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 = pika_selection_data_get_stream (selection, &stream_length);
PIKA_LOG (DND, "stream %p, length %" G_GSIZE_FORMAT, stream, stream_length);
if (! stream)
return FALSE;
(* (PikaDndDropStreamFunc) set_stream_func) (widget, x, y,
stream, stream_length,
set_stream_data);
return TRUE;
}
void
pika_dnd_svg_source_add (GtkWidget *widget,
PikaDndDragStreamFunc get_svg_func,
gpointer data)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_source_add (PIKA_DND_TYPE_SVG, widget,
G_CALLBACK (get_svg_func),
data);
pika_dnd_data_source_add (PIKA_DND_TYPE_SVG_XML, widget,
G_CALLBACK (get_svg_func),
data);
}
void
pika_dnd_svg_source_remove (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_source_remove (PIKA_DND_TYPE_SVG, widget);
pika_dnd_data_source_remove (PIKA_DND_TYPE_SVG_XML, widget);
}
void
pika_dnd_svg_dest_add (GtkWidget *widget,
PikaDndDropStreamFunc set_svg_func,
gpointer data)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_dest_add (PIKA_DND_TYPE_SVG, widget,
G_CALLBACK (set_svg_func),
data);
pika_dnd_data_dest_add (PIKA_DND_TYPE_SVG_XML, widget,
G_CALLBACK (set_svg_func),
data);
}
void
pika_dnd_svg_dest_remove (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_dest_remove (PIKA_DND_TYPE_SVG, widget);
pika_dnd_data_dest_remove (PIKA_DND_TYPE_SVG_XML, widget);
}
/**************************/
/* pixbuf dnd functions */
/**************************/
static void
pika_dnd_get_pixbuf_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_pixbuf_func,
gpointer get_pixbuf_data,
GtkSelectionData *selection)
{
GdkPixbuf *pixbuf;
pixbuf = (* (PikaDndDragPixbufFunc) get_pixbuf_func) (widget,
get_pixbuf_data);
PIKA_LOG (DND, "pixbuf %p", pixbuf);
if (pixbuf)
{
pika_set_busy (the_dnd_pika);
gtk_selection_data_set_pixbuf (selection, pixbuf);
g_object_unref (pixbuf);
pika_unset_busy (the_dnd_pika);
}
}
static gboolean
pika_dnd_set_pixbuf_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_pixbuf_func,
gpointer set_pixbuf_data,
GtkSelectionData *selection)
{
GdkPixbuf *pixbuf;
pika_set_busy (the_dnd_pika);
pixbuf = gtk_selection_data_get_pixbuf (selection);
pika_unset_busy (the_dnd_pika);
PIKA_LOG (DND, "pixbuf %p", pixbuf);
if (! pixbuf)
return FALSE;
(* (PikaDndDropPixbufFunc) set_pixbuf_func) (widget, x, y,
pixbuf,
set_pixbuf_data);
g_object_unref (pixbuf);
return TRUE;
}
void
pika_dnd_pixbuf_source_add (GtkWidget *widget,
PikaDndDragPixbufFunc get_pixbuf_func,
gpointer data)
{
GtkTargetList *target_list;
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_source_add (PIKA_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);
pika_pixbuf_targets_add (target_list, PIKA_DND_TYPE_PIXBUF, TRUE);
gtk_drag_source_set_target_list (widget, target_list);
gtk_target_list_unref (target_list);
}
void
pika_dnd_pixbuf_source_remove (GtkWidget *widget)
{
GtkTargetList *target_list;
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_source_remove (PIKA_DND_TYPE_PIXBUF, widget);
target_list = gtk_drag_source_get_target_list (widget);
if (target_list)
pika_pixbuf_targets_remove (target_list);
}
void
pika_dnd_pixbuf_dest_add (GtkWidget *widget,
PikaDndDropPixbufFunc set_pixbuf_func,
gpointer data)
{
GtkTargetList *target_list;
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_dest_add (PIKA_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);
pika_pixbuf_targets_add (target_list, PIKA_DND_TYPE_PIXBUF, FALSE);
gtk_drag_dest_set_target_list (widget, target_list);
gtk_target_list_unref (target_list);
}
void
pika_dnd_pixbuf_dest_remove (GtkWidget *widget)
{
GtkTargetList *target_list;
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_dest_remove (PIKA_DND_TYPE_PIXBUF, widget);
target_list = gtk_drag_dest_get_target_list (widget);
if (target_list)
pika_pixbuf_targets_remove (target_list);
}
/*****************************/
/* component dnd functions */
/*****************************/
static GtkWidget *
pika_dnd_get_component_icon (GtkWidget *widget,
GdkDragContext *context,
GCallback get_comp_func,
gpointer get_comp_data)
{
GtkWidget *view;
PikaImage *image;
PikaContext *pika_context;
PikaChannelType channel;
image = (* (PikaDndDragComponentFunc) get_comp_func) (widget, &pika_context,
&channel,
get_comp_data);
PIKA_LOG (DND, "image %p, component %d", image, channel);
if (! image)
return NULL;
g_object_set_data_full (G_OBJECT (context),
"pika-dnd-viewable", g_object_ref (image),
(GDestroyNotify) g_object_unref);
g_object_set_data (G_OBJECT (context),
"pika-dnd-component", GINT_TO_POINTER (channel));
view = pika_view_new (pika_context, PIKA_VIEWABLE (image),
DRAG_PREVIEW_SIZE, 0, TRUE);
PIKA_VIEW_RENDERER_IMAGE (PIKA_VIEW (view)->renderer)->channel = channel;
return view;
}
static void
pika_dnd_get_component_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_comp_func,
gpointer get_comp_data,
GtkSelectionData *selection)
{
PikaImage *image;
PikaContext *pika_context;
PikaChannelType channel = 0;
image = g_object_get_data (G_OBJECT (context), "pika-dnd-viewable");
channel = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
"pika-dnd-component"));
if (! image)
image = (* (PikaDndDragComponentFunc) get_comp_func) (widget, &pika_context,
&channel,
get_comp_data);
PIKA_LOG (DND, "image %p, component %d", image, channel);
if (image)
pika_selection_data_set_component (selection, image, channel);
}
static gboolean
pika_dnd_set_component_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_comp_func,
gpointer set_comp_data,
GtkSelectionData *selection)
{
PikaImage *image;
PikaChannelType channel = 0;
image = pika_selection_data_get_component (selection, the_dnd_pika,
&channel);
PIKA_LOG (DND, "image %p, component %d", image, channel);
if (! image)
return FALSE;
(* (PikaDndDropComponentFunc) set_comp_func) (widget, x, y,
image, channel,
set_comp_data);
return TRUE;
}
void
pika_dnd_component_source_add (GtkWidget *widget,
PikaDndDragComponentFunc get_comp_func,
gpointer data)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_source_add (PIKA_DND_TYPE_COMPONENT, widget,
G_CALLBACK (get_comp_func),
data);
}
void
pika_dnd_component_source_remove (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_source_remove (PIKA_DND_TYPE_COMPONENT, widget);
}
void
pika_dnd_component_dest_add (GtkWidget *widget,
PikaDndDropComponentFunc set_comp_func,
gpointer data)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_dest_add (PIKA_DND_TYPE_COMPONENT, widget,
G_CALLBACK (set_comp_func),
data);
}
void
pika_dnd_component_dest_remove (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
pika_dnd_data_dest_remove (PIKA_DND_TYPE_COMPONENT, widget);
}
/*******************************************/
/* PikaViewable (by GType) dnd functions */
/*******************************************/
static GtkWidget *
pika_dnd_get_viewable_icon (GtkWidget *widget,
GdkDragContext *context,
GCallback get_viewable_func,
gpointer get_viewable_data)
{
PikaViewable *viewable;
PikaContext *pika_context;
GtkWidget *view;
gchar *desc;
viewable = (* (PikaDndDragViewableFunc) get_viewable_func) (widget,
&pika_context,
get_viewable_data);
PIKA_LOG (DND, "viewable %p", viewable);
if (! viewable)
return NULL;
g_object_set_data_full (G_OBJECT (context),
"pika-dnd-viewable", g_object_ref (viewable),
(GDestroyNotify) g_object_unref);
view = pika_view_new (pika_context, viewable,
DRAG_PREVIEW_SIZE, 0, TRUE);
desc = pika_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,
"xalign", 0.0,
"yalign", 0.5,
"max-width-chars", 30,
"width-chars", MIN (strlen (desc), 10),
"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 PikaDndType
pika_dnd_data_type_get_by_g_type (GType type,
gboolean list)
{
PikaDndType dnd_type = PIKA_DND_TYPE_NONE;
if (g_type_is_a (type, PIKA_TYPE_IMAGE) && ! list)
{
dnd_type = PIKA_DND_TYPE_IMAGE;
}
else if (g_type_is_a (type, PIKA_TYPE_LAYER))
{
dnd_type = list ? PIKA_DND_TYPE_LAYER_LIST : PIKA_DND_TYPE_LAYER;
}
else if (g_type_is_a (type, PIKA_TYPE_LAYER_MASK) && ! list)
{
dnd_type = PIKA_DND_TYPE_LAYER_MASK;
}
else if (g_type_is_a (type, PIKA_TYPE_CHANNEL))
{
dnd_type = list ? PIKA_DND_TYPE_CHANNEL_LIST : PIKA_DND_TYPE_CHANNEL;
}
else if (g_type_is_a (type, PIKA_TYPE_VECTORS))
{
dnd_type = list ? PIKA_DND_TYPE_VECTORS_LIST : PIKA_DND_TYPE_VECTORS;
}
else if (g_type_is_a (type, PIKA_TYPE_BRUSH) && ! list)
{
dnd_type = PIKA_DND_TYPE_BRUSH;
}
else if (g_type_is_a (type, PIKA_TYPE_PATTERN) && ! list)
{
dnd_type = PIKA_DND_TYPE_PATTERN;
}
else if (g_type_is_a (type, PIKA_TYPE_GRADIENT) && ! list)
{
dnd_type = PIKA_DND_TYPE_GRADIENT;
}
else if (g_type_is_a (type, PIKA_TYPE_PALETTE) && ! list)
{
dnd_type = PIKA_DND_TYPE_PALETTE;
}
else if (g_type_is_a (type, PIKA_TYPE_FONT) && ! list)
{
dnd_type = PIKA_DND_TYPE_FONT;
}
else if (g_type_is_a (type, PIKA_TYPE_BUFFER) && ! list)
{
dnd_type = PIKA_DND_TYPE_BUFFER;
}
else if (g_type_is_a (type, PIKA_TYPE_IMAGEFILE) && ! list)
{
dnd_type = PIKA_DND_TYPE_IMAGEFILE;
}
else if (g_type_is_a (type, PIKA_TYPE_TEMPLATE) && ! list)
{
dnd_type = PIKA_DND_TYPE_TEMPLATE;
}
else if (g_type_is_a (type, PIKA_TYPE_TOOL_ITEM) && ! list)
{
dnd_type = PIKA_DND_TYPE_TOOL_ITEM;
}
return dnd_type;
}
gboolean
pika_dnd_drag_source_set_by_type (GtkWidget *widget,
GdkModifierType start_button_mask,
GType type,
GdkDragAction actions)
{
PikaDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = pika_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type == PIKA_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
pika_dnd_drag_dest_set_by_type (GtkWidget *widget,
GtkDestDefaults flags,
GType type,
gboolean list_accepted,
GdkDragAction actions)
{
GtkTargetEntry target_entries[2];
PikaDndType dnd_type;
gint target_entries_n = 0;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
if (list_accepted)
{
dnd_type = pika_dnd_data_type_get_by_g_type (type, TRUE);
if (dnd_type != PIKA_DND_TYPE_NONE)
{
target_entries[target_entries_n] = dnd_data_defs[dnd_type].target_entry;
target_entries_n++;
}
}
dnd_type = pika_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type != PIKA_DND_TYPE_NONE)
{
target_entries[target_entries_n] = dnd_data_defs[dnd_type].target_entry;
target_entries_n++;
}
if (target_entries_n == 0)
return FALSE;
gtk_drag_dest_set (widget, flags,
(const GtkTargetEntry *) &target_entries,
target_entries_n,
actions);
return TRUE;
}
/**
* pika_dnd_viewable_source_add:
* @widget:
* @type:
* @get_viewable_func:
* @data:
*
* Sets up @widget as a drag source for a #PikaViewable object, as
* returned by @get_viewable_func on @widget and @data.
*
* @type must be a list type for drag operations.
*/
gboolean
pika_dnd_viewable_source_add (GtkWidget *widget,
GType type,
PikaDndDragViewableFunc get_viewable_func,
gpointer data)
{
PikaDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
g_return_val_if_fail (get_viewable_func != NULL, FALSE);
dnd_type = pika_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type == PIKA_DND_TYPE_NONE)
return FALSE;
pika_dnd_data_source_add (dnd_type, widget,
G_CALLBACK (get_viewable_func),
data);
return TRUE;
}
gboolean
pika_dnd_viewable_source_remove (GtkWidget *widget,
GType type)
{
PikaDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = pika_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type == PIKA_DND_TYPE_NONE)
return FALSE;
return pika_dnd_data_source_remove (dnd_type, widget);
}
gboolean
pika_dnd_viewable_dest_add (GtkWidget *widget,
GType type,
PikaDndDropViewableFunc set_viewable_func,
gpointer data)
{
PikaDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = pika_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type == PIKA_DND_TYPE_NONE)
return FALSE;
pika_dnd_data_dest_add (dnd_type, widget,
G_CALLBACK (set_viewable_func),
data);
return TRUE;
}
gboolean
pika_dnd_viewable_dest_remove (GtkWidget *widget,
GType type)
{
PikaDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = pika_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type == PIKA_DND_TYPE_NONE)
return FALSE;
pika_dnd_data_dest_remove (dnd_type, widget);
return TRUE;
}
PikaViewable *
pika_dnd_get_drag_viewable (GtkWidget *widget)
{
const PikaDndDataDef *dnd_data;
PikaDndType data_type;
PikaDndDragViewableFunc get_data_func = NULL;
gpointer get_data_data = NULL;
PikaContext *context;
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
data_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
"pika-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 (PikaViewable *) (* get_data_func) (widget, &context, get_data_data);
}
/*************************************************/
/* PikaViewable (by GType) GList dnd functions */
/*************************************************/
static GtkWidget *
pika_dnd_get_viewable_list_icon (GtkWidget *widget,
GdkDragContext *context,
GCallback get_list_func,
gpointer get_list_data)
{
GList *viewables;
PikaViewable *viewable;
PikaContext *pika_context;
GtkWidget *view;
gchar *desc;
gboolean desc_use_markup = FALSE;
gfloat desc_yalign = 0.5f;
gint desc_width_chars;
viewables = (* (PikaDndDragViewableListFunc) get_list_func) (widget,
&pika_context,
get_list_data);
if (! viewables)
return NULL;
viewable = viewables->data;
PIKA_LOG (DND, "viewable %p", viewable);
g_object_set_data_full (G_OBJECT (context),
"pika-dnd-viewable", g_object_ref (viewable),
(GDestroyNotify) g_object_unref);
view = pika_view_new (pika_context, viewable,
DRAG_PREVIEW_SIZE, 0, TRUE);
if (g_list_length (viewables) > 1)
{
/* When dragging multiple viewable, just show the first of them in the
* icon box, and the number of viewables being dragged in the
* description label (instead of the viewable name).
*/
desc = g_strdup_printf ("<sup><i>(%d)</i></sup>", g_list_length (viewables));
desc_use_markup = TRUE;
desc_yalign = 0.0f;
desc_width_chars = (gint) log10 (g_list_length (viewables)) + 3;
}
else
{
desc = pika_viewable_get_description (viewable, NULL);
desc_width_chars = MIN (strlen (desc), 10);
}
g_list_free (viewables);
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,
"use-markup", desc_use_markup,
"xalign", 0.0,
"yalign", desc_yalign,
"max-width-chars", 30,
"width-chars", desc_width_chars,
"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;
}
/**
* pika_dnd_viewable_list_source_add:
* @widget:
* @type:
* @get_viewable_func:
* @data:
*
* Sets up @widget as a drag source for a #GList of #PikaViewable
* object, as returned by @get_viewable_func on @widget and @data.
*
* @type must be a list type for drag operations (only PikaLayer so
* far).
*/
gboolean
pika_dnd_viewable_list_source_add (GtkWidget *widget,
GType type,
PikaDndDragViewableListFunc get_viewable_list_func,
gpointer data)
{
PikaDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
g_return_val_if_fail (get_viewable_list_func != NULL, FALSE);
dnd_type = pika_dnd_data_type_get_by_g_type (type, TRUE);
if (dnd_type == PIKA_DND_TYPE_NONE)
return FALSE;
pika_dnd_data_source_add (dnd_type, widget,
G_CALLBACK (get_viewable_list_func),
data);
return TRUE;
}
gboolean
pika_dnd_viewable_list_source_remove (GtkWidget *widget,
GType type)
{
PikaDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = pika_dnd_data_type_get_by_g_type (type, TRUE);
if (dnd_type == PIKA_DND_TYPE_NONE)
return FALSE;
return pika_dnd_data_source_remove (dnd_type, widget);
}
gboolean
pika_dnd_viewable_list_dest_add (GtkWidget *widget,
GType type,
PikaDndDropViewableListFunc set_viewable_func,
gpointer data)
{
PikaDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = pika_dnd_data_type_get_by_g_type (type, TRUE);
if (dnd_type == PIKA_DND_TYPE_NONE)
return FALSE;
pika_dnd_data_dest_add (dnd_type, widget,
G_CALLBACK (set_viewable_func),
data);
return TRUE;
}
gboolean
pika_dnd_viewable_list_dest_remove (GtkWidget *widget,
GType type)
{
PikaDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = pika_dnd_data_type_get_by_g_type (type, TRUE);
if (dnd_type == PIKA_DND_TYPE_NONE)
return FALSE;
pika_dnd_data_dest_remove (dnd_type, widget);
return TRUE;
}
GList *
pika_dnd_get_drag_list (GtkWidget *widget)
{
const PikaDndDataDef *dnd_data;
PikaDndType data_type;
PikaDndDragViewableListFunc get_data_func = NULL;
gpointer get_data_data = NULL;
PikaContext *context;
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
data_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
"pika-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 (GList *) (* get_data_func) (widget, &context, get_data_data);
}
/*****************************/
/* PikaImage dnd functions */
/*****************************/
static void
pika_dnd_get_image_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_image_func,
gpointer get_image_data,
GtkSelectionData *selection)
{
PikaImage *image;
PikaContext *pika_context;
image = g_object_get_data (G_OBJECT (context), "pika-dnd-viewable");
if (! image)
image = (PikaImage *)
(* (PikaDndDragViewableFunc) get_image_func) (widget, &pika_context,
get_image_data);
PIKA_LOG (DND, "image %p", image);
if (image)
pika_selection_data_set_image (selection, image);
}
static gboolean
pika_dnd_set_image_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_image_func,
gpointer set_image_data,
GtkSelectionData *selection)
{
PikaImage *image = pika_selection_data_get_image (selection, the_dnd_pika);
PIKA_LOG (DND, "image %p", image);
if (! image)
return FALSE;
(* (PikaDndDropViewableFunc) set_image_func) (widget, x, y,
PIKA_VIEWABLE (image),
set_image_data);
return TRUE;
}
/****************************/
/* PikaItem dnd functions */
/****************************/
static void
pika_dnd_get_item_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_item_func,
gpointer get_item_data,
GtkSelectionData *selection)
{
PikaItem *item;
PikaContext *pika_context;
item = g_object_get_data (G_OBJECT (context), "pika-dnd-viewable");
if (! item)
item = (PikaItem *)
(* (PikaDndDragViewableFunc) get_item_func) (widget, &pika_context,
get_item_data);
PIKA_LOG (DND, "item %p", item);
if (item)
pika_selection_data_set_item (selection, item);
}
static gboolean
pika_dnd_set_item_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_item_func,
gpointer set_item_data,
GtkSelectionData *selection)
{
PikaItem *item = pika_selection_data_get_item (selection, the_dnd_pika);
PIKA_LOG (DND, "item %p", item);
if (! item)
return FALSE;
(* (PikaDndDropViewableFunc) set_item_func) (widget, x, y,
PIKA_VIEWABLE (item),
set_item_data);
return TRUE;
}
/**********************************/
/* PikaItem GList dnd functions */
/**********************************/
static void
pika_dnd_get_item_list_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_item_func,
gpointer get_item_data,
GtkSelectionData *selection)
{
GList *items;
PikaContext *pika_context;
items = (* (PikaDndDragViewableListFunc) get_item_func) (widget, &pika_context,
get_item_data);
if (items)
pika_selection_data_set_item_list (selection, items);
g_list_free (items);
}
static gboolean
pika_dnd_set_item_list_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_item_func,
gpointer set_item_data,
GtkSelectionData *selection)
{
GList *items = pika_selection_data_get_item_list (selection, the_dnd_pika);
if (! items)
return FALSE;
(* (PikaDndDropViewableListFunc) set_item_func) (widget, x, y, items,
set_item_data);
g_list_free (items);
return TRUE;
}
/******************************/
/* PikaObject dnd functions */
/******************************/
static void
pika_dnd_get_object_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_object_func,
gpointer get_object_data,
GtkSelectionData *selection)
{
PikaObject *object;
PikaContext *pika_context;
object = g_object_get_data (G_OBJECT (context), "pika-dnd-viewable");
if (! object)
object = (PikaObject *)
(* (PikaDndDragViewableFunc) get_object_func) (widget, &pika_context,
get_object_data);
PIKA_LOG (DND, "object %p", object);
if (PIKA_IS_OBJECT (object))
pika_selection_data_set_object (selection, object);
}
/*****************************/
/* PikaBrush dnd functions */
/*****************************/
static gboolean
pika_dnd_set_brush_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_brush_func,
gpointer set_brush_data,
GtkSelectionData *selection)
{
PikaBrush *brush = pika_selection_data_get_brush (selection, the_dnd_pika);
PIKA_LOG (DND, "brush %p", brush);
if (! brush)
return FALSE;
(* (PikaDndDropViewableFunc) set_brush_func) (widget, x, y,
PIKA_VIEWABLE (brush),
set_brush_data);
return TRUE;
}
/*******************************/
/* PikaPattern dnd functions */
/*******************************/
static gboolean
pika_dnd_set_pattern_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_pattern_func,
gpointer set_pattern_data,
GtkSelectionData *selection)
{
PikaPattern *pattern = pika_selection_data_get_pattern (selection,
the_dnd_pika);
PIKA_LOG (DND, "pattern %p", pattern);
if (! pattern)
return FALSE;
(* (PikaDndDropViewableFunc) set_pattern_func) (widget, x, y,
PIKA_VIEWABLE (pattern),
set_pattern_data);
return TRUE;
}
/********************************/
/* PikaGradient dnd functions */
/********************************/
static gboolean
pika_dnd_set_gradient_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_gradient_func,
gpointer set_gradient_data,
GtkSelectionData *selection)
{
PikaGradient *gradient = pika_selection_data_get_gradient (selection,
the_dnd_pika);
PIKA_LOG (DND, "gradient %p", gradient);
if (! gradient)
return FALSE;
(* (PikaDndDropViewableFunc) set_gradient_func) (widget, x, y,
PIKA_VIEWABLE (gradient),
set_gradient_data);
return TRUE;
}
/*******************************/
/* PikaPalette dnd functions */
/*******************************/
static gboolean
pika_dnd_set_palette_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_palette_func,
gpointer set_palette_data,
GtkSelectionData *selection)
{
PikaPalette *palette = pika_selection_data_get_palette (selection,
the_dnd_pika);
PIKA_LOG (DND, "palette %p", palette);
if (! palette)
return FALSE;
(* (PikaDndDropViewableFunc) set_palette_func) (widget, x, y,
PIKA_VIEWABLE (palette),
set_palette_data);
return TRUE;
}
/****************************/
/* PikaFont dnd functions */
/****************************/
static gboolean
pika_dnd_set_font_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_font_func,
gpointer set_font_data,
GtkSelectionData *selection)
{
PikaFont *font = pika_selection_data_get_font (selection, the_dnd_pika);
PIKA_LOG (DND, "font %p", font);
if (! font)
return FALSE;
(* (PikaDndDropViewableFunc) set_font_func) (widget, x, y,
PIKA_VIEWABLE (font),
set_font_data);
return TRUE;
}
/******************************/
/* PikaBuffer dnd functions */
/******************************/
static gboolean
pika_dnd_set_buffer_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_buffer_func,
gpointer set_buffer_data,
GtkSelectionData *selection)
{
PikaBuffer *buffer = pika_selection_data_get_buffer (selection, the_dnd_pika);
PIKA_LOG (DND, "buffer %p", buffer);
if (! buffer)
return FALSE;
(* (PikaDndDropViewableFunc) set_buffer_func) (widget, x, y,
PIKA_VIEWABLE (buffer),
set_buffer_data);
return TRUE;
}
/*********************************/
/* PikaImagefile dnd functions */
/*********************************/
static gboolean
pika_dnd_set_imagefile_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_imagefile_func,
gpointer set_imagefile_data,
GtkSelectionData *selection)
{
PikaImagefile *imagefile = pika_selection_data_get_imagefile (selection,
the_dnd_pika);
PIKA_LOG (DND, "imagefile %p", imagefile);
if (! imagefile)
return FALSE;
(* (PikaDndDropViewableFunc) set_imagefile_func) (widget, x, y,
PIKA_VIEWABLE (imagefile),
set_imagefile_data);
return TRUE;
}
/********************************/
/* PikaTemplate dnd functions */
/********************************/
static gboolean
pika_dnd_set_template_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_template_func,
gpointer set_template_data,
GtkSelectionData *selection)
{
PikaTemplate *template = pika_selection_data_get_template (selection,
the_dnd_pika);
PIKA_LOG (DND, "template %p", template);
if (! template)
return FALSE;
(* (PikaDndDropViewableFunc) set_template_func) (widget, x, y,
PIKA_VIEWABLE (template),
set_template_data);
return TRUE;
}
/*********************************/
/* PikaToolEntry dnd functions */
/*********************************/
static gboolean
pika_dnd_set_tool_item_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_tool_item_func,
gpointer set_tool_item_data,
GtkSelectionData *selection)
{
PikaToolItem *tool_item = pika_selection_data_get_tool_item (selection,
the_dnd_pika);
PIKA_LOG (DND, "tool_item %p", tool_item);
if (! tool_item)
return FALSE;
(* (PikaDndDropViewableFunc) set_tool_item_func) (widget, x, y,
PIKA_VIEWABLE (tool_item),
set_tool_item_data);
return TRUE;
}