app: make drag'n drop multi-drawable selection aware.

This whole drag'n drop code is quite overwhelming, I'm pretty sure this
commit introduced various bugs, and there are already several areas of
improvements I noticed. But at some point, I need to split at a not-too
broken code state or I'll just make things worse.
This commit is contained in:
Jehan 2020-03-24 22:06:28 +01:00
parent 48410d9ea4
commit 83b3d9e52e
17 changed files with 1012 additions and 309 deletions

View file

@ -245,7 +245,7 @@ quit_close_all_dialog_new (Gimp *gimp,
dnd_widget = gimp_container_view_get_dnd_widget (GIMP_CONTAINER_VIEW (view));
gimp_dnd_xds_source_add (dnd_widget,
(GimpDndDragViewableFunc) gimp_dnd_get_drag_data,
(GimpDndDragViewableFunc) gimp_dnd_get_drag_viewable,
NULL);
g_signal_connect (tree_view->view, "query-tooltip",

View file

@ -61,8 +61,8 @@ static void gimp_channel_tree_view_view_iface_init (GimpContainerViewInterfac
static void gimp_channel_tree_view_constructed (GObject *object);
static void gimp_channel_tree_view_drop_viewable (GimpContainerTreeView *view,
GimpViewable *src_viewable,
static void gimp_channel_tree_view_drop_viewables (GimpContainerTreeView *view,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
static void gimp_channel_tree_view_drop_component (GimpContainerTreeView *tree_view,
@ -99,7 +99,7 @@ gimp_channel_tree_view_class_init (GimpChannelTreeViewClass *klass)
object_class->constructed = gimp_channel_tree_view_constructed;
view_class->drop_viewable = gimp_channel_tree_view_drop_viewable;
view_class->drop_viewables = gimp_channel_tree_view_drop_viewables;
view_class->drop_component = gimp_channel_tree_view_drop_component;
iv_class->set_image = gimp_channel_tree_view_set_image;
@ -189,46 +189,52 @@ gimp_channel_tree_view_constructed (GObject *object)
/* GimpContainerTreeView methods */
static void
gimp_channel_tree_view_drop_viewable (GimpContainerTreeView *tree_view,
GimpViewable *src_viewable,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
gimp_channel_tree_view_drop_viewables (GimpContainerTreeView *tree_view,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
{
GimpItemTreeView *item_view = GIMP_ITEM_TREE_VIEW (tree_view);
GimpImage *image = gimp_item_tree_view_get_image (item_view);
GimpItemTreeViewClass *item_view_class;
GList *iter;
item_view_class = GIMP_ITEM_TREE_VIEW_GET_CLASS (item_view);
if (GIMP_IS_DRAWABLE (src_viewable) &&
(image != gimp_item_get_image (GIMP_ITEM (src_viewable)) ||
G_TYPE_FROM_INSTANCE (src_viewable) != item_view_class->item_type))
for (iter = src_viewables; iter; iter = iter->next)
{
GimpItem *new_item;
GimpItem *parent;
gint index;
GimpViewable *src_viewable = iter->data;
index = gimp_item_tree_view_get_drop_index (item_view, dest_viewable,
drop_pos,
(GimpViewable **) &parent);
if (GIMP_IS_DRAWABLE (src_viewable) &&
(image != gimp_item_get_image (GIMP_ITEM (src_viewable)) ||
G_TYPE_FROM_INSTANCE (src_viewable) != item_view_class->item_type))
{
GimpItem *new_item;
GimpItem *parent;
gint index;
new_item = gimp_item_convert (GIMP_ITEM (src_viewable),
gimp_item_tree_view_get_image (item_view),
item_view_class->item_type);
index = gimp_item_tree_view_get_drop_index (item_view, dest_viewable,
drop_pos,
(GimpViewable **) &parent);
gimp_item_set_linked (new_item, FALSE, FALSE);
new_item = gimp_item_convert (GIMP_ITEM (src_viewable),
gimp_item_tree_view_get_image (item_view),
item_view_class->item_type);
item_view_class->add_item (image, new_item, parent, index, TRUE);
gimp_item_set_linked (new_item, FALSE, FALSE);
gimp_image_flush (image);
item_view_class->add_item (image, new_item, parent, index, TRUE);
return;
gimp_image_flush (image);
return;
}
}
GIMP_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_viewable (tree_view,
src_viewable,
dest_viewable,
drop_pos);
GIMP_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_viewables (tree_view,
src_viewables,
dest_viewable,
drop_pos);
}
static void

View file

@ -38,6 +38,97 @@
#include "gimpselectiondata.h"
static gint
gimp_container_tree_view_viewable_sort (GimpViewable *v1,
GimpViewable *v2,
GimpContainerTreeView *tree_view)
{
GimpContainerView *view = GIMP_CONTAINER_VIEW (tree_view);
GimpViewable *parent1;
GimpViewable *parent2;
GimpContainer *container1 = NULL;
GimpContainer *container2 = NULL;
GimpContainer *container = gimp_container_view_get_container (view);
gint index1 = -1;
gint index2 = -1;
gint depth1;
gint depth2;
parent1 = gimp_viewable_get_parent (v1);
parent2 = gimp_viewable_get_parent (v2);
if (parent1)
container1 = gimp_viewable_get_children (parent1);
else if (gimp_container_have (container, GIMP_OBJECT (v1)))
container1 = container;
if (parent2)
container2 = gimp_viewable_get_children (parent2);
else if (gimp_container_have (container, GIMP_OBJECT (v2)))
container2 = container;
g_return_val_if_fail (container1 && container2, 0);
if (container1 == container2)
{
index1 = gimp_container_get_child_index (container1, GIMP_OBJECT (v1));
index2 = gimp_container_get_child_index (container2, GIMP_OBJECT (v2));
return index1 < index2 ? -1 : (index1 > index2 ? 1 : 0);
}
depth1 = gimp_viewable_get_depth (v1);
depth2 = gimp_viewable_get_depth (v2);
if (depth1 == depth2)
{
return gimp_container_tree_view_viewable_sort (parent1, parent2, tree_view);
}
else if (depth1 > depth2)
{
depth1 = gimp_viewable_get_depth (parent1);
while (depth1 > depth2)
{
parent1 = gimp_viewable_get_parent (parent1);
depth1 = gimp_viewable_get_depth (parent1);
}
return gimp_container_tree_view_viewable_sort (parent1, v2, tree_view);
}
else /* if (depth1 < depth2) */
{
depth2 = gimp_viewable_get_depth (parent2);
while (depth1 < depth2)
{
parent2 = gimp_viewable_get_parent (parent2);
depth2 = gimp_viewable_get_depth (parent2);
}
return gimp_container_tree_view_viewable_sort (v1, parent2, tree_view);
}
}
/**
* gimp_container_tree_view_drop_status:
* @tree_view:
* @context:
* @x:
* @y:
* @time:
* @return_path: the #GtkTreePath of the drop position if the drop is
* possible.
* @return_atom:
* @return_src_type: the type of drag'n drop.
* @return_src: allocated #GList of #GimpViewable being dragged.
* @return_dest: the #GimpViewable you are dropping on.
* @return_pos: the drop position (before, after or into @return_dest).
*
* Check whether the current drag can be dropped into @tree_view at
* position (@x, @y). If so, the various return value information will
* be optionally filled.
* Note: if @return_src is not %NULL, hence is filled, it must be freed
* with g_list_free().
*
* Returns: %TRUE is the drop is possible, %FALSE otherwise.
*/
static gboolean
gimp_container_tree_view_drop_status (GimpContainerTreeView *tree_view,
GdkDragContext *context,
@ -47,11 +138,11 @@ gimp_container_tree_view_drop_status (GimpContainerTreeView *tree_view,
GtkTreePath **return_path,
GdkAtom *return_atom,
GimpDndType *return_src_type,
GimpViewable **return_src,
GList **return_src,
GimpViewable **return_dest,
GtkTreeViewDropPosition *return_pos)
{
GimpViewable *src_viewable = NULL;
GList *src_viewables = NULL;
GimpViewable *dest_viewable = NULL;
GtkTreePath *drop_path = NULL;
GtkTargetList *target_list;
@ -82,19 +173,67 @@ gimp_container_tree_view_drop_status (GimpContainerTreeView *tree_view,
case GIMP_DND_TYPE_PIXBUF:
break;
default:
case GIMP_DND_TYPE_XDS:
case GIMP_DND_TYPE_IMAGE:
case GIMP_DND_TYPE_LAYER:
case GIMP_DND_TYPE_CHANNEL:
case GIMP_DND_TYPE_LAYER_MASK:
case GIMP_DND_TYPE_VECTORS:
case GIMP_DND_TYPE_BRUSH:
case GIMP_DND_TYPE_PATTERN:
case GIMP_DND_TYPE_GRADIENT:
case GIMP_DND_TYPE_PALETTE:
case GIMP_DND_TYPE_FONT:
case GIMP_DND_TYPE_BUFFER:
case GIMP_DND_TYPE_IMAGEFILE:
case GIMP_DND_TYPE_TEMPLATE:
case GIMP_DND_TYPE_TOOL_ITEM:
case GIMP_DND_TYPE_NOTEBOOK_TAB:
/* Various GimpViewable drag data. */
{
GtkWidget *src_widget = gtk_drag_get_source_widget (context);
GtkWidget *src_widget = gtk_drag_get_source_widget (context);
GimpViewable *src_viewable = NULL;
if (! src_widget)
goto drop_impossible;
src_viewable = gimp_dnd_get_drag_data (src_widget);
src_viewable = gimp_dnd_get_drag_viewable (src_widget);
if (! GIMP_IS_VIEWABLE (src_viewable))
goto drop_impossible;
src_viewables = g_list_prepend (src_viewables, src_viewable);
}
break;
case GIMP_DND_TYPE_LAYER_LIST:
/* Various GimpViewable list (GList) drag data. */
{
GtkWidget *src_widget = gtk_drag_get_source_widget (context);
GList *iter;
if (! src_widget)
goto drop_impossible;
src_viewables = gimp_dnd_get_drag_list (src_widget);
if (! src_viewables)
goto drop_impossible;
for (iter = src_viewables; iter; iter = iter->next)
if (! GIMP_IS_VIEWABLE (iter->data))
{
g_warning ("%s: contents of the viewable list has the wrong type '%s'.",
G_STRFUNC, G_OBJECT_TYPE_NAME (iter->data));
g_list_free (src_viewables);
goto drop_impossible;
}
}
break;
default:
goto drop_impossible;
break;
}
if (gtk_tree_view_get_path_at_pos (tree_view->view, x, y,
@ -173,7 +312,7 @@ gimp_container_tree_view_drop_status (GimpContainerTreeView *tree_view,
{
if (GIMP_CONTAINER_TREE_VIEW_GET_CLASS (tree_view)->drop_possible (tree_view,
src_type,
src_viewable,
src_viewables,
dest_viewable,
drop_path,
drop_pos,
@ -191,7 +330,12 @@ gimp_container_tree_view_drop_status (GimpContainerTreeView *tree_view,
*return_atom = target_atom;
if (return_src)
*return_src = src_viewable;
{
src_viewables = g_list_sort_with_data (src_viewables,
(GCompareDataFunc) gimp_container_tree_view_viewable_sort,
tree_view);
*return_src = src_viewables;
}
if (return_dest)
*return_dest = dest_viewable;
@ -359,7 +503,7 @@ gimp_container_tree_view_drag_drop (GtkWidget *widget,
GimpContainerTreeView *tree_view)
{
GimpDndType src_type;
GimpViewable *src_viewable;
GList *src_viewables;
GimpViewable *dest_viewable;
GdkAtom target;
GtkTreeViewDropPosition drop_pos;
@ -373,28 +517,25 @@ gimp_container_tree_view_drag_drop (GtkWidget *widget,
if (gimp_container_tree_view_drop_status (tree_view,
context, x, y, time,
NULL, &target, &src_type,
&src_viewable,
&src_viewables,
&dest_viewable, &drop_pos))
{
GimpContainerTreeViewClass *tree_view_class;
tree_view_class = GIMP_CONTAINER_TREE_VIEW_GET_CLASS (tree_view);
if (src_viewable)
if (src_viewables)
{
gboolean success = TRUE;
/* XXX: Make GimpContainerTreeViewClass::drop_viewable()
* return success?
*/
tree_view_class->drop_viewable (tree_view, src_viewable,
dest_viewable, drop_pos);
tree_view_class->drop_viewables (tree_view, src_viewables,
dest_viewable, drop_pos);
gtk_drag_finish (context, success, FALSE, time);
}
else
{
gtk_drag_get_data (widget, context, target, time);
g_list_free (src_viewables);
}
return TRUE;
@ -538,7 +679,7 @@ gimp_container_tree_view_drag_data_received (GtkWidget *widget,
gboolean
gimp_container_tree_view_real_drop_possible (GimpContainerTreeView *tree_view,
GimpDndType src_type,
GimpViewable *src_viewable,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
@ -549,23 +690,10 @@ gimp_container_tree_view_real_drop_possible (GimpContainerTreeView *tree_view,
GimpContainer *container = gimp_container_view_get_container (view);
GimpContainer *src_container = NULL;
GimpContainer *dest_container = NULL;
GList *iter;
gint src_index = -1;
gint dest_index = -1;
if (src_viewable)
{
GimpViewable *parent = gimp_viewable_get_parent (src_viewable);
if (parent)
src_container = gimp_viewable_get_children (parent);
else if (gimp_container_have (container, GIMP_OBJECT (src_viewable)))
src_container = container;
if (src_container)
src_index = gimp_container_get_child_index (src_container,
GIMP_OBJECT (src_viewable));
}
if (dest_viewable)
{
GimpViewable *parent;
@ -593,140 +721,181 @@ gimp_container_tree_view_real_drop_possible (GimpContainerTreeView *tree_view,
GIMP_OBJECT (dest_viewable));
}
if (src_viewable && g_type_is_a (G_TYPE_FROM_INSTANCE (src_viewable),
gimp_container_get_children_type (container)))
if (return_drag_action)
{
if (src_viewable == dest_viewable)
return FALSE;
if (src_index == -1 || dest_index == -1)
return FALSE;
/* don't allow dropping a parent node onto one of its descendants
*/
if (gimp_viewable_is_ancestor (src_viewable, dest_viewable))
return FALSE;
if (! src_viewables)
*return_drag_action = GDK_ACTION_COPY;
else
*return_drag_action = GDK_ACTION_MOVE;
}
if (src_container == dest_container)
for (iter = src_viewables; iter; iter = iter->next)
{
if (drop_pos == GTK_TREE_VIEW_DROP_BEFORE)
GimpViewable *src_viewable = iter->data;
GimpViewable *parent;
parent = gimp_viewable_get_parent (src_viewable);
if (parent)
src_container = gimp_viewable_get_children (parent);
else if (gimp_container_have (container, GIMP_OBJECT (src_viewable)))
src_container = container;
if (src_container)
src_index = gimp_container_get_child_index (src_container,
GIMP_OBJECT (src_viewable));
if (g_type_is_a (G_TYPE_FROM_INSTANCE (src_viewable),
gimp_container_get_children_type (container)))
{
if (dest_index == (src_index + 1))
/* The drop won't change a thing. This is not a fatal drop
* failure, unless there is only one source viewable.
* See also the XXX below.
*/
if (src_viewable == dest_viewable && g_list_length (src_viewables) == 1)
return FALSE;
if (src_index == -1 || dest_index == -1)
return FALSE;
/* don't allow dropping a parent node onto one of its descendants
*/
if (gimp_viewable_is_ancestor (src_viewable, dest_viewable))
return FALSE;
}
else if (drop_pos == GTK_TREE_VIEW_DROP_AFTER)
/* XXX only check these for list of 1 viewable for now.
* Actually this drop failure would also happen for more than 1
* viewable if all the sources are from the same src_container
* with successive indexes.
*/
if (src_container == dest_container && g_list_length (src_viewables) == 1)
{
if (dest_index == (src_index - 1))
return FALSE;
if (drop_pos == GTK_TREE_VIEW_DROP_BEFORE)
{
if (dest_index == (src_index + 1))
return FALSE;
}
else if (drop_pos == GTK_TREE_VIEW_DROP_AFTER)
{
if (dest_index == (src_index - 1))
return FALSE;
}
}
if (return_drag_action)
{
if (! g_type_is_a (G_TYPE_FROM_INSTANCE (src_viewable),
gimp_container_get_children_type (container)))
*return_drag_action = GDK_ACTION_COPY;
}
}
if (return_drop_pos)
*return_drop_pos = drop_pos;
if (return_drag_action)
{
if (src_viewable && g_type_is_a (G_TYPE_FROM_INSTANCE (src_viewable),
gimp_container_get_children_type (container)))
*return_drag_action = GDK_ACTION_MOVE;
else
*return_drag_action = GDK_ACTION_COPY;
}
return TRUE;
}
void
gimp_container_tree_view_real_drop_viewable (GimpContainerTreeView *tree_view,
GimpViewable *src_viewable,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
gimp_container_tree_view_real_drop_viewables (GimpContainerTreeView *tree_view,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
{
GimpContainerView *view = GIMP_CONTAINER_VIEW (tree_view);
GimpContainer *src_container;
GimpContainer *dest_container;
GList *iter;
gint dest_index = 0;
if (gimp_viewable_get_parent (src_viewable))
{
src_container = gimp_viewable_get_children (
gimp_viewable_get_parent (src_viewable));
}
else
{
src_container = gimp_container_view_get_container (view);
}
g_return_if_fail (g_list_length (src_viewables) > 0);
if ((drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE) &&
gimp_viewable_get_children (dest_viewable))
src_viewables = g_list_reverse (src_viewables);
for (iter = src_viewables; iter; iter = iter->next)
{
dest_container = gimp_viewable_get_children (dest_viewable);
dest_viewable = NULL;
drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
}
else if (gimp_viewable_get_parent (dest_viewable))
{
dest_container = gimp_viewable_get_children (
gimp_viewable_get_parent (dest_viewable));
}
else
{
dest_container = gimp_container_view_get_container (view);
}
GimpViewable *src_viewable = iter->data;
if (dest_viewable)
{
dest_index = gimp_container_get_child_index (dest_container,
GIMP_OBJECT (dest_viewable));
}
if (src_container == dest_container)
{
gint src_index;
src_index = gimp_container_get_child_index (src_container,
GIMP_OBJECT (src_viewable));
switch (drop_pos)
if ((drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE) &&
gimp_viewable_get_children (dest_viewable))
{
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
if (src_index > dest_index)
dest_index++;
break;
case GTK_TREE_VIEW_DROP_BEFORE:
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
if (src_index < dest_index)
dest_index--;
break;
dest_container = gimp_viewable_get_children (dest_viewable);
dest_viewable = NULL;
drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
}
else if (gimp_viewable_get_parent (dest_viewable))
{
dest_container = gimp_viewable_get_children (gimp_viewable_get_parent (dest_viewable));
}
else
{
dest_container = gimp_container_view_get_container (view);
}
gimp_container_reorder (src_container,
GIMP_OBJECT (src_viewable), dest_index);
}
else
{
switch (drop_pos)
if (dest_viewable)
{
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
dest_index++;
break;
case GTK_TREE_VIEW_DROP_BEFORE:
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
break;
dest_index = gimp_container_get_child_index (dest_container,
GIMP_OBJECT (dest_viewable));
}
g_object_ref (src_viewable);
if (gimp_viewable_get_parent (src_viewable))
{
src_container = gimp_viewable_get_children (
gimp_viewable_get_parent (src_viewable));
}
else
{
src_container = gimp_container_view_get_container (view);
}
gimp_container_remove (src_container, GIMP_OBJECT (src_viewable));
gimp_container_insert (dest_container, GIMP_OBJECT (src_viewable),
dest_index);
if (src_container == dest_container)
{
gint src_index;
g_object_unref (src_viewable);
src_index = gimp_container_get_child_index (src_container,
GIMP_OBJECT (src_viewable));
switch (drop_pos)
{
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
if (src_index > dest_index)
dest_index++;
break;
case GTK_TREE_VIEW_DROP_BEFORE:
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
if (src_index < dest_index)
dest_index--;
break;
}
gimp_container_reorder (src_container,
GIMP_OBJECT (src_viewable), dest_index);
}
else
{
switch (drop_pos)
{
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
dest_index++;
break;
case GTK_TREE_VIEW_DROP_BEFORE:
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
break;
}
g_object_ref (src_viewable);
gimp_container_remove (src_container, GIMP_OBJECT (src_viewable));
gimp_container_insert (dest_container, GIMP_OBJECT (src_viewable),
dest_index);
g_object_unref (src_viewable);
}
}
}

View file

@ -55,17 +55,17 @@ void gimp_container_tree_view_drag_data_received
gboolean
gimp_container_tree_view_real_drop_possible (GimpContainerTreeView *tree_view,
GimpDndType src_type,
GimpViewable *src_viewable,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
GtkTreeViewDropPosition *return_drop_pos,
GdkDragAction *return_drag_action);
void
gimp_container_tree_view_real_drop_viewable (GimpContainerTreeView *tree_view,
GimpViewable *src_viewable,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
gimp_container_tree_view_real_drop_viewables (GimpContainerTreeView *tree_view,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
#endif /* __GIMP_CONTAINER_TREE_VIEW_DND_H__ */

View file

@ -123,6 +123,9 @@ static gboolean gimp_container_tree_view_tooltip (GtkWidget
static GimpViewable *gimp_container_tree_view_drag_viewable (GtkWidget *widget,
GimpContext **context,
gpointer data);
static GList * gimp_container_tree_view_drag_viewable_list (GtkWidget *widget,
GimpContext **context,
gpointer data);
static GdkPixbuf *gimp_container_tree_view_drag_pixbuf (GtkWidget *widget,
gpointer data);
@ -171,7 +174,7 @@ gimp_container_tree_view_class_init (GimpContainerTreeViewClass *klass)
klass->edit_name = gimp_container_tree_view_real_edit_name;
klass->drop_possible = gimp_container_tree_view_real_drop_possible;
klass->drop_viewable = gimp_container_tree_view_real_drop_viewable;
klass->drop_viewables = gimp_container_tree_view_real_drop_viewables;
klass->drop_color = NULL;
klass->drop_uri_list = NULL;
klass->drop_svg = NULL;
@ -653,8 +656,8 @@ gimp_container_tree_view_set_container (GimpContainerView *view,
tree_view);
if (! container)
{
if (gimp_dnd_viewable_source_remove (GTK_WIDGET (tree_view->view),
gimp_container_get_children_type (old_container)))
if (gimp_dnd_viewable_list_source_remove (GTK_WIDGET (tree_view->view),
gimp_container_get_children_type (old_container)))
{
if (GIMP_VIEWABLE_CLASS (g_type_class_peek (gimp_container_get_children_type (old_container)))->get_size)
gimp_dnd_pixbuf_source_remove (GTK_WIDGET (tree_view->view));
@ -674,6 +677,10 @@ gimp_container_tree_view_set_container (GimpContainerView *view,
gimp_container_get_children_type (container),
GDK_ACTION_COPY))
{
gimp_dnd_viewable_list_source_add (GTK_WIDGET (tree_view->view),
gimp_container_get_children_type (container),
gimp_container_tree_view_drag_viewable_list,
tree_view);
gimp_dnd_viewable_source_add (GTK_WIDGET (tree_view->view),
gimp_container_get_children_type (container),
gimp_container_tree_view_drag_viewable,
@ -1544,6 +1551,21 @@ gimp_container_tree_view_drag_viewable (GtkWidget *widget,
return NULL;
}
static GList *
gimp_container_tree_view_drag_viewable_list (GtkWidget *widget,
GimpContext **context,
gpointer data)
{
GList *items = NULL;
if (context)
*context = gimp_container_view_get_context (GIMP_CONTAINER_VIEW (data));
gimp_container_tree_view_get_selected (GIMP_CONTAINER_VIEW (data), &items, NULL);
return items;
}
static GdkPixbuf *
gimp_container_tree_view_drag_pixbuf (GtkWidget *widget,
gpointer data)

View file

@ -66,14 +66,14 @@ struct _GimpContainerTreeViewClass
gboolean (* drop_possible) (GimpContainerTreeView *tree_view,
GimpDndType src_type,
GimpViewable *src_viewable,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
GtkTreeViewDropPosition *return_drop_pos,
GdkDragAction *return_drag_action);
void (* drop_viewable) (GimpContainerTreeView *tree_view,
GimpViewable *src_viewable,
void (* drop_viewables) (GimpContainerTreeView *tree_view,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
void (* drop_color) (GimpContainerTreeView *tree_view,

View file

@ -98,6 +98,10 @@ struct _GimpDndDataDef
};
static GtkWidget * gimp_dnd_get_viewable_list_icon (GtkWidget *widget,
GdkDragContext *context,
GCallback get_viewable_list_func,
gpointer get_viewable_list_data);
static GtkWidget * gimp_dnd_get_viewable_icon (GtkWidget *widget,
GdkDragContext *context,
GCallback get_viewable_func,
@ -200,6 +204,18 @@ static gboolean gimp_dnd_set_item_data (GtkWidget *widget,
gpointer set_item_data,
GtkSelectionData *selection);
static void gimp_dnd_get_item_list_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_item_func,
gpointer get_item_data,
GtkSelectionData *selection);
static gboolean gimp_dnd_set_item_list_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_item_func,
gpointer set_item_data,
GtkSelectionData *selection);
static void gimp_dnd_get_object_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_object_func,
@ -610,7 +626,22 @@ static const GimpDndDataDef dnd_data_defs[] =
NULL,
NULL,
NULL
}
},
{
GIMP_TARGET_LAYER_LIST,
"gimp-dnd-get-layer-list-func",
"gimp-dnd-get-layer-list-data",
"gimp-dnd-set-layer-list-func",
"gimp-dnd-set-layer-list-data",
gimp_dnd_get_viewable_list_icon,
gimp_dnd_get_item_list_data,
gimp_dnd_set_item_list_data,
},
};
@ -1867,63 +1898,64 @@ gimp_dnd_get_viewable_icon (GtkWidget *widget,
}
static GimpDndType
gimp_dnd_data_type_get_by_g_type (GType type)
gimp_dnd_data_type_get_by_g_type (GType type,
gboolean list)
{
GimpDndType dnd_type = GIMP_DND_TYPE_NONE;
if (g_type_is_a (type, GIMP_TYPE_IMAGE))
if (g_type_is_a (type, GIMP_TYPE_IMAGE) && ! list)
{
dnd_type = GIMP_DND_TYPE_IMAGE;
}
else if (g_type_is_a (type, GIMP_TYPE_LAYER))
{
dnd_type = GIMP_DND_TYPE_LAYER;
dnd_type = list ? GIMP_DND_TYPE_LAYER_LIST : GIMP_DND_TYPE_LAYER;
}
else if (g_type_is_a (type, GIMP_TYPE_LAYER_MASK))
else if (g_type_is_a (type, GIMP_TYPE_LAYER_MASK) && ! list)
{
dnd_type = GIMP_DND_TYPE_LAYER_MASK;
}
else if (g_type_is_a (type, GIMP_TYPE_CHANNEL))
else if (g_type_is_a (type, GIMP_TYPE_CHANNEL) && ! list)
{
dnd_type = GIMP_DND_TYPE_CHANNEL;
}
else if (g_type_is_a (type, GIMP_TYPE_VECTORS))
else if (g_type_is_a (type, GIMP_TYPE_VECTORS) && ! list)
{
dnd_type = GIMP_DND_TYPE_VECTORS;
}
else if (g_type_is_a (type, GIMP_TYPE_BRUSH))
else if (g_type_is_a (type, GIMP_TYPE_BRUSH) && ! list)
{
dnd_type = GIMP_DND_TYPE_BRUSH;
}
else if (g_type_is_a (type, GIMP_TYPE_PATTERN))
else if (g_type_is_a (type, GIMP_TYPE_PATTERN) && ! list)
{
dnd_type = GIMP_DND_TYPE_PATTERN;
}
else if (g_type_is_a (type, GIMP_TYPE_GRADIENT))
else if (g_type_is_a (type, GIMP_TYPE_GRADIENT) && ! list)
{
dnd_type = GIMP_DND_TYPE_GRADIENT;
}
else if (g_type_is_a (type, GIMP_TYPE_PALETTE))
else if (g_type_is_a (type, GIMP_TYPE_PALETTE) && ! list)
{
dnd_type = GIMP_DND_TYPE_PALETTE;
}
else if (g_type_is_a (type, GIMP_TYPE_FONT))
else if (g_type_is_a (type, GIMP_TYPE_FONT) && ! list)
{
dnd_type = GIMP_DND_TYPE_FONT;
}
else if (g_type_is_a (type, GIMP_TYPE_BUFFER))
else if (g_type_is_a (type, GIMP_TYPE_BUFFER) && ! list)
{
dnd_type = GIMP_DND_TYPE_BUFFER;
}
else if (g_type_is_a (type, GIMP_TYPE_IMAGEFILE))
else if (g_type_is_a (type, GIMP_TYPE_IMAGEFILE) && ! list)
{
dnd_type = GIMP_DND_TYPE_IMAGEFILE;
}
else if (g_type_is_a (type, GIMP_TYPE_TEMPLATE))
else if (g_type_is_a (type, GIMP_TYPE_TEMPLATE) && ! list)
{
dnd_type = GIMP_DND_TYPE_TEMPLATE;
}
else if (g_type_is_a (type, GIMP_TYPE_TOOL_ITEM))
else if (g_type_is_a (type, GIMP_TYPE_TOOL_ITEM) && ! list)
{
dnd_type = GIMP_DND_TYPE_TOOL_ITEM;
}
@ -1941,7 +1973,7 @@ gimp_dnd_drag_source_set_by_type (GtkWidget *widget,
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
dnd_type = gimp_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type == GIMP_DND_TYPE_NONE)
return FALSE;
@ -1957,24 +1989,56 @@ gboolean
gimp_dnd_drag_dest_set_by_type (GtkWidget *widget,
GtkDestDefaults flags,
GType type,
gboolean list_accepted,
GdkDragAction actions)
{
GimpDndType dnd_type;
GtkTargetEntry target_entries[2];
GimpDndType dnd_type;
gint target_entries_n = 0;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
if (list_accepted)
{
dnd_type = gimp_dnd_data_type_get_by_g_type (type, TRUE);
if (dnd_type == GIMP_DND_TYPE_NONE)
if (dnd_type != GIMP_DND_TYPE_NONE)
{
target_entries[target_entries_n] = dnd_data_defs[dnd_type].target_entry;
target_entries_n++;
}
}
dnd_type = gimp_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type != GIMP_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,
&dnd_data_defs[dnd_type].target_entry, 1,
(const GtkTargetEntry *) &target_entries,
target_entries_n,
actions);
return TRUE;
}
/**
* gimp_dnd_viewable_source_add:
* @widget:
* @type:
* @get_viewable_func:
* @data:
*
* Sets up @widget as a drag source for a #GimpViewable object, as
* returned by @get_viewable_func on @widget and @data.
*
* @type must be a list type for drag operations.
*/
gboolean
gimp_dnd_viewable_source_add (GtkWidget *widget,
GType type,
@ -1986,7 +2050,7 @@ gimp_dnd_viewable_source_add (GtkWidget *widget,
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
g_return_val_if_fail (get_viewable_func != NULL, FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
dnd_type = gimp_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type == GIMP_DND_TYPE_NONE)
return FALSE;
@ -2006,7 +2070,7 @@ gimp_dnd_viewable_source_remove (GtkWidget *widget,
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
dnd_type = gimp_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type == GIMP_DND_TYPE_NONE)
return FALSE;
@ -2024,7 +2088,7 @@ gimp_dnd_viewable_dest_add (GtkWidget *widget,
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
dnd_type = gimp_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type == GIMP_DND_TYPE_NONE)
return FALSE;
@ -2044,7 +2108,7 @@ gimp_dnd_viewable_dest_remove (GtkWidget *widget,
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
dnd_type = gimp_dnd_data_type_get_by_g_type (type, FALSE);
if (dnd_type == GIMP_DND_TYPE_NONE)
return FALSE;
@ -2055,7 +2119,7 @@ gimp_dnd_viewable_dest_remove (GtkWidget *widget,
}
GimpViewable *
gimp_dnd_get_drag_data (GtkWidget *widget)
gimp_dnd_get_drag_viewable (GtkWidget *widget)
{
const GimpDndDataDef *dnd_data;
GimpDndType data_type;
@ -2088,6 +2152,201 @@ gimp_dnd_get_drag_data (GtkWidget *widget)
}
/*************************************************/
/* GimpViewable (by GType) GList dnd functions */
/*************************************************/
static GtkWidget *
gimp_dnd_get_viewable_list_icon (GtkWidget *widget,
GdkDragContext *context,
GCallback get_list_func,
gpointer get_list_data)
{
GList *viewables;
GimpViewable *viewable;
GimpContext *gimp_context;
GtkWidget *view;
gchar *desc;
viewables = (* (GimpDndDragViewableListFunc) get_list_func) (widget,
&gimp_context,
get_list_data);
if (! viewables)
return NULL;
/* Should we just show one of the viewable? Add a number to show we
* are dragging several viewables? Something else? XXX
*/
viewable = viewables->data;
GIMP_LOG (DND, "viewable %p", viewable);
g_object_set_data_full (G_OBJECT (context),
"gimp-dnd-viewable", g_object_ref (viewable),
(GDestroyNotify) g_object_unref);
view = gimp_view_new (gimp_context, viewable,
DRAG_PREVIEW_SIZE, 0, TRUE);
desc = gimp_viewable_get_description (viewable, NULL);
if (desc)
{
GtkWidget *hbox;
GtkWidget *label;
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
gtk_box_pack_start (GTK_BOX (hbox), view, FALSE, FALSE, 0);
gtk_widget_show (view);
label = g_object_new (GTK_TYPE_LABEL,
"label", desc,
"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;
}
/**
* gimp_dnd_viewable_list_source_add:
* @widget:
* @type:
* @get_viewable_func:
* @data:
*
* Sets up @widget as a drag source for a #GList of #GimpViewable
* object, as returned by @get_viewable_func on @widget and @data.
*
* @type must be a list type for drag operations (only GimpLayer so
* far).
*/
gboolean
gimp_dnd_viewable_list_source_add (GtkWidget *widget,
GType type,
GimpDndDragViewableListFunc get_viewable_list_func,
gpointer data)
{
GimpDndType 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 = gimp_dnd_data_type_get_by_g_type (type, TRUE);
if (dnd_type == GIMP_DND_TYPE_NONE)
return FALSE;
gimp_dnd_data_source_add (dnd_type, widget,
G_CALLBACK (get_viewable_list_func),
data);
return TRUE;
}
gboolean
gimp_dnd_viewable_list_source_remove (GtkWidget *widget,
GType type)
{
GimpDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type, TRUE);
if (dnd_type == GIMP_DND_TYPE_NONE)
return FALSE;
return gimp_dnd_data_source_remove (dnd_type, widget);
}
gboolean
gimp_dnd_viewable_list_dest_add (GtkWidget *widget,
GType type,
GimpDndDropViewableListFunc set_viewable_func,
gpointer data)
{
GimpDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type, TRUE);
if (dnd_type == GIMP_DND_TYPE_NONE)
return FALSE;
gimp_dnd_data_dest_add (dnd_type, widget,
G_CALLBACK (set_viewable_func),
data);
return TRUE;
}
gboolean
gimp_dnd_viewable_list_dest_remove (GtkWidget *widget,
GType type)
{
GimpDndType dnd_type;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type, TRUE);
if (dnd_type == GIMP_DND_TYPE_NONE)
return FALSE;
gimp_dnd_data_dest_remove (dnd_type, widget);
return TRUE;
}
GList *
gimp_dnd_get_drag_list (GtkWidget *widget)
{
const GimpDndDataDef *dnd_data;
GimpDndType data_type;
GimpDndDragViewableListFunc get_data_func = NULL;
gpointer get_data_data = NULL;
GimpContext *context;
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
data_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
"gimp-dnd-get-data-type"));
if (! data_type)
return NULL;
dnd_data = dnd_data_defs + data_type;
if (dnd_data->get_data_func_name)
get_data_func = g_object_get_data (G_OBJECT (widget),
dnd_data->get_data_func_name);
if (dnd_data->get_data_data_name)
get_data_data = g_object_get_data (G_OBJECT (widget),
dnd_data->get_data_data_name);
if (! get_data_func)
return NULL;
return (GList *) (* get_data_func) (widget, &context, get_data_data);
}
/*****************************/
/* GimpImage dnd functions */
/*****************************/
@ -2188,6 +2447,48 @@ gimp_dnd_set_item_data (GtkWidget *widget,
}
/**********************************/
/* GimpItem GList dnd functions */
/**********************************/
static void
gimp_dnd_get_item_list_data (GtkWidget *widget,
GdkDragContext *context,
GCallback get_item_func,
gpointer get_item_data,
GtkSelectionData *selection)
{
GList *items;
GimpContext *gimp_context;
items = (* (GimpDndDragViewableListFunc) get_item_func) (widget, &gimp_context,
get_item_data);
if (items)
gimp_selection_data_set_item_list (selection, items);
g_list_free (items);
}
static gboolean
gimp_dnd_set_item_list_data (GtkWidget *widget,
gint x,
gint y,
GCallback set_item_func,
gpointer set_item_data,
GtkSelectionData *selection)
{
GList *items = gimp_selection_data_get_item_list (selection, the_dnd_gimp);
if (! items)
return FALSE;
(* (GimpDndDropViewableListFunc) set_item_func) (widget, x, y, items,
set_item_data);
g_list_free (items);
return TRUE;
}
/******************************/
/* GimpObject dnd functions */
/******************************/

View file

@ -94,6 +94,9 @@
#define GIMP_TARGET_NOTEBOOK_TAB \
{ "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, GIMP_DND_TYPE_NOTEBOOK_TAB }
#define GIMP_TARGET_LAYER_LIST \
{ "application/x-gimp-layer-list", GTK_TARGET_SAME_APP, GIMP_DND_TYPE_LAYER_LIST }
/* dnd initialization */
@ -237,6 +240,7 @@ gboolean gimp_dnd_viewable_source_remove (GtkWidget *widget,
gboolean gimp_dnd_drag_dest_set_by_type (GtkWidget *widget,
GtkDestDefaults flags,
GType type,
gboolean list_accepted,
GdkDragAction actions);
gboolean gimp_dnd_viewable_dest_add (GtkWidget *widget,
@ -246,8 +250,32 @@ gboolean gimp_dnd_viewable_dest_add (GtkWidget *widget,
gboolean gimp_dnd_viewable_dest_remove (GtkWidget *widget,
GType type);
GimpViewable * gimp_dnd_get_drag_data (GtkWidget *widget);
GimpViewable * gimp_dnd_get_drag_viewable (GtkWidget *widget);
/* GimpViewable (by GType) GList dnd functions */
typedef GList * (* GimpDndDragViewableListFunc) (GtkWidget *widget,
GimpContext **context,
gpointer data);
typedef void (* GimpDndDropViewableListFunc) (GtkWidget *widget,
gint x,
gint y,
GList *viewables,
gpointer data);
gboolean gimp_dnd_viewable_list_source_add (GtkWidget *widget,
GType type,
GimpDndDragViewableListFunc get_viewable_list_func,
gpointer data);
gboolean gimp_dnd_viewable_list_source_remove (GtkWidget *widget,
GType type);
gboolean gimp_dnd_viewable_list_dest_add (GtkWidget *widget,
GType type,
GimpDndDropViewableListFunc set_viewable_func,
gpointer data);
gboolean gimp_dnd_viewable_list_dest_remove (GtkWidget *widget,
GType type);
GList * gimp_dnd_get_drag_list (GtkWidget *widget);
/* Direct Save Protocol (XDS) */

View file

@ -176,7 +176,7 @@ static GList *
gimp_document_view_drag_uri_list (GtkWidget *widget,
gpointer data)
{
GimpViewable *viewable = gimp_dnd_get_drag_data (widget);
GimpViewable *viewable = gimp_dnd_get_drag_viewable (widget);
if (viewable)
{

View file

@ -53,16 +53,16 @@ static gboolean gimp_drawable_tree_view_select_item (GimpContainerView *view,
static gboolean gimp_drawable_tree_view_drop_possible(GimpContainerTreeView *view,
GimpDndType src_type,
GimpViewable *src_viewable,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
GtkTreeViewDropPosition *return_drop_pos,
GdkDragAction *return_drag_action);
static void gimp_drawable_tree_view_drop_viewable (GimpContainerTreeView *view,
GimpViewable *src_viewable,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
static void gimp_drawable_tree_view_drop_viewables (GimpContainerTreeView *view,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
static void gimp_drawable_tree_view_drop_color (GimpContainerTreeView *view,
const GimpRGB *color,
GimpViewable *dest_viewable,
@ -112,9 +112,9 @@ gimp_drawable_tree_view_class_init (GimpDrawableTreeViewClass *klass)
object_class->constructed = gimp_drawable_tree_view_constructed;
tree_view_class->drop_possible = gimp_drawable_tree_view_drop_possible;
tree_view_class->drop_viewable = gimp_drawable_tree_view_drop_viewable;
tree_view_class->drop_color = gimp_drawable_tree_view_drop_color;
tree_view_class->drop_possible = gimp_drawable_tree_view_drop_possible;
tree_view_class->drop_viewables = gimp_drawable_tree_view_drop_viewables;
tree_view_class->drop_color = gimp_drawable_tree_view_drop_color;
item_view_class->set_image = gimp_drawable_tree_view_set_image;
@ -203,7 +203,7 @@ gimp_drawable_tree_view_select_item (GimpContainerView *view,
static gboolean
gimp_drawable_tree_view_drop_possible (GimpContainerTreeView *tree_view,
GimpDndType src_type,
GimpViewable *src_viewable,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
@ -212,7 +212,7 @@ gimp_drawable_tree_view_drop_possible (GimpContainerTreeView *tree_view,
{
if (GIMP_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_possible (tree_view,
src_type,
src_viewable,
src_viewables,
dest_viewable,
drop_path,
drop_pos,
@ -240,34 +240,41 @@ gimp_drawable_tree_view_drop_possible (GimpContainerTreeView *tree_view,
}
static void
gimp_drawable_tree_view_drop_viewable (GimpContainerTreeView *view,
GimpViewable *src_viewable,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
gimp_drawable_tree_view_drop_viewables (GimpContainerTreeView *view,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
{
if (dest_viewable && GIMP_IS_PATTERN (src_viewable))
GList *iter;
for (iter = src_viewables; iter; iter = iter->next)
{
GimpImage *image = gimp_item_get_image (GIMP_ITEM (dest_viewable));
GimpFillOptions *options = gimp_fill_options_new (image->gimp, NULL, FALSE);
GimpViewable *src_viewable = iter->data;
gimp_fill_options_set_style (options, GIMP_FILL_STYLE_PATTERN);
gimp_context_set_pattern (GIMP_CONTEXT (options),
GIMP_PATTERN (src_viewable));
if (dest_viewable && GIMP_IS_PATTERN (src_viewable))
{
GimpImage *image = gimp_item_get_image (GIMP_ITEM (dest_viewable));
GimpFillOptions *options = gimp_fill_options_new (image->gimp, NULL, FALSE);
gimp_drawable_edit_fill (GIMP_DRAWABLE (dest_viewable),
options,
C_("undo-type", "Drop pattern to layer"));
gimp_fill_options_set_style (options, GIMP_FILL_STYLE_PATTERN);
gimp_context_set_pattern (GIMP_CONTEXT (options),
GIMP_PATTERN (src_viewable));
g_object_unref (options);
gimp_drawable_edit_fill (GIMP_DRAWABLE (dest_viewable),
options,
C_("undo-type", "Drop pattern to layer"));
gimp_image_flush (image);
return;
g_object_unref (options);
gimp_image_flush (image);
return;
}
}
GIMP_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_viewable (view,
src_viewable,
dest_viewable,
drop_pos);
GIMP_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_viewables (view,
src_viewables,
dest_viewable,
drop_pos);
}
static void

View file

@ -120,7 +120,7 @@ gimp_image_view_new (GimpViewType view_type,
dnd_widget = gimp_container_view_get_dnd_widget (editor->view);
gimp_dnd_xds_source_add (dnd_widget,
(GimpDndDragViewableFunc) gimp_dnd_get_drag_data,
(GimpDndDragViewableFunc) gimp_dnd_get_drag_viewable,
NULL);
}

View file

@ -141,15 +141,15 @@ static void gimp_item_tree_view_context_item (GimpContainerView *view,
static gboolean gimp_item_tree_view_drop_possible (GimpContainerTreeView *view,
GimpDndType src_type,
GimpViewable *src_viewable,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
GtkTreeViewDropPosition *return_drop_pos,
GdkDragAction *return_drag_action);
static void gimp_item_tree_view_drop_viewable (GimpContainerTreeView *view,
GimpViewable *src_viewable,
GimpViewable *dest_viewable,
static void gimp_item_tree_view_drop_viewables (GimpContainerTreeView *view,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
static void gimp_item_tree_view_new_dropped (GtkWidget *widget,
@ -157,6 +157,11 @@ static void gimp_item_tree_view_new_dropped (GtkWidget *widget,
gint y,
GimpViewable *viewable,
gpointer data);
static void gimp_item_tree_view_new_list_dropped (GtkWidget *widget,
gint x,
gint y,
GList *viewables,
gpointer data);
static void gimp_item_tree_view_item_changed (GimpImage *image,
GimpItemTreeView *view);
@ -252,8 +257,8 @@ gimp_item_tree_view_class_init (GimpItemTreeViewClass *klass)
widget_class->style_updated = gimp_item_tree_view_style_updated;
tree_view_class->drop_possible = gimp_item_tree_view_drop_possible;
tree_view_class->drop_viewable = gimp_item_tree_view_drop_viewable;
tree_view_class->drop_possible = gimp_item_tree_view_drop_possible;
tree_view_class->drop_viewables = gimp_item_tree_view_drop_viewables;
klass->set_image = gimp_item_tree_view_real_set_image;
@ -419,6 +424,7 @@ gimp_item_tree_view_constructed (GObject *object)
gimp_dnd_drag_dest_set_by_type (GTK_WIDGET (tree_view->view),
GTK_DEST_DEFAULT_HIGHLIGHT,
item_view_class->item_type,
TRUE,
GDK_ACTION_MOVE | GDK_ACTION_COPY);
item_view->priv->new_button =
@ -430,6 +436,10 @@ gimp_item_tree_view_constructed (GObject *object)
/* connect "drop to new" manually as it makes a difference whether
* it was clicked or dropped
*/
gimp_dnd_viewable_list_dest_add (item_view->priv->new_button,
item_view_class->item_type,
gimp_item_tree_view_new_list_dropped,
item_view);
gimp_dnd_viewable_dest_add (item_view->priv->new_button,
item_view_class->item_type,
gimp_item_tree_view_new_dropped,
@ -1147,17 +1157,37 @@ gimp_item_tree_view_context_item (GimpContainerView *view,
static gboolean
gimp_item_tree_view_drop_possible (GimpContainerTreeView *tree_view,
GimpDndType src_type,
GimpViewable *src_viewable,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
GtkTreeViewDropPosition *return_drop_pos,
GdkDragAction *return_drag_action)
{
if (GIMP_IS_ITEM (src_viewable) &&
(dest_viewable == NULL ||
gimp_item_get_image (GIMP_ITEM (src_viewable)) !=
gimp_item_get_image (GIMP_ITEM (dest_viewable))))
GList *iter;
gboolean other_image_items;
if (src_viewables)
other_image_items = TRUE;
else
other_image_items = FALSE;
for (iter = src_viewables; iter; iter = iter->next)
{
GimpViewable *src_viewable = iter->data;
if (! GIMP_IS_ITEM (src_viewable) ||
(dest_viewable != NULL &&
gimp_item_get_image (GIMP_ITEM (src_viewable)) ==
gimp_item_get_image (GIMP_ITEM (dest_viewable))))
{
/* Not an item or from the same image. */
other_image_items = FALSE;
break;
}
}
if (other_image_items)
{
if (return_drop_pos)
*return_drop_pos = drop_pos;
@ -1170,7 +1200,7 @@ gimp_item_tree_view_drop_possible (GimpContainerTreeView *tree_view,
return GIMP_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_possible (tree_view,
src_type,
src_viewable,
src_viewables,
dest_viewable,
drop_path,
drop_pos,
@ -1179,65 +1209,88 @@ gimp_item_tree_view_drop_possible (GimpContainerTreeView *tree_view,
}
static void
gimp_item_tree_view_drop_viewable (GimpContainerTreeView *tree_view,
GimpViewable *src_viewable,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
gimp_item_tree_view_drop_viewables (GimpContainerTreeView *tree_view,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
{
GimpItemTreeViewClass *item_view_class;
GimpItemTreeView *item_view = GIMP_ITEM_TREE_VIEW (tree_view);
GList *iter;
GType src_viewable_type = G_TYPE_NONE;
gint dest_index = -1;
item_view_class = GIMP_ITEM_TREE_VIEW_GET_CLASS (item_view);
if (item_view->priv->image != gimp_item_get_image (GIMP_ITEM (src_viewable)) ||
! g_type_is_a (G_TYPE_FROM_INSTANCE (src_viewable),
item_view_class->item_type))
for (iter = src_viewables; iter; iter = iter->next)
{
GType item_type = item_view_class->item_type;
GimpItem *new_item;
GimpItem *parent;
GimpViewable *src_viewable = iter->data;
if (g_type_is_a (G_TYPE_FROM_INSTANCE (src_viewable), item_type))
item_type = G_TYPE_FROM_INSTANCE (src_viewable);
dest_index = gimp_item_tree_view_get_drop_index (item_view, dest_viewable,
drop_pos,
(GimpViewable **) &parent);
new_item = gimp_item_convert (GIMP_ITEM (src_viewable),
item_view->priv->image, item_type);
gimp_item_set_linked (new_item, FALSE, FALSE);
item_view_class->add_item (item_view->priv->image, new_item,
parent, dest_index, TRUE);
/* All dropped viewables must be of the same finale type. */
if (src_viewable_type == G_TYPE_NONE)
src_viewable_type = G_TYPE_FROM_INSTANCE (src_viewable);
else
g_return_if_fail (src_viewable_type == G_TYPE_FROM_INSTANCE (src_viewable));
}
else if (dest_viewable)
if (drop_pos == GTK_TREE_VIEW_DROP_AFTER ||
(drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER &&
dest_viewable &&
gimp_viewable_get_children (dest_viewable)))
src_viewables = g_list_reverse (src_viewables);
for (iter = src_viewables; iter; iter = iter->next)
{
GimpItem *src_parent;
GimpItem *dest_parent;
gint src_index;
gint dest_index;
GimpViewable *src_viewable = iter->data;
src_parent = GIMP_ITEM (gimp_viewable_get_parent (src_viewable));
src_index = gimp_item_get_index (GIMP_ITEM (src_viewable));
dest_index = gimp_item_tree_view_get_drop_index (item_view, dest_viewable,
drop_pos,
(GimpViewable **) &dest_parent);
if (src_parent == dest_parent)
if (item_view->priv->image != gimp_item_get_image (GIMP_ITEM (src_viewable)) ||
! g_type_is_a (src_viewable_type, item_view_class->item_type))
{
if (src_index < dest_index)
dest_index--;
}
GType item_type = item_view_class->item_type;
GimpItem *new_item;
GimpItem *parent;
gimp_image_reorder_item (item_view->priv->image,
GIMP_ITEM (src_viewable),
dest_parent,
dest_index,
TRUE, NULL);
if (g_type_is_a (src_viewable_type, item_type))
item_type = G_TYPE_FROM_INSTANCE (src_viewable);
dest_index = gimp_item_tree_view_get_drop_index (item_view, dest_viewable,
drop_pos,
(GimpViewable **) &parent);
new_item = gimp_item_convert (GIMP_ITEM (src_viewable),
item_view->priv->image, item_type);
gimp_item_set_linked (new_item, FALSE, FALSE);
item_view_class->add_item (item_view->priv->image, new_item,
parent, dest_index, TRUE);
}
else if (dest_viewable)
{
GimpItem *src_parent;
GimpItem *dest_parent;
gint src_index;
gint dest_index;
src_parent = GIMP_ITEM (gimp_viewable_get_parent (src_viewable));
src_index = gimp_item_get_index (GIMP_ITEM (src_viewable));
dest_index = gimp_item_tree_view_get_drop_index (item_view, dest_viewable,
drop_pos,
(GimpViewable **) &dest_parent);
if (src_parent == dest_parent)
{
if (src_index < dest_index)
dest_index--;
}
gimp_image_reorder_item (item_view->priv->image,
GIMP_ITEM (src_viewable),
dest_parent,
dest_index,
TRUE, NULL);
}
}
gimp_image_flush (item_view->priv->image);
@ -1274,6 +1327,38 @@ gimp_item_tree_view_new_dropped (GtkWidget *widget,
}
}
static void
gimp_item_tree_view_new_list_dropped (GtkWidget *widget,
gint x,
gint y,
GList *viewables,
gpointer data)
{
GimpItemTreeViewClass *item_view_class = GIMP_ITEM_TREE_VIEW_GET_CLASS (data);
GimpContainerView *view = GIMP_CONTAINER_VIEW (data);
GimpAction *action;
action = gimp_ui_manager_find_action (gimp_editor_get_ui_manager (GIMP_EDITOR (view)),
item_view_class->action_group,
item_view_class->new_default_action);
if (item_view_class->new_default_action && viewables && action)
{
GList *iter;
for (iter = viewables; iter; iter = iter->next)
{
GimpViewable *viewable = iter->data;
if (gimp_container_view_lookup (view, viewable))
{
g_object_set (action, "viewable", viewable, NULL);
gimp_action_activate (action);
g_object_set (action, "viewable", NULL, NULL);
}
}
}
}
/* GimpImage callbacks */

View file

@ -108,7 +108,7 @@ static gboolean gimp_layer_tree_view_select_items (GimpContainer
static void gimp_layer_tree_view_set_view_size (GimpContainerView *view);
static gboolean gimp_layer_tree_view_drop_possible (GimpContainerTreeView *view,
GimpDndType src_type,
GimpViewable *src_viewable,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
@ -716,7 +716,7 @@ gimp_layer_tree_view_set_view_size (GimpContainerView *view)
static gboolean
gimp_layer_tree_view_drop_possible (GimpContainerTreeView *tree_view,
GimpDndType src_type,
GimpViewable *src_viewable,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
@ -731,7 +731,7 @@ gimp_layer_tree_view_drop_possible (GimpContainerTreeView *tree_view,
src_type == GIMP_DND_TYPE_NETSCAPE_URL ||
src_type == GIMP_DND_TYPE_COMPONENT ||
src_type == GIMP_DND_TYPE_PIXBUF ||
GIMP_IS_DRAWABLE (src_viewable))
g_list_length (src_viewables) > 0)
{
GimpImage *dest_image = gimp_item_tree_view_get_image (GIMP_ITEM_TREE_VIEW (tree_view));
@ -741,7 +741,7 @@ gimp_layer_tree_view_drop_possible (GimpContainerTreeView *tree_view,
return GIMP_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_possible (tree_view,
src_type,
src_viewable,
src_viewables,
dest_viewable,
drop_path,
drop_pos,

View file

@ -626,6 +626,72 @@ gimp_selection_data_get_item (GtkSelectionData *selection,
return NULL;
}
void
gimp_selection_data_set_item_list (GtkSelectionData *selection,
GList *items)
{
GString *str;
GList *iter;
g_return_if_fail (selection != NULL);
g_return_if_fail (items);
for (iter = items; iter; iter = iter->next)
g_return_if_fail (GIMP_IS_ITEM (iter->data));
str = g_string_new (NULL);
g_string_printf (str, "%d", gimp_get_pid ());
for (iter = items; iter; iter = iter->next)
g_string_append_printf (str, ":%d", gimp_item_get_id (iter->data));
gtk_selection_data_set (selection,
gtk_selection_data_get_target (selection),
8, (guchar *) str->str, str->len);
g_string_free (str, TRUE);
}
GList *
gimp_selection_data_get_item_list (GtkSelectionData *selection,
Gimp *gimp)
{
const gchar *str;
GList *items = NULL;
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
g_return_val_if_fail (selection != NULL, NULL);
str = gimp_selection_data_get_name (selection, G_STRFUNC);
if (str)
{
gchar **tokens;
gint64 pid;
tokens = g_strsplit (str, ":", -1);
g_return_val_if_fail (tokens[0] != NULL && tokens[1] != NULL, NULL);
pid = g_ascii_strtoll (tokens[0], NULL, 10);
if (pid == gimp_get_pid ())
{
gint i = 1;
while (tokens[i])
{
gint64 id = g_ascii_strtoll (tokens[i], NULL, 10);
items = g_list_prepend (items, gimp_item_get_by_id (gimp, id));
i++;
}
items = g_list_reverse (items);
}
g_strfreev (tokens);
}
return items;
}
void
gimp_selection_data_set_object (GtkSelectionData *selection,
GimpObject *object)

View file

@ -84,6 +84,14 @@ GimpItem * gimp_selection_data_get_item (GtkSelectionData *selection,
Gimp *gimp);
/* item list */
void gimp_selection_data_set_item_list (GtkSelectionData *selection,
GList *items);
GList * gimp_selection_data_get_item_list (GtkSelectionData *selection,
Gimp *gimp);
/* various data */
void gimp_selection_data_set_object (GtkSelectionData *selection,

View file

@ -86,14 +86,14 @@ static void gimp_tool_editor_set_context (GimpContainer
static gboolean gimp_tool_editor_drop_possible (GimpContainerTreeView *tree_view,
GimpDndType src_type,
GimpViewable *src_viewable,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
GtkTreeViewDropPosition *return_drop_pos,
GdkDragAction *return_drag_action);
static void gimp_tool_editor_drop_viewable (GimpContainerTreeView *tree_view,
GimpViewable *src_viewable,
static void gimp_tool_editor_drop_viewables (GimpContainerTreeView *tree_view,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
@ -155,10 +155,10 @@ gimp_tool_editor_class_init (GimpToolEditorClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpContainerTreeViewClass *tree_view_class = GIMP_CONTAINER_TREE_VIEW_CLASS (klass);
object_class->constructed = gimp_tool_editor_constructed;
object_class->constructed = gimp_tool_editor_constructed;
tree_view_class->drop_possible = gimp_tool_editor_drop_possible;
tree_view_class->drop_viewable = gimp_tool_editor_drop_viewable;
tree_view_class->drop_possible = gimp_tool_editor_drop_possible;
tree_view_class->drop_viewables = gimp_tool_editor_drop_viewables;
}
static void
@ -319,7 +319,7 @@ gimp_tool_editor_set_context (GimpContainerView *container_view,
static gboolean
gimp_tool_editor_drop_possible (GimpContainerTreeView *tree_view,
GimpDndType src_type,
GimpViewable *src_viewable,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
@ -328,7 +328,7 @@ gimp_tool_editor_drop_possible (GimpContainerTreeView *tree_view,
{
if (GIMP_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_possible (
tree_view,
src_type, src_viewable, dest_viewable, drop_path, drop_pos,
src_type, src_viewables, dest_viewable, drop_path, drop_pos,
return_drop_pos, return_drag_action))
{
if (gimp_viewable_get_parent (dest_viewable) ||
@ -336,7 +336,15 @@ gimp_tool_editor_drop_possible (GimpContainerTreeView *tree_view,
(drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)))
{
return ! gimp_viewable_get_children (src_viewable);
GList *iter;
for (iter = src_viewables; iter; iter = iter->next)
{
GimpViewable *src_viewable = iter->data;
if (gimp_viewable_get_children (src_viewable))
return FALSE;
}
}
return TRUE;
@ -346,19 +354,20 @@ gimp_tool_editor_drop_possible (GimpContainerTreeView *tree_view,
}
static void
gimp_tool_editor_drop_viewable (GimpContainerTreeView *tree_view,
GimpViewable *src_viewable,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
gimp_tool_editor_drop_viewables (GimpContainerTreeView *tree_view,
GList *src_viewables,
GimpViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
{
GimpContainerView *container_view = GIMP_CONTAINER_VIEW (tree_view);
GIMP_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_viewable (tree_view,
src_viewable,
dest_viewable,
drop_pos);
GIMP_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_viewables (tree_view,
src_viewables,
dest_viewable,
drop_pos);
gimp_container_view_select_item (container_view, src_viewable);
if (src_viewables)
gimp_container_view_select_item (container_view, src_viewables->data);
}
static void

View file

@ -167,7 +167,9 @@ typedef enum /*< skip >*/
GIMP_DND_TYPE_TOOL_ITEM = 23,
GIMP_DND_TYPE_NOTEBOOK_TAB = 24,
GIMP_DND_TYPE_LAST = GIMP_DND_TYPE_NOTEBOOK_TAB
GIMP_DND_TYPE_LAYER_LIST = 25,
GIMP_DND_TYPE_LAST = GIMP_DND_TYPE_LAYER_LIST
} GimpDndType;
typedef enum /*< skip >*/