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