2015-02-12 23:16:25 +08:00
|
|
|
// Aseprite
|
2023-01-05 23:30:24 +08:00
|
|
|
// Copyright (C) 2019-2023 Igara Studio S.A.
|
2018-03-30 03:45:57 +08:00
|
|
|
// Copyright (C) 2001-2018 David Capello
|
2015-02-12 23:16:25 +08:00
|
|
|
//
|
2016-08-27 04:02:58 +08:00
|
|
|
// This program is distributed under the terms of
|
|
|
|
// the End-User License Agreement for Aseprite.
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2007-09-19 07:57:02 +08:00
|
|
|
#include "config.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#endif
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/app.h"
|
2015-01-19 09:05:33 +08:00
|
|
|
#include "app/cmd/clear_mask.h"
|
2015-05-11 08:36:46 +08:00
|
|
|
#include "app/cmd/deselect_mask.h"
|
2019-06-28 02:34:56 +08:00
|
|
|
#include "app/cmd/set_mask.h"
|
2016-05-04 23:32:39 +08:00
|
|
|
#include "app/cmd/trim_cel.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/console.h"
|
|
|
|
#include "app/context_access.h"
|
2018-07-07 22:54:44 +08:00
|
|
|
#include "app/doc.h"
|
2018-07-07 14:07:16 +08:00
|
|
|
#include "app/doc_api.h"
|
2018-07-07 21:07:21 +08:00
|
|
|
#include "app/doc_range.h"
|
|
|
|
#include "app/doc_range_ops.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/modules/gfx.h"
|
|
|
|
#include "app/modules/gui.h"
|
2018-05-24 23:56:07 +08:00
|
|
|
#include "app/pref/preferences.h"
|
2018-08-21 03:00:59 +08:00
|
|
|
#include "app/tx.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/ui/color_bar.h"
|
|
|
|
#include "app/ui/editor/editor.h"
|
|
|
|
#include "app/ui/skin/skin_theme.h"
|
2017-03-27 00:33:12 +08:00
|
|
|
#include "app/ui/timeline/timeline.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/ui_context.h"
|
2019-03-30 02:57:10 +08:00
|
|
|
#include "app/util/cel_ops.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/util/clipboard.h"
|
2015-04-02 20:55:18 +08:00
|
|
|
#include "app/util/new_image_from_mask.h"
|
2019-09-06 02:03:13 +08:00
|
|
|
#include "app/util/range_utils.h"
|
2018-05-24 23:56:07 +08:00
|
|
|
#include "clip/clip.h"
|
2014-10-21 09:21:31 +08:00
|
|
|
#include "doc/doc.h"
|
2019-04-04 06:32:24 +08:00
|
|
|
#include "render/dithering.h"
|
2017-05-20 02:49:31 +08:00
|
|
|
#include "render/ordered_dither.h"
|
2014-12-28 22:06:11 +08:00
|
|
|
#include "render/quantization.h"
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2019-08-02 06:14:46 +08:00
|
|
|
#include <memory>
|
2014-08-08 21:52:21 +08:00
|
|
|
#include <stdexcept>
|
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
namespace app {
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
using namespace doc;
|
|
|
|
|
2015-02-12 23:16:25 +08:00
|
|
|
namespace {
|
2014-08-08 21:33:45 +08:00
|
|
|
|
2018-07-07 19:38:04 +08:00
|
|
|
class ClipboardRange : public DocsObserver {
|
2014-08-08 21:33:45 +08:00
|
|
|
public:
|
2016-01-30 20:13:46 +08:00
|
|
|
ClipboardRange() : m_doc(nullptr) {
|
|
|
|
}
|
|
|
|
|
|
|
|
~ClipboardRange() {
|
|
|
|
ASSERT(!m_doc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void observeUIContext() {
|
2023-01-05 23:30:24 +08:00
|
|
|
#ifdef ENABLE_UI
|
2016-09-14 02:02:00 +08:00
|
|
|
UIContext::instance()->documents().add_observer(this);
|
2023-01-05 23:30:24 +08:00
|
|
|
#endif
|
2016-01-30 20:13:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void unobserveUIContext() {
|
2023-01-05 23:30:24 +08:00
|
|
|
#ifdef ENABLE_UI
|
2016-09-14 02:02:00 +08:00
|
|
|
UIContext::instance()->documents().remove_observer(this);
|
2023-01-05 23:30:24 +08:00
|
|
|
#endif
|
2014-08-08 21:33:45 +08:00
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
bool valid() const {
|
2016-01-30 20:13:46 +08:00
|
|
|
return (m_doc != nullptr);
|
2014-08-08 21:33:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void invalidate() {
|
2016-01-30 20:13:46 +08:00
|
|
|
m_doc = nullptr;
|
2014-08-08 21:33:45 +08:00
|
|
|
}
|
|
|
|
|
2018-07-07 22:54:44 +08:00
|
|
|
void setRange(Doc* doc, const DocRange& range) {
|
2014-08-08 21:33:45 +08:00
|
|
|
m_doc = doc;
|
|
|
|
m_range = range;
|
|
|
|
}
|
|
|
|
|
2018-07-07 22:54:44 +08:00
|
|
|
Doc* document() const { return m_doc; }
|
2018-07-07 21:07:21 +08:00
|
|
|
DocRange range() const { return m_range; }
|
2014-08-08 21:33:45 +08:00
|
|
|
|
2018-07-07 19:38:04 +08:00
|
|
|
// DocsObserver impl
|
2018-07-07 22:54:44 +08:00
|
|
|
void onRemoveDocument(Doc* doc) override {
|
2016-01-30 20:13:46 +08:00
|
|
|
if (doc == m_doc)
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
2014-08-08 21:33:45 +08:00
|
|
|
private:
|
2018-07-07 22:54:44 +08:00
|
|
|
Doc* m_doc;
|
2018-07-07 21:07:21 +08:00
|
|
|
DocRange m_range;
|
2014-08-08 21:33:45 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
// Data in the clipboard
|
|
|
|
struct Clipboard::Data {
|
|
|
|
// Text used when the native clipboard is disabled
|
|
|
|
std::string text;
|
2016-01-30 20:13:46 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
// RGB/Grayscale/Indexed image
|
|
|
|
ImageRef image;
|
|
|
|
|
|
|
|
// The palette of the image (or tileset) if it's indexed
|
|
|
|
std::shared_ptr<Palette> palette;
|
|
|
|
|
|
|
|
// In case we copy a tilemap information
|
|
|
|
ImageRef tilemap;
|
|
|
|
|
|
|
|
// Tileset for the tilemap or a set of tiles if we are copying tiles
|
|
|
|
// in the color bar
|
|
|
|
std::shared_ptr<Tileset> tileset;
|
2013-08-06 08:20:19 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
// Selected entries copied from the palette or the tileset
|
|
|
|
PalettePicks picks;
|
2009-03-08 03:14:40 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
// Original selection used to copy the image
|
|
|
|
std::shared_ptr<Mask> mask;
|
|
|
|
|
|
|
|
// Selected set of layers/layers/cels
|
|
|
|
ClipboardRange range;
|
|
|
|
|
|
|
|
Data() {
|
|
|
|
range.observeUIContext();
|
|
|
|
}
|
|
|
|
|
|
|
|
~Data() {
|
|
|
|
clear();
|
|
|
|
range.unobserveUIContext();
|
|
|
|
}
|
|
|
|
|
|
|
|
void clear() {
|
|
|
|
text.clear();
|
|
|
|
image.reset();
|
|
|
|
palette.reset();
|
|
|
|
tilemap.reset();
|
|
|
|
tileset.reset();
|
|
|
|
picks.clear();
|
|
|
|
mask.reset();
|
|
|
|
range.invalidate();
|
|
|
|
}
|
|
|
|
|
|
|
|
ClipboardFormat format() const {
|
|
|
|
if (image)
|
|
|
|
return ClipboardFormat::Image;
|
|
|
|
else if (tilemap)
|
|
|
|
return ClipboardFormat::Tilemap;
|
|
|
|
else if (range.valid())
|
|
|
|
return ClipboardFormat::DocRange;
|
|
|
|
else if (palette && picks.picks())
|
|
|
|
return ClipboardFormat::PaletteEntries;
|
|
|
|
else if (tileset && picks.picks())
|
|
|
|
return ClipboardFormat::Tileset;
|
|
|
|
else
|
|
|
|
return ClipboardFormat::None;
|
|
|
|
}
|
|
|
|
};
|
2016-01-30 20:13:46 +08:00
|
|
|
|
2018-05-24 23:56:07 +08:00
|
|
|
static bool use_native_clipboard()
|
|
|
|
{
|
|
|
|
return Preferences::instance().experimental.useNativeClipboard();
|
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
static Clipboard* g_instance = nullptr;
|
|
|
|
|
|
|
|
Clipboard* Clipboard::instance()
|
2018-05-24 23:56:07 +08:00
|
|
|
{
|
|
|
|
return g_instance;
|
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
Clipboard::Clipboard()
|
|
|
|
: m_data(new Data)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2016-01-30 20:13:46 +08:00
|
|
|
ASSERT(!g_instance);
|
|
|
|
g_instance = this;
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
registerNativeFormats();
|
2016-01-30 20:13:46 +08:00
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
Clipboard::~Clipboard()
|
2016-01-30 20:13:46 +08:00
|
|
|
{
|
2016-01-30 20:16:46 +08:00
|
|
|
ASSERT(g_instance == this);
|
2016-01-30 20:13:46 +08:00
|
|
|
g_instance = nullptr;
|
2010-03-01 10:36:05 +08:00
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
void Clipboard::setClipboardText(const std::string& text)
|
2009-03-08 03:14:40 +08:00
|
|
|
{
|
2018-05-24 23:56:07 +08:00
|
|
|
if (use_native_clipboard()) {
|
|
|
|
clip::set_text(text);
|
|
|
|
}
|
|
|
|
else {
|
2020-09-25 22:13:52 +08:00
|
|
|
m_data->text = text;
|
2018-05-24 23:56:07 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
bool Clipboard::getClipboardText(std::string& text)
|
2018-05-24 23:56:07 +08:00
|
|
|
{
|
|
|
|
if (use_native_clipboard()) {
|
|
|
|
return clip::get_text(text);
|
|
|
|
}
|
|
|
|
else {
|
2020-09-25 22:13:52 +08:00
|
|
|
text = m_data->text;
|
2018-05-24 23:56:07 +08:00
|
|
|
return true;
|
|
|
|
}
|
2016-01-30 20:13:46 +08:00
|
|
|
}
|
2009-03-08 03:14:40 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
void Clipboard::setData(Image* image,
|
|
|
|
Mask* mask,
|
|
|
|
Palette* palette,
|
2020-10-03 06:03:53 +08:00
|
|
|
Tileset* tileset,
|
|
|
|
bool set_native_clipboard,
|
2020-09-25 22:13:52 +08:00
|
|
|
bool image_source_is_transparent)
|
2016-01-30 20:13:46 +08:00
|
|
|
{
|
2020-10-03 06:03:53 +08:00
|
|
|
const bool isTilemap = (image && image->isTilemap());
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
m_data->clear();
|
|
|
|
m_data->palette.reset(palette);
|
2020-10-03 06:03:53 +08:00
|
|
|
m_data->tileset.reset(tileset);
|
2020-09-25 22:13:52 +08:00
|
|
|
m_data->mask.reset(mask);
|
2020-10-03 06:03:53 +08:00
|
|
|
if (isTilemap)
|
|
|
|
m_data->tilemap.reset(image);
|
|
|
|
else
|
|
|
|
m_data->image.reset(image);
|
2009-03-08 03:14:40 +08:00
|
|
|
|
2020-10-03 06:03:53 +08:00
|
|
|
if (set_native_clipboard) {
|
|
|
|
// Copy tilemap to the native clipboard
|
|
|
|
if (isTilemap) {
|
|
|
|
ASSERT(tileset);
|
|
|
|
setNativeBitmap(image, mask, palette, tileset);
|
2016-04-30 07:42:05 +08:00
|
|
|
}
|
2020-10-03 06:03:53 +08:00
|
|
|
// Copy non-tilemap images to the native clipboard
|
|
|
|
else {
|
2022-09-29 03:20:03 +08:00
|
|
|
color_t oldMask = 0;
|
2020-10-03 06:03:53 +08:00
|
|
|
if (image) {
|
|
|
|
oldMask = image->maskColor();
|
|
|
|
if (!image_source_is_transparent)
|
|
|
|
image->setMaskColor(-1);
|
|
|
|
}
|
2016-04-30 07:42:05 +08:00
|
|
|
|
2020-10-03 06:03:53 +08:00
|
|
|
if (use_native_clipboard())
|
|
|
|
setNativeBitmap(image, mask, palette);
|
2016-04-30 07:42:05 +08:00
|
|
|
|
2020-10-03 06:03:53 +08:00
|
|
|
if (image && !image_source_is_transparent)
|
|
|
|
image->setMaskColor(oldMask);
|
|
|
|
}
|
2016-04-30 07:42:05 +08:00
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
bool Clipboard::copyFromDocument(const Site& site, bool merged)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2020-10-03 06:03:53 +08:00
|
|
|
ASSERT(site.document());
|
|
|
|
const Doc* doc = static_cast<const Doc*>(site.document());
|
|
|
|
const Mask* mask = doc->mask();
|
|
|
|
const Palette* pal = doc->sprite()->palette(site.frame());
|
|
|
|
|
|
|
|
if (!merged &&
|
|
|
|
site.layer() &&
|
|
|
|
site.layer()->isTilemap() &&
|
|
|
|
site.tilemapMode() == TilemapMode::Tiles) {
|
|
|
|
const Tileset* ts = static_cast<LayerTilemap*>(site.layer())->tileset();
|
|
|
|
|
|
|
|
Image* image = new_tilemap_from_mask(site, mask);
|
|
|
|
if (!image)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
setData(
|
|
|
|
image,
|
|
|
|
(mask ? new Mask(*mask): nullptr),
|
|
|
|
(pal ? new Palette(*pal): nullptr),
|
2021-06-03 05:54:24 +08:00
|
|
|
Tileset::MakeCopyCopyingImages(ts),
|
2020-10-03 06:03:53 +08:00
|
|
|
true, // set native clipboard
|
|
|
|
site.layer() && !site.layer()->isBackground());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2013-03-12 07:29:45 +08:00
|
|
|
|
2019-02-26 05:02:58 +08:00
|
|
|
Image* image = new_image_from_mask(site, mask,
|
|
|
|
Preferences::instance().experimental.newBlend(),
|
|
|
|
merged);
|
2009-03-08 03:14:40 +08:00
|
|
|
if (!image)
|
|
|
|
return false;
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
setData(
|
2016-04-30 07:42:05 +08:00
|
|
|
image,
|
|
|
|
(mask ? new Mask(*mask): nullptr),
|
|
|
|
(pal ? new Palette(*pal): nullptr),
|
2020-10-03 06:03:53 +08:00
|
|
|
nullptr,
|
|
|
|
true, // set native clipboard
|
2016-04-30 07:42:05 +08:00
|
|
|
site.layer() && !site.layer()->isBackground());
|
|
|
|
|
2009-03-08 03:14:40 +08:00
|
|
|
return true;
|
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
ClipboardFormat Clipboard::format() const
|
2009-03-08 03:14:40 +08:00
|
|
|
{
|
2016-04-30 07:42:05 +08:00
|
|
|
// Check if the native clipboard has an image
|
2020-10-03 06:03:53 +08:00
|
|
|
if (use_native_clipboard() && hasNativeBitmap()) {
|
2020-09-25 22:13:52 +08:00
|
|
|
return ClipboardFormat::Image;
|
2020-10-03 06:03:53 +08:00
|
|
|
}
|
|
|
|
else {
|
2020-09-25 22:13:52 +08:00
|
|
|
return m_data->format();
|
2020-10-03 06:03:53 +08:00
|
|
|
}
|
2014-08-08 21:33:45 +08:00
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
void Clipboard::getDocumentRangeInfo(Doc** document, DocRange* range)
|
2014-08-08 21:33:45 +08:00
|
|
|
{
|
2020-09-25 22:13:52 +08:00
|
|
|
if (m_data->range.valid()) {
|
|
|
|
*document = m_data->range.document();
|
|
|
|
*range = m_data->range.range();
|
2014-08-08 21:33:45 +08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
*document = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
void Clipboard::clearMaskFromCels(Tx& tx,
|
|
|
|
Doc* doc,
|
2021-02-23 03:39:40 +08:00
|
|
|
const Site& site,
|
2020-09-25 22:13:52 +08:00
|
|
|
const CelList& cels,
|
|
|
|
const bool deselectMask)
|
2019-09-06 02:03:13 +08:00
|
|
|
{
|
|
|
|
for (Cel* cel : cels) {
|
|
|
|
ObjectId celId = cel->id();
|
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
clear_mask_from_cel(
|
2020-08-28 07:32:22 +08:00
|
|
|
tx, cel,
|
2021-02-23 03:39:40 +08:00
|
|
|
site.tilemapMode(),
|
|
|
|
site.tilesetMode());
|
2019-09-06 02:03:13 +08:00
|
|
|
|
|
|
|
// Get cel again just in case the cmd::ClearMask() called cmd::ClearCel()
|
|
|
|
cel = doc::get<Cel>(celId);
|
2021-02-23 03:39:40 +08:00
|
|
|
if (site.shouldTrimCel(cel))
|
2019-09-06 02:03:13 +08:00
|
|
|
tx(new cmd::TrimCel(cel));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (deselectMask)
|
|
|
|
tx(new cmd::DeselectMask(doc));
|
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
void Clipboard::clearContent()
|
2014-08-08 21:33:45 +08:00
|
|
|
{
|
2021-04-20 01:45:18 +08:00
|
|
|
if (use_native_clipboard())
|
|
|
|
clearNativeContent();
|
2020-09-25 22:13:52 +08:00
|
|
|
m_data->clear();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
void Clipboard::cut(ContextWriter& writer)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2013-03-12 07:29:45 +08:00
|
|
|
ASSERT(writer.document() != NULL);
|
|
|
|
ASSERT(writer.sprite() != NULL);
|
|
|
|
ASSERT(writer.layer() != NULL);
|
2011-03-23 08:11:25 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
if (!copyFromDocument(*writer.site())) {
|
2009-06-11 23:11:11 +08:00
|
|
|
Console console;
|
|
|
|
console.printf("Can't copying an image portion from the current layer\n");
|
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
else {
|
2019-03-30 02:57:10 +08:00
|
|
|
// TODO This code is similar to DocView::onClear()
|
2008-10-11 23:59:13 +08:00
|
|
|
{
|
2023-12-13 08:34:30 +08:00
|
|
|
Tx tx(writer, "Cut");
|
2019-09-06 02:03:13 +08:00
|
|
|
Site site = writer.context()->activeSite();
|
|
|
|
CelList cels;
|
|
|
|
if (site.range().enabled()) {
|
2021-04-16 21:23:35 +08:00
|
|
|
cels = get_unique_cels_to_edit_pixels(site.sprite(), site.range());
|
2019-09-06 02:03:13 +08:00
|
|
|
}
|
|
|
|
else if (site.cel()) {
|
|
|
|
cels.push_back(site.cel());
|
|
|
|
}
|
2020-09-25 22:13:52 +08:00
|
|
|
clearMaskFromCels(tx,
|
|
|
|
writer.document(),
|
2021-02-23 03:39:40 +08:00
|
|
|
site,
|
2020-09-25 22:13:52 +08:00
|
|
|
cels,
|
|
|
|
true); // Deselect mask
|
2018-08-21 03:00:59 +08:00
|
|
|
tx.commit();
|
2008-10-11 23:59:13 +08:00
|
|
|
}
|
2013-03-12 07:29:45 +08:00
|
|
|
writer.document()->generateMaskBoundaries();
|
2023-01-05 23:30:24 +08:00
|
|
|
#ifdef ENABLE_UI
|
2013-03-12 07:29:45 +08:00
|
|
|
update_screen_for_document(writer.document());
|
2023-01-05 23:30:24 +08:00
|
|
|
#endif
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
void Clipboard::copy(const ContextReader& reader)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2013-03-12 07:29:45 +08:00
|
|
|
ASSERT(reader.document() != NULL);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
if (!copyFromDocument(*reader.site())) {
|
2009-06-11 23:11:11 +08:00
|
|
|
Console console;
|
2010-09-19 10:54:56 +08:00
|
|
|
console.printf("Can't copying an image portion from the current layer\n");
|
2014-08-08 21:33:45 +08:00
|
|
|
return;
|
2009-06-11 23:11:11 +08:00
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
void Clipboard::copyMerged(const ContextReader& reader)
|
2016-05-17 23:59:48 +08:00
|
|
|
{
|
|
|
|
ASSERT(reader.document() != NULL);
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
copyFromDocument(*reader.site(), true);
|
2016-05-17 23:59:48 +08:00
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
void Clipboard::copyRange(const ContextReader& reader, const DocRange& range)
|
2014-08-08 21:33:45 +08:00
|
|
|
{
|
|
|
|
ASSERT(reader.document() != NULL);
|
|
|
|
|
|
|
|
ContextWriter writer(reader);
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
clearContent();
|
|
|
|
m_data->range.setRange(writer.document(), range);
|
2014-08-08 21:33:45 +08:00
|
|
|
|
2023-01-05 23:30:24 +08:00
|
|
|
#ifdef ENABLE_UI
|
2014-08-08 21:33:45 +08:00
|
|
|
// TODO Replace this with a signal, because here the timeline
|
2015-03-17 06:53:20 +08:00
|
|
|
// depends on the clipboard and the clipboard on the timeline.
|
2016-04-23 00:19:06 +08:00
|
|
|
App::instance()->timeline()->activateClipboardRange();
|
2023-01-05 23:30:24 +08:00
|
|
|
#endif
|
2014-08-08 21:33:45 +08:00
|
|
|
}
|
|
|
|
|
2020-10-03 06:03:53 +08:00
|
|
|
void Clipboard::copyImage(const Image* image,
|
|
|
|
const Mask* mask,
|
|
|
|
const Palette* pal)
|
2009-03-08 03:14:40 +08:00
|
|
|
{
|
2021-04-30 21:47:57 +08:00
|
|
|
ASSERT(image->pixelFormat() != IMAGE_TILEMAP);
|
2020-09-25 22:13:52 +08:00
|
|
|
setData(
|
2015-07-24 09:42:14 +08:00
|
|
|
Image::createCopy(image),
|
|
|
|
(mask ? new Mask(*mask): nullptr),
|
2016-04-30 07:42:05 +08:00
|
|
|
(pal ? new Palette(*pal): nullptr),
|
2020-10-03 06:03:53 +08:00
|
|
|
nullptr,
|
2016-04-30 07:42:05 +08:00
|
|
|
true, false);
|
2009-03-08 03:14:40 +08:00
|
|
|
}
|
|
|
|
|
2021-04-30 21:47:57 +08:00
|
|
|
void Clipboard::copyTilemap(const Image* image,
|
|
|
|
const Mask* mask,
|
|
|
|
const Palette* pal,
|
|
|
|
const Tileset* tileset)
|
|
|
|
{
|
|
|
|
ASSERT(image->pixelFormat() == IMAGE_TILEMAP);
|
|
|
|
setData(
|
|
|
|
Image::createCopy(image),
|
|
|
|
(mask ? new Mask(*mask): nullptr),
|
|
|
|
(pal ? new Palette(*pal): nullptr),
|
|
|
|
Tileset::MakeCopyCopyingImages(tileset),
|
|
|
|
true, false);
|
|
|
|
}
|
|
|
|
|
2020-10-03 06:03:53 +08:00
|
|
|
void Clipboard::copyPalette(const Palette* palette,
|
|
|
|
const PalettePicks& picks)
|
2015-05-11 08:36:46 +08:00
|
|
|
{
|
|
|
|
if (!picks.picks())
|
|
|
|
return; // Do nothing case
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
setData(nullptr,
|
|
|
|
nullptr,
|
|
|
|
new Palette(*palette),
|
2020-10-03 06:03:53 +08:00
|
|
|
nullptr,
|
|
|
|
true, // set native clipboard
|
|
|
|
false);
|
2020-09-25 22:13:52 +08:00
|
|
|
m_data->picks = picks;
|
2015-05-11 08:36:46 +08:00
|
|
|
}
|
|
|
|
|
2023-12-30 04:11:44 +08:00
|
|
|
void Clipboard::paste(Context* ctx,
|
2020-10-03 06:03:53 +08:00
|
|
|
const bool interactive)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2023-12-30 04:11:44 +08:00
|
|
|
const Site site = ctx->activeSite();
|
2019-06-28 02:34:56 +08:00
|
|
|
Doc* dstDoc = site.document();
|
|
|
|
if (!dstDoc)
|
2013-01-21 05:40:37 +08:00
|
|
|
return;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2019-06-28 02:34:56 +08:00
|
|
|
Sprite* dstSpr = site.sprite();
|
|
|
|
if (!dstSpr)
|
|
|
|
return;
|
2014-08-08 21:33:45 +08:00
|
|
|
|
2023-01-05 23:30:24 +08:00
|
|
|
#ifdef ENABLE_UI
|
|
|
|
Editor* editor = Editor::activeEditor();
|
|
|
|
#endif
|
2022-11-17 22:56:01 +08:00
|
|
|
bool updateDstDoc = false;
|
2022-10-20 23:31:22 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
switch (format()) {
|
2014-08-08 21:33:45 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
case ClipboardFormat::Image: {
|
2018-05-24 23:56:07 +08:00
|
|
|
// Get the image from the native clipboard.
|
2020-09-25 22:13:52 +08:00
|
|
|
if (!getImage(nullptr))
|
2014-08-08 21:33:45 +08:00
|
|
|
return;
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
ASSERT(m_data->image);
|
2019-06-29 05:50:13 +08:00
|
|
|
|
2019-06-28 02:34:56 +08:00
|
|
|
Palette* dst_palette = dstSpr->palette(site.frame());
|
2014-08-08 21:33:45 +08:00
|
|
|
|
|
|
|
// Source image (clipboard or a converted copy to the destination 'imgtype')
|
2015-05-11 08:36:46 +08:00
|
|
|
ImageRef src_image;
|
2020-10-03 06:03:53 +08:00
|
|
|
if (// Copy image of the same pixel format
|
|
|
|
(m_data->image->pixelFormat() == dstSpr->pixelFormat() &&
|
|
|
|
// Indexed images can be copied directly only if both images
|
|
|
|
// have the same palette.
|
|
|
|
(m_data->image->pixelFormat() != IMAGE_INDEXED ||
|
|
|
|
m_data->palette->countDiff(dst_palette, NULL, NULL) == 0))) {
|
2020-09-25 22:13:52 +08:00
|
|
|
src_image = m_data->image;
|
2014-08-08 21:33:45 +08:00
|
|
|
}
|
|
|
|
else {
|
2019-06-28 02:34:56 +08:00
|
|
|
RgbMap* dst_rgbmap = dstSpr->rgbMap(site.frame());
|
2014-08-08 21:33:45 +08:00
|
|
|
|
2015-05-11 08:36:46 +08:00
|
|
|
src_image.reset(
|
|
|
|
render::convert_pixel_format(
|
2020-09-25 22:13:52 +08:00
|
|
|
m_data->image.get(), NULL, dstSpr->pixelFormat(),
|
2019-04-04 06:32:24 +08:00
|
|
|
render::Dithering(),
|
2020-09-25 22:13:52 +08:00
|
|
|
dst_rgbmap, m_data->palette.get(),
|
2015-07-14 20:44:31 +08:00
|
|
|
false,
|
|
|
|
0));
|
2014-08-08 21:33:45 +08:00
|
|
|
}
|
|
|
|
|
2023-01-05 23:30:24 +08:00
|
|
|
#ifdef ENABLE_UI
|
2022-10-20 23:31:22 +08:00
|
|
|
if (editor && interactive) {
|
2020-01-31 06:47:23 +08:00
|
|
|
// TODO we don't support pasting in multiple cels at the
|
|
|
|
// moment, so we clear the range here (same as in
|
|
|
|
// PasteTextCommand::onExecute())
|
2019-09-06 02:03:13 +08:00
|
|
|
App::instance()->timeline()->clearAndInvalidateRange();
|
|
|
|
|
2019-06-28 02:34:56 +08:00
|
|
|
// Change to MovingPixelsState
|
2022-10-20 23:31:22 +08:00
|
|
|
editor->pasteImage(src_image.get(),
|
|
|
|
m_data->mask.get());
|
2019-06-28 02:34:56 +08:00
|
|
|
}
|
2023-01-05 23:30:24 +08:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
2019-06-28 02:34:56 +08:00
|
|
|
// Non-interactive version (just copy the image to the cel)
|
|
|
|
Layer* dstLayer = site.layer();
|
|
|
|
ASSERT(dstLayer);
|
|
|
|
if (!dstLayer || !dstLayer->isImage())
|
|
|
|
return;
|
|
|
|
|
2023-12-30 04:11:44 +08:00
|
|
|
ContextWriter writer(ctx);
|
2023-12-13 08:34:30 +08:00
|
|
|
Tx tx(writer, "Paste Image");
|
2019-06-28 02:34:56 +08:00
|
|
|
DocApi api = dstDoc->getApi(tx);
|
|
|
|
Cel* dstCel = api.addCel(
|
|
|
|
static_cast<LayerImage*>(dstLayer), site.frame(),
|
|
|
|
ImageRef(Image::createCopy(src_image.get())));
|
|
|
|
|
|
|
|
// Adjust bounds
|
|
|
|
if (dstCel) {
|
2020-09-25 22:13:52 +08:00
|
|
|
if (m_data->mask) {
|
2019-06-28 02:34:56 +08:00
|
|
|
if (dstLayer->isReference()) {
|
|
|
|
dstCel->setBounds(dstSpr->bounds());
|
|
|
|
|
|
|
|
Mask emptyMask;
|
|
|
|
tx(new cmd::SetMask(dstDoc, &emptyMask));
|
|
|
|
}
|
|
|
|
else {
|
2020-09-25 22:13:52 +08:00
|
|
|
dstCel->setBounds(m_data->mask->bounds());
|
|
|
|
tx(new cmd::SetMask(dstDoc, m_data->mask.get()));
|
2019-06-28 02:34:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tx.commit();
|
|
|
|
}
|
2014-08-08 21:33:45 +08:00
|
|
|
break;
|
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2020-10-03 06:03:53 +08:00
|
|
|
case ClipboardFormat::Tilemap: {
|
2023-01-05 23:30:24 +08:00
|
|
|
#ifdef ENABLE_UI
|
2022-10-20 23:31:22 +08:00
|
|
|
if (editor && interactive) {
|
2020-10-03 06:03:53 +08:00
|
|
|
// TODO match both tilesets?
|
|
|
|
// TODO add post-command parameters (issue #2324)
|
|
|
|
|
|
|
|
// Change to MovingTilemapState
|
2022-10-20 23:31:22 +08:00
|
|
|
editor->pasteImage(m_data->tilemap.get(),
|
|
|
|
m_data->mask.get());
|
2020-10-03 06:03:53 +08:00
|
|
|
}
|
2023-01-05 23:30:24 +08:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
2020-10-03 06:03:53 +08:00
|
|
|
// TODO non-interactive version (for scripts)
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
case ClipboardFormat::DocRange: {
|
|
|
|
DocRange srcRange = m_data->range.range();
|
|
|
|
Doc* srcDoc = m_data->range.document();
|
2014-08-18 11:21:03 +08:00
|
|
|
Sprite* srcSpr = srcDoc->sprite();
|
|
|
|
|
|
|
|
switch (srcRange.type()) {
|
|
|
|
|
2018-07-07 21:07:21 +08:00
|
|
|
case DocRange::kCels: {
|
2019-06-28 02:34:56 +08:00
|
|
|
Layer* dstLayer = site.layer();
|
|
|
|
ASSERT(dstLayer);
|
|
|
|
if (!dstLayer)
|
|
|
|
return;
|
|
|
|
|
|
|
|
frame_t dstFrameFirst = site.frame();
|
2016-08-25 23:31:00 +08:00
|
|
|
|
2018-07-07 21:07:21 +08:00
|
|
|
DocRange dstRange;
|
|
|
|
dstRange.startRange(dstLayer, dstFrameFirst, DocRange::kCels);
|
2016-08-25 23:31:00 +08:00
|
|
|
for (layer_t i=1; i<srcRange.layers(); ++i) {
|
2016-12-08 20:11:54 +08:00
|
|
|
dstLayer = dstLayer->getPreviousBrowsable();
|
2016-08-25 23:31:00 +08:00
|
|
|
if (dstLayer == nullptr)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
dstRange.endRange(dstLayer, dstFrameFirst+srcRange.frames()-1);
|
|
|
|
|
2015-08-08 05:27:48 +08:00
|
|
|
// We can use a document range op (copy_range) to copy/paste
|
|
|
|
// cels in the same document.
|
|
|
|
if (srcDoc == dstDoc) {
|
2015-08-20 03:43:13 +08:00
|
|
|
// This is the app::copy_range (not clipboard::copy_range()).
|
2022-11-17 22:56:01 +08:00
|
|
|
if (srcRange.layers() == dstRange.layers()) {
|
2018-07-07 21:07:21 +08:00
|
|
|
app::copy_range(srcDoc, srcRange, dstRange, kDocRangeBefore);
|
2022-11-17 22:56:01 +08:00
|
|
|
updateDstDoc = true;
|
|
|
|
}
|
|
|
|
break;
|
2015-08-08 05:27:48 +08:00
|
|
|
}
|
2014-09-01 01:24:29 +08:00
|
|
|
|
2023-12-30 04:11:44 +08:00
|
|
|
ContextWriter writer(ctx);
|
2023-12-13 08:34:30 +08:00
|
|
|
Tx tx(writer, "Paste Cels");
|
2018-08-21 03:00:59 +08:00
|
|
|
DocApi api = dstDoc->getApi(tx);
|
2014-08-18 11:21:03 +08:00
|
|
|
|
2015-08-08 04:09:03 +08:00
|
|
|
// Add extra frames if needed
|
2016-08-25 23:31:00 +08:00
|
|
|
while (dstFrameFirst+srcRange.frames() > dstSpr->totalFrames())
|
2015-08-08 04:09:03 +08:00
|
|
|
api.addFrame(dstSpr, dstSpr->totalFrames());
|
|
|
|
|
2020-08-18 04:14:12 +08:00
|
|
|
auto srcLayers = srcRange.selectedLayers().toBrowsableLayerList();
|
|
|
|
auto dstLayers = dstRange.selectedLayers().toBrowsableLayerList();
|
2018-03-30 03:45:57 +08:00
|
|
|
|
|
|
|
auto srcIt = srcLayers.begin();
|
|
|
|
auto dstIt = dstLayers.begin();
|
|
|
|
auto srcEnd = srcLayers.end();
|
|
|
|
auto dstEnd = dstLayers.end();
|
2016-08-25 23:31:00 +08:00
|
|
|
|
|
|
|
for (; srcIt != srcEnd && dstIt != dstEnd; ++srcIt, ++dstIt) {
|
|
|
|
auto srcLayer = *srcIt;
|
|
|
|
auto dstLayer = *dstIt;
|
|
|
|
|
|
|
|
if (!srcLayer->isImage() ||
|
|
|
|
!dstLayer->isImage())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
frame_t dstFrame = dstFrameFirst;
|
|
|
|
for (frame_t srcFrame : srcRange.selectedFrames()) {
|
|
|
|
Cel* srcCel = srcLayer->cel(srcFrame);
|
2015-08-08 04:09:03 +08:00
|
|
|
|
|
|
|
if (srcCel && srcCel->image()) {
|
2019-09-27 06:09:23 +08:00
|
|
|
api.copyCel(
|
|
|
|
static_cast<LayerImage*>(srcLayer), srcFrame,
|
|
|
|
static_cast<LayerImage*>(dstLayer), dstFrame);
|
2014-08-18 11:21:03 +08:00
|
|
|
}
|
|
|
|
else {
|
2019-09-27 06:09:23 +08:00
|
|
|
if (Cel* dstCel = dstLayer->cel(dstFrame))
|
2014-09-17 20:53:25 +08:00
|
|
|
api.clearCel(dstCel);
|
2014-08-18 11:21:03 +08:00
|
|
|
}
|
|
|
|
|
2016-08-25 23:31:00 +08:00
|
|
|
++dstFrame;
|
|
|
|
}
|
2014-08-18 11:21:03 +08:00
|
|
|
}
|
|
|
|
|
2018-08-21 03:00:59 +08:00
|
|
|
tx.commit();
|
2022-11-17 22:56:01 +08:00
|
|
|
updateDstDoc = true;
|
2014-08-18 11:21:03 +08:00
|
|
|
break;
|
|
|
|
}
|
2014-03-30 08:31:27 +08:00
|
|
|
|
2018-07-07 21:07:21 +08:00
|
|
|
case DocRange::kFrames: {
|
2019-06-28 02:34:56 +08:00
|
|
|
frame_t dstFrame = site.frame();
|
2014-08-18 11:21:03 +08:00
|
|
|
|
2018-07-07 21:07:21 +08:00
|
|
|
// We use a DocRange operation to copy frames inside
|
2016-08-25 23:31:00 +08:00
|
|
|
// the same sprite.
|
2016-05-10 02:47:09 +08:00
|
|
|
if (srcSpr == dstSpr) {
|
2018-07-07 21:07:21 +08:00
|
|
|
DocRange dstRange;
|
|
|
|
dstRange.startRange(nullptr, dstFrame, DocRange::kFrames);
|
2016-08-25 23:31:00 +08:00
|
|
|
dstRange.endRange(nullptr, dstFrame);
|
2018-07-07 21:07:21 +08:00
|
|
|
app::copy_range(srcDoc, srcRange, dstRange, kDocRangeBefore);
|
2022-11-17 22:56:01 +08:00
|
|
|
updateDstDoc = true;
|
2016-08-25 23:31:00 +08:00
|
|
|
break;
|
2016-05-10 02:47:09 +08:00
|
|
|
}
|
|
|
|
|
2023-12-30 04:11:44 +08:00
|
|
|
ContextWriter writer(ctx);
|
2023-12-13 08:34:30 +08:00
|
|
|
Tx tx(writer, "Paste Frames");
|
2018-08-21 03:00:59 +08:00
|
|
|
DocApi api = dstDoc->getApi(tx);
|
2016-05-10 02:47:09 +08:00
|
|
|
|
2016-08-25 23:31:00 +08:00
|
|
|
auto srcLayers = srcSpr->allBrowsableLayers();
|
|
|
|
auto dstLayers = dstSpr->allBrowsableLayers();
|
2016-05-10 02:47:09 +08:00
|
|
|
|
2016-08-25 23:31:00 +08:00
|
|
|
for (frame_t srcFrame : srcRange.selectedFrames()) {
|
|
|
|
api.addEmptyFrame(dstSpr, dstFrame);
|
2016-05-10 02:47:09 +08:00
|
|
|
api.setFrameDuration(dstSpr, dstFrame, srcSpr->frameDuration(srcFrame));
|
2014-08-18 11:21:03 +08:00
|
|
|
|
2016-08-25 23:31:00 +08:00
|
|
|
auto srcIt = srcLayers.begin();
|
|
|
|
auto dstIt = dstLayers.begin();
|
|
|
|
auto srcEnd = srcLayers.end();
|
|
|
|
auto dstEnd = dstLayers.end();
|
|
|
|
|
|
|
|
for (; srcIt != srcEnd && dstIt != dstEnd; ++srcIt, ++dstIt) {
|
|
|
|
auto srcLayer = *srcIt;
|
|
|
|
auto dstLayer = *dstIt;
|
|
|
|
|
|
|
|
if (!srcLayer->isImage() ||
|
|
|
|
!dstLayer->isImage())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Cel* cel = static_cast<LayerImage*>(srcLayer)->cel(srcFrame);
|
2014-08-18 11:21:03 +08:00
|
|
|
if (cel && cel->image()) {
|
|
|
|
api.copyCel(
|
2016-08-25 23:31:00 +08:00
|
|
|
static_cast<LayerImage*>(srcLayer), srcFrame,
|
|
|
|
static_cast<LayerImage*>(dstLayer), dstFrame);
|
2014-08-18 11:21:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-29 07:39:11 +08:00
|
|
|
++dstFrame;
|
2014-08-18 11:21:03 +08:00
|
|
|
}
|
2014-03-30 08:31:27 +08:00
|
|
|
|
2018-08-21 03:00:59 +08:00
|
|
|
tx.commit();
|
2022-11-17 22:56:01 +08:00
|
|
|
updateDstDoc = true;
|
2014-08-18 11:21:03 +08:00
|
|
|
break;
|
|
|
|
}
|
2015-02-12 23:16:25 +08:00
|
|
|
|
2018-07-07 21:07:21 +08:00
|
|
|
case DocRange::kLayers: {
|
2014-08-18 11:21:03 +08:00
|
|
|
if (srcDoc->colorMode() != dstDoc->colorMode())
|
2014-08-08 21:33:45 +08:00
|
|
|
throw std::runtime_error("You cannot copy layers of document with different color modes");
|
|
|
|
|
2023-12-30 04:11:44 +08:00
|
|
|
ContextWriter writer(ctx);
|
2023-12-13 08:34:30 +08:00
|
|
|
Tx tx(writer, "Paste Layers");
|
2018-08-21 03:00:59 +08:00
|
|
|
DocApi api = dstDoc->getApi(tx);
|
2014-08-08 21:33:45 +08:00
|
|
|
|
2016-08-25 23:31:00 +08:00
|
|
|
// Remove children if their parent is selected so we only
|
|
|
|
// copy the parent.
|
|
|
|
SelectedLayers srcLayersSet = srcRange.selectedLayers();
|
|
|
|
srcLayersSet.removeChildrenIfParentIsSelected();
|
2020-08-18 04:14:12 +08:00
|
|
|
LayerList srcLayers = srcLayersSet.toBrowsableLayerList();
|
2016-08-25 23:31:00 +08:00
|
|
|
|
2014-08-18 11:21:03 +08:00
|
|
|
// Expand frames of dstDoc if it's needed.
|
2016-08-25 23:31:00 +08:00
|
|
|
frame_t maxFrame = 0;
|
|
|
|
for (Layer* srcLayer : srcLayers) {
|
|
|
|
if (!srcLayer->isImage())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Cel* lastCel = static_cast<LayerImage*>(srcLayer)->getLastCel();
|
2014-08-20 19:29:21 +08:00
|
|
|
if (lastCel && maxFrame < lastCel->frame())
|
2014-08-12 18:57:40 +08:00
|
|
|
maxFrame = lastCel->frame();
|
|
|
|
}
|
2015-03-17 20:29:24 +08:00
|
|
|
while (dstSpr->totalFrames() < maxFrame+1)
|
|
|
|
api.addEmptyFrame(dstSpr, dstSpr->totalFrames());
|
2014-08-12 18:57:40 +08:00
|
|
|
|
2016-08-25 23:31:00 +08:00
|
|
|
for (Layer* srcLayer : srcLayers) {
|
2015-03-17 21:06:37 +08:00
|
|
|
Layer* afterThis;
|
2016-08-25 23:31:00 +08:00
|
|
|
if (srcLayer->isBackground() && !dstDoc->sprite()->backgroundLayer())
|
2015-03-17 21:06:37 +08:00
|
|
|
afterThis = nullptr;
|
|
|
|
else
|
2016-06-08 06:38:56 +08:00
|
|
|
afterThis = dstSpr->root()->lastLayer();
|
2015-03-17 21:06:37 +08:00
|
|
|
|
2016-08-25 23:31:00 +08:00
|
|
|
Layer* newLayer = nullptr;
|
|
|
|
if (srcLayer->isImage())
|
|
|
|
newLayer = new LayerImage(dstSpr);
|
|
|
|
else if (srcLayer->isGroup())
|
|
|
|
newLayer = new LayerGroup(dstSpr);
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
|
2016-06-08 06:38:56 +08:00
|
|
|
api.addLayer(dstSpr->root(), newLayer, afterThis);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2016-08-25 23:31:00 +08:00
|
|
|
srcDoc->copyLayerContent(srcLayer, dstDoc, newLayer);
|
2014-08-08 21:33:45 +08:00
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2018-08-21 03:00:59 +08:00
|
|
|
tx.commit();
|
2022-11-17 22:56:01 +08:00
|
|
|
updateDstDoc = true;
|
2014-08-08 21:33:45 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2014-08-18 11:21:03 +08:00
|
|
|
|
2014-08-08 21:33:45 +08:00
|
|
|
}
|
2022-11-17 22:56:01 +08:00
|
|
|
|
|
|
|
// Update all editors/views showing this document
|
|
|
|
if (updateDstDoc)
|
|
|
|
dstDoc->notifyGeneralUpdate();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2012-02-12 04:06:35 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
ImageRef Clipboard::getImage(Palette* palette)
|
2019-06-29 05:50:13 +08:00
|
|
|
{
|
|
|
|
// Get the image from the native clipboard.
|
|
|
|
if (use_native_clipboard()) {
|
|
|
|
Image* native_image = nullptr;
|
|
|
|
Mask* native_mask = nullptr;
|
|
|
|
Palette* native_palette = nullptr;
|
2020-10-03 06:03:53 +08:00
|
|
|
Tileset* native_tileset = nullptr;
|
2020-09-25 22:13:52 +08:00
|
|
|
getNativeBitmap(&native_image,
|
|
|
|
&native_mask,
|
2020-10-03 06:03:53 +08:00
|
|
|
&native_palette,
|
|
|
|
&native_tileset);
|
|
|
|
if (native_image) {
|
2020-09-25 22:13:52 +08:00
|
|
|
setData(native_image,
|
|
|
|
native_mask,
|
|
|
|
native_palette,
|
2020-10-03 06:03:53 +08:00
|
|
|
native_tileset,
|
2020-09-25 22:13:52 +08:00
|
|
|
false, false);
|
2020-10-03 06:03:53 +08:00
|
|
|
}
|
2019-06-29 05:50:13 +08:00
|
|
|
}
|
2020-09-25 22:13:52 +08:00
|
|
|
if (m_data->palette && palette)
|
|
|
|
m_data->palette->copyColorsTo(palette);
|
|
|
|
return m_data->image;
|
2019-06-29 05:50:13 +08:00
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
bool Clipboard::getImageSize(gfx::Size& size)
|
2012-02-12 04:06:35 +08:00
|
|
|
{
|
2020-09-25 22:13:52 +08:00
|
|
|
if (use_native_clipboard() && getNativeBitmapSize(&size))
|
2016-04-30 07:42:05 +08:00
|
|
|
return true;
|
2018-05-24 23:56:07 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
if (m_data->image) {
|
|
|
|
size.w = m_data->image->width();
|
|
|
|
size.h = m_data->image->height();
|
2012-02-12 04:06:35 +08:00
|
|
|
return true;
|
|
|
|
}
|
2016-04-30 07:42:05 +08:00
|
|
|
|
|
|
|
return false;
|
2012-02-12 04:06:35 +08:00
|
|
|
}
|
2013-08-06 08:20:19 +08:00
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
Palette* Clipboard::getPalette()
|
2015-05-11 08:36:46 +08:00
|
|
|
{
|
2020-09-25 22:13:52 +08:00
|
|
|
if (format() == ClipboardFormat::PaletteEntries) {
|
|
|
|
ASSERT(m_data->palette);
|
|
|
|
return m_data->palette.get();
|
2015-05-11 08:36:46 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
const PalettePicks& Clipboard::getPalettePicks()
|
2015-05-11 08:36:46 +08:00
|
|
|
{
|
2020-09-25 22:13:52 +08:00
|
|
|
return m_data->picks;
|
2015-05-11 08:36:46 +08:00
|
|
|
}
|
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
} // namespace app
|