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:
David Capello 2016-08-25 12:31:00 -03:00
parent 8bc6149798
commit f2f1c1008b
23 changed files with 1471 additions and 1157 deletions

View File

@ -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));
}

View File

@ -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()));

View File

@ -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));

View File

@ -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 {

View File

@ -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)

View File

@ -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);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
@ -22,103 +22,139 @@ using namespace doc;
DocumentRange::DocumentRange()
: m_type(kNone)
, m_layerBegin(0)
, m_layerEnd(-1)
, m_frameBegin(0)
, m_frameEnd(-1)
, m_selectingFromLayer(nullptr)
, m_selectingFromFrame(-1)
{
}
DocumentRange::DocumentRange(Cel* cel)
: m_type(kCels)
, m_layerBegin(cel->sprite()->layerToIndex(cel->layer()))
, m_layerEnd(m_layerBegin)
, m_frameBegin(cel->frame())
, m_frameEnd(m_frameBegin)
, m_selectingFromLayer(nullptr)
, m_selectingFromFrame(-1)
{
m_selectedLayers.insert(cel->layer());
m_selectedFrames.insert(cel->frame());
}
void DocumentRange::startRange(LayerIndex layer, frame_t frame, Type type)
{
m_type = type;
m_layerBegin = m_layerEnd = layer;
m_frameBegin = m_frameEnd = frame;
}
void DocumentRange::endRange(LayerIndex layer, frame_t frame)
{
ASSERT(enabled());
m_layerEnd = layer;
m_frameEnd = frame;
}
void DocumentRange::disableRange()
void DocumentRange::clearRange()
{
m_type = kNone;
m_selectedLayers.clear();
m_selectedFrames.clear();
}
bool DocumentRange::inRange(LayerIndex layer) const
void DocumentRange::startRange(Layer* fromLayer, frame_t fromFrame, Type type)
{
m_type = type;
m_selectingFromLayer = fromLayer;
m_selectingFromFrame = fromFrame;
if (fromLayer)
m_selectedLayers.insert(fromLayer);
if (fromFrame >= 0)
m_selectedFrames.insert(fromFrame);
}
void DocumentRange::endRange(Layer* toLayer, frame_t toFrame)
{
ASSERT(enabled());
if (m_selectingFromLayer && toLayer)
selectLayerRange(m_selectingFromLayer, toLayer);
if (m_selectingFromFrame >= 0)
selectFrameRange(m_selectingFromFrame, toFrame);
}
bool DocumentRange::contains(Layer* layer) const
{
if (enabled())
return (layer >= layerBegin() && layer <= layerEnd());
return m_selectedLayers.contains(layer);
else
return false;
}
bool DocumentRange::inRange(frame_t frame) const
void DocumentRange::displace(layer_t layerDelta, frame_t frameDelta)
{
if (enabled())
return (frame >= frameBegin() && frame <= frameEnd());
else
return false;
m_selectedLayers.displace(layerDelta);
m_selectedFrames.displace(frameDelta);
}
bool DocumentRange::inRange(LayerIndex layer, frame_t frame) const
{
return inRange(layer) && inRange(frame);
}
void DocumentRange::setLayers(int layers)
{
if (m_layerBegin <= m_layerEnd) m_layerEnd = m_layerBegin + LayerIndex(layers - 1);
else m_layerBegin = m_layerEnd + LayerIndex(layers - 1);
}
void DocumentRange::setFrames(frame_t frames)
{
if (m_frameBegin <= m_frameEnd)
m_frameEnd = (m_frameBegin + frames) - 1;
else
m_frameBegin = (m_frameEnd + frames) - 1;
}
void DocumentRange::displace(int layerDelta, int frameDelta)
{
m_layerBegin += LayerIndex(layerDelta);
m_layerEnd += LayerIndex(layerDelta);
m_frameBegin += frame_t(frameDelta);
m_frameEnd += frame_t(frameDelta);
}
bool DocumentRange::convertToCels(Sprite* sprite)
bool DocumentRange::convertToCels(const Sprite* sprite)
{
switch (m_type) {
case DocumentRange::kNone:
return false;
case DocumentRange::kCels:
break;
case DocumentRange::kFrames:
m_layerBegin = sprite->firstLayer();
m_layerEnd = sprite->lastLayer();
m_type = DocumentRange::kCels;
case DocumentRange::kFrames: {
LayerList layers = sprite->allBrowsableLayers();
ASSERT(layers.empty());
if (!layers.empty()) {
selectLayerRange(layers.front(), layers.back());
m_type = DocumentRange::kCels;
}
else
return false;
break;
}
case DocumentRange::kLayers:
m_frameBegin = frame_t(0);
m_frameEnd = sprite->lastFrame();
selectFrameRange(0, sprite->lastFrame());
m_type = DocumentRange::kCels;
break;
}
return true;
}
void DocumentRange::selectLayerRange(Layer* fromLayer, Layer* toLayer)
{
ASSERT(fromLayer);
ASSERT(toLayer);
bool goNext = false;
bool goPrev = false;
Layer* it;
if (fromLayer != toLayer) {
it = m_selectingFromLayer;
while (it) {
if (it == toLayer) {
goNext = true;
break;
}
it = it->getNextInWholeHierarchy();
}
if (!goNext) {
it = m_selectingFromLayer;
while (it) {
if (it == toLayer) {
goPrev = true;
break;
}
it = it->getPreviousInWholeHierarchy();
}
}
}
it = m_selectingFromLayer;
do {
m_selectedLayers.insert(it);
if (it == toLayer)
break;
if (goNext)
it = it->getNextInWholeHierarchy();
else if (goPrev)
it = it->getPreviousInWholeHierarchy();
else
break;
} while (it);
}
void DocumentRange::selectFrameRange(frame_t fromFrame, frame_t toFrame)
{
m_selectedFrames.insert(fromFrame, toFrame);
}
} // namespace app

View File

@ -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

View File

@ -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,36 +182,64 @@ static DocumentRange drop_range_op(
// Check noop/trivial/do nothing cases, i.e., move a range to the same place.
// Also check invalid cases, like moving a Background layer.
switch (from.type()) {
case DocumentRange::kCels:
if (from == to)
return from;
break;
case DocumentRange::kFrames:
if (op == Move) {
if ((to.frameBegin() >= from.frameBegin() && to.frameEnd() <= from.frameEnd()) ||
(place == kDocumentRangeBefore && to.frameBegin() == from.frameEnd()+1) ||
(place == kDocumentRangeAfter && to.frameEnd() == from.frameBegin()-1))
// Simple cases with one continuos range of frames that are a
// no-op.
if ((from.selectedFrames().ranges() == 1) &&
((to.firstFrame() >= from.firstFrame() &&
to.lastFrame() <= from.lastFrame()) ||
(place == kDocumentRangeBefore && to.firstFrame() == from.lastFrame()+1) ||
(place == kDocumentRangeAfter && to.lastFrame() == from.firstFrame()-1)))
return from;
}
break;
case DocumentRange::kLayers:
if (op == Move) {
if ((to.layerBegin() >= from.layerBegin() && to.layerEnd() <= from.layerEnd()) ||
(place == kDocumentRangeBefore && to.layerBegin() == from.layerEnd()+1) ||
(place == kDocumentRangeAfter && to.layerEnd() == from.layerBegin()-1))
return from;
SelectedLayers srcSelLayers = from.selectedLayers();
SelectedLayers dstSelLayers = to.selectedLayers();
LayerList srcLayers = srcSelLayers.toLayerList();
LayerList dstLayers = dstSelLayers.toLayerList();
if (srcLayers.empty() || dstLayers.empty())
return from;
// Check no-ops when we move layers at the same level (all
// layers with the same parent), all adjacents, and which are
// moved to the same place.
if (srcSelLayers.hasSameParent() &&
dstSelLayers.hasSameParent() &&
are_layers_adjacent(srcLayers) &&
are_layers_adjacent(dstLayers)) {
for (Layer* srcLayer : srcLayers)
if (dstSelLayers.contains(srcLayer))
return from;
if ((place == kDocumentRangeBefore
&& dstLayers.front() == srcLayers.back()->getNext()) ||
(place == kDocumentRangeAfter
&& dstLayers.back() == srcLayers.front()->getPrevious()))
return from;
}
// We cannot move the background
for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i)
if (sprite->indexToLayer(i)->isBackground())
for (Layer* layer : srcSelLayers)
if (layer->isBackground())
throw std::runtime_error("The background layer cannot be moved");
}
// Before background
if (place == kDocumentRangeBefore) {
Layer* background = sprite->indexToLayer(to.layerBegin());
if (background && background->isBackground())
throw std::runtime_error("You cannot move or copy something below the background layer");
for (Layer* background : to.selectedLayers()) {
if (background && background->isBackground())
throw std::runtime_error("You cannot move or copy something below the background layer");
}
}
break;
}
@ -92,238 +262,121 @@ static DocumentRange drop_range_op(
DocumentApi api = doc->getApi(transaction);
// TODO Try to add the range with just one call to DocumentApi
// methods, to avoid generating a lot of SetCelFrame undoers (see
// DocumentApi::setCelFramePosition).
// methods, to avoid generating a lot of cmd::SetCelFrame (see
// DocumentApi::setCelFramePosition() function).
switch (from.type()) {
case DocumentRange::kCels:
{
LayerList layers = sprite->allBrowsableLayers();
int srcLayerBegin, srcLayerStep, srcLayerEnd;
int dstLayerBegin, dstLayerStep;
frame_t srcFrameBegin, srcFrameStep, srcFrameEnd;
frame_t dstFrameBegin, dstFrameStep;
case DocumentRange::kCels: {
LayerList allLayers = sprite->allBrowsableLayers();
if (allLayers.empty())
break;
if (to.layerBegin() <= from.layerBegin()) {
srcLayerBegin = from.layerBegin();
srcLayerStep = 1;
srcLayerEnd = from.layerEnd()+1;
dstLayerBegin = to.layerBegin();
dstLayerStep = 1;
}
else {
srcLayerBegin = from.layerEnd();
srcLayerStep = -1;
srcLayerEnd = from.layerBegin()-1;
dstLayerBegin = to.layerEnd();
dstLayerStep = -1;
}
LayerList srcLayers = from.selectedLayers().toLayerList();
LayerList dstLayers = to.selectedLayers().toLayerList();
if (srcLayers.empty())
throw std::invalid_argument("You need to specify a non-empty cels range");
if (to.frameBegin() <= from.frameBegin()) {
srcFrameBegin = from.frameBegin();
srcFrameStep = frame_t(1);
srcFrameEnd = from.frameEnd()+1;
dstFrameBegin = to.frameBegin();
dstFrameStep = frame_t(1);
}
else {
srcFrameBegin = from.frameEnd();
srcFrameStep = frame_t(-1);
srcFrameEnd = from.frameBegin()-1;
dstFrameBegin = to.frameEnd();
dstFrameStep = frame_t(-1);
}
for (int srcLayerIdx = srcLayerBegin,
dstLayerIdx = dstLayerBegin; srcLayerIdx != srcLayerEnd; ) {
for (frame_t srcFrame = srcFrameBegin,
dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) {
if (dstLayerIdx < 0 || dstLayerIdx >= int(layers.size()) ||
srcLayerIdx < 0 || srcLayerIdx >= int(layers.size()))
break;
ASSERT(layers[srcLayerIdx]->isImage());
ASSERT(layers[dstLayerIdx]->isImage());
LayerImage* srcLayer = static_cast<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;
}
srcFrame += srcFrameStep;
dstFrame += dstFrameStep;
}
srcLayerIdx += srcLayerStep;
dstLayerIdx += dstLayerStep;
}
resultRange = to;
if (find_layer_index(allLayers, srcLayers.front()) <
find_layer_index(allLayers, dstLayers.front())) {
std::reverse(srcLayers.begin(), srcLayers.end());
std::reverse(dstLayers.begin(), dstLayers.end());
}
break;
case DocumentRange::kFrames:
{
frame_t srcFrameBegin = 0, srcFrameStep, srcFrameEnd = 0;
frame_t dstFrameBegin = 0, dstFrameStep;
if (from.firstFrame() < to.firstFrame()) {
auto srcFrames = from.selectedFrames().reversed();
auto dstFrames = to.selectedFrames().reversed();
switch (op) {
case Move:
if (place == kDocumentRangeBefore) {
if (to.frameBegin() <= from.frameBegin()) {
srcFrameBegin = from.frameBegin();
srcFrameStep = frame_t(1);
srcFrameEnd = from.frameEnd()+1;
dstFrameBegin = to.frameBegin();
dstFrameStep = frame_t(1);
}
else {
srcFrameBegin = from.frameEnd();
srcFrameStep = frame_t(-1);
srcFrameEnd = from.frameBegin()-1;
dstFrameBegin = to.frameBegin();
dstFrameStep = frame_t(-1);
}
}
else if (place == kDocumentRangeAfter) {
if (to.frameEnd() <= from.frameBegin()) {
srcFrameBegin = from.frameBegin();
srcFrameStep = frame_t(1);
srcFrameEnd = from.frameEnd()+1;
dstFrameBegin = to.frameEnd()+1;
dstFrameStep = frame_t(1);
}
else {
srcFrameBegin = from.frameEnd();
srcFrameStep = frame_t(-1);
srcFrameEnd = from.frameBegin()-1;
dstFrameBegin = to.frameEnd()+1;
dstFrameStep = frame_t(-1);
}
}
break;
case Copy:
if (place == kDocumentRangeBefore) {
if (to.frameBegin() <= from.frameBegin()) {
srcFrameBegin = from.frameBegin();
srcFrameStep = frame_t(2);
srcFrameEnd = from.frameBegin() + 2*from.frames();
dstFrameBegin = to.frameBegin();
dstFrameStep = frame_t(1);
}
else {
srcFrameBegin = from.frameEnd();
srcFrameStep = frame_t(-1);
srcFrameEnd = from.frameBegin()-1;
dstFrameBegin = to.frameBegin();
dstFrameStep = frame_t(0);
}
}
else if (place == kDocumentRangeAfter) {
if (to.frameEnd() <= from.frameBegin()) {
srcFrameBegin = from.frameBegin();
srcFrameStep = frame_t(2);
srcFrameEnd = from.frameBegin() + 2*from.frames();
dstFrameBegin = to.frameEnd()+1;
dstFrameStep = frame_t(1);
}
else {
srcFrameBegin = from.frameEnd();
srcFrameStep = frame_t(-1);
srcFrameEnd = from.frameBegin()-1;
dstFrameBegin = to.frameEnd()+1;
dstFrameStep = frame_t(0);
}
}
break;
}
for (frame_t srcFrame = srcFrameBegin,
dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) {
switch (op) {
case Move: api.moveFrame(sprite, srcFrame, dstFrame); break;
case Copy: api.copyFrame(sprite, srcFrame, dstFrame); break;
}
srcFrame += srcFrameStep;
dstFrame += dstFrameStep;
}
if (place == kDocumentRangeBefore) {
resultRange.startRange(LayerIndex::NoLayer, frame_t(to.frameBegin()), from.type());
resultRange.endRange(LayerIndex::NoLayer, frame_t(to.frameBegin()+from.frames()-1));
}
else if (place == kDocumentRangeAfter) {
resultRange.startRange(LayerIndex::NoLayer, frame_t(to.frameEnd()+1), from.type());
resultRange.endRange(LayerIndex::NoLayer, frame_t(to.frameEnd()+1+from.frames()-1));
}
if (op == Move && from.frameBegin() < to.frameBegin())
resultRange.displace(0, -from.frames());
move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames);
}
else {
const auto& srcFrames = from.selectedFrames();
const auto& dstFrames = to.selectedFrames();
move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames);
}
resultRange = to;
break;
}
case DocumentRange::kLayers:
{
LayerList layers = sprite->allBrowsableLayers();
case DocumentRange::kFrames: {
frame_t dstFrame;
if (place == kDocumentRangeBefore)
dstFrame = to.firstFrame();
else
dstFrame = to.lastFrame()+1;
if (layers.empty())
resultRange =
move_or_copy_frames(api, op, sprite,
from.selectedFrames(), dstFrame);
break;
}
case DocumentRange::kLayers: {
LayerList allLayers = sprite->allBrowsableLayers();
if (allLayers.empty())
break;
LayerList srcLayers = from.selectedLayers().toLayerList();
LayerList dstLayers = to.selectedLayers().toLayerList();
ASSERT(!srcLayers.empty());
ASSERT(!dstLayers.empty());
switch (op) {
case Move:
if (place == kDocumentRangeBefore) {
Layer* beforeThis = dstLayers.front();
Layer* afterThis = nullptr;
for (Layer* srcLayer : srcLayers) {
if (afterThis)
api.restackLayerAfter(srcLayer, afterThis);
else
api.restackLayerBefore(srcLayer, beforeThis);
afterThis = srcLayer;
}
}
else if (place == kDocumentRangeAfter) {
Layer* afterThis = dstLayers.back();
for (Layer* srcLayer : srcLayers) {
api.restackLayerAfter(srcLayer, afterThis);
afterThis = srcLayer;
}
}
// Same set of layers than the "from" range
resultRange = from;
break;
switch (op) {
case Copy: {
if (place == kDocumentRangeBefore) {
for (Layer* srcLayer : srcLayers) {
Layer* copiedLayer =
api.duplicateLayerBefore(srcLayer, dstLayers.front());
case Move:
if (place == kDocumentRangeBefore) {
for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) {
api.restackLayerBefore(
layers[i],
layers[to.layerBegin()]);
}
resultRange.startRange(copiedLayer, -1, DocumentRange::kLayers);
resultRange.endRange(copiedLayer, -1);
}
else if (place == kDocumentRangeAfter) {
for (LayerIndex i = from.layerEnd(); i >= from.layerBegin(); --i) {
api.restackLayerAfter(
layers[i],
layers[to.layerEnd()]);
}
}
break;
}
else if (place == kDocumentRangeAfter) {
std::reverse(srcLayers.begin(), srcLayers.end());
case Copy:
if (place == kDocumentRangeBefore) {
for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) {
api.duplicateLayerBefore(
layers[i],
layers[to.layerBegin()]);
}
for (Layer* srcLayer : srcLayers) {
Layer* copiedLayer =
api.duplicateLayerAfter(srcLayer, dstLayers.back());
resultRange.startRange(copiedLayer, -1, DocumentRange::kLayers);
resultRange.endRange(copiedLayer, -1);
}
else if (place == kDocumentRangeAfter) {
for (LayerIndex i = from.layerEnd(); i >= from.layerBegin(); --i) {
api.duplicateLayerAfter(
layers[i],
layers[to.layerEnd()]);
}
}
break;
}
break;
}
if (place == kDocumentRangeBefore) {
resultRange.startRange(LayerIndex(to.layerBegin()), frame_t(-1), from.type());
resultRange.endRange(LayerIndex(to.layerBegin()+from.layers()-1), frame_t(-1));
}
else if (place == kDocumentRangeAfter) {
resultRange.startRange(LayerIndex(to.layerEnd()+1), frame_t(-1), from.type());
resultRange.endRange(LayerIndex(to.layerEnd()+1+from.layers()-1), frame_t(-1));
}
if (op == Move && from.layerBegin() < to.layerBegin())
resultRange.displace(-from.layers(), 0);
}
break;
}
}
transaction.commit();
@ -350,29 +403,28 @@ void reverse_frames(Document* doc, const DocumentRange& range)
Transaction transaction(writer.context(), "Reverse Frames");
DocumentApi api = doc->getApi(transaction);
Sprite* sprite = doc->sprite();
LayerList layers;
frame_t frameBegin, frameEnd;
int layerBegin, layerEnd;
bool moveFrames = false;
bool swapCels = false;
switch (range.type()) {
case DocumentRange::kCels:
frameBegin = range.frameBegin();
frameEnd = range.frameEnd();
layerBegin = range.layerBegin();
layerEnd = range.layerEnd() + 1;
frameBegin = range.firstFrame();
frameEnd = range.lastFrame();
layers = range.selectedLayers().toLayerList();
swapCels = true;
break;
case DocumentRange::kFrames:
frameBegin = range.frameBegin();
frameEnd = range.frameEnd();
frameBegin = range.firstFrame();
frameEnd = range.lastFrame();
layers = sprite->allLayers();
moveFrames = true;
break;
case DocumentRange::kLayers:
frameBegin = frame_t(0);
frameEnd = sprite->totalFrames()-1;
layerBegin = range.layerBegin();
layerEnd = range.layerEnd() + 1;
layers = range.selectedLayers().toLayerList();
swapCels = true;
break;
}
@ -385,10 +437,8 @@ void reverse_frames(Document* doc, const DocumentRange& range)
}
}
else if (swapCels) {
LayerList layers = sprite->allBrowsableLayers();
for (int layerIdx = layerBegin; layerIdx != layerEnd; ++layerIdx) {
if (!layers[layerIdx]->isImage())
for (Layer* layer : layers) {
if (!layer->isImage())
continue;
for (frame_t frame = frameBegin,
@ -398,8 +448,8 @@ void reverse_frames(Document* doc, const DocumentRange& range)
if (frame == frameRev)
continue;
LayerImage* layer = static_cast<LayerImage*>(layers[layerIdx]);
api.swapCel(layer, frame, frameRev);
LayerImage* imageLayer = static_cast<LayerImage*>(layer);
api.swapCel(imageLayer, frame, frameRev);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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"

View File

@ -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,57 +2716,24 @@ void Timeline::updateDropRange(const gfx::Point& pt)
m_dropTarget.vhit = DropTarget::VNone;
if (m_state != STATE_MOVING_RANGE) {
m_dropRange.disableRange();
m_dropRange.clearRange();
return;
}
switch (m_range.type()) {
case Range::kCels: {
frame_t dx = m_hot.frame - m_clk.frame;
LayerIndex dy = m_hot.layer - m_clk.layer;
LayerIndex layerIdx = dy+m_range.layerBegin();
layerIdx = MID(firstLayer(), layerIdx, LayerIndex(m_layers.size() - m_range.layers()));
frame_t frame = dx+m_range.frameBegin();
frame = MAX(firstFrame(), frame);
m_dropRange.startRange(layerIdx, frame, m_range.type());
m_dropRange.endRange(
layerIdx+LayerIndex(m_range.layers()-1),
frame+m_range.frames()-1);
case Range::kCels:
m_dropRange = m_range;
m_dropRange.displace(m_hot.layer - m_clk.layer,
m_hot.frame - m_clk.frame);
break;
}
case Range::kFrames: {
frame_t frame = m_hot.frame;
frame_t frameEnd = frame;
if (frame >= m_range.frameBegin() && frame <= m_range.frameEnd()) {
frame = m_range.frameBegin();
frameEnd = frame + m_range.frames() - 1;
}
LayerIndex layerIdx = getLayerIndex(m_layer);
m_dropRange.startRange(layerIdx, frame, m_range.type());
m_dropRange.endRange(layerIdx, frameEnd);
case Range::kFrames:
case Range::kLayers:
m_dropRange.clearRange();
m_dropRange.startRange(m_layers[m_hot.layer].layer, m_hot.frame, m_range.type());
m_dropRange.endRange(m_layers[m_hot.layer].layer, m_hot.frame);
break;
}
case Range::kLayers: {
LayerIndex layer = m_hot.layer;
LayerIndex layerEnd = layer;
if (layer >= m_range.layerBegin() && layer <= m_range.layerEnd()) {
layer = m_range.layerBegin();
layerEnd = layer + LayerIndex(m_range.layers() - 1);
}
m_dropRange.startRange(layer, m_frame, m_range.type());
m_dropRange.endRange(layerEnd, m_frame);
break;
}
}
gfx::Rect bounds = getRangeBounds(m_dropRange);
@ -2794,7 +2813,7 @@ void Timeline::onNewInputPriority(InputChainElement* element)
// That is why we don't disable the range in this case.
Workspace* workspace = dynamic_cast<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();
}

View File

@ -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;
};

View File

@ -348,31 +348,28 @@ void paste()
DocumentRange srcRange = clipboard_range.range();
Document* srcDoc = clipboard_range.document();
Sprite* srcSpr = srcDoc->sprite();
LayerList srcLayers = srcSpr->allBrowsableLayers();
LayerList dstLayers = dstSpr->allBrowsableLayers();
switch (srcRange.type()) {
case DocumentRange::kCels: {
Layer* dstLayer = editor->layer();
frame_t dstFrameFirst = editor->frame();
DocumentRange dstRange;
dstRange.startRange(dstLayer, dstFrameFirst, DocumentRange::kCels);
for (layer_t i=1; 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()).
app::copy_range(srcDoc, srcRange, dstRange, kDocumentRangeBefore);
if (srcRange.layers() == dstRange.layers())
app::copy_range(srcDoc, srcRange, dstRange, kDocumentRangeBefore);
editor->invalidate();
return;
}
@ -381,32 +378,36 @@ void paste()
DocumentApi api = dstDoc->getApi(transaction);
// Add extra frames if needed
frame_t dstFrameBegin = editor->frame();
while (dstFrameBegin+srcRange.frames() > dstSpr->totalFrames())
while (dstFrameFirst+srcRange.frames() > dstSpr->totalFrames())
api.addFrame(dstSpr, dstSpr->totalFrames());
for (LayerIndex
i = srcRange.layerEnd(),
j = dstSpr->layerToIndex(editor->layer());
i >= srcRange.layerBegin() &&
i >= LayerIndex(0) &&
j >= LayerIndex(0); --i, --j) {
auto srcIt = srcRange.selectedLayers().begin();
auto dstIt = dstRange.selectedLayers().begin();
auto srcEnd = srcRange.selectedLayers().end();
auto dstEnd = dstRange.selectedLayers().end();
for (; srcIt != srcEnd && dstIt != dstEnd; ++srcIt, ++dstIt) {
auto srcLayer = *srcIt;
auto dstLayer = *dstIt;
if (!srcLayer->isImage() ||
!dstLayer->isImage())
continue;
// Maps a linked Cel in the original sprite with its
// corresponding copy in the new sprite. In this way
// we can.
std::map<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;
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;