Move Timeline logic to drag-and-drop ranges to app::DocumentRange/move_range/copy_range

This fixes issue 433.
This commit is contained in:
David Capello 2014-08-07 20:19:31 -03:00
parent 0f99c78174
commit 9fd60f1119
17 changed files with 1224 additions and 350 deletions

View File

@ -105,6 +105,8 @@ add_library(app-lib
document_api.cpp
document_exporter.cpp
document_location.cpp
document_range.cpp
document_range_ops.cpp
document_undo.cpp
file/ase_format.cpp
file/bmp_format.cpp

View File

@ -399,8 +399,12 @@ int app_get_color_to_clear_layer(Layer* layer)
app::Color color;
// The `Background' is erased with the `Background Color'
if (layer->isBackground())
color = ColorBar::instance()->getBgColor();
if (layer->isBackground()) {
if (ColorBar::instance())
color = ColorBar::instance()->getBgColor();
else
color = app::Color::fromRgb(0, 0, 0); // TODO get background color color from doc::Settings
}
else // All transparent layers are cleared with the mask color
color = app::Color::fromMask();

View File

@ -71,7 +71,7 @@ void DuplicateLayerCommand::onExecute(Context* context)
{
UndoTransaction undo(writer.context(), "Layer Duplication");
LayerImage* sourceLayer = static_cast<LayerImage*>(writer.layer());
api.duplicateLayer(sourceLayer, sourceLayer);
api.duplicateLayerAfter(sourceLayer, sourceLayer);
undo.commit();
}

View File

@ -58,16 +58,14 @@ void Context::sendDocumentToTop(doc::Document* document)
app::Document* Context::activeDocument() const
{
DocumentLocation location;
onGetActiveLocation(&location);
ASSERT(location.document() == doc::Context::activeDocument());
return location.document();
return static_cast<app::Document*>(doc::Context::activeDocument());
}
DocumentLocation Context::activeLocation() const
{
DocumentLocation location;
onGetActiveLocation(&location);
ASSERT(location.document() == doc::Context::activeDocument());
return location;
}

View File

@ -407,7 +407,7 @@ void DocumentApi::moveFrame(Sprite* sprite, FrameNumber frame, FrameNumber befor
frame >= 0 &&
frame <= sprite->lastFrame() &&
beforeFrame >= 0 &&
beforeFrame <= sprite->lastFrame()) {
beforeFrame <= sprite->lastFrame().next()) {
// Change the frame-lengths...
int frlen_aux = sprite->getFrameDuration(frame);
@ -817,6 +817,14 @@ void DocumentApi::restackLayerAfter(Layer* layer, Layer* afterThis)
m_document->notifyObservers<doc::DocumentEvent&>(&doc::DocumentObserver::onLayerRestacked, ev);
}
void DocumentApi::restackLayerBefore(Layer* layer, Layer* beforeThis)
{
LayerIndex beforeThisIdx = layer->sprite()->layerToIndex(beforeThis);
LayerIndex afterThisIdx = beforeThisIdx.previous();
restackLayerAfter(layer, layer->sprite()->indexToLayer(afterThisIdx));
}
void DocumentApi::cropLayer(Layer* layer, int x, int y, int w, int h, color_t bgcolor)
{
if (!layer->isImage())
@ -1016,7 +1024,7 @@ void DocumentApi::flattenLayers(Sprite* sprite, color_t bgcolor)
removeLayer(*it);
}
void DocumentApi::duplicateLayer(Layer* sourceLayer, Layer* afterLayer)
void DocumentApi::duplicateLayerAfter(Layer* sourceLayer, Layer* afterLayer)
{
base::UniquePtr<LayerImage> newLayerPtr(new LayerImage(sourceLayer->sprite()));
@ -1030,6 +1038,14 @@ void DocumentApi::duplicateLayer(Layer* sourceLayer, Layer* afterLayer)
newLayerPtr.release();
}
void DocumentApi::duplicateLayerBefore(Layer* sourceLayer, Layer* beforeLayer)
{
LayerIndex beforeThisIdx = sourceLayer->sprite()->layerToIndex(beforeLayer);
LayerIndex afterThisIdx = beforeThisIdx.previous();
duplicateLayerAfter(sourceLayer, sourceLayer->sprite()->indexToLayer(afterThisIdx));
}
// Adds a new image in the stock. Returns the image index in the
// stock.
int DocumentApi::addImageInStock(Sprite* sprite, Image* image)

View File

@ -95,12 +95,14 @@ namespace app {
void addLayer(LayerFolder* folder, Layer* newLayer, Layer* afterThis);
void removeLayer(Layer* layer);
void restackLayerAfter(Layer* layer, Layer* afterThis);
void restackLayerBefore(Layer* layer, Layer* beforeThis);
void cropLayer(Layer* layer, int x, int y, int w, int h, color_t bgcolor);
void displaceLayers(Layer* layer, int dx, int dy);
void backgroundFromLayer(LayerImage* layer, color_t bgcolor);
void layerFromBackground(Layer* layer);
void flattenLayers(Sprite* sprite, color_t bgcolor);
void duplicateLayer(Layer* sourceLayer, Layer* afterLayer);
void duplicateLayerAfter(Layer* sourceLayer, Layer* afterLayer);
void duplicateLayerBefore(Layer* sourceLayer, Layer* beforeLayer);
// Images stock API
int addImageInStock(Sprite* sprite, Image* image);

View File

@ -0,0 +1,89 @@
/* Aseprite
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/document_range.h"
namespace app {
using namespace raster;
void DocumentRange::startRange(LayerIndex layer, FrameNumber frame, Type type)
{
m_type = type;
m_layerBegin = m_layerEnd = layer;
m_frameBegin = m_frameEnd = frame;
}
void DocumentRange::endRange(LayerIndex layer, FrameNumber frame)
{
ASSERT(enabled());
m_layerEnd = layer;
m_frameEnd = frame;
}
void DocumentRange::disableRange()
{
m_type = kNone;
}
bool DocumentRange::inRange(LayerIndex layer) const
{
if (enabled())
return (layer >= layerBegin() && layer <= layerEnd());
else
return false;
}
bool DocumentRange::inRange(FrameNumber frame) const
{
if (enabled())
return (frame >= frameBegin() && frame <= frameEnd());
else
return false;
}
bool DocumentRange::inRange(LayerIndex layer, FrameNumber 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(FrameNumber frames)
{
if (m_frameBegin <= m_frameEnd) m_frameEnd = (m_frameBegin + frames).previous();
else m_frameBegin = (m_frameEnd + frames).previous();
}
void DocumentRange::displace(int layerDelta, int frameDelta)
{
m_layerBegin += LayerIndex(layerDelta);
m_layerEnd += LayerIndex(layerDelta);
m_frameBegin += FrameNumber(frameDelta);
m_frameEnd += FrameNumber(frameDelta);
}
} // namespace app

74
src/app/document_range.h Normal file
View File

@ -0,0 +1,74 @@
/* Aseprite
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef APP_DOCUMENT_RANGE_H_INCLUDED
#define APP_DOCUMENT_RANGE_H_INCLUDED
#pragma once
#include "raster/frame_number.h"
#include "raster/layer_index.h"
#include <algorithm>
namespace app {
using namespace raster;
class DocumentRange {
public:
enum Type { kNone, kCels, kFrames, kLayers };
DocumentRange() : m_type(kNone) { }
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); }
FrameNumber frameBegin() const { return std::min(m_frameBegin, m_frameEnd); }
FrameNumber frameEnd() const { return std::max(m_frameBegin, m_frameEnd); }
int layers() const { return layerEnd() - layerBegin() + 1; }
FrameNumber frames() const { return (frameEnd() - frameBegin()).next(); }
void setLayers(int layers);
void setFrames(FrameNumber frames);
void displace(int layerDelta, int frameDelta);
bool inRange(LayerIndex layer) const;
bool inRange(FrameNumber frame) const;
bool inRange(LayerIndex layer, FrameNumber frame) const;
void startRange(LayerIndex layer, FrameNumber frame, Type type);
void endRange(LayerIndex layer, FrameNumber 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();
}
private:
Type m_type;
LayerIndex m_layerBegin;
LayerIndex m_layerEnd;
FrameNumber m_frameBegin;
FrameNumber m_frameEnd;
};
} // namespace app
#endif

View File

@ -0,0 +1,332 @@
/* Aseprite
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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"
#include "app/undo_transaction.h"
#include "raster/layer.h"
#include "raster/sprite.h"
#include <stdexcept>
namespace app {
enum Op { Move, Copy };
static DocumentRange drop_range_op(
Document* doc, Op op, const DocumentRange& from,
DocumentRangePlace place, const DocumentRange& to)
{
Sprite* sprite = doc->sprite();
// 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))
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;
// We cannot move the background
for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i)
if (sprite->indexToLayer(i)->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 something below the background layer");
}
}
break;
}
const char* undoLabel = NULL;
switch (op) {
case Move: undoLabel = "Move Range"; break;
case Copy: undoLabel = "Copy Range"; break;
}
DocumentRange resultRange;
{
const app::Context* context = static_cast<app::Context*>(doc->context());
const ContextReader reader(context);
ContextWriter writer(reader);
UndoTransaction undo(writer.context(), undoLabel, undo::ModifyDocument);
DocumentApi api = doc->getApi();
// TODO Try to add the range with just one call to DocumentApi
// methods, to avoid generating a lot of SetCelFrame undoers (see
// DocumentApi::setCelFramePosition).
switch (from.type()) {
case DocumentRange::kCels:
{
std::vector<Layer*> layers;
sprite->getLayersList(layers);
int srcLayerBegin, srcLayerStep, srcLayerEnd;
int dstLayerBegin, dstLayerStep;
FrameNumber srcFrameBegin, srcFrameStep, srcFrameEnd;
FrameNumber dstFrameBegin, dstFrameStep;
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;
}
if (to.frameBegin() <= from.frameBegin()) {
srcFrameBegin = from.frameBegin();
srcFrameStep = FrameNumber(1);
srcFrameEnd = from.frameEnd().next();
dstFrameBegin = to.frameBegin();
dstFrameStep = FrameNumber(1);
}
else {
srcFrameBegin = from.frameEnd();
srcFrameStep = FrameNumber(-1);
srcFrameEnd = from.frameBegin().previous();
dstFrameBegin = to.frameEnd();
dstFrameStep = FrameNumber(-1);
}
for (int srcLayerIdx = srcLayerBegin,
dstLayerIdx = dstLayerBegin; srcLayerIdx != srcLayerEnd; ) {
for (FrameNumber srcFrame = srcFrameBegin,
dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) {
LayerImage* srcLayer = static_cast<LayerImage*>(layers[srcLayerIdx]);
LayerImage* dstLayer = static_cast<LayerImage*>(layers[dstLayerIdx]);
color_t bgcolor = app_get_color_to_clear_layer(dstLayer);
switch (op) {
case Move: api.moveCel(sprite, srcLayer, dstLayer, srcFrame, dstFrame, bgcolor); break;
case Copy: api.copyCel(sprite, srcLayer, dstLayer, srcFrame, dstFrame, bgcolor); break;
}
srcFrame += srcFrameStep;
dstFrame += dstFrameStep;
}
srcLayerIdx += srcLayerStep;
dstLayerIdx += dstLayerStep;
}
resultRange = to;
}
break;
case DocumentRange::kFrames:
{
FrameNumber srcFrameBegin, srcFrameStep, srcFrameEnd;
FrameNumber dstFrameBegin, dstFrameStep;
switch (op) {
case Move:
if (place == kDocumentRangeBefore) {
if (to.frameBegin() <= from.frameBegin()) {
srcFrameBegin = from.frameBegin();
srcFrameStep = FrameNumber(1);
srcFrameEnd = from.frameEnd().next();
dstFrameBegin = to.frameBegin();
dstFrameStep = FrameNumber(1);
}
else {
srcFrameBegin = from.frameEnd();
srcFrameStep = FrameNumber(-1);
srcFrameEnd = from.frameBegin().previous();
dstFrameBegin = to.frameBegin();
dstFrameStep = FrameNumber(-1);
}
}
else if (place == kDocumentRangeAfter) {
if (to.frameEnd() <= from.frameBegin()) {
srcFrameBegin = from.frameBegin();
srcFrameStep = FrameNumber(1);
srcFrameEnd = from.frameEnd().next();
dstFrameBegin = to.frameEnd();
dstFrameStep = FrameNumber(1);
}
else {
srcFrameBegin = from.frameEnd();
srcFrameStep = FrameNumber(-1);
srcFrameEnd = from.frameBegin().previous();
dstFrameBegin = to.frameEnd().next();
dstFrameStep = FrameNumber(-1);
}
}
break;
case Copy:
if (to.frameBegin() <= from.frameBegin()) {
srcFrameBegin = from.frameBegin();
srcFrameStep = FrameNumber(2);
srcFrameEnd = from.frameBegin().next(2*from.frames());
dstFrameBegin = to.frameBegin();
dstFrameStep = FrameNumber(1);
}
else {
srcFrameBegin = from.frameEnd();
srcFrameStep = FrameNumber(-1);
srcFrameEnd = from.frameBegin().previous();
dstFrameBegin = to.frameBegin();
dstFrameStep = FrameNumber(0);
}
if (place == kDocumentRangeAfter)
dstFrameBegin = dstFrameBegin.next();
break;
}
for (FrameNumber 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, FrameNumber(to.frameBegin()), from.type());
resultRange.endRange(LayerIndex::NoLayer, FrameNumber(to.frameBegin()+from.frames()-1));
}
else if (place == kDocumentRangeAfter) {
resultRange.startRange(LayerIndex::NoLayer, FrameNumber(to.frameEnd()+1), from.type());
resultRange.endRange(LayerIndex::NoLayer, FrameNumber(to.frameEnd()+1+from.frames()-1));
}
if (op == Move && from.frameBegin() < to.frameBegin())
resultRange.displace(0, -from.frames());
}
break;
case DocumentRange::kLayers:
{
std::vector<Layer*> layers;
sprite->getLayersList(layers);
if (layers.empty())
break;
Layer* firstLayer = layers.front();
Layer* lastLayer = layers.back();
switch (op) {
case Move:
if (place == kDocumentRangeBefore) {
for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) {
api.restackLayerBefore(
layers[i],
layers[to.layerBegin()]);
}
}
else if (place == kDocumentRangeAfter) {
for (LayerIndex i = from.layerEnd(); i >= from.layerBegin(); --i) {
api.restackLayerAfter(
layers[i],
layers[to.layerEnd()]);
}
}
break;
case Copy:
if (place == kDocumentRangeBefore) {
for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) {
api.duplicateLayerBefore(
layers[i],
layers[to.layerBegin()]);
}
}
else if (place == kDocumentRangeAfter) {
for (LayerIndex i = from.layerEnd(); i >= from.layerBegin(); --i) {
api.duplicateLayerAfter(
layers[i],
layers[to.layerEnd()]);
}
}
break;
}
if (place == kDocumentRangeBefore) {
resultRange.startRange(LayerIndex(to.layerBegin()), FrameNumber(-1), from.type());
resultRange.endRange(LayerIndex(to.layerBegin()+from.layers()-1), FrameNumber(-1));
}
else if (place == kDocumentRangeAfter) {
resultRange.startRange(LayerIndex(to.layerEnd()+1), FrameNumber(-1), from.type());
resultRange.endRange(LayerIndex(to.layerEnd()+1+from.layers()-1), FrameNumber(-1));
}
if (op == Move && from.layerBegin() < to.layerBegin())
resultRange.displace(-from.layers(), 0);
}
break;
}
undo.commit();
}
return resultRange;
}
DocumentRange move_range(Document* doc, const DocumentRange& from, const DocumentRange& to, DocumentRangePlace place)
{
return drop_range_op(doc, Move, from, place, to);
}
DocumentRange copy_range(Document* doc, const DocumentRange& from, const DocumentRange& to, DocumentRangePlace place)
{
return drop_range_op(doc, Copy, from, place, to);
}
} // namespace app

View File

@ -0,0 +1,42 @@
/* Aseprite
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef APP_DOCUMENT_RANGE_OPS_H_INCLUDED
#define APP_DOCUMENT_RANGE_OPS_H_INCLUDED
#pragma once
#include <vector>
namespace app {
class Document;
class DocumentRange;
enum DocumentRangePlace {
kDocumentRangeBefore,
kDocumentRangeAfter,
};
// These functions returns the new location of the "from" range or
// throws an std::runtime_error() in case that the operation cannot
// be done. (E.g. the background layer cannot be moved.)
DocumentRange move_range(Document* doc, const DocumentRange& from, const DocumentRange& to, DocumentRangePlace place);
DocumentRange copy_range(Document* doc, const DocumentRange& from, const DocumentRange& to, DocumentRangePlace place);
} // namespace app
#endif

View File

@ -0,0 +1,601 @@
/* Aseprite
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "tests/test.h"
#include "app/document.h"
#include "app/document_api.h"
#include "app/document_range.h"
#include "app/document_range_ops.h"
#include "app/document_undo.h"
#include "app/test_context.h"
#include "base/unique_ptr.h"
#include "raster/raster.h"
#include "undo/undo_history.h"
using namespace app;
using namespace raster;
typedef base::UniquePtr<Document> DocumentPtr;
#define EXPECT_LAYER_ORDER(a, b, c, d) \
EXPECT_EQ(a, sprite->indexToLayer(LayerIndex(0))); \
EXPECT_EQ(b, sprite->indexToLayer(LayerIndex(1))); \
EXPECT_EQ(c, sprite->indexToLayer(LayerIndex(2))); \
EXPECT_EQ(d, sprite->indexToLayer(LayerIndex(3)));
#define EXPECT_FRAME_ORDER(a, b, c, d) \
EXPECT_EQ((a+1), sprite->getFrameDuration(FrameNumber(0))); \
EXPECT_EQ((b+1), sprite->getFrameDuration(FrameNumber(1))); \
EXPECT_EQ((c+1), sprite->getFrameDuration(FrameNumber(2))); \
EXPECT_EQ((d+1), sprite->getFrameDuration(FrameNumber(3)));
class DocRangeOps : public ::testing::Test {
public:
DocRangeOps() {
doc.reset(static_cast<Document*>(ctx.documents().add(2, 2)));
sprite = doc->sprite();
layer1 = dynamic_cast<LayerImage*>(sprite->folder()->getFirstLayer());
layer2 = new LayerImage(sprite);
layer3 = new LayerImage(sprite);
layer4 = new LayerImage(sprite);
sprite->folder()->addLayer(layer2);
sprite->folder()->addLayer(layer3);
sprite->folder()->addLayer(layer4);
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
layer1->setName("layer1");
layer2->setName("layer2");
layer3->setName("layer3");
layer4->setName("layer4");
sprite->setTotalFrames(FrameNumber(4));
sprite->setFrameDuration(FrameNumber(0), 1); // These durations can be used to identify
sprite->setFrameDuration(FrameNumber(1), 2); // frames after a move operation
sprite->setFrameDuration(FrameNumber(2), 3);
sprite->setFrameDuration(FrameNumber(3), 4);
}
~DocRangeOps() {
doc->close();
}
protected:
TestContext ctx;
DocumentPtr doc;
Sprite* sprite;
LayerImage* layer1;
LayerImage* layer2;
LayerImage* layer3;
LayerImage* layer4;
};
inline DocumentRange range(Layer* fromLayer, int fromFrNum, Layer* toLayer, int toFrNum, DocumentRange::Type type) {
DocumentRange r;
r.startRange(fromLayer->sprite()->layerToIndex(fromLayer), FrameNumber(fromFrNum), type);
r.endRange(toLayer->sprite()->layerToIndex(toLayer), FrameNumber(toFrNum));
return r;
}
inline DocumentRange range(int fromLayer, int fromFrNum, int toLayer, int toFrNum, DocumentRange::Type type) {
DocumentRange r;
r.startRange(LayerIndex(fromLayer), FrameNumber(fromFrNum), type);
r.endRange(LayerIndex(toLayer), FrameNumber(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(int fromFrame, int toFrame) {
return range(0, fromFrame, 0, toFrame, DocumentRange::kFrames);
}
inline DocumentRange frames_range(int frame) {
return range(0, frame, 0, frame, DocumentRange::kFrames);
}
std::ostream& operator<<(std::ostream& os, const DocumentRange& range) {
return os << "{ layers: [" << range.layerBegin() << ", " << range.layerEnd() << "]"
<< ", frames: [" << range.frameBegin() << ", " << range.frameEnd() << "] }";
}
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));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer1),
move_range(doc,
layers_range(layer1),
layers_range(layer2), kDocumentRangeBefore));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer4),
move_range(doc,
layers_range(layer4),
layers_range(layer4), kDocumentRangeAfter));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer4),
move_range(doc,
layers_range(layer4),
layers_range(layer4), kDocumentRangeBefore));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer4),
move_range(doc,
layers_range(layer4),
layers_range(layer3), kDocumentRangeAfter));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->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));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer1, layer2),
move_range(doc,
layers_range(layer1, layer2),
layers_range(layer1), kDocumentRangeAfter));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer1, layer2),
move_range(doc,
layers_range(layer1, layer2),
layers_range(layer2), kDocumentRangeBefore));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer1, layer2),
move_range(doc,
layers_range(layer1, layer2),
layers_range(layer2), kDocumentRangeAfter));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer1, layer2),
move_range(doc,
layers_range(layer1, layer2),
layers_range(layer3), kDocumentRangeBefore));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer3, layer4),
move_range(doc,
layers_range(layer3, layer4),
layers_range(layer2), kDocumentRangeAfter));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer3, layer4),
move_range(doc,
layers_range(layer3, layer4),
layers_range(layer3), kDocumentRangeBefore));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer3, layer4),
move_range(doc,
layers_range(layer3, layer4),
layers_range(layer3), kDocumentRangeAfter));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer3, layer4),
move_range(doc,
layers_range(layer3, layer4),
layers_range(layer4), kDocumentRangeBefore));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(layers_range(layer3, layer4),
move_range(doc,
layers_range(layer3, layer4),
layers_range(layer4), kDocumentRangeAfter));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
// Move four layers
DocumentRangePlace places[] = { kDocumentRangeBefore, kDocumentRangeAfter };
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]));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->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]));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->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]));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
}
EXPECT_EQ(layers_range(layer1, layer4),
move_range(doc,
layers_range(layer1, layer4),
layers_range(layer1, layer4), places[i]));
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
EXPECT_FALSE(doc->getUndo()->canUndo());
}
}
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));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(0),
move_range(doc,
frames_range(0),
frames_range(1), kDocumentRangeBefore));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(3),
move_range(doc,
frames_range(3),
frames_range(3), kDocumentRangeAfter));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(3),
move_range(doc,
frames_range(3),
frames_range(3), kDocumentRangeBefore));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(3),
move_range(doc,
frames_range(3),
frames_range(2), kDocumentRangeAfter));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->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));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(0, 1),
move_range(doc,
frames_range(0, 1),
frames_range(0), kDocumentRangeAfter));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(0, 1),
move_range(doc,
frames_range(0, 1),
frames_range(1), kDocumentRangeBefore));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(0, 1),
move_range(doc,
frames_range(0, 1),
frames_range(1), kDocumentRangeAfter));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(0, 1),
move_range(doc,
frames_range(0, 1),
frames_range(2), kDocumentRangeBefore));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(2, 3),
move_range(doc,
frames_range(2, 3),
frames_range(1), kDocumentRangeAfter));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(2, 3),
move_range(doc,
frames_range(2, 3),
frames_range(2), kDocumentRangeBefore));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(2, 3),
move_range(doc,
frames_range(2, 3),
frames_range(2), kDocumentRangeAfter));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(2, 3),
move_range(doc,
frames_range(2, 3),
frames_range(3), kDocumentRangeBefore));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
EXPECT_EQ(frames_range(2, 3),
move_range(doc,
frames_range(2, 3),
frames_range(3), kDocumentRangeAfter));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
// Move four frames
DocumentRangePlace places[] = { kDocumentRangeBefore, kDocumentRangeAfter };
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]));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->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]));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->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]));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
}
EXPECT_EQ(frames_range(0, 3),
move_range(doc,
frames_range(0, 3),
frames_range(0, 3), places[i]));
EXPECT_FRAME_ORDER(0, 1, 2, 3);
EXPECT_FALSE(doc->getUndo()->canUndo());
}
}
TEST_F(DocRangeOps, MoveCelsNoOp) {
// TODO
}
TEST_F(DocRangeOps, CopyCelsNoOp) {
// TODO
}
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_LAYER_ORDER(layer2, layer1, layer3, layer4);
EXPECT_EQ(layers_range(layer1), result);
doc->getUndo()->doUndo();
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
// One layer at the bottom
result = move_range(doc,
layers_range(layer2),
layers_range(layer1), kDocumentRangeBefore);
EXPECT_LAYER_ORDER(layer2, layer1, layer3, layer4);
EXPECT_EQ(layers_range(layer2), result);
doc->getUndo()->doUndo();
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
// Try with a background
layer1->setBackground(true);
EXPECT_ANY_THROW({
move_range(doc,
layers_range(layer1),
layers_range(layer2), kDocumentRangeAfter);
});
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
layer1->setBackground(false);
// Move one layer to the top
result = move_range(doc,
layers_range(layer2),
layers_range(layer4), kDocumentRangeAfter);
EXPECT_LAYER_ORDER(layer1, layer3, layer4, layer2);
EXPECT_EQ(layers_range(layer2), result);
doc->getUndo()->doUndo();
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
// Move one layers before other
result = move_range(doc,
layers_range(layer2),
layers_range(layer4), kDocumentRangeBefore);
EXPECT_LAYER_ORDER(layer1, layer3, layer2, layer4);
EXPECT_EQ(layers_range(layer2), result);
doc->getUndo()->doUndo();
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
result = 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->getUndo()->doUndo();
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_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_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);
EXPECT_ANY_THROW({
move_range(doc,
layers_range(layer2, layer4),
layers_range(layer1), kDocumentRangeBefore);
});
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
layer1->setBackground(false);
// Move three layers at the top
result = 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->getUndo()->doUndo();
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_LAYER_ORDER(layer2, layer3, layer4, layer1);
EXPECT_EQ(layers_range(layer2, layer4), result);
doc->getUndo()->doUndo();
EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4);
}
TEST_F(DocRangeOps, MoveFrames) {
move_range(doc,
frames_range(0, 0),
frames_range(1, 1), kDocumentRangeAfter);
EXPECT_FRAME_ORDER(1, 0, 2, 3);
doc->getUndo()->doUndo();
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);
EXPECT_FRAME_ORDER(0, 2, 3, 1);
doc->getUndo()->doUndo();
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->getUndo()->doUndo();
EXPECT_FRAME_ORDER(0, 1, 2, 3);
move_range(doc,
frames_range(1, 2),
frames_range(0, 0), kDocumentRangeAfter);
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_FRAME_ORDER(1, 2, 3, 0);
doc->getUndo()->doUndo();
EXPECT_FRAME_ORDER(0, 1, 2, 3);
}
TEST_F(DocRangeOps, MoveCels) {
// TODO
}
TEST_F(DocRangeOps, CopyLayers) {
// TODO
}
TEST_F(DocRangeOps, CopyFrames) {
// TODO
}
TEST_F(DocRangeOps, CopyCels) {
// TODO
}

View File

@ -20,6 +20,7 @@
#define APP_TEST_CONTEXT_H_INCLUDED
#pragma once
#include "app/context.h"
#include "app/document_location.h"
#include "doc/settings.h"
#include "raster/layer.h"

View File

@ -31,6 +31,7 @@
#include "app/context_access.h"
#include "app/document.h"
#include "app/document_api.h"
#include "app/document_range_ops.h"
#include "app/document_undo.h"
#include "app/modules/editors.h"
#include "app/modules/gfx.h"
@ -1614,9 +1615,9 @@ void Timeline::updateStatusBar(ui::Message* msg)
case Range::kLayers: {
int layerIdx = -1;
if (m_dropTarget.vhit == DropTarget::Bottom)
layerIdx = m_dropRange.layerEnd();
else if (m_dropTarget.vhit == DropTarget::Top)
layerIdx = m_dropRange.layerBegin();
else if (m_dropTarget.vhit == DropTarget::Top)
layerIdx = m_dropRange.layerEnd();
Layer* layer = ((layerIdx >= 0 && layerIdx < (int)m_layers.size()) ? m_layers[layerIdx]: NULL);
if (layer) {
@ -1837,245 +1838,47 @@ bool Timeline::isFrameActive(FrameNumber frame) const
return m_range.inRange(frame);
}
// TODO This must be re-implemented
void Timeline::dropRange(DropOp op)
{
Range drop = m_dropRange;
bool copy = (op == Timeline::kCopy);
Range newFromRange;
DocumentRangePlace place = kDocumentRangeAfter;
// "Do nothing" cases. The user drops in the same place. (We don't
// even add the undo information.)
if (!doesDropModifySprite(drop, op))
return;
const char* undoLabel = NULL;
switch (op) {
case Timeline::kMove: undoLabel = "Move Range"; break;
case Timeline::kCopy: undoLabel = "Copy Range"; break;
switch (m_range.type()) {
case Range::kFrames:
if (m_dropTarget.hhit == DropTarget::Before)
place = kDocumentRangeBefore;
break;
case Range::kLayers:
if (m_dropTarget.vhit == DropTarget::Bottom)
place = kDocumentRangeBefore;
break;
}
const ContextReader reader(m_context);
ContextWriter writer(reader);
UndoTransaction undo(writer.context(), undoLabel, undo::ModifyDocument);
int activeRelativeLayer = getLayerIndex(m_layer) - m_range.layerBegin();
FrameNumber activeRelativeFrame = m_frame - m_range.frameBegin();
switch (drop.type()) {
case Range::kCels: dropCels(op, drop); break;
case Range::kFrames: dropFrames(op, drop); break;
case Range::kLayers: dropLayers(op, drop); break;
try {
if (copy)
newFromRange = copy_range(m_document, m_range, m_dropRange, place);
else
newFromRange = move_range(m_document, m_range, m_dropRange, place);
regenerateLayers();
m_range = newFromRange;
if (m_range.layerBegin() >= LayerIndex(0))
setLayer(m_layers[m_range.layerBegin() + activeRelativeLayer]);
if (m_range.frameBegin() >= FrameNumber(0))
setFrame(m_range.frameBegin() + activeRelativeFrame);
}
undo.commit();
regenerateLayers();
// Adjust "drop" range so we can select the same selected range that
// the user had selected.
switch (drop.type()) {
case Range::kFrames:
if (op == Timeline::kMove && m_range.frameBegin() < drop.frameBegin()) {
drop.displace(firstLayer(), -m_range.frames());
}
drop.setFrames(m_range.frames());
break;
case Range::kLayers:
if (op == Timeline::kMove && m_range.layerBegin() < drop.layerBegin()) {
drop.displace(-m_range.layers(), 0);
}
drop.setLayers(m_range.layers());
break;
catch (const std::exception& e) {
ui::Alert::show("Problem<<%s||&OK", e.what());
}
setLayer(m_layers[drop.layerBegin() + activeRelativeLayer]);
setFrame(drop.frameBegin() + activeRelativeFrame);
m_range = drop;
invalidate();
}
void Timeline::dropCels(DropOp op, const Range& drop)
{
ASSERT(validLayer(drop.layerBegin()));
ASSERT(validLayer(drop.layerEnd()));
ASSERT(validFrame(drop.frameBegin()));
ASSERT(validFrame(drop.frameEnd()));
int srcLayerBegin, srcLayerStep, srcLayerEnd;
int dstLayerBegin, dstLayerStep;
FrameNumber srcFrameBegin, srcFrameStep, srcFrameEnd;
FrameNumber dstFrameBegin, dstFrameStep;
if (drop.layerBegin() <= m_range.layerBegin()) {
srcLayerBegin = m_range.layerBegin();
srcLayerStep = 1;
srcLayerEnd = m_range.layerEnd()+1;
dstLayerBegin = drop.layerBegin();
dstLayerStep = 1;
}
else {
srcLayerBegin = m_range.layerEnd();
srcLayerStep = -1;
srcLayerEnd = m_range.layerBegin()-1;
dstLayerBegin = drop.layerEnd();
dstLayerStep = -1;
}
if (drop.frameBegin() <= m_range.frameBegin()) {
srcFrameBegin = m_range.frameBegin();
srcFrameStep = FrameNumber(1);
srcFrameEnd = m_range.frameEnd().next();
dstFrameBegin = drop.frameBegin();
dstFrameStep = FrameNumber(1);
}
else {
srcFrameBegin = m_range.frameEnd();
srcFrameStep = FrameNumber(-1);
srcFrameEnd = m_range.frameBegin().previous();
dstFrameBegin = drop.frameEnd();
dstFrameStep = FrameNumber(-1);
}
DocumentApi api = m_document->getApi();
for (int srcLayerIdx = srcLayerBegin,
dstLayerIdx = dstLayerBegin; srcLayerIdx != srcLayerEnd; ) {
for (FrameNumber srcFrame = srcFrameBegin,
dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) {
LayerImage* srcLayer = static_cast<LayerImage*>(m_layers[srcLayerIdx]);
LayerImage* dstLayer = static_cast<LayerImage*>(m_layers[dstLayerIdx]);
color_t bgcolor = app_get_color_to_clear_layer(dstLayer);
switch (op) {
case Timeline::kMove: api.moveCel(m_sprite, srcLayer, dstLayer, srcFrame, dstFrame, bgcolor); break;
case Timeline::kCopy: api.copyCel(m_sprite, srcLayer, dstLayer, srcFrame, dstFrame, bgcolor); break;
}
srcFrame += srcFrameStep;
dstFrame += dstFrameStep;
}
srcLayerIdx += srcLayerStep;
dstLayerIdx += dstLayerStep;
}
}
void Timeline::dropFrames(DropOp op, const Range& drop)
{
FrameNumber srcFrameBegin, srcFrameStep, srcFrameEnd;
FrameNumber dstFrameBegin, dstFrameStep;
// TODO Try to add the range with just one call to DocumentApi
// methods, to avoid generating a lot of SetCelFrame undoers (see
// DocumentApi::setCelFramePosition).
switch (op) {
case Timeline::kMove:
if (drop.frameBegin() <= m_range.frameBegin()) {
srcFrameBegin = m_range.frameBegin();
srcFrameStep = FrameNumber(1);
srcFrameEnd = m_range.frameEnd().next();
dstFrameBegin = drop.frameBegin();
dstFrameStep = FrameNumber(1);
}
else {
srcFrameBegin = m_range.frameEnd();
srcFrameStep = FrameNumber(-1);
srcFrameEnd = m_range.frameBegin().previous();
dstFrameBegin = drop.frameBegin();
dstFrameStep = FrameNumber(-1);
}
break;
case Timeline::kCopy:
if (drop.frameBegin() <= m_range.frameBegin()) {
srcFrameBegin = m_range.frameBegin();
srcFrameStep = FrameNumber(2);
srcFrameEnd = m_range.frameBegin().next(2*m_range.frames());
dstFrameBegin = drop.frameBegin();
dstFrameStep = FrameNumber(1);
}
else {
srcFrameBegin = m_range.frameEnd();
srcFrameStep = FrameNumber(-1);
srcFrameEnd = m_range.frameBegin().previous();
dstFrameBegin = drop.frameBegin();
dstFrameStep = firstFrame();
}
break;
}
DocumentApi api = m_document->getApi();
for (FrameNumber srcFrame = srcFrameBegin,
dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) {
switch (op) {
case Timeline::kMove: api.moveFrame(m_sprite, srcFrame, dstFrame); break;
case Timeline::kCopy: api.copyFrame(m_sprite, srcFrame, dstFrame); break;
}
srcFrame += srcFrameStep;
dstFrame += dstFrameStep;
}
}
void Timeline::dropLayers(DropOp op, const Range& drop)
{
ASSERT(m_clk_layer >= 0 && m_clk_layer < (int)m_layers.size());
if (m_clk_layer < 0)
return;
if (m_layers[m_clk_layer]->isBackground()) {
Alert::show(PACKAGE "<<You can't move the `Background' layer.||&OK");
return;
}
Layer* firstLayer = m_layers[m_range.layerBegin()];
Layer* lastLayer = m_layers[m_range.layerEnd()];
std::vector<Layer*> layers = m_layers;
switch (op) {
case Timeline::kMove:
for (int i = m_range.layerBegin(); i <= m_range.layerEnd(); ++i) {
m_document->getApi().restackLayerAfter(
layers[i], layers[drop.layerBegin()]);
}
break;
case Timeline::kCopy:
for (int i = m_range.layerBegin(); i <= m_range.layerEnd(); ++i) {
m_document->getApi().duplicateLayer(
layers[i], layers[drop.layerBegin()]);
}
break;
}
}
bool Timeline::doesDropModifySprite(const Range& drop, DropOp op) const
{
switch (drop.type()) {
case Range::kCels:
if (drop == m_range)
return false;
break;
case Range::kFrames:
if (op == Timeline::kMove && drop.frameBegin() == m_range.frameBegin())
return false;
break;
case Range::kLayers:
if (op == Timeline::kMove && drop.layerBegin() == m_range.layerBegin())
return false;
break;
default:
ASSERT(false && "You shouldn't call dropRange() if the range is disabled");
return false;
}
return true;
}
void Timeline::updateDropRange(const gfx::Point& pt)
{
DropTarget::HHit oldHHit = m_dropTarget.hhit;
@ -2165,64 +1968,4 @@ bool Timeline::isCopyKeyPressed(ui::Message* msg)
return msg->ctrlPressed();
}
void Timeline::Range::startRange(LayerIndex layer, FrameNumber frame, Type type)
{
m_type = type;
m_layerBegin = m_layerEnd = layer;
m_frameBegin = m_frameEnd = frame;
}
void Timeline::Range::endRange(LayerIndex layer, FrameNumber frame)
{
ASSERT(enabled());
m_layerEnd = layer;
m_frameEnd = frame;
}
void Timeline::Range::disableRange()
{
m_type = kNone;
}
bool Timeline::Range::inRange(LayerIndex layer) const
{
if (enabled())
return (layer >= layerBegin() && layer <= layerEnd());
else
return false;
}
bool Timeline::Range::inRange(FrameNumber frame) const
{
if (enabled())
return (frame >= frameBegin() && frame <= frameEnd());
else
return false;
}
bool Timeline::Range::inRange(LayerIndex layer, FrameNumber frame) const
{
return inRange(layer) && inRange(frame);
}
void Timeline::Range::setLayers(int layers)
{
if (m_layerBegin <= m_layerEnd) m_layerEnd = m_layerBegin + LayerIndex(layers - 1);
else m_layerBegin = m_layerEnd + LayerIndex(layers - 1);
}
void Timeline::Range::setFrames(FrameNumber frames)
{
if (m_frameBegin <= m_frameEnd) m_frameEnd = (m_frameBegin + frames).previous();
else m_frameBegin = (m_frameEnd + frames).previous();
}
void Timeline::Range::displace(int layerDelta, int frameDelta)
{
m_layerBegin += LayerIndex(layerDelta);
m_layerEnd += LayerIndex(layerDelta);
m_frameBegin += FrameNumber(frameDelta);
m_frameEnd += FrameNumber(frameDelta);
}
} // namespace app

View File

@ -20,6 +20,7 @@
#define APP_UI_TIMELINE_H_INCLUDED
#pragma once
#include "app/document_range.h"
#include "app/ui/editor/editor_observer.h"
#include "app/ui/skin/style.h"
#include "base/compiler_specific.h"
@ -57,6 +58,8 @@ namespace app {
, public doc::DocumentObserver
, public app::EditorObserver {
public:
typedef DocumentRange Range;
enum State {
STATE_STANDBY,
STATE_SCROLLING,
@ -69,46 +72,6 @@ namespace app {
STATE_MOVING_ONIONSKIN_RANGE_RIGHT
};
struct Range {
enum Type { kNone, kCels, kFrames, kLayers };
Range() : m_type(kNone) { }
Type type() const { return m_type; }
bool enabled() const { return m_type != kNone; }
LayerIndex layerBegin() const { return MIN(m_layerBegin, m_layerEnd); }
LayerIndex layerEnd() const { return MAX(m_layerBegin, m_layerEnd); }
FrameNumber frameBegin() const { return MIN(m_frameBegin, m_frameEnd); }
FrameNumber frameEnd() const { return MAX(m_frameBegin, m_frameEnd); }
int layers() const { return layerEnd() - layerBegin() + 1; }
FrameNumber frames() const { return (frameEnd() - frameBegin()).next(); }
void setLayers(int layers);
void setFrames(FrameNumber frames);
void displace(int layerDelta, int frameDelta);
bool inRange(LayerIndex layer) const;
bool inRange(FrameNumber frame) const;
bool inRange(LayerIndex layer, FrameNumber frame) const;
void startRange(LayerIndex layer, FrameNumber frame, Type type);
void endRange(LayerIndex layer, FrameNumber frame);
void disableRange();
bool operator==(const Range& o) const {
return m_type == o.m_type &&
layerBegin() == o.layerBegin() && layerEnd() == o.layerEnd() &&
frameBegin() == o.frameBegin() && frameEnd() == o.frameEnd();
}
private:
Type m_type;
LayerIndex m_layerBegin;
LayerIndex m_layerEnd;
FrameNumber m_frameBegin;
FrameNumber m_frameEnd;
};
enum DropOp { kMove, kCopy };
Timeline();
@ -211,13 +174,8 @@ namespace app {
bool isLayerActive(LayerIndex layerIdx) const;
bool isFrameActive(FrameNumber frame) const;
void updateStatusBar(ui::Message* msg);
bool doesDropModifySprite(const Range& drop, DropOp op) const;
void updateDropRange(const gfx::Point& pt);
void dropCels(DropOp op, const Range& drop);
void dropFrames(DropOp op, const Range& drop);
void dropLayers(DropOp op, const Range& drop);
bool isCopyKeyPressed(ui::Message* msg);
// The layer of the bottom (e.g. Background layer)

View File

@ -287,6 +287,10 @@ void LayerFolder::removeLayer(Layer* layer)
void LayerFolder::stackLayer(Layer* layer, Layer* after)
{
ASSERT(layer != after);
if (layer == after)
return;
LayerIterator it = std::find(m_layers.begin(), m_layers.end(), layer);
ASSERT(it != m_layers.end());
m_layers.erase(it);
@ -299,14 +303,6 @@ void LayerFolder::stackLayer(Layer* layer, Layer* after)
}
else
m_layers.push_front(layer);
// TODO
// if (after) {
// JLink before = jlist_find(m_layers, after)->next;
// jlist_insert_before(m_layers, before, layer);
// }
// else
// jlist_prepend(m_layers, layer);
}
void layer_render(const Layer* layer, Image* image, int x, int y, FrameNumber frame)

View File

@ -227,6 +227,9 @@ LayerIndex Sprite::countLayers() const
Layer* Sprite::indexToLayer(LayerIndex index) const
{
if (index < LayerIndex(0))
return NULL;
int index_count = -1;
return index2layer(folder(), index, &index_count);
}
@ -237,6 +240,17 @@ LayerIndex Sprite::layerToIndex(const Layer* layer) const
return layer2index(folder(), layer, &index_count);
}
void Sprite::getLayersList(std::vector<Layer*>& layers) const
{
// TODO support subfolders
LayerConstIterator it = m_folder->getLayerBegin();
LayerConstIterator end = m_folder->getLayerEnd();
for (; it != end; ++it) {
layers.push_back(*it);
}
}
//////////////////////////////////////////////////////////////////////
// Palettes

View File

@ -88,6 +88,8 @@ namespace raster {
Layer* indexToLayer(LayerIndex index) const;
LayerIndex layerToIndex(const Layer* layer) const;
void getLayersList(std::vector<Layer*>& layers) const;
////////////////////////////////////////
// Palettes