app, pdb: layers-merge-down action now multi-layer aware.

When several layers are selected, each layer will merge down with the
layer below it. This is similar to running Merge Down several times, one
for each selected layer.
This commit is contained in:
Jehan 2020-06-30 23:29:05 +02:00
parent 5cafcbe920
commit 955aecab92
6 changed files with 166 additions and 119 deletions

View file

@ -173,7 +173,7 @@ static const GimpActionEntry layers_actions[] =
{ "layers-merge-down", GIMP_ICON_LAYER_MERGE_DOWN,
NC_("layers-action", "Merge Do_wn"), NULL,
NC_("layers-action", "Merge this layer with the first visible layer below it"),
NC_("layers-action", "Merge these layers with the first visible layer below each"),
layers_merge_down_cmd_callback,
GIMP_HELP_LAYER_MERGE_DOWN },
@ -182,7 +182,7 @@ static const GimpActionEntry layers_actions[] =
*/
{ "layers-merge-down-button", GIMP_ICON_LAYER_MERGE_DOWN,
NC_("layers-action", "Merge Do_wn"), NULL,
NC_("layers-action", "Merge this layer with the first visible layer below it"),
NC_("layers-action", "Merge these layers with the first visible layer below each"),
layers_merge_down_cmd_callback,
GIMP_HELP_LAYER_MERGE_DOWN },
@ -774,14 +774,12 @@ layers_actions_update (GimpActionGroup *group,
gboolean lock_alpha = TRUE;
gboolean can_lock_alpha = FALSE;
gboolean text_layer = FALSE;
gboolean visible = FALSE;
gboolean writable = FALSE;
gboolean movable = FALSE;
gboolean children = FALSE;
gboolean bs_mutable = FALSE; /* At least 1 selected layers' blend space is mutable. */
gboolean cs_mutable = FALSE; /* At least 1 selected layers' composite space is mutable. */
gboolean cm_mutable = FALSE; /* At least 1 selected layers' composite mode is mutable. */
GList *next_visible = NULL;
gboolean next_mode = TRUE;
gboolean prev_mode = TRUE;
gboolean last_mode = FALSE;
@ -795,6 +793,8 @@ layers_actions_update (GimpActionGroup *group,
gboolean have_prev = FALSE; /* At least 1 selected layer has a previous sibling. */
gboolean have_next = FALSE; /* At least 1 selected layer has a next sibling. */
gboolean all_visible = TRUE;
gboolean all_next_visible = TRUE;
gboolean all_masks_shown = TRUE;
gboolean all_masks_disabled = TRUE;
@ -872,11 +872,33 @@ layers_actions_update (GimpActionGroup *group,
if (iter2)
{
GList *next_visible;
if (g_list_previous (iter2))
have_prev = TRUE;
if (g_list_next (iter2))
have_next = TRUE;
for (next_visible = g_list_next (iter2);
next_visible;
next_visible = g_list_next (next_visible))
{
if (gimp_item_get_visible (next_visible->data))
{
/* "next_visible" is actually "next_visible" and
* "writable" and "not group"
*/
if (gimp_item_is_content_locked (next_visible->data) ||
gimp_viewable_get_children (next_visible->data))
next_visible = NULL;
break;
}
}
if (! next_visible)
all_next_visible = FALSE;
}
if (gimp_layer_mode_is_blend_space_mutable (mode))
@ -886,23 +908,25 @@ layers_actions_update (GimpActionGroup *group,
if (gimp_layer_mode_is_composite_mode_mutable (mode))
cm_mutable = TRUE;
if (have_masks && have_no_masks &&
have_groups && have_no_groups &&
have_writable && ! all_masks_shown &&
! all_masks_disabled &&
! lock_alpha && can_lock_alpha &&
! prev_mode && ! next_mode &&
have_prev && have_next &&
bs_mutable && cs_mutable && cm_mutable)
if (! gimp_item_get_visible (iter->data))
all_visible = FALSE;
if (have_masks && have_no_masks &&
have_groups && have_no_groups &&
have_writable && ! all_masks_shown &&
! all_masks_disabled &&
! lock_alpha && can_lock_alpha &&
! prev_mode && ! next_mode &&
have_prev && have_next &&
bs_mutable && cs_mutable && cm_mutable &&
! all_visible && ! all_next_visible)
break;
}
if (n_layers == 1)
{
/* Special unique layer case. */
const gchar *action = NULL;
GList *layer_list;
GList *list;
const gchar *action = NULL;
layer = layers->data;
switch (gimp_layer_get_blend_space (layer))
@ -953,37 +977,12 @@ layers_actions_update (GimpActionGroup *group,
mask = gimp_layer_get_mask (layer);
alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
visible = gimp_item_get_visible (GIMP_ITEM (layer));
writable = ! gimp_item_is_content_locked (GIMP_ITEM (layer));
movable = ! gimp_item_is_position_locked (GIMP_ITEM (layer));
if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
children = TRUE;
layer_list = gimp_item_get_container_iter (GIMP_ITEM (layer));
list = g_list_find (layer_list, layer);
if (list)
{
for (next_visible = g_list_next (list);
next_visible;
next_visible = g_list_next (next_visible))
{
if (gimp_item_get_visible (next_visible->data))
{
/* "next_visible" is actually "next_visible" and
* "writable" and "not group"
*/
if (gimp_item_is_content_locked (next_visible->data) ||
gimp_viewable_get_children (next_visible->data))
next_visible = NULL;
break;
}
}
}
text_layer = gimp_item_is_text_layer (GIMP_ITEM (layer));
}
}
@ -1037,9 +1036,9 @@ layers_actions_update (GimpActionGroup *group,
SET_VISIBLE ("layers-anchor", fs && !ac);
SET_VISIBLE ("layers-merge-down", !fs);
SET_SENSITIVE ("layers-merge-down", layer && !fs && !ac && visible && next_visible);
SET_SENSITIVE ("layers-merge-down", n_layers > 0 && !fs && !ac && all_visible && all_next_visible);
SET_VISIBLE ("layers-merge-down-button", !fs);
SET_SENSITIVE ("layers-merge-down-button", layer && !fs && !ac);
SET_SENSITIVE ("layers-merge-down-button", n_layers > 0 && !fs && !ac);
SET_VISIBLE ("layers-merge-group", have_groups);
SET_SENSITIVE ("layers-merge-group", n_layers && !fs && !ac && have_groups);
SET_SENSITIVE ("layers-merge-layers", n_layers > 0 && !fs && !ac);

View file

@ -822,14 +822,28 @@ layers_merge_down_cmd_callback (GimpAction *action,
gpointer data)
{
GimpImage *image;
GimpLayer *layer;
GList *layers;
GimpDisplay *display;
return_if_no_layer (image, layer, data);
GError *error = NULL;
return_if_no_layers (image, layers, data);
return_if_no_display (display, data);
gimp_image_merge_down (image, layer, action_data_get_context (data),
GIMP_EXPAND_AS_NECESSARY,
GIMP_PROGRESS (display), NULL);
layers = gimp_image_merge_down (image, layers, action_data_get_context (data),
GIMP_EXPAND_AS_NECESSARY,
GIMP_PROGRESS (display), &error);
if (error)
{
gimp_message_literal (image->gimp,
G_OBJECT (display), GIMP_MESSAGE_WARNING,
error->message);
g_clear_error (&error);
return;
}
gimp_image_set_selected_layers (image, layers);
g_list_free (layers);
gimp_image_flush (image);
}

View file

@ -264,86 +264,98 @@ gimp_image_flatten (GimpImage *image,
return NULL;
}
GimpLayer *
GList *
gimp_image_merge_down (GimpImage *image,
GimpLayer *current_layer,
GList *layers,
GimpContext *context,
GimpMergeType merge_type,
GimpProgress *progress,
GError **error)
{
GimpLayer *layer;
GList *merged_layers = NULL;
GList *list;
GList *layer_list = NULL;
GSList *merge_list = NULL;
GList *merge_lists = NULL;
GSList *merge_list;
const gchar *undo_desc;
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (GIMP_IS_LAYER (current_layer), NULL);
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (current_layer)), NULL);
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (gimp_layer_is_floating_sel (current_layer))
for (list = layers; list; list = list->next)
{
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("Cannot merge down a floating selection."));
return NULL;
}
GList *list2;
GList *layer_list = NULL;
if (! gimp_item_get_visible (GIMP_ITEM (current_layer)))
{
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("Cannot merge down an invisible layer."));
return NULL;
}
g_return_val_if_fail (GIMP_IS_LAYER (list->data), NULL);
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (list->data)), NULL);
for (list = gimp_item_get_container_iter (GIMP_ITEM (current_layer));
list;
list = g_list_next (list))
{
layer = list->data;
if (layer == current_layer)
break;
}
for (layer_list = g_list_next (list);
layer_list;
layer_list = g_list_next (layer_list))
{
layer = layer_list->data;
if (gimp_item_get_visible (GIMP_ITEM (layer)))
if (gimp_layer_is_floating_sel (list->data))
{
if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
{
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("Cannot merge down to a layer group."));
return NULL;
}
if (gimp_item_is_content_locked (GIMP_ITEM (layer)))
{
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("The layer to merge down to is locked."));
return NULL;
}
merge_list = g_slist_append (NULL, layer);
break;
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("Cannot merge down a floating selection."));
return NULL;
}
}
if (! merge_list)
{
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("There is no visible layer to merge down to."));
return NULL;
}
if (! gimp_item_get_visible (GIMP_ITEM (list->data)))
{
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("Cannot merge down an invisible layer."));
return NULL;
}
merge_list = g_slist_prepend (merge_list, current_layer);
for (list2 = gimp_item_get_container_iter (GIMP_ITEM (list->data));
list2;
list2 = g_list_next (list2))
{
layer = list2->data;
if (layer == list->data)
break;
}
merge_list = NULL;
for (layer_list = g_list_next (list2);
layer_list;
layer_list = g_list_next (layer_list))
{
layer = layer_list->data;
if (gimp_item_get_visible (GIMP_ITEM (layer)))
{
if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
{
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("Cannot merge down to a layer group."));
return NULL;
}
if (gimp_item_is_content_locked (GIMP_ITEM (layer)))
{
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("The layer to merge down to is locked."));
return NULL;
}
merge_list = g_slist_append (NULL, layer);
break;
}
}
if (! merge_list)
{
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("There is no visible layer to merge down to."));
return NULL;
}
merge_list = g_slist_prepend (merge_list, list->data);
merge_lists = g_list_prepend (merge_lists, merge_list);
}
undo_desc = C_("undo-type", "Merge Down");
@ -353,17 +365,23 @@ gimp_image_merge_down (GimpImage *image,
GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE,
undo_desc);
layer = gimp_image_merge_layers (image,
gimp_item_get_container (GIMP_ITEM (current_layer)),
merge_list, context, merge_type,
undo_desc, progress);
g_slist_free (merge_list);
for (list = merge_lists; list; list = list->next)
{
merge_list = list->data;
layer = gimp_image_merge_layers (image,
gimp_item_get_container (merge_list->data),
merge_list, context, merge_type,
undo_desc, progress);
merged_layers = g_list_prepend (merged_layers, layer);
}
g_list_free_full (merge_lists, (GDestroyNotify) g_slist_free);
gimp_image_undo_group_end (image);
gimp_unset_busy (image->gimp);
return layer;
return merged_layers;
}
GimpLayer *

View file

@ -25,8 +25,8 @@ GList * gimp_image_merge_visible_layers (GimpImage *image,
gboolean merge_active_group,
gboolean discard_invisible,
GimpProgress *progress);
GimpLayer * gimp_image_merge_down (GimpImage *image,
GimpLayer *current_layer,
GList * gimp_image_merge_down (GimpImage *image,
GList *layers,
GimpContext *context,
GimpMergeType merge_type,
GimpProgress *progress,

View file

@ -1468,11 +1468,19 @@ image_merge_down_invoker (GimpProcedure *procedure,
{
if (gimp_pdb_item_is_attached (GIMP_ITEM (merge_layer), image, 0, error))
{
layer = gimp_image_merge_down (image, merge_layer, context, merge_type,
progress, error);
GList *merge_layers = g_list_prepend (NULL, merge_layer);
GList *layers;
if (! layer)
layers = gimp_image_merge_down (image, merge_layers, context,
merge_type, progress, error);
g_list_free (merge_layers);
if (! layers)
success = FALSE;
else
layer = layers->data;
g_list_free (layers);
}
else
success = FALSE;

View file

@ -826,11 +826,19 @@ HELP
{
if (gimp_pdb_item_is_attached (GIMP_ITEM (merge_layer), image, 0, error))
{
layer = gimp_image_merge_down (image, merge_layer, context, merge_type,
progress, error);
GList *merge_layers = g_list_prepend (NULL, merge_layer);
GList *layers;
if (! layer)
layers = gimp_image_merge_down (image, merge_layers, context,
merge_type, progress, error);
g_list_free (merge_layers);
if (! layers)
success = FALSE;
else
layer = layers->data;
g_list_free (layers);
}
else
success = FALSE;