diff --git a/data/gui.xml b/data/gui.xml
index 9aa0d5aa8..4415f50eb 100644
--- a/data/gui.xml
+++ b/data/gui.xml
@@ -92,13 +92,13 @@
-
+
-
+
-
+
@@ -496,6 +496,9 @@
+
+
+
@@ -816,10 +819,10 @@
-
-
+
-
-
+
@@ -1005,10 +1008,10 @@
-
-
+
-
-
+
diff --git a/data/strings/en.ini b/data/strings/en.ini
index f61a769a7..e56e52da7 100644
--- a/data/strings/en.ini
+++ b/data/strings/en.ini
@@ -337,8 +337,9 @@ NewFile = New File
NewFile_FromClipboard = New File from Clipboard
NewFrame = New Frame
NewFrame_NewEmptyFrame = New Empty Frame
-NewFrame_DuplicateCels = Duplicate Linked Cels
-NewFrame_DuplicateCelsBlock = Duplicate Cels
+NewFrame_DuplicateCels = Duplicate Cels w/Layer Mode
+NewFrame_DuplicateCelsCopies = Duplicate Cels
+NewFrame_DuplicateCelsLinked = Duplicate Linked Cels
NewFrameTag = New Frame Tag
NewLayer = New {}
NewLayer_BeforeActiveLayer = New {} Below
diff --git a/src/app/cmd/copy_frame.cpp b/src/app/cmd/copy_frame.cpp
index e7e97ca09..94ff4efa8 100644
--- a/src/app/cmd/copy_frame.cpp
+++ b/src/app/cmd/copy_frame.cpp
@@ -1,4 +1,5 @@
// Aseprite
+// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
@@ -37,16 +38,7 @@ void CopyFrame::onExecute()
executeAndAdd(new cmd::AddFrame(sprite, m_newFrame));
executeAndAdd(new cmd::SetFrameDuration(sprite, m_newFrame, msecs));
- if (fromFrame >= m_newFrame)
- ++fromFrame;
-
- for (Layer* layer : sprite->allLayers()) {
- if (layer->isImage()) {
- executeAndAdd(new cmd::CopyCel(
- static_cast(layer), fromFrame,
- static_cast(layer), m_newFrame, layer->isContinuous()));
- }
- }
+ // Do not copy cels (cmd::CopyCel must be called from outside)
}
} // namespace cmd
diff --git a/src/app/commands/cmd_new_frame.cpp b/src/app/commands/cmd_new_frame.cpp
index 36a9db769..a19f60d6b 100644
--- a/src/app/commands/cmd_new_frame.cpp
+++ b/src/app/commands/cmd_new_frame.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018 Igara Studio S.A.
+// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -42,7 +42,8 @@ public:
DUPLICATE_FRAME,
NEW_EMPTY_FRAME,
DUPLICATE_CELS,
- DUPLICATE_CELS_BLOCK,
+ DUPLICATE_CELS_COPIES,
+ DUPLICATE_CELS_LINKED,
};
NewFrameCommand();
@@ -74,8 +75,11 @@ void NewFrameCommand::onLoadParams(const Params& params)
m_content = Content::NEW_EMPTY_FRAME;
else if (content == "cel")
m_content = Content::DUPLICATE_CELS;
- else if (content == "celblock")
- m_content = Content::DUPLICATE_CELS_BLOCK;
+ else if (content == "celblock" ||
+ content == "celcopies")
+ m_content = Content::DUPLICATE_CELS_COPIES;
+ else if (content == "cellinked")
+ m_content = Content::DUPLICATE_CELS_LINKED;
}
bool NewFrameCommand::onEnabled(Context* context)
@@ -104,13 +108,12 @@ void NewFrameCommand::onExecute(Context* context)
break;
case Content::DUPLICATE_CELS:
- case Content::DUPLICATE_CELS_BLOCK: {
+ case Content::DUPLICATE_CELS_LINKED:
+ case Content::DUPLICATE_CELS_COPIES: {
const Site* site = writer.site();
if (site->inTimeline() &&
!site->selectedLayers().empty() &&
!site->selectedFrames().empty()) {
- std::map relatedCels;
-
#if ENABLE_UI
auto timeline = App::instance()->timeline();
timeline->prepareToMoveRange();
@@ -129,36 +132,19 @@ void NewFrameCommand::onExecute(Context* context)
(site->selectedFrames().lastFrame() -
site->selectedFrames().firstFrame() + 1);
+ std::unique_ptr continuous = nullptr;
+ switch (m_content) {
+ case Content::DUPLICATE_CELS_COPIES: continuous.reset(new bool(false)); break;
+ case Content::DUPLICATE_CELS_LINKED: continuous.reset(new bool(true)); break;
+ }
+
for (Layer* layer : selLayers) {
if (layer->isImage()) {
for (frame_t srcFrame : site->selectedFrames().reversed()) {
frame_t dstFrame = srcFrame+frameRange;
- bool continuous;
- CelData* srcCelData = nullptr;
-
- if (m_content == Content::DUPLICATE_CELS_BLOCK) {
- continuous = false;
-
- Cel* srcCel = static_cast(layer)->cel(srcFrame);
- if (srcCel) {
- srcCelData = srcCel->data();
-
- auto it = relatedCels.find(srcCelData);
- if (it != relatedCels.end()) {
- srcFrame = it->second->frame();
- continuous = true;
- }
- }
- }
- else
- continuous = layer->isContinuous();
-
api.copyCel(
static_cast(layer), srcFrame,
- static_cast(layer), dstFrame, continuous);
-
- if (srcCelData && !relatedCels[srcCelData])
- relatedCels[srcCelData] = layer->cel(dstFrame);
+ static_cast(layer), dstFrame, continuous.get());
}
}
}
@@ -168,10 +154,10 @@ void NewFrameCommand::onExecute(Context* context)
timeline->moveRange(range);
#endif
}
- else {
+ else if (auto layer = static_cast(writer.layer())) {
api.copyCel(
- static_cast(writer.layer()), writer.frame(),
- static_cast(writer.layer()), writer.frame()+1);
+ layer, writer.frame(),
+ layer, writer.frame()+1);
#ifdef ENABLE_UI // TODO the active frame should be part of the Site
// TODO should we use DocObserver?
@@ -216,8 +202,11 @@ std::string NewFrameCommand::onGetFriendlyName() const
case Content::DUPLICATE_CELS:
text = Strings::commands_NewFrame_DuplicateCels();
break;
- case Content::DUPLICATE_CELS_BLOCK:
- text = Strings::commands_NewFrame_DuplicateCelsBlock();
+ case Content::DUPLICATE_CELS_COPIES:
+ text = Strings::commands_NewFrame_DuplicateCelsCopies();
+ break;
+ case Content::DUPLICATE_CELS_LINKED:
+ text = Strings::commands_NewFrame_DuplicateCelsLinked();
break;
}
diff --git a/src/app/doc_api.cpp b/src/app/doc_api.cpp
index 318f8302f..b4eb56602 100644
--- a/src/app/doc_api.cpp
+++ b/src/app/doc_api.cpp
@@ -337,19 +337,33 @@ void DocApi::addEmptyFramesTo(Sprite* sprite, frame_t newFrame)
}
void DocApi::copyFrame(Sprite* sprite,
- const frame_t fromFrame,
- const frame_t newFrame,
- const DropFramePlace dropFramePlace,
- const TagsHandling tagsHandling)
+ frame_t fromFrame,
+ const frame_t newFrame0,
+ const DropFramePlace dropFramePlace,
+ const TagsHandling tagsHandling)
{
ASSERT(sprite);
+
+ frame_t newFrame =
+ (dropFramePlace == kDropBeforeFrame ? newFrame0:
+ newFrame0+1);
+
m_transaction.execute(
new cmd::CopyFrame(
- sprite, fromFrame,
- (dropFramePlace == kDropBeforeFrame ? newFrame:
- newFrame+1)));
+ sprite, fromFrame, newFrame));
- adjustFrameTags(sprite, newFrame, +1,
+ if (fromFrame >= newFrame)
+ ++fromFrame;
+
+ for (Layer* layer : sprite->allLayers()) {
+ if (layer->isImage()) {
+ copyCel(
+ static_cast(layer), fromFrame,
+ static_cast(layer), newFrame);
+ }
+ }
+
+ adjustFrameTags(sprite, newFrame0, +1,
dropFramePlace,
tagsHandling);
}
@@ -553,26 +567,41 @@ void DocApi::moveCel(
void DocApi::copyCel(
LayerImage* srcLayer, frame_t srcFrame,
- LayerImage* dstLayer, frame_t dstFrame)
-{
- copyCel(
- srcLayer, srcFrame,
- dstLayer, dstFrame, dstLayer->isContinuous());
-}
-
-void DocApi::copyCel(
- LayerImage* srcLayer, frame_t srcFrame,
- LayerImage* dstLayer, frame_t dstFrame, bool continuous)
+ LayerImage* dstLayer, frame_t dstFrame,
+ const bool* forceContinuous)
{
ASSERT(srcLayer != dstLayer || srcFrame != dstFrame);
if (srcLayer == dstLayer && srcFrame == dstFrame)
return; // Nothing to be done
+ Cel* srcCel = srcLayer->cel(srcFrame);
+ if (srcCel) {
+ auto it = m_linkedCels.find(srcCel->data());
+ if (it != m_linkedCels.end()) {
+ Cel* dstRelated = it->second;
+ if (dstRelated && dstRelated->layer() == dstLayer) {
+ // Create a link
+ m_transaction.execute(
+ new cmd::CopyCel(
+ dstRelated->layer(), dstRelated->frame(),
+ dstLayer, dstFrame, true));
+ return;
+ }
+ }
+ }
+
m_transaction.execute(
new cmd::CopyCel(
srcLayer, srcFrame,
- dstLayer, dstFrame, continuous));
+ dstLayer, dstFrame,
+ (forceContinuous ? *forceContinuous:
+ dstLayer->isContinuous())));
+
+ if (srcCel && srcCel->links()) {
+ if (Cel* dstCel = dstLayer->cel(dstFrame))
+ m_linkedCels[srcCel->data()] = dstCel;
+ }
}
void DocApi::swapCel(
diff --git a/src/app/doc_api.h b/src/app/doc_api.h
index e7d0203b3..e526595f3 100644
--- a/src/app/doc_api.h
+++ b/src/app/doc_api.h
@@ -17,8 +17,11 @@
#include "doc/image_ref.h"
#include "gfx/rect.h"
+#include