diff --git a/src/app/commands/cmd_cel_properties.cpp b/src/app/commands/cmd_cel_properties.cpp index 533228810..7b9273497 100644 --- a/src/app/commands/cmd_cel_properties.cpp +++ b/src/app/commands/cmd_cel_properties.cpp @@ -108,9 +108,8 @@ private: else if (m_range.enabled()) { Sprite* sprite = m_document->sprite(); int count = 0; - for (Cel* cel : sprite->uniqueCels(m_range.frameBegin(), - m_range.frameEnd())) { - if (m_range.inRange(sprite->layerToIndex(cel->layer()))) { + for (Cel* cel : sprite->uniqueCels(m_range.selectedFrames())) { + if (m_range.contains(cel->layer())) { if (backgroundCount && cel->layer()->isBackground()) ++(*backgroundCount); ++count; @@ -186,9 +185,8 @@ private: } else if (m_range.enabled()) { Sprite* sprite = m_document->sprite(); - for (Cel* cel : sprite->uniqueCels(m_range.frameBegin(), - m_range.frameEnd())) { - if (m_range.inRange(sprite->layerToIndex(cel->layer()))) { + for (Cel* cel : sprite->uniqueCels(m_range.selectedFrames())) { + if (m_range.contains(cel->layer())) { if (!cel->layer()->isBackground() && newOpacity != cel->opacity()) { transaction.execute(new cmd::SetCelOpacity(cel, newOpacity)); } diff --git a/src/app/commands/cmd_export_sprite_sheet.cpp b/src/app/commands/cmd_export_sprite_sheet.cpp index 99699e72c..6a200ac6a 100644 --- a/src/app/commands/cmd_export_sprite_sheet.cpp +++ b/src/app/commands/cmd_export_sprite_sheet.cpp @@ -164,7 +164,7 @@ namespace { // TODO the range of selected frames should be in doc::Site. auto range = App::instance()->timeline()->range(); if (range.enabled()) { - return range.frameBegin(); + return range.firstFrame(); } else if (current_editor) { return current_editor->frame(); @@ -176,7 +176,7 @@ namespace { static frame_t To() { auto range = App::instance()->timeline()->range(); if (range.enabled()) { - return range.frameEnd(); + return range.lastFrame(); } else if (current_editor) { return current_editor->frame(); @@ -218,9 +218,10 @@ namespace { if (!range.enabled()) { if (current_editor) { ASSERT(current_editor->sprite() == sprite); - range.startRange(sprite->layerToIndex(current_editor->layer()), + range.clearRange(); + range.startRange(current_editor->layer(), current_editor->frame(), DocumentRange::kCels); - range.endRange(sprite->layerToIndex(current_editor->layer()), + range.endRange(current_editor->layer(), current_editor->frame()); } else @@ -230,7 +231,7 @@ namespace { LayerList layers = sprite->allLayers(); for (int i=0; iisVisible()) { m_restore.push_back(std::make_pair(layer, layer->isVisible())); diff --git a/src/app/commands/cmd_new_frame_tag.cpp b/src/app/commands/cmd_new_frame_tag.cpp index a7b5af3ee..386be462d 100644 --- a/src/app/commands/cmd_new_frame_tag.cpp +++ b/src/app/commands/cmd_new_frame_tag.cpp @@ -59,8 +59,8 @@ void NewFrameTagCommand::onExecute(Context* context) if (range.enabled() && (range.type() == DocumentRange::kFrames || range.type() == DocumentRange::kCels)) { - from = range.frameBegin(); - to = range.frameEnd(); + from = range.selectedFrames().firstFrame(); + to = range.selectedFrames().lastFrame(); } base::UniquePtr frameTag(new FrameTag(from, to)); diff --git a/src/app/commands/cmd_set_loop_section.cpp b/src/app/commands/cmd_set_loop_section.cpp index 9d20f81b1..f3266bec5 100644 --- a/src/app/commands/cmd_set_loop_section.cpp +++ b/src/app/commands/cmd_set_loop_section.cpp @@ -85,8 +85,8 @@ void SetLoopSectionCommand::onExecute(Context* ctx) case Action::Auto: { auto range = App::instance()->timeline()->range(); if (range.enabled() && (range.frames() > 1)) { - begin = range.frameBegin(); - end = range.frameEnd(); + begin = range.selectedFrames().firstFrame(); + end = range.selectedFrames().lastFrame(); on = true; } else { diff --git a/src/app/document_api.cpp b/src/app/document_api.cpp index 776749925..c7121fa88 100644 --- a/src/app/document_api.cpp +++ b/src/app/document_api.cpp @@ -427,7 +427,11 @@ void DocumentApi::restackLayerAfter(Layer* layer, Layer* afterThis) if (layer == afterThis) return; - m_transaction.execute(new cmd::MoveLayer(layer, layer->parent(), afterThis)); + m_transaction.execute( + new cmd::MoveLayer( + layer, + (afterThis ? afterThis->parent(): layer->parent()), + afterThis)); } void DocumentApi::restackLayerBefore(Layer* layer, Layer* beforeThis) @@ -436,13 +440,23 @@ void DocumentApi::restackLayerBefore(Layer* layer, Layer* beforeThis) return; Layer* afterThis; - if (beforeThis) afterThis = beforeThis->getPrevious(); else afterThis = layer->sprite()->root()->lastLayer(); - restackLayerAfter(layer, afterThis); + // The following code is similar to DocumentApi::restackLayerAfter() + // but we use the parent from "beforeThis" (as afterThis might be + // nullptr now). + + if (layer == afterThis) + return; + + m_transaction.execute( + new cmd::MoveLayer( + layer, + (beforeThis ? beforeThis->parent(): layer->parent()), + afterThis)); } void DocumentApi::backgroundFromLayer(Layer* layer) @@ -460,9 +474,16 @@ void DocumentApi::flattenLayers(Sprite* sprite) m_transaction.execute(new cmd::FlattenLayers(sprite)); } -void DocumentApi::duplicateLayerAfter(Layer* sourceLayer, Layer* afterLayer) +Layer* DocumentApi::duplicateLayerAfter(Layer* sourceLayer, Layer* afterLayer) { - base::UniquePtr newLayerPtr(new LayerImage(sourceLayer->sprite())); + base::UniquePtr newLayerPtr; + + if (sourceLayer->isImage()) + newLayerPtr.reset(new LayerImage(sourceLayer->sprite())); + else if (sourceLayer->isGroup()) + newLayerPtr.reset(new LayerGroup(sourceLayer->sprite())); + else + throw std::runtime_error("Invalid layer type"); m_document->copyLayerContent(sourceLayer, m_document, newLayerPtr); @@ -471,14 +492,14 @@ void DocumentApi::duplicateLayerAfter(Layer* sourceLayer, Layer* afterLayer) addLayer(sourceLayer->parent(), newLayerPtr, afterLayer); // Release the pointer as it is owned by the sprite now. - newLayerPtr.release(); + return newLayerPtr.release(); } -void DocumentApi::duplicateLayerBefore(Layer* sourceLayer, Layer* beforeLayer) +Layer* DocumentApi::duplicateLayerBefore(Layer* sourceLayer, Layer* beforeLayer) { Layer* afterThis = (beforeLayer ? beforeLayer->getPreviousInWholeHierarchy(): nullptr); - duplicateLayerAfter(sourceLayer, afterThis); + return duplicateLayerAfter(sourceLayer, afterThis); } Cel* DocumentApi::addCel(LayerImage* layer, frame_t frameNumber, const ImageRef& image) diff --git a/src/app/document_api.h b/src/app/document_api.h index 8bfb999ac..9b53a97f2 100644 --- a/src/app/document_api.h +++ b/src/app/document_api.h @@ -89,8 +89,8 @@ namespace app { void backgroundFromLayer(Layer* layer); void layerFromBackground(Layer* layer); void flattenLayers(Sprite* sprite); - void duplicateLayerAfter(Layer* sourceLayer, Layer* afterLayer); - void duplicateLayerBefore(Layer* sourceLayer, Layer* beforeLayer); + Layer* duplicateLayerAfter(Layer* sourceLayer, Layer* afterLayer); + Layer* duplicateLayerBefore(Layer* sourceLayer, Layer* beforeLayer); // Images API void replaceImage(Sprite* sprite, const ImageRef& oldImage, const ImageRef& newImage); diff --git a/src/app/document_range.cpp b/src/app/document_range.cpp index 59ab7fba1..4ccfbf3bf 100644 --- a/src/app/document_range.cpp +++ b/src/app/document_range.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -22,103 +22,139 @@ using namespace doc; DocumentRange::DocumentRange() : m_type(kNone) - , m_layerBegin(0) - , m_layerEnd(-1) - , m_frameBegin(0) - , m_frameEnd(-1) + , m_selectingFromLayer(nullptr) + , m_selectingFromFrame(-1) { } DocumentRange::DocumentRange(Cel* cel) : m_type(kCels) - , m_layerBegin(cel->sprite()->layerToIndex(cel->layer())) - , m_layerEnd(m_layerBegin) - , m_frameBegin(cel->frame()) - , m_frameEnd(m_frameBegin) + , m_selectingFromLayer(nullptr) + , m_selectingFromFrame(-1) { + m_selectedLayers.insert(cel->layer()); + m_selectedFrames.insert(cel->frame()); } -void DocumentRange::startRange(LayerIndex layer, frame_t frame, Type type) -{ - m_type = type; - m_layerBegin = m_layerEnd = layer; - m_frameBegin = m_frameEnd = frame; -} - -void DocumentRange::endRange(LayerIndex layer, frame_t frame) -{ - ASSERT(enabled()); - m_layerEnd = layer; - m_frameEnd = frame; -} - -void DocumentRange::disableRange() +void DocumentRange::clearRange() { m_type = kNone; + m_selectedLayers.clear(); + m_selectedFrames.clear(); } -bool DocumentRange::inRange(LayerIndex layer) const +void DocumentRange::startRange(Layer* fromLayer, frame_t fromFrame, Type type) +{ + m_type = type; + m_selectingFromLayer = fromLayer; + m_selectingFromFrame = fromFrame; + + if (fromLayer) + m_selectedLayers.insert(fromLayer); + if (fromFrame >= 0) + m_selectedFrames.insert(fromFrame); +} + +void DocumentRange::endRange(Layer* toLayer, frame_t toFrame) +{ + ASSERT(enabled()); + + if (m_selectingFromLayer && toLayer) + selectLayerRange(m_selectingFromLayer, toLayer); + + if (m_selectingFromFrame >= 0) + selectFrameRange(m_selectingFromFrame, toFrame); +} + +bool DocumentRange::contains(Layer* layer) const { if (enabled()) - return (layer >= layerBegin() && layer <= layerEnd()); + return m_selectedLayers.contains(layer); else return false; } -bool DocumentRange::inRange(frame_t frame) const +void DocumentRange::displace(layer_t layerDelta, frame_t frameDelta) { - if (enabled()) - return (frame >= frameBegin() && frame <= frameEnd()); - else - return false; + m_selectedLayers.displace(layerDelta); + m_selectedFrames.displace(frameDelta); } -bool DocumentRange::inRange(LayerIndex layer, frame_t frame) const -{ - return inRange(layer) && inRange(frame); -} - -void DocumentRange::setLayers(int layers) -{ - if (m_layerBegin <= m_layerEnd) m_layerEnd = m_layerBegin + LayerIndex(layers - 1); - else m_layerBegin = m_layerEnd + LayerIndex(layers - 1); -} - -void DocumentRange::setFrames(frame_t frames) -{ - if (m_frameBegin <= m_frameEnd) - m_frameEnd = (m_frameBegin + frames) - 1; - else - m_frameBegin = (m_frameEnd + frames) - 1; -} - -void DocumentRange::displace(int layerDelta, int frameDelta) -{ - m_layerBegin += LayerIndex(layerDelta); - m_layerEnd += LayerIndex(layerDelta); - m_frameBegin += frame_t(frameDelta); - m_frameEnd += frame_t(frameDelta); -} - -bool DocumentRange::convertToCels(Sprite* sprite) +bool DocumentRange::convertToCels(const Sprite* sprite) { switch (m_type) { case DocumentRange::kNone: return false; case DocumentRange::kCels: break; - case DocumentRange::kFrames: - m_layerBegin = sprite->firstLayer(); - m_layerEnd = sprite->lastLayer(); - m_type = DocumentRange::kCels; + case DocumentRange::kFrames: { + LayerList layers = sprite->allBrowsableLayers(); + ASSERT(layers.empty()); + if (!layers.empty()) { + selectLayerRange(layers.front(), layers.back()); + m_type = DocumentRange::kCels; + } + else + return false; break; + } case DocumentRange::kLayers: - m_frameBegin = frame_t(0); - m_frameEnd = sprite->lastFrame(); + selectFrameRange(0, sprite->lastFrame()); m_type = DocumentRange::kCels; break; } return true; } +void DocumentRange::selectLayerRange(Layer* fromLayer, Layer* toLayer) +{ + ASSERT(fromLayer); + ASSERT(toLayer); + + bool goNext = false; + bool goPrev = false; + Layer* it; + + if (fromLayer != toLayer) { + it = m_selectingFromLayer; + while (it) { + if (it == toLayer) { + goNext = true; + break; + } + it = it->getNextInWholeHierarchy(); + } + + if (!goNext) { + it = m_selectingFromLayer; + while (it) { + if (it == toLayer) { + goPrev = true; + break; + } + it = it->getPreviousInWholeHierarchy(); + } + } + } + + it = m_selectingFromLayer; + do { + m_selectedLayers.insert(it); + if (it == toLayer) + break; + + if (goNext) + it = it->getNextInWholeHierarchy(); + else if (goPrev) + it = it->getPreviousInWholeHierarchy(); + else + break; + } while (it); +} + +void DocumentRange::selectFrameRange(frame_t fromFrame, frame_t toFrame) +{ + m_selectedFrames.insert(fromFrame, toFrame); +} + } // namespace app diff --git a/src/app/document_range.h b/src/app/document_range.h index 989f8b2f2..66b4e94f8 100644 --- a/src/app/document_range.h +++ b/src/app/document_range.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -10,9 +10,8 @@ #pragma once #include "doc/frame.h" -#include "doc/layer_index.h" - -#include +#include "doc/selected_frames.h" +#include "doc/selected_layers.h" namespace doc { class Cel; @@ -31,39 +30,47 @@ namespace app { Type type() const { return m_type; } bool enabled() const { return m_type != kNone; } - LayerIndex layerBegin() const { return std::min(m_layerBegin, m_layerEnd); } - LayerIndex layerEnd() const { return std::max(m_layerBegin, m_layerEnd); } - frame_t frameBegin() const { return std::min(m_frameBegin, m_frameEnd); } - frame_t frameEnd() const { return std::max(m_frameBegin, m_frameEnd); } + layer_t layers() const { return int(m_selectedLayers.size()); } + frame_t frames() const { return int(m_selectedFrames.size()); } + const SelectedLayers& selectedLayers() const { return m_selectedLayers; } + const SelectedFrames& selectedFrames() const { return m_selectedFrames; } - int layers() const { return layerEnd() - layerBegin() + 1; } - frame_t frames() const { return frameEnd() - frameBegin() + 1; } - void setLayers(int layers); - void setFrames(frame_t frames); - void displace(int layerDelta, int frameDelta); + void displace(layer_t layerDelta, frame_t frameDelta); - bool inRange(LayerIndex layer) const; - bool inRange(frame_t frame) const; - bool inRange(LayerIndex layer, frame_t frame) const; + bool contains(Layer* layer) const; - void startRange(LayerIndex layer, frame_t frame, Type type); - void endRange(LayerIndex layer, frame_t frame); - void disableRange(); - - bool operator==(const DocumentRange& o) const { - return m_type == o.m_type && - layerBegin() == o.layerBegin() && layerEnd() == o.layerEnd() && - frameBegin() == o.frameBegin() && frameEnd() == o.frameEnd(); + bool contains(frame_t frame) const { + return m_selectedFrames.contains(frame); } - bool convertToCels(Sprite* sprite); + bool contains(Layer* layer, frame_t frame) const { + return contains(layer) && contains(frame); + } + + void clearRange(); + void startRange(Layer* fromLayer, frame_t fromFrame, Type type); + void endRange(Layer* toLayer, frame_t toFrame); + + frame_t firstFrame() const { return m_selectedFrames.firstFrame(); } + frame_t lastFrame() const { return m_selectedFrames.lastFrame(); } + + bool operator==(const DocumentRange& o) const { + return (m_type == o.m_type && + m_selectedLayers == o.m_selectedLayers && + m_selectedFrames == o.m_selectedFrames); + } + + bool convertToCels(const Sprite* sprite); private: + void selectLayerRange(Layer* fromLayer, Layer* toLayer); + void selectFrameRange(frame_t fromFrame, frame_t toFrame); + Type m_type; - LayerIndex m_layerBegin; - LayerIndex m_layerEnd; - frame_t m_frameBegin; - frame_t m_frameEnd; + SelectedLayers m_selectedLayers; + SelectedFrames m_selectedFrames; + Layer* m_selectingFromLayer; + frame_t m_selectingFromFrame; }; } // namespace app diff --git a/src/app/document_range_ops.cpp b/src/app/document_range_ops.cpp index e741aefc0..1c9fec472 100644 --- a/src/app/document_range_ops.cpp +++ b/src/app/document_range_ops.cpp @@ -5,13 +5,15 @@ // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. +// Uncomment this in case you want to debug range ops +//#define TRACE_RANGE_OPS + #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "app/document_range_ops.h" -#include "app/app.h" // TODO remove this dependency #include "app/context_access.h" #include "app/document_api.h" #include "app/document_range.h" @@ -21,10 +23,150 @@ #include +#ifdef TRACE_RANGE_OPS +#include +#endif + namespace app { enum Op { Move, Copy }; +template +static void move_or_copy_cels( + DocumentApi& api, Op op, + LayerList& srcLayers, + LayerList& dstLayers, + T& srcFrames, + T& dstFrames) +{ + ASSERT(srcLayers.size() == dstLayers.size()); + + for (layer_t i=0; i= 0 && i < srcLayers.size() && srcLayers[i]->isImage()) { + LayerImage* srcLayer = static_cast(srcLayers[i]); + + if (i < dstLayers.size() && dstLayers[i]->isImage()) { + LayerImage* srcLayer = static_cast(srcLayers[i]); + LayerImage* dstLayer = static_cast(dstLayers[i]); + +#ifdef TRACE_RANGE_OPS + std::clog << (op == Move ? "Moving": "Copying") + << " cel " << srcLayer->name() << "[" << *srcFrame << "]" + << " into " << dstLayer->name() << "[" << *dstFrame << "]\n"; +#endif + + switch (op) { + case Move: api.moveCel(srcLayer, *srcFrame, dstLayer, *dstFrame); break; + case Copy: api.copyCel(srcLayer, *srcFrame, dstLayer, *dstFrame); break; + } + } + else if (op == Move) { + api.clearCel(srcLayer, *srcFrame); + } + } + } + } +} + +template +static DocumentRange move_or_copy_frames( + DocumentApi& api, Op op, + Sprite* sprite, + T& srcFrames, + frame_t dstFrame) +{ +#ifdef TRACE_RANGE_OPS + std::clog << "move_or_copy_frames frames["; + for (auto srcFrame : srcFrames) { + std::clog << srcFrame << ", "; + } + std::clog << "] => " << dstFrame << "\n"; +#endif + + auto srcFrame = srcFrames.begin(); + auto srcFrameEnd = srcFrames.end(); + frame_t srcDelta = 0; + frame_t firstCopiedBlock = 0; + + for (; srcFrame != srcFrameEnd; ++srcFrame) { + frame_t fromFrame = (*srcFrame)+srcDelta; + + switch (op) { + + case Move: + if ((*srcFrame) >= dstFrame) { + srcDelta = 0; + fromFrame = *srcFrame; + } + break; + + case Copy: + if (fromFrame >= dstFrame-1 && firstCopiedBlock) { + srcDelta += firstCopiedBlock; + fromFrame += firstCopiedBlock; + firstCopiedBlock = 0; + } + break; + } + +#ifdef TRACE_RANGE_OPS + std::clog << " ["; + for (frame_t i=0; i<=sprite->lastFrame(); ++i) { + std::clog << (sprite->frameDuration(i)-1); + } + std::clog << "] => " + << (op == Move ? "Move": "Copy") + << " " << (*srcFrame) << "+" << (srcDelta) << " -> " << dstFrame << " => "; +#endif + + switch (op) { + + case Move: + api.moveFrame(sprite, fromFrame, dstFrame); + + if (fromFrame < dstFrame-1) { + --srcDelta; + } + else if (fromFrame > dstFrame-1) { + ++dstFrame; + } + break; + + case Copy: + api.copyFrame(sprite, fromFrame, dstFrame); + + if (fromFrame < dstFrame-1) { + ++firstCopiedBlock; + } + else if (fromFrame >= dstFrame-1) { + ++srcDelta; + } + ++dstFrame; + break; + } + +#ifdef TRACE_RANGE_OPS + std::clog << " ["; + for (frame_t i=0; i<=sprite->lastFrame(); ++i) { + std::clog << (sprite->frameDuration(i)-1); + } + std::clog << "]\n"; +#endif + } + + DocumentRange result; + result.startRange(nullptr, dstFrame-srcFrames.size(), DocumentRange::kFrames); + result.endRange(nullptr, dstFrame-1); + return result; +} + static DocumentRange drop_range_op( Document* doc, Op op, const DocumentRange& from, DocumentRangePlace place, const DocumentRange& to) @@ -40,36 +182,64 @@ static DocumentRange drop_range_op( // Check noop/trivial/do nothing cases, i.e., move a range to the same place. // Also check invalid cases, like moving a Background layer. switch (from.type()) { + case DocumentRange::kCels: if (from == to) return from; break; + case DocumentRange::kFrames: if (op == Move) { - if ((to.frameBegin() >= from.frameBegin() && to.frameEnd() <= from.frameEnd()) || - (place == kDocumentRangeBefore && to.frameBegin() == from.frameEnd()+1) || - (place == kDocumentRangeAfter && to.frameEnd() == from.frameBegin()-1)) + // Simple cases with one continuos range of frames that are a + // no-op. + if ((from.selectedFrames().ranges() == 1) && + ((to.firstFrame() >= from.firstFrame() && + to.lastFrame() <= from.lastFrame()) || + (place == kDocumentRangeBefore && to.firstFrame() == from.lastFrame()+1) || + (place == kDocumentRangeAfter && to.lastFrame() == from.firstFrame()-1))) return from; } break; + case DocumentRange::kLayers: if (op == Move) { - if ((to.layerBegin() >= from.layerBegin() && to.layerEnd() <= from.layerEnd()) || - (place == kDocumentRangeBefore && to.layerBegin() == from.layerEnd()+1) || - (place == kDocumentRangeAfter && to.layerEnd() == from.layerBegin()-1)) - return from; + SelectedLayers srcSelLayers = from.selectedLayers(); + SelectedLayers dstSelLayers = to.selectedLayers(); + LayerList srcLayers = srcSelLayers.toLayerList(); + LayerList dstLayers = dstSelLayers.toLayerList(); + if (srcLayers.empty() || dstLayers.empty()) + return from; + + // Check no-ops when we move layers at the same level (all + // layers with the same parent), all adjacents, and which are + // moved to the same place. + if (srcSelLayers.hasSameParent() && + dstSelLayers.hasSameParent() && + are_layers_adjacent(srcLayers) && + are_layers_adjacent(dstLayers)) { + for (Layer* srcLayer : srcLayers) + if (dstSelLayers.contains(srcLayer)) + return from; + + if ((place == kDocumentRangeBefore + && dstLayers.front() == srcLayers.back()->getNext()) || + (place == kDocumentRangeAfter + && dstLayers.back() == srcLayers.front()->getPrevious())) + return from; + } // We cannot move the background - for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) - if (sprite->indexToLayer(i)->isBackground()) + for (Layer* layer : srcSelLayers) + if (layer->isBackground()) throw std::runtime_error("The background layer cannot be moved"); } // Before background if (place == kDocumentRangeBefore) { - Layer* background = sprite->indexToLayer(to.layerBegin()); - if (background && background->isBackground()) - throw std::runtime_error("You cannot move or copy something below the background layer"); + for (Layer* background : to.selectedLayers()) { + if (background && background->isBackground()) + throw std::runtime_error("You cannot move or copy something below the background layer"); + } } break; } @@ -92,238 +262,121 @@ static DocumentRange drop_range_op( DocumentApi api = doc->getApi(transaction); // TODO Try to add the range with just one call to DocumentApi - // methods, to avoid generating a lot of SetCelFrame undoers (see - // DocumentApi::setCelFramePosition). + // methods, to avoid generating a lot of cmd::SetCelFrame (see + // DocumentApi::setCelFramePosition() function). switch (from.type()) { - case DocumentRange::kCels: - { - LayerList layers = sprite->allBrowsableLayers(); - int srcLayerBegin, srcLayerStep, srcLayerEnd; - int dstLayerBegin, dstLayerStep; - frame_t srcFrameBegin, srcFrameStep, srcFrameEnd; - frame_t dstFrameBegin, dstFrameStep; + case DocumentRange::kCels: { + LayerList allLayers = sprite->allBrowsableLayers(); + if (allLayers.empty()) + break; - if (to.layerBegin() <= from.layerBegin()) { - srcLayerBegin = from.layerBegin(); - srcLayerStep = 1; - srcLayerEnd = from.layerEnd()+1; - dstLayerBegin = to.layerBegin(); - dstLayerStep = 1; - } - else { - srcLayerBegin = from.layerEnd(); - srcLayerStep = -1; - srcLayerEnd = from.layerBegin()-1; - dstLayerBegin = to.layerEnd(); - dstLayerStep = -1; - } + LayerList srcLayers = from.selectedLayers().toLayerList(); + LayerList dstLayers = to.selectedLayers().toLayerList(); + if (srcLayers.empty()) + throw std::invalid_argument("You need to specify a non-empty cels range"); - if (to.frameBegin() <= from.frameBegin()) { - srcFrameBegin = from.frameBegin(); - srcFrameStep = frame_t(1); - srcFrameEnd = from.frameEnd()+1; - dstFrameBegin = to.frameBegin(); - dstFrameStep = frame_t(1); - } - else { - srcFrameBegin = from.frameEnd(); - srcFrameStep = frame_t(-1); - srcFrameEnd = from.frameBegin()-1; - dstFrameBegin = to.frameEnd(); - dstFrameStep = frame_t(-1); - } - - for (int srcLayerIdx = srcLayerBegin, - dstLayerIdx = dstLayerBegin; srcLayerIdx != srcLayerEnd; ) { - for (frame_t srcFrame = srcFrameBegin, - dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) { - if (dstLayerIdx < 0 || dstLayerIdx >= int(layers.size()) || - srcLayerIdx < 0 || srcLayerIdx >= int(layers.size())) - break; - - ASSERT(layers[srcLayerIdx]->isImage()); - ASSERT(layers[dstLayerIdx]->isImage()); - - LayerImage* srcLayer = static_cast(layers[srcLayerIdx]); - LayerImage* dstLayer = static_cast(layers[dstLayerIdx]); - - switch (op) { - case Move: api.moveCel(srcLayer, srcFrame, dstLayer, dstFrame); break; - case Copy: api.copyCel(srcLayer, srcFrame, dstLayer, dstFrame); break; - } - - srcFrame += srcFrameStep; - dstFrame += dstFrameStep; - } - srcLayerIdx += srcLayerStep; - dstLayerIdx += dstLayerStep; - } - - resultRange = to; + if (find_layer_index(allLayers, srcLayers.front()) < + find_layer_index(allLayers, dstLayers.front())) { + std::reverse(srcLayers.begin(), srcLayers.end()); + std::reverse(dstLayers.begin(), dstLayers.end()); } - break; - case DocumentRange::kFrames: - { - frame_t srcFrameBegin = 0, srcFrameStep, srcFrameEnd = 0; - frame_t dstFrameBegin = 0, dstFrameStep; + if (from.firstFrame() < to.firstFrame()) { + auto srcFrames = from.selectedFrames().reversed(); + auto dstFrames = to.selectedFrames().reversed(); - switch (op) { - - case Move: - if (place == kDocumentRangeBefore) { - if (to.frameBegin() <= from.frameBegin()) { - srcFrameBegin = from.frameBegin(); - srcFrameStep = frame_t(1); - srcFrameEnd = from.frameEnd()+1; - dstFrameBegin = to.frameBegin(); - dstFrameStep = frame_t(1); - } - else { - srcFrameBegin = from.frameEnd(); - srcFrameStep = frame_t(-1); - srcFrameEnd = from.frameBegin()-1; - dstFrameBegin = to.frameBegin(); - dstFrameStep = frame_t(-1); - } - } - else if (place == kDocumentRangeAfter) { - if (to.frameEnd() <= from.frameBegin()) { - srcFrameBegin = from.frameBegin(); - srcFrameStep = frame_t(1); - srcFrameEnd = from.frameEnd()+1; - dstFrameBegin = to.frameEnd()+1; - dstFrameStep = frame_t(1); - } - else { - srcFrameBegin = from.frameEnd(); - srcFrameStep = frame_t(-1); - srcFrameEnd = from.frameBegin()-1; - dstFrameBegin = to.frameEnd()+1; - dstFrameStep = frame_t(-1); - } - } - break; - - case Copy: - if (place == kDocumentRangeBefore) { - if (to.frameBegin() <= from.frameBegin()) { - srcFrameBegin = from.frameBegin(); - srcFrameStep = frame_t(2); - srcFrameEnd = from.frameBegin() + 2*from.frames(); - dstFrameBegin = to.frameBegin(); - dstFrameStep = frame_t(1); - } - else { - srcFrameBegin = from.frameEnd(); - srcFrameStep = frame_t(-1); - srcFrameEnd = from.frameBegin()-1; - dstFrameBegin = to.frameBegin(); - dstFrameStep = frame_t(0); - } - } - else if (place == kDocumentRangeAfter) { - if (to.frameEnd() <= from.frameBegin()) { - srcFrameBegin = from.frameBegin(); - srcFrameStep = frame_t(2); - srcFrameEnd = from.frameBegin() + 2*from.frames(); - dstFrameBegin = to.frameEnd()+1; - dstFrameStep = frame_t(1); - } - else { - srcFrameBegin = from.frameEnd(); - srcFrameStep = frame_t(-1); - srcFrameEnd = from.frameBegin()-1; - dstFrameBegin = to.frameEnd()+1; - dstFrameStep = frame_t(0); - } - } - break; - } - - for (frame_t srcFrame = srcFrameBegin, - dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) { - switch (op) { - case Move: api.moveFrame(sprite, srcFrame, dstFrame); break; - case Copy: api.copyFrame(sprite, srcFrame, dstFrame); break; - } - srcFrame += srcFrameStep; - dstFrame += dstFrameStep; - } - - if (place == kDocumentRangeBefore) { - resultRange.startRange(LayerIndex::NoLayer, frame_t(to.frameBegin()), from.type()); - resultRange.endRange(LayerIndex::NoLayer, frame_t(to.frameBegin()+from.frames()-1)); - } - else if (place == kDocumentRangeAfter) { - resultRange.startRange(LayerIndex::NoLayer, frame_t(to.frameEnd()+1), from.type()); - resultRange.endRange(LayerIndex::NoLayer, frame_t(to.frameEnd()+1+from.frames()-1)); - } - - if (op == Move && from.frameBegin() < to.frameBegin()) - resultRange.displace(0, -from.frames()); + move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames); } + else { + const auto& srcFrames = from.selectedFrames(); + const auto& dstFrames = to.selectedFrames(); + + move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames); + } + + resultRange = to; break; + } - case DocumentRange::kLayers: - { - LayerList layers = sprite->allBrowsableLayers(); + case DocumentRange::kFrames: { + frame_t dstFrame; + if (place == kDocumentRangeBefore) + dstFrame = to.firstFrame(); + else + dstFrame = to.lastFrame()+1; - if (layers.empty()) + resultRange = + move_or_copy_frames(api, op, sprite, + from.selectedFrames(), dstFrame); + break; + } + + case DocumentRange::kLayers: { + LayerList allLayers = sprite->allBrowsableLayers(); + if (allLayers.empty()) + break; + + LayerList srcLayers = from.selectedLayers().toLayerList(); + LayerList dstLayers = to.selectedLayers().toLayerList(); + ASSERT(!srcLayers.empty()); + ASSERT(!dstLayers.empty()); + + switch (op) { + + case Move: + if (place == kDocumentRangeBefore) { + Layer* beforeThis = dstLayers.front(); + Layer* afterThis = nullptr; + + for (Layer* srcLayer : srcLayers) { + if (afterThis) + api.restackLayerAfter(srcLayer, afterThis); + else + api.restackLayerBefore(srcLayer, beforeThis); + + afterThis = srcLayer; + } + } + else if (place == kDocumentRangeAfter) { + Layer* afterThis = dstLayers.back(); + for (Layer* srcLayer : srcLayers) { + api.restackLayerAfter(srcLayer, afterThis); + afterThis = srcLayer; + } + } + + // Same set of layers than the "from" range + resultRange = from; break; - switch (op) { + case Copy: { + if (place == kDocumentRangeBefore) { + for (Layer* srcLayer : srcLayers) { + Layer* copiedLayer = + api.duplicateLayerBefore(srcLayer, dstLayers.front()); - case Move: - if (place == kDocumentRangeBefore) { - for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) { - api.restackLayerBefore( - layers[i], - layers[to.layerBegin()]); - } + resultRange.startRange(copiedLayer, -1, DocumentRange::kLayers); + resultRange.endRange(copiedLayer, -1); } - else if (place == kDocumentRangeAfter) { - for (LayerIndex i = from.layerEnd(); i >= from.layerBegin(); --i) { - api.restackLayerAfter( - layers[i], - layers[to.layerEnd()]); - } - } - break; + } + else if (place == kDocumentRangeAfter) { + std::reverse(srcLayers.begin(), srcLayers.end()); - case Copy: - if (place == kDocumentRangeBefore) { - for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) { - api.duplicateLayerBefore( - layers[i], - layers[to.layerBegin()]); - } + for (Layer* srcLayer : srcLayers) { + Layer* copiedLayer = + api.duplicateLayerAfter(srcLayer, dstLayers.back()); + + resultRange.startRange(copiedLayer, -1, DocumentRange::kLayers); + resultRange.endRange(copiedLayer, -1); } - else if (place == kDocumentRangeAfter) { - for (LayerIndex i = from.layerEnd(); i >= from.layerBegin(); --i) { - api.duplicateLayerAfter( - layers[i], - layers[to.layerEnd()]); - } - } - break; + } + break; } - - if (place == kDocumentRangeBefore) { - resultRange.startRange(LayerIndex(to.layerBegin()), frame_t(-1), from.type()); - resultRange.endRange(LayerIndex(to.layerBegin()+from.layers()-1), frame_t(-1)); - } - else if (place == kDocumentRangeAfter) { - resultRange.startRange(LayerIndex(to.layerEnd()+1), frame_t(-1), from.type()); - resultRange.endRange(LayerIndex(to.layerEnd()+1+from.layers()-1), frame_t(-1)); - } - - if (op == Move && from.layerBegin() < to.layerBegin()) - resultRange.displace(-from.layers(), 0); } break; + } } transaction.commit(); @@ -350,29 +403,28 @@ void reverse_frames(Document* doc, const DocumentRange& range) Transaction transaction(writer.context(), "Reverse Frames"); DocumentApi api = doc->getApi(transaction); Sprite* sprite = doc->sprite(); + LayerList layers; frame_t frameBegin, frameEnd; - int layerBegin, layerEnd; bool moveFrames = false; bool swapCels = false; switch (range.type()) { case DocumentRange::kCels: - frameBegin = range.frameBegin(); - frameEnd = range.frameEnd(); - layerBegin = range.layerBegin(); - layerEnd = range.layerEnd() + 1; + frameBegin = range.firstFrame(); + frameEnd = range.lastFrame(); + layers = range.selectedLayers().toLayerList(); swapCels = true; break; case DocumentRange::kFrames: - frameBegin = range.frameBegin(); - frameEnd = range.frameEnd(); + frameBegin = range.firstFrame(); + frameEnd = range.lastFrame(); + layers = sprite->allLayers(); moveFrames = true; break; case DocumentRange::kLayers: frameBegin = frame_t(0); frameEnd = sprite->totalFrames()-1; - layerBegin = range.layerBegin(); - layerEnd = range.layerEnd() + 1; + layers = range.selectedLayers().toLayerList(); swapCels = true; break; } @@ -385,10 +437,8 @@ void reverse_frames(Document* doc, const DocumentRange& range) } } else if (swapCels) { - LayerList layers = sprite->allBrowsableLayers(); - - for (int layerIdx = layerBegin; layerIdx != layerEnd; ++layerIdx) { - if (!layers[layerIdx]->isImage()) + for (Layer* layer : layers) { + if (!layer->isImage()) continue; for (frame_t frame = frameBegin, @@ -398,8 +448,8 @@ void reverse_frames(Document* doc, const DocumentRange& range) if (frame == frameRev) continue; - LayerImage* layer = static_cast(layers[layerIdx]); - api.swapCel(layer, frame, frameRev); + LayerImage* imageLayer = static_cast(layer); + api.swapCel(imageLayer, frame, frameRev); } } } diff --git a/src/app/document_range_tests.cpp b/src/app/document_range_tests.cpp index 49fa6affb..216b99b00 100644 --- a/src/app/document_range_tests.cpp +++ b/src/app/document_range_tests.cpp @@ -21,15 +21,59 @@ using namespace app; using namespace doc; -namespace app { +namespace std { - std::ostream& operator<<(std::ostream& os, const DocumentRange& range) { - return os << "{ layers: [" << range.layerBegin() << ", " << range.layerEnd() << "]" - << ", frames: [" << range.frameBegin() << ", " << range.frameEnd() << "] }"; +std::ostream& operator<<(std::ostream& os, const doc::Layer* layer) { + if (layer) + os << '"' << layer->name() << '"'; + else + os << "(null layer)"; + return os; +} + +std::ostream& operator<<(std::ostream& os, const app::DocumentRange& range) { + os << "{ layers: ["; + + bool first = true; + for (auto layer : range.selectedLayers()) { + if (first) + first = false; + else + os << ", "; + os << layer; } + os << "]"; + os << ", frames: ["; + + first = true; + for (auto frame : range.selectedFrames()) { + if (first) + first = false; + else + os << ", "; + os << frame; + } + + os << "] }"; + return os; } +std::ostream& operator<<(std::ostream& os, const doc::LayerList& layers) { + os << "["; + bool first = true; + for (auto layer : layers) { + if (first) + first = false; + else + os << ", "; + os << layer; + } + return os << "]"; +} + +} // namespace std + typedef base::UniquePtr DocumentPtr; #define EXPECT_LAYER_ORDER(a, b, c, d) \ @@ -44,6 +88,14 @@ typedef base::UniquePtr DocumentPtr; expect_frame(c, 2) && \ expect_frame(d, 3)); +#define EXPECT_FRAME_ORDER6(a, b, c, d, e, f) \ + EXPECT_TRUE(expect_frame(a, 0) && \ + expect_frame(b, 1) && \ + expect_frame(c, 2) && \ + expect_frame(d, 3) && \ + expect_frame(e, 4) && \ + expect_frame(f, 5)); + #define EXPECT_FRAME_COPY1(a, b, c, d, e) \ EXPECT_TRUE(expect_frame(a, 0)); \ EXPECT_TRUE(expect_frame(b, 1)); \ @@ -79,7 +131,7 @@ public: DocRangeOps() { expected_color = rgba(255, 255, 255, 255); - doc.reset(static_cast(ctx.documents().add(4, 4))); + doc.reset(static_cast(ctx.documents().add(6, 4))); sprite = doc->sprite(); layer1 = dynamic_cast(sprite->root()->firstLayer()); layer2 = new LayerImage(sprite); @@ -95,24 +147,26 @@ public: layer3->setName("layer3"); layer4->setName("layer4"); - sprite->setTotalFrames(frame_t(4)); + sprite->setTotalFrames(frame_t(6)); sprite->setFrameDuration(frame_t(0), 1); // These durations can be used to identify sprite->setFrameDuration(frame_t(1), 2); // frames after a move operation sprite->setFrameDuration(frame_t(2), 3); sprite->setFrameDuration(frame_t(3), 4); + sprite->setFrameDuration(frame_t(4), 5); + sprite->setFrameDuration(frame_t(5), 6); LayerList layers = sprite->allLayers(); - for (int i=0; i<4; i++) { + for (layer_t i=0; i(layers[i]); - for (int j=0; j<4; j++) { + for (frame_t j=0; jtotalFrames(); j++) { Cel* cel = layer->cel(frame_t(j)); ImageRef image; if (cel) image = cel->imageRef(); else { - image.reset(Image::create(IMAGE_RGB, 4, 4)); + image.reset(Image::create(IMAGE_RGB, 6, 4)); cel = new Cel(frame_t(j), image); layer->addCel(cel); } @@ -157,7 +211,7 @@ protected: LayerList layers = sprite->allLayers(); Layer* a = layers[expected_layer]; Layer* b = layers[layer]; - EXPECT_EQ(a->name(), b->name()); + EXPECT_EQ(a->name(), b->name()) << layers; if (a != b) return false; } @@ -191,6 +245,50 @@ protected: return (cel == NULL); } + DocumentRange range(Layer* fromLayer, frame_t fromFrNum, Layer* toLayer, frame_t toFrNum, DocumentRange::Type type) { + DocumentRange r; + r.startRange(fromLayer, fromFrNum, type); + r.endRange(toLayer, toFrNum); + return r; + } + + DocumentRange range(layer_t fromLayer, frame_t fromFrNum, + layer_t toLayer, frame_t toFrNum, DocumentRange::Type type) { + LayerList layers = sprite->allLayers(); + return range(layers[fromLayer], fromFrNum, layers[toLayer], toFrNum, type); + } + + DocumentRange layers_range(Layer* fromLayer, Layer* toLayer) { + return range(fromLayer, -1, toLayer, -1, DocumentRange::kLayers); + } + + DocumentRange layers_range(layer_t fromLayer, layer_t toLayer) { + LayerList layers = sprite->allLayers(); + return layers_range(layers[fromLayer], layers[toLayer]); + } + + DocumentRange layers_range(Layer* layer) { + return range(layer, -1, layer, -1, DocumentRange::kLayers); + } + + DocumentRange layers_range(layer_t layer) { + LayerList layers = sprite->allLayers(); + return layers_range(layers[layer]); + } + + DocumentRange frames_range(frame_t fromFrame, frame_t toFrame) { + return range(nullptr, fromFrame, nullptr, toFrame, DocumentRange::kFrames); + } + + DocumentRange frames_range(frame_t frame) { + return range(nullptr, frame, nullptr, frame, DocumentRange::kFrames); + } + + DocumentRange cels_range(layer_t fromLayer, frame_t fromFrNum, + layer_t toLayer, frame_t toFrNum) { + return range(fromLayer, fromFrNum, toLayer, toFrNum, DocumentRange::kCels); + } + TestContextT ctx; DocumentPtr doc; Sprite* sprite; @@ -201,155 +299,113 @@ protected: color_t expected_color; }; -inline DocumentRange range(Layer* fromLayer, frame_t fromFrNum, Layer* toLayer, frame_t toFrNum, DocumentRange::Type type) { - DocumentRange r; - r.startRange(fromLayer->sprite()->layerToIndex(fromLayer), fromFrNum, type); - r.endRange(toLayer->sprite()->layerToIndex(toLayer), toFrNum); - return r; -} - -inline DocumentRange range(int fromLayer, frame_t fromFrNum, int toLayer, frame_t toFrNum, DocumentRange::Type type) { - DocumentRange r; - r.startRange(LayerIndex(fromLayer), fromFrNum, type); - r.endRange(LayerIndex(toLayer), toFrNum); - return r; -} - -inline DocumentRange layers_range(Layer* fromLayer, Layer* toLayer) { - return range(fromLayer, -1, toLayer, -1, DocumentRange::kLayers); -} - -inline DocumentRange layers_range(int fromLayer, int toLayer) { - return range(fromLayer, -1, toLayer, -1, DocumentRange::kLayers); -} - -inline DocumentRange layers_range(Layer* layer) { - return range(layer, -1, layer, -1, DocumentRange::kLayers); -} - -inline DocumentRange layers_range(int layer) { - return range(layer, -1, layer, -1, DocumentRange::kLayers); -} - -inline DocumentRange frames_range(frame_t fromFrame, frame_t toFrame) { - return range(0, fromFrame, 0, toFrame, DocumentRange::kFrames); -} - -inline DocumentRange frames_range(frame_t frame) { - return range(0, frame, 0, frame, DocumentRange::kFrames); -} - -inline DocumentRange cels_range(int fromLayer, frame_t fromFrNum, int toLayer, frame_t toFrNum) { - return range(fromLayer, fromFrNum, toLayer, toFrNum, DocumentRange::kCels); -} - TEST_F(DocRangeOps, MoveLayersNoOp) { // Move one layer to the same place EXPECT_EQ(layers_range(layer1), - move_range(doc, - layers_range(layer1), - layers_range(layer1), kDocumentRangeAfter)); + move_range(doc, + layers_range(layer1), + layers_range(layer1), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer1), - move_range(doc, - layers_range(layer1), - layers_range(layer2), kDocumentRangeBefore)); + move_range(doc, + layers_range(layer1), + layers_range(layer2), kDocumentRangeBefore)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer4), - move_range(doc, - layers_range(layer4), - layers_range(layer4), kDocumentRangeAfter)); + move_range(doc, + layers_range(layer4), + layers_range(layer4), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer4), - move_range(doc, - layers_range(layer4), - layers_range(layer4), kDocumentRangeBefore)); + move_range(doc, + layers_range(layer4), + layers_range(layer4), kDocumentRangeBefore)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer4), - move_range(doc, - layers_range(layer4), - layers_range(layer3), kDocumentRangeAfter)); + move_range(doc, + layers_range(layer4), + layers_range(layer3), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); // Move two layer to the same place EXPECT_EQ(layers_range(layer1, layer2), - move_range(doc, - layers_range(layer1, layer2), - layers_range(layer1), kDocumentRangeBefore)); + move_range(doc, + layers_range(layer1, layer2), + layers_range(layer1), kDocumentRangeBefore)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer1, layer2), - move_range(doc, - layers_range(layer1, layer2), - layers_range(layer1), kDocumentRangeAfter)); + move_range(doc, + layers_range(layer1, layer2), + layers_range(layer1), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer1, layer2), - move_range(doc, - layers_range(layer1, layer2), - layers_range(layer2), kDocumentRangeBefore)); + move_range(doc, + layers_range(layer1, layer2), + layers_range(layer2), kDocumentRangeBefore)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer1, layer2), - move_range(doc, - layers_range(layer1, layer2), - layers_range(layer2), kDocumentRangeAfter)); + move_range(doc, + layers_range(layer1, layer2), + layers_range(layer2), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer1, layer2), - move_range(doc, - layers_range(layer1, layer2), - layers_range(layer3), kDocumentRangeBefore)); + move_range(doc, + layers_range(layer1, layer2), + layers_range(layer3), kDocumentRangeBefore)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer3, layer4), - move_range(doc, - layers_range(layer3, layer4), - layers_range(layer2), kDocumentRangeAfter)); + move_range(doc, + layers_range(layer3, layer4), + layers_range(layer2), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer3, layer4), - move_range(doc, - layers_range(layer3, layer4), - layers_range(layer3), kDocumentRangeBefore)); + move_range(doc, + layers_range(layer3, layer4), + layers_range(layer3), kDocumentRangeBefore)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer3, layer4), - move_range(doc, - layers_range(layer3, layer4), - layers_range(layer3), kDocumentRangeAfter)); + move_range(doc, + layers_range(layer3, layer4), + layers_range(layer3), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer3, layer4), - move_range(doc, - layers_range(layer3, layer4), - layers_range(layer4), kDocumentRangeBefore)); + move_range(doc, + layers_range(layer3, layer4), + layers_range(layer4), kDocumentRangeBefore)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(layers_range(layer3, layer4), - move_range(doc, - layers_range(layer3, layer4), - layers_range(layer4), kDocumentRangeAfter)); + move_range(doc, + layers_range(layer3, layer4), + layers_range(layer4), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); @@ -359,35 +415,35 @@ TEST_F(DocRangeOps, MoveLayersNoOp) { for (int i=0; i<2; ++i) { for (int layer=0; layer<4; ++layer) { EXPECT_EQ(layers_range(layer1, layer4), - move_range(doc, - layers_range(layer1, layer4), - layers_range(layer), places[i])); + move_range(doc, + layers_range(layer1, layer4), + layers_range(layer), places[i])); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); } for (int layer=0; layer<3; ++layer) { EXPECT_EQ(layers_range(layer1, layer4), - move_range(doc, - layers_range(layer1, layer4), - layers_range(layer, layer+1), places[i])); + move_range(doc, + layers_range(layer1, layer4), + layers_range(layer, layer+1), places[i])); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); } for (int layer=0; layer<2; ++layer) { EXPECT_EQ(layers_range(layer1, layer4), - move_range(doc, - layers_range(layer1, layer4), - layers_range(layer, layer+2), places[i])); + move_range(doc, + layers_range(layer1, layer4), + layers_range(layer, layer+2), places[i])); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); } EXPECT_EQ(layers_range(layer1, layer4), - move_range(doc, - layers_range(layer1, layer4), - layers_range(layer1, layer4), places[i])); + move_range(doc, + layers_range(layer1, layer4), + layers_range(layer1, layer4), places[i])); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); EXPECT_FALSE(doc->undoHistory()->canUndo()); } @@ -397,109 +453,109 @@ TEST_F(DocRangeOps, MoveFramesNoOp) { // Move one frame to the same place EXPECT_EQ(frames_range(0), - move_range(doc, - frames_range(0), - frames_range(0), kDocumentRangeAfter)); + move_range(doc, + frames_range(0), + frames_range(0), kDocumentRangeAfter)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(0), - move_range(doc, - frames_range(0), - frames_range(1), kDocumentRangeBefore)); + move_range(doc, + frames_range(0), + frames_range(1), kDocumentRangeBefore)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(3), - move_range(doc, - frames_range(3), - frames_range(3), kDocumentRangeAfter)); + move_range(doc, + frames_range(3), + frames_range(3), kDocumentRangeAfter)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(3), - move_range(doc, - frames_range(3), - frames_range(3), kDocumentRangeBefore)); + move_range(doc, + frames_range(3), + frames_range(3), kDocumentRangeBefore)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(3), - move_range(doc, - frames_range(3), - frames_range(2), kDocumentRangeAfter)); + move_range(doc, + frames_range(3), + frames_range(2), kDocumentRangeAfter)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); // Move two frame to the same place EXPECT_EQ(frames_range(0, 1), - move_range(doc, - frames_range(0, 1), - frames_range(0), kDocumentRangeBefore)); + move_range(doc, + frames_range(0, 1), + frames_range(0), kDocumentRangeBefore)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(0, 1), - move_range(doc, - frames_range(0, 1), - frames_range(0), kDocumentRangeAfter)); + move_range(doc, + frames_range(0, 1), + frames_range(0), kDocumentRangeAfter)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(0, 1), - move_range(doc, - frames_range(0, 1), - frames_range(1), kDocumentRangeBefore)); + move_range(doc, + frames_range(0, 1), + frames_range(1), kDocumentRangeBefore)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(0, 1), - move_range(doc, - frames_range(0, 1), - frames_range(1), kDocumentRangeAfter)); + move_range(doc, + frames_range(0, 1), + frames_range(1), kDocumentRangeAfter)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(0, 1), - move_range(doc, - frames_range(0, 1), - frames_range(2), kDocumentRangeBefore)); + move_range(doc, + frames_range(0, 1), + frames_range(2), kDocumentRangeBefore)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(2, 3), - move_range(doc, - frames_range(2, 3), - frames_range(1), kDocumentRangeAfter)); + move_range(doc, + frames_range(2, 3), + frames_range(1), kDocumentRangeAfter)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(2, 3), - move_range(doc, - frames_range(2, 3), - frames_range(2), kDocumentRangeBefore)); + move_range(doc, + frames_range(2, 3), + frames_range(2), kDocumentRangeBefore)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(2, 3), - move_range(doc, - frames_range(2, 3), - frames_range(2), kDocumentRangeAfter)); + move_range(doc, + frames_range(2, 3), + frames_range(2), kDocumentRangeAfter)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(2, 3), - move_range(doc, - frames_range(2, 3), - frames_range(3), kDocumentRangeBefore)); + move_range(doc, + frames_range(2, 3), + frames_range(3), kDocumentRangeBefore)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); EXPECT_EQ(frames_range(2, 3), - move_range(doc, - frames_range(2, 3), - frames_range(3), kDocumentRangeAfter)); + move_range(doc, + frames_range(2, 3), + frames_range(3), kDocumentRangeAfter)); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); @@ -509,42 +565,50 @@ TEST_F(DocRangeOps, MoveFramesNoOp) { for (int i=0; i<2; ++i) { for (int frame=0; frame<4; ++frame) { EXPECT_EQ(frames_range(0, 3), - move_range(doc, - frames_range(0, 3), - frames_range(frame), places[i])); + move_range(doc, + frames_range(0, 3), + frames_range(frame), places[i])); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); } for (int frame=0; frame<3; ++frame) { EXPECT_EQ(frames_range(0, 3), - move_range(doc, - frames_range(0, 3), - frames_range(frame, frame+1), places[i])); + move_range(doc, + frames_range(0, 3), + frames_range(frame, frame+1), places[i])); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); } for (int frame=0; frame<2; ++frame) { EXPECT_EQ(frames_range(0, 3), - move_range(doc, - frames_range(0, 3), - frames_range(frame, frame+2), places[i])); + move_range(doc, + frames_range(0, 3), + frames_range(frame, frame+2), places[i])); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); } EXPECT_EQ(frames_range(0, 3), - move_range(doc, - frames_range(0, 3), - frames_range(0, 3), places[i])); + move_range(doc, + frames_range(0, 3), + frames_range(0, 3), places[i])); EXPECT_FRAME_ORDER(0, 1, 2, 3); EXPECT_FALSE(doc->undoHistory()->canUndo()); } } TEST_F(DocRangeOps, MoveCelsNoOp) { - // TODO + EXPECT_EQ(cels_range(0, 0, 1, 1), + move_range(doc, + cels_range(0, 0, 1, 1), + cels_range(0, 0, 1, 1), kDocumentRangeAfter)); + EXPECT_CEL(0, 0, 0, 0); + EXPECT_CEL(0, 1, 0, 1); + EXPECT_CEL(1, 0, 1, 0); + EXPECT_CEL(1, 1, 1, 1); + EXPECT_FALSE(doc->undoHistory()->canUndo()); } TEST_F(DocRangeOps, CopyCelsNoOp) { @@ -552,25 +616,21 @@ TEST_F(DocRangeOps, CopyCelsNoOp) { } TEST_F(DocRangeOps, MoveLayers) { - DocumentRange result; - // One layer at the bottom of another - result = move_range(doc, - layers_range(layer1), - layers_range(layer2), kDocumentRangeAfter); + EXPECT_EQ(layers_range(layer1), + move_range(doc, + layers_range(layer1), + layers_range(layer2), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer2, layer1, layer3, layer4); - EXPECT_EQ(layers_range(layer1), result); - doc->undoHistory()->undo(); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); // One layer at the bottom - result = move_range(doc, - layers_range(layer2), - layers_range(layer1), kDocumentRangeBefore); + EXPECT_EQ(layers_range(layer2), + move_range(doc, + layers_range(layer2), + layers_range(layer1), kDocumentRangeBefore)); EXPECT_LAYER_ORDER(layer2, layer1, layer3, layer4); - EXPECT_EQ(layers_range(layer2), result); - doc->undoHistory()->undo(); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); @@ -585,46 +645,43 @@ TEST_F(DocRangeOps, MoveLayers) { layer1->setBackground(false); // Move one layer to the top - result = move_range(doc, - layers_range(layer2), - layers_range(layer4), kDocumentRangeAfter); + EXPECT_EQ(layers_range(layer2), + move_range(doc, + layers_range(layer2), + layers_range(layer4), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer1, layer3, layer4, layer2); - EXPECT_EQ(layers_range(layer2), result); - doc->undoHistory()->undo(); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); // Move one layers before other - result = move_range(doc, - layers_range(layer2), - layers_range(layer4), kDocumentRangeBefore); + EXPECT_EQ(layers_range(layer2), + move_range(doc, + layers_range(layer2), + layers_range(layer4), kDocumentRangeBefore)); EXPECT_LAYER_ORDER(layer1, layer3, layer2, layer4); - EXPECT_EQ(layers_range(layer2), result); - doc->undoHistory()->undo(); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); - result = move_range(doc, - layers_range(layer1), - layers_range(layer3, layer4), kDocumentRangeBefore); + EXPECT_EQ(layers_range(layer1), + move_range(doc, + layers_range(layer1), + layers_range(layer3, layer4), kDocumentRangeBefore)); EXPECT_LAYER_ORDER(layer2, layer1, layer3, layer4); - EXPECT_EQ(layers_range(layer1), result); - doc->undoHistory()->undo(); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); // Move two layers on top of other - result = move_range(doc, - layers_range(layer2, layer3), - layers_range(layer4), kDocumentRangeAfter); + EXPECT_EQ(layers_range(layer2, layer3), + move_range(doc, + layers_range(layer2, layer3), + layers_range(layer4), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer1, layer4, layer2, layer3); - EXPECT_EQ(layers_range(layer2, layer3), result); - result = move_range(doc, - layers_range(layer2, layer3), - layers_range(layer1), kDocumentRangeAfter); + EXPECT_EQ(layers_range(layer2, layer3), + move_range(doc, + layers_range(layer2, layer3), + layers_range(layer1), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); - EXPECT_EQ(layers_range(layer2, layer3), result); // Move three layers at the bottom (but we cannot because the bottom is a background layer) layer1->setBackground(true); @@ -637,85 +694,243 @@ TEST_F(DocRangeOps, MoveLayers) { layer1->setBackground(false); // Move three layers at the top - result = move_range(doc, - layers_range(layer1, layer3), - layers_range(layer4), kDocumentRangeAfter); + EXPECT_EQ(layers_range(layer1, layer3), + move_range(doc, + layers_range(layer1, layer3), + layers_range(layer4), kDocumentRangeAfter)); EXPECT_LAYER_ORDER(layer4, layer1, layer2, layer3); - EXPECT_EQ(layers_range(layer1, layer3), result); - doc->undoHistory()->undo(); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); // Move three layers at the bottom - result = move_range(doc, - layers_range(layer2, layer4), - layers_range(layer1), kDocumentRangeBefore); + EXPECT_EQ(layers_range(layer2, layer4), + move_range(doc, + layers_range(layer2, layer4), + layers_range(layer1), kDocumentRangeBefore)); EXPECT_LAYER_ORDER(layer2, layer3, layer4, layer1); - EXPECT_EQ(layers_range(layer2, layer4), result); - doc->undoHistory()->undo(); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); } TEST_F(DocRangeOps, MoveFrames) { - move_range(doc, - frames_range(0, 0), - frames_range(1, 1), kDocumentRangeAfter); + // Move frame 0 after 1 + EXPECT_EQ(frames_range(1), + move_range(doc, + frames_range(0), + frames_range(1), kDocumentRangeAfter)); EXPECT_FRAME_ORDER(1, 0, 2, 3); - doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); - // Move one frame at the end - move_range(doc, - frames_range(1, 1), - frames_range(3, 3), kDocumentRangeAfter); + // Move frame 1 after frame 5 (at the end) + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + EXPECT_EQ(frames_range(5), + move_range(doc, + frames_range(1), + frames_range(5), kDocumentRangeAfter)); + EXPECT_FRAME_ORDER6(0, 2, 3, 4, 5, 1); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + + // Move frames 1,2 after 5 + EXPECT_EQ(frames_range(4, 5), + move_range( + doc, + frames_range(1, 2), + frames_range(5), kDocumentRangeAfter)); + EXPECT_FRAME_ORDER6(0, 3, 4, 5, 1, 2); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + + // Move frames 1,2 after 3 + EXPECT_EQ(frames_range(1, 2), + move_range(doc, + frames_range(2, 3), + frames_range(0), kDocumentRangeAfter)); EXPECT_FRAME_ORDER(0, 2, 3, 1); - - doc->undoHistory()->undo(); - EXPECT_FRAME_ORDER(0, 1, 2, 3); - - // Move two frames after other - move_range(doc, - frames_range(1, 2), - frames_range(3, 3), kDocumentRangeAfter); - EXPECT_FRAME_ORDER(0, 3, 1, 2); - - doc->undoHistory()->undo(); - EXPECT_FRAME_ORDER(0, 1, 2, 3); - - move_range(doc, - frames_range(2, 3), - frames_range(0, 0), kDocumentRangeAfter); - EXPECT_FRAME_ORDER(0, 2, 3, 1); - doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); // Move three frames at the beginning - move_range(doc, - frames_range(1, 3), - frames_range(0, 0), kDocumentRangeBefore); + EXPECT_EQ(frames_range(0, 2), + move_range(doc, + frames_range(1, 3), + frames_range(0), kDocumentRangeBefore)); EXPECT_FRAME_ORDER(1, 2, 3, 0); - doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); + + // Move three frames at the end + EXPECT_EQ(frames_range(1, 3), + move_range(doc, + frames_range(0, 2), + frames_range(3), kDocumentRangeAfter)); + EXPECT_FRAME_ORDER(3, 0, 1, 2); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); +} + +TEST_F(DocRangeOps, MoveFramesNonAdjacent) { + // Move frames 0,2... + + DocumentRange from; + from.startRange(nullptr, 0, DocumentRange::kFrames); from.endRange(nullptr, 0); + from.startRange(nullptr, 2, DocumentRange::kFrames); from.endRange(nullptr, 2); + + // Move frames 0,2 after 3... + + EXPECT_EQ(frames_range(2, 3), + move_range(doc, from, frames_range(3), kDocumentRangeAfter)); + EXPECT_FRAME_ORDER(1, 3, 0, 2); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); + + // Move frames 0,2 before 3... + + EXPECT_EQ(frames_range(1, 2), + move_range(doc, from, frames_range(3), kDocumentRangeBefore)); + EXPECT_FRAME_ORDER(1, 0, 2, 3); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); + + // Move frames 0,2 before 0... + + EXPECT_EQ(frames_range(0, 1), + move_range(doc, from, frames_range(0), kDocumentRangeBefore)); + EXPECT_FRAME_ORDER(0, 2, 1, 3); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); + + // Move frames 0,2 after 0... + + EXPECT_EQ(frames_range(0, 1), + move_range(doc, from, frames_range(0), kDocumentRangeAfter)); + EXPECT_FRAME_ORDER(0, 2, 1, 3); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); + + // Move frames 0,2 before 1... + + EXPECT_EQ(frames_range(0, 1), + move_range(doc, from, frames_range(1), kDocumentRangeBefore)); + EXPECT_FRAME_ORDER(0, 2, 1, 3); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); + + // Move frames 0,2 after 1... + + EXPECT_EQ(frames_range(1, 2), + move_range(doc, from, frames_range(1), kDocumentRangeAfter)); + EXPECT_FRAME_ORDER(1, 0, 2, 3); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); + + // Move 1,2,5... + + from.clearRange(); + from.startRange(nullptr, 1, DocumentRange::kFrames); + from.endRange(nullptr, 2); + from.startRange(nullptr, 5, DocumentRange::kFrames); + from.endRange(nullptr, 5); + + // Move 1,2,5 before 4... + + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + EXPECT_EQ(frames_range(2, 4), + move_range(doc, from, frames_range(4), kDocumentRangeBefore)); + EXPECT_FRAME_ORDER6(0, 3, 1, 2, 5, 4); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + + // Move 1,2,5 after 4... + + EXPECT_EQ(frames_range(3, 5), + move_range(doc, from, frames_range(4), kDocumentRangeAfter)); + EXPECT_FRAME_ORDER6(0, 3, 4, 1, 2, 5); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + + // Move 1,2,5 before 2... + + EXPECT_EQ(frames_range(1, 3), + move_range(doc, from, frames_range(2), kDocumentRangeBefore)); + EXPECT_FRAME_ORDER6(0, 1, 2, 5, 3, 4); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + + // Move 1,2,5 after 2... + + EXPECT_EQ(frames_range(1, 3), + move_range(doc, from, frames_range(2), kDocumentRangeAfter)); + EXPECT_FRAME_ORDER6(0, 1, 2, 5, 3, 4); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + + // Move 1,2,5 before 1... + + EXPECT_EQ(frames_range(1, 3), + move_range(doc, from, frames_range(1), kDocumentRangeBefore)); + EXPECT_FRAME_ORDER6(0, 1, 2, 5, 3, 4); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + + // Move 1,3,5... + + from.clearRange(); + from.startRange(nullptr, 1, DocumentRange::kFrames); + from.endRange(nullptr, 1); + from.startRange(nullptr, 3, DocumentRange::kFrames); + from.endRange(nullptr, 3); + from.startRange(nullptr, 5, DocumentRange::kFrames); + from.endRange(nullptr, 5); + + // Move 1,3,5 before 4... + + EXPECT_EQ(frames_range(2, 4), + move_range(doc, from, frames_range(4), kDocumentRangeBefore)); + EXPECT_FRAME_ORDER6(0, 2, 1, 3, 5, 4); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + + // Move 1,3,5 after 4... + + EXPECT_EQ(frames_range(3, 5), + move_range(doc, from, frames_range(4), kDocumentRangeAfter)); + EXPECT_FRAME_ORDER6(0, 2, 4, 1, 3, 5); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + + // Move 1,3,5 before 5... + + EXPECT_EQ(frames_range(3, 5), + move_range(doc, from, frames_range(5), kDocumentRangeBefore)); + EXPECT_FRAME_ORDER6(0, 2, 4, 1, 3, 5); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); + + // Move 1,3,5 after 5... + + EXPECT_EQ(frames_range(3, 5), + move_range(doc, from, frames_range(5), kDocumentRangeAfter)); + EXPECT_FRAME_ORDER6(0, 2, 4, 1, 3, 5); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER6(0, 1, 2, 3, 4, 5); } TEST_F(DocRangeOps, MoveCels) { DocumentRangePlace ignore = kDocumentRangeBefore; move_range(doc, - cels_range(0, 0, 0, 0), - cels_range(0, 1, 0, 1), ignore); + cels_range(0, 0, 0, 0), + cels_range(0, 1, 0, 1), ignore); EXPECT_CEL(0, 0, 0, 1); EXPECT_EMPTY_CEL(0, 0); doc->undoHistory()->undo(); EXPECT_CEL(0, 0, 0, 0); move_range(doc, - cels_range(0, 0, 0, 1), - cels_range(0, 2, 0, 3), ignore); + cels_range(0, 0, 0, 1), + cels_range(0, 2, 0, 3), ignore); EXPECT_CEL(0, 0, 0, 2); EXPECT_CEL(0, 1, 0, 3); EXPECT_EMPTY_CEL(0, 0); @@ -723,8 +938,21 @@ TEST_F(DocRangeOps, MoveCels) { doc->undoHistory()->undo(); move_range(doc, - cels_range(0, 0, 0, 3), - cels_range(1, 0, 1, 3), ignore); + cels_range(0, 0, 1, 1), + cels_range(2, 2, 3, 3), ignore); + EXPECT_CEL(0, 0, 2, 2); + EXPECT_CEL(0, 1, 2, 3); + EXPECT_CEL(1, 0, 3, 2); + EXPECT_CEL(1, 1, 3, 3); + EXPECT_EMPTY_CEL(0, 0); + EXPECT_EMPTY_CEL(0, 1); + EXPECT_EMPTY_CEL(1, 0); + EXPECT_EMPTY_CEL(1, 1); + doc->undoHistory()->undo(); + + move_range(doc, + cels_range(0, 0, 0, 3), + cels_range(1, 0, 1, 3), ignore); EXPECT_CEL(0, 0, 1, 0); EXPECT_CEL(0, 1, 1, 1); EXPECT_CEL(0, 2, 1, 2); @@ -734,6 +962,16 @@ TEST_F(DocRangeOps, MoveCels) { EXPECT_EMPTY_CEL(0, 2); EXPECT_EMPTY_CEL(0, 3); doc->undoHistory()->undo(); + + // Moving with overlapping areas + move_range(doc, + cels_range(0, 0, 0, 2), + cels_range(0, 1, 0, 3), ignore); + EXPECT_CEL(0, 0, 0, 1); + EXPECT_CEL(0, 1, 0, 2); + EXPECT_CEL(0, 2, 0, 3); + EXPECT_EMPTY_CEL(0, 0); + doc->undoHistory()->undo(); } TEST_F(DocRangeOps, CopyLayers) { @@ -742,79 +980,139 @@ TEST_F(DocRangeOps, CopyLayers) { TEST_F(DocRangeOps, CopyFrames) { // Copy one frame - copy_range(doc, - frames_range(0), - frames_range(2, 3), kDocumentRangeBefore); + EXPECT_EQ(frames_range(2), + copy_range(doc, + frames_range(0), + frames_range(2, 3), kDocumentRangeBefore)); EXPECT_FRAME_COPY1(0, 1, 0, 2, 3); doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); - copy_range(doc, - frames_range(0), - frames_range(2, 3), kDocumentRangeAfter); + EXPECT_EQ(frames_range(4), + copy_range(doc, + frames_range(0), + frames_range(2, 3), kDocumentRangeAfter)); EXPECT_FRAME_COPY1(0, 1, 2, 3, 0); doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); - copy_range(doc, - frames_range(3), - frames_range(0, 1), kDocumentRangeBefore); + EXPECT_EQ(frames_range(0), + copy_range(doc, + frames_range(3), + frames_range(0, 1), kDocumentRangeBefore)); EXPECT_FRAME_COPY1(3, 0, 1, 2, 3); doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); - copy_range(doc, - frames_range(3), - frames_range(0, 1), kDocumentRangeAfter); + EXPECT_EQ(frames_range(2), + copy_range(doc, + frames_range(3), + frames_range(0, 1), kDocumentRangeAfter)); EXPECT_FRAME_COPY1(0, 1, 3, 2, 3); doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); // Copy three frames - copy_range(doc, - frames_range(0, 2), - frames_range(3), kDocumentRangeBefore); + EXPECT_EQ(frames_range(3, 5), + copy_range(doc, + frames_range(0, 2), + frames_range(3), kDocumentRangeBefore)); EXPECT_FRAME_COPY3(0, 1, 2, 0, 1, 2, 3); doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); - copy_range(doc, - frames_range(0, 2), - frames_range(3), kDocumentRangeAfter); + EXPECT_EQ(frames_range(4, 6), + copy_range(doc, + frames_range(0, 2), + frames_range(3), kDocumentRangeAfter)); EXPECT_FRAME_COPY3(0, 1, 2, 3, 0, 1, 2); doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); - copy_range(doc, - frames_range(1, 3), - frames_range(0), kDocumentRangeBefore); + EXPECT_EQ(frames_range(0, 2), + copy_range(doc, + frames_range(1, 3), + frames_range(0), kDocumentRangeBefore)); EXPECT_FRAME_COPY3(1, 2, 3, 0, 1, 2, 3); doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); - copy_range(doc, - frames_range(1, 3), - frames_range(0), kDocumentRangeAfter); + EXPECT_EQ(frames_range(1, 3), + copy_range(doc, + frames_range(1, 3), + frames_range(0), kDocumentRangeAfter)); EXPECT_FRAME_COPY3(0, 1, 2, 3, 1, 2, 3); doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); - copy_range(doc, - frames_range(0, 2), - frames_range(0, 2), kDocumentRangeBefore); + EXPECT_EQ(frames_range(0, 2), + copy_range(doc, + frames_range(0, 2), + frames_range(0, 2), kDocumentRangeBefore)); EXPECT_FRAME_COPY3(0, 1, 2, 0, 1, 2, 3); doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); - copy_range(doc, - frames_range(0, 2), - frames_range(0, 2), kDocumentRangeAfter); + EXPECT_EQ(frames_range(3, 5), + copy_range(doc, + frames_range(0, 2), + frames_range(0, 2), kDocumentRangeAfter)); EXPECT_FRAME_COPY3(0, 1, 2, 0, 1, 2, 3); doc->undoHistory()->undo(); EXPECT_FRAME_ORDER(0, 1, 2, 3); } +TEST_F(DocRangeOps, CopyFramesNonAdjacent) { + // Copy frames 0 and 2... + + DocumentRange from; + from.startRange(nullptr, 0, DocumentRange::kFrames); from.endRange(nullptr, 0); + from.startRange(nullptr, 2, DocumentRange::kFrames); from.endRange(nullptr, 2); + + EXPECT_EQ(frames_range(3, 4), + copy_range(doc, from, frames_range(3), kDocumentRangeBefore)); + EXPECT_FRAME_COPY2(0, 1, 2, 0, 2, 3); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); + + EXPECT_EQ(frames_range(4, 5), + copy_range(doc, from, frames_range(3), kDocumentRangeAfter)); + EXPECT_FRAME_COPY2(0, 1, 2, 3, 0, 2); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); + + EXPECT_EQ(frames_range(2, 3), + copy_range(doc, from, frames_range(1), kDocumentRangeAfter)); + EXPECT_FRAME_COPY2(0, 1, 0, 2, 2, 3); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); + + EXPECT_EQ(frames_range(1, 2), + copy_range(doc, from, frames_range(1), kDocumentRangeBefore)); + EXPECT_FRAME_COPY2(0, 0, 2, 1, 2, 3); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); + + // Copy frames 1 and 3... + + from.clearRange(); + from.startRange(nullptr, 1, DocumentRange::kFrames); from.endRange(nullptr, 1); + from.startRange(nullptr, 3, DocumentRange::kFrames); from.endRange(nullptr, 3); + + EXPECT_EQ(frames_range(0, 1), + copy_range(doc, from, frames_range(0), kDocumentRangeBefore)); + EXPECT_FRAME_COPY2(1, 3, 0, 1, 2, 3); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); + + EXPECT_EQ(frames_range(1, 2), + copy_range(doc, from, frames_range(0), kDocumentRangeAfter)); + EXPECT_FRAME_COPY2(0, 1, 3, 1, 2, 3); + doc->undoHistory()->undo(); + EXPECT_FRAME_ORDER(0, 1, 2, 3); +} + TEST_F(DocRangeOps, CopyCels) { // TODO } diff --git a/src/app/ui/status_bar.h b/src/app/ui/status_bar.h index 8b9aa8e2e..40bb18a01 100644 --- a/src/app/ui/status_bar.h +++ b/src/app/ui/status_bar.h @@ -15,7 +15,6 @@ #include "doc/context_observer.h" #include "doc/document_observer.h" #include "doc/documents_observer.h" -#include "doc/layer_index.h" #include "ui/base.h" #include "ui/box.h" diff --git a/src/app/ui/timeline.cpp b/src/app/ui/timeline.cpp index 98be39f1f..14c7f275a 100644 --- a/src/app/ui/timeline.cpp +++ b/src/app/ui/timeline.cpp @@ -145,7 +145,7 @@ namespace { } } -} +} // anonymous namespace Timeline::Timeline() : Widget(kGenericWidget) @@ -200,7 +200,7 @@ void Timeline::updateUsingEditor(Editor* editor) detachDocument(); if (m_range.enabled()) { - m_range.disableRange(); + m_range.clearRange(); invalidate(); } @@ -262,6 +262,23 @@ bool Timeline::isMovingCel() const m_range.type() == Range::kCels); } +bool Timeline::selectedLayersBounds(const SelectedLayers& layers, + layer_t* first, layer_t* last) const +{ + if (layers.empty()) + return false; + + *first = *last = getLayerIndex(*layers.begin()); + + for (auto layer : layers) { + layer_t i = getLayerIndex(layer); + if (*first > i) *first = i; + if (*last < i) *last = i; + } + + return true; +} + void Timeline::setLayer(Layer* layer) { ASSERT(m_editor != NULL); @@ -299,49 +316,49 @@ void Timeline::setFrame(frame_t frame, bool byUser) } } -SelectedLayers Timeline::selectedLayers() const -{ - SelectedLayers layers; - - for (LayerIndex layer = m_range.layerBegin(); layer <= m_range.layerEnd(); ++layer) { - layers.insert(m_layers[layer].layer); - } - - return layers; -} - -SelectedFrames Timeline::selectedFrames() const -{ - SelectedFrames frames; - - if (m_range.enabled() && - m_range.frameBegin() >= frame_t(0)) { - frames.insert(m_range.frameBegin(), - m_range.frameEnd()); - } - - return frames; -} - void Timeline::prepareToMoveRange() { ASSERT(m_range.enabled()); - m_moveRangeData.activeRelativeLayer = getLayerIndex(m_layer) - m_range.layerBegin(); - m_moveRangeData.activeRelativeFrame = m_frame - m_range.frameBegin(); + layer_t i = 0; + for (auto layer : m_range.selectedLayers().toLayerList()) { + if (layer == m_layer) + break; + ++i; + } + + frame_t j = 0; + for (auto frame : m_range.selectedFrames()) { + if (frame == m_frame) + break; + ++j; + } + + m_moveRangeData.activeRelativeLayer = i; + m_moveRangeData.activeRelativeFrame = j; } void Timeline::moveRange(Range& range) { regenerateLayers(); - if (range.layerBegin() >= LayerIndex(0) && - range.layerBegin() + m_moveRangeData.activeRelativeLayer < int(m_layers.size())) { - setLayer(m_layers[range.layerBegin() + m_moveRangeData.activeRelativeLayer].layer); + layer_t i = 0; + for (auto layer : range.selectedLayers().toLayerList()) { + if (i == m_moveRangeData.activeRelativeLayer) { + setLayer(layer); + break; + } + ++i; } - if (range.frameBegin() >= frame_t(0)) - setFrame(range.frameBegin() + m_moveRangeData.activeRelativeFrame, true); + frame_t j = 0; + for (auto frame : range.selectedFrames()) { + if (j == m_moveRangeData.activeRelativeFrame) { + setFrame(frame, true); + break; + } + ++j; + } m_range = range; } @@ -404,6 +421,14 @@ bool Timeline::onProcessMessage(Message* msg) // Clicked-part = hot-part. m_clk = m_hot; + // With Ctrl+click (Win/Linux) or Shift+click (OS X) we can + // select non-adjacents layer/frame ranges + bool clearRange = +#if !defined(__APPLE__) + !msg->ctrlPressed() || +#endif + !msg->shiftPressed(); + captureMouse(); switch (m_hot.part) { @@ -425,7 +450,10 @@ bool Timeline::onProcessMessage(Message* msg) if (selectFrame) { m_state = STATE_SELECTING_FRAMES; - m_range.startRange(getLayerIndex(m_layer), m_clk.frame, Range::kFrames); + if (clearRange) + m_range.clearRange(); + m_range.startRange(m_layer, m_clk.frame, Range::kFrames); + m_startRange = m_range; setFrame(m_clk.frame, true); } @@ -433,12 +461,16 @@ bool Timeline::onProcessMessage(Message* msg) } case PART_LAYER_TEXT: { base::ScopedValue lock(m_fromTimeline, true, false); - LayerIndex old_layer = getLayerIndex(m_layer); + layer_t old_layer = getLayerIndex(m_layer); bool selectLayer = (mouseMsg->left() || !isLayerActive(m_clk.layer)); if (selectLayer) { m_state = STATE_SELECTING_LAYERS; - m_range.startRange(m_clk.layer, m_frame, Range::kLayers); + if (clearRange) + m_range.clearRange(); + m_range.startRange(m_layers[m_clk.layer].layer, + m_frame, Range::kLayers); + m_startRange = m_range; // Did the user select another layer? if (old_layer != m_clk.layer) { @@ -459,7 +491,7 @@ bool Timeline::onProcessMessage(Message* msg) break; case PART_CEL: { base::ScopedValue lock(m_fromTimeline, true, false); - LayerIndex old_layer = getLayerIndex(m_layer); + layer_t old_layer = getLayerIndex(m_layer); bool selectCel = (mouseMsg->left() || !isLayerActive(m_clk.layer) || !isFrameActive(m_clk.frame)); @@ -467,7 +499,10 @@ bool Timeline::onProcessMessage(Message* msg) if (selectCel) { m_state = STATE_SELECTING_CELS; - m_range.startRange(m_clk.layer, m_clk.frame, Range::kCels); + m_range.clearRange(); + m_range.startRange(m_layers[m_clk.layer].layer, + m_clk.frame, Range::kCels); + m_startRange = m_range; } // Select the new clicked-part. @@ -493,15 +528,20 @@ bool Timeline::onProcessMessage(Message* msg) if (m_range.type() == Range::kCels) { m_clk = hitTestCel(mouseMsg->position() - bounds().origin()); - if (m_clk.layer < m_range.layerBegin()) - m_clk.layer = m_range.layerBegin(); - else if (m_clk.layer > m_range.layerEnd()) - m_clk.layer = m_range.layerEnd(); + if (m_range.layers() > 0) { + layer_t layerFirst, layerLast; + if (selectedLayersBounds(selectedLayers(), + &layerFirst, &layerLast)) { + layer_t layerIdx = m_clk.layer; + layerIdx = MID(layerFirst, layerIdx, layerLast); + m_clk.layer = layerIdx; + } + } - if (m_clk.frame < m_range.frameBegin()) - m_clk.frame = m_range.frameBegin(); - else if (m_clk.frame > m_range.frameEnd()) - m_clk.frame = m_range.frameEnd(); + if (m_clk.frame < m_range.firstFrame()) + m_clk.frame = m_range.firstFrame(); + else if (m_clk.frame > m_range.lastFrame()) + m_clk.frame = m_range.lastFrame(); } break; } @@ -573,25 +613,31 @@ bool Timeline::onProcessMessage(Message* msg) switch (m_state) { case STATE_SELECTING_LAYERS: { - if (m_layer != m_layers[hit.layer].layer) { - m_range.endRange(hit.layer, m_frame); - setLayer(m_layers[m_clk.layer = hit.layer].layer); + Layer* hitLayer = m_layers[hit.layer].layer; + if (m_layer != hitLayer) { + m_clk.layer = hit.layer; + setLayer(hitLayer); + m_range = m_startRange; + m_range.endRange(hitLayer, m_frame); } break; } case STATE_SELECTING_FRAMES: { - m_range.endRange(getLayerIndex(m_layer), hit.frame); setFrame(m_clk.frame = hit.frame, true); + m_range = m_startRange; + m_range.endRange(m_layer, hit.frame); break; } case STATE_SELECTING_CELS: - if ((m_layer != m_layers[hit.layer].layer) - || (m_frame != hit.frame)) { - m_range.endRange(hit.layer, hit.frame); - setLayer(m_layers[m_clk.layer = hit.layer].layer); + Layer* hitLayer = m_layers[hit.layer].layer; + if ((m_layer != hitLayer) || (m_frame != hit.frame)) { + m_clk.layer = hit.layer; + setLayer(hitLayer); setFrame(m_clk.frame = hit.frame, true); + m_range = m_startRange; + m_range.endRange(hitLayer, hit.frame); } break; } @@ -929,7 +975,7 @@ bool Timeline::onProcessMessage(Message* msg) case kKeyEsc: if (m_state == STATE_STANDBY) { - m_range.disableRange(); + m_range.clearRange(); invalidate(); } else { @@ -1054,11 +1100,11 @@ void Timeline::onPaint(ui::PaintEvent& ev) // Lock the sprite to read/render it. const DocumentReader documentReader(m_document, 0); - LayerIndex layer, first_layer, last_layer; - frame_t frame, first_frame, last_frame; + layer_t layer, firstLayer, lastLayer; + frame_t frame, firstFrame, lastFrame; - getDrawableLayers(g, &first_layer, &last_layer); - getDrawableFrames(g, &first_frame, &last_frame); + getDrawableLayers(g, &firstLayer, &lastLayer); + getDrawableFrames(g, &firstFrame, &lastFrame); drawTop(g); @@ -1069,7 +1115,7 @@ void Timeline::onPaint(ui::PaintEvent& ev) { IntersectClip clip(g, getFrameHeadersBounds()); if (clip) { - for (frame=first_frame; frame<=last_frame; ++frame) + for (frame=firstFrame; frame<=lastFrame; ++frame) drawHeaderFrame(g, frame); // Draw onionskin indicators. @@ -1084,7 +1130,7 @@ void Timeline::onPaint(ui::PaintEvent& ev) // Draw each visible layer. DrawCelData data; - for (layer=last_layer; layer>=first_layer; --layer) { + for (layer=lastLayer; layer>=firstLayer; --layer) { { IntersectClip clip(g, getLayerHeadersBounds()); if (clip) @@ -1098,7 +1144,7 @@ void Timeline::onPaint(ui::PaintEvent& ev) Layer* layerPtr = m_layers[layer].layer; if (!layerPtr->isImage()) { // Draw empty cels - for (frame=first_frame; frame<=last_frame; ++frame) { + for (frame=firstFrame; frame<=lastFrame; ++frame) { drawCel(g, layer, frame, nullptr, nullptr); } continue; @@ -1108,7 +1154,7 @@ void Timeline::onPaint(ui::PaintEvent& ev) LayerImage* layerImagePtr = static_cast(layerPtr); data.begin = layerImagePtr->getCelBegin(); data.end = layerImagePtr->getCelEnd(); - data.it = layerImagePtr->findFirstCelIteratorAfter(first_frame-1); + data.it = layerImagePtr->findFirstCelIteratorAfter(firstFrame-1); data.prevIt = data.end; data.nextIt = (data.it != data.end ? data.it+1: data.end); @@ -1130,7 +1176,7 @@ void Timeline::onPaint(ui::PaintEvent& ev) --it2; if ((*it2)->image()->id() == imageId) { data.firstLink = it2; - if ((*data.firstLink)->frame() < first_frame) + if ((*data.firstLink)->frame() < firstFrame) break; } } while (it2 != data.begin); @@ -1140,7 +1186,7 @@ void Timeline::onPaint(ui::PaintEvent& ev) while (it2 != data.end) { if ((*it2)->image()->id() == imageId) { data.lastLink = it2; - if ((*data.lastLink)->frame() > last_frame) + if ((*data.lastLink)->frame() > lastFrame) break; } ++it2; @@ -1151,7 +1197,7 @@ void Timeline::onPaint(ui::PaintEvent& ev) data.activeIt = data.end; // Draw every visible cel for each layer. - for (frame=first_frame; frame<=last_frame; ++frame) { + for (frame=firstFrame; frame<=lastFrame; ++frame) { Cel* cel = (data.it != data.end && (*data.it)->frame() == frame ? *data.it: nullptr); @@ -1279,7 +1325,7 @@ void Timeline::onRemoveFrame(doc::DocumentEvent& ev) void Timeline::onSelectionChanged(doc::DocumentEvent& ev) { - m_range.disableRange(); + m_range.clearRange(); invalidate(); } @@ -1301,7 +1347,7 @@ void Timeline::onAfterFrameChanged(Editor* editor) setFrame(editor->frame(), false); if (!hasCapture()) - m_range.disableRange(); + m_range.clearRange(); showCurrentCel(); invalidate(); @@ -1315,7 +1361,7 @@ void Timeline::onAfterLayerChanged(Editor* editor) setLayer(editor->layer()); if (!hasCapture()) - m_range.disableRange(); + m_range.clearRange(); showCurrentCel(); invalidate(); @@ -1366,28 +1412,28 @@ void Timeline::setCursor(ui::Message* msg, const Hit& hit) } } -void Timeline::getDrawableLayers(ui::Graphics* g, LayerIndex* first_layer, LayerIndex* last_layer) +void Timeline::getDrawableLayers(ui::Graphics* g, layer_t* firstLayer, layer_t* lastLayer) { int hpx = (clientBounds().h - HDRSIZE - topHeight()); - LayerIndex i = lastLayer() - LayerIndex((viewScroll().y+hpx) / LAYSIZE); - i = MID(firstLayer(), i, lastLayer()); + layer_t i = this->lastLayer() - ((viewScroll().y+hpx) / LAYSIZE); + i = MID(this->firstLayer(), i, this->lastLayer()); - LayerIndex j = i + LayerIndex(hpx / LAYSIZE + 1); + layer_t j = i + (hpx / LAYSIZE + 1); if (!m_layers.empty()) - j = MID(firstLayer(), j, lastLayer()); + j = MID(this->firstLayer(), j, this->lastLayer()); else - j = LayerIndex::NoLayer; + j = -1; - *first_layer = i; - *last_layer = j; + *firstLayer = i; + *lastLayer = j; } -void Timeline::getDrawableFrames(ui::Graphics* g, frame_t* first_frame, frame_t* last_frame) +void Timeline::getDrawableFrames(ui::Graphics* g, frame_t* firstFrame, frame_t* lastFrame) { int availW = (clientBounds().w - m_separator_x); - *first_frame = frame_t(viewScroll().x / FRMSIZE); - *last_frame = *first_frame + frame_t(availW / FRMSIZE) + ((availW % FRMSIZE) > 0 ? 1: 0); + *firstFrame = frame_t(viewScroll().x / FRMSIZE); + *lastFrame = *firstFrame + frame_t(availW / FRMSIZE) + ((availW % FRMSIZE) > 0 ? 1: 0); } void Timeline::drawPart(ui::Graphics* g, const gfx::Rect& bounds, @@ -1500,7 +1546,7 @@ void Timeline::drawHeaderFrame(ui::Graphics* g, frame_t frame) g->setFont(oldFont); } -void Timeline::drawLayer(ui::Graphics* g, LayerIndex layerIdx) +void Timeline::drawLayer(ui::Graphics* g, int layerIdx) { SkinTheme::Styles& styles = skinTheme()->styles; Layer* layer = m_layers[layerIdx].layer; @@ -1614,7 +1660,7 @@ void Timeline::drawLayer(ui::Graphics* g, LayerIndex layerIdx) } } -void Timeline::drawCel(ui::Graphics* g, LayerIndex layerIndex, frame_t frame, Cel* cel, DrawCelData* data) +void Timeline::drawCel(ui::Graphics* g, layer_t layerIndex, frame_t frame, Cel* cel, DrawCelData* data) { SkinTheme::Styles& styles = skinTheme()->styles; Layer* layer = m_layers[layerIndex].layer; @@ -1622,8 +1668,9 @@ void Timeline::drawCel(ui::Graphics* g, LayerIndex layerIndex, frame_t frame, Ce bool is_hover = (m_hot.part == PART_CEL && m_hot.layer == layerIndex && m_hot.frame == frame); - bool is_active = (isLayerActive(layerIndex) || isFrameActive(frame)); - bool is_empty = (image == nullptr); + const bool is_active = (isLayerActive(layerIndex) || + isFrameActive(frame)); + const bool is_empty = (image == nullptr); gfx::Rect bounds = getPartBounds(Hit(PART_CEL, layerIndex, frame)); IntersectClip clip(g, bounds); if (!clip) @@ -1740,7 +1787,7 @@ void Timeline::drawFrameTags(ui::Graphics* g) } { - bounds = getPartBounds(Hit(PART_FRAME_TAG, LayerIndex(0), 0, frameTag->id())); + bounds = getPartBounds(Hit(PART_FRAME_TAG, 0, 0, frameTag->id())); gfx::Color bg = frameTag->color(); if (m_clk.part == PART_FRAME_TAG && m_clk.frameTag == frameTag->id()) { @@ -2054,18 +2101,26 @@ gfx::Rect Timeline::getRangeBounds(const Range& range) const { gfx::Rect rc; switch (range.type()) { - case Range::kNone: break; // Return empty rectangle + case Range::kNone: + // Return empty rectangle + break; case Range::kCels: - rc = getPartBounds(Hit(PART_CEL, range.layerBegin(), range.frameBegin())).createUnion( - getPartBounds(Hit(PART_CEL, range.layerEnd(), range.frameEnd()))); + for (auto layer : range.selectedLayers()) { + layer_t layerIdx = getLayerIndex(layer); + for (auto frame : range.selectedFrames()) + rc |= getPartBounds(Hit(PART_CEL, layerIdx, frame)); + } break; - case Range::kFrames: - rc = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), range.frameBegin())).createUnion( - getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), range.frameEnd()))); + case Range::kFrames: { + for (auto frame : range.selectedFrames()) + rc |= getPartBounds(Hit(PART_HEADER_FRAME, 0, frame)); break; + } case Range::kLayers: - rc = getPartBounds(Hit(PART_LAYER, range.layerBegin())).createUnion( - getPartBounds(Hit(PART_LAYER, range.layerEnd()))); + for (auto layer : range.selectedLayers()) { + layer_t layerIdx = getLayerIndex(layer); + rc |= getPartBounds(Hit(PART_LAYER, layerIdx)); + } break; } return rc; @@ -2125,11 +2180,7 @@ void Timeline::updateByMousePos(ui::Message* msg, const gfx::Point& mousePos) Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos) { - Hit hit( - PART_NOTHING, - LayerIndex::NoLayer, - frame_t(-1)); - + Hit hit(PART_NOTHING, -1, -1); if (!m_document) return hit; @@ -2140,8 +2191,8 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos) gfx::Point scroll = viewScroll(); int top = topHeight(); - hit.layer = lastLayer() - LayerIndex( - (mousePos.y + hit.layer = lastLayer() - + ((mousePos.y - top - HDRSIZE + scroll.y) / LAYSIZE); @@ -2159,8 +2210,8 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos) hit.frame = MID(firstFrame(), hit.frame, lastFrame()); } else { - if (hit.layer > lastLayer()) hit.layer = LayerIndex::NoLayer; - if (hit.frame > lastFrame()) hit.frame = frame_t(-1); + if (hit.layer > lastLayer()) hit.layer = -1; + if (hit.frame > lastFrame()) hit.frame = -1; } // Is the mouse over onionskin handles? @@ -2179,7 +2230,7 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos) // Is the mouse on the frame tags area? else if (getPartBounds(Hit(PART_HEADER_FRAME_TAGS)).contains(mousePos)) { for (FrameTag* frameTag : m_sprite->frameTags()) { - gfx::Rect bounds = getPartBounds(Hit(PART_FRAME_TAG, LayerIndex(0), 0, frameTag->id())); + gfx::Rect bounds = getPartBounds(Hit(PART_FRAME_TAG, 0, 0, frameTag->id())); if (bounds.contains(mousePos)) { hit.part = PART_FRAME_TAG; hit.frameTag = frameTag->id(); @@ -2252,18 +2303,14 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos) Timeline::Hit Timeline::hitTestCel(const gfx::Point& mousePos) { - Hit hit( - PART_NOTHING, - LayerIndex::NoLayer, - frame_t(-1)); - + Hit hit(PART_NOTHING, -1, -1); if (!m_document) return hit; gfx::Point scroll = viewScroll(); int top = topHeight(); - hit.layer = lastLayer() - LayerIndex( + hit.layer = lastLayer() - ( (mousePos.y - top - HDRSIZE @@ -2320,25 +2367,30 @@ void Timeline::updateStatusBar(ui::Message* msg) case Range::kFrames: if (validFrame(m_hot.frame)) { if (m_dropTarget.hhit == DropTarget::Before) { - sb->setStatusText(0, "%s before frame %d", verb, int(m_dropRange.frameBegin()+1)); + sb->setStatusText(0, "%s before frame %d", verb, int(m_dropRange.firstFrame()+1)); return; } else if (m_dropTarget.hhit == DropTarget::After) { - sb->setStatusText(0, "%s after frame %d", verb, int(m_dropRange.frameEnd()+1)); + sb->setStatusText(0, "%s after frame %d", verb, int(m_dropRange.lastFrame()+1)); return; } } break; case Range::kLayers: { - int layerIdx = -1; - if (m_dropTarget.vhit == DropTarget::Bottom) - layerIdx = m_dropRange.layerBegin(); - else if (m_dropTarget.vhit == DropTarget::Top) - layerIdx = m_dropRange.layerEnd(); + layer_t firstLayer; + layer_t lastLayer; + if (!selectedLayersBounds(m_dropRange.selectedLayers(), + &firstLayer, &lastLayer)) + break; - Layer* layer = ((layerIdx >= 0 && layerIdx < (int)m_layers.size()) ? m_layers[layerIdx].layer: - nullptr); + layer_t layerIdx = -1; + if (m_dropTarget.vhit == DropTarget::Bottom) + layerIdx = firstLayer; + else if (m_dropTarget.vhit == DropTarget::Top) + layerIdx = lastLayer; + + Layer* layer = (validLayer(layerIdx) ? m_layers[layerIdx].layer: nullptr); if (layer) { if (m_dropTarget.vhit == DropTarget::Bottom) { sb->setStatusText(0, "%s at bottom of layer %s", verb, layer->name().c_str()); @@ -2440,7 +2492,7 @@ void Timeline::updateStatusBar(ui::Message* msg) sb->clearText(); } -void Timeline::showCel(LayerIndex layer, frame_t frame) +void Timeline::showCel(layer_t layer, frame_t frame) { gfx::Point scroll = viewScroll(); @@ -2478,7 +2530,7 @@ void Timeline::showCel(LayerIndex layer, frame_t frame) void Timeline::showCurrentCel() { - LayerIndex layer = getLayerIndex(m_layer); + layer_t layer = getLayerIndex(m_layer); if (layer >= firstLayer()) showCel(layer, m_frame); } @@ -2568,29 +2620,29 @@ bool Timeline::allLayersDiscontinuous() return true; } -LayerIndex Timeline::getLayerIndex(const Layer* layer) const +layer_t Timeline::getLayerIndex(const Layer* layer) const { for (int i=0; i<(int)m_layers.size(); i++) if (m_layers[i].layer == layer) - return LayerIndex(i); + return i; - return LayerIndex::NoLayer; + return -1; } -bool Timeline::isLayerActive(LayerIndex layerIndex) const +bool Timeline::isLayerActive(const layer_t layerIndex) const { if (layerIndex == getLayerIndex(m_layer)) return true; else - return m_range.inRange(layerIndex); + return m_range.contains(m_layers[layerIndex].layer); } -bool Timeline::isFrameActive(frame_t frame) const +bool Timeline::isFrameActive(const frame_t frame) const { if (frame == m_frame) return true; else - return m_range.inRange(frame); + return m_range.contains(frame); } void Timeline::dropRange(DropOp op) @@ -2664,57 +2716,24 @@ void Timeline::updateDropRange(const gfx::Point& pt) m_dropTarget.vhit = DropTarget::VNone; if (m_state != STATE_MOVING_RANGE) { - m_dropRange.disableRange(); + m_dropRange.clearRange(); return; } switch (m_range.type()) { - case Range::kCels: { - frame_t dx = m_hot.frame - m_clk.frame; - LayerIndex dy = m_hot.layer - m_clk.layer; - - LayerIndex layerIdx = dy+m_range.layerBegin(); - layerIdx = MID(firstLayer(), layerIdx, LayerIndex(m_layers.size() - m_range.layers())); - - frame_t frame = dx+m_range.frameBegin(); - frame = MAX(firstFrame(), frame); - - m_dropRange.startRange(layerIdx, frame, m_range.type()); - m_dropRange.endRange( - layerIdx+LayerIndex(m_range.layers()-1), - frame+m_range.frames()-1); + case Range::kCels: + m_dropRange = m_range; + m_dropRange.displace(m_hot.layer - m_clk.layer, + m_hot.frame - m_clk.frame); break; - } - case Range::kFrames: { - frame_t frame = m_hot.frame; - frame_t frameEnd = frame; - - if (frame >= m_range.frameBegin() && frame <= m_range.frameEnd()) { - frame = m_range.frameBegin(); - frameEnd = frame + m_range.frames() - 1; - } - - LayerIndex layerIdx = getLayerIndex(m_layer); - m_dropRange.startRange(layerIdx, frame, m_range.type()); - m_dropRange.endRange(layerIdx, frameEnd); + case Range::kFrames: + case Range::kLayers: + m_dropRange.clearRange(); + m_dropRange.startRange(m_layers[m_hot.layer].layer, m_hot.frame, m_range.type()); + m_dropRange.endRange(m_layers[m_hot.layer].layer, m_hot.frame); break; - } - - case Range::kLayers: { - LayerIndex layer = m_hot.layer; - LayerIndex layerEnd = layer; - - if (layer >= m_range.layerBegin() && layer <= m_range.layerEnd()) { - layer = m_range.layerBegin(); - layerEnd = layer + LayerIndex(m_range.layers() - 1); - } - - m_dropRange.startRange(layer, m_frame, m_range.type()); - m_dropRange.endRange(layerEnd, m_frame); - break; - } } gfx::Rect bounds = getRangeBounds(m_dropRange); @@ -2794,7 +2813,7 @@ void Timeline::onNewInputPriority(InputChainElement* element) // That is why we don't disable the range in this case. Workspace* workspace = dynamic_cast(element); if (!workspace) { - m_range.disableRange(); + m_range.clearRange(); invalidate(); } } @@ -2879,7 +2898,7 @@ bool Timeline::onClear(Context* ctx) void Timeline::onCancel(Context* ctx) { - m_range.disableRange(); + m_range.clearRange(); clearClipboardRange(); invalidate(); } diff --git a/src/app/ui/timeline.h b/src/app/ui/timeline.h index 0fbb8dcd7..31e472ccb 100644 --- a/src/app/ui/timeline.h +++ b/src/app/ui/timeline.h @@ -18,7 +18,6 @@ #include "doc/document_observer.h" #include "doc/documents_observer.h" #include "doc/frame.h" -#include "doc/layer_index.h" #include "doc/selected_frames.h" #include "doc/selected_layers.h" #include "doc/sprite.h" @@ -90,8 +89,8 @@ namespace app { bool isMovingCel() const; Range range() const { return m_range; } - SelectedLayers selectedLayers() const; - SelectedFrames selectedFrames() const; + const SelectedLayers& selectedLayers() const { return m_range.selectedLayers(); } + const SelectedFrames& selectedFrames() const { return m_range.selectedFrames(); } void prepareToMoveRange(); void moveRange(Range& range); @@ -151,11 +150,11 @@ namespace app { struct Hit { int part; - LayerIndex layer; + layer_t layer; frame_t frame; ObjectId frameTag; - Hit(int part = 0, LayerIndex layer = LayerIndex(0), frame_t frame = 0, ObjectId frameTag = NullId) + Hit(int part = 0, layer_t layer = -1, frame_t frame = 0, ObjectId frameTag = NullId) : part(part), layer(layer), frame(frame), frameTag(frameTag) { } @@ -182,84 +181,11 @@ namespace app { HHit hhit; VHit vhit; Layer* layer; - LayerIndex layerIdx; + ObjectId layerId; frame_t frame; int xpos, ypos; }; - void setLayer(Layer* layer); - void setFrame(frame_t frame, bool byUser); - bool allLayersVisible(); - bool allLayersInvisible(); - bool allLayersLocked(); - bool allLayersUnlocked(); - bool allLayersContinuous(); - bool allLayersDiscontinuous(); - void detachDocument(); - void setCursor(ui::Message* msg, const Hit& hit); - void getDrawableLayers(ui::Graphics* g, LayerIndex* first_layer, LayerIndex* last_layer); - void getDrawableFrames(ui::Graphics* g, frame_t* first_frame, frame_t* last_frame); - void drawPart(ui::Graphics* g, const gfx::Rect& bounds, - const char* text, skin::Style* style, - bool is_active = false, bool is_hover = false, - bool is_clicked = false, bool is_disabled = false); - void drawTop(ui::Graphics* g); - void drawHeader(ui::Graphics* g); - void drawHeaderFrame(ui::Graphics* g, frame_t frame); - void drawLayer(ui::Graphics* g, LayerIndex layerIdx); - void drawCel(ui::Graphics* g, LayerIndex layerIdx, frame_t frame, Cel* cel, DrawCelData* data); - void drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds, - Cel* cel, frame_t frame, bool is_active, bool is_hover, - DrawCelData* data); - void drawFrameTags(ui::Graphics* g); - void drawRangeOutline(ui::Graphics* g); - void drawPaddings(ui::Graphics* g); - bool drawPart(ui::Graphics* g, int part, LayerIndex layer, frame_t frame); - void drawClipboardRange(ui::Graphics* g); - gfx::Rect getLayerHeadersBounds() const; - gfx::Rect getFrameHeadersBounds() const; - gfx::Rect getOnionskinFramesBounds() const; - gfx::Rect getCelsBounds() const; - gfx::Rect getPartBounds(const Hit& hit) const; - gfx::Rect getRangeBounds(const Range& range) const; - void invalidateHit(const Hit& hit); - void regenerateLayers(); - void updateScrollBars(); - void updateByMousePos(ui::Message* msg, const gfx::Point& mousePos); - Hit hitTest(ui::Message* msg, const gfx::Point& mousePos); - Hit hitTestCel(const gfx::Point& mousePos); - void setHot(const Hit& hit); - void showCel(LayerIndex layer, frame_t frame); - void showCurrentCel(); - void cleanClk(); - gfx::Size getScrollableSize() const; - gfx::Point getMaxScrollablePos() const; - LayerIndex getLayerIndex(const Layer* layer) const; - bool isLayerActive(LayerIndex layerIdx) const; - bool isFrameActive(frame_t frame) const; - void updateStatusBar(ui::Message* msg); - void updateDropRange(const gfx::Point& pt); - void clearClipboardRange(); - - bool isCopyKeyPressed(ui::Message* msg); - - // The layer of the bottom (e.g. Background layer) - LayerIndex firstLayer() const { return LayerIndex(0); } - - // The layer of the top. - LayerIndex lastLayer() const { return LayerIndex(m_layers.size()-1); } - - frame_t firstFrame() const { return frame_t(0); } - frame_t lastFrame() const { return m_sprite->lastFrame(); } - - bool validLayer(LayerIndex layer) const { return layer >= firstLayer() && layer <= lastLayer(); } - bool validFrame(frame_t frame) const { return frame >= firstFrame() && frame <= lastFrame(); } - - int topHeight() const; - - DocumentPreferences& docPref() const; - skin::SkinTheme* skinTheme() const; - struct LayerInfo { Layer* layer; int level; @@ -286,6 +212,81 @@ namespace app { } }; + bool selectedLayersBounds(const SelectedLayers& layers, + layer_t* first, layer_t* last) const; + + void setLayer(Layer* layer); + void setFrame(frame_t frame, bool byUser); + bool allLayersVisible(); + bool allLayersInvisible(); + bool allLayersLocked(); + bool allLayersUnlocked(); + bool allLayersContinuous(); + bool allLayersDiscontinuous(); + void detachDocument(); + void setCursor(ui::Message* msg, const Hit& hit); + void getDrawableLayers(ui::Graphics* g, layer_t* firstLayer, layer_t* lastLayer); + void getDrawableFrames(ui::Graphics* g, frame_t* firstFrame, frame_t* lastFrame); + void drawPart(ui::Graphics* g, const gfx::Rect& bounds, + const char* text, skin::Style* style, + bool is_active = false, bool is_hover = false, + bool is_clicked = false, bool is_disabled = false); + void drawTop(ui::Graphics* g); + void drawHeader(ui::Graphics* g); + void drawHeaderFrame(ui::Graphics* g, frame_t frame); + void drawLayer(ui::Graphics* g, layer_t layerIdx); + void drawCel(ui::Graphics* g, layer_t layerIdx, frame_t frame, Cel* cel, DrawCelData* data); + void drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds, + Cel* cel, frame_t frame, bool is_active, bool is_hover, + DrawCelData* data); + void drawFrameTags(ui::Graphics* g); + void drawRangeOutline(ui::Graphics* g); + void drawPaddings(ui::Graphics* g); + bool drawPart(ui::Graphics* g, int part, layer_t layer, frame_t frame); + void drawClipboardRange(ui::Graphics* g); + gfx::Rect getLayerHeadersBounds() const; + gfx::Rect getFrameHeadersBounds() const; + gfx::Rect getOnionskinFramesBounds() const; + gfx::Rect getCelsBounds() const; + gfx::Rect getPartBounds(const Hit& hit) const; + gfx::Rect getRangeBounds(const Range& range) const; + void invalidateHit(const Hit& hit); + void regenerateLayers(); + void updateScrollBars(); + void updateByMousePos(ui::Message* msg, const gfx::Point& mousePos); + Hit hitTest(ui::Message* msg, const gfx::Point& mousePos); + Hit hitTestCel(const gfx::Point& mousePos); + void setHot(const Hit& hit); + void showCel(layer_t layer, frame_t frame); + void showCurrentCel(); + void cleanClk(); + gfx::Size getScrollableSize() const; + gfx::Point getMaxScrollablePos() const; + layer_t getLayerIndex(const Layer* layer) const; + bool isLayerActive(const layer_t layerIdx) const; + bool isFrameActive(const frame_t frame) const; + void updateStatusBar(ui::Message* msg); + void updateDropRange(const gfx::Point& pt); + void clearClipboardRange(); + + bool isCopyKeyPressed(ui::Message* msg); + + // The layer of the bottom (e.g. Background layer) + layer_t firstLayer() const { return 0; } + // The layer of the top. + layer_t lastLayer() const { return m_layers.size()-1; } + + frame_t firstFrame() const { return frame_t(0); } + frame_t lastFrame() const { return m_sprite->lastFrame(); } + + bool validLayer(layer_t layer) const { return layer >= firstLayer() && layer <= lastLayer(); } + bool validFrame(frame_t frame) const { return frame >= firstFrame() && frame <= lastFrame(); } + + int topHeight() const; + + DocumentPreferences& docPref() const; + skin::SkinTheme* skinTheme() const; + ui::ScrollBar m_hbar; ui::ScrollBar m_vbar; gfx::Rect m_viewportArea; @@ -296,6 +297,7 @@ namespace app { Layer* m_layer; frame_t m_frame; Range m_range; + Range m_startRange; Range m_dropRange; State m_state; std::vector m_layers; @@ -324,7 +326,7 @@ namespace app { // Temporal data used to move the range. struct MoveRange { - int activeRelativeLayer; + layer_t activeRelativeLayer; frame_t activeRelativeFrame; } m_moveRangeData; }; diff --git a/src/app/util/clipboard.cpp b/src/app/util/clipboard.cpp index e6c6b1a04..181888da0 100644 --- a/src/app/util/clipboard.cpp +++ b/src/app/util/clipboard.cpp @@ -348,31 +348,28 @@ void paste() DocumentRange srcRange = clipboard_range.range(); Document* srcDoc = clipboard_range.document(); Sprite* srcSpr = srcDoc->sprite(); - LayerList srcLayers = srcSpr->allBrowsableLayers(); - LayerList dstLayers = dstSpr->allBrowsableLayers(); switch (srcRange.type()) { case DocumentRange::kCels: { + Layer* dstLayer = editor->layer(); + frame_t dstFrameFirst = editor->frame(); + + DocumentRange dstRange; + dstRange.startRange(dstLayer, dstFrameFirst, DocumentRange::kCels); + for (layer_t i=1; igetPreviousInWholeHierarchy(); + if (dstLayer == nullptr) + break; + } + dstRange.endRange(dstLayer, dstFrameFirst+srcRange.frames()-1); + // We can use a document range op (copy_range) to copy/paste // cels in the same document. if (srcDoc == dstDoc) { - Timeline* timeline = App::instance()->timeline(); - DocumentRange dstRange = timeline->range(); - LayerIndex dstLayer = srcSpr->layerToIndex(editor->layer()); - frame_t dstFrame = editor->frame(); - - if (dstRange.enabled()) { - dstLayer = dstRange.layerEnd(); - dstFrame = dstRange.frameBegin(); - } - - LayerIndex dstLayer2(int(dstLayer)-srcRange.layers()+1); - dstRange.startRange(dstLayer, dstFrame, DocumentRange::kCels); - dstRange.endRange(dstLayer2, dstFrame+srcRange.frames()-1); - // This is the app::copy_range (not clipboard::copy_range()). - app::copy_range(srcDoc, srcRange, dstRange, kDocumentRangeBefore); + if (srcRange.layers() == dstRange.layers()) + app::copy_range(srcDoc, srcRange, dstRange, kDocumentRangeBefore); editor->invalidate(); return; } @@ -381,32 +378,36 @@ void paste() DocumentApi api = dstDoc->getApi(transaction); // Add extra frames if needed - frame_t dstFrameBegin = editor->frame(); - while (dstFrameBegin+srcRange.frames() > dstSpr->totalFrames()) + while (dstFrameFirst+srcRange.frames() > dstSpr->totalFrames()) api.addFrame(dstSpr, dstSpr->totalFrames()); - for (LayerIndex - i = srcRange.layerEnd(), - j = dstSpr->layerToIndex(editor->layer()); - i >= srcRange.layerBegin() && - i >= LayerIndex(0) && - j >= LayerIndex(0); --i, --j) { + auto srcIt = srcRange.selectedLayers().begin(); + auto dstIt = dstRange.selectedLayers().begin(); + auto srcEnd = srcRange.selectedLayers().end(); + auto dstEnd = dstRange.selectedLayers().end(); + + for (; srcIt != srcEnd && dstIt != dstEnd; ++srcIt, ++dstIt) { + auto srcLayer = *srcIt; + auto dstLayer = *dstIt; + + if (!srcLayer->isImage() || + !dstLayer->isImage()) + continue; + // Maps a linked Cel in the original sprite with its // corresponding copy in the new sprite. In this way // we can. std::map relatedCels; - for (frame_t frame = srcRange.frameBegin(), - dstFrame = dstFrameBegin; - frame <= srcRange.frameEnd(); - ++frame, ++dstFrame) { - Cel* srcCel = srcLayers[i]->cel(frame); + frame_t dstFrame = dstFrameFirst; + for (frame_t srcFrame : srcRange.selectedFrames()) { + Cel* srcCel = srcLayer->cel(srcFrame); Cel* srcLink = nullptr; if (srcCel && srcCel->image()) { bool createCopy = true; - if (dstLayers[j]->isContinuous() && + if (dstLayer->isContinuous() && srcCel->links()) { srcLink = srcCel->link(); if (!srcLink) @@ -419,28 +420,29 @@ void paste() // Create a link from dstRelated api.copyCel( - static_cast(dstLayers[j]), dstRelated->frame(), - static_cast(dstLayers[j]), dstFrame); + static_cast(dstLayer), dstRelated->frame(), + static_cast(dstLayer), dstFrame); } } } if (createCopy) { api.copyCel( - static_cast(srcLayers[i]), frame, - static_cast(dstLayers[j]), dstFrame); + static_cast(srcLayer), srcFrame, + static_cast(dstLayer), dstFrame); if (srcLink) - relatedCels[srcLink] = dstLayers[j]->cel(dstFrame); + relatedCels[srcLink] = dstLayer->cel(dstFrame); } } else { - Cel* dstCel = dstLayers[j]->cel(dstFrame); + Cel* dstCel = dstLayer->cel(dstFrame); if (dstCel) api.clearCel(dstCel); } - } + ++dstFrame; + } } transaction.commit(); @@ -449,42 +451,49 @@ void paste() } case DocumentRange::kFrames: { - Transaction transaction(UIContext::instance(), "Paste Frames"); - DocumentApi api = dstDoc->getApi(transaction); frame_t dstFrame = editor->frame(); + // We use a DocumentRange operation to copy frames inside + // the same sprite. if (srcSpr == dstSpr) { - if (srcRange.inRange(dstFrame)) - dstFrame = srcRange.frameEnd()+1; + DocumentRange dstRange; + dstRange.startRange(nullptr, dstFrame, DocumentRange::kFrames); + dstRange.endRange(nullptr, dstFrame); + app::copy_range(srcDoc, srcRange, dstRange, kDocumentRangeBefore); + break; } - frame_t srcFrame = srcRange.frameBegin(); - for (frame_t frame = srcRange.frameBegin(); frame <= srcRange.frameEnd(); ++frame) { + Transaction transaction(UIContext::instance(), "Paste Frames"); + DocumentApi api = dstDoc->getApi(transaction); + + auto srcLayers = srcSpr->allBrowsableLayers(); + auto dstLayers = dstSpr->allBrowsableLayers(); + + for (frame_t srcFrame : srcRange.selectedFrames()) { api.addEmptyFrame(dstSpr, dstFrame); - - // If we are copying frames from/to the same sprite, we - // have to adjust the source frame. - if (srcSpr == dstSpr) { - if (dstFrame < srcFrame) - ++srcFrame; - } - api.setFrameDuration(dstSpr, dstFrame, srcSpr->frameDuration(srcFrame)); - for (LayerIndex - i = LayerIndex(srcLayers.size()-1), - j = LayerIndex(dstLayers.size()-1); - i >= LayerIndex(0) && - j >= LayerIndex(0); --i, --j) { - Cel* cel = static_cast(srcLayers[i])->cel(srcFrame); + 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(srcLayer)->cel(srcFrame); if (cel && cel->image()) { api.copyCel( - static_cast(srcLayers[i]), srcFrame, - static_cast(dstLayers[j]), dstFrame); + static_cast(srcLayer), srcFrame, + static_cast(dstLayer), dstFrame); } } - ++srcFrame; ++dstFrame; } @@ -500,39 +509,49 @@ void paste() Transaction transaction(UIContext::instance(), "Paste Layers"); DocumentApi api = dstDoc->getApi(transaction); + // Remove children if their parent is selected so we only + // copy the parent. + SelectedLayers srcLayersSet = srcRange.selectedLayers(); + srcLayersSet.removeChildrenIfParentIsSelected(); + LayerList srcLayers = srcLayersSet.toLayerList(); + // Expand frames of dstDoc if it's needed. - frame_t maxFrame(0); - for (LayerIndex i = srcRange.layerBegin(); - i <= srcRange.layerEnd() && - i < LayerIndex(srcLayers.size()); ++i) { - Cel* lastCel = static_cast(srcLayers[i])->getLastCel(); + frame_t maxFrame = 0; + for (Layer* srcLayer : srcLayers) { + if (!srcLayer->isImage()) + continue; + + Cel* lastCel = static_cast(srcLayer)->getLastCel(); if (lastCel && maxFrame < lastCel->frame()) maxFrame = lastCel->frame(); } while (dstSpr->totalFrames() < maxFrame+1) api.addEmptyFrame(dstSpr, dstSpr->totalFrames()); - for (LayerIndex i = srcRange.layerBegin(); i <= srcRange.layerEnd(); ++i) { + for (Layer* srcLayer : srcLayers) { Layer* afterThis; - if (srcLayers[i]->isBackground() && - !dstDoc->sprite()->backgroundLayer()) { + if (srcLayer->isBackground() && !dstDoc->sprite()->backgroundLayer()) afterThis = nullptr; - } else afterThis = dstSpr->root()->lastLayer(); - LayerImage* newLayer = new LayerImage(dstSpr); + Layer* newLayer = nullptr; + if (srcLayer->isImage()) + newLayer = new LayerImage(dstSpr); + else if (srcLayer->isGroup()) + newLayer = new LayerGroup(dstSpr); + else + continue; + api.addLayer(dstSpr->root(), newLayer, afterThis); - srcDoc->copyLayerContent( - srcLayers[i], dstDoc, newLayer); + srcDoc->copyLayerContent(srcLayer, dstDoc, newLayer); } transaction.commit(); editor->invalidate(); break; } - } break; } diff --git a/src/app/util/range_utils.cpp b/src/app/util/range_utils.cpp index 3ddab85aa..5475bbe7d 100644 --- a/src/app/util/range_utils.cpp +++ b/src/app/util/range_utils.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -34,16 +34,12 @@ CelList get_unique_cels(Sprite* sprite, const DocumentRange& inrange) std::set visited; - for (LayerIndex layerIdx = range.layerBegin(); layerIdx <= range.layerEnd(); ++layerIdx) { - Layer* layer = sprite->indexToLayer(layerIdx); + for (Layer* layer : range.selectedLayers()) { if (!layer || !layer->isImage()) continue; LayerImage* layerImage = static_cast(layer); - for (frame_t frame = range.frameEnd(), - begin = range.frameBegin()-1; - frame != begin; - --frame) { + for (frame_t frame : range.selectedFrames()) { Cel* cel = layerImage->cel(frame); if (!cel) continue; diff --git a/src/doc/CMakeLists.txt b/src/doc/CMakeLists.txt index 847e4170f..6985030a2 100644 --- a/src/doc/CMakeLists.txt +++ b/src/doc/CMakeLists.txt @@ -42,7 +42,6 @@ add_library(doc-lib image_io.cpp images_collector.cpp layer.cpp - layer_index.cpp layer_io.cpp layer_list.cpp mask.cpp diff --git a/src/doc/cels_range.cpp b/src/doc/cels_range.cpp index d2a0d41e6..79582fbfc 100644 --- a/src/doc/cels_range.cpp +++ b/src/doc/cels_range.cpp @@ -17,29 +17,37 @@ namespace doc { CelsRange::CelsRange(const Sprite* sprite, - frame_t first, frame_t last, Flags flags) - : m_begin(sprite, first, last, flags) - , m_end() + const SelectedFrames& selFrames, + const Flags flags) + : m_selFrames(selFrames) + , m_begin(sprite, m_selFrames, flags) + , m_end(m_selFrames) { } -CelsRange::iterator::iterator() +CelsRange::iterator::iterator(const SelectedFrames& selFrames) : m_cel(nullptr) + , m_selFrames(selFrames) + , m_frameIterator(selFrames.begin()) { } -CelsRange::iterator::iterator(const Sprite* sprite, frame_t first, frame_t last, CelsRange::Flags flags) +CelsRange::iterator::iterator(const Sprite* sprite, + const SelectedFrames& selFrames, + const CelsRange::Flags flags) : m_cel(nullptr) - , m_first(first) - , m_last(last) + , m_selFrames(selFrames) + , m_frameIterator(selFrames.begin()) , m_flags(flags) { // Get first cel Layer* layer = sprite->root()->firstLayer(); while (layer && !m_cel) { if (layer->isImage()) { - for (frame_t f=first; f<=last; ++f) { - m_cel = layer->cel(f); + m_frameIterator = m_selFrames.begin(); + auto endFrame = m_selFrames.end(); + for (; m_frameIterator!=endFrame; ++m_frameIterator) { + m_cel = layer->cel(*m_frameIterator); if (m_cel) break; } @@ -58,15 +66,16 @@ CelsRange::iterator& CelsRange::iterator::operator++() if (!m_cel) return *this; - // Get next cel - Layer* layer = m_cel->layer(); - frame_t first = m_cel->frame()+1; - m_cel = nullptr; + auto endFrame = m_selFrames.end(); + if (m_frameIterator != endFrame) + ++m_frameIterator; + Layer* layer = m_cel->layer(); + m_cel = nullptr; while (layer && !m_cel) { if (layer->isImage()) { - for (frame_t f=first; f<=m_last; ++f) { - m_cel = layer->cel(f); + for (; m_frameIterator!=endFrame; ++m_frameIterator) { + m_cel = layer->cel(*m_frameIterator); if (m_cel) { if (m_flags == CelsRange::UNIQUE) { if (m_visited.find(m_cel->data()->id()) == m_visited.end()) { @@ -84,7 +93,7 @@ CelsRange::iterator& CelsRange::iterator::operator++() if (!m_cel) { layer = layer->getNextInWholeHierarchy(); - first = m_first; + m_frameIterator = m_selFrames.begin(); } } return *this; diff --git a/src/doc/cels_range.h b/src/doc/cels_range.h index c12152204..ecb057d06 100644 --- a/src/doc/cels_range.h +++ b/src/doc/cels_range.h @@ -10,12 +10,15 @@ #include "doc/frame.h" #include "doc/object_id.h" +#include "doc/selected_frames.h" #include namespace doc { + class Cel; class Layer; + class SelectedFrames; class Sprite; class CelsRange { @@ -26,12 +29,15 @@ namespace doc { }; CelsRange(const Sprite* sprite, - frame_t first, frame_t last, Flags flags = ALL); + const SelectedFrames& selFrames, + const Flags flags = ALL); class iterator { public: - iterator(); - iterator(const Sprite* sprite, frame_t first, frame_t last, Flags flags); + iterator(const SelectedFrames& selFrames); + iterator(const Sprite* sprite, + const SelectedFrames& selFrames, + const Flags flags); bool operator==(const iterator& other) const { return m_cel == other.m_cel; @@ -49,7 +55,8 @@ namespace doc { private: Cel* m_cel; - frame_t m_first, m_last; + const SelectedFrames& m_selFrames; + SelectedFrames::const_iterator m_frameIterator; Flags m_flags; std::set m_visited; }; @@ -58,6 +65,7 @@ namespace doc { iterator end() { return m_end; } private: + SelectedFrames m_selFrames; iterator m_begin, m_end; }; diff --git a/src/doc/layer.cpp b/src/doc/layer.cpp index 9c6ba8ff3..e0f5635d9 100644 --- a/src/doc/layer.cpp +++ b/src/doc/layer.cpp @@ -424,14 +424,13 @@ void LayerGroup::removeLayer(Layer* layer) void LayerGroup::insertLayer(Layer* layer, Layer* after) { + auto after_it = m_layers.begin(); if (after) { - auto after_it = std::find(m_layers.begin(), m_layers.end(), after); - ASSERT(after_it != m_layers.end()); - after_it++; - m_layers.insert(after_it, layer); + after_it = std::find(m_layers.begin(), m_layers.end(), after); + if (after_it != m_layers.end()) + ++after_it; } - else - m_layers.insert(m_layers.begin(), layer); + m_layers.insert(after_it, layer); layer->setParent(this); } diff --git a/src/doc/layer_index.cpp b/src/doc/layer_index.cpp deleted file mode 100644 index 0b1bdb828..000000000 --- a/src/doc/layer_index.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Aseprite Document Library -// Copyright (c) 2001-2014 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "doc/layer_index.h" - -namespace doc { - -const LayerIndex LayerIndex::NoLayer(-1); - -} // namespace doc diff --git a/src/doc/layer_index.h b/src/doc/layer_index.h deleted file mode 100644 index af3393c65..000000000 --- a/src/doc/layer_index.h +++ /dev/null @@ -1,54 +0,0 @@ -// Aseprite Document Library -// Copyright (c) 2001-2014 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#ifndef DOC_LAYER_INDEX_H_INCLUDED -#define DOC_LAYER_INDEX_H_INCLUDED -#pragma once - -namespace doc { - - class LayerIndex { - public: - static const LayerIndex NoLayer; - - LayerIndex() : m_value(0) { } - explicit LayerIndex(int value) : m_value(value) { } - - LayerIndex next(int i = 1) const { return LayerIndex(m_value+i); }; - LayerIndex previous(int i = 1) const { return LayerIndex(m_value-i); }; - - operator int() { return m_value; } - operator const int() const { return m_value; } - - LayerIndex& operator=(const LayerIndex& o) { m_value = o.m_value; return *this; } - LayerIndex& operator++() { ++m_value; return *this; } - LayerIndex& operator--() { --m_value; return *this; } - LayerIndex operator++(int) { LayerIndex old(*this); ++m_value; return old; } - LayerIndex operator--(int) { LayerIndex old(*this); --m_value; return old; } - LayerIndex& operator+=(const LayerIndex& o) { m_value += o.m_value; return *this; } - LayerIndex& operator-=(const LayerIndex& o) { m_value -= o.m_value; return *this; } - bool operator<(const LayerIndex& o) const { return m_value < o.m_value; } - bool operator>(const LayerIndex& o) const { return m_value > o.m_value; } - bool operator<=(const LayerIndex& o) const { return m_value <= o.m_value; } - bool operator>=(const LayerIndex& o) const { return m_value >= o.m_value; } - bool operator==(const LayerIndex& o) const { return m_value == o.m_value; } - bool operator!=(const LayerIndex& o) const { return m_value != o.m_value; } - - private: - int m_value; - }; - - inline LayerIndex operator+(const LayerIndex& x, const LayerIndex& y) { - return LayerIndex((int)x + (int)y); - } - - inline LayerIndex operator-(const LayerIndex& x, const LayerIndex& y) { - return LayerIndex((int)x - (int)y); - } - -} // namespace doc - -#endif diff --git a/src/doc/sprite.cpp b/src/doc/sprite.cpp index d94034186..00e8718f8 100644 --- a/src/doc/sprite.cpp +++ b/src/doc/sprite.cpp @@ -29,9 +29,6 @@ namespace doc { -static Layer* index2layer(const Layer* layer, const LayerIndex& index, int* index_count); -static LayerIndex layer2index(const Layer* layer, const Layer* find_layer, int* index_count); - ////////////////////////////////////////////////////////////////////// // Constructors/Destructor @@ -212,11 +209,6 @@ LayerImage* Sprite::backgroundLayer() const return NULL; } -LayerIndex Sprite::firstLayer() const -{ - return LayerIndex(0); -} - Layer* Sprite::firstBrowsableLayer() const { Layer* layer = root()->firstLayer(); @@ -225,31 +217,6 @@ Layer* Sprite::firstBrowsableLayer() const return layer; } -LayerIndex Sprite::lastLayer() const -{ - return LayerIndex(root()->layersCount()-1); -} - -Layer* Sprite::layer(int layerIndex) const -{ - return indexToLayer(LayerIndex(layerIndex)); -} - -Layer* Sprite::indexToLayer(LayerIndex index) const -{ - if (index < LayerIndex(0)) - return NULL; - - int index_count = -1; - return index2layer(root(), index, &index_count); -} - -LayerIndex Sprite::layerToIndex(const Layer* layer) const -{ - int index_count = -1; - return layer2index(root(), layer, &index_count); -} - layer_t Sprite::allLayersCount() const { return root()->allLayersCount(); @@ -554,64 +521,28 @@ LayerList Sprite::allBrowsableLayers() const CelsRange Sprite::cels() const { - return CelsRange(this, frame_t(0), lastFrame()); + SelectedFrames selFrames; + selFrames.insert(0, lastFrame()); + return CelsRange(this, selFrames); } CelsRange Sprite::cels(frame_t frame) const { - return CelsRange(this, frame, frame); + SelectedFrames selFrames; + selFrames.insert(frame); + return CelsRange(this, selFrames); } CelsRange Sprite::uniqueCels() const { - return CelsRange(this, frame_t(0), lastFrame(), CelsRange::UNIQUE); + SelectedFrames selFrames; + selFrames.insert(0, lastFrame()); + return CelsRange(this, selFrames, CelsRange::UNIQUE); } -CelsRange Sprite::uniqueCels(frame_t from, frame_t to) const +CelsRange Sprite::uniqueCels(const SelectedFrames& selFrames) const { - return CelsRange(this, from, to, CelsRange::UNIQUE); -} - -////////////////////////////////////////////////////////////////////// - -static Layer* index2layer(const Layer* layer, const LayerIndex& index, int* index_count) -{ - if (index == *index_count) - return (Layer*)layer; - else { - (*index_count)++; - - if (layer->isGroup()) { - Layer *found; - - for (const Layer* child : static_cast(layer)->layers()) { - if ((found = index2layer(child, index, index_count))) - return found; - } - } - - return NULL; - } -} - -static LayerIndex layer2index(const Layer* layer, const Layer* find_layer, int* index_count) -{ - if (layer == find_layer) - return LayerIndex(*index_count); - else { - (*index_count)++; - - if (layer->isGroup()) { - int found; - - for (const Layer* child : static_cast(layer)->layers()) { - if ((found = layer2index(child, find_layer, index_count)) >= 0) - return LayerIndex(found); - } - } - - return LayerIndex(-1); - } + return CelsRange(this, selFrames, CelsRange::UNIQUE); } } // namespace doc diff --git a/src/doc/sprite.h b/src/doc/sprite.h index 577c4b3bc..fd1428ba7 100644 --- a/src/doc/sprite.h +++ b/src/doc/sprite.h @@ -16,7 +16,6 @@ #include "doc/frame_tags.h" #include "doc/image_ref.h" #include "doc/image_spec.h" -#include "doc/layer_index.h" #include "doc/object.h" #include "doc/pixel_format.h" #include "doc/pixel_ratio.h" @@ -37,6 +36,7 @@ namespace doc { class Palette; class Remap; class RgbMap; + class SelectedFrames; typedef std::vector PalettesList; @@ -91,14 +91,7 @@ namespace doc { LayerGroup* root() const { return m_root; } LayerImage* backgroundLayer() const; - - LayerIndex firstLayer() const; Layer* firstBrowsableLayer() const; - LayerIndex lastLayer() const; - - Layer* layer(int layerIndex) const; - Layer* indexToLayer(LayerIndex index) const; - LayerIndex layerToIndex(const Layer* layer) const; layer_t allLayersCount() const; //////////////////////////////////////// @@ -159,7 +152,7 @@ namespace doc { CelsRange cels() const; CelsRange cels(frame_t frame) const; CelsRange uniqueCels() const; - CelsRange uniqueCels(frame_t from, frame_t to) const; + CelsRange uniqueCels(const SelectedFrames& selFrames) const; private: Document* m_document;