[gimp] Bug 637313 - Should be possible to copy/paste whole layers in a single step



commit 67863dabf92e45ac5bda18c814e893f55d62cc9a
Author: Michael Natterer <mitch gimp org>
Date:   Mon Sep 19 23:53:40 2016 +0200

    Bug 637313 - Should be possible to copy/paste whole layers in a single step
    
    Use the newly added clipboard for entire images to copy/paste layers
    (we only create single-layer clipboard images, and use only the first
    layer of any recieved image, the layers can be arbitrarily complex
    though):
    
    - change gimp_edit_copy,cut,paste() to return/take a GimpObject
      that can be a GimpImage or GimpBuffer
    - cut/copy the whole layer if there is no selection
    - always paste layers as new layers, not floating selections
    - always paste news layers on top of the active layer, where
      we would attach a floating selection
    - add enum GimpPasteType { FLOATING, FLOATING_INTO, NEW_LAYER }
    - add GimpPasteType parameter to gimp_edit_paste() and handle all
      three cases there because there is now a lot of common code
      involved
    - change all callers accordingly, use only legacy buffer pasting
      from the PDB for now

 app/actions/buffers-commands.c     |   11 +-
 app/actions/edit-commands.c        |  291 +++++++++++++++++-------------------
 app/core/core-enums.c              |   31 ++++
 app/core/core-enums.h              |   12 ++
 app/core/gimp-edit.c               |  180 ++++++++++++++++-------
 app/core/gimp-edit.h               |    8 +-
 app/display/gimpdisplayshell-dnd.c |   24 ++-
 app/pdb/edit-cmds.c                |   14 ++-
 tools/pdbgen/pdb/edit.pdb          |   14 ++-
 9 files changed, 348 insertions(+), 237 deletions(-)
---
diff --git a/app/actions/buffers-commands.c b/app/actions/buffers-commands.c
index d21c5cb..813e1c1 100644
--- a/app/actions/buffers-commands.c
+++ b/app/actions/buffers-commands.c
@@ -47,7 +47,7 @@
 /*  local function prototypes  */
 
 static void   buffers_paste (GimpBufferView *view,
-                             gboolean        paste_into);
+                             GimpPasteType   paste_type);
 
 
 /*  public functionss */
@@ -56,14 +56,14 @@ void
 buffers_paste_cmd_callback (GtkAction *action,
                             gpointer   data)
 {
-  buffers_paste (GIMP_BUFFER_VIEW (data), FALSE);
+  buffers_paste (GIMP_BUFFER_VIEW (data), GIMP_PASTE_TYPE_FLOATING);
 }
 
 void
 buffers_paste_into_cmd_callback (GtkAction *action,
                                  gpointer   data)
 {
-  buffers_paste (GIMP_BUFFER_VIEW (data), TRUE);
+  buffers_paste (GIMP_BUFFER_VIEW (data), GIMP_PASTE_TYPE_FLOATING_INTO);
 }
 
 void
@@ -108,7 +108,7 @@ buffers_delete_cmd_callback (GtkAction *action,
 
 static void
 buffers_paste (GimpBufferView *view,
-               gboolean        paste_into)
+               GimpPasteType   paste_type)
 {
   GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (view);
   GimpContainer       *container;
@@ -146,7 +146,8 @@ buffers_paste (GimpBufferView *view,
       if (image)
         {
           gimp_edit_paste (image, gimp_image_get_active_drawable (image),
-                           buffer, paste_into, x, y, width, height);
+                           GIMP_OBJECT (buffer), paste_type,
+                           x, y, width, height);
 
           gimp_image_flush (image);
         }
diff --git a/app/actions/edit-commands.c b/app/actions/edit-commands.c
index 6372022..f571775 100644
--- a/app/actions/edit-commands.c
+++ b/app/actions/edit-commands.c
@@ -36,6 +36,7 @@
 #include "core/gimplayer.h"
 #include "core/gimplayer-new.h"
 #include "core/gimpimage.h"
+#include "core/gimpimage-duplicate.h"
 #include "core/gimpimage-new.h"
 #include "core/gimpimage-undo.h"
 
@@ -65,17 +66,18 @@
 
 /*  local function prototypes  */
 
-static void   edit_paste                         (GimpDisplay *display,
-                                                  gboolean     paste_into);
-static void   cut_named_buffer_callback          (GtkWidget   *widget,
-                                                  const gchar *name,
-                                                  gpointer     data);
-static void   copy_named_buffer_callback         (GtkWidget   *widget,
-                                                  const gchar *name,
-                                                  gpointer     data);
-static void   copy_named_visible_buffer_callback (GtkWidget   *widget,
-                                                  const gchar *name,
-                                                  gpointer     data);
+static void   edit_paste                         (GimpDisplay   *display,
+                                                  GimpPasteType  paste_into,
+                                                  gboolean       try_svg);
+static void   cut_named_buffer_callback          (GtkWidget     *widget,
+                                                  const gchar   *name,
+                                                  gpointer       data);
+static void   copy_named_buffer_callback         (GtkWidget     *widget,
+                                                  const gchar   *name,
+                                                  gpointer       data);
+static void   copy_named_visible_buffer_callback (GtkWidget     *widget,
+                                                  const gchar   *name,
+                                                  gpointer       data);
 
 
 /*  public functions  */
@@ -229,17 +231,23 @@ edit_cut_cmd_callback (GtkAction *action,
 {
   GimpImage    *image;
   GimpDrawable *drawable;
+  GimpObject   *cut;
   GError       *error = NULL;
   return_if_no_drawable (image, drawable, data);
 
-  if (gimp_edit_cut (image, drawable, action_data_get_context (data), &error))
+  cut = gimp_edit_cut (image, drawable, action_data_get_context (data),
+                       &error);
+
+  if (cut)
     {
       GimpDisplay *display = action_data_get_display (data);
 
       if (display)
         gimp_message_literal (image->gimp,
                               G_OBJECT (display), GIMP_MESSAGE_INFO,
-                              _("Cut pixels to the clipboard"));
+                              GIMP_IS_IMAGE (cut) ?
+                              _("Cut layer to the clipboard.") :
+                              _("Cut pixels to the clipboard."));
 
       gimp_image_flush (image);
     }
@@ -259,17 +267,23 @@ edit_copy_cmd_callback (GtkAction *action,
 {
   GimpImage    *image;
   GimpDrawable *drawable;
+  GimpObject   *copy;
   GError       *error = NULL;
   return_if_no_drawable (image, drawable, data);
 
-  if (gimp_edit_copy (image, drawable, action_data_get_context (data), &error))
+  copy = gimp_edit_copy (image, drawable, action_data_get_context (data),
+                         &error);
+
+  if (copy)
     {
       GimpDisplay *display = action_data_get_display (data);
 
       if (display)
         gimp_message_literal (image->gimp,
                               G_OBJECT (display), GIMP_MESSAGE_INFO,
-                              _("Copied pixels to the clipboard"));
+                              GIMP_IS_IMAGE (copy) ?
+                              _("Copied layer to the clipboard.") :
+                              _("Copied pixels to the clipboard."));
 
       gimp_image_flush (image);
     }
@@ -298,7 +312,7 @@ edit_copy_visible_cmd_callback (GtkAction *action,
       if (display)
         gimp_message_literal (image->gimp,
                               G_OBJECT (display), GIMP_MESSAGE_INFO,
-                              _("Copied pixels to the clipboard"));
+                              _("Copied pixels to the clipboard."));
 
       gimp_image_flush (image);
     }
@@ -320,31 +334,7 @@ edit_paste_cmd_callback (GtkAction *action,
 
   if (display && gimp_display_get_image (display))
     {
-      GimpImage    *image    = gimp_display_get_image (display);
-      GimpDrawable *drawable = gimp_image_get_active_drawable (image);;
-
-      if (drawable && gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
-        {
-          gimp_message_literal (display->gimp, G_OBJECT (display),
-                                GIMP_MESSAGE_INFO,
-                                _("Pasting as new layer because the "
-                                  "target is a group layer."));
-
-          edit_paste_as_new_layer_cmd_callback (action, data);
-        }
-      else if (drawable && gimp_item_is_content_locked (GIMP_ITEM (drawable)))
-        {
-          gimp_message_literal (display->gimp, G_OBJECT (display),
-                                GIMP_MESSAGE_INFO,
-                                _("Pasting as new layer because the "
-                                  "target's pixels are locked."));
-
-          edit_paste_as_new_layer_cmd_callback (action, data);
-        }
-      else
-        {
-          edit_paste (display, FALSE);
-        }
+      edit_paste (display, GIMP_PASTE_TYPE_FLOATING, TRUE);
     }
   else
     {
@@ -356,36 +346,10 @@ void
 edit_paste_into_cmd_callback (GtkAction *action,
                               gpointer   data)
 {
-  GimpImage    *image;
-  GimpDisplay  *display;
-  GimpDrawable *drawable;
-  return_if_no_image (image, data);
+  GimpDisplay *display;
   return_if_no_display (display, data);
 
-  drawable = gimp_image_get_active_drawable (image);;
-
-  if (drawable && gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
-    {
-      gimp_message_literal (display->gimp, G_OBJECT (display),
-                            GIMP_MESSAGE_INFO,
-                            _("Pasting as new layer because the "
-                              "target is a group layer."));
-
-      edit_paste_as_new_layer_cmd_callback (action, data);
-    }
-  else if (drawable && gimp_item_is_content_locked (GIMP_ITEM (drawable)))
-    {
-      gimp_message_literal (display->gimp, G_OBJECT (display),
-                            GIMP_MESSAGE_INFO,
-                            _("Pasting as new layer because the "
-                              "target's pixels are locked."));
-
-      edit_paste_as_new_layer_cmd_callback (action, data);
-    }
-  else
-    {
-      edit_paste (display, TRUE);
-    }
+  edit_paste (display, GIMP_PASTE_TYPE_FLOATING_INTO, TRUE);
 }
 
 void
@@ -393,30 +357,44 @@ edit_paste_as_new_image_cmd_callback (GtkAction *action,
                                       gpointer   data)
 {
   Gimp       *gimp;
-  GimpBuffer *buffer;
+  GimpImage  *image;
+  GimpImage  *new_image = NULL;
   GtkWidget  *widget;
   return_if_no_gimp (gimp, data);
   return_if_no_widget (widget, data);
 
-  buffer = gimp_clipboard_get_buffer (gimp);
+  image = gimp_clipboard_get_image (gimp);
 
-  if (buffer)
+  if (image)
+    {
+      new_image = gimp_image_duplicate (image);
+      g_object_unref (image);
+    }
+  else
     {
-      GimpImage *image;
+      GimpBuffer *buffer = gimp_clipboard_get_buffer (gimp);
 
-      image = gimp_image_new_from_buffer (gimp, action_data_get_image (data),
-                                          buffer);
-      g_object_unref (buffer);
+      if (buffer)
+        {
+          new_image = gimp_image_new_from_buffer (gimp,
+                                                  action_data_get_image (data),
+                                                  buffer);
+          g_object_unref (buffer);
+        }
+    }
 
-      gimp_create_display (image->gimp, image, GIMP_UNIT_PIXEL, 1.0,
+  if (new_image)
+    {
+      gimp_create_display (gimp, new_image, GIMP_UNIT_PIXEL, 1.0,
                            G_OBJECT (gtk_widget_get_screen (widget)),
                            gimp_widget_get_monitor (widget));
-      g_object_unref (image);
+      g_object_unref (new_image);
     }
   else
     {
       gimp_message_literal (gimp, NULL, GIMP_MESSAGE_WARNING,
-                            _("There is no image data in the clipboard to paste."));
+                            _("There is no image data in the clipboard "
+                              "to paste."));
     }
 }
 
@@ -424,54 +402,10 @@ void
 edit_paste_as_new_layer_cmd_callback (GtkAction *action,
                                       gpointer   data)
 {
-  Gimp        *gimp;
-  GimpImage   *image;
   GimpDisplay *display;
-  GimpBuffer  *buffer;
-  return_if_no_gimp (gimp, data);
-  return_if_no_image (image, data);
   return_if_no_display (display, data);
 
-  buffer = gimp_clipboard_get_buffer (gimp);
-
-  if (buffer)
-    {
-      GimpLayer *layer;
-      gint       x, y;
-      gint       width, height;
-      gint       offset_x;
-      gint       offset_y;
-
-      layer = gimp_layer_new_from_buffer (buffer, image,
-                                          gimp_image_get_layer_format (image,
-                                                                       TRUE),
-                                          _("Clipboard"),
-                                          GIMP_OPACITY_OPAQUE,
-                                          GIMP_NORMAL_MODE);
-      g_object_unref (buffer);
-
-      gimp_display_shell_untransform_viewport (gimp_display_get_shell (display),
-                                               &x, &y, &width, &height);
-
-      gimp_edit_get_paste_offset (image,
-                                  gimp_image_get_active_drawable (image),
-                                  GIMP_OBJECT (buffer),
-                                  x, y, width, height,
-                                  &offset_x,
-                                  &offset_y);
-
-      gimp_item_set_offset (GIMP_ITEM (layer), offset_x, offset_y);
-
-      gimp_image_add_layer (image, layer,
-                            GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
-
-      gimp_image_flush (image);
-    }
-  else
-    {
-      gimp_message_literal (gimp, NULL, GIMP_MESSAGE_WARNING,
-                            _("There is no image data in the clipboard to paste."));
-    }
+  edit_paste (display, GIMP_PASTE_TYPE_NEW_LAYER, FALSE);
 }
 
 void
@@ -600,57 +534,100 @@ edit_fill_cmd_callback (GtkAction *action,
 /*  private functions  */
 
 static void
-edit_paste (GimpDisplay *display,
-            gboolean     paste_into)
+edit_paste (GimpDisplay   *display,
+            GimpPasteType  paste_type,
+            gboolean       try_svg)
 {
-  GimpImage *image = gimp_display_get_image (display);
-  gchar     *svg;
-  gsize      svg_size;
-
-  svg = gimp_clipboard_get_svg (display->gimp, &svg_size);
+  GimpImage  *image = gimp_display_get_image (display);
+  GimpObject *paste;
 
-  if (svg)
+  if (try_svg)
     {
-      if (gimp_vectors_import_buffer (image, svg, svg_size,
-                                      TRUE, FALSE,
-                                      GIMP_IMAGE_ACTIVE_PARENT, -1,
-                                      NULL, NULL))
+      gchar *svg;
+      gsize  svg_size;
+
+      svg = gimp_clipboard_get_svg (display->gimp, &svg_size);
+
+      if (svg)
         {
-          gimp_image_flush (image);
+          if (gimp_vectors_import_buffer (image, svg, svg_size,
+                                          TRUE, FALSE,
+                                          GIMP_IMAGE_ACTIVE_PARENT, -1,
+                                          NULL, NULL))
+            {
+              gimp_image_flush (image);
+            }
+
+          g_free (svg);
+
+          return;
         }
+    }
 
-      g_free (svg);
+  paste = GIMP_OBJECT (gimp_clipboard_get_image (display->gimp));
+
+  if (paste)
+    {
+      if (paste_type != GIMP_PASTE_TYPE_NEW_LAYER)
+        {
+          gimp_message_literal (display->gimp, G_OBJECT (display),
+                                GIMP_MESSAGE_INFO,
+                                _("Pasted as new layer because "
+                                  "there was a layer the clipboard."));
+
+          paste_type = GIMP_PASTE_TYPE_NEW_LAYER;
+        }
     }
   else
     {
-      GimpBuffer *buffer;
+      paste = GIMP_OBJECT (gimp_clipboard_get_buffer (display->gimp));
+    }
 
-      buffer = gimp_clipboard_get_buffer (display->gimp);
+  if (paste)
+    {
+      GimpDisplayShell *shell    = gimp_display_get_shell (display);
+      GimpDrawable     *drawable = gimp_image_get_active_drawable (image);
+      gint              x, y;
+      gint              width, height;
 
-      if (buffer)
+      if (drawable)
         {
-          GimpDisplayShell *shell = gimp_display_get_shell (display);
-          gint              x, y;
-          gint              width, height;
-
-          gimp_display_shell_untransform_viewport (shell,
-                                                   &x, &y, &width, &height);
-
-          if (gimp_edit_paste (image,
-                               gimp_image_get_active_drawable (image),
-                               buffer, paste_into, x, y, width, height))
+          if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
             {
-              gimp_image_flush (image);
+              gimp_message_literal (display->gimp, G_OBJECT (display),
+                                    GIMP_MESSAGE_INFO,
+                                    _("Pasted as new layer because the "
+                                      "target is a layer group."));
+
+              paste_type = GIMP_PASTE_TYPE_NEW_LAYER;
             }
+          else if (gimp_item_is_content_locked (GIMP_ITEM (drawable)))
+            {
+              gimp_message_literal (display->gimp, G_OBJECT (display),
+                                    GIMP_MESSAGE_INFO,
+                                    _("Pasted as new layer because the "
+                                      "target's pixels are locked."));
 
-          g_object_unref (buffer);
+              paste_type = GIMP_PASTE_TYPE_NEW_LAYER;
+            }
         }
-      else
+
+      gimp_display_shell_untransform_viewport (shell, &x, &y, &width, &height);
+
+      if (gimp_edit_paste (image, drawable, paste,
+                           paste_type, x, y, width, height))
         {
-          gimp_message_literal (display->gimp, G_OBJECT (display),
-                                GIMP_MESSAGE_WARNING,
-                                _("There is no image data in the clipboard to paste."));
+          gimp_image_flush (image);
         }
+
+      g_object_unref (paste);
+    }
+  else
+    {
+      gimp_message_literal (display->gimp, G_OBJECT (display),
+                            GIMP_MESSAGE_WARNING,
+                            _("There is no image data in the clipboard "
+                              "to paste."));
     }
 }
 
diff --git a/app/core/core-enums.c b/app/core/core-enums.c
index 157a93b..4725723 100644
--- a/app/core/core-enums.c
+++ b/app/core/core-enums.c
@@ -401,6 +401,37 @@ gimp_matting_engine_get_type (void)
 }
 
 GType
+gimp_paste_type_get_type (void)
+{
+  static const GEnumValue values[] =
+  {
+    { GIMP_PASTE_TYPE_FLOATING, "GIMP_PASTE_TYPE_FLOATING", "floating" },
+    { GIMP_PASTE_TYPE_FLOATING_INTO, "GIMP_PASTE_TYPE_FLOATING_INTO", "floating-into" },
+    { GIMP_PASTE_TYPE_NEW_LAYER, "GIMP_PASTE_TYPE_NEW_LAYER", "new-layer" },
+    { 0, NULL, NULL }
+  };
+
+  static const GimpEnumDesc descs[] =
+  {
+    { GIMP_PASTE_TYPE_FLOATING, "GIMP_PASTE_TYPE_FLOATING", NULL },
+    { GIMP_PASTE_TYPE_FLOATING_INTO, "GIMP_PASTE_TYPE_FLOATING_INTO", NULL },
+    { GIMP_PASTE_TYPE_NEW_LAYER, "GIMP_PASTE_TYPE_NEW_LAYER", NULL },
+    { 0, NULL, NULL }
+  };
+
+  static GType type = 0;
+
+  if (G_UNLIKELY (! type))
+    {
+      type = g_enum_register_static ("GimpPasteType", values);
+      gimp_type_set_translation_context (type, "paste-type");
+      gimp_enum_set_value_descriptions (type, descs);
+    }
+
+  return type;
+}
+
+GType
 gimp_alignment_type_get_type (void)
 {
   static const GEnumValue values[] =
diff --git a/app/core/core-enums.h b/app/core/core-enums.h
index 49ce8d1..96c837f 100644
--- a/app/core/core-enums.h
+++ b/app/core/core-enums.h
@@ -201,6 +201,18 @@ typedef enum  /*< pdb-skip >*/
 } GimpMattingEngine;
 
 
+#define GIMP_TYPE_PASTE_TYPE (gimp_paste_type_get_type ())
+
+GType gimp_paste_type_get_type (void) G_GNUC_CONST;
+
+typedef enum  /*< pdb-skip >*/
+{
+  GIMP_PASTE_TYPE_FLOATING,
+  GIMP_PASTE_TYPE_FLOATING_INTO,
+  GIMP_PASTE_TYPE_NEW_LAYER
+} GimpPasteType;
+
+
 #define GIMP_TYPE_ALIGNMENT_TYPE (gimp_alignment_type_get_type ())
 
 GType gimp_alignment_type_get_type (void) G_GNUC_CONST;
diff --git a/app/core/gimp-edit.c b/app/core/gimp-edit.c
index 034b291..4430772 100644
--- a/app/core/gimp-edit.c
+++ b/app/core/gimp-edit.c
@@ -39,6 +39,7 @@
 #include "gimpfilloptions.h"
 #include "gimpdrawableundo.h"
 #include "gimpimage.h"
+#include "gimpimage-new.h"
 #include "gimpimage-undo.h"
 #include "gimplayer.h"
 #include "gimplayer-floating-selection.h"
@@ -62,57 +63,95 @@ static GimpBuffer * gimp_edit_extract (GimpImage     *image,
 
 /*  public functions  */
 
-GimpBuffer *
+GimpObject *
 gimp_edit_cut (GimpImage     *image,
                GimpDrawable  *drawable,
                GimpContext   *context,
                GError       **error)
 {
-  GimpBuffer *buffer;
-
   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);
 
-  buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable),
-                              context, TRUE, error);
+  if (GIMP_IS_LAYER (drawable) &&
+      gimp_channel_is_empty (gimp_image_get_mask (image)))
+    {
+      GimpImage *clip_image;
 
-  if (buffer)
+      clip_image = gimp_image_new_from_drawable (image->gimp, drawable);
+      gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image));
+      gimp_set_clipboard_image (image->gimp, clip_image);
+      g_object_unref (clip_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),
+                               TRUE, NULL);
+
+      gimp_image_undo_group_end (image);
+
+      return GIMP_OBJECT (gimp_get_clipboard_image (image->gimp));
+    }
+  else
     {
-      gimp_set_clipboard_buffer (image->gimp, buffer);
-      g_object_unref (buffer);
+      GimpBuffer *buffer;
 
-      return gimp_get_clipboard_buffer (image->gimp);
+      buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable),
+                                  context, TRUE, error);
+
+      if (buffer)
+        {
+          gimp_set_clipboard_buffer (image->gimp, buffer);
+          g_object_unref (buffer);
+
+          return GIMP_OBJECT (gimp_get_clipboard_buffer (image->gimp));
+        }
     }
 
   return NULL;
 }
 
-GimpBuffer *
+GimpObject *
 gimp_edit_copy (GimpImage     *image,
                 GimpDrawable  *drawable,
                 GimpContext   *context,
                 GError       **error)
 {
-  GimpBuffer *buffer;
-
   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);
 
-  buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable),
-                              context, FALSE, error);
+  if (GIMP_IS_LAYER (drawable) &&
+      gimp_channel_is_empty (gimp_image_get_mask (image)))
+    {
+      GimpImage *clip_image;
 
-  if (buffer)
+      clip_image = gimp_image_new_from_drawable (image->gimp, drawable);
+      gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image));
+      gimp_set_clipboard_image (image->gimp, clip_image);
+      g_object_unref (clip_image);
+
+      return GIMP_OBJECT (gimp_get_clipboard_image (image->gimp));
+    }
+  else
     {
-      gimp_set_clipboard_buffer (image->gimp, buffer);
-      g_object_unref (buffer);
+      GimpBuffer *buffer;
 
-      return gimp_get_clipboard_buffer (image->gimp);
+      buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable),
+                                  context, FALSE, error);
+
+      if (buffer)
+        {
+          gimp_set_clipboard_buffer (image->gimp, buffer);
+          g_object_unref (buffer);
+
+          return GIMP_OBJECT (gimp_get_clipboard_buffer (image->gimp));
+        }
     }
 
   return NULL;
@@ -262,39 +301,48 @@ gimp_edit_get_paste_offset (GimpImage    *image,
 }
 
 GimpLayer *
-gimp_edit_paste (GimpImage    *image,
-                 GimpDrawable *drawable,
-                 GimpBuffer   *paste,
-                 gboolean      paste_into,
-                 gint          viewport_x,
-                 gint          viewport_y,
-                 gint          viewport_width,
-                 gint          viewport_height)
+gimp_edit_paste (GimpImage     *image,
+                 GimpDrawable  *drawable,
+                 GimpObject    *paste,
+                 GimpPasteType  paste_type,
+                 gint           viewport_x,
+                 gint           viewport_y,
+                 gint           viewport_width,
+                 gint           viewport_height)
 {
-  GimpLayer  *layer;
-  const Babl *format;
-  gint        offset_x;
-  gint        offset_y;
+  GimpLayer *layer;
+  gint       offset_x;
+  gint       offset_y;
 
   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
   g_return_val_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable), NULL);
   g_return_val_if_fail (drawable == NULL ||
                         gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
-  g_return_val_if_fail (GIMP_IS_BUFFER (paste), NULL);
+  g_return_val_if_fail (GIMP_IS_IMAGE (paste) || GIMP_IS_BUFFER (paste), NULL);
 
-  /*  Make a new layer: if drawable == NULL,
-   *  user is pasting into an empty image.
-   */
+  if (GIMP_IS_IMAGE (paste))
+    {
+      layer = gimp_image_get_layer_iter (GIMP_IMAGE (paste))->data;
 
-  if (drawable)
-    format = gimp_drawable_get_format_with_alpha (drawable);
+      layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (layer),
+                                             image,
+                                             G_TYPE_FROM_INSTANCE (layer)));
+    }
   else
-    format = gimp_image_get_layer_format (image, TRUE);
+    {
+      const Babl *format;
+
+      /*  If drawable == NULL, user is pasting into an empty image  */
+      if (drawable)
+        format = gimp_drawable_get_format_with_alpha (drawable);
+      else
+        format = gimp_image_get_layer_format (image, TRUE);
 
-  layer = gimp_layer_new_from_buffer (paste, image,
-                                      format,
-                                      _("Pasted Layer"),
-                                      GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE);
+      layer = gimp_layer_new_from_buffer (GIMP_BUFFER (paste), image,
+                                          format,
+                                          _("Pasted Layer"),
+                                          GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE);
+    }
 
   if (! layer)
     return NULL;
@@ -309,24 +357,48 @@ gimp_edit_paste (GimpImage    *image,
 
   gimp_item_set_offset (GIMP_ITEM (layer), offset_x, offset_y);
 
-  /*  Start a group undo  */
+  /* change paste type to NEW_LAYER for cases where we can't attach
+   * a floating selection
+   */
+  if (GIMP_IS_IMAGE (paste)                                 ||
+      ! drawable                                            ||
+      gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) ||
+      gimp_item_is_content_locked (GIMP_ITEM (drawable)))
+    {
+      paste_type = GIMP_PASTE_TYPE_NEW_LAYER;
+    }
+
   gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE,
                                C_("undo-type", "Paste"));
 
-  /*  If there is a selection mask clear it--
-   *  this might not always be desired, but in general,
-   *  it seems like the correct behavior.
-   */
-  if (! gimp_channel_is_empty (gimp_image_get_mask (image)) && ! paste_into)
-    gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE);
+  switch (paste_type)
+    {
+    case GIMP_PASTE_TYPE_FLOATING:
+      /*  If there is a selection mask clear it--
+       *  this might not always be desired, but in general,
+       *  it seems like the correct behavior.
+       */
+      if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
+        gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE);
 
-  /*  if there's a drawable, add a new floating selection  */
-  if (drawable)
-    floating_sel_attach (layer, drawable);
-  else
-    gimp_image_add_layer (image, layer, NULL, 0, TRUE);
+      /* fall thru */
+
+    case GIMP_PASTE_TYPE_FLOATING_INTO:
+      floating_sel_attach (layer, drawable);
+      break;
+
+    case GIMP_PASTE_TYPE_NEW_LAYER:
+      /* always add on top of the drawable or the active layer, where
+       * we would attach a floating selection
+       */
+      gimp_image_add_layer (image, layer,
+                            GIMP_IS_LAYER (drawable) ?
+                            gimp_item_get_parent (GIMP_ITEM (drawable)) :
+                            NULL,
+                            -1, TRUE);
+      break;
+    }
 
-  /*  end the group undo  */
   gimp_image_undo_group_end (image);
 
   return layer;
diff --git a/app/core/gimp-edit.h b/app/core/gimp-edit.h
index 6829d52..8d38f78 100644
--- a/app/core/gimp-edit.h
+++ b/app/core/gimp-edit.h
@@ -19,11 +19,11 @@
 #define __GIMP_EDIT_H__
 
 
-GimpBuffer  * gimp_edit_cut                (GimpImage       *image,
+GimpObject  * gimp_edit_cut                (GimpImage       *image,
                                             GimpDrawable    *drawable,
                                             GimpContext     *context,
                                             GError         **error);
-GimpBuffer  * gimp_edit_copy               (GimpImage       *image,
+GimpObject  * gimp_edit_copy               (GimpImage       *image,
                                             GimpDrawable    *drawable,
                                             GimpContext     *context,
                                             GError         **error);
@@ -43,8 +43,8 @@ void          gimp_edit_get_paste_offset   (GimpImage       *image,
 
 GimpLayer   * gimp_edit_paste              (GimpImage       *image,
                                             GimpDrawable    *drawable,
-                                            GimpBuffer      *paste,
-                                            gboolean         paste_into,
+                                            GimpObject      *paste,
+                                            GimpPasteType    paste_type,
                                             gint             viewport_x,
                                             gint             viewport_y,
                                             gint             viewport_width,
diff --git a/app/display/gimpdisplayshell-dnd.c b/app/display/gimpdisplayshell-dnd.c
index 6a368f0..68f4511 100644
--- a/app/display/gimpdisplayshell-dnd.c
+++ b/app/display/gimpdisplayshell-dnd.c
@@ -448,6 +448,7 @@ gimp_display_shell_drop_buffer (GtkWidget    *widget,
   GimpImage        *image = gimp_display_get_image (shell->display);
   GimpDrawable     *drawable;
   GimpBuffer       *buffer;
+  GimpPasteType     paste_type;
   gint              x, y, width, height;
 
   GIMP_LOG (DND, NULL);
@@ -467,6 +468,8 @@ gimp_display_shell_drop_buffer (GtkWidget    *widget,
       return;
     }
 
+  paste_type = GIMP_PASTE_TYPE_FLOATING;
+
   drawable = gimp_image_get_active_drawable (image);
 
   if (drawable)
@@ -474,17 +477,20 @@ gimp_display_shell_drop_buffer (GtkWidget    *widget,
       if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
         {
           gimp_message_literal (shell->display->gimp, G_OBJECT (shell->display),
-                                GIMP_MESSAGE_ERROR,
-                                _("Cannot modify the pixels of layer groups."));
-          return;
-        }
+                                GIMP_MESSAGE_INFO,
+                                _("Pasted as new layer because the "
+                                  "target is a layer group."));
 
-      if (gimp_item_is_content_locked (GIMP_ITEM (drawable)))
+          paste_type = GIMP_PASTE_TYPE_NEW_LAYER;
+        }
+      else if (gimp_item_is_content_locked (GIMP_ITEM (drawable)))
         {
           gimp_message_literal (shell->display->gimp, G_OBJECT (shell->display),
                                 GIMP_MESSAGE_ERROR,
-                                _("The active layer's pixels are locked."));
-          return;
+                                _("Pasted as new layer because the "
+                                  "target's pixels are locked."));
+
+          paste_type = GIMP_PASTE_TYPE_NEW_LAYER;
         }
     }
 
@@ -494,8 +500,8 @@ gimp_display_shell_drop_buffer (GtkWidget    *widget,
 
   /* FIXME: popup a menu for selecting "Paste Into" */
 
-  gimp_edit_paste (image, drawable, buffer, FALSE,
-                   x, y, width, height);
+  gimp_edit_paste (image, drawable, GIMP_OBJECT (buffer),
+                   paste_type, x, y, width, height);
 
   gimp_display_shell_dnd_flush (shell, image);
 }
diff --git a/app/pdb/edit-cmds.c b/app/pdb/edit-cmds.c
index fde1fe4..982add6 100644
--- a/app/pdb/edit-cmds.c
+++ b/app/pdb/edit-cmds.c
@@ -211,8 +211,11 @@ edit_paste_invoker (GimpProcedure         *procedure,
           gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
         {
           floating_sel = gimp_edit_paste (gimp_item_get_image (GIMP_ITEM (drawable)),
-                                          drawable, buffer,
-                                          paste_into, -1, -1, -1, -1);
+                                          drawable, GIMP_OBJECT (buffer),
+                                          paste_into ?
+                                          GIMP_PASTE_TYPE_FLOATING_INTO :
+                                          GIMP_PASTE_TYPE_FLOATING,
+                                          -1, -1, -1, -1);
 
           if (! floating_sel)
             success = FALSE;
@@ -446,8 +449,11 @@ edit_named_paste_invoker (GimpProcedure         *procedure,
           gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
         {
           floating_sel = gimp_edit_paste (gimp_item_get_image (GIMP_ITEM (drawable)),
-                                          drawable, buffer,
-                                          paste_into, -1, -1, -1, -1);
+                                          drawable, GIMP_OBJECT (buffer),
+                                          paste_into ?
+                                          GIMP_PASTE_TYPE_FLOATING_INTO :
+                                          GIMP_PASTE_TYPE_FLOATING,
+                                          -1, -1, -1, -1);
           if (! floating_sel)
             success = FALSE;
         }
diff --git a/tools/pdbgen/pdb/edit.pdb b/tools/pdbgen/pdb/edit.pdb
index 04af99a..605d0b3 100644
--- a/tools/pdbgen/pdb/edit.pdb
+++ b/tools/pdbgen/pdb/edit.pdb
@@ -209,8 +209,11 @@ HELP
       gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
     {
       floating_sel = gimp_edit_paste (gimp_item_get_image (GIMP_ITEM (drawable)),
-                                      drawable, buffer,
-                                      paste_into, -1, -1, -1, -1);
+                                      drawable, GIMP_OBJECT (buffer),
+                                      paste_into ?
+                                      GIMP_PASTE_TYPE_FLOATING_INTO :
+                                      GIMP_PASTE_TYPE_FLOATING,
+                                      -1, -1, -1, -1);
 
       if (! floating_sel)
         success = FALSE;
@@ -451,8 +454,11 @@ HELP
       gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
     {
       floating_sel = gimp_edit_paste (gimp_item_get_image (GIMP_ITEM (drawable)),
-                                      drawable, buffer,
-                                      paste_into, -1, -1, -1, -1);
+                                      drawable, GIMP_OBJECT (buffer),
+                                      paste_into ?
+                                      GIMP_PASTE_TYPE_FLOATING_INTO :
+                                      GIMP_PASTE_TYPE_FLOATING,
+                                      -1, -1, -1, -1);
       if (! floating_sel)
         success = FALSE;
     }


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