mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-28 16:11:35 +00:00
Move Timeline logic to drag-and-drop ranges to app::DocumentRange/move_range/copy_range
This fixes issue 433.
This commit is contained in:
parent
0f99c78174
commit
9fd60f1119
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
89
src/app/document_range.cpp
Normal file
89
src/app/document_range.cpp
Normal 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
74
src/app/document_range.h
Normal 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
|
332
src/app/document_range_ops.cpp
Normal file
332
src/app/document_range_ops.cpp
Normal 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
|
42
src/app/document_range_ops.h
Normal file
42
src/app/document_range_ops.h
Normal 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
|
601
src/app/document_range_tests.cpp
Normal file
601
src/app/document_range_tests.cpp
Normal 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
|
||||
}
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user