Commit 76096459 authored by Michael Natterer's avatar Michael Natterer 😴 Committed by Michael Natterer
Browse files

Implement dragging and dropping in any GdkPixbuf supported format. Fixes

2005-04-09  Michael Natterer  <mitch@gimp.org>

	Implement dragging and dropping in any GdkPixbuf supported
	format. Fixes bug #172794 and bug #172795.

	* app/core/gimplayer.[ch] (gimp_layer_new_from_region): new
	function which contains all stuff that was in
	gimp_layer_new_from_tiles().

	(gimp_layer_new_from_tiles): use above function.
	(gimp_layer_new_from_pixbuf): new function.

	* app/widgets/Makefile.am
	* app/widgets/gimppixbuf.[ch]: new files containing GdkPixbuf
	utility functions for clipboard and DnD.

	* app/widgets/gimpselectiondata.[ch]: removed
	gimp_selection_data_set,get_pixbuf(), GTK+ provides the same API.
	Also removed GdkAtom parameters all over the place because it's
	always the same as selection_data->target.

	* app/widgets/gimpclipboard.c: use the new pixbuf utility
	functions and gtk_selection_data_set,get_pixbuf().

	* app/widgets/widgets-enums.h
	* app/widgets/gimpdnd.[ch]: removed never-implemented
	GIMP_DND_TYPE_PNG and added a generic GIMP_DND_TYPE_PIXBUF
	instead. Added API to drag and drop GdkPixbufs which transparently
	converts from/to and GdkPixbuf-supported image format. Removed
	passing around of GdkAtoms, since they were always the same
	as selection_data->target.

	* app/widgets/gimpdnd-xds.[ch]: follow GdkAtom parameter removal.

	* app/widgets/gimpcontainertreeview.[ch]: added virtual function
	GimpContainerTreeView::drop_pixbuf().

	* app/widgets/gimpcontainertreeview-dnd.c: dispatch drop_pixbuf().

	* app/widgets/gimplayertreeview.c: implement drop_pixbuf().

	* app/widgets/gimpdrawabletreeview.c: allow to drag all drawables
	as pixbufs.

	* app/display/gimpdisplayshell-dnd.c: allow dropping of pixbufs.
parent 97992621
2005-04-09 Michael Natterer <mitch@gimp.org>
Implement dragging and dropping in any GdkPixbuf supported
format. Fixes bug #172794 and bug #172795.
* app/core/gimplayer.[ch] (gimp_layer_new_from_region): new
function which contains all stuff that was in
gimp_layer_new_from_tiles().
(gimp_layer_new_from_tiles): use above function.
(gimp_layer_new_from_pixbuf): new function.
* app/widgets/Makefile.am
* app/widgets/gimppixbuf.[ch]: new files containing GdkPixbuf
utility functions for clipboard and DnD.
* app/widgets/gimpselectiondata.[ch]: removed
gimp_selection_data_set,get_pixbuf(), GTK+ provides the same API.
Also removed GdkAtom parameters all over the place because it's
always the same as selection_data->target.
* app/widgets/gimpclipboard.c: use the new pixbuf utility
functions and gtk_selection_data_set,get_pixbuf().
* app/widgets/widgets-enums.h
* app/widgets/gimpdnd.[ch]: removed never-implemented
GIMP_DND_TYPE_PNG and added a generic GIMP_DND_TYPE_PIXBUF
instead. Added API to drag and drop GdkPixbufs which transparently
converts from/to and GdkPixbuf-supported image format. Removed
passing around of GdkAtoms, since they were always the same
as selection_data->target.
* app/widgets/gimpdnd-xds.[ch]: follow GdkAtom parameter removal.
* app/widgets/gimpcontainertreeview.[ch]: added virtual function
GimpContainerTreeView::drop_pixbuf().
* app/widgets/gimpcontainertreeview-dnd.c: dispatch drop_pixbuf().
* app/widgets/gimplayertreeview.c: implement drop_pixbuf().
* app/widgets/gimpdrawabletreeview.c: allow to drag all drawables
as pixbufs.
* app/display/gimpdisplayshell-dnd.c: allow dropping of pixbufs.
2005-04-10 Bill Skaggs <weskaggs@primate.ucdavis.edu>
* plug-ins/common/screenshot.c: Change default back to Window
......
......@@ -882,22 +882,101 @@ gimp_layer_new_from_tiles (TileManager *tiles,
const gchar *name,
gdouble opacity,
GimpLayerModeEffects mode)
{
PixelRegion bufPR;
g_return_val_if_fail (tiles != NULL, NULL);
g_return_val_if_fail (GIMP_IS_IMAGE (dest_gimage), NULL);
g_return_val_if_fail (name != NULL, NULL);
pixel_region_init (&bufPR, tiles,
0, 0,
tile_manager_width (tiles),
tile_manager_height (tiles),
FALSE);
return gimp_layer_new_from_region (&bufPR, dest_gimage, type,
name, opacity, mode);
}
/**
* gimp_layer_new_from_pixbuf:
* @tiles: The pixbuf to make the new layer from.
* @dest_gimage: The image the new layer will be added to.
* @type: The #GimpImageType of the new layer.
* @name: The new layer's name.
* @opacity: The new layer's opacity.
* @mode: The new layer's mode.
*
* Copies %pixbuf to a layer taking into consideration the
* possibility of transforming the contents to meet the requirements
* of the target image type
*
* Return value: The new layer.
**/
GimpLayer *
gimp_layer_new_from_pixbuf (GdkPixbuf *pixbuf,
GimpImage *dest_gimage,
GimpImageType type,
const gchar *name,
gdouble opacity,
GimpLayerModeEffects mode)
{
PixelRegion bufPR = { 0, };
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
g_return_val_if_fail (GIMP_IS_IMAGE (dest_gimage), NULL);
g_return_val_if_fail (name != NULL, NULL);
bufPR.data = gdk_pixbuf_get_pixels (pixbuf);
bufPR.rowstride = gdk_pixbuf_get_rowstride (pixbuf);
bufPR.x = 0;
bufPR.y = 0;
bufPR.w = gdk_pixbuf_get_width (pixbuf);
bufPR.h = gdk_pixbuf_get_height (pixbuf);
bufPR.bytes = gdk_pixbuf_get_n_channels (pixbuf);
return gimp_layer_new_from_region (&bufPR, dest_gimage, type,
name, opacity, mode);
}
/**
* gimp_layer_new_from_region:
* @region: A readable pixel region.
* @dest_gimage: The image the new layer will be added to.
* @type: The #GimpImageType of the new layer.
* @name: The new layer's name.
* @opacity: The new layer's opacity.
* @mode: The new layer's mode.
*
* Copies %region to a layer taking into consideration the
* possibility of transforming the contents to meet the requirements
* of the target image type
*
* Return value: The new layer.
**/
GimpLayer *
gimp_layer_new_from_region (PixelRegion *region,
GimpImage *dest_gimage,
GimpImageType type,
const gchar *name,
gdouble opacity,
GimpLayerModeEffects mode)
{
GimpLayer *new_layer;
PixelRegion layerPR;
PixelRegion bufPR;
GimpImageType src_type;
gint width;
gint height;
g_return_val_if_fail (tiles != NULL, NULL);
g_return_val_if_fail (region != NULL, NULL);
g_return_val_if_fail (GIMP_IS_IMAGE (dest_gimage), NULL);
g_return_val_if_fail (name != NULL, NULL);
width = tile_manager_width (tiles);
height = tile_manager_height (tiles);
width = region->w;
height = region->h;
switch (tile_manager_bpp (tiles))
switch (region->bytes)
{
case 1: src_type = GIMP_GRAY_IMAGE; break;
case 2: src_type = GIMP_GRAYA_IMAGE; break;
......@@ -913,14 +992,10 @@ gimp_layer_new_from_tiles (TileManager *tiles,
if (! new_layer)
{
g_message ("gimp_layer_new_from_tiles: could not allocate new layer");
g_message ("gimp_layer_new_from_region: could not allocate new layer");
return NULL;
}
/* Configure the pixel regions */
pixel_region_init (&bufPR, tiles,
0, 0, width, height,
FALSE);
pixel_region_init (&layerPR, GIMP_DRAWABLE (new_layer)->tiles,
0, 0, width, height,
TRUE);
......@@ -931,7 +1006,7 @@ gimp_layer_new_from_tiles (TileManager *tiles,
switch (src_type)
{
case GIMP_RGB_IMAGE:
copy_region (&bufPR, &layerPR);
copy_region (region, &layerPR);
break;
default:
g_warning ("%s: unhandled type conversion", G_STRFUNC);
......@@ -943,13 +1018,13 @@ gimp_layer_new_from_tiles (TileManager *tiles,
switch (src_type)
{
case GIMP_RGBA_IMAGE:
copy_region (&bufPR, &layerPR);
copy_region (region, &layerPR);
break;
case GIMP_RGB_IMAGE:
add_alpha_region (&bufPR, &layerPR);
add_alpha_region (region, &layerPR);
break;
case GIMP_GRAYA_IMAGE:
gimp_layer_transform_color (dest_gimage, &layerPR, &bufPR,
gimp_layer_transform_color (dest_gimage, &layerPR, region,
GIMP_DRAWABLE (new_layer), GIMP_GRAY);
break;
default:
......@@ -962,7 +1037,7 @@ gimp_layer_new_from_tiles (TileManager *tiles,
switch (src_type)
{
case GIMP_GRAY_IMAGE:
copy_region (&bufPR, &layerPR);
copy_region (region, &layerPR);
break;
default:
g_warning ("%s: unhandled type conversion", G_STRFUNC);
......@@ -974,14 +1049,14 @@ gimp_layer_new_from_tiles (TileManager *tiles,
switch (src_type)
{
case GIMP_RGBA_IMAGE:
gimp_layer_transform_color (dest_gimage, &layerPR, &bufPR,
gimp_layer_transform_color (dest_gimage, &layerPR, region,
GIMP_DRAWABLE (new_layer), GIMP_RGB);
break;
case GIMP_GRAYA_IMAGE:
copy_region (&bufPR, &layerPR);
copy_region (region, &layerPR);
break;
case GIMP_GRAY_IMAGE:
add_alpha_region (&bufPR, &layerPR);
add_alpha_region (region, &layerPR);
break;
default:
g_warning ("%s: unhandled type conversion", G_STRFUNC);
......@@ -997,11 +1072,11 @@ gimp_layer_new_from_tiles (TileManager *tiles,
switch (src_type)
{
case GIMP_RGBA_IMAGE:
gimp_layer_transform_color (dest_gimage, &layerPR, &bufPR,
gimp_layer_transform_color (dest_gimage, &layerPR, region,
GIMP_DRAWABLE (new_layer), GIMP_RGB);
break;
case GIMP_GRAYA_IMAGE:
gimp_layer_transform_color (dest_gimage, &layerPR, &bufPR,
gimp_layer_transform_color (dest_gimage, &layerPR, region,
GIMP_DRAWABLE (new_layer), GIMP_GRAY);
break;
default:
......
......@@ -85,6 +85,18 @@ GimpLayer * gimp_layer_new_from_tiles (TileManager *tiles,
const gchar *name,
gdouble opacity,
GimpLayerModeEffects mode);
GimpLayer * gimp_layer_new_from_pixbuf (GdkPixbuf *pixbuf,
GimpImage *dest_gimage,
GimpImageType type,
const gchar *name,
gdouble opacity,
GimpLayerModeEffects mode);
GimpLayer * gimp_layer_new_from_region (PixelRegion *region,
GimpImage *dest_gimage,
GimpImageType type,
const gchar *name,
gdouble opacity,
GimpLayerModeEffects mode);
GimpLayerMask * gimp_layer_create_mask (const GimpLayer *layer,
GimpAddMaskType mask_type);
......
......@@ -112,6 +112,11 @@ static void gimp_display_shell_drop_component (GtkWidget *widget,
GimpImage *image,
GimpChannelType component,
gpointer data);
static void gimp_display_shell_drop_pixbuf (GtkWidget *widget,
gint x,
gint y,
GdkPixbuf *pixbuf,
gpointer data);
/* public functions */
......@@ -151,6 +156,9 @@ gimp_display_shell_dnd_init (GimpDisplayShell *shell)
gimp_dnd_component_dest_add (GTK_WIDGET (shell),
gimp_display_shell_drop_component,
shell);
gimp_dnd_pixbuf_dest_add (GTK_WIDGET (shell),
gimp_display_shell_drop_pixbuf,
shell);
}
......@@ -511,3 +519,56 @@ gimp_display_shell_drop_component (GtkWidget *widget,
shell->gdisp);
}
}
static void
gimp_display_shell_drop_pixbuf (GtkWidget *widget,
gint x,
gint y,
GdkPixbuf *pixbuf,
gpointer data)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data);
GimpImage *gimage = shell->gdisp->gimage;
GimpLayer *new_layer;
D (g_print ("drop pixbuf on canvas\n"));
if (gimage->gimp->busy)
return;
new_layer =
gimp_layer_new_from_pixbuf (pixbuf, gimage,
gimp_image_base_type_with_alpha (gimage),
_("Dropped Buffer"),
GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE);
if (new_layer)
{
GimpItem *new_item;
gint x, y, width, height;
gint off_x, off_y;
new_item = GIMP_ITEM (new_layer);
gimp_image_undo_group_start (gimage, GIMP_UNDO_GROUP_EDIT_PASTE,
_("Drop New Layer"));
gimp_display_shell_untransform_viewport (shell, &x, &y, &width, &height);
gimp_item_offsets (new_item, &off_x, &off_y);
off_x = x + (width - gimp_item_width (new_item)) / 2 - off_x;
off_y = y + (height - gimp_item_height (new_item)) / 2 - off_y;
gimp_item_translate (new_item, off_x, off_y, FALSE);
gimp_image_add_layer (gimage, new_layer, -1);
gimp_image_undo_group_end (gimage);
gimp_image_flush (gimage);
gimp_context_set_display (gimp_get_user_context (gimage->gimp),
shell->gdisp);
}
}
......@@ -189,6 +189,8 @@ libappwidgets_a_sources = \
gimppatternselect.h \
gimppdbdialog.c \
gimppdbdialog.h \
gimppixbuf.c \
gimppixbuf.h \
gimppluginaction.c \
gimppluginaction.h \
gimpprogressbox.c \
......
......@@ -32,7 +32,7 @@
#include "core/gimpviewable.h"
#include "gimpclipboard.h"
#include "gimpdnd.h"
#include "gimppixbuf.h"
#include "gimpselectiondata.h"
#include "gimp-intl.h"
......@@ -49,7 +49,6 @@ struct _GimpClipboard
GtkTargetEntry *target_entries;
gint n_target_entries;
gchar **savers;
};
......@@ -68,9 +67,6 @@ static void gimp_clipboard_send_buffer (GtkClipboard *clipboard,
static GdkAtom * gimp_clipboard_wait_for_targets (gint *n_targets);
static GdkAtom gimp_clipboard_wait_for_buffer (Gimp *gimp);
static gint gimp_clipboard_format_compare (GdkPixbufFormat *a,
GdkPixbufFormat *b);
void
gimp_clipboard_init (Gimp *gimp)
......@@ -95,9 +91,7 @@ gimp_clipboard_init (Gimp *gimp)
G_CALLBACK (gimp_clipboard_buffer_changed),
NULL, 0);
gimp_clip->pixbuf_formats =
g_slist_sort (gdk_pixbuf_get_formats (),
(GCompareFunc) gimp_clipboard_format_compare);
gimp_clip->pixbuf_formats = gimp_pixbuf_get_formats ();
for (list = gimp_clip->pixbuf_formats; list; list = g_slist_next (list))
{
......@@ -123,8 +117,6 @@ gimp_clipboard_init (Gimp *gimp)
gimp_clip->target_entries = g_new0 (GtkTargetEntry,
gimp_clip->n_target_entries);
gimp_clip->savers = g_new0 (gchar *,
gimp_clip->n_target_entries + 1);
for (list = gimp_clip->pixbuf_formats; list; list = g_slist_next (list))
{
......@@ -151,8 +143,6 @@ gimp_clipboard_init (Gimp *gimp)
gimp_clip->target_entries[i].flags = 0;
gimp_clip->target_entries[i].info = i;
gimp_clip->savers[i] = g_strdup (format_name);
i++;
}
......@@ -278,7 +268,7 @@ gimp_clipboard_get_buffer (Gimp *gimp)
if (data)
{
GdkPixbuf *pixbuf = gimp_selection_data_get_pixbuf (data);
GdkPixbuf *pixbuf = gtk_selection_data_get_pixbuf (data);
gtk_selection_data_free (data);
......@@ -315,7 +305,6 @@ gimp_clipboard_free (GimpClipboard *gimp_clip)
{
g_slist_free (gimp_clip->pixbuf_formats);
g_free (gimp_clip->target_entries);
g_strfreev (gimp_clip->savers);
g_free (gimp_clip);
}
......@@ -472,15 +461,10 @@ gimp_clipboard_send_buffer (GtkClipboard *clipboard,
if (pixbuf)
{
GdkAtom atom = gdk_atom_intern (gimp_clip->target_entries[info].target,
FALSE);
g_printerr ("sending pixbuf data as '%s'\n",
gimp_clip->target_entries[info].target);
g_printerr ("sending pixbuf data as '%s' (%s)\n",
gimp_clip->target_entries[info].target,
gimp_clip->savers[info]);
gimp_selection_data_set_pixbuf (selection_data, atom, pixbuf,
gimp_clip->savers[info]);
gtk_selection_data_set_pixbuf (selection_data, pixbuf);
}
else
{
......@@ -489,44 +473,3 @@ gimp_clipboard_send_buffer (GtkClipboard *clipboard,
gimp_unset_busy (gimp);
}
static gint
gimp_clipboard_format_compare (GdkPixbufFormat *a,
GdkPixbufFormat *b)
{
gchar *a_name = gdk_pixbuf_format_get_name (a);
gchar *b_name = gdk_pixbuf_format_get_name (b);
gint retval = 0;
#ifdef GDK_WINDOWING_WIN32
/* move BMP to the front of the list */
if (strcmp (a_name, "bmp") == 0)
retval = -1;
else if (strcmp (b_name, "bmp") == 0)
retval = 1;
else
#endif
/* move PNG to the front of the list */
if (strcmp (a_name, "png") == 0)
retval = -1;
else if (strcmp (b_name, "png") == 0)
retval = 1;
/* move JPEG to the end of the list */
else if (strcmp (a_name, "jpeg") == 0)
retval = 1;
else if (strcmp (b_name, "jpeg") == 0)
retval = -1;
/* move GIF to the end of the list */
else if (strcmp (a_name, "gif") == 0)
retval = 1;
else if (strcmp (b_name, "gif") == 0)
retval = -1;
g_free (a_name);
g_free (b_name);
return retval;
}
......@@ -76,6 +76,7 @@ gimp_container_tree_view_drop_status (GimpContainerTreeView *tree_view,
case GIMP_DND_TYPE_SVG:
case GIMP_DND_TYPE_SVG_XML:
case GIMP_DND_TYPE_COMPONENT:
case GIMP_DND_TYPE_PIXBUF:
break;
default:
......@@ -429,6 +430,25 @@ gimp_container_tree_view_drag_data_received (GtkWidget *widget,
}
break;
case GIMP_DND_TYPE_PIXBUF:
if (tree_view_class->drop_pixbuf)
{
GdkPixbuf *pixbuf;
pixbuf = gtk_selection_data_get_pixbuf (selection_data);
if (pixbuf)
{
tree_view_class->drop_pixbuf (tree_view,
pixbuf,
dest_viewable, drop_pos);
g_object_unref (pixbuf);
success = TRUE;
}
}
break;
default:
break;
}
......
......@@ -164,6 +164,7 @@ gimp_container_tree_view_class_init (GimpContainerTreeViewClass *klass)
klass->drop_uri_list = NULL;
klass->drop_svg = NULL;
klass->drop_component = NULL;
klass->drop_pixbuf = NULL;
}
static void
......
......@@ -103,6 +103,10 @@ struct _GimpContainerTreeViewClass
GimpChannelType component,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
void (* drop_pixbuf) (GimpContainerTreeView *tree_view,
GdkPixbuf *pixbuf,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
};
......
......@@ -102,8 +102,7 @@ gimp_dnd_xds_source_set (GdkDragContext *context,
void
gimp_dnd_xds_save_image (GdkDragContext *context,
GimpImage *image,
GtkSelectionData *selection,
GdkAtom atom)
GtkSelectionData *selection)
{
PlugInProcDef *proc;
GdkAtom property = gdk_atom_intern ("XdndDirectSave0", FALSE);
......@@ -143,11 +142,11 @@ gimp_dnd_xds_save_image (GdkDragContext *context,
uri, proc, GIMP_RUN_INTERACTIVE, FALSE,
&error) == GIMP_PDB_SUCCESS)
{
gtk_selection_data_set (selection, atom, 8, "S", 1);
gtk_selection_data_set (selection, selection->target, 8, "S", 1);
}
else
{
gtk_selection_data_set (selection, atom, 8, "E", 1);
gtk_selection_data_set (selection, selection->target, 8, "E", 1);
if (error)
{
......@@ -166,7 +165,7 @@ gimp_dnd_xds_save_image (GdkDragContext *context,
}
else
{
gtk_selection_data_set (selection, atom, 8, "E", 1);
gtk_selection_data_set (selection, selection->target, 8, "E", 1);
g_message (_("The given filename does not have any known "
"file extension."));
......
......@@ -30,8 +30,7 @@ void gimp_dnd_xds_source_set (GdkDragContext *context,
GimpImage *image);
void gimp_dnd_xds_save_image (GdkDragContext *context,
GimpImage *image,
GtkSelectionData *selection,
GdkAtom atom);
GtkSelectionData *selection);
#endif /* __GIMP_DND_XDS_H__ */
This diff is collapsed.
......@@ -35,15 +35,18 @@
#define GIMP_TARGET_COLOR \
{ "application/x-color", 0, GIMP_DND_TYPE_COLOR }
#define GIMP_TARGET_PNG \
{ "image/png", 0, GIMP_DND_TYPE_PNG }
#define GIMP_TARGET_SVG \
{ "image/svg", 0, GIMP_DND_TYPE_SVG }
#define GIMP_TARGET_SVG_XML \
{ "image/svg+xml", 0, GIMP_DND_TYPE_SVG_XML }
/* just here for documentation purposes, the actual list of targets
* is created dynamically from available GdkPixbuf loaders
*/
#define GIMP_TARGET_PIXBUF \
{ NULL, 0, GIMP_DND_TYPE_PIXBUF }
#define GIMP_TARGET_IMAGE \
{ "application/x-gimp-image-id", GTK_TARGET_SAME_APP, GIMP_DND_TYPE_IMAGE }
......@@ -164,6 +167,27 @@ void gimp_dnd_svg_dest_add (GtkWidget *widget,
void gimp_dnd_svg_dest_remove (GtkWidget *widget);
/* pixbuf dnd functions */
typedef GdkPixbuf * (* GimpDndDragPixbufFunc) (GtkWidget *widget,
gpointer data);
typedef void (* GimpDndDropPixbufFunc) (GtkWidget *widget,
gint x,
gint y,