From 143496af228f7a01f6f90675049f7fdc0e9d6db6 Mon Sep 17 00:00:00 2001 From: Jehan Date: Sat, 12 Nov 2022 22:17:00 +0100 Subject: [PATCH] app, menus, pdb: new "Paste as Single Layer( in Place)?" actions. When the clipboard contains raw image data or single layers, it's the same as the normal "Paste" (and "Paste In Place" respectively). These actions are useful if you want to copy a bunch of layers and paste them "merged" into a single layers (since now the copy-paste of multiple layers will create multiple layers). It is somehow similar to the "Copy Visible" action except that it works on selected layers only and work at paste time, making the action more versatile. --- app/actions/buffers-commands.c | 1 + app/actions/edit-actions.c | 13 +++++++ app/actions/edit-commands.c | 31 ++++++++++------ app/core/core-enums.c | 4 +++ app/core/core-enums.h | 4 ++- app/core/gimp-edit.c | 57 +++++++++++++++++++++++++++++- app/core/gimp-edit.h | 2 ++ app/display/gimpdisplayshell-dnd.c | 8 +++-- app/pdb/edit-cmds.c | 2 ++ menus/image-menu.xml.in | 2 ++ pdb/groups/edit.pdb | 2 ++ 11 files changed, 111 insertions(+), 15 deletions(-) diff --git a/app/actions/buffers-commands.c b/app/actions/buffers-commands.c index 367aba7ff9..98bac107ed 100644 --- a/app/actions/buffers-commands.c +++ b/app/actions/buffers-commands.c @@ -93,6 +93,7 @@ buffers_paste_cmd_callback (GimpAction *action, g_list_free (gimp_edit_paste (image, g_list_length (drawables) == 1 ? drawables->data : NULL, GIMP_OBJECT (buffer), paste_type, + context, FALSE, x, y, width, height)); gimp_image_flush (image); diff --git a/app/actions/edit-actions.c b/app/actions/edit-actions.c index 0cb8a07417..f7fb41446c 100644 --- a/app/actions/edit-actions.c +++ b/app/actions/edit-actions.c @@ -178,6 +178,19 @@ static const GimpEnumActionEntry edit_paste_actions[] = GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE, FALSE, GIMP_HELP_EDIT_PASTE_IN_PLACE }, + { "edit-paste-merged", GIMP_ICON_EDIT_PASTE, + NC_("edit-action", "_Paste as Single Layer"), NULL, + NC_("edit-action", "Paste the content of the clipboard as a single layer"), + GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING, FALSE, + GIMP_HELP_EDIT_PASTE }, + + { "edit-paste-merged-in-place", GIMP_ICON_EDIT_PASTE, + NC_("edit-action", "Paste as Single Layer In P_lace"), NULL, + NC_("edit-action", + "Paste the content of the clipboard at its original position as a single layer"), + GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE, FALSE, + GIMP_HELP_EDIT_PASTE_IN_PLACE }, + { "edit-paste-into", GIMP_ICON_EDIT_PASTE_INTO, NC_("edit-action", "Paste _Into Selection"), NULL, NC_("edit-action", diff --git a/app/actions/edit-commands.c b/app/actions/edit-commands.c index d358b3cd0e..81b9385c70 100644 --- a/app/actions/edit-commands.c +++ b/app/actions/edit-commands.c @@ -69,6 +69,7 @@ static gboolean check_drawable_alpha (GimpDrawable *drawable, gpointer data); static void edit_paste (GimpDisplay *display, GimpPasteType paste_type, + gboolean merged, gboolean try_svg); static void cut_named_buffer_callback (GtkWidget *widget, const gchar *name, @@ -346,6 +347,7 @@ edit_paste_cmd_callback (GimpAction *action, GimpPasteType paste_type = (GimpPasteType) g_variant_get_int32 (value); GimpPasteType converted_type; GList *drawables; + gboolean merged = FALSE; return_if_no_image (image, data); @@ -367,14 +369,17 @@ edit_paste_cmd_callback (GimpAction *action, case GIMP_PASTE_TYPE_FLOATING_IN_PLACE: case GIMP_PASTE_TYPE_FLOATING_INTO: case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE: - edit_paste (display, paste_type, TRUE); + edit_paste (display, paste_type, merged, TRUE); break; case GIMP_PASTE_TYPE_NEW_LAYER: case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE: - edit_paste (display, paste_type, FALSE); + edit_paste (display, paste_type, merged, FALSE); break; + case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING: + case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE: + merged = TRUE; case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING: case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE: drawables = gimp_image_get_selected_drawables (image); @@ -383,19 +388,21 @@ edit_paste_cmd_callback (GimpAction *action, (g_list_length (drawables) == 1) && GIMP_IS_LAYER_MASK (drawables->data)) { - converted_type = (paste_type == GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING) ? + converted_type = (paste_type == GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING || + paste_type == GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING) ? GIMP_PASTE_TYPE_FLOATING : GIMP_PASTE_TYPE_FLOATING_IN_PLACE; - edit_paste (display, converted_type, TRUE); + edit_paste (display, converted_type, merged, TRUE); } else { - converted_type = (paste_type == GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING) ? + converted_type = (paste_type == GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING || + paste_type == GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING) ? GIMP_PASTE_TYPE_NEW_LAYER : GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE; - edit_paste (display, converted_type, FALSE); + edit_paste (display, converted_type, merged, FALSE); } g_list_free (drawables); @@ -627,13 +634,16 @@ check_drawable_alpha (GimpDrawable *drawable, static void edit_paste (GimpDisplay *display, GimpPasteType paste_type, + gboolean merged, gboolean try_svg) { GimpImage *image = gimp_display_get_image (display); GimpObject *paste; - g_return_if_fail (paste_type != GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING && - paste_type != GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE); + g_return_if_fail (paste_type != GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING && + paste_type != GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE && + paste_type != GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING && + paste_type != GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE); if (try_svg) { @@ -702,8 +712,9 @@ edit_paste (GimpDisplay *display, ! gimp_display_shell_get_infinite_canvas (shell), &x, &y, &width, &height); - if ((pasted_layers = gimp_edit_paste (image, drawables, paste, - paste_type, x, y, width, height))) + if ((pasted_layers = gimp_edit_paste (image, drawables, paste, paste_type, + gimp_get_user_context (display->gimp), + merged, x, y, width, height))) { gimp_image_set_selected_layers (image, pasted_layers); g_list_free (pasted_layers); diff --git a/app/core/core-enums.c b/app/core/core-enums.c index 57fc28c764..6811c6ad37 100644 --- a/app/core/core-enums.c +++ b/app/core/core-enums.c @@ -950,6 +950,8 @@ gimp_paste_type_get_type (void) { GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE, "GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE", "new-layer-in-place" }, { GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING, "GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING", "new-layer-or-floating" }, { GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE, "GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE", "new-layer-or-floating-in-place" }, + { GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING, "GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING", "new-merged-layer-or-floating" }, + { GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE, "GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE", "new-merged-layer-or-floating-in-place" }, { 0, NULL, NULL } }; @@ -963,6 +965,8 @@ gimp_paste_type_get_type (void) { GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE, "GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE", NULL }, { GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING, "GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING", NULL }, { GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE, "GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE", NULL }, + { GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING, "GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING", NULL }, + { GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE, "GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE", NULL }, { 0, NULL, NULL } }; diff --git a/app/core/core-enums.h b/app/core/core-enums.h index 3db5ad7238..a50b087cae 100644 --- a/app/core/core-enums.h +++ b/app/core/core-enums.h @@ -437,7 +437,9 @@ typedef enum /*< pdb-skip >*/ GIMP_PASTE_TYPE_NEW_LAYER, GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE, GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING, - GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE + GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE, + GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING, + GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE, } GimpPasteType; diff --git a/app/core/gimp-edit.c b/app/core/gimp-edit.c index 313ff04611..8a41fce775 100644 --- a/app/core/gimp-edit.c +++ b/app/core/gimp-edit.c @@ -34,6 +34,7 @@ #include "gimpgrouplayer.h" #include "gimpimage.h" #include "gimpimage-duplicate.h" +#include "gimpimage-merge.h" #include "gimpimage-new.h" #include "gimpimage-undo.h" #include "gimplayer-floating-selection.h" @@ -296,12 +297,14 @@ gimp_edit_paste_is_in_place (GimpPasteType paste_type) case GIMP_PASTE_TYPE_FLOATING_INTO: case GIMP_PASTE_TYPE_NEW_LAYER: case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING: + case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING: return FALSE; case GIMP_PASTE_TYPE_FLOATING_IN_PLACE: case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE: case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE: case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE: + case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE: return TRUE; } @@ -326,6 +329,8 @@ gimp_edit_paste_is_floating (GimpPasteType paste_type, case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING: case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE: + case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING: + case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE: if (GIMP_IS_LAYER_MASK (drawable)) return TRUE; else @@ -775,6 +780,10 @@ gimp_edit_paste_paste (GimpImage *image, gimp_image_add_layer (image, iter->data, parent, position, TRUE); } break; + + case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING: + case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE: + g_return_val_if_reached (NULL); } } @@ -788,6 +797,8 @@ gimp_edit_paste (GimpImage *image, GList *drawables, GimpObject *paste, GimpPasteType paste_type, + GimpContext *context, + gboolean merged, gint viewport_x, gint viewport_y, gint viewport_width, @@ -809,7 +820,51 @@ gimp_edit_paste (GimpImage *image, g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (iter->data)), NULL); } - layers = gimp_edit_paste_get_layers (image, drawables, paste, &paste_type); + if (merged && GIMP_IS_IMAGE (paste)) + { + GimpImage *tmp_image; + + tmp_image = gimp_image_duplicate (GIMP_IMAGE (paste)); + gimp_container_remove (image->gimp->images, GIMP_OBJECT (tmp_image)); + gimp_image_merge_visible_layers (tmp_image, context, GIMP_EXPAND_AS_NECESSARY, + FALSE, FALSE, NULL); + layers = g_list_copy (gimp_image_get_layer_iter (tmp_image)); + + /* The merge process should ensure that we get a single non-group and + * no-mask layer. + */ + g_return_val_if_fail (g_list_length (layers) == 1, NULL); + + layers->data = gimp_item_convert (GIMP_ITEM (layers->data), image, + G_TYPE_FROM_INSTANCE (layers->data)); + + 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: + if (gimp_drawable_get_format (GIMP_DRAWABLE (layers->data)) != + gimp_drawable_get_format_with_alpha (GIMP_DRAWABLE (layers->data))) + { + gimp_drawable_convert_type (GIMP_DRAWABLE (layers->data), image, + gimp_drawable_get_base_type (layers->data), + gimp_drawable_get_precision (layers->data), + TRUE, NULL, NULL, + GEGL_DITHER_NONE, GEGL_DITHER_NONE, + FALSE, NULL); + } + break; + + default: + break; + } + g_object_unref (tmp_image); + } + else + { + layers = gimp_edit_paste_get_layers (image, drawables, paste, &paste_type); + } if (! layers) return NULL; diff --git a/app/core/gimp-edit.h b/app/core/gimp-edit.h index b37a083978..501c07c3f2 100644 --- a/app/core/gimp-edit.h +++ b/app/core/gimp-edit.h @@ -35,6 +35,8 @@ GList * gimp_edit_paste (GimpImage *image, GList *drawables, GimpObject *paste, GimpPasteType paste_type, + GimpContext *context, + gboolean merged, gint viewport_x, gint viewport_y, gint viewport_width, diff --git a/app/display/gimpdisplayshell-dnd.c b/app/display/gimpdisplayshell-dnd.c index ce8a2bed91..e22f681a46 100644 --- a/app/display/gimpdisplayshell-dnd.c +++ b/app/display/gimpdisplayshell-dnd.c @@ -459,6 +459,7 @@ gimp_display_shell_drop_buffer (GtkWidget *widget, { GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); GimpImage *image = gimp_display_get_image (shell->display); + GimpContext *context; GList *drawables; GimpBuffer *buffer; GimpPasteType paste_type; @@ -482,8 +483,8 @@ gimp_display_shell_drop_buffer (GtkWidget *widget, paste_type = GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING; drawables = gimp_image_get_selected_drawables (image); - - buffer = GIMP_BUFFER (viewable); + context = gimp_get_user_context (shell->display->gimp); + buffer = GIMP_BUFFER (viewable); gimp_display_shell_untransform_viewport ( shell, @@ -493,7 +494,8 @@ gimp_display_shell_drop_buffer (GtkWidget *widget, /* FIXME: popup a menu for selecting "Paste Into" */ g_list_free (gimp_edit_paste (image, drawables, GIMP_OBJECT (buffer), - paste_type, x, y, width, height)); + paste_type, context, FALSE, + x, y, width, height)); g_list_free (drawables); gimp_display_shell_dnd_flush (shell, image); diff --git a/app/pdb/edit-cmds.c b/app/pdb/edit-cmds.c index f027e5076e..7b460e0877 100644 --- a/app/pdb/edit-cmds.c +++ b/app/pdb/edit-cmds.c @@ -278,6 +278,7 @@ edit_paste_invoker (GimpProcedure *procedure, paste_into ? GIMP_PASTE_TYPE_FLOATING_INTO : GIMP_PASTE_TYPE_FLOATING, + context, FALSE, -1, -1, -1, -1); g_list_free (drawables); @@ -595,6 +596,7 @@ edit_named_paste_invoker (GimpProcedure *procedure, paste_into ? GIMP_PASTE_TYPE_FLOATING_INTO : GIMP_PASTE_TYPE_FLOATING, + context, FALSE, -1, -1, -1, -1); g_list_free (drawables); diff --git a/menus/image-menu.xml.in b/menus/image-menu.xml.in index b6043974ed..81ef18ae87 100644 --- a/menus/image-menu.xml.in +++ b/menus/image-menu.xml.in @@ -197,6 +197,8 @@ + + diff --git a/pdb/groups/edit.pdb b/pdb/groups/edit.pdb index 7ed7dc029b..b7699f099d 100644 --- a/pdb/groups/edit.pdb +++ b/pdb/groups/edit.pdb @@ -287,6 +287,7 @@ HELP paste_into ? GIMP_PASTE_TYPE_FLOATING_INTO : GIMP_PASTE_TYPE_FLOATING, + context, FALSE, -1, -1, -1, -1); g_list_free (drawables); @@ -614,6 +615,7 @@ HELP paste_into ? GIMP_PASTE_TYPE_FLOATING_INTO : GIMP_PASTE_TYPE_FLOATING, + context, FALSE, -1, -1, -1, -1); g_list_free (drawables);