/* 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 . */ #include "config.h" #include #include #include #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 ("(%d)", 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; }