Commit 67863dab authored by Michael Natterer's avatar Michael Natterer 😴
Browse files

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
parent f960e8e6
......@@ -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);
}
......
......@@ -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."));
}
}
......
......@@ -400,6 +400,37 @@ gimp_matting_engine_get_type (void)
return type;
}
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)
{
......
......@@ -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;
......
......@@ -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));