app: rework and fix the logic for copy-pasting multiple drawables.
There were a lot of incertainty of what should happen when we copy layers being descendant of each other (i.e. when you select a group layer and some of its children), then when you paste such data. So we sat down with Aryeom and tried to come up with some consistent behavior which is somewhat expectable, but also which would allow the most use-case. Otherwise it was making very weird result when pasting the data, duplicating some layers and whatnot, which was obviously a buggy behavior and never the expected result. We decided that if you select one leaf item, then even if you also selected a parent item, it would be as though the parent was not selected. This is very often what you expect anyway when you select a whole bunch of layers and would work well if, say, you shift-click over many layers in sub-groups. Then you wouldn't have to manually ctrl-click to unselect every group. Then what if you were instead expecting to copy many groups? Then you could shift-click the group arrow, closing all same-level groups. Once they are all closed, you can shift-click the groups to only select group layers, not their contents. This way, both use cases are still quite doable easily with this default choice.
This commit is contained in:
parent
6e92077a1f
commit
9a2f5b0709
12 changed files with 180 additions and 109 deletions
|
|
@ -118,7 +118,7 @@ gimp_edit_cut (GimpImage *image,
|
|||
g_list_free (remove);
|
||||
|
||||
/* Now copy all layers into the clipboard image. */
|
||||
clip_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE);
|
||||
clip_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE, TRUE);
|
||||
gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image));
|
||||
gimp_set_clipboard_image (image->gimp, clip_image);
|
||||
g_object_unref (clip_image);
|
||||
|
|
@ -193,7 +193,7 @@ gimp_edit_copy (GimpImage *image,
|
|||
GimpImage *clip_image;
|
||||
GimpChannel *clip_selection;
|
||||
|
||||
clip_image = gimp_image_new_from_drawables (image->gimp, drawables, TRUE);
|
||||
clip_image = gimp_image_new_from_drawables (image->gimp, drawables, TRUE, TRUE);
|
||||
gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image));
|
||||
gimp_set_clipboard_image (image->gimp, clip_image);
|
||||
g_object_unref (clip_image);
|
||||
|
|
@ -335,6 +335,101 @@ gimp_edit_paste_is_floating (GimpPasteType paste_type,
|
|||
g_return_val_if_reached (FALSE);
|
||||
}
|
||||
|
||||
static GList *
|
||||
gimp_edit_paste_get_tagged_layers (GimpImage *image,
|
||||
GList *layers,
|
||||
GList *returned_layers,
|
||||
const Babl *floating_format,
|
||||
GimpImageBaseType base_type,
|
||||
GimpPrecision precision,
|
||||
GimpPasteType paste_type)
|
||||
{
|
||||
GList *iter;
|
||||
|
||||
for (iter = layers; iter; iter = iter->next)
|
||||
{
|
||||
GimpLayer *layer;
|
||||
GType layer_type;
|
||||
gboolean copied = TRUE;
|
||||
|
||||
switch (paste_type)
|
||||
{
|
||||
case GIMP_PASTE_TYPE_FLOATING:
|
||||
case GIMP_PASTE_TYPE_FLOATING_IN_PLACE:
|
||||
case GIMP_PASTE_TYPE_FLOATING_INTO:
|
||||
case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE:
|
||||
/* when pasting as floating make sure gimp_item_convert()
|
||||
* will turn group layers into normal layers, otherwise use
|
||||
* the same layer type so e.g. text information gets
|
||||
* preserved. See issue #2667.
|
||||
*/
|
||||
if (GIMP_IS_GROUP_LAYER (iter->data))
|
||||
layer_type = GIMP_TYPE_LAYER;
|
||||
else
|
||||
layer_type = G_TYPE_FROM_INSTANCE (iter->data);
|
||||
break;
|
||||
|
||||
case GIMP_PASTE_TYPE_NEW_LAYER:
|
||||
case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE:
|
||||
layer_type = G_TYPE_FROM_INSTANCE (iter->data);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
if (GIMP_IS_GROUP_LAYER (iter->data))
|
||||
copied = (gboolean) GPOINTER_TO_INT (g_object_get_data (G_OBJECT (iter->data),
|
||||
"gimp-image-copied-layer"));
|
||||
if (copied)
|
||||
{
|
||||
layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (iter->data),
|
||||
image, layer_type));
|
||||
returned_layers = g_list_prepend (returned_layers, layer);
|
||||
|
||||
switch (paste_type)
|
||||
{
|
||||
case GIMP_PASTE_TYPE_FLOATING:
|
||||
case GIMP_PASTE_TYPE_FLOATING_IN_PLACE:
|
||||
case GIMP_PASTE_TYPE_FLOATING_INTO:
|
||||
case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE:
|
||||
/* when pasting as floating selection, get rid of the layer mask,
|
||||
* and make sure the layer has the right format
|
||||
*/
|
||||
if (gimp_layer_get_mask (iter->data))
|
||||
gimp_layer_apply_mask (iter->data, GIMP_MASK_DISCARD, FALSE);
|
||||
|
||||
if (gimp_drawable_get_format (GIMP_DRAWABLE (iter->data)) !=
|
||||
floating_format)
|
||||
{
|
||||
gimp_drawable_convert_type (GIMP_DRAWABLE (iter->data), image,
|
||||
base_type, precision,
|
||||
TRUE, NULL, NULL,
|
||||
GEGL_DITHER_NONE, GEGL_DITHER_NONE,
|
||||
FALSE, NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GimpContainer *container;
|
||||
|
||||
container = gimp_viewable_get_children (iter->data);
|
||||
returned_layers = gimp_edit_paste_get_tagged_layers (image,
|
||||
GIMP_LIST (container)->queue->head,
|
||||
returned_layers,
|
||||
floating_format,
|
||||
base_type, precision, paste_type);
|
||||
}
|
||||
}
|
||||
|
||||
return returned_layers;
|
||||
}
|
||||
|
||||
static GList *
|
||||
gimp_edit_paste_get_layers (GimpImage *image,
|
||||
GList *drawables,
|
||||
|
|
@ -367,9 +462,7 @@ gimp_edit_paste_get_layers (GimpImage *image,
|
|||
|
||||
if (GIMP_IS_IMAGE (paste))
|
||||
{
|
||||
GList *iter;
|
||||
|
||||
layers = g_list_copy (gimp_image_get_layer_iter (GIMP_IMAGE (paste)));
|
||||
layers = gimp_image_get_layer_iter (GIMP_IMAGE (paste));
|
||||
|
||||
if (g_list_length (layers) > 1)
|
||||
{
|
||||
|
|
@ -379,68 +472,11 @@ gimp_edit_paste_get_layers (GimpImage *image,
|
|||
*paste_type = GIMP_PASTE_TYPE_NEW_LAYER;
|
||||
}
|
||||
|
||||
for (iter = layers; iter; iter = iter->next)
|
||||
{
|
||||
GType layer_type;
|
||||
|
||||
switch (*paste_type)
|
||||
{
|
||||
case GIMP_PASTE_TYPE_FLOATING:
|
||||
case GIMP_PASTE_TYPE_FLOATING_IN_PLACE:
|
||||
case GIMP_PASTE_TYPE_FLOATING_INTO:
|
||||
case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE:
|
||||
/* when pasting as floating make sure gimp_item_convert()
|
||||
* will turn group layers into normal layers, otherwise use
|
||||
* the same layer type so e.g. text information gets
|
||||
* preserved. See issue #2667.
|
||||
*/
|
||||
if (GIMP_IS_GROUP_LAYER (iter->data))
|
||||
layer_type = GIMP_TYPE_LAYER;
|
||||
else
|
||||
layer_type = G_TYPE_FROM_INSTANCE (iter->data);
|
||||
break;
|
||||
|
||||
case GIMP_PASTE_TYPE_NEW_LAYER:
|
||||
case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE:
|
||||
layer_type = G_TYPE_FROM_INSTANCE (iter->data);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
iter->data = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (iter->data),
|
||||
image, layer_type));
|
||||
|
||||
switch (*paste_type)
|
||||
{
|
||||
case GIMP_PASTE_TYPE_FLOATING:
|
||||
case GIMP_PASTE_TYPE_FLOATING_IN_PLACE:
|
||||
case GIMP_PASTE_TYPE_FLOATING_INTO:
|
||||
case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE:
|
||||
/* when pasting as floating selection, get rid of the layer mask,
|
||||
* and make sure the layer has the right format
|
||||
*/
|
||||
if (gimp_layer_get_mask (iter->data))
|
||||
gimp_layer_apply_mask (iter->data, GIMP_MASK_DISCARD, FALSE);
|
||||
|
||||
if (gimp_drawable_get_format (GIMP_DRAWABLE (iter->data)) !=
|
||||
floating_format)
|
||||
{
|
||||
gimp_drawable_convert_type (GIMP_DRAWABLE (iter->data), image,
|
||||
gimp_drawable_get_base_type (drawables->data),
|
||||
gimp_drawable_get_precision (drawables->data),
|
||||
TRUE,
|
||||
NULL, NULL,
|
||||
GEGL_DITHER_NONE, GEGL_DITHER_NONE,
|
||||
FALSE, NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
layers = gimp_edit_paste_get_tagged_layers (image, layers, NULL, floating_format,
|
||||
gimp_drawable_get_base_type (drawables->data),
|
||||
gimp_drawable_get_precision (drawables->data),
|
||||
*paste_type);
|
||||
layers = g_list_reverse (layers);
|
||||
}
|
||||
else if (GIMP_IS_BUFFER (paste))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -534,7 +534,7 @@ gimp_channel_combine_items (GimpChannel *mask,
|
|||
{
|
||||
GList *merged_layers;
|
||||
|
||||
temp_image = gimp_image_new_from_drawables (image->gimp, items, FALSE);
|
||||
temp_image = gimp_image_new_from_drawables (image->gimp, items, FALSE, FALSE);
|
||||
merged_layers = gimp_image_merge_visible_layers (temp_image,
|
||||
gimp_get_user_context (temp_image->gimp),
|
||||
GIMP_CLIP_TO_IMAGE,
|
||||
|
|
|
|||
|
|
@ -577,7 +577,7 @@ gimp_channel_select_by_color (GimpChannel *channel,
|
|||
}
|
||||
else
|
||||
{
|
||||
sel_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE);
|
||||
sel_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE, FALSE);
|
||||
gimp_container_remove (image->gimp->images, GIMP_OBJECT (sel_image));
|
||||
|
||||
pickable = GIMP_PICKABLE (sel_image);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "gimpchannel.h"
|
||||
#include "gimpcontext.h"
|
||||
#include "gimpdrawable-fill.h"
|
||||
#include "gimpgrouplayer.h"
|
||||
#include "gimpimage.h"
|
||||
#include "gimpimage-color-profile.h"
|
||||
#include "gimpimage-colormap.h"
|
||||
|
|
@ -246,8 +247,12 @@ gimp_image_new_from_drawable (Gimp *gimp,
|
|||
|
||||
/**
|
||||
* gimp_image_new_copy_drawables:
|
||||
* @image:
|
||||
* @drawables: the drawables to insert into @image.
|
||||
* @image: the image where @drawables belong to.
|
||||
* @drawables: the drawables to copy into @new_image.
|
||||
* @new_image: the image to insert to.
|
||||
* @tag_copies: tag copies of @drawable with "gimp-image-copied-layer".
|
||||
* @copied_drawables:
|
||||
* @tagged_drawables:
|
||||
* @parent:
|
||||
* @new_parent:
|
||||
*
|
||||
|
|
@ -259,13 +264,17 @@ gimp_image_new_from_drawable (Gimp *gimp,
|
|||
* full opacity and default layer mode. Otherwise, visibility, opacity
|
||||
* and layer mode will be copied as-is, allowing proper compositing.
|
||||
*
|
||||
* The @parent and @new_parent arguments are only used internally for
|
||||
* recursive calls and must be set to NULL for the initial call.
|
||||
* The @copied_drawables, @tagged_drawables, @parent and @new_parent arguments
|
||||
* are only used internally for recursive calls and must be set to NULL for the
|
||||
* initial call.
|
||||
*/
|
||||
static void
|
||||
gimp_image_new_copy_drawables (GimpImage *image,
|
||||
GList *drawables,
|
||||
GimpImage *new_image,
|
||||
gboolean tag_copies,
|
||||
GList *copied_drawables,
|
||||
GList *tagged_drawables,
|
||||
GimpLayer *parent,
|
||||
GimpLayer *new_parent)
|
||||
{
|
||||
|
|
@ -277,26 +286,32 @@ gimp_image_new_copy_drawables (GimpImage *image,
|
|||
n_drawables = g_list_length (drawables);
|
||||
if (parent == NULL)
|
||||
{
|
||||
if (n_drawables == 1)
|
||||
{
|
||||
layers = drawables;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Root layers. */
|
||||
layers = gimp_image_get_layer_iter (image);
|
||||
/* Root layers. */
|
||||
layers = gimp_image_get_layer_iter (image);
|
||||
|
||||
/* Add any item parent. */
|
||||
drawables = g_list_copy (drawables);
|
||||
for (iter = drawables; iter; iter = iter->next)
|
||||
{
|
||||
GimpItem *item = iter->data;
|
||||
while ((item = gimp_item_get_parent (item)))
|
||||
{
|
||||
if (! g_list_find (drawables, item))
|
||||
drawables = g_list_prepend (drawables, item);
|
||||
}
|
||||
}
|
||||
copied_drawables = g_list_copy (drawables);
|
||||
for (iter = copied_drawables; iter; iter = iter->next)
|
||||
{
|
||||
/* Tagged drawables are the explicitly copied drawables which have no
|
||||
* explicitly copied descendant items.
|
||||
*/
|
||||
GList *iter2;
|
||||
|
||||
for (iter2 = iter; iter2; iter2 = iter2->next)
|
||||
if (gimp_viewable_is_ancestor (iter->data, iter2->data))
|
||||
break;
|
||||
|
||||
if (iter2 == NULL)
|
||||
tagged_drawables = g_list_prepend (tagged_drawables, iter->data);
|
||||
}
|
||||
|
||||
/* Add any item parent. */
|
||||
for (iter = copied_drawables; iter; iter = iter->next)
|
||||
{
|
||||
GimpItem *item = iter->data;
|
||||
while ((item = gimp_item_get_parent (item)))
|
||||
if (! g_list_find (copied_drawables, item))
|
||||
copied_drawables = g_list_prepend (copied_drawables, item);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -310,18 +325,31 @@ gimp_image_new_copy_drawables (GimpImage *image,
|
|||
index = 0;
|
||||
for (iter = layers; iter; iter = iter->next)
|
||||
{
|
||||
if (g_list_find (drawables, iter->data))
|
||||
if (g_list_find (copied_drawables, iter->data))
|
||||
{
|
||||
GimpLayer *new_layer;
|
||||
GType new_type;
|
||||
gboolean is_group;
|
||||
gboolean is_tagged;
|
||||
|
||||
if (GIMP_IS_LAYER (iter->data))
|
||||
new_type = G_TYPE_FROM_INSTANCE (iter->data);
|
||||
else
|
||||
new_type = GIMP_TYPE_LAYER;
|
||||
|
||||
new_layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (iter->data),
|
||||
new_image, new_type));
|
||||
is_group = (gimp_viewable_get_children (iter->data) != NULL);
|
||||
is_tagged = (g_list_find (tagged_drawables, iter->data) != NULL);
|
||||
|
||||
if (is_group && ! is_tagged)
|
||||
new_layer = gimp_group_layer_new (new_image);
|
||||
else
|
||||
new_layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (iter->data),
|
||||
new_image, new_type));
|
||||
|
||||
if (tag_copies && is_tagged)
|
||||
g_object_set_data (G_OBJECT (new_layer),
|
||||
"gimp-image-copied-layer",
|
||||
GINT_TO_POINTER (TRUE));
|
||||
|
||||
gimp_object_set_name (GIMP_OBJECT (new_layer),
|
||||
gimp_object_get_name (iter->data));
|
||||
|
|
@ -349,19 +377,25 @@ gimp_image_new_copy_drawables (GimpImage *image,
|
|||
gimp_image_add_layer (new_image, new_layer, new_parent, index++, TRUE);
|
||||
|
||||
/* If a group, loop through children. */
|
||||
if (n_drawables > 1 && gimp_viewable_get_children (iter->data))
|
||||
gimp_image_new_copy_drawables (image, drawables, new_image, iter->data, new_layer);
|
||||
if (is_group && ! is_tagged)
|
||||
gimp_image_new_copy_drawables (image, drawables, new_image, tag_copies,
|
||||
copied_drawables, tagged_drawables,
|
||||
iter->data, new_layer);
|
||||
}
|
||||
}
|
||||
|
||||
if (parent == NULL && n_drawables != 1)
|
||||
g_list_free (drawables);
|
||||
if (parent == NULL)
|
||||
{
|
||||
g_list_free (copied_drawables);
|
||||
g_list_free (tagged_drawables);
|
||||
}
|
||||
}
|
||||
|
||||
GimpImage *
|
||||
gimp_image_new_from_drawables (Gimp *gimp,
|
||||
GList *drawables,
|
||||
gboolean copy_selection)
|
||||
gboolean copy_selection,
|
||||
gboolean tag_copies)
|
||||
{
|
||||
GimpImage *image = NULL;
|
||||
GimpImage *new_image;
|
||||
|
|
@ -426,7 +460,7 @@ gimp_image_new_from_drawables (Gimp *gimp,
|
|||
}
|
||||
}
|
||||
|
||||
gimp_image_new_copy_drawables (image, drawables, new_image, NULL, NULL);
|
||||
gimp_image_new_copy_drawables (image, drawables, new_image, tag_copies, NULL, NULL, NULL, NULL);
|
||||
gimp_image_undo_enable (new_image);
|
||||
|
||||
return new_image;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ GimpImage * gimp_image_new_from_drawable (Gimp *gimp,
|
|||
GimpDrawable *drawable);
|
||||
GimpImage * gimp_image_new_from_drawables (Gimp *gimp,
|
||||
GList *drawables,
|
||||
gboolean copy_selection);
|
||||
gboolean copy_selection,
|
||||
gboolean tag_copies);
|
||||
GimpImage * gimp_image_new_from_component (Gimp *gimp,
|
||||
GimpImage *image,
|
||||
GimpChannelType component);
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ gimp_image_pick_color (GimpImage *image,
|
|||
}
|
||||
else /* length > 1 */
|
||||
{
|
||||
pick_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE);
|
||||
pick_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE, FALSE);
|
||||
gimp_container_remove (image->gimp->images, GIMP_OBJECT (pick_image));
|
||||
|
||||
if (! show_all)
|
||||
|
|
|
|||
|
|
@ -708,7 +708,7 @@ gimp_selection_extract (GimpSelection *selection,
|
|||
for (iter = pickables; iter; iter = iter->next)
|
||||
g_return_val_if_fail (GIMP_IS_DRAWABLE (iter->data), NULL);
|
||||
|
||||
temp_image = gimp_image_new_from_drawables (image->gimp, pickables, TRUE);
|
||||
temp_image = gimp_image_new_from_drawables (image->gimp, pickables, TRUE, FALSE);
|
||||
selection = GIMP_SELECTION (gimp_image_get_mask (temp_image));
|
||||
|
||||
pickable = GIMP_PICKABLE (temp_image);
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ file_pat_image_to_pattern (GimpImage *image,
|
|||
for (gint i = 0; i < n_drawables; i++)
|
||||
drawable_list = g_list_prepend (drawable_list, drawables[i]);
|
||||
|
||||
subimage = gimp_image_new_from_drawables (image->gimp, drawable_list, FALSE);
|
||||
subimage = gimp_image_new_from_drawables (image->gimp, drawable_list, FALSE, FALSE);
|
||||
g_list_free (drawable_list);
|
||||
gimp_container_remove (image->gimp->images, GIMP_OBJECT (subimage));
|
||||
gimp_image_resize_to_layers (subimage, context,
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ gimp_perspective_clone_paint (GimpPaintCore *paint_core,
|
|||
{
|
||||
/* A composited image of the drawables */
|
||||
del_image = gimp_image_new_from_drawables (src_image->gimp, drawables,
|
||||
FALSE);
|
||||
FALSE, FALSE);
|
||||
gimp_container_remove (src_image->gimp->images, GIMP_OBJECT (del_image));
|
||||
|
||||
src_pickable = GIMP_PICKABLE (del_image);
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ gimp_source_options_make_pickable (GimpSourceOptions *options)
|
|||
* to these drawables.
|
||||
*/
|
||||
options->src_image = gimp_image_new_from_drawables (image->gimp, options->src_drawables,
|
||||
FALSE);
|
||||
FALSE, FALSE);
|
||||
gimp_container_remove (image->gimp->images, GIMP_OBJECT (options->src_image));
|
||||
|
||||
options->src_pickable = GIMP_PICKABLE (options->src_image);
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ gimp_by_color_select_tool_get_mask (GimpRegionSelectTool *region_select,
|
|||
}
|
||||
else
|
||||
{
|
||||
select_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE);
|
||||
select_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE, FALSE);
|
||||
gimp_container_remove (image->gimp->images, GIMP_OBJECT (select_image));
|
||||
|
||||
pickable = GIMP_PICKABLE (select_image);
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ gimp_fuzzy_select_tool_get_mask (GimpRegionSelectTool *region_select,
|
|||
}
|
||||
else
|
||||
{
|
||||
select_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE);
|
||||
select_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE, FALSE);
|
||||
gimp_container_remove (image->gimp->images, GIMP_OBJECT (select_image));
|
||||
|
||||
pickable = GIMP_PICKABLE (select_image);
|
||||
|
|
|
|||
Loading…
Reference in a new issue