Refactor clipboard code

* Moved all clipboard:: functions inside app::Clipboard class
* Convert app:📋:ClipboardFormat enum to
  app::ClipboardFormat enum class
* Added app::Context::clipboard()
This commit is contained in:
David Capello 2020-09-25 11:13:52 -03:00
parent 44f65ad305
commit edebb57f66
15 changed files with 295 additions and 254 deletions

View File

@ -139,7 +139,7 @@ public:
#ifdef ENABLE_UI #ifdef ENABLE_UI
RecentFiles m_recent_files; RecentFiles m_recent_files;
InputChain m_inputChain; InputChain m_inputChain;
clipboard::ClipboardManager m_clipboardManager; Clipboard m_clipboard;
#endif #endif
// This is a raw pointer because we want to delete it explicitly. // This is a raw pointer because we want to delete it explicitly.
// (e.g. if an exception occurs, the ~Modules() doesn't have to // (e.g. if an exception occurs, the ~Modules() doesn't have to
@ -296,7 +296,7 @@ int App::initialize(const AppOptions& options)
// Set the ClipboardDelegate impl to copy/paste text in the native // Set the ClipboardDelegate impl to copy/paste text in the native
// clipboard from the ui::Entry control. // clipboard from the ui::Entry control.
m_uiSystem->setClipboardDelegate(&m_modules->m_clipboardManager); m_uiSystem->setClipboardDelegate(&m_modules->m_clipboard);
// Setup the GUI cursor and redraw screen // Setup the GUI cursor and redraw screen
ui::set_use_native_cursors(preferences().cursor.useNativeCursor()); ui::set_use_native_cursors(preferences().cursor.useNativeCursor());

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2016-2017 David Capello // Copyright (C) 2016-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -37,7 +38,7 @@ bool CopyMergedCommand::onEnabled(Context* ctx)
void CopyMergedCommand::onExecute(Context* ctx) void CopyMergedCommand::onExecute(Context* ctx)
{ {
ContextReader reader(ctx); ContextReader reader(ctx);
clipboard::copy_merged(reader); ctx->clipboard()->copyMerged(reader);
} }
Command* CommandFactory::createCopyMergedCommand() Command* CommandFactory::createCopyMergedCommand()

View File

@ -25,7 +25,6 @@
#include "app/ui/workspace.h" #include "app/ui/workspace.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "app/util/clipboard.h" #include "app/util/clipboard.h"
#include "app/util/clipboard.h"
#include "app/util/pixel_ratio.h" #include "app/util/pixel_ratio.h"
#include "base/clamp.h" #include "base/clamp.h"
#include "doc/cel.h" #include "doc/cel.h"
@ -72,12 +71,12 @@ NewFileCommand::NewFileCommand()
{ {
} }
bool NewFileCommand::onEnabled(Context* context) bool NewFileCommand::onEnabled(Context* ctx)
{ {
return return
(!params().fromClipboard() (!params().fromClipboard()
#ifdef ENABLE_UI #ifdef ENABLE_UI
|| (clipboard::get_current_format() == clipboard::ClipboardImage) || (ctx->clipboard()->format() == ClipboardFormat::Image)
#endif #endif
); );
} }
@ -97,7 +96,7 @@ void NewFileCommand::onExecute(Context* ctx)
#ifdef ENABLE_UI #ifdef ENABLE_UI
if (params().fromClipboard()) { if (params().fromClipboard()) {
clipboardImage = clipboard::get_image(&clipboardPalette); clipboardImage = ctx->clipboard()->getImage(&clipboardPalette);
if (!clipboardImage) if (!clipboardImage)
return; return;
@ -133,7 +132,7 @@ void NewFileCommand::onExecute(Context* ctx)
// If the clipboard contains an image, we can show the size of the // If the clipboard contains an image, we can show the size of the
// clipboard as default image size. // clipboard as default image size.
gfx::Size clipboardSize; gfx::Size clipboardSize;
if (clipboard::get_image_size(clipboardSize)) { if (ctx->clipboard()->getImageSize(clipboardSize)) {
w = clipboardSize.w; w = clipboardSize.w;
h = clipboardSize.h; h = clipboardSize.h;
} }

View File

@ -118,21 +118,21 @@ void NewLayerCommand::onLoadParams(const Params& commandParams)
m_place = Place::BeforeActiveLayer; m_place = Place::BeforeActiveLayer;
} }
bool NewLayerCommand::onEnabled(Context* context) bool NewLayerCommand::onEnabled(Context* ctx)
{ {
if (!context->checkFlags(ContextFlags::ActiveDocumentIsWritable | if (!ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::HasActiveSprite)) ContextFlags::HasActiveSprite))
return false; return false;
#ifdef ENABLE_UI #ifdef ENABLE_UI
if (params().fromClipboard() && if (params().fromClipboard() &&
clipboard::get_current_format() != clipboard::ClipboardImage) ctx->clipboard()->format() != ClipboardFormat::Image)
return false; return false;
#endif #endif
if ((params().viaCut() || if ((params().viaCut() ||
params().viaCopy()) && params().viaCopy()) &&
!context->checkFlags(ContextFlags::HasVisibleMask)) !ctx->checkFlags(ContextFlags::HasVisibleMask))
return false; return false;
return true; return true;
@ -392,7 +392,7 @@ void NewLayerCommand::onExecute(Context* context)
#ifdef ENABLE_UI #ifdef ENABLE_UI
// Paste new layer from clipboard // Paste new layer from clipboard
else if (params().fromClipboard() && layer->isImage()) { else if (params().fromClipboard() && layer->isImage()) {
clipboard::paste(context, false); context->clipboard()->paste(context, false);
if (layer->isReference()) { if (layer->isReference()) {
if (Cel* cel = layer->cel(site.frame())) { if (Cel* cel = layer->cel(site.frame())) {

View File

@ -19,6 +19,7 @@
#include "app/doc.h" #include "app/doc.h"
#include "app/pref/preferences.h" #include "app/pref/preferences.h"
#include "app/site.h" #include "app/site.h"
#include "app/util/clipboard.h"
#include "base/scoped_value.h" #include "base/scoped_value.h"
#include "doc/layer.h" #include "doc/layer.h"
#include "ui/system.h" #include "ui/system.h"
@ -59,6 +60,16 @@ Preferences& Context::preferences() const
return *m_preferences; return *m_preferences;
} }
Clipboard* Context::clipboard() const
{
#ifdef ENABLE_UI
return Clipboard::instance();
#else
// TODO support clipboard when !ENABLE_UI
throw std::runtime_error("Clipboard not supported");
#endif
}
void Context::sendDocumentToTop(Doc* document) void Context::sendDocumentToTop(Doc* document)
{ {
ASSERT(document != NULL); ASSERT(document != NULL);

View File

@ -30,6 +30,7 @@ namespace doc {
namespace app { namespace app {
class ActiveSiteHandler; class ActiveSiteHandler;
class Clipboard;
class Command; class Command;
class Doc; class Doc;
class DocRange; class DocRange;
@ -72,6 +73,7 @@ namespace app {
Docs& documents() { return m_docs; } Docs& documents() { return m_docs; }
Preferences& preferences() const; Preferences& preferences() const;
Clipboard* clipboard() const;
virtual bool isUIAvailable() const { return false; } virtual bool isUIAvailable() const { return false; }
virtual bool isRecordingMacro() const { return false; } virtual bool isRecordingMacro() const { return false; }

View File

@ -1407,10 +1407,11 @@ bool ColorBar::onCanCopy(Context* ctx)
bool ColorBar::onCanPaste(Context* ctx) bool ColorBar::onCanPaste(Context* ctx)
{ {
auto format = ctx->clipboard()->format();
if (m_tilemapMode == TilemapMode::Tiles) if (m_tilemapMode == TilemapMode::Tiles)
return (clipboard::get_current_format() == clipboard::ClipboardTiles); return (format == ClipboardFormat::Tileset);
else else
return (clipboard::get_current_format() == clipboard::ClipboardPaletteEntries); return (format == ClipboardFormat::PaletteEntries);
} }
bool ColorBar::onCanClear(Context* ctx) bool ColorBar::onCanClear(Context* ctx)

View File

@ -499,7 +499,7 @@ bool DocView::onCanCopy(Context* ctx)
bool DocView::onCanPaste(Context* ctx) bool DocView::onCanPaste(Context* ctx)
{ {
return return
(clipboard::get_current_format() == clipboard::ClipboardImage (ctx->clipboard()->format() == ClipboardFormat::Image
&& ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable | && ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::ActiveLayerIsVisible | ContextFlags::ActiveLayerIsVisible |
ContextFlags::ActiveLayerIsEditable | ContextFlags::ActiveLayerIsEditable |
@ -526,7 +526,7 @@ bool DocView::onCanClear(Context* ctx)
bool DocView::onCut(Context* ctx) bool DocView::onCut(Context* ctx)
{ {
ContextWriter writer(ctx); ContextWriter writer(ctx);
clipboard::cut(writer); ctx->clipboard()->cut(writer);
return true; return true;
} }
@ -536,7 +536,7 @@ bool DocView::onCopy(Context* ctx)
if (reader.site()->document() && if (reader.site()->document() &&
static_cast<const Doc*>(reader.site()->document())->isMaskVisible() && static_cast<const Doc*>(reader.site()->document())->isMaskVisible() &&
reader.site()->image()) { reader.site()->image()) {
clipboard::copy(reader); ctx->clipboard()->copy(reader);
return true; return true;
} }
else else
@ -545,8 +545,9 @@ bool DocView::onCopy(Context* ctx)
bool DocView::onPaste(Context* ctx) bool DocView::onPaste(Context* ctx)
{ {
if (clipboard::get_current_format() == clipboard::ClipboardImage) { auto clipboard = ctx->clipboard();
clipboard::paste(ctx, true); if (clipboard->format() == ClipboardFormat::Image) {
clipboard->paste(ctx, true);
return true; return true;
} }
else else
@ -587,7 +588,7 @@ bool DocView::onClear(Context* ctx)
(visibleMask && (visibleMask &&
!Preferences::instance().selection.keepSelectionAfterClear()); !Preferences::instance().selection.keepSelectionAfterClear());
clipboard::clear_mask_from_cels( ctx->clipboard()->clearMaskFromCels(
tx, document, cels, tx, document, cels,
deselectMask); deselectMask);

View File

@ -625,9 +625,10 @@ void MovingPixelsState::onBeforeCommandExecution(CommandExecutionEvent& ev)
std::unique_ptr<Mask> floatingMask; std::unique_ptr<Mask> floatingMask;
m_pixelsMovement->getDraggedImageCopy(floatingImage, floatingMask); m_pixelsMovement->getDraggedImageCopy(floatingImage, floatingMask);
clipboard::copy_image(floatingImage.get(), Clipboard::instance()->
floatingMask.get(), copyImage(floatingImage.get(),
document->sprite()->palette(m_editor->frame())); floatingMask.get(),
document->sprite()->palette(m_editor->frame()));
} }
// Clear floating pixels on Cut/Clear. // Clear floating pixels on Cut/Clear.

View File

@ -534,7 +534,7 @@ void PaletteView::cutToClipboard()
if (!m_selectedEntries.picks()) if (!m_selectedEntries.picks())
return; return;
clipboard::copy_palette(currentPalette(), m_selectedEntries); Clipboard::instance()->copyPalette(currentPalette(), m_selectedEntries);
clearSelection(); clearSelection();
} }
@ -544,7 +544,7 @@ void PaletteView::copyToClipboard()
if (!m_selectedEntries.picks()) if (!m_selectedEntries.picks())
return; return;
clipboard::copy_palette(currentPalette(), m_selectedEntries); Clipboard::instance()->copyPalette(currentPalette(), m_selectedEntries);
startMarchingAnts(); startMarchingAnts();
invalidate(); invalidate();
@ -552,11 +552,12 @@ void PaletteView::copyToClipboard()
void PaletteView::pasteFromClipboard() void PaletteView::pasteFromClipboard()
{ {
if (clipboard::get_current_format() == clipboard::ClipboardPaletteEntries) { auto clipboard = Clipboard::instance();
if (clipboard->format() == ClipboardFormat::PaletteEntries) {
if (m_delegate) if (m_delegate)
m_delegate->onPaletteViewPasteColors( m_delegate->onPaletteViewPasteColors(
clipboard::get_palette(), clipboard->getPalette(),
clipboard::get_palette_picks(), clipboard->getPalettePicks(),
m_selectedEntries); m_selectedEntries);
// We just hide the marching ants, the user can paste multiple // We just hide the marching ants, the user can paste multiple
@ -886,9 +887,10 @@ void PaletteView::onPaint(ui::PaintEvent& ev)
// Draw marching ants // Draw marching ants
if ((m_state == State::WAITING) && if ((m_state == State::WAITING) &&
(isMarchingAntsRunning()) && (isMarchingAntsRunning()) &&
(clipboard::get_current_format() == clipboard::ClipboardPaletteEntries)) { (Clipboard::instance()->format() == ClipboardFormat::PaletteEntries)) {
Palette* clipboardPalette = clipboard::get_palette(); auto clipboard = Clipboard::instance();
const PalettePicks& clipboardPicks = clipboard::get_palette_picks(); Palette* clipboardPalette = clipboard->getPalette();
const PalettePicks& clipboardPicks = clipboard->getPalettePicks();
if (clipboardPalette && if (clipboardPalette &&
clipboardPalette->countDiff(palette, nullptr, nullptr) == 0) { clipboardPalette->countDiff(palette, nullptr, nullptr) == 0) {

View File

@ -608,7 +608,7 @@ bool Timeline::onProcessMessage(Message* msg)
if (static_cast<TimerMessage*>(msg)->timer() == &m_clipboard_timer) { if (static_cast<TimerMessage*>(msg)->timer() == &m_clipboard_timer) {
Doc* clipboard_document; Doc* clipboard_document;
DocRange clipboard_range; DocRange clipboard_range;
clipboard::get_document_range_info( Clipboard::instance()->getDocumentRangeInfo(
&clipboard_document, &clipboard_document,
&clipboard_range); &clipboard_range);
@ -1944,7 +1944,7 @@ void Timeline::drawClipboardRange(ui::Graphics* g)
{ {
Doc* clipboard_document; Doc* clipboard_document;
DocRange clipboard_range; DocRange clipboard_range;
clipboard::get_document_range_info( Clipboard::instance()->getDocumentRangeInfo(
&clipboard_document, &clipboard_document,
&clipboard_range); &clipboard_range);
@ -3865,14 +3865,15 @@ void Timeline::clearClipboardRange()
{ {
Doc* clipboard_document; Doc* clipboard_document;
DocRange clipboard_range; DocRange clipboard_range;
clipboard::get_document_range_info( auto clipboard = Clipboard::instance();
clipboard->getDocumentRangeInfo(
&clipboard_document, &clipboard_document,
&clipboard_range); &clipboard_range);
if (!m_document || clipboard_document != m_document) if (!m_document || clipboard_document != m_document)
return; return;
clipboard::clear_content(); clipboard->clearContent();
m_clipboard_timer.stop(); m_clipboard_timer.stop();
} }
@ -4012,7 +4013,7 @@ bool Timeline::onCanCopy(Context* ctx)
bool Timeline::onCanPaste(Context* ctx) bool Timeline::onCanPaste(Context* ctx)
{ {
return return
(clipboard::get_current_format() == clipboard::ClipboardDocRange && (ctx->clipboard()->format() == ClipboardFormat::DocRange &&
ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable)); ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable));
} }
@ -4031,7 +4032,7 @@ bool Timeline::onCopy(Context* ctx)
if (m_range.enabled()) { if (m_range.enabled()) {
const ContextReader reader(ctx); const ContextReader reader(ctx);
if (reader.document()) { if (reader.document()) {
clipboard::copy_range(reader, m_range); ctx->clipboard()->copyRange(reader, m_range);
return true; return true;
} }
} }
@ -4040,8 +4041,9 @@ bool Timeline::onCopy(Context* ctx)
bool Timeline::onPaste(Context* ctx) bool Timeline::onPaste(Context* ctx)
{ {
if (clipboard::get_current_format() == clipboard::ClipboardDocRange) { auto clipboard = ctx->clipboard();
clipboard::paste(ctx, true); if (clipboard->format() == ClipboardFormat::DocRange) {
clipboard->paste(ctx, true);
return true; return true;
} }
else else

View File

@ -32,7 +32,6 @@
#include "app/ui_context.h" #include "app/ui_context.h"
#include "app/util/cel_ops.h" #include "app/util/cel_ops.h"
#include "app/util/clipboard.h" #include "app/util/clipboard.h"
#include "app/util/clipboard_native.h"
#include "app/util/new_image_from_mask.h" #include "app/util/new_image_from_mask.h"
#include "app/util/range_utils.h" #include "app/util/range_utils.h"
#include "clip/clip.h" #include "clip/clip.h"
@ -46,6 +45,8 @@
namespace app { namespace app {
using namespace doc;
namespace { namespace {
class ClipboardRange : public DocsObserver { class ClipboardRange : public DocsObserver {
@ -65,7 +66,7 @@ namespace {
UIContext::instance()->documents().remove_observer(this); UIContext::instance()->documents().remove_observer(this);
} }
bool valid() { bool valid() const {
return (m_doc != nullptr); return (m_doc != nullptr);
} }
@ -94,84 +95,127 @@ namespace {
} }
namespace clipboard { // Data in the clipboard
struct Clipboard::Data {
// Text used when the native clipboard is disabled
std::string text;
using namespace doc; // RGB/Grayscale/Indexed image
ImageRef image;
static std::shared_ptr<Palette> clipboard_palette; // The palette of the image (or tileset) if it's indexed
static std::shared_ptr<Tileset> clipboard_tiles; std::shared_ptr<Palette> palette;
static PalettePicks clipboard_picks;
static ImageRef clipboard_image;
static std::shared_ptr<Mask> clipboard_mask;
static ClipboardRange clipboard_range;
static ClipboardManager* g_instance = nullptr; // 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;
// Selected entries copied from the palette or the tileset
PalettePicks picks;
// 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;
}
};
static bool use_native_clipboard() static bool use_native_clipboard()
{ {
return Preferences::instance().experimental.useNativeClipboard(); return Preferences::instance().experimental.useNativeClipboard();
} }
ClipboardManager* ClipboardManager::instance() static Clipboard* g_instance = nullptr;
Clipboard* Clipboard::instance()
{ {
return g_instance; return g_instance;
} }
ClipboardManager::ClipboardManager() Clipboard::Clipboard()
: m_data(new Data)
{ {
ASSERT(!g_instance); ASSERT(!g_instance);
g_instance = this; g_instance = this;
register_native_clipboard_formats(); registerNativeFormats();
clipboard_range.observeUIContext();
} }
ClipboardManager::~ClipboardManager() Clipboard::~Clipboard()
{ {
clipboard_range.invalidate();
clipboard_range.unobserveUIContext();
// Clean the whole clipboard
clipboard_palette.reset();
clipboard_image.reset();
clipboard_mask.reset();
ASSERT(g_instance == this); ASSERT(g_instance == this);
g_instance = nullptr; g_instance = nullptr;
} }
void ClipboardManager::setClipboardText(const std::string& text) void Clipboard::setClipboardText(const std::string& text)
{ {
if (use_native_clipboard()) { if (use_native_clipboard()) {
clip::set_text(text); clip::set_text(text);
} }
else { else {
m_text = text; m_data->text = text;
} }
} }
bool ClipboardManager::getClipboardText(std::string& text) bool Clipboard::getClipboardText(std::string& text)
{ {
if (use_native_clipboard()) { if (use_native_clipboard()) {
return clip::get_text(text); return clip::get_text(text);
} }
else { else {
text = m_text; text = m_data->text;
return true; return true;
} }
} }
static void set_clipboard_image(Image* image, void Clipboard::setData(Image* image,
Mask* mask, Mask* mask,
Palette* palette, Palette* palette,
bool set_system_clipboard, bool set_system_clipboard,
bool image_source_is_transparent) bool image_source_is_transparent)
{ {
clipboard_palette.reset(palette); m_data->clear();
clipboard_picks.clear(); m_data->palette.reset(palette);
clipboard_image.reset(image); m_data->image.reset(image);
clipboard_mask.reset(mask); m_data->mask.reset(mask);
// Copy image to the native clipboard // Copy image to the native clipboard
if (set_system_clipboard) { if (set_system_clipboard) {
@ -183,16 +227,14 @@ static void set_clipboard_image(Image* image,
} }
if (use_native_clipboard()) if (use_native_clipboard())
set_native_clipboard_bitmap(image, mask, palette); setNativeBitmap(image, mask, palette);
if (image && !image_source_is_transparent) if (image && !image_source_is_transparent)
image->setMaskColor(oldMask); image->setMaskColor(oldMask);
} }
clipboard_range.invalidate();
} }
static bool copy_from_document(const Site& site, bool merged = false) bool Clipboard::copyFromDocument(const Site& site, bool merged)
{ {
const Doc* document = static_cast<const Doc*>(site.document()); const Doc* document = static_cast<const Doc*>(site.document());
ASSERT(document); ASSERT(document);
@ -205,7 +247,7 @@ static bool copy_from_document(const Site& site, bool merged = false)
return false; return false;
const Palette* pal = document->sprite()->palette(site.frame()); const Palette* pal = document->sprite()->palette(site.frame());
set_clipboard_image( setData(
image, image,
(mask ? new Mask(*mask): nullptr), (mask ? new Mask(*mask): nullptr),
(pal ? new Palette(*pal): nullptr), (pal ? new Palette(*pal): nullptr),
@ -215,39 +257,30 @@ static bool copy_from_document(const Site& site, bool merged = false)
return true; return true;
} }
ClipboardFormat get_current_format() ClipboardFormat Clipboard::format() const
{ {
// Check if the native clipboard has an image // Check if the native clipboard has an image
if (use_native_clipboard() && if (use_native_clipboard() && hasNativeBitmap())
has_native_clipboard_bitmap()) return ClipboardFormat::Image;
return ClipboardImage;
else if (clipboard_image)
return ClipboardImage;
else if (clipboard_range.valid())
return ClipboardDocRange;
else if (clipboard_palette && clipboard_picks.picks())
return ClipboardPaletteEntries;
else if (clipboard_tiles && clipboard_picks.picks())
return ClipboardTiles;
else else
return ClipboardNone; return m_data->format();
} }
void get_document_range_info(Doc** document, DocRange* range) void Clipboard::getDocumentRangeInfo(Doc** document, DocRange* range)
{ {
if (clipboard_range.valid()) { if (m_data->range.valid()) {
*document = clipboard_range.document(); *document = m_data->range.document();
*range = clipboard_range.range(); *range = m_data->range.range();
} }
else { else {
*document = NULL; *document = NULL;
} }
} }
void clear_mask_from_cels(Tx& tx, void Clipboard::clearMaskFromCels(Tx& tx,
Doc* doc, Doc* doc,
const CelList& cels, const CelList& cels,
const bool deselectMask) const bool deselectMask)
{ {
for (Cel* cel : cels) { for (Cel* cel : cels) {
ObjectId celId = cel->id(); ObjectId celId = cel->id();
@ -272,18 +305,18 @@ void clear_mask_from_cels(Tx& tx,
tx(new cmd::DeselectMask(doc)); tx(new cmd::DeselectMask(doc));
} }
void clear_content() void Clipboard::clearContent()
{ {
set_clipboard_image(nullptr, nullptr, nullptr, true, false); m_data->clear();
} }
void cut(ContextWriter& writer) void Clipboard::cut(ContextWriter& writer)
{ {
ASSERT(writer.document() != NULL); ASSERT(writer.document() != NULL);
ASSERT(writer.sprite() != NULL); ASSERT(writer.sprite() != NULL);
ASSERT(writer.layer() != NULL); ASSERT(writer.layer() != NULL);
if (!copy_from_document(*writer.site())) { if (!copyFromDocument(*writer.site())) {
Console console; Console console;
console.printf("Can't copying an image portion from the current layer\n"); console.printf("Can't copying an image portion from the current layer\n");
} }
@ -299,10 +332,10 @@ void cut(ContextWriter& writer)
else if (site.cel()) { else if (site.cel()) {
cels.push_back(site.cel()); cels.push_back(site.cel());
} }
clear_mask_from_cels(tx, clearMaskFromCels(tx,
writer.document(), writer.document(),
cels, cels,
true); // Deselect mask true); // Deselect mask
tx.commit(); tx.commit();
} }
writer.document()->generateMaskBoundaries(); writer.document()->generateMaskBoundaries();
@ -310,60 +343,60 @@ void cut(ContextWriter& writer)
} }
} }
void copy(const ContextReader& reader) void Clipboard::copy(const ContextReader& reader)
{ {
ASSERT(reader.document() != NULL); ASSERT(reader.document() != NULL);
if (!copy_from_document(*reader.site())) { if (!copyFromDocument(*reader.site())) {
Console console; Console console;
console.printf("Can't copying an image portion from the current layer\n"); console.printf("Can't copying an image portion from the current layer\n");
return; return;
} }
} }
void copy_merged(const ContextReader& reader) void Clipboard::copyMerged(const ContextReader& reader)
{ {
ASSERT(reader.document() != NULL); ASSERT(reader.document() != NULL);
copy_from_document(*reader.site(), true); copyFromDocument(*reader.site(), true);
} }
void copy_range(const ContextReader& reader, const DocRange& range) void Clipboard::copyRange(const ContextReader& reader, const DocRange& range)
{ {
ASSERT(reader.document() != NULL); ASSERT(reader.document() != NULL);
ContextWriter writer(reader); ContextWriter writer(reader);
clear_content(); clearContent();
clipboard_range.setRange(writer.document(), range); m_data->range.setRange(writer.document(), range);
// TODO Replace this with a signal, because here the timeline // TODO Replace this with a signal, because here the timeline
// depends on the clipboard and the clipboard on the timeline. // depends on the clipboard and the clipboard on the timeline.
App::instance()->timeline()->activateClipboardRange(); App::instance()->timeline()->activateClipboardRange();
} }
void copy_image(const Image* image, const Mask* mask, const Palette* pal) void Clipboard::copyImage(const Image* image, const Mask* mask, const Palette* pal)
{ {
set_clipboard_image( setData(
Image::createCopy(image), Image::createCopy(image),
(mask ? new Mask(*mask): nullptr), (mask ? new Mask(*mask): nullptr),
(pal ? new Palette(*pal): nullptr), (pal ? new Palette(*pal): nullptr),
true, false); true, false);
} }
void copy_palette(const Palette* palette, const doc::PalettePicks& picks) void Clipboard::copyPalette(const Palette* palette, const doc::PalettePicks& picks)
{ {
if (!picks.picks()) if (!picks.picks())
return; // Do nothing case return; // Do nothing case
set_clipboard_image(nullptr, setData(nullptr,
nullptr, nullptr,
new Palette(*palette), new Palette(*palette),
true, false); true, false);
clipboard_picks = picks; m_data->picks = picks;
} }
void paste(Context* ctx, const bool interactive) void Clipboard::paste(Context* ctx, const bool interactive)
{ {
Site site = ctx->activeSite(); Site site = ctx->activeSite();
Doc* dstDoc = site.document(); Doc* dstDoc = site.document();
@ -374,34 +407,34 @@ void paste(Context* ctx, const bool interactive)
if (!dstSpr) if (!dstSpr)
return; return;
switch (get_current_format()) { switch (format()) {
case clipboard::ClipboardImage: { case ClipboardFormat::Image: {
// Get the image from the native clipboard. // Get the image from the native clipboard.
if (!get_image(nullptr)) if (!getImage(nullptr))
return; return;
ASSERT(clipboard_image); ASSERT(m_data->image);
Palette* dst_palette = dstSpr->palette(site.frame()); Palette* dst_palette = dstSpr->palette(site.frame());
// Source image (clipboard or a converted copy to the destination 'imgtype') // Source image (clipboard or a converted copy to the destination 'imgtype')
ImageRef src_image; ImageRef src_image;
if (clipboard_image->pixelFormat() == dstSpr->pixelFormat() && if (m_data->image->pixelFormat() == dstSpr->pixelFormat() &&
// Indexed images can be copied directly only if both images // Indexed images can be copied directly only if both images
// have the same palette. // have the same palette.
(clipboard_image->pixelFormat() != IMAGE_INDEXED || (m_data->image->pixelFormat() != IMAGE_INDEXED ||
clipboard_palette->countDiff(dst_palette, NULL, NULL) == 0)) { m_data->palette->countDiff(dst_palette, NULL, NULL) == 0)) {
src_image = clipboard_image; src_image = m_data->image;
} }
else { else {
RgbMap* dst_rgbmap = dstSpr->rgbMap(site.frame()); RgbMap* dst_rgbmap = dstSpr->rgbMap(site.frame());
src_image.reset( src_image.reset(
render::convert_pixel_format( render::convert_pixel_format(
clipboard_image.get(), NULL, dstSpr->pixelFormat(), m_data->image.get(), NULL, dstSpr->pixelFormat(),
render::Dithering(), render::Dithering(),
dst_rgbmap, clipboard_palette.get(), dst_rgbmap, m_data->palette.get(),
false, false,
0)); 0));
} }
@ -414,7 +447,7 @@ void paste(Context* ctx, const bool interactive)
// Change to MovingPixelsState // Change to MovingPixelsState
current_editor->pasteImage(src_image.get(), current_editor->pasteImage(src_image.get(),
clipboard_mask.get()); m_data->mask.get());
} }
else { else {
// Non-interactive version (just copy the image to the cel) // Non-interactive version (just copy the image to the cel)
@ -431,7 +464,7 @@ void paste(Context* ctx, const bool interactive)
// Adjust bounds // Adjust bounds
if (dstCel) { if (dstCel) {
if (clipboard_mask) { if (m_data->mask) {
if (dstLayer->isReference()) { if (dstLayer->isReference()) {
dstCel->setBounds(dstSpr->bounds()); dstCel->setBounds(dstSpr->bounds());
@ -439,8 +472,8 @@ void paste(Context* ctx, const bool interactive)
tx(new cmd::SetMask(dstDoc, &emptyMask)); tx(new cmd::SetMask(dstDoc, &emptyMask));
} }
else { else {
dstCel->setBounds(clipboard_mask->bounds()); dstCel->setBounds(m_data->mask->bounds());
tx(new cmd::SetMask(dstDoc, clipboard_mask.get())); tx(new cmd::SetMask(dstDoc, m_data->mask.get()));
} }
} }
} }
@ -450,9 +483,9 @@ void paste(Context* ctx, const bool interactive)
break; break;
} }
case clipboard::ClipboardDocRange: { case ClipboardFormat::DocRange: {
DocRange srcRange = clipboard_range.range(); DocRange srcRange = m_data->range.range();
Doc* srcDoc = clipboard_range.document(); Doc* srcDoc = m_data->range.document();
Sprite* srcSpr = srcDoc->sprite(); Sprite* srcSpr = srcDoc->sprite();
switch (srcRange.type()) { switch (srcRange.type()) {
@ -643,53 +676,54 @@ void paste(Context* ctx, const bool interactive)
} }
} }
ImageRef get_image(Palette* palette) ImageRef Clipboard::getImage(Palette* palette)
{ {
// Get the image from the native clipboard. // Get the image from the native clipboard.
if (use_native_clipboard()) { if (use_native_clipboard()) {
Image* native_image = nullptr; Image* native_image = nullptr;
Mask* native_mask = nullptr; Mask* native_mask = nullptr;
Palette* native_palette = nullptr; Palette* native_palette = nullptr;
get_native_clipboard_bitmap(&native_image, &native_mask, &native_palette); getNativeBitmap(&native_image,
&native_mask,
&native_palette);
if (native_image) if (native_image)
set_clipboard_image(native_image, native_mask, native_palette, setData(native_image,
false, false); native_mask,
native_palette,
false, false);
} }
if (clipboard_palette && palette) if (m_data->palette && palette)
clipboard_palette->copyColorsTo(palette); m_data->palette->copyColorsTo(palette);
return clipboard_image; return m_data->image;
} }
bool get_image_size(gfx::Size& size) bool Clipboard::getImageSize(gfx::Size& size)
{ {
if (use_native_clipboard() && if (use_native_clipboard() && getNativeBitmapSize(&size))
get_native_clipboard_bitmap_size(&size))
return true; return true;
if (clipboard_image) { if (m_data->image) {
size.w = clipboard_image->width(); size.w = m_data->image->width();
size.h = clipboard_image->height(); size.h = m_data->image->height();
return true; return true;
} }
return false; return false;
} }
Palette* get_palette() Palette* Clipboard::getPalette()
{ {
if (clipboard::get_current_format() == ClipboardPaletteEntries) { if (format() == ClipboardFormat::PaletteEntries) {
ASSERT(clipboard_palette); ASSERT(m_data->palette);
return clipboard_palette.get(); return m_data->palette.get();
} }
else else
return nullptr; return nullptr;
} }
const PalettePicks& get_palette_picks() const PalettePicks& Clipboard::getPalettePicks()
{ {
return clipboard_picks; return m_data->picks;
} }
} // namespace clipboard
} // namespace app } // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -10,11 +10,14 @@
#pragma once #pragma once
#include "doc/cel_list.h" #include "doc/cel_list.h"
#include "doc/image_ref.h"
#include "gfx/point.h" #include "gfx/point.h"
#include "gfx/size.h" #include "gfx/size.h"
#include "ui/base.h" #include "ui/base.h"
#include "ui/clipboard_delegate.h" #include "ui/clipboard_delegate.h"
#include <memory>
namespace doc { namespace doc {
class Image; class Image;
class Mask; class Mask;
@ -28,62 +31,82 @@ namespace app {
class ContextWriter; class ContextWriter;
class Doc; class Doc;
class DocRange; class DocRange;
class Site;
class Tx; class Tx;
namespace clipboard { enum class ClipboardFormat {
using namespace doc; None,
Image,
DocRange,
PaletteEntries,
Tilemap,
Tileset,
};
enum ClipboardFormat { class Clipboard : public ui::ClipboardDelegate {
ClipboardNone, public:
ClipboardImage, static Clipboard* instance();
ClipboardDocRange,
ClipboardPaletteEntries,
ClipboardTiles,
};
// TODO Horrible API: refactor it (maybe a merge with os::clipboard). Clipboard();
~Clipboard();
class ClipboardManager : public ui::ClipboardDelegate { ClipboardFormat format() const;
public: void getDocumentRangeInfo(Doc** document, DocRange* range);
static ClipboardManager* instance();
ClipboardManager(); void clearMaskFromCels(Tx& tx,
~ClipboardManager(); Doc* doc,
const doc::CelList& cels,
const bool deselectMask);
void setClipboardText(const std::string& text) override; void clearContent();
bool getClipboardText(std::string& text) override;
private:
std::string m_text; // Text used when the native clipboard is disabled
};
ClipboardFormat get_current_format();
void get_document_range_info(Doc** document, DocRange* range);
void clear_mask_from_cels(Tx& tx,
Doc* doc,
const doc::CelList& cels,
const bool deselectMask);
void clear_content();
void cut(ContextWriter& context); void cut(ContextWriter& context);
void copy(const ContextReader& context); void copy(const ContextReader& context);
void copy_merged(const ContextReader& context); void copyMerged(const ContextReader& context);
void copy_range(const ContextReader& context, const DocRange& range); void copyRange(const ContextReader& context, const DocRange& range);
void copy_image(const Image* image, const Mask* mask, const Palette* palette); void copyImage(const doc::Image* image,
void copy_palette(const Palette* palette, const PalettePicks& picks); const doc::Mask* mask,
const doc::Palette* palette);
void copyPalette(const doc::Palette* palette,
const doc::PalettePicks& picks);
void paste(Context* ctx, const bool interactive); void paste(Context* ctx, const bool interactive);
ImageRef get_image(Palette* palette); doc::ImageRef getImage(doc::Palette* palette);
// Returns true and fills the specified "size"" with the image's // Returns true and fills the specified "size"" with the image's
// size in the clipboard, or return false in case that the clipboard // size in the clipboard, or return false in case that the clipboard
// doesn't contain an image at all. // doesn't contain an image at all.
bool get_image_size(gfx::Size& size); bool getImageSize(gfx::Size& size);
Palette* get_palette(); doc::Palette* getPalette();
const PalettePicks& get_palette_picks(); const doc::PalettePicks& getPalettePicks();
// ui::ClipboardDelegate impl
void setClipboardText(const std::string& text) override;
bool getClipboardText(std::string& text) override;
private:
void setData(doc::Image* image,
doc::Mask* mask,
doc::Palette* palette,
bool set_system_clipboard,
bool image_source_is_transparent);
bool copyFromDocument(const Site& site, bool merged = false);
// Native clipboard
void registerNativeFormats();
bool hasNativeBitmap() const;
bool setNativeBitmap(const doc::Image* image,
const doc::Mask* mask,
const doc::Palette* palette);
bool getNativeBitmap(doc::Image** image,
doc::Mask** mask,
doc::Palette** palette);
bool getNativeBitmapSize(gfx::Size* size);
struct Data;
std::unique_ptr<Data> m_data;
};
} // namespace clipboard
} // namespace app } // namespace app
#endif #endif

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2016-2018 David Capello // Copyright (C) 2016-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -8,7 +9,7 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/util/clipboard_native.h" #include "app/util/clipboard.h"
#include "app/i18n/strings.h" #include "app/i18n/strings.h"
#include "base/serialization.h" #include "base/serialization.h"
@ -28,7 +29,6 @@
#include <vector> #include <vector>
namespace app { namespace app {
namespace clipboard {
using namespace base::serialization; using namespace base::serialization;
using namespace base::serialization::little_endian; using namespace base::serialization::little_endian;
@ -53,20 +53,20 @@ namespace {
} }
void register_native_clipboard_formats() void Clipboard::registerNativeFormats()
{ {
clip::set_error_handler(custom_error_handler); clip::set_error_handler(custom_error_handler);
custom_image_format = clip::register_format("org.aseprite.Image"); custom_image_format = clip::register_format("org.aseprite.Image");
} }
bool has_native_clipboard_bitmap() bool Clipboard::hasNativeBitmap() const
{ {
return clip::has(clip::image_format()); return clip::has(clip::image_format());
} }
bool set_native_clipboard_bitmap(const doc::Image* image, bool Clipboard::setNativeBitmap(const doc::Image* image,
const doc::Mask* mask, const doc::Mask* mask,
const doc::Palette* palette) const doc::Palette* palette)
{ {
clip::lock l(native_display_handle()); clip::lock l(native_display_handle());
if (!l.locked()) if (!l.locked())
@ -163,9 +163,9 @@ bool set_native_clipboard_bitmap(const doc::Image* image,
return true; return true;
} }
bool get_native_clipboard_bitmap(doc::Image** image, bool Clipboard::getNativeBitmap(doc::Image** image,
doc::Mask** mask, doc::Mask** mask,
doc::Palette** palette) doc::Palette** palette)
{ {
*image = nullptr; *image = nullptr;
*mask = nullptr; *mask = nullptr;
@ -286,7 +286,7 @@ bool get_native_clipboard_bitmap(doc::Image** image,
return true; return true;
} }
bool get_native_clipboard_bitmap_size(gfx::Size* size) bool Clipboard::getNativeBitmapSize(gfx::Size* size)
{ {
clip::image_spec spec; clip::image_spec spec;
if (clip::get_image_spec(spec)) { if (clip::get_image_spec(spec)) {
@ -298,5 +298,4 @@ bool get_native_clipboard_bitmap_size(gfx::Size* size)
return false; return false;
} }
} // namespace clipboard
} // namespace app } // namespace app

View File

@ -1,35 +0,0 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_UTIL_CLIPBOARD_NATIVE_H_INCLUDED
#define APP_UTIL_CLIPBOARD_NATIVE_H_INCLUDED
#pragma once
#include "gfx/fwd.h"
namespace doc {
class Image;
class Mask;
class Palette;
}
namespace app {
namespace clipboard {
void register_native_clipboard_formats();
bool has_native_clipboard_bitmap();
bool set_native_clipboard_bitmap(const doc::Image* image,
const doc::Mask* mask,
const doc::Palette* palette);
bool get_native_clipboard_bitmap(doc::Image** image,
doc::Mask** mask,
doc::Palette** palette);
bool get_native_clipboard_bitmap_size(gfx::Size* size);
} // namespace clipboard
} // namespace app
#endif