[gimp] app: GimpTransformTool multi-layer aware.



commit 647ebffe7b802156f930263a919a4838535385ec
Author: Jehan <jehan girinstud io>
Date:   Tue May 26 16:15:15 2020 +0200

    app: GimpTransformTool multi-layer aware.
    
    This implied a lot of other core changes, which also pushed me into
    improving some of the edit actions and PDB calls to be multi-layer aware
    in the same time.
    
    Note that it is still work-in-progress, but I just had to commit
    something in an acceptable intermediate state otherwise I was just going
    crazy.
    
    In particular now the various transform tools are multi-layer aware and
    work simultaneously on all selected layers (and the linked layers if any
    of the selected layers is linked too). Both preview and final transform
    processing works.
    In the limitations, preview doesn't work well (only one layer in the
    preview) when there is a selection (though the actual transform works).
    
    Also I am left to wonder how we should process this case of canvas
    selection+transform on multi-layers. Indeed currently I am just creating
    a floating selection (like we used to for the selection+transform case)
    containing a transform result of the composited version of all selected
    layers. This is a possible expected result, but another could be to get
    several transformed layers (without composition). But then should the
    "Floating Selection" concept allow for multiple Floating Selections?
    Sooo many questions left to answer.

 app/actions/edit-commands.c       |  45 +++++----
 app/core/gimp-edit.c              |  81 +++++++++------
 app/core/gimp-edit.h              |   6 +-
 app/core/gimpdrawable-transform.c |  53 +++++++---
 app/core/gimpdrawable-transform.h |   2 +-
 app/core/gimpimage.c              | 103 +++++++++++++++++++
 app/core/gimpimage.h              |   6 ++
 app/core/gimpselection.c          |  78 ++++++++++-----
 app/core/gimpselection.h          |   2 +-
 app/pdb/edit-cmds.c               | 203 +++++++++++++++++++++++++++++--------
 app/tools/gimpfliptool.c          |  67 +++++++------
 app/tools/gimptransformgridtool.c | 191 ++++++++++++++++++-----------------
 app/tools/gimptransformgridtool.h |   4 +-
 app/tools/gimptransformtool.c     | 206 ++++++++++++++++++--------------------
 app/tools/gimptransformtool.h     |  10 +-
 libgimp/gimpedit_pdb.c            |  48 +++++----
 libgimp/gimpedit_pdb.h            |   9 +-
 pdb/groups/edit.pdb               | 167 ++++++++++++++++++++++++------
 18 files changed, 857 insertions(+), 424 deletions(-)
---
diff --git a/app/actions/edit-commands.c b/app/actions/edit-commands.c
index 7ca2de0a77..4789337325 100644
--- a/app/actions/edit-commands.c
+++ b/app/actions/edit-commands.c
@@ -216,16 +216,20 @@ edit_cut_cmd_callback (GimpAction *action,
                        gpointer    data)
 {
   GimpImage    *image;
-  GimpDrawable *drawable;
+  GList        *drawables;
+  GList        *iter;
   GimpObject   *cut;
   GError       *error = NULL;
-  return_if_no_drawable (image, drawable, data);
+  return_if_no_drawables (image, drawables, data);
 
-  if (! check_drawable_alpha (drawable, data))
-    return;
+  for (iter = drawables; iter; iter = iter->next)
+    if (! check_drawable_alpha (iter->data, data))
+      {
+        g_list_free (drawables);
+        return;
+      }
 
-  cut = gimp_edit_cut (image, drawable, action_data_get_context (data),
-                       &error);
+  cut = gimp_edit_cut (image, drawables, action_data_get_context (data), &error);
 
   if (cut)
     {
@@ -248,6 +252,7 @@ edit_cut_cmd_callback (GimpAction *action,
                             error->message);
       g_clear_error (&error);
     }
+  g_list_free (drawables);
 }
 
 void
@@ -539,7 +544,7 @@ check_drawable_alpha (GimpDrawable *drawable,
         {
           gimp_message_literal (
             gimp, G_OBJECT (display), GIMP_MESSAGE_WARNING,
-            _("The active layer's alpha channel is locked."));
+            _("A selected layer's alpha channel is locked."));
 
           gimp_tools_blink_lock_box (gimp, GIMP_ITEM (drawable));
         }
@@ -639,21 +644,21 @@ cut_named_buffer_callback (GtkWidget   *widget,
                            const gchar *name,
                            gpointer     data)
 {
-  GimpImage    *image    = GIMP_IMAGE (data);
-  GimpDrawable *drawable = gimp_image_get_active_drawable (image);
-  GError       *error    = NULL;
+  GimpImage *image     = GIMP_IMAGE (data);
+  GList     *drawables = gimp_image_get_selected_drawables (image);
+  GError    *error     = NULL;
 
-  if (! drawable)
+  if (! drawables)
     {
       gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_WARNING,
-                            _("There is no active layer or channel to cut from."));
+                            _("There are no selected layers or channels to cut from."));
       return;
     }
 
   if (! (name && strlen (name)))
     name = _("(Unnamed Buffer)");
 
-  if (gimp_edit_named_cut (image, name, drawable,
+  if (gimp_edit_named_cut (image, name, drawables,
                            gimp_get_user_context (image->gimp), &error))
     {
       gimp_image_flush (image);
@@ -664,6 +669,7 @@ cut_named_buffer_callback (GtkWidget   *widget,
                             error->message);
       g_clear_error (&error);
     }
+  g_list_free (drawables);
 }
 
 static void
@@ -671,21 +677,21 @@ copy_named_buffer_callback (GtkWidget   *widget,
                             const gchar *name,
                             gpointer     data)
 {
-  GimpImage    *image    = GIMP_IMAGE (data);
-  GimpDrawable *drawable = gimp_image_get_active_drawable (image);
-  GError       *error    = NULL;
+  GimpImage *image     = GIMP_IMAGE (data);
+  GList     *drawables = gimp_image_get_selected_drawables (image);
+  GError    *error     = NULL;
 
-  if (! drawable)
+  if (! drawables)
     {
       gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_WARNING,
-                            _("There is no active layer or channel to copy from."));
+                            _("There are no selected layers or channels to copy from."));
       return;
     }
 
   if (! (name && strlen (name)))
     name = _("(Unnamed Buffer)");
 
-  if (gimp_edit_named_copy (image, name, drawable,
+  if (gimp_edit_named_copy (image, name, drawables,
                             gimp_get_user_context (image->gimp), &error))
     {
       gimp_image_flush (image);
@@ -696,6 +702,7 @@ copy_named_buffer_callback (GtkWidget   *widget,
                             error->message);
       g_clear_error (&error);
     }
+  g_list_free (drawables);
 }
 
 static void
diff --git a/app/core/gimp-edit.c b/app/core/gimp-edit.c
index e0b334458e..08a2b032e2 100644
--- a/app/core/gimp-edit.c
+++ b/app/core/gimp-edit.c
@@ -47,7 +47,7 @@
 /*  local function protypes  */
 
 static GimpBuffer * gimp_edit_extract (GimpImage     *image,
-                                       GimpPickable  *pickable,
+                                       GList         *pickables,
                                        GimpContext   *context,
                                        gboolean       cut_pixels,
                                        GError       **error);
@@ -57,25 +57,24 @@ static GimpBuffer * gimp_edit_extract (GimpImage     *image,
 
 GimpObject *
 gimp_edit_cut (GimpImage     *image,
-               GimpDrawable  *drawable,
+               GList         *drawables,
                GimpContext   *context,
                GError       **error)
 {
   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
-  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
-  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
   g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  if (GIMP_IS_LAYER (drawable) &&
+  if (g_list_length (drawables) == 1  &&
+      GIMP_IS_LAYER (drawables->data) &&
       gimp_channel_is_empty (gimp_image_get_mask (image)))
     {
       GimpImage *clip_image;
       gint       off_x, off_y;
 
-      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
+      gimp_item_get_offset (GIMP_ITEM (drawables->data), &off_x, &off_y);
 
-      clip_image = gimp_image_new_from_drawable (image->gimp, drawable);
+      clip_image = gimp_image_new_from_drawable (image->gimp, drawables->data);
       g_object_set_data (G_OBJECT (clip_image), "offset-x",
                          GINT_TO_POINTER (off_x));
       g_object_set_data (G_OBJECT (clip_image), "offset-y",
@@ -87,7 +86,7 @@ gimp_edit_cut (GimpImage     *image,
       gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_CUT,
                                    C_("undo-type", "Cut Layer"));
 
-      gimp_image_remove_layer (image, GIMP_LAYER (drawable),
+      gimp_image_remove_layer (image, GIMP_LAYER (drawables->data),
                                TRUE, NULL);
 
       gimp_image_undo_group_end (image);
@@ -98,8 +97,7 @@ gimp_edit_cut (GimpImage     *image,
     {
       GimpBuffer *buffer;
 
-      buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable),
-                                  context, TRUE, error);
+      buffer = gimp_edit_extract (image, drawables, context, TRUE, error);
 
       if (buffer)
         {
@@ -183,8 +181,7 @@ gimp_edit_copy (GimpImage     *image,
     {
       GimpBuffer *buffer;
 
-      buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawables->data),
-                                  context, FALSE, error);
+      buffer = gimp_edit_extract (image, drawables, context, FALSE, error);
 
       if (buffer)
         {
@@ -204,13 +201,15 @@ gimp_edit_copy_visible (GimpImage    *image,
                         GError      **error)
 {
   GimpBuffer *buffer;
+  GList      *pickables;
 
   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
   g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  buffer = gimp_edit_extract (image, GIMP_PICKABLE (image),
-                              context, FALSE, error);
+  pickables = g_list_prepend (NULL, image);
+  buffer = gimp_edit_extract (image, pickables, context, FALSE, error);
+  g_list_free (pickables);
 
   if (buffer)
     {
@@ -662,7 +661,7 @@ gimp_edit_paste_as_new_image (Gimp       *gimp,
 const gchar *
 gimp_edit_named_cut (GimpImage     *image,
                      const gchar   *name,
-                     GimpDrawable  *drawable,
+                     GList         *drawables,
                      GimpContext   *context,
                      GError       **error)
 {
@@ -670,13 +669,10 @@ gimp_edit_named_cut (GimpImage     *image,
 
   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
   g_return_val_if_fail (name != NULL, NULL);
-  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
-  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
   g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable),
-                              context, TRUE, error);
+  buffer = gimp_edit_extract (image, drawables, context, TRUE, error);
 
   if (buffer)
     {
@@ -693,7 +689,7 @@ gimp_edit_named_cut (GimpImage     *image,
 const gchar *
 gimp_edit_named_copy (GimpImage     *image,
                       const gchar   *name,
-                      GimpDrawable  *drawable,
+                      GList         *drawables,
                       GimpContext   *context,
                       GError       **error)
 {
@@ -701,13 +697,10 @@ gimp_edit_named_copy (GimpImage     *image,
 
   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
   g_return_val_if_fail (name != NULL, NULL);
-  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
-  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
   g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable),
-                              context, FALSE, error);
+  buffer = gimp_edit_extract (image, drawables, context, FALSE, error);
 
   if (buffer)
     {
@@ -728,14 +721,16 @@ gimp_edit_named_copy_visible (GimpImage    *image,
                               GError      **error)
 {
   GimpBuffer *buffer;
+  GList      *pickables;
 
   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
   g_return_val_if_fail (name != NULL, NULL);
   g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  buffer = gimp_edit_extract (image, GIMP_PICKABLE (image),
-                              context, FALSE, error);
+  pickables = g_list_prepend (NULL, image);
+  buffer = gimp_edit_extract (image, pickables, context, FALSE, error);
+  g_list_free (pickables);
 
   if (buffer)
     {
@@ -752,9 +747,29 @@ gimp_edit_named_copy_visible (GimpImage    *image,
 
 /*  private functions  */
 
+/**
+ * gimp_edit_extract:
+ * @image:
+ * @pickables:
+ * @context:
+ * @cut_pixels:
+ * @error:
+ *
+ * Extracts the selected part of @image from the list of @pickables.
+ * If @cut_pixels is %TRUE, and there is only one pickable input, and if
+ * this pickable is a #GimpDrawable, then the selected pixels will be
+ * effectively erased from the input pickable.
+ * Otherwise @cut_pixels has no additional effect.
+ * Note that all @pickables must belong to the same @image.
+ *
+ * Returns: a #GimpBuffer of the selected part of @image as if only the
+ * selected @pickables were present (composited according to their
+ * properties, unless there is only one pickable, in which case direct
+ * pixel information is used without composition).
+ */
 static GimpBuffer *
 gimp_edit_extract (GimpImage     *image,
-                   GimpPickable  *pickable,
+                   GList         *pickables,
                    GimpContext   *context,
                    gboolean       cut_pixels,
                    GError       **error)
@@ -763,13 +778,19 @@ gimp_edit_extract (GimpImage     *image,
   gint        offset_x;
   gint        offset_y;
 
+  g_return_val_if_fail (g_list_length (pickables) > 0, NULL);
+
+  if (g_list_length (pickables) > 1 ||
+      ! GIMP_IS_DRAWABLE (pickables->data))
+    cut_pixels = FALSE;
+
   if (cut_pixels)
     gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_CUT,
                                  C_("undo-type", "Cut"));
 
   /*  Cut/copy the mask portion from the image  */
   buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)),
-                                   pickable, context,
+                                   pickables, context,
                                    cut_pixels, FALSE, FALSE,
                                    &offset_x, &offset_y, error);
 
@@ -790,10 +811,10 @@ gimp_edit_extract (GimpImage     *image,
       gimp_buffer_set_resolution (gimp_buffer, res_x, res_y);
       gimp_buffer_set_unit (gimp_buffer, gimp_image_get_unit (image));
 
-      if (GIMP_IS_COLOR_MANAGED (pickable))
+      if (GIMP_IS_COLOR_MANAGED (pickables->data))
         {
           GimpColorProfile *profile =
-            gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (pickable));
+            gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (pickables->data));
 
           if (profile)
             gimp_buffer_set_color_profile (gimp_buffer, profile);
diff --git a/app/core/gimp-edit.h b/app/core/gimp-edit.h
index 3b59089e17..84f97b7481 100644
--- a/app/core/gimp-edit.h
+++ b/app/core/gimp-edit.h
@@ -20,7 +20,7 @@
 
 
 GimpObject  * gimp_edit_cut                (GimpImage       *image,
-                                            GimpDrawable    *drawable,
+                                            GList           *drawables,
                                             GimpContext     *context,
                                             GError         **error);
 GimpObject  * gimp_edit_copy               (GimpImage       *image,
@@ -44,12 +44,12 @@ GimpImage   * gimp_edit_paste_as_new_image (Gimp            *gimp,
 
 const gchar * gimp_edit_named_cut          (GimpImage       *image,
                                             const gchar     *name,
-                                            GimpDrawable    *drawable,
+                                            GList           *drawables,
                                             GimpContext     *context,
                                             GError         **error);
 const gchar * gimp_edit_named_copy         (GimpImage       *image,
                                             const gchar     *name,
-                                            GimpDrawable    *drawable,
+                                            GList           *drawables,
                                             GimpContext     *context,
                                             GError         **error);
 const gchar * gimp_edit_named_copy_visible (GimpImage       *image,
diff --git a/app/core/gimpdrawable-transform.c b/app/core/gimpdrawable-transform.c
index 99cc0f5c52..76492b03a6 100644
--- a/app/core/gimpdrawable-transform.c
+++ b/app/core/gimpdrawable-transform.c
@@ -700,6 +700,7 @@ gimp_drawable_transform_affine (GimpDrawable           *drawable,
                                 GimpProgress           *progress)
 {
   GimpImage    *image;
+  GList        *drawables;
   GeglBuffer   *orig_buffer;
   gint          orig_offset_x;
   gint          orig_offset_y;
@@ -720,9 +721,11 @@ gimp_drawable_transform_affine (GimpDrawable           *drawable,
                                C_("undo-type", "Transform"));
 
   /* Cut/Copy from the specified drawable */
-  orig_buffer = gimp_drawable_transform_cut (drawable, context,
+  drawables   = g_list_prepend (NULL, drawable);
+  orig_buffer = gimp_drawable_transform_cut (drawables, context,
                                              &orig_offset_x, &orig_offset_y,
                                              &new_layer);
+  g_free (drawables);
 
   if (orig_buffer)
     {
@@ -786,6 +789,7 @@ gimp_drawable_transform_flip (GimpDrawable        *drawable,
                               gboolean             clip_result)
 {
   GimpImage    *image;
+  GList        *drawables;
   GeglBuffer   *orig_buffer;
   gint          orig_offset_x;
   gint          orig_offset_y;
@@ -804,9 +808,11 @@ gimp_drawable_transform_flip (GimpDrawable        *drawable,
                                C_("undo-type", "Flip"));
 
   /* Cut/Copy from the specified drawable */
-  orig_buffer = gimp_drawable_transform_cut (drawable, context,
+  drawables   = g_list_prepend (NULL, drawable);
+  orig_buffer = gimp_drawable_transform_cut (drawables, context,
                                              &orig_offset_x, &orig_offset_y,
                                              &new_layer);
+  g_free (drawables);
 
   if (orig_buffer)
     {
@@ -866,6 +872,7 @@ gimp_drawable_transform_rotate (GimpDrawable     *drawable,
                                 gboolean          clip_result)
 {
   GimpImage    *image;
+  GList        *drawables;
   GeglBuffer   *orig_buffer;
   gint          orig_offset_x;
   gint          orig_offset_y;
@@ -884,9 +891,11 @@ gimp_drawable_transform_rotate (GimpDrawable     *drawable,
                                C_("undo-type", "Rotate"));
 
   /* Cut/Copy from the specified drawable */
-  orig_buffer = gimp_drawable_transform_cut (drawable, context,
+  drawables   = g_list_prepend (NULL, drawable);
+  orig_buffer = gimp_drawable_transform_cut (drawables, context,
                                              &orig_offset_x, &orig_offset_y,
                                              &new_layer);
+  g_free (drawables);
 
   if (orig_buffer)
     {
@@ -940,23 +949,38 @@ gimp_drawable_transform_rotate (GimpDrawable     *drawable,
 }
 
 GeglBuffer *
-gimp_drawable_transform_cut (GimpDrawable *drawable,
+gimp_drawable_transform_cut (GList        *drawables,
                              GimpContext  *context,
                              gint         *offset_x,
                              gint         *offset_y,
                              gboolean     *new_layer)
 {
-  GimpImage  *image;
-  GeglBuffer *buffer;
+  GimpImage  *image  = NULL;
+  GeglBuffer *buffer = NULL;
+  GList      *iter;
+  gboolean    drawables_are_layers = FALSE;
 
-  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
-  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
+  g_return_val_if_fail (g_list_length (drawables), NULL);
   g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
   g_return_val_if_fail (offset_x != NULL, NULL);
   g_return_val_if_fail (offset_y != NULL, NULL);
   g_return_val_if_fail (new_layer != NULL, NULL);
 
-  image = gimp_item_get_image (GIMP_ITEM (drawable));
+  for (iter = drawables; iter; iter = iter->next)
+    {
+      g_return_val_if_fail (GIMP_IS_DRAWABLE (iter->data), NULL);
+      g_return_val_if_fail (gimp_item_is_attached (iter->data), NULL);
+
+      if (! image)
+        image = gimp_item_get_image (iter->data);
+      else
+        g_return_val_if_fail (image == gimp_item_get_image (iter->data), NULL);
+
+      if (drawables_are_layers)
+        g_return_val_if_fail (GIMP_IS_LAYER (iter->data), NULL);
+      else if (GIMP_IS_LAYER (iter->data))
+        drawables_are_layers = TRUE;
+    }
 
   /*  extract the selected mask if there is a selection  */
   if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
@@ -967,11 +991,10 @@ gimp_drawable_transform_cut (GimpDrawable *drawable,
        * gimp_layer_new_from_gegl_buffer() later which assumes that
        * the buffer are either RGB or GRAY.  Eeek!!!  (Sven)
        */
-      if (gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &w, &h))
+      if (gimp_image_mask_intersect (image, drawables, &x, &y, &w, &h))
         {
           buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)),
-                                           GIMP_PICKABLE (drawable),
-                                           context,
+                                           drawables, context,
                                            TRUE, FALSE, TRUE,
                                            offset_x, offset_y,
                                            NULL);
@@ -989,9 +1012,9 @@ gimp_drawable_transform_cut (GimpDrawable *drawable,
   else  /*  otherwise, just copy the layer  */
     {
       buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)),
-                                       GIMP_PICKABLE (drawable),
-                                       context,
-                                       FALSE, TRUE, GIMP_IS_LAYER (drawable),
+                                       drawables, context,
+                                       FALSE, TRUE,
+                                       drawables_are_layers,
                                        offset_x, offset_y,
                                        NULL);
 
diff --git a/app/core/gimpdrawable-transform.h b/app/core/gimpdrawable-transform.h
index 8cdd53f8c1..e265dcf26e 100644
--- a/app/core/gimpdrawable-transform.h
+++ b/app/core/gimpdrawable-transform.h
@@ -78,7 +78,7 @@ GimpDrawable         * gimp_drawable_transform_rotate            (GimpDrawable
                                                                   gdouble                  center_y,
                                                                   gboolean                 clip_result);
 
-GeglBuffer           * gimp_drawable_transform_cut               (GimpDrawable            *drawable,
+GeglBuffer           * gimp_drawable_transform_cut               (GList                   *drawables,
                                                                   GimpContext             *context,
                                                                   gint                    *offset_x,
                                                                   gint                    *offset_y,
diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c
index f490e310eb..42a0699a35 100644
--- a/app/core/gimpimage.c
+++ b/app/core/gimpimage.c
@@ -3082,6 +3082,109 @@ gimp_image_mask_changed (GimpImage *image)
   g_signal_emit (image, gimp_image_signals[MASK_CHANGED], 0);
 }
 
+/**
+ * gimp_item_mask_intersect:
+ * @image:   the #GimpImage
+ * @items:   a list of #GimpItem
+ * @x: (out) (optional): return location for x
+ * @y: (out) (optional): return location for y
+ * @width: (out) (optional): return location for the width
+ * @height: (out) (optional): return location for the height
+ *
+ * Intersect the area of the @items and its image's selection mask.
+ * The computed area is the bounding box of the selection intersection
+ * within the image. These values are only returned if the function
+ * returns %TRUE.
+ *
+ * Note that even though the items bounding box may not be empty, it is
+ * possible to get a return value of %FALSE. Imagine disjoint items, one
+ * on the left, one on the right, and a selection in the middle not
+ * intersecting with any of the items.
+ *
+ * Returns: %TRUE if the selection intersects with any of the @items.
+ */
+gboolean
+gimp_image_mask_intersect (GimpImage *image,
+                           GList     *items,
+                           gint      *x,
+                           gint      *y,
+                           gint      *width,
+                           gint      *height)
+{
+  GimpChannel *selection;
+  GList       *iter;
+  gint         sel_x, sel_y, sel_width, sel_height;
+  gint         x1 = G_MAXINT;
+  gint         y1 = G_MAXINT;
+  gint         x2 = G_MININT;
+  gint         y2 = G_MININT;
+  gboolean     intersect = FALSE;
+
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+  for (iter = items; iter; iter = iter->next)
+    {
+      g_return_val_if_fail (GIMP_IS_ITEM (iter->data), FALSE);
+      g_return_val_if_fail (gimp_item_is_attached (iter->data), FALSE);
+      g_return_val_if_fail (gimp_item_get_image (iter->data) == image, FALSE);
+    }
+
+  selection = gimp_image_get_mask (image);
+  if (selection)
+    gimp_item_bounds (GIMP_ITEM (selection),
+                      &sel_x, &sel_y, &sel_width, &sel_height);
+
+  for (iter = items; iter; iter = iter->next)
+    {
+      GimpItem *item = iter->data;
+      gboolean  item_intersect;
+      gint      tmp_x, tmp_y;
+      gint      tmp_width, tmp_height;
+
+      gimp_item_get_offset (item, &tmp_x, &tmp_y);
+
+      /* check for is_empty() before intersecting so we ignore the
+       * selection if it is suspended (like when stroking)
+       */
+      if (GIMP_ITEM (selection) != item       &&
+          ! gimp_channel_is_empty (selection))
+        {
+          item_intersect = gimp_rectangle_intersect (sel_x, sel_y, sel_width, sel_height,
+                                                     tmp_x, tmp_y,
+                                                     gimp_item_get_width  (item),
+                                                     gimp_item_get_height (item),
+                                                     &tmp_x, &tmp_y,
+                                                     &tmp_width, &tmp_height);
+        }
+      else
+        {
+          tmp_width  = gimp_item_get_width  (item);
+          tmp_height = gimp_item_get_height (item);
+
+          item_intersect = TRUE;
+        }
+
+      if (item_intersect)
+        {
+          x1 = MIN (x1, tmp_x);
+          y1 = MIN (y1, tmp_y);
+          x2 = MAX (x2, tmp_x + tmp_width);
+          y2 = MAX (y2, tmp_y + tmp_height);
+
+          intersect = TRUE;
+        }
+    }
+
+  if (intersect)
+    {
+      if (x)      *x      = x1;
+      if (y)      *y      = y1;
+      if (width)  *width  = x2 - x1;
+      if (height) *height = y2 - y1;
+    }
+
+  return intersect;
+}
+
 void
 gimp_image_take_mask (GimpImage   *image,
                       GimpChannel *mask)
diff --git a/app/core/gimpimage.h b/app/core/gimpimage.h
index 49fb50bb03..41bfedf153 100644
--- a/app/core/gimpimage.h
+++ b/app/core/gimpimage.h
@@ -216,6 +216,12 @@ void       gimp_image_floating_selection_changed (GimpImage          *image);
 
 GimpChannel   * gimp_image_get_mask              (GimpImage          *image);
 void            gimp_image_mask_changed          (GimpImage          *image);
+gboolean        gimp_image_mask_intersect        (GimpImage          *image,
+                                                  GList              *items,
+                                                  gint               *x,
+                                                  gint               *y,
+                                                  gint               *width,
+                                                  gint               *height);
 
 
 /*  image components  */
diff --git a/app/core/gimpselection.c b/app/core/gimpselection.c
index 27ed9a3793..cc8fda49ad 100644
--- a/app/core/gimpselection.c
+++ b/app/core/gimpselection.c
@@ -660,7 +660,7 @@ gimp_selection_resume (GimpSelection *selection)
 
 GeglBuffer *
 gimp_selection_extract (GimpSelection *selection,
-                        GimpPickable  *pickable,
+                        GList         *pickables,
                         GimpContext   *context,
                         gboolean       cut_image,
                         gboolean       keep_indexed,
@@ -669,23 +669,53 @@ gimp_selection_extract (GimpSelection *selection,
                         gint          *offset_y,
                         GError       **error)
 {
-  GimpImage  *image;
-  GeglBuffer *src_buffer;
-  GeglBuffer *dest_buffer;
-  const Babl *src_format;
-  const Babl *dest_format;
-  gint        x1, y1, x2, y2;
-  gboolean    non_empty;
-  gint        off_x, off_y;
+  GimpImage    *image      = NULL;
+  GimpImage    *temp_image = NULL;
+  GimpPickable *pickable   = NULL;
+  GeglBuffer   *src_buffer;
+  GeglBuffer   *dest_buffer;
+  GList        *iter;
+  const Babl   *src_format;
+  const Babl   *dest_format;
+  gint          x1, y1, x2, y2;
+  gboolean      non_empty;
+  gint          off_x, off_y;
 
   g_return_val_if_fail (GIMP_IS_SELECTION (selection), NULL);
-  g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
-  if (GIMP_IS_ITEM (pickable))
-    g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (pickable)), NULL);
   g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+  g_return_val_if_fail (pickables != NULL, NULL);
 
-  image = gimp_pickable_get_image (pickable);
+  for (iter = pickables; iter; iter = iter->next)
+    {
+      g_return_val_if_fail (GIMP_IS_PICKABLE (iter->data), NULL);
+
+      if (GIMP_IS_ITEM (iter->data))
+        g_return_val_if_fail (gimp_item_is_attached (iter->data), NULL);
+
+      if (! image)
+        image = gimp_pickable_get_image (iter->data);
+      else
+        g_return_val_if_fail (image == gimp_pickable_get_image (iter->data), NULL);
+    }
+
+  if (g_list_length (pickables) == 1)
+    {
+      pickable = pickables->data;
+    }
+  else
+    {
+      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);
+      selection  = GIMP_SELECTION (gimp_image_get_mask (temp_image));
+
+      pickable   = GIMP_PICKABLE (temp_image);
+
+      /* Don't cut from the temporary image. */
+      cut_image = FALSE;
+    }
 
   /*  If there are no bounds, then just extract the entire image
    *  This may not be the correct behavior, but after getting rid
@@ -719,6 +749,10 @@ gimp_selection_extract (GimpSelection *selection,
       g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
                            _("Unable to cut or copy because the "
                              "selected region is empty."));
+
+      if (temp_image)
+        g_object_unref (temp_image);
+
       return NULL;
     }
 
@@ -804,6 +838,9 @@ gimp_selection_extract (GimpSelection *selection,
   *offset_x = x1 + off_x;
   *offset_y = y1 + off_y;
 
+  if (temp_image)
+    g_object_unref (temp_image);
+
   return dest_buffer;
 }
 
@@ -818,7 +855,6 @@ gimp_selection_float (GimpSelection *selection,
 {
   GimpImage        *image;
   GimpLayer        *layer;
-  GimpPickable     *pickable;
   GeglBuffer       *buffer;
   GimpColorProfile *profile;
   GimpImage        *temp_image = NULL;
@@ -864,22 +900,12 @@ gimp_selection_float (GimpSelection *selection,
   gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_FS_FLOAT,
                                C_("undo-type", "Float Selection"));
 
-  if (g_list_length (drawables) > 1)
-    {
-      temp_image = gimp_image_new_from_drawables (image->gimp, drawables, TRUE);
-      pickable = GIMP_PICKABLE (temp_image);
-    }
-  else
-    {
-      pickable = GIMP_PICKABLE (drawables->data);
-    }
-
   /*  Cut or copy the selected region  */
-  buffer = gimp_selection_extract (selection, pickable, context,
+  buffer = gimp_selection_extract (selection, drawables, context,
                                    cut_image, FALSE, TRUE,
                                    &x1, &y1, NULL);
 
-  profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (pickable));
+  profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawables->data));
 
   /*  Clear the selection  */
   gimp_channel_clear (GIMP_CHANNEL (selection), NULL, TRUE);
diff --git a/app/core/gimpselection.h b/app/core/gimpselection.h
index cdbda90cec..310614efe8 100644
--- a/app/core/gimpselection.h
+++ b/app/core/gimpselection.h
@@ -55,7 +55,7 @@ gint          gimp_selection_suspend  (GimpSelection *selection);
 gint          gimp_selection_resume   (GimpSelection *selection);
 
 GeglBuffer  * gimp_selection_extract  (GimpSelection *selection,
-                                       GimpPickable  *pickable,
+                                       GList         *pickables,
                                        GimpContext   *context,
                                        gboolean       cut_image,
                                        gboolean       keep_indexed,
diff --git a/app/pdb/edit-cmds.c b/app/pdb/edit-cmds.c
index 044d199325..37f1f55d8f 100644
--- a/app/pdb/edit-cmds.c
+++ b/app/pdb/edit-cmds.c
@@ -58,21 +58,50 @@ edit_cut_invoker (GimpProcedure         *procedure,
 {
   gboolean success = TRUE;
   GimpValueArray *return_vals;
-  GimpDrawable *drawable;
+  gint num_drawables;
+  const GimpItem **drawables;
   gboolean non_empty = FALSE;
 
-  drawable = g_value_get_object (gimp_value_array_index (args, 0));
+  num_drawables = g_value_get_int (gimp_value_array_index (args, 0));
+  drawables = (const GimpItem **) gimp_value_get_object_array (gimp_value_array_index (args, 1));
 
   if (success)
     {
-      if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                     GIMP_PDB_ITEM_CONTENT, error) &&
-          gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+      GimpImage *image         = NULL;
+      GList     *drawable_list = NULL;
+      gint       i;
+
+      for (i = 0; i < num_drawables; i++)
+        {
+          if (! gimp_pdb_item_is_attached (GIMP_ITEM (drawables[i]), NULL,
+                                           GIMP_PDB_ITEM_CONTENT, error) ||
+              gimp_pdb_item_is_group (GIMP_ITEM (drawables[i]), error))
+            {
+              success = FALSE;
+              break;
+            }
+
+          if (! image)
+            {
+              image = gimp_item_get_image (GIMP_ITEM (drawables[i]));
+            }
+          else if (image != gimp_item_get_image (GIMP_ITEM (drawables[i])))
+            {
+              success = FALSE;
+              gimp_message_literal (gimp,
+                                    G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                                    _("All specified drawables must belong to the same image."));
+              break;
+            }
+
+          drawable_list = g_list_prepend (drawable_list, (gpointer) drawables[i]);
+        }
+
+      if (success && image)
         {
-          GimpImage *image    = gimp_item_get_image (GIMP_ITEM (drawable));
-          GError    *my_error = NULL;
+          GError *my_error = NULL;
 
-          non_empty = gimp_edit_cut (image, drawable, context, &my_error) != NULL;
+          non_empty = gimp_edit_cut (image, drawable_list, context, &my_error) != NULL;
 
           if (! non_empty)
             {
@@ -83,7 +112,10 @@ edit_cut_invoker (GimpProcedure         *procedure,
             }
         }
       else
-        success = FALSE;
+        {
+          success = FALSE;
+        }
+      g_list_free (drawable_list);
     }
 
   return_vals = gimp_procedure_get_return_values (procedure, success,
@@ -295,24 +327,53 @@ edit_named_cut_invoker (GimpProcedure         *procedure,
 {
   gboolean success = TRUE;
   GimpValueArray *return_vals;
-  GimpDrawable *drawable;
+  gint num_drawables;
+  const GimpItem **drawables;
   const gchar *buffer_name;
   gchar *real_name = NULL;
 
-  drawable = g_value_get_object (gimp_value_array_index (args, 0));
-  buffer_name = g_value_get_string (gimp_value_array_index (args, 1));
+  num_drawables = g_value_get_int (gimp_value_array_index (args, 0));
+  drawables = (const GimpItem **) gimp_value_get_object_array (gimp_value_array_index (args, 1));
+  buffer_name = g_value_get_string (gimp_value_array_index (args, 2));
 
   if (success)
     {
-      if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                     GIMP_PDB_ITEM_CONTENT, error) &&
-          gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+      GimpImage *image         = NULL;
+      GList     *drawable_list = NULL;
+      gint       i;
+
+      for (i = 0; i < num_drawables; i++)
+        {
+          if (! gimp_pdb_item_is_attached (GIMP_ITEM (drawables[i]), NULL,
+                                           GIMP_PDB_ITEM_CONTENT, error) ||
+              gimp_pdb_item_is_group (GIMP_ITEM (drawables[i]), error))
+            {
+              success = FALSE;
+              break;
+            }
+
+          if (! image)
+            {
+              image = gimp_item_get_image (GIMP_ITEM (drawables[i]));
+            }
+          else if (image != gimp_item_get_image (GIMP_ITEM (drawables[i])))
+            {
+              success = FALSE;
+              gimp_message_literal (gimp,
+                                    G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                                    _("All specified drawables must belong to the same image."));
+              break;
+            }
+
+          drawable_list = g_list_prepend (drawable_list, (gpointer) drawables[i]);
+        }
+
+      if (success && image)
         {
-          GimpImage *image    = gimp_item_get_image (GIMP_ITEM (drawable));
-          GError    *my_error = NULL;
+          GError *my_error = NULL;
 
           real_name = (gchar *) gimp_edit_named_cut (image, buffer_name,
-                                                     drawable, context, &my_error);
+                                                     drawable_list, context, &my_error);
 
           if (real_name)
             {
@@ -327,7 +388,10 @@ edit_named_cut_invoker (GimpProcedure         *procedure,
             }
         }
       else
-        success = FALSE;
+        {
+          success = FALSE;
+        }
+      g_list_free (drawable_list);
     }
 
   return_vals = gimp_procedure_get_return_values (procedure, success,
@@ -349,22 +413,52 @@ edit_named_copy_invoker (GimpProcedure         *procedure,
 {
   gboolean success = TRUE;
   GimpValueArray *return_vals;
-  GimpDrawable *drawable;
+  gint num_drawables;
+  const GimpItem **drawables;
   const gchar *buffer_name;
   gchar *real_name = NULL;
 
-  drawable = g_value_get_object (gimp_value_array_index (args, 0));
-  buffer_name = g_value_get_string (gimp_value_array_index (args, 1));
+  num_drawables = g_value_get_int (gimp_value_array_index (args, 0));
+  drawables = (const GimpItem **) gimp_value_get_object_array (gimp_value_array_index (args, 1));
+  buffer_name = g_value_get_string (gimp_value_array_index (args, 2));
 
   if (success)
     {
-      if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL, 0, error))
+      GimpImage *image         = NULL;
+      GList     *drawable_list = NULL;
+      gint       i;
+
+      for (i = 0; i < num_drawables; i++)
+        {
+          if (! gimp_pdb_item_is_attached (GIMP_ITEM (drawables[i]), NULL,
+                                           0, error))
+            {
+              success = FALSE;
+              break;
+            }
+
+          if (! image)
+            {
+              image = gimp_item_get_image (GIMP_ITEM (drawables[i]));
+            }
+          else if (image != gimp_item_get_image (GIMP_ITEM (drawables[i])))
+            {
+              success = FALSE;
+              gimp_message_literal (gimp,
+                                    G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                                    _("All specified drawables must belong to the same image."));
+              break;
+            }
+
+          drawable_list = g_list_prepend (drawable_list, (gpointer) drawables[i]);
+        }
+
+      if (success && image)
         {
-          GimpImage *image    = gimp_item_get_image (GIMP_ITEM (drawable));
-          GError    *my_error = NULL;
+          GError *my_error = NULL;
 
           real_name = (gchar *) gimp_edit_named_copy (image, buffer_name,
-                                                      drawable, context, &my_error);
+                                                      drawable_list, context, &my_error);
 
           if (real_name)
             {
@@ -379,7 +473,10 @@ edit_named_copy_invoker (GimpProcedure         *procedure,
             }
         }
       else
-        success = FALSE;
+        {
+          success = FALSE;
+        }
+      g_list_free (drawable_list);
     }
 
   return_vals = gimp_procedure_get_return_values (procedure, success,
@@ -538,19 +635,25 @@ register_edit_procs (GimpPDB *pdb)
   gimp_object_set_static_name (GIMP_OBJECT (procedure),
                                "gimp-edit-cut");
   gimp_procedure_set_static_help (procedure,
-                                  "Cut from the specified drawable.",
-                                  "If there is a selection in the image, then the area specified by the 
selection is cut from the specified drawable and placed in an internal GIMP edit buffer. It can subsequently 
be retrieved using the 'gimp-edit-paste' command. If there is no selection, then the specified drawable will 
be removed and its contents stored in the internal GIMP edit buffer. This procedure will fail if the selected 
area lies completely outside the bounds of the current drawable and there is nothing to copy from.",
+                                  "Cut from the specified drawables.",
+                                  "If there is a selection in the image, then the area specified by the 
selection is cut from the specified drawables and placed in an internal GIMP edit buffer. It can subsequently 
be retrieved using the 'gimp-edit-paste' command. If there is no selection and only one specified drawable, 
then the specified drawable will be removed and its contents stored in the internal GIMP edit buffer. This 
procedure will fail if the selected area lies completely outside the bounds of the current drawables and 
there is nothing to cut from.",
                                   NULL);
   gimp_procedure_set_static_attribution (procedure,
                                          "Spencer Kimball & Peter Mattis",
                                          "Spencer Kimball & Peter Mattis",
                                          "1995-1996");
   gimp_procedure_add_argument (procedure,
-                               gimp_param_spec_drawable ("drawable",
-                                                         "drawable",
-                                                         "The drawable to cut from",
-                                                         FALSE,
-                                                         GIMP_PARAM_READWRITE));
+                               g_param_spec_int ("num-drawables",
+                                                 "num drawables",
+                                                 "The number of drawables",
+                                                 1, G_MAXINT32, 1,
+                                                 GIMP_PARAM_READWRITE));
+  gimp_procedure_add_argument (procedure,
+                               gimp_param_spec_object_array ("drawables",
+                                                             "drawables",
+                                                             "The drawables to cut from",
+                                                             GIMP_TYPE_ITEM,
+                                                             GIMP_PARAM_READWRITE | GIMP_PARAM_NO_VALIDATE));
   gimp_procedure_add_return_value (procedure,
                                    g_param_spec_boolean ("non-empty",
                                                          "non empty",
@@ -697,11 +800,17 @@ register_edit_procs (GimpPDB *pdb)
                                          "Michael Natterer",
                                          "2005");
   gimp_procedure_add_argument (procedure,
-                               gimp_param_spec_drawable ("drawable",
-                                                         "drawable",
-                                                         "The drawable to cut from",
-                                                         FALSE,
-                                                         GIMP_PARAM_READWRITE));
+                               g_param_spec_int ("num-drawables",
+                                                 "num drawables",
+                                                 "The number of drawables",
+                                                 1, G_MAXINT32, 1,
+                                                 GIMP_PARAM_READWRITE));
+  gimp_procedure_add_argument (procedure,
+                               gimp_param_spec_object_array ("drawables",
+                                                             "drawables",
+                                                             "The drawables to cut from",
+                                                             GIMP_TYPE_ITEM,
+                                                             GIMP_PARAM_READWRITE | GIMP_PARAM_NO_VALIDATE));
   gimp_procedure_add_argument (procedure,
                                gimp_param_spec_string ("buffer-name",
                                                        "buffer name",
@@ -734,11 +843,17 @@ register_edit_procs (GimpPDB *pdb)
                                          "Michael Natterer",
                                          "2005");
   gimp_procedure_add_argument (procedure,
-                               gimp_param_spec_drawable ("drawable",
-                                                         "drawable",
-                                                         "The drawable to copy from",
-                                                         FALSE,
-                                                         GIMP_PARAM_READWRITE));
+                               g_param_spec_int ("num-drawables",
+                                                 "num drawables",
+                                                 "The number of drawables",
+                                                 1, G_MAXINT32, 1,
+                                                 GIMP_PARAM_READWRITE));
+  gimp_procedure_add_argument (procedure,
+                               gimp_param_spec_object_array ("drawables",
+                                                             "drawables",
+                                                             "The drawables to copy from",
+                                                             GIMP_TYPE_ITEM,
+                                                             GIMP_PARAM_READWRITE | GIMP_PARAM_NO_VALIDATE));
   gimp_procedure_add_argument (procedure,
                                gimp_param_spec_string ("buffer-name",
                                                        "buffer name",
diff --git a/app/tools/gimpfliptool.c b/app/tools/gimpfliptool.c
index 8714b6182b..855ae73f08 100644
--- a/app/tools/gimpfliptool.c
+++ b/app/tools/gimpfliptool.c
@@ -81,7 +81,7 @@ static void         gimp_flip_tool_draw          (GimpDrawTool         *draw_too
 
 static gchar      * gimp_flip_tool_get_undo_desc (GimpTransformTool    *tr_tool);
 static GeglBuffer * gimp_flip_tool_transform     (GimpTransformTool    *tr_tool,
-                                                  GimpObject           *object,
+                                                  GList                *objects,
                                                   GeglBuffer           *orig_buffer,
                                                   gint                  orig_offset_x,
                                                   gint                  orig_offset_y,
@@ -258,8 +258,11 @@ gimp_flip_tool_cursor_update (GimpTool         *tool,
 {
   GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
   GimpFlipTool      *flip    = GIMP_FLIP_TOOL (tool);
+  GList             *selected_objects;
 
-  if (! gimp_transform_tool_check_active_object (tr_tool, display, NULL))
+  selected_objects = gimp_transform_tool_check_selected_objects (tr_tool, display, NULL);
+
+  if (! selected_objects)
     {
       gimp_tool_set_cursor (tool, display,
                             gimp_tool_control_get_cursor (tool->control),
@@ -267,6 +270,7 @@ gimp_flip_tool_cursor_update (GimpTool         *tool,
                             GIMP_CURSOR_MODIFIER_BAD);
       return;
     }
+  g_list_free (selected_objects);
 
   gimp_tool_control_set_toggled (tool->control,
                                  gimp_flip_tool_get_flip_type (flip) ==
@@ -318,7 +322,7 @@ gimp_flip_tool_get_undo_desc (GimpTransformTool *tr_tool)
 
 static GeglBuffer *
 gimp_flip_tool_transform (GimpTransformTool *tr_tool,
-                          GimpObject        *object,
+                          GList             *objects,
                           GeglBuffer        *orig_buffer,
                           gint               orig_offset_x,
                           gint               orig_offset_y,
@@ -380,9 +384,9 @@ gimp_flip_tool_transform (GimpTransformTool *tr_tool,
        *  normal drawable
        */
 
-      g_return_val_if_fail (GIMP_IS_DRAWABLE (object), NULL);
+      g_return_val_if_fail (GIMP_IS_DRAWABLE (objects->data), NULL);
 
-      ret = gimp_drawable_transform_buffer_flip (GIMP_DRAWABLE (object),
+      ret = gimp_drawable_transform_buffer_flip (GIMP_DRAWABLE (objects->data),
                                                  context,
                                                  orig_buffer,
                                                  orig_offset_x,
@@ -393,43 +397,50 @@ gimp_flip_tool_transform (GimpTransformTool *tr_tool,
                                                  new_offset_x,
                                                  new_offset_y);
     }
-  else if (GIMP_IS_ITEM (object))
-    {
-      /*  this happens for entire drawables, paths and layer groups  */
-
-      GimpItem *item = GIMP_ITEM (object);
-
-      if (gimp_item_get_linked (item))
-        {
-          gimp_item_linked_flip (item, context,
-                                 flip_type, axis, clip_result);
-        }
-      else
-        {
-          clip_result = gimp_item_get_clip (item, clip_result);
-
-          gimp_item_flip (item, context,
-                          flip_type, axis, clip_result);
-        }
-    }
-  else
+  else if (g_list_length (objects) == 1 && GIMP_IS_IMAGE (objects->data))
     {
       /*  this happens for images  */
       GimpTransformToolClass *tr_class = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool);
       GimpProgress           *progress;
 
-      g_return_val_if_fail (GIMP_IS_IMAGE (object), NULL);
-
       progress = gimp_progress_start (GIMP_PROGRESS (tr_tool), FALSE,
                                       "%s", tr_class->progress_text);
 
-      gimp_image_flip_full (GIMP_IMAGE (object), context,
+      gimp_image_flip_full (GIMP_IMAGE (objects->data), context,
                             flip_type, axis, clip_result,
                             progress);
 
       if (progress)
         gimp_progress_end (progress);
     }
+  else
+    {
+      /*  this happens for entire drawables, paths and layer groups  */
+
+      GList *iter;
+
+      for (iter = objects; iter; iter = iter->next)
+        {
+          GimpItem *item;
+
+          g_return_val_if_fail (GIMP_IS_ITEM (iter->data), NULL);
+
+          item = GIMP_ITEM (iter->data);
+
+          if (gimp_item_get_linked (item))
+            {
+              gimp_item_linked_flip (item, context,
+                                     flip_type, axis, clip_result);
+            }
+          else
+            {
+              clip_result = gimp_item_get_clip (item, clip_result);
+
+              gimp_item_flip (item, context,
+                              flip_type, axis, clip_result);
+            }
+        }
+    }
 
   return ret;
 }
diff --git a/app/tools/gimptransformgridtool.c b/app/tools/gimptransformgridtool.c
index 191c56dc4f..842b3dec6f 100644
--- a/app/tools/gimptransformgridtool.c
+++ b/app/tools/gimptransformgridtool.c
@@ -162,7 +162,7 @@ static gchar   * gimp_transform_grid_tool_get_undo_desc      (GimpTransformTool
 static GimpTransformDirection gimp_transform_grid_tool_get_direction
                                                              (GimpTransformTool      *tr_tool);
 static GeglBuffer * gimp_transform_grid_tool_transform       (GimpTransformTool      *tr_tool,
-                                                              GimpObject             *object,
+                                                              GList                  *objects,
                                                               GeglBuffer             *orig_buffer,
                                                               gint                    orig_offset_x,
                                                               gint                    orig_offset_y,
@@ -176,7 +176,7 @@ static gchar  * gimp_transform_grid_tool_real_get_undo_desc  (GimpTransformGridT
 static void     gimp_transform_grid_tool_real_update_widget  (GimpTransformGridTool  *tg_tool);
 static void     gimp_transform_grid_tool_real_widget_changed (GimpTransformGridTool  *tg_tool);
 static GeglBuffer * gimp_transform_grid_tool_real_transform  (GimpTransformGridTool  *tg_tool,
-                                                              GimpObject             *object,
+                                                              GList                  *objects,
                                                               GeglBuffer             *orig_buffer,
                                                               gint                    orig_offset_x,
                                                               gint                    orig_offset_y,
@@ -215,9 +215,9 @@ static gboolean  gimp_transform_grid_tool_composited_preview (GimpTransformGridT
 static void      gimp_transform_grid_tool_update_sensitivity (GimpTransformGridTool  *tg_tool);
 static void      gimp_transform_grid_tool_update_preview     (GimpTransformGridTool  *tg_tool);
 static void      gimp_transform_grid_tool_update_filters     (GimpTransformGridTool  *tg_tool);
-static void      gimp_transform_grid_tool_hide_active_object (GimpTransformGridTool  *tg_tool,
-                                                              GimpObject             *object);
-static void      gimp_transform_grid_tool_show_active_object (GimpTransformGridTool  *tg_tool);
+static void   gimp_transform_grid_tool_hide_selected_objects (GimpTransformGridTool  *tg_tool,
+                                                              GList                  *objects);
+static void   gimp_transform_grid_tool_show_selected_objects (GimpTransformGridTool  *tg_tool);
 
 static void      gimp_transform_grid_tool_add_filter         (GimpDrawable           *drawable,
                                                               AddFilterData          *data);
@@ -340,34 +340,32 @@ gimp_transform_grid_tool_initialize (GimpTool     *tool,
   GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tool);
   GimpImage                *image      = gimp_display_get_image (display);
   GList                    *drawables;
-  GimpObject               *object;
+  GList                    *objects;
+  GList                    *iter;
   UndoInfo                 *undo_info;
 
-  object = gimp_transform_tool_check_active_object (tr_tool, display, error);
+  objects = gimp_transform_tool_check_selected_objects (tr_tool, display, error);
 
-  if (! object)
+  if (! objects)
     return FALSE;
 
   drawables = gimp_image_get_selected_drawables (image);
-  if (g_list_length (drawables) != 1)
+  if (g_list_length (drawables) < 1)
     {
-      if (g_list_length (drawables) > 1)
-        gimp_tool_message_literal (tool, display,
-                                   _("Cannot modify multiple drawables. Select only one."));
-      else
-        gimp_tool_message_literal (tool, display, _("No active drawables."));
-
+      gimp_tool_message_literal (tool, display, _("No selected drawables."));
       g_list_free (drawables);
+      g_list_free (objects);
       return FALSE;
     }
 
-  tool->display   = display;
-  tool->drawables = drawables;
+  tool->display    = display;
+  tool->drawables  = drawables;
 
-  tr_tool->object = object;
+  tr_tool->objects = objects;
 
-  if (GIMP_IS_DRAWABLE (object))
-    gimp_viewable_preview_freeze (GIMP_VIEWABLE (object));
+  for (iter = objects; iter; iter = iter->next)
+    if (GIMP_IS_DRAWABLE (iter->data))
+      gimp_viewable_preview_freeze (iter->data);
 
   /*  Initialize the transform_grid tool dialog  */
   if (! tg_tool->gui)
@@ -387,7 +385,7 @@ gimp_transform_grid_tool_initialize (GimpTool     *tool,
   /*  Get the on-canvas gui  */
   tg_tool->widget = gimp_transform_grid_tool_get_widget (tg_tool);
 
-  gimp_transform_grid_tool_hide_active_object (tg_tool, object);
+  gimp_transform_grid_tool_hide_selected_objects (tg_tool, objects);
 
   /*  start drawing the bounding box and handles...  */
   gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
@@ -562,9 +560,11 @@ gimp_transform_grid_tool_cursor_update (GimpTool         *tool,
                                         GimpDisplay      *display)
 {
   GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
+  GList             *objects;
 
-  if (display != tool->display &&
-      ! gimp_transform_tool_check_active_object (tr_tool, display, NULL))
+  objects = gimp_transform_tool_check_selected_objects (tr_tool, display, NULL);
+
+  if (display != tool->display && ! objects)
     {
       gimp_tool_set_cursor (tool, display,
                             gimp_tool_control_get_cursor (tool->control),
@@ -572,6 +572,7 @@ gimp_transform_grid_tool_cursor_update (GimpTool         *tool,
                             GIMP_CURSOR_MODIFIER_BAD);
       return;
     }
+  g_list_free (objects);
 
   GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
 }
@@ -703,21 +704,21 @@ gimp_transform_grid_tool_options_notify (GimpTool         *tool,
       if (tg_tool->preview)
         {
           GimpDisplay *display;
-          GimpObject  *object;
+          GList       *objects;
 
           display = tool->display;
-          object  = gimp_transform_tool_get_active_object (tr_tool, display);
+          objects = gimp_transform_tool_get_selected_objects (tr_tool, display);
 
-          if (object)
+          if (objects)
             {
               if (tg_options->show_preview &&
                   ! gimp_transform_grid_tool_composited_preview (tg_tool))
                 {
-                  gimp_transform_grid_tool_hide_active_object (tg_tool, object);
+                  gimp_transform_grid_tool_hide_selected_objects (tg_tool, objects);
                 }
               else
                 {
-                  gimp_transform_grid_tool_show_active_object (tg_tool);
+                  gimp_transform_grid_tool_show_selected_objects (tg_tool);
                 }
             }
 
@@ -775,8 +776,7 @@ gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool)
         }
       else
         {
-          g_return_if_fail (g_list_length (tool->drawables) == 1);
-
+          /* TODO */
           pickable = GIMP_PICKABLE (tool->drawables->data);
         }
 
@@ -1017,7 +1017,7 @@ gimp_transform_grid_tool_get_direction (GimpTransformTool *tr_tool)
 
 static GeglBuffer *
 gimp_transform_grid_tool_transform (GimpTransformTool  *tr_tool,
-                                    GimpObject         *object,
+                                    GList              *objects,
                                     GeglBuffer         *orig_buffer,
                                     gint                orig_offset_x,
                                     gint                orig_offset_y,
@@ -1035,7 +1035,7 @@ gimp_transform_grid_tool_transform (GimpTransformTool  *tr_tool,
    */
   new_buffer =
     GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->transform (tg_tool,
-                                                             object,
+                                                             objects,
                                                              orig_buffer,
                                                              orig_offset_x,
                                                              orig_offset_y,
@@ -1101,7 +1101,7 @@ gimp_transform_grid_tool_real_widget_changed (GimpTransformGridTool *tg_tool)
 
 static GeglBuffer *
 gimp_transform_grid_tool_real_transform (GimpTransformGridTool  *tg_tool,
-                                         GimpObject             *object,
+                                         GList                  *objects,
                                          GeglBuffer             *orig_buffer,
                                          gint                    orig_offset_x,
                                          gint                    orig_offset_y,
@@ -1112,7 +1112,7 @@ gimp_transform_grid_tool_real_transform (GimpTransformGridTool  *tg_tool,
   GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
 
   return GIMP_TRANSFORM_TOOL_CLASS (parent_class)->transform (tr_tool,
-                                                              object,
+                                                              objects,
                                                               orig_buffer,
                                                               orig_offset_x,
                                                               orig_offset_y,
@@ -1212,7 +1212,7 @@ gimp_transform_grid_tool_halt (GimpTransformGridTool *tg_tool)
       tg_tool->undo_list = NULL;
     }
 
-  gimp_transform_grid_tool_show_active_object (tg_tool);
+  gimp_transform_grid_tool_show_selected_objects (tg_tool);
 
   if (tg_options->direction_chain_button)
     {
@@ -1227,12 +1227,16 @@ gimp_transform_grid_tool_halt (GimpTransformGridTool *tg_tool)
   g_list_free (tool->drawables);
   tool->drawables = NULL;
 
-  if (tr_tool->object)
+  if (tr_tool->objects)
     {
-      if (GIMP_IS_DRAWABLE (tr_tool->object))
-        gimp_viewable_preview_thaw (GIMP_VIEWABLE (tr_tool->object));
+      GList *iter;
+
+      for (iter = tr_tool->objects; iter; iter = iter->next)
+        if (GIMP_IS_DRAWABLE (iter->data))
+          gimp_viewable_preview_thaw (iter->data);
 
-      tr_tool->object = NULL;
+      g_list_free (tr_tool->objects);
+      tr_tool->objects = NULL;
     }
 }
 
@@ -1313,11 +1317,11 @@ gimp_transform_grid_tool_prepare (GimpTransformGridTool *tg_tool,
 
   if (tg_tool->gui)
     {
-      GimpObject *object = gimp_transform_tool_get_active_object (tr_tool,
-                                                                  display);
+      GList *objects = gimp_transform_tool_get_selected_objects (tr_tool, display);
 
       gimp_tool_gui_set_shell (tg_tool->gui, gimp_display_get_shell (display));
-      gimp_tool_gui_set_viewable (tg_tool->gui, GIMP_VIEWABLE (object));
+      gimp_tool_gui_set_viewables (tg_tool->gui, objects);
+      g_list_free (objects);
     }
 
   if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->prepare)
@@ -1786,8 +1790,9 @@ gimp_transform_grid_tool_update_preview (GimpTransformGridTool *tg_tool)
 static void
 gimp_transform_grid_tool_update_filters (GimpTransformGridTool *tg_tool)
 {
-  GimpTool                 *tool    = GIMP_TOOL (tg_tool);
-  GimpTransformGridOptions *options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
+  GimpTool                 *tool      = GIMP_TOOL (tg_tool);
+  GimpTransformGridOptions *options   = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
+  GimpImage                *image     = gimp_display_get_image (tool->display);
   GHashTable               *new_drawables;
   GList                    *drawables;
   GList                    *iter;
@@ -1797,24 +1802,10 @@ gimp_transform_grid_tool_update_filters (GimpTransformGridTool *tg_tool)
   if (! tg_tool->filters)
     return;
 
-  g_return_if_fail (g_list_length (tool->drawables) == 1);
-
-  if (options->preview_linked &&
-      gimp_item_get_linked (GIMP_ITEM (tool->drawables->data)))
-    {
-      GimpImage *image = gimp_display_get_image (tool->display);
-
-      drawables = gimp_image_item_list_get_list (image,
-                                                 GIMP_ITEM_TYPE_LAYERS |
-                                                 GIMP_ITEM_TYPE_CHANNELS,
-                                                 GIMP_ITEM_SET_LINKED);
-
-      drawables = gimp_image_item_list_filter (drawables);
-    }
+  if (options->preview_linked)
+    drawables = gimp_image_item_list_linked (image, tool->drawables);
   else
-    {
-      drawables = g_list_copy (tool->drawables);
-    }
+    drawables = gimp_image_item_list_filter (g_list_copy (tool->drawables));
 
   new_drawables = g_hash_table_new (g_direct_hash, g_direct_equal);
 
@@ -1848,62 +1839,80 @@ gimp_transform_grid_tool_update_filters (GimpTransformGridTool *tg_tool)
 }
 
 static void
-gimp_transform_grid_tool_hide_active_object (GimpTransformGridTool *tg_tool,
-                                             GimpObject            *object)
+gimp_transform_grid_tool_hide_selected_objects (GimpTransformGridTool *tg_tool,
+                                                GList                 *objects)
 {
   GimpTransformGridOptions *options    = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
   GimpTransformOptions     *tr_options = GIMP_TRANSFORM_OPTIONS (options);
   GimpDisplay              *display    = GIMP_TOOL (tg_tool)->display;
   GimpImage                *image      = gimp_display_get_image (display);
 
+  g_return_if_fail (tr_options->type != GIMP_TRANSFORM_TYPE_IMAGE ||
+                    (g_list_length (objects) == 1 && GIMP_IS_IMAGE (objects->data)));
+
+  g_list_free (tg_tool->hidden_objects);
+  tg_tool->hidden_objects = NULL;
+
   if (options->show_preview)
     {
-      /*  hide only complete layers and channels, not layer masks  */
-      if (tr_options->type == GIMP_TRANSFORM_TYPE_LAYER &&
-          ! options->composited_preview                 &&
-          GIMP_IS_DRAWABLE (object)                     &&
-          ! GIMP_IS_LAYER_MASK (object)                 &&
-          gimp_item_get_visible (GIMP_ITEM (object))    &&
-          gimp_channel_is_empty (gimp_image_get_mask (image)))
+      if (tr_options->type == GIMP_TRANSFORM_TYPE_IMAGE)
         {
-          tg_tool->hidden_object = object;
-
-          gimp_item_set_visible (GIMP_ITEM (object), FALSE, FALSE);
-
-          gimp_projection_flush (gimp_image_get_projection (image));
+          tg_tool->hidden_objects = g_list_copy (objects);
+          gimp_display_shell_set_show_image (gimp_display_get_shell (display),
+                                             FALSE);
         }
-      else if (tr_options->type == GIMP_TRANSFORM_TYPE_IMAGE)
+      else
         {
-          tg_tool->hidden_object = object;
+          /*  hide only complete layers and channels, not layer masks  */
+          GList *iter;
 
-          gimp_display_shell_set_show_image (gimp_display_get_shell (display),
-                                             FALSE);
+          for (iter = objects; iter; iter = iter->next)
+            {
+              if (tr_options->type == GIMP_TRANSFORM_TYPE_LAYER &&
+                  ! options->composited_preview                 &&
+                  GIMP_IS_DRAWABLE (iter->data)                 &&
+                  ! GIMP_IS_LAYER_MASK (iter->data)             &&
+                  gimp_item_get_visible (iter->data)            &&
+                  gimp_channel_is_empty (gimp_image_get_mask (image)))
+                {
+                  tg_tool->hidden_objects = g_list_prepend (tg_tool->hidden_objects, iter->data);
+
+                  gimp_item_set_visible (iter->data, FALSE, FALSE);
+                }
+            }
+
+          gimp_projection_flush (gimp_image_get_projection (image));
         }
     }
 }
 
 static void
-gimp_transform_grid_tool_show_active_object (GimpTransformGridTool *tg_tool)
+gimp_transform_grid_tool_show_selected_objects (GimpTransformGridTool *tg_tool)
 {
-  if (tg_tool->hidden_object)
+  if (tg_tool->hidden_objects)
     {
       GimpDisplay *display = GIMP_TOOL (tg_tool)->display;
       GimpImage   *image   = gimp_display_get_image (display);
+      GList       *iter;
 
-      if (GIMP_IS_ITEM (tg_tool->hidden_object))
+      for (iter = tg_tool->hidden_objects; iter; iter = iter->next)
         {
-          gimp_item_set_visible (GIMP_ITEM (tg_tool->hidden_object), TRUE,
-                                            FALSE);
-        }
-      else
-        {
-          g_return_if_fail (GIMP_IS_IMAGE (tg_tool->hidden_object));
+          if (GIMP_IS_ITEM (iter->data))
+            {
+              gimp_item_set_visible (GIMP_ITEM (iter->data), TRUE,
+                                     FALSE);
+            }
+          else
+            {
+              g_return_if_fail (GIMP_IS_IMAGE (iter->data));
 
-          gimp_display_shell_set_show_image (gimp_display_get_shell (display),
-                                             TRUE);
+              gimp_display_shell_set_show_image (gimp_display_get_shell (display),
+                                                 TRUE);
+            }
         }
 
-      tg_tool->hidden_object = NULL;
+      g_list_free (tg_tool->hidden_objects);
+      tg_tool->hidden_objects = NULL;
 
       gimp_image_flush (image);
     }
diff --git a/app/tools/gimptransformgridtool.h b/app/tools/gimptransformgridtool.h
index 3a8341841b..79ec263c08 100644
--- a/app/tools/gimptransformgridtool.h
+++ b/app/tools/gimptransformgridtool.h
@@ -56,7 +56,7 @@ struct _GimpTransformGridTool
   GList              *redo_list;        /*  list of all undone states,
                                             NULL when nothing undone */
 
-  GimpObject         *hidden_object;    /*  the object that was hidden during
+  GList              *hidden_objects;   /*  the objects that was hidden during
                                             the transform                         */
 
   GimpToolWidget     *widget;
@@ -92,7 +92,7 @@ struct _GimpTransformGridToolClass
   void             (* update_widget)  (GimpTransformGridTool  *tg_tool);
   void             (* widget_changed) (GimpTransformGridTool  *tg_tool);
   GeglBuffer     * (* transform)      (GimpTransformGridTool  *tg_tool,
-                                       GimpObject             *object,
+                                       GList                  *objects,
                                        GeglBuffer             *orig_buffer,
                                        gint                    orig_offset_x,
                                        gint                    orig_offset_y,
diff --git a/app/tools/gimptransformtool.c b/app/tools/gimptransformtool.c
index 286900eebd..7b8a81b708 100644
--- a/app/tools/gimptransformtool.c
+++ b/app/tools/gimptransformtool.c
@@ -72,7 +72,7 @@ static void                     gimp_transform_tool_control            (GimpTool
 static gchar                  * gimp_transform_tool_real_get_undo_desc (GimpTransformTool  *tr_tool);
 static GimpTransformDirection   gimp_transform_tool_real_get_direction (GimpTransformTool  *tr_tool);
 static GeglBuffer             * gimp_transform_tool_real_transform     (GimpTransformTool  *tr_tool,
-                                                                        GimpObject         *object,
+                                                                        GList              *objects,
                                                                         GeglBuffer         *orig_buffer,
                                                                         gint                orig_offset_x,
                                                                         gint                orig_offset_y,
@@ -159,7 +159,7 @@ gimp_transform_tool_real_get_direction (GimpTransformTool *tr_tool)
 
 static GeglBuffer *
 gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
-                                    GimpObject        *object,
+                                    GList             *objects,
                                     GeglBuffer        *orig_buffer,
                                     gint               orig_offset_x,
                                     gint               orig_offset_y,
@@ -186,13 +186,11 @@ gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
 
   if (orig_buffer)
     {
-      /*  this happens when transforming a selection cut out of a
-       *  normal drawable
+      /*  this happens when transforming a selection cut out of
+       *  normal drawables.
        */
 
-      g_return_val_if_fail (GIMP_IS_DRAWABLE (object), NULL);
-
-      ret = gimp_drawable_transform_buffer_affine (GIMP_DRAWABLE (object),
+      ret = gimp_drawable_transform_buffer_affine (objects->data,
                                                    context,
                                                    orig_buffer,
                                                    orig_offset_x,
@@ -206,45 +204,34 @@ gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
                                                    new_offset_y,
                                                    progress);
     }
-  else if (GIMP_IS_ITEM (object))
+  else if (g_list_length (objects) == 1 && GIMP_IS_IMAGE (objects->data))
+    {
+      /*  this happens for images  */
+
+      gimp_image_transform (objects->data, context,
+                            &tr_tool->transform,
+                            direction,
+                            options->interpolation,
+                            clip,
+                            progress);
+    }
+  else
     {
+      GList *items;
+
       /*  this happens for entire drawables, paths and layer groups  */
+      g_return_val_if_fail (g_list_length (objects) > 0, NULL);
 
-      GimpItem *item = GIMP_ITEM (object);
+      items = gimp_image_item_list_linked (gimp_item_get_image (objects->data), objects);
 
-      if (gimp_item_get_linked (item))
-        {
-          gimp_item_linked_transform (item, context,
+      gimp_image_item_list_transform (gimp_item_get_image (objects->data),
+                                      items, context,
                                       &tr_tool->transform,
                                       direction,
                                       options->interpolation,
                                       clip,
                                       progress);
-        }
-      else
-        {
-          clip = gimp_item_get_clip (item, clip);
-
-          gimp_item_transform (item, context,
-                               &tr_tool->transform,
-                               direction,
-                               options->interpolation,
-                               clip,
-                               progress);
-        }
-    }
-  else
-    {
-      /*  this happens for images  */
-
-      g_return_val_if_fail (GIMP_IS_IMAGE (object), NULL);
-
-      gimp_image_transform (GIMP_IMAGE (object), context,
-                            &tr_tool->transform,
-                            direction,
-                            options->interpolation,
-                            clip,
-                            progress);
+      g_list_free (items);
     }
 
   if (progress)
@@ -280,11 +267,11 @@ gimp_transform_tool_confirm (GimpTransformTool *tr_tool,
   GimpTransformOptions *options          = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
   GimpDisplayShell     *shell            = gimp_display_get_shell (display);
   GimpImage            *image            = gimp_display_get_image (display);
-  GimpObject           *active_object;
+  GList                *selected_objects;
   gdouble               max_ratio        = 0.0;
   GimpObject           *max_ratio_object = NULL;
 
-  active_object = gimp_transform_tool_get_active_object (tr_tool, display);
+  selected_objects = gimp_transform_tool_get_selected_objects (tr_tool, display);
 
   if (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix)
     {
@@ -292,7 +279,7 @@ gimp_transform_tool_confirm (GimpTransformTool *tr_tool,
       GimpTransformDirection  direction;
       GeglRectangle           selection_bounds;
       gboolean                selection_empty = TRUE;
-      GList                  *objects;
+      GList                  *objects         = NULL;
       GList                  *iter;
 
       transform = tr_tool->transform;
@@ -302,26 +289,31 @@ gimp_transform_tool_confirm (GimpTransformTool *tr_tool,
       if (direction == GIMP_TRANSFORM_BACKWARD)
         gimp_matrix3_invert (&transform);
 
-      if (options->type == GIMP_TRANSFORM_TYPE_LAYER &&
-          ! gimp_viewable_get_children (GIMP_VIEWABLE (active_object)))
+      if (options->type == GIMP_TRANSFORM_TYPE_LAYER)
         {
-          selection_empty = ! gimp_item_bounds (
-            GIMP_ITEM (gimp_image_get_mask (image)),
-            &selection_bounds.x,     &selection_bounds.y,
-            &selection_bounds.width, &selection_bounds.height);
+          for (iter = selected_objects; iter; iter = iter->next)
+            if (! gimp_viewable_get_children (GIMP_VIEWABLE (iter->data)))
+          {
+            if (gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
+                                  &selection_bounds.x,     &selection_bounds.y,
+                                  &selection_bounds.width, &selection_bounds.height))
+              {
+                selection_empty = FALSE;
+                break;
+              }
+          }
         }
 
-      if (selection_empty              &&
-          GIMP_IS_ITEM (active_object) &&
-          gimp_item_get_linked (GIMP_ITEM (active_object)))
+      if (selection_empty  &&
+          selected_objects &&
+          GIMP_IS_ITEM (selected_objects->data))
         {
-          objects = gimp_image_item_list_get_list (image,
-                                                   GIMP_ITEM_TYPE_ALL,
-                                                   GIMP_ITEM_SET_LINKED);
+          objects = gimp_image_item_list_linked (image, selected_objects);
+          g_list_free (selected_objects);
         }
       else
         {
-          objects = g_list_append (NULL, active_object);
+          objects = selected_objects;
         }
 
       if (options->type == GIMP_TRANSFORM_TYPE_IMAGE)
@@ -506,22 +498,24 @@ gimp_transform_tool_bounds (GimpTransformTool *tr_tool,
     {
     case GIMP_TRANSFORM_TYPE_LAYER:
       {
-        GimpDrawable *drawable;
-        gint          offset_x;
-        gint          offset_y;
-        gint          x, y;
-        gint          width, height;
+        GList *drawables;
+        gint   offset_x;
+        gint   offset_y;
+        gint   x, y;
+        gint   width, height;
 
-        drawable = gimp_image_get_active_drawable (image);
+        drawables = gimp_image_get_selected_drawables (image);
 
-        gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y);
+        gimp_item_get_offset (GIMP_ITEM (drawables->data), &offset_x, &offset_y);
 
-        non_empty = gimp_item_mask_intersect (GIMP_ITEM (drawable),
+        non_empty = gimp_item_mask_intersect (GIMP_ITEM (drawables->data),
                                               &x, &y, &width, &height);
         tr_tool->x1 = x + offset_x;
         tr_tool->y1 = y + offset_y;
         tr_tool->x2 = x + width  + offset_x;
         tr_tool->y2 = y + height + offset_y;
+
+        g_list_free (drawables);
       }
       break;
 
@@ -612,13 +606,13 @@ gimp_transform_tool_recalc_matrix (GimpTransformTool *tr_tool,
     GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix (tr_tool);
 }
 
-GimpObject *
-gimp_transform_tool_get_active_object (GimpTransformTool  *tr_tool,
-                                       GimpDisplay        *display)
+GList *
+gimp_transform_tool_get_selected_objects (GimpTransformTool  *tr_tool,
+                                          GimpDisplay        *display)
 {
   GimpTransformOptions *options;
   GimpImage            *image;
-  GimpObject           *object = NULL;
+  GList                *objects = NULL;
 
   g_return_val_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool), NULL);
   g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL);
@@ -629,41 +623,40 @@ gimp_transform_tool_get_active_object (GimpTransformTool  *tr_tool,
 
   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
 
-  if (tr_tool->object)
-    return tr_tool->object;
+  if (tr_tool->objects)
+    return g_list_copy (tr_tool->objects);
 
   switch (options->type)
     {
     case GIMP_TRANSFORM_TYPE_LAYER:
-      object = GIMP_OBJECT (gimp_image_get_active_drawable (image));
+      objects = gimp_image_get_selected_drawables (image);
       break;
 
     case GIMP_TRANSFORM_TYPE_SELECTION:
-      object = GIMP_OBJECT (gimp_image_get_mask (image));
-
-      if (gimp_channel_is_empty (GIMP_CHANNEL (object)))
-        object = NULL;
+      if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
+        objects = g_list_prepend (NULL, gimp_image_get_mask (image));
       break;
 
     case GIMP_TRANSFORM_TYPE_PATH:
-      object = GIMP_OBJECT (gimp_image_get_active_vectors (image));
+      objects = g_list_copy (gimp_image_get_selected_vectors (image));
       break;
 
     case GIMP_TRANSFORM_TYPE_IMAGE:
-      object = GIMP_OBJECT (image);
+      objects = g_list_prepend (NULL, image);
       break;
     }
 
-  return object;
+  return objects;
 }
 
-GimpObject *
-gimp_transform_tool_check_active_object (GimpTransformTool  *tr_tool,
-                                         GimpDisplay        *display,
-                                         GError            **error)
+GList *
+gimp_transform_tool_check_selected_objects (GimpTransformTool  *tr_tool,
+                                            GimpDisplay        *display,
+                                            GError            **error)
 {
   GimpTransformOptions *options;
-  GimpObject           *object;
+  GList                *objects;
+  GList                *iter;
   const gchar          *null_message   = NULL;
   const gchar          *locked_message = NULL;
   GimpGuiConfig        *config         = GIMP_GUI_CONFIG (display->gimp->config);
@@ -674,35 +667,35 @@ gimp_transform_tool_check_active_object (GimpTransformTool  *tr_tool,
 
   options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
 
-  object = gimp_transform_tool_get_active_object (tr_tool, display);
+  objects = gimp_transform_tool_get_selected_objects (tr_tool, display);
 
   switch (options->type)
     {
     case GIMP_TRANSFORM_TYPE_LAYER:
       null_message = _("There is no layer to transform.");
 
-      if (object)
+      for (iter = objects; iter; iter = iter->next)
         {
-          GimpItem *item = GIMP_ITEM (object);
+          GimpItem *item = iter->data;
 
           if (gimp_item_is_content_locked (item))
-            locked_message = _("The active layer's pixels are locked.");
+            locked_message = _("A selected layer's pixels are locked.");
           else if (gimp_item_is_position_locked (item))
-            locked_message = _("The active layer's position and size are locked.");
+            locked_message = _("A selected layer's position and size are locked.");
 
           if (! gimp_item_is_visible (item) &&
               ! config->edit_non_visible &&
-              object != tr_tool->object) /* see bug #759194 */
+              ! g_list_find (tr_tool->objects, item)) /* see bug #759194 */
             {
               g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
-                                   _("The active layer is not visible."));
+                                   _("A selected layer is not visible."));
               return NULL;
             }
 
           if (! gimp_transform_tool_bounds (tr_tool, display))
             {
               g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
-                                   _("The selection does not intersect with the layer."));
+                                   _("The selection does not intersect with a selected layer."));
               return NULL;
             }
         }
@@ -711,9 +704,9 @@ gimp_transform_tool_check_active_object (GimpTransformTool  *tr_tool,
     case GIMP_TRANSFORM_TYPE_SELECTION:
       null_message = _("There is no selection to transform.");
 
-      if (object)
+      for (iter = objects; iter; iter = iter->next)
         {
-          GimpItem *item = GIMP_ITEM (object);
+          GimpItem *item = iter->data;
 
           /* cannot happen, so don't translate these messages */
           if (gimp_item_is_content_locked (item))
@@ -726,9 +719,9 @@ gimp_transform_tool_check_active_object (GimpTransformTool  *tr_tool,
     case GIMP_TRANSFORM_TYPE_PATH:
       null_message = _("There is no path to transform.");
 
-      if (object)
+      for (iter = objects; iter; iter = iter->next)
         {
-          GimpItem *item = GIMP_ITEM (object);
+          GimpItem *item = iter->data;
 
           if (gimp_item_is_content_locked (item))
             locked_message = _("The active path's strokes are locked.");
@@ -745,7 +738,7 @@ gimp_transform_tool_check_active_object (GimpTransformTool  *tr_tool,
       break;
     }
 
-  if (! object)
+  if (! objects)
     {
       g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, null_message);
       if (error)
@@ -757,11 +750,11 @@ gimp_transform_tool_check_active_object (GimpTransformTool  *tr_tool,
     {
       g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, locked_message);
       if (error)
-        gimp_tools_blink_lock_box (display->gimp, GIMP_ITEM (object));
+        gimp_tools_blink_lock_box (display->gimp, GIMP_ITEM (objects->data));
       return NULL;
     }
 
-  return object;
+  return objects;
 }
 
 gboolean
@@ -770,9 +763,8 @@ gimp_transform_tool_transform (GimpTransformTool *tr_tool,
 {
   GimpTool             *tool;
   GimpTransformOptions *options;
-  GimpContext          *context;
   GimpImage            *image;
-  GimpObject           *active_object;
+  GList                *selected_objects;
   GeglBuffer           *orig_buffer   = NULL;
   gint                  orig_offset_x = 0;
   gint                  orig_offset_y = 0;
@@ -789,15 +781,14 @@ gimp_transform_tool_transform (GimpTransformTool *tr_tool,
 
   tool    = GIMP_TOOL (tr_tool);
   options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
-  context = GIMP_CONTEXT (options);
   image   = gimp_display_get_image (display);
 
   g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
 
-  active_object = gimp_transform_tool_check_active_object (tr_tool, display,
-                                                           &error);
+  selected_objects = gimp_transform_tool_check_selected_objects (tr_tool, display,
+                                                                 &error);
 
-  if (! active_object)
+  if (! selected_objects)
     {
       gimp_tool_message_literal (tool, display, error->message);
       g_clear_error (&error);
@@ -835,12 +826,11 @@ gimp_transform_tool_transform (GimpTransformTool *tr_tool,
   switch (options->type)
     {
     case GIMP_TRANSFORM_TYPE_LAYER:
-      if (! gimp_viewable_get_children (GIMP_VIEWABLE (active_object)) &&
-          ! gimp_channel_is_empty (gimp_image_get_mask (image)))
+      if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
         {
           orig_buffer = gimp_drawable_transform_cut (
-            GIMP_DRAWABLE (active_object),
-            context,
+            selected_objects,
+            GIMP_CONTEXT (options),
             &orig_offset_x,
             &orig_offset_y,
             &new_layer);
@@ -857,7 +847,7 @@ gimp_transform_tool_transform (GimpTransformTool *tr_tool,
    */
   new_buffer = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->transform (
     tr_tool,
-    active_object,
+    selected_objects,
     orig_buffer,
     orig_offset_x,
     orig_offset_y,
@@ -876,7 +866,7 @@ gimp_transform_tool_transform (GimpTransformTool *tr_tool,
           /*  paste the new transformed image to the image...also implement
            *  undo...
            */
-          gimp_drawable_transform_paste (GIMP_DRAWABLE (active_object),
+          gimp_drawable_transform_paste (GIMP_DRAWABLE (selected_objects->data),
                                          new_buffer, buffer_profile,
                                          new_offset_x, new_offset_y,
                                          new_layer);
@@ -902,6 +892,8 @@ gimp_transform_tool_transform (GimpTransformTool *tr_tool,
 
   gimp_image_flush (image);
 
+  g_list_free (selected_objects);
+
   return TRUE;
 }
 
diff --git a/app/tools/gimptransformtool.h b/app/tools/gimptransformtool.h
index afe17e3bd4..b43fcb7e6d 100644
--- a/app/tools/gimptransformtool.h
+++ b/app/tools/gimptransformtool.h
@@ -47,7 +47,9 @@ struct _GimpTransformTool
 {
   GimpDrawTool       parent_instance;
 
-  GimpObject        *object;
+  GList             *objects;            /*  List of GimpObject initially
+                                             selected and set for
+                                             transform processing.        */
 
   gint               x1, y1;             /*  upper left hand coordinate   */
   gint               x2, y2;             /*  lower right hand coords      */
@@ -68,7 +70,7 @@ struct _GimpTransformToolClass
   gchar                  * (* get_undo_desc) (GimpTransformTool  *tr_tool);
   GimpTransformDirection   (* get_direction) (GimpTransformTool  *tr_tool);
   GeglBuffer             * (* transform)     (GimpTransformTool  *tr_tool,
-                                              GimpObject         *object,
+                                              GList              *objects,
                                               GeglBuffer         *orig_buffer,
                                               gint                orig_offset_x,
                                               gint                orig_offset_y,
@@ -83,9 +85,9 @@ struct _GimpTransformToolClass
 
 GType        gimp_transform_tool_get_type            (void) G_GNUC_CONST;
 
-GimpObject * gimp_transform_tool_get_active_object   (GimpTransformTool  *tr_tool,
+GList     * gimp_transform_tool_get_selected_objects (GimpTransformTool  *tr_tool,
                                                       GimpDisplay        *display);
-GimpObject * gimp_transform_tool_check_active_object (GimpTransformTool  *tr_tool,
+GList   * gimp_transform_tool_check_selected_objects (GimpTransformTool  *tr_tool,
                                                       GimpDisplay        *display,
                                                       GError            **error);
 
diff --git a/libgimp/gimpedit_pdb.c b/libgimp/gimpedit_pdb.c
index 5d922d7db1..00d543b7d3 100644
--- a/libgimp/gimpedit_pdb.c
+++ b/libgimp/gimpedit_pdb.c
@@ -36,31 +36,35 @@
 
 /**
  * gimp_edit_cut:
- * @drawable: The drawable to cut from.
+ * @num_drawables: The number of drawables.
+ * @drawables: (array length=num_drawables) (element-type GimpItem): The drawables to cut from.
  *
- * Cut from the specified drawable.
+ * Cut from the specified drawables.
  *
  * If there is a selection in the image, then the area specified by the
- * selection is cut from the specified drawable and placed in an
+ * selection is cut from the specified drawables and placed in an
  * internal GIMP edit buffer. It can subsequently be retrieved using
- * the gimp_edit_paste() command. If there is no selection, then the
- * specified drawable will be removed and its contents stored in the
- * internal GIMP edit buffer. This procedure will fail if the selected
- * area lies completely outside the bounds of the current drawable and
- * there is nothing to copy from.
+ * the gimp_edit_paste() command. If there is no selection and only one
+ * specified drawable, then the specified drawable will be removed and
+ * its contents stored in the internal GIMP edit buffer. This procedure
+ * will fail if the selected area lies completely outside the bounds of
+ * the current drawables and there is nothing to cut from.
  *
  * Returns: TRUE if the cut was successful, FALSE if there was nothing to copy from.
  **/
 gboolean
-gimp_edit_cut (GimpDrawable *drawable)
+gimp_edit_cut (gint             num_drawables,
+               const GimpItem **drawables)
 {
   GimpValueArray *args;
   GimpValueArray *return_vals;
   gboolean non_empty = FALSE;
 
   args = gimp_value_array_new_from_types (NULL,
-                                          GIMP_TYPE_DRAWABLE, drawable,
+                                          G_TYPE_INT, num_drawables,
+                                          GIMP_TYPE_OBJECT_ARRAY, NULL,
                                           G_TYPE_NONE);
+  gimp_value_set_object_array (gimp_value_array_index (args, 1), GIMP_TYPE_ITEM, (GObject **) drawables, 
num_drawables);
 
   return_vals = gimp_pdb_run_procedure_array (gimp_get_pdb (),
                                               "gimp-edit-cut",
@@ -252,7 +256,8 @@ gimp_edit_paste_as_new_image (void)
 
 /**
  * gimp_edit_named_cut:
- * @drawable: The drawable to cut from.
+ * @num_drawables: The number of drawables.
+ * @drawables: (array length=num_drawables) (element-type GimpItem): The drawables to cut from.
  * @buffer_name: The name of the buffer to create.
  *
  * Cut into a named buffer.
@@ -269,17 +274,20 @@ gimp_edit_paste_as_new_image (void)
  * Since: 2.4
  **/
 gchar *
-gimp_edit_named_cut (GimpDrawable *drawable,
-                     const gchar  *buffer_name)
+gimp_edit_named_cut (gint             num_drawables,
+                     const GimpItem **drawables,
+                     const gchar     *buffer_name)
 {
   GimpValueArray *args;
   GimpValueArray *return_vals;
   gchar *real_name = NULL;
 
   args = gimp_value_array_new_from_types (NULL,
-                                          GIMP_TYPE_DRAWABLE, drawable,
+                                          G_TYPE_INT, num_drawables,
+                                          GIMP_TYPE_OBJECT_ARRAY, NULL,
                                           G_TYPE_STRING, buffer_name,
                                           G_TYPE_NONE);
+  gimp_value_set_object_array (gimp_value_array_index (args, 1), GIMP_TYPE_ITEM, (GObject **) drawables, 
num_drawables);
 
   return_vals = gimp_pdb_run_procedure_array (gimp_get_pdb (),
                                               "gimp-edit-named-cut",
@@ -296,7 +304,8 @@ gimp_edit_named_cut (GimpDrawable *drawable,
 
 /**
  * gimp_edit_named_copy:
- * @drawable: The drawable to copy from.
+ * @num_drawables: The number of drawables.
+ * @drawables: (array length=num_drawables) (element-type GimpItem): The drawables to copy from.
  * @buffer_name: The name of the buffer to create.
  *
  * Copy into a named buffer.
@@ -313,17 +322,20 @@ gimp_edit_named_cut (GimpDrawable *drawable,
  * Since: 2.4
  **/
 gchar *
-gimp_edit_named_copy (GimpDrawable *drawable,
-                      const gchar  *buffer_name)
+gimp_edit_named_copy (gint             num_drawables,
+                      const GimpItem **drawables,
+                      const gchar     *buffer_name)
 {
   GimpValueArray *args;
   GimpValueArray *return_vals;
   gchar *real_name = NULL;
 
   args = gimp_value_array_new_from_types (NULL,
-                                          GIMP_TYPE_DRAWABLE, drawable,
+                                          G_TYPE_INT, num_drawables,
+                                          GIMP_TYPE_OBJECT_ARRAY, NULL,
                                           G_TYPE_STRING, buffer_name,
                                           G_TYPE_NONE);
+  gimp_value_set_object_array (gimp_value_array_index (args, 1), GIMP_TYPE_ITEM, (GObject **) drawables, 
num_drawables);
 
   return_vals = gimp_pdb_run_procedure_array (gimp_get_pdb (),
                                               "gimp-edit-named-copy",
diff --git a/libgimp/gimpedit_pdb.h b/libgimp/gimpedit_pdb.h
index b11f01e88e..013a440e74 100644
--- a/libgimp/gimpedit_pdb.h
+++ b/libgimp/gimpedit_pdb.h
@@ -32,16 +32,19 @@ G_BEGIN_DECLS
 /* For information look into the C source or the html documentation */
 
 
-gboolean   gimp_edit_cut                      (GimpDrawable    *drawable);
+gboolean   gimp_edit_cut                      (gint             num_drawables,
+                                               const GimpItem **drawables);
 gboolean   gimp_edit_copy                     (gint             num_drawables,
                                                const GimpItem **drawables);
 gboolean   gimp_edit_copy_visible             (GimpImage       *image);
 GimpLayer* gimp_edit_paste                    (GimpDrawable    *drawable,
                                                gboolean         paste_into);
 GimpImage* gimp_edit_paste_as_new_image       (void);
-gchar*     gimp_edit_named_cut                (GimpDrawable    *drawable,
+gchar*     gimp_edit_named_cut                (gint             num_drawables,
+                                               const GimpItem **drawables,
                                                const gchar     *buffer_name);
-gchar*     gimp_edit_named_copy               (GimpDrawable    *drawable,
+gchar*     gimp_edit_named_copy               (gint             num_drawables,
+                                               const GimpItem **drawables,
                                                const gchar     *buffer_name);
 gchar*     gimp_edit_named_copy_visible       (GimpImage       *image,
                                                const gchar     *buffer_name);
diff --git a/pdb/groups/edit.pdb b/pdb/groups/edit.pdb
index 37c9a1fa41..af28071cd5 100644
--- a/pdb/groups/edit.pdb
+++ b/pdb/groups/edit.pdb
@@ -17,24 +17,28 @@
 # "Perlized" from C source by Manish Singh <yosh gimp org>
 
 sub edit_cut {
-    $blurb = 'Cut from the specified drawable.';
+    $blurb = 'Cut from the specified drawables.';
 
     $help = <<'HELP';
 If there is a selection in the image, then the area specified by the
-selection is cut from the specified drawable and placed in an internal
+selection is cut from the specified drawables and placed in an internal
 GIMP edit buffer. It can subsequently be retrieved using the
-gimp_edit_paste() command. If there is no selection, then the
-specified drawable will be removed and its contents stored in the
-internal GIMP edit buffer. This procedure will fail if the selected area
-lies completely outside the bounds of the current drawable and there is
-nothing to copy from.
+gimp_edit_paste() command. If there is no selection and only one
+specified drawable, then the specified drawable will be removed and its
+contents stored in the internal GIMP edit buffer.
+This procedure will fail if the selected area lies completely outside
+the bounds of the current drawables and there is nothing to cut from.
 HELP
 
     &std_pdb_misc;
 
     @inargs = (
-       { name => 'drawable', type => 'drawable',
-         desc => 'The drawable to cut from' }
+        { name => 'drawables', type => 'itemarray',
+         desc => 'The drawables to cut from',
+          no_validate => 1,
+          array => { name => 'num_drawables',
+                     type => '1 <= int32',
+                     desc => "The number of drawables" } }
     );
 
     @outargs = (
@@ -46,14 +50,41 @@ HELP
     %invoke = (
        code => <<CODE
 {
-  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                 GIMP_PDB_ITEM_CONTENT, error) &&
-      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+  GimpImage *image         = NULL;
+  GList     *drawable_list = NULL;
+  gint       i;
+
+  for (i = 0; i < num_drawables; i++)
+    {
+      if (! gimp_pdb_item_is_attached (GIMP_ITEM (drawables[i]), NULL,
+                                       GIMP_PDB_ITEM_CONTENT, error) ||
+          gimp_pdb_item_is_group (GIMP_ITEM (drawables[i]), error))
+        {
+          success = FALSE;
+          break;
+        }
+
+      if (! image)
+        {
+          image = gimp_item_get_image (GIMP_ITEM (drawables[i]));
+        }
+      else if (image != gimp_item_get_image (GIMP_ITEM (drawables[i])))
+        {
+          success = FALSE;
+          gimp_message_literal (gimp,
+                                G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                                _("All specified drawables must belong to the same image."));
+          break;
+        }
+
+      drawable_list = g_list_prepend (drawable_list, (gpointer) drawables[i]);
+    }
+
+  if (success && image)
     {
-      GimpImage *image    = gimp_item_get_image (GIMP_ITEM (drawable));
-      GError    *my_error = NULL;
+      GError *my_error = NULL;
 
-      non_empty = gimp_edit_cut (image, drawable, context, &my_error) != NULL;
+      non_empty = gimp_edit_cut (image, drawable_list, context, &my_error) != NULL;
 
       if (! non_empty)
         {
@@ -64,7 +95,10 @@ HELP
         }
     }
   else
-    success = FALSE;
+    {
+      success = FALSE;
+    }
+  g_list_free (drawable_list);
 }
 CODE
     );
@@ -299,8 +333,12 @@ HELP
     &mitch_pdb_misc('2005', '2.4');
 
     @inargs = (
-       { name => 'drawable', type => 'drawable',
-         desc => "The drawable to cut from" },
+        { name => 'drawables', type => 'itemarray',
+         desc => 'The drawables to cut from',
+          no_validate => 1,
+          array => { name => 'num_drawables',
+                     type => '1 <= int32',
+                     desc => "The number of drawables" } },
         { name => 'buffer_name', type => 'string', non_empty => 1,
           desc => 'The name of the buffer to create' }
     );
@@ -314,15 +352,42 @@ HELP
     %invoke = (
        code => <<CODE
 {
-  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                 GIMP_PDB_ITEM_CONTENT, error) &&
-      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+  GimpImage *image         = NULL;
+  GList     *drawable_list = NULL;
+  gint       i;
+
+  for (i = 0; i < num_drawables; i++)
     {
-      GimpImage *image    = gimp_item_get_image (GIMP_ITEM (drawable));
-      GError    *my_error = NULL;
+      if (! gimp_pdb_item_is_attached (GIMP_ITEM (drawables[i]), NULL,
+                                       GIMP_PDB_ITEM_CONTENT, error) ||
+          gimp_pdb_item_is_group (GIMP_ITEM (drawables[i]), error))
+        {
+          success = FALSE;
+          break;
+        }
+
+      if (! image)
+        {
+          image = gimp_item_get_image (GIMP_ITEM (drawables[i]));
+        }
+      else if (image != gimp_item_get_image (GIMP_ITEM (drawables[i])))
+        {
+          success = FALSE;
+          gimp_message_literal (gimp,
+                                G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                                _("All specified drawables must belong to the same image."));
+          break;
+        }
+
+      drawable_list = g_list_prepend (drawable_list, (gpointer) drawables[i]);
+    }
+
+  if (success && image)
+    {
+      GError *my_error = NULL;
 
       real_name = (gchar *) gimp_edit_named_cut (image, buffer_name,
-                                                 drawable, context, &my_error);
+                                                 drawable_list, context, &my_error);
 
       if (real_name)
         {
@@ -337,7 +402,10 @@ HELP
         }
     }
   else
-    success = FALSE;
+    {
+      success = FALSE;
+    }
+  g_list_free (drawable_list);
 }
 CODE
     );
@@ -355,8 +423,12 @@ HELP
     &mitch_pdb_misc('2005', '2.4');
 
     @inargs = (
-       { name => 'drawable', type => 'drawable',
-         desc => "The drawable to copy from" },
+        { name => 'drawables', type => 'itemarray',
+         desc => 'The drawables to copy from',
+          no_validate => 1,
+          array => { name => 'num_drawables',
+                     type => '1 <= int32',
+                     desc => "The number of drawables" } },
         { name => 'buffer_name', type => 'string', non_empty => 1,
           desc => 'The name of the buffer to create' }
     );
@@ -370,13 +442,41 @@ HELP
     %invoke = (
        code => <<CODE
 {
-  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL, 0, error))
+  GimpImage *image         = NULL;
+  GList     *drawable_list = NULL;
+  gint       i;
+
+  for (i = 0; i < num_drawables; i++)
     {
-      GimpImage *image    = gimp_item_get_image (GIMP_ITEM (drawable));
-      GError    *my_error = NULL;
+      if (! gimp_pdb_item_is_attached (GIMP_ITEM (drawables[i]), NULL,
+                                       0, error))
+        {
+          success = FALSE;
+          break;
+        }
+
+      if (! image)
+        {
+          image = gimp_item_get_image (GIMP_ITEM (drawables[i]));
+        }
+      else if (image != gimp_item_get_image (GIMP_ITEM (drawables[i])))
+        {
+          success = FALSE;
+          gimp_message_literal (gimp,
+                                G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                                _("All specified drawables must belong to the same image."));
+          break;
+        }
+
+      drawable_list = g_list_prepend (drawable_list, (gpointer) drawables[i]);
+    }
+
+  if (success && image)
+    {
+      GError *my_error = NULL;
 
       real_name = (gchar *) gimp_edit_named_copy (image, buffer_name,
-                                                  drawable, context, &my_error);
+                                                  drawable_list, context, &my_error);
 
       if (real_name)
         {
@@ -391,7 +491,10 @@ HELP
         }
     }
   else
-    success = FALSE;
+    {
+      success = FALSE;
+    }
+  g_list_free (drawable_list);
 }
 CODE
     );


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]