Timeline: add support to move/copy ranges (multiple cels/frames/layers)

- Merge Timeline::STATE_MOVING_LAYER/CEL/FRAME to STATE_MOVING_RANGE.
- Remove celmove.h/cpp and move_cel/copy_cel functions. Now they are
  in the Timeline as dropRange/Cels/Frames/Layers member functions.
- Add DocumentApi::copyFrame/moveCel/copyCel member functions.
- Add timeline_drop_layer_deco and timeline_drop_frame_deco skin parts.
- Move code from DuplicateLayerCommand::onExecute() to new member
  function DocumentApi::duplicateLayer().
- Fix a bug changing Cel's frame number: we weren't updating the Cel
  position properly inside the LayerImage::m_cels collection. Now we use
  LayerImage::moveCel() to change the Cel frame number.
- Other fixes to DocumentApi: Change bgcolor from int to color_t.
This commit is contained in:
David Capello 2014-04-09 21:56:06 -03:00
parent cb2c094212
commit 942dba36fd
19 changed files with 716 additions and 645 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -310,6 +310,8 @@
<part id="timeline_padding_tr" x="288" y="12" w1="1" w2="10" w3="1" h1="1" h2="10" h3="1" />
<part id="timeline_padding_bl" x="276" y="24" w1="1" w2="10" w3="1" h1="1" h2="10" h3="1" />
<part id="timeline_padding_br" x="288" y="24" w1="1" w2="10" w3="1" h1="1" h2="10" h3="1" />
<part id="timeline_drop_layer_deco" x="252" y="127" w1="3" w2="1" w3="3" h1="2" h2="1" h3="2" />
<part id="timeline_drop_frame_deco" x="252" y="120" w1="2" w2="1" w3="2" h1="3" h2="1" h3="3" />
</parts>
<stylesheet>
@ -502,6 +504,16 @@
<background part="colorbar_border_fg" />
</style>
<!-- timeline_drop_layer_deco -->
<style id="timeline_drop_layer_deco">
<background part="timeline_drop_layer_deco" />
</style>
<!-- timeline_drop_frame_deco -->
<style id="timeline_drop_frame_deco">
<background part="timeline_drop_frame_deco" />
</style>
</stylesheet>
</skin>

View File

@ -227,7 +227,6 @@ add_library(app-lib
undoers/set_total_frames.cpp
util/autocrop.cpp
util/boundary.cpp
util/celmove.cpp
util/clipboard.cpp
util/expand_cel_canvas.cpp
util/filetoks.cpp

View File

@ -25,7 +25,6 @@
#include "app/context_access.h"
#include "app/ui/timeline.h"
#include "app/ui/main_window.h"
#include "app/util/celmove.h"
#include "ui/base.h"
namespace app {
@ -54,8 +53,7 @@ bool CopyCelCommand::onEnabled(Context* context)
void CopyCelCommand::onExecute(Context* context)
{
ContextWriter writer(context);
copy_cel(writer);
App::instance()->getMainWindow()->getTimeline()->dropRange(Timeline::kCopy);
}
Command* CommandFactory::createCopyCelCommand()

View File

@ -66,33 +66,14 @@ void DuplicateLayerCommand::onExecute(Context* context)
{
ContextWriter writer(context);
Document* document = writer.document();
Sprite* sprite = writer.sprite();
UndoTransaction undo(writer.context(), "Layer Duplication");
LayerImage* sourceLayer = static_cast<LayerImage*>(writer.layer());
DocumentApi api = document->getApi();
// Create a new layer
base::UniquePtr<LayerImage> newLayerPtr(new LayerImage(sprite));
// Disable undo because the layer content is added as a whole with
// AddLayer() undoer.
document->getUndo()->setEnabled(false);
// Copy the layer content (cels + images)
document->copyLayerContent(sourceLayer, document, newLayerPtr);
// Restore enabled status.
document->getUndo()->setEnabled(undo.isEnabled());
// Copy the layer name
newLayerPtr->setName(newLayerPtr->getName() + " Copy");
// Add the new layer in the sprite.
document->getApi().addLayer(sourceLayer->getParent(), newLayerPtr, sourceLayer);
// Release the pointer as it is owned by the sprite now
Layer* newLayer = newLayerPtr.release();
undo.commit();
{
UndoTransaction undo(writer.context(), "Layer Duplication");
LayerImage* sourceLayer = static_cast<LayerImage*>(writer.layer());
api.duplicateLayer(sourceLayer, sourceLayer);
undo.commit();
}
update_screen_for_document(document);
}

View File

@ -25,7 +25,6 @@
#include "app/context_access.h"
#include "app/ui/main_window.h"
#include "app/ui/timeline.h"
#include "app/util/celmove.h"
#include "ui/base.h"
namespace app {
@ -54,8 +53,7 @@ bool MoveCelCommand::onEnabled(Context* context)
void MoveCelCommand::onExecute(Context* context)
{
ContextWriter writer(context);
move_cel(writer);
App::instance()->getMainWindow()->getTimeline()->dropRange(Timeline::kMove);
}
Command* CommandFactory::createMoveCelCommand()

View File

@ -360,8 +360,6 @@ void Document::resetTransformation()
void Document::copyLayerContent(const Layer* sourceLayer0, Document* destDoc, Layer* destLayer0) const
{
DocumentUndo* undo = destDoc->getUndo();
// Copy the layer name
destLayer0->setName(sourceLayer0->getName());
@ -386,12 +384,6 @@ void Document::copyLayerContent(const Layer* sourceLayer0, Document* destDoc, La
Image* newImage = Image::createCopy(sourceImage);
newCel->setImage(destLayer->getSprite()->getStock()->addImage(newImage));
if (undo->isEnabled()) {
undo->pushUndoer(new undoers::AddImage(undo->getObjects(),
destLayer->getSprite()->getStock(),
newCel->getImage()));
}
destLayer->addCel(newCel);
newCel.release();
}
@ -422,9 +414,14 @@ void Document::copyLayerContent(const Layer* sourceLayer0, Document* destDoc, La
ASSERT(destChild != NULL);
// Add the new layer in the sprite.
destDoc->getApi().addLayer(destLayer,
destChild.release(),
destLayer->getLastLayer());
Layer* newLayer = destChild.release();
Layer* afterThis = destLayer->getLastLayer();
destLayer->addLayer(newLayer);
destChild.release();
destLayer->stackLayer(newLayer, afterThis);
}
}
else {
@ -461,14 +458,8 @@ Document* Document::duplicate(DuplicateType type) const
switch (type) {
case DuplicateExactCopy:
// Disable the undo
documentCopy->getUndo()->setEnabled(false);
// Copy the layer folder
copyLayerContent(getSprite()->getFolder(), documentCopy, spriteCopy->getFolder());
// Re-enable the undo
documentCopy->getUndo()->setEnabled(true);
break;
case DuplicateWithFlattenLayers:

View File

@ -43,6 +43,7 @@
#include "app/undoers/remove_palette.h"
#include "app/undoers/replace_image.h"
#include "app/undoers/set_cel_frame.h"
#include "app/undoers/set_cel_opacity.h"
#include "app/undoers/set_cel_position.h"
#include "app/undoers/set_frame_duration.h"
#include "app/undoers/set_layer_flags.h"
@ -71,8 +72,6 @@
#include "raster/sprite.h"
#include "raster/stock.h"
namespace app {
DocumentApi::DocumentApi(Document* document, undo::UndoersCollector* undoers)
@ -101,7 +100,7 @@ void DocumentApi::setSpriteSize(Sprite* sprite, int w, int h)
m_document->notifyObservers<DocumentEvent&>(&DocumentObserver::onSpriteSizeChanged, ev);
}
void DocumentApi::cropSprite(Sprite* sprite, const gfx::Rect& bounds, int bgcolor)
void DocumentApi::cropSprite(Sprite* sprite, const gfx::Rect& bounds, color_t bgcolor)
{
setSpriteSize(sprite, bounds.w, bounds.h);
displaceLayers(sprite->getFolder(), -bounds.x, -bounds.y);
@ -115,7 +114,7 @@ void DocumentApi::cropSprite(Sprite* sprite, const gfx::Rect& bounds, int bgcolo
m_document->getMask()->getBounds().y-bounds.y);
}
void DocumentApi::trimSprite(Sprite* sprite, int bgcolor)
void DocumentApi::trimSprite(Sprite* sprite, color_t bgcolor)
{
gfx::Rect bounds;
@ -210,9 +209,11 @@ void DocumentApi::setPixelFormat(Sprite* sprite, PixelFormat newFormat, Ditherin
void DocumentApi::addFrame(Sprite* sprite, FrameNumber newFrame)
{
// Move cels, and create copies of the cels in the given "newFrame".
addFrameForLayer(sprite->getFolder(), newFrame);
copyFrame(sprite, newFrame.previous(), newFrame);
}
void DocumentApi::copyFrame(Sprite* sprite, FrameNumber fromFrame, FrameNumber newFrame)
{
// Add the frame in the sprite structure, it adjusts the total
// number of frames in the sprite.
if (undoEnabled())
@ -220,6 +221,9 @@ void DocumentApi::addFrame(Sprite* sprite, FrameNumber newFrame)
sprite->addFrame(newFrame);
// Move cels, and create copies of the cels in the given "newFrame".
copyFrameForLayer(sprite->getFolder(), fromFrame, newFrame);
// Notify observers about the new frame.
DocumentEvent ev(m_document);
ev.sprite(sprite);
@ -227,7 +231,7 @@ void DocumentApi::addFrame(Sprite* sprite, FrameNumber newFrame)
m_document->notifyObservers<DocumentEvent&>(&DocumentObserver::onAddFrame, ev);
}
void DocumentApi::addFrameForLayer(Layer* layer, FrameNumber frame)
void DocumentApi::copyFrameForLayer(Layer* layer, FrameNumber fromFrame, FrameNumber frame)
{
ASSERT(layer);
ASSERT(frame >= 0);
@ -236,60 +240,35 @@ void DocumentApi::addFrameForLayer(Layer* layer, FrameNumber frame)
switch (layer->type()) {
case OBJECT_LAYER_IMAGE:
case OBJECT_LAYER_IMAGE: {
LayerImage* imglayer = static_cast<LayerImage*>(layer);
// Displace all cels in '>=frame' to the next frame.
for (FrameNumber c=sprite->getLastFrame(); c>=frame; --c) {
Cel* cel = static_cast<LayerImage*>(layer)->getCel(c);
Cel* cel = imglayer->getCel(c);
if (cel)
setCelFramePosition(sprite, cel, cel->getFrame().next());
setCelFramePosition(imglayer, cel, cel->getFrame().next());
}
copyPreviousFrame(layer, frame);
if (fromFrame >= frame)
fromFrame = fromFrame.next();
copyCel(sprite, imglayer, imglayer, fromFrame, frame, 0);
break;
}
case OBJECT_LAYER_FOLDER: {
LayerIterator it = static_cast<LayerFolder*>(layer)->getLayerBegin();
LayerIterator end = static_cast<LayerFolder*>(layer)->getLayerEnd();
for (; it != end; ++it)
addFrameForLayer(*it, frame);
copyFrameForLayer(*it, fromFrame, frame);
break;
}
}
}
void DocumentApi::copyPreviousFrame(Layer* layer, FrameNumber frame)
{
ASSERT(layer);
ASSERT(frame >= 0);
Sprite* sprite = layer->getSprite();
// create a copy of the previous cel
Cel* src_cel = static_cast<LayerImage*>(layer)->getCel(frame.previous());
Image* src_image = src_cel ? sprite->getStock()->getImage(src_cel->getImage()):
NULL;
// nothing to copy, it will be a transparent cel
if (!src_image)
return;
// copy the image
Image* dst_image = Image::createCopy(src_image);
int image_index = addImageInStock(sprite, dst_image);
// create the new cel
Cel* dst_cel = new Cel(frame, image_index);
if (src_cel) { // copy the data from the previous cel
dst_cel->setPosition(src_cel->getX(), src_cel->getY());
dst_cel->setOpacity(src_cel->getOpacity());
}
// add the cel in the layer
addCel(static_cast<LayerImage*>(layer), dst_cel);
}
void DocumentApi::removeFrame(Sprite* sprite, FrameNumber frame)
{
ASSERT(frame >= 0);
@ -325,14 +304,16 @@ void DocumentApi::removeFrameOfLayer(Layer* layer, FrameNumber frame)
switch (layer->type()) {
case OBJECT_LAYER_IMAGE:
if (Cel* cel = static_cast<LayerImage*>(layer)->getCel(frame))
removeCel(static_cast<LayerImage*>(layer), cel);
case OBJECT_LAYER_IMAGE: {
LayerImage* imglayer = static_cast<LayerImage*>(layer);
if (Cel* cel = imglayer->getCel(frame))
removeCel(imglayer, cel);
for (++frame; frame<sprite->getTotalFrames(); ++frame)
if (Cel* cel = static_cast<LayerImage*>(layer)->getCel(frame))
setCelFramePosition(sprite, cel, cel->getFrame().previous());
if (Cel* cel = imglayer->getCel(frame))
setCelFramePosition(imglayer, cel, cel->getFrame().previous());
break;
}
case OBJECT_LAYER_FOLDER: {
LayerIterator it = static_cast<LayerFolder*>(layer)->getLayerBegin();
@ -398,7 +379,7 @@ void DocumentApi::setFrameRangeDuration(Sprite* sprite, FrameNumber from, FrameN
sprite->setFrameRangeDuration(from, to, msecs);
}
void DocumentApi::moveFrameBefore(Sprite* sprite, FrameNumber frame, FrameNumber beforeFrame)
void DocumentApi::moveFrame(Sprite* sprite, FrameNumber frame, FrameNumber beforeFrame)
{
if (frame != beforeFrame &&
frame >= 0 &&
@ -424,47 +405,53 @@ void DocumentApi::moveFrameBefore(Sprite* sprite, FrameNumber frame, FrameNumber
}
// change the cels of position...
moveFrameBeforeLayer(sprite->getFolder(), frame, beforeFrame);
moveFrameLayer(sprite->getFolder(), frame, beforeFrame);
}
}
void DocumentApi::moveFrameBeforeLayer(Layer* layer, FrameNumber frame, FrameNumber beforeFrame)
void DocumentApi::moveFrameLayer(Layer* layer, FrameNumber frame, FrameNumber beforeFrame)
{
ASSERT(layer);
switch (layer->type()) {
case OBJECT_LAYER_IMAGE: {
CelIterator it = ((LayerImage*)layer)->getCelBegin();
CelIterator end = ((LayerImage*)layer)->getCelEnd();
LayerImage* imglayer = static_cast<LayerImage*>(layer);
CelList cels;
imglayer->getCels(cels);
CelIterator it = cels.begin();
CelIterator end = cels.end();
for (; it != end; ++it) {
Cel* cel = *it;
FrameNumber newFrame = cel->getFrame();
FrameNumber celFrame = cel->getFrame();
FrameNumber newFrame = celFrame;
// moving the frame to the future
if (frame < beforeFrame) {
if (cel->getFrame() == frame) {
if (celFrame == frame) {
newFrame = beforeFrame.previous();
}
else if (cel->getFrame() > frame &&
cel->getFrame() < beforeFrame) {
else if (celFrame > frame &&
celFrame < beforeFrame) {
--newFrame;
}
}
// moving the frame to the past
else if (beforeFrame < frame) {
if (cel->getFrame() == frame) {
if (celFrame == frame) {
newFrame = beforeFrame;
}
else if (cel->getFrame() >= beforeFrame &&
cel->getFrame() < frame) {
else if (celFrame >= beforeFrame &&
celFrame < frame) {
++newFrame;
}
}
if (cel->getFrame() != newFrame)
setCelFramePosition(layer->getSprite(), cel, newFrame);
if (celFrame != newFrame)
setCelFramePosition(imglayer, cel, newFrame);
}
break;
}
@ -474,7 +461,7 @@ void DocumentApi::moveFrameBeforeLayer(Layer* layer, FrameNumber frame, FrameNum
LayerIterator end = static_cast<LayerFolder*>(layer)->getLayerEnd();
for (; it != end; ++it)
moveFrameBeforeLayer(*it, frame, beforeFrame);
moveFrameLayer(*it, frame, beforeFrame);
break;
}
@ -511,8 +498,8 @@ void DocumentApi::removeCel(LayerImage* layer, Cel* cel)
ev.cel(cel);
m_document->notifyObservers<DocumentEvent&>(&DocumentObserver::onRemoveCel, ev);
// find if the image that use the cel to remove, is used by
// another cels
// Find if the image that use the cel to remove, is used by another
// cels.
bool used = false;
for (FrameNumber frame(0); frame<sprite->getTotalFrames(); ++frame) {
Cel* it = layer->getCel(frame);
@ -522,8 +509,8 @@ void DocumentApi::removeCel(LayerImage* layer, Cel* cel)
}
}
// if the image is only used by this cel,
// we can remove the image from the stock
// If the image is only used by this cel, we can remove the image
// from the stock.
if (!used)
removeImageFromStock(sprite, cel->getImage());
@ -531,25 +518,26 @@ void DocumentApi::removeCel(LayerImage* layer, Cel* cel)
m_undoers->pushUndoer(new undoers::RemoveCel(getObjects(),
layer, cel));
// remove the cel from the layer
// Remove the cel from the layer.
layer->removeCel(cel);
// and here we destroy the cel
delete cel;
}
void DocumentApi::setCelFramePosition(Sprite* sprite, Cel* cel, FrameNumber frame)
void DocumentApi::setCelFramePosition(LayerImage* layer, Cel* cel, FrameNumber frame)
{
ASSERT(cel);
ASSERT(frame >= 0);
if (undoEnabled())
m_undoers->pushUndoer(new undoers::SetCelFrame(getObjects(), cel));
m_undoers->pushUndoer(new undoers::SetCelFrame(getObjects(), layer, cel));
cel->setFrame(frame);
layer->moveCel(cel, frame);
DocumentEvent ev(m_document);
ev.sprite(sprite);
ev.sprite(layer->getSprite());
ev.layer(layer);
ev.cel(cel);
ev.frame(frame);
m_document->notifyObservers<DocumentEvent&>(&DocumentObserver::onCelFrameChanged, ev);
@ -570,7 +558,7 @@ void DocumentApi::setCelPosition(Sprite* sprite, Cel* cel, int x, int y)
m_document->notifyObservers<DocumentEvent&>(&DocumentObserver::onCelPositionChanged, ev);
}
void DocumentApi::cropCel(Sprite* sprite, Cel* cel, int x, int y, int w, int h, int bgcolor)
void DocumentApi::cropCel(Sprite* sprite, Cel* cel, int x, int y, int w, int h, color_t bgcolor)
{
Image* cel_image = sprite->getStock()->getImage(cel->getImage());
ASSERT(cel_image);
@ -585,6 +573,142 @@ void DocumentApi::cropCel(Sprite* sprite, Cel* cel, int x, int y, int w, int h,
setCelPosition(sprite, cel, x, y);
}
void DocumentApi::moveCel(Sprite* sprite,
LayerImage* srcLayer, LayerImage* dstLayer,
FrameNumber srcFrame, FrameNumber dstFrame,
color_t bgcolor)
{
ASSERT(srcLayer != NULL);
ASSERT(dstLayer != NULL);
ASSERT(srcFrame >= 0 && srcFrame < sprite->getTotalFrames());
ASSERT(dstFrame >= 0 && dstFrame < sprite->getTotalFrames());
if (srcLayer->isBackground()) {
copyCel(sprite, srcLayer, dstLayer, srcFrame, dstFrame, bgcolor);
return;
}
Cel* srcCel = srcLayer->getCel(srcFrame);
Cel* dstCel = dstLayer->getCel(dstFrame);
// Remove the dstCel (if it exists) because it must be replaced with
// srcCel.
if ((dstCel != NULL) && (!dstLayer->isBackground() || srcCel != NULL))
removeCel(dstLayer, dstCel);
if (srcCel != NULL) {
// Move the cel in the same layer.
if (srcLayer == dstLayer) {
setCelFramePosition(srcLayer, srcCel, dstFrame);
}
// Move the cel between different layers.
else {
if (undoEnabled())
m_undoers->pushUndoer(new undoers::RemoveCel(getObjects(), srcLayer, srcCel));
srcLayer->removeCel(srcCel);
srcCel->setFrame(dstFrame);
// If we are moving a cel from a transparent layer to the
// background layer, we have to clear the background of the
// image.
if (!srcLayer->isBackground() &&
dstLayer->isBackground()) {
Image* srcImage = sprite->getStock()->getImage(srcCel->getImage());
Image* dstImage = crop_image(srcImage,
-srcCel->getX(),
-srcCel->getY(),
sprite->getWidth(),
sprite->getHeight(), 0);
if (undoEnabled()) {
m_undoers->pushUndoer(new undoers::ReplaceImage(getObjects(),
sprite->getStock(), srcCel->getImage()));
m_undoers->pushUndoer(new undoers::SetCelPosition(getObjects(), srcCel));
m_undoers->pushUndoer(new undoers::SetCelOpacity(getObjects(), srcCel));
}
clear_image(dstImage, bgcolor);
composite_image(dstImage, srcImage, srcCel->getX(), srcCel->getY(), 255, BLEND_MODE_NORMAL);
srcCel->setPosition(0, 0);
srcCel->setOpacity(255);
sprite->getStock()->replaceImage(srcCel->getImage(), dstImage);
delete srcImage;
}
addCel(dstLayer, srcCel);
}
}
m_document->notifyCelMoved(srcLayer, srcFrame, dstLayer, dstFrame);
}
void DocumentApi::copyCel(Sprite* sprite,
LayerImage* srcLayer, LayerImage* dstLayer,
FrameNumber srcFrame, FrameNumber dstFrame,
color_t bgcolor)
{
ASSERT(srcLayer != NULL);
ASSERT(dstLayer != NULL);
ASSERT(srcFrame >= 0 && srcFrame < sprite->getTotalFrames());
ASSERT(dstFrame >= 0 && dstFrame < sprite->getTotalFrames());
Cel* srcCel = srcLayer->getCel(srcFrame);
Cel* dstCel = dstLayer->getCel(dstFrame);
// Remove the 'dstCel' (if it exists) because it must be replaced
// with 'srcCel'
if ((dstCel != NULL) && (!dstLayer->isBackground() || srcCel != NULL))
removeCel(dstLayer, dstCel);
// Move the cel in the same layer.
if (srcCel != NULL) {
Image *srcImage = sprite->getStock()->getImage(srcCel->getImage());
Image *dstImage;
int dstCel_x;
int dstCel_y;
int dstCel_opacity;
// If we are moving a cel from a transparent layer to the
// background layer, we have to clear the background of the image.
if (!srcLayer->isBackground() &&
dstLayer->isBackground()) {
dstImage = crop_image(srcImage,
-srcCel->getX(),
-srcCel->getY(),
sprite->getWidth(),
sprite->getHeight(), 0);
clear_image(dstImage, bgcolor);
composite_image(dstImage, srcImage, srcCel->getX(), srcCel->getY(), 255, BLEND_MODE_NORMAL);
dstCel_x = 0;
dstCel_y = 0;
dstCel_opacity = 255;
}
else {
dstImage = Image::createCopy(srcImage);
dstCel_x = srcCel->getX();
dstCel_y = srcCel->getY();
dstCel_opacity = srcCel->getOpacity();
}
// Add the image in the stock
int image_index = addImageInStock(sprite, dstImage);
// Create the new cel
dstCel = new Cel(dstFrame, image_index);
dstCel->setPosition(dstCel_x, dstCel_y);
dstCel->setOpacity(dstCel_opacity);
addCel(dstLayer, dstCel);
}
m_document->notifyCelCopied(srcLayer, srcFrame, dstLayer, dstFrame);
}
LayerImage* DocumentApi::newLayer(Sprite* sprite)
{
LayerImage* layer = new LayerImage(sprite);
@ -672,13 +796,13 @@ void DocumentApi::restackLayerAfter(Layer* layer, Layer* afterThis)
m_document->notifyObservers<DocumentEvent&>(&DocumentObserver::onLayerRestacked, ev);
}
void DocumentApi::cropLayer(Layer* layer, int x, int y, int w, int h, int bgcolor)
void DocumentApi::cropLayer(Layer* layer, int x, int y, int w, int h, color_t bgcolor)
{
if (!layer->isImage())
return;
if (!layer->isBackground())
bgcolor = 0;
bgcolor = 0; // TODO use proper mask color
Sprite* sprite = layer->getSprite();
CelIterator it = ((LayerImage*)layer)->getCelBegin();
@ -713,7 +837,7 @@ void DocumentApi::displaceLayers(Layer* layer, int dx, int dy)
}
}
void DocumentApi::backgroundFromLayer(LayerImage* layer, int bgcolor)
void DocumentApi::backgroundFromLayer(LayerImage* layer, color_t bgcolor)
{
ASSERT(layer);
ASSERT(layer->isImage());
@ -806,7 +930,7 @@ void DocumentApi::layerFromBackground(Layer* layer)
layer->setName("Layer 0");
}
void DocumentApi::flattenLayers(Sprite* sprite, int bgcolor)
void DocumentApi::flattenLayers(Sprite* sprite, color_t bgcolor)
{
Image* cel_image;
Cel* cel;
@ -873,6 +997,20 @@ void DocumentApi::flattenLayers(Sprite* sprite, int bgcolor)
removeLayer(*it);
}
void DocumentApi::duplicateLayer(Layer* sourceLayer, Layer* afterLayer)
{
base::UniquePtr<LayerImage> newLayerPtr(new LayerImage(sourceLayer->getSprite()));
m_document->copyLayerContent(sourceLayer, m_document, newLayerPtr);
newLayerPtr->setName(newLayerPtr->getName() + " Copy");
addLayer(sourceLayer->getParent(), newLayerPtr, afterLayer);
// Release the pointer as it is owned by the sprite now.
newLayerPtr.release();
}
// Adds a new image in the stock. Returns the image index in the
// stock.
int DocumentApi::addImageInStock(Sprite* sprite, Image* image)
@ -930,7 +1068,7 @@ Image* DocumentApi::getCelImage(Sprite* sprite, Cel* cel)
}
// Clears the mask region in the current sprite with the specified background color.
void DocumentApi::clearMask(Layer* layer, Cel* cel, int bgcolor)
void DocumentApi::clearMask(Layer* layer, Cel* cel, color_t bgcolor)
{
Image* image = getCelImage(layer->getSprite(), cel);
if (!image)
@ -1007,7 +1145,7 @@ void DocumentApi::flipImage(Image* image, const gfx::Rect& bounds,
raster::algorithm::flip_image(image, bounds, flipType);
}
void DocumentApi::flipImageWithMask(Image* image, const Mask* mask, raster::algorithm::FlipType flipType, int bgcolor)
void DocumentApi::flipImageWithMask(Image* image, const Mask* mask, raster::algorithm::FlipType flipType, color_t bgcolor)
{
base::UniquePtr<Image> flippedImage((Image::createCopy(image)));

View File

@ -22,6 +22,7 @@
#include "gfx/rect.h"
#include "raster/algorithm/flip_type.h"
#include "raster/color.h"
#include "raster/dithering_method.h"
#include "raster/frame_number.h"
#include "raster/pixel_format.h"
@ -61,24 +62,30 @@ namespace app {
// Sprite API
void setSpriteSize(Sprite* sprite, int w, int h);
void cropSprite(Sprite* sprite, const gfx::Rect& bounds, int bgcolor);
void trimSprite(Sprite* sprite, int bgcolor);
void cropSprite(Sprite* sprite, const gfx::Rect& bounds, color_t bgcolor);
void trimSprite(Sprite* sprite, color_t bgcolor);
void setPixelFormat(Sprite* sprite, PixelFormat newFormat, DitheringMethod dithering_method);
// Frames API
void addFrame(Sprite* sprite, FrameNumber newFrame);
void copyFrame(Sprite* sprite, FrameNumber fromFrame, FrameNumber newFrame);
void removeFrame(Sprite* sprite, FrameNumber frame);
void setTotalFrames(Sprite* sprite, FrameNumber frames);
void setFrameDuration(Sprite* sprite, FrameNumber frame, int msecs);
void setFrameRangeDuration(Sprite* sprite, FrameNumber from, FrameNumber to, int msecs);
void moveFrameBefore(Sprite* sprite, FrameNumber frame, FrameNumber beforeFrame);
void moveFrame(Sprite* sprite, FrameNumber frame, FrameNumber beforeFrame);
// Cels API
void addCel(LayerImage* layer, Cel* cel);
void removeCel(LayerImage* layer, Cel* cel);
void setCelFramePosition(Sprite* sprite, Cel* cel, FrameNumber frame);
void setCelPosition(Sprite* sprite, Cel* cel, int x, int y);
void cropCel(Sprite* sprite, Cel* cel, int x, int y, int w, int h, int bgcolor);
void cropCel(Sprite* sprite, Cel* cel, int x, int y, int w, int h, color_t bgcolor);
void moveCel(Sprite* sprite,
LayerImage* srcLayer, LayerImage* dstLayer,
FrameNumber srcFrame, FrameNumber dstFrame, color_t bgcolor);
void copyCel(Sprite* sprite,
LayerImage* srcLayer, LayerImage* dstLayer,
FrameNumber srcFrame, FrameNumber dstFrame, color_t bgcolor);
// Layers API
LayerImage* newLayer(Sprite* sprite);
@ -86,11 +93,12 @@ namespace app {
void addLayer(LayerFolder* folder, Layer* newLayer, Layer* afterThis);
void removeLayer(Layer* layer);
void restackLayerAfter(Layer* layer, Layer* afterThis);
void cropLayer(Layer* layer, int x, int y, int w, int h, int bgcolor);
void cropLayer(Layer* layer, int x, int y, int w, int h, color_t bgcolor);
void displaceLayers(Layer* layer, int dx, int dy);
void backgroundFromLayer(LayerImage* layer, int bgcolor);
void backgroundFromLayer(LayerImage* layer, color_t bgcolor);
void layerFromBackground(Layer* layer);
void flattenLayers(Sprite* sprite, int bgcolor);
void flattenLayers(Sprite* sprite, color_t bgcolor);
void duplicateLayer(Layer* sourceLayer, Layer* afterLayer);
// Images stock API
int addImageInStock(Sprite* sprite, Image* image);
@ -99,9 +107,9 @@ namespace app {
// Image API
Image* getCelImage(Sprite* sprite, Cel* cel);
void clearMask(Layer* layer, Cel* cel, int bgcolor);
void clearMask(Layer* layer, Cel* cel, color_t bgcolor);
void flipImage(Image* image, const gfx::Rect& bounds, raster::algorithm::FlipType flipType);
void flipImageWithMask(Image* image, const Mask* mask, raster::algorithm::FlipType flipType, int bgcolor);
void flipImageWithMask(Image* image, const Mask* mask, raster::algorithm::FlipType flipType, color_t bgcolor);
void pasteImage(Sprite* sprite, Cel* cel, const Image* src_image, int x, int y, int opacity);
// Mask API
@ -114,10 +122,10 @@ namespace app {
private:
undo::ObjectsContainer* getObjects() const;
void addFrameForLayer(Layer* layer, FrameNumber frame);
void setCelFramePosition(LayerImage* layer, Cel* cel, FrameNumber frame);
void copyFrameForLayer(Layer* layer, FrameNumber fromFrame, FrameNumber frame);
void removeFrameOfLayer(Layer* layer, FrameNumber frame);
void copyPreviousFrame(Layer* layer, FrameNumber frame);
void moveFrameBeforeLayer(Layer* layer, FrameNumber frame, FrameNumber beforeFrame);
void moveFrameLayer(Layer* layer, FrameNumber frame, FrameNumber beforeFrame);
void configureLayerAsBackground(LayerImage* layer);
bool undoEnabled();

View File

@ -22,6 +22,7 @@
#include "app/ui/timeline.h"
#include "app/app.h"
#include "app/app_menus.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
@ -43,7 +44,6 @@
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
#include "app/undo_transaction.h"
#include "app/util/celmove.h"
#include "base/compiler_specific.h"
#include "base/memory.h"
#include "gfx/point.h"
@ -103,6 +103,8 @@ static const char* kTimelinePaddingBl = "timeline_padding_bl";
static const char* kTimelinePaddingBr = "timeline_padding_br";
static const char* kTimelineSelectedCelStyle = "timeline_selected_cel";
static const char* kTimelineRangeOutlineStyle = "timeline_range_outline";
static const char* kTimelineDropLayerDecoStyle = "timeline_drop_layer_deco";
static const char* kTimelineDropFrameDecoStyle = "timeline_drop_frame_deco";
static const char* kTimelineActiveColor = "timeline_active";
@ -146,6 +148,8 @@ Timeline::Timeline()
, m_timelinePaddingBrStyle(get_style(kTimelinePaddingBr))
, m_timelineSelectedCelStyle(get_style(kTimelineSelectedCelStyle))
, m_timelineRangeOutlineStyle(get_style(kTimelineRangeOutlineStyle))
, m_timelineDropLayerDecoStyle(get_style(kTimelineDropLayerDecoStyle))
, m_timelineDropFrameDecoStyle(get_style(kTimelineDropFrameDecoStyle))
, m_context(UIContext::instance())
, m_editor(NULL)
, m_document(NULL)
@ -230,7 +234,8 @@ void Timeline::detachDocument()
bool Timeline::isMovingCel() const
{
return (m_state == Timeline::STATE_MOVING_CEL);
return (m_state == Timeline::STATE_MOVING_RANGE &&
m_range.type() == Range::kCels);
}
void Timeline::setLayer(Layer* layer)
@ -286,7 +291,6 @@ bool Timeline::onProcessMessage(Message* msg)
m_clk_frame = m_hot_frame;
captureMouse();
m_oldPos = static_cast<MouseMessage*>(msg)->position();
switch (m_hot_part) {
case A_PART_SEPARATOR:
@ -311,12 +315,9 @@ bool Timeline::onProcessMessage(Message* msg)
if (selectFrame) {
setFrame(m_clk_frame);
if (msg->ctrlPressed())
m_state = STATE_MOVING_FRAME;
else {
m_state = STATE_SELECTING_FRAMES;
m_range.startRange(getLayerIndex(m_layer), m_clk_frame, Range::Frames);
}
m_state = STATE_SELECTING_FRAMES;
m_range.startRange(getLayerIndex(m_layer), m_clk_frame, Range::kFrames);
}
break;
}
@ -339,20 +340,8 @@ bool Timeline::onProcessMessage(Message* msg)
showCel(m_clk_layer, m_frame);
if (selectLayer) {
bool startRange = false;
if (msg->ctrlPressed()) {
m_state = STATE_MOVING_LAYER;
if (!m_range.inRange(m_clk_layer))
startRange = true;
}
else {
m_state = STATE_SELECTING_LAYERS;
startRange = true;
}
if (startRange)
m_range.startRange(m_clk_layer, m_frame, Range::Layers);
m_state = STATE_SELECTING_LAYERS;
m_range.startRange(m_clk_layer, m_frame, Range::kLayers);
}
break;
}
@ -380,16 +369,15 @@ bool Timeline::onProcessMessage(Message* msg)
// Change the scroll to show the new selected cel.
showCel(m_clk_layer, m_frame);
if (msg->ctrlPressed())
m_state = STATE_MOVING_CEL;
else {
m_state = STATE_SELECTING_CELS;
if (selectCel)
m_range.startRange(m_clk_layer, m_clk_frame, Range::Cels);
invalidate();
}
m_state = STATE_SELECTING_CELS;
if (selectCel)
m_range.startRange(m_clk_layer, m_clk_frame, Range::kCels);
invalidate();
break;
}
case A_PART_RANGE_OUTLINE:
m_state = STATE_MOVING_RANGE;
break;
}
// Redraw the new clicked part (header, layer or cel).
@ -607,29 +595,9 @@ bool Timeline::onProcessMessage(Message* msg)
if (popup_menu != NULL) {
gfx::Point mousePos = mouseMsg->position();
popup_menu->showPopup(mousePos.x, mousePos.y);
invalidate();
}
}
}
#if 0
// Show the frame's properties dialog.
else if (mouseMsg->left()) {
const ContextReader reader(m_context);
if (m_hot_frame >= 0 &&
m_hot_frame < m_sprite->getTotalFrames() &&
m_hot_frame != m_clk_frame+1) {
{
ContextWriter writer(reader);
UndoTransaction undoTransaction(m_context, "Move Frame");
m_document->getApi().moveFrameBefore(writer.sprite(), m_clk_frame, m_hot_frame);
undoTransaction.commit();
}
invalidate();
}
}
#endif
break;
case A_PART_LAYER_TEXT:
// Show the layer pop-up menu.
@ -639,56 +607,9 @@ bool Timeline::onProcessMessage(Message* msg)
if (popup_menu != NULL) {
gfx::Point mousePos = mouseMsg->position();
popup_menu->showPopup(mousePos.x, mousePos.y);
invalidate();
regenerateLayers();
}
}
}
// Move a layer.
else if (m_state == STATE_MOVING_LAYER) {
if (m_hot_layer >= 0
&& m_hot_layer < (int)m_layers.size()
&& !isLayerActive(m_hot_layer)) {
if (m_layers[m_clk_layer]->isBackground()) {
Alert::show(PACKAGE "<<You can't move the `Background' layer.||&OK");
break;
}
// Move the clicked-layer after the hot-layer.
try {
const ContextReader reader(m_context);
ContextWriter writer(reader);
UndoTransaction undoTransaction(m_context, "Move Layer");
Layer* firstLayer = m_layers[m_range.layerBegin()];
Layer* lastLayer = m_layers[m_range.layerEnd()];
for (int i = m_range.layerBegin(); i <= m_range.layerEnd(); ++i) {
m_document->getApi().restackLayerAfter(
m_layers[i], m_layers[m_hot_layer]);
}
undoTransaction.commit();
// Select the new layer.
setLayer(m_layers[m_clk_layer]);
regenerateLayers();
m_range.startRange(getLayerIndex(firstLayer), m_frame, m_range.type());
m_range.endRange(getLayerIndex(lastLayer), m_frame);
}
catch (LockedDocumentException& e) {
Console::showException(e);
regenerateLayers();
}
invalidate();
}
}
break;
case A_PART_LAYER_EYE_ICON:
// Hide/show layer.
@ -714,49 +635,29 @@ bool Timeline::onProcessMessage(Message* msg)
}
break;
case A_PART_CEL: {
if (m_state == STATE_MOVING_CEL) {
Layer* src_layer = m_layers[m_clk_layer];
Layer* dst_layer = m_layers[m_hot_layer];
FrameNumber src_frame = m_clk_frame;
FrameNumber dst_frame = m_hot_frame;
if (src_layer == dst_layer &&
src_frame == dst_frame)
break;
set_frame_to_handle(src_layer, src_frame, dst_layer, dst_frame);
}
// Show the cel pop-up menu.
if (mouseMsg->right()) {
Menu* popup_menu = (m_state == STATE_MOVING_CEL) ?
AppMenus::instance()->getCelMovementPopupMenu():
AppMenus::instance()->getCelPopupMenu();
Menu* popup_menu =
(m_state == STATE_MOVING_RANGE &&
m_range.type() == Range::kCels) ?
AppMenus::instance()->getCelMovementPopupMenu():
AppMenus::instance()->getCelPopupMenu();
if (popup_menu != NULL) {
gfx::Point mousePos = mouseMsg->position();
popup_menu->showPopup(mousePos.x, mousePos.y);
regenerateLayers();
invalidate();
}
}
// Move the cel.
else if (mouseMsg->left()) {
if (m_state == STATE_MOVING_CEL) {
{
const ContextReader reader(m_context);
ContextWriter writer(reader);
move_cel(writer);
}
regenerateLayers();
invalidate();
}
}
break;
}
}
if (mouseMsg->left() && m_state == STATE_MOVING_RANGE) {
dropRange(mouseMsg->ctrlPressed() ?
Timeline::kCopy:
Timeline::kMove);
}
// Clean the clicked-part & redraw the hot-part.
cleanClk();
@ -937,7 +838,6 @@ void Timeline::onPaint(ui::PaintEvent& ev)
// Draw every visible cel for each layer.
for (frame=first_frame; frame<=last_frame; ++frame) {
Cel* cel = (layerPtr->isImage() && it != end && (*it)->getFrame() == frame ? *it: NULL);
drawCel(g, layer, frame, cel);
if (cel)
@ -1060,42 +960,19 @@ void Timeline::onLayerChanged(Editor* editor)
void Timeline::setCursor(int x, int y)
{
int mx = x - getBounds().x;
//int my = y - getBounds().y;
// Scrolling.
if (m_state == STATE_SCROLLING || key[KEY_SPACE]) {
jmouse_set_cursor(kScrollCursor);
}
// Moving a frame.
else if (m_state == STATE_MOVING_FRAME &&
m_clk_part == A_PART_HEADER_FRAME &&
m_hot_part == A_PART_HEADER_FRAME &&
m_clk_frame != m_hot_frame) {
jmouse_set_cursor(kMoveCursor);
}
// Moving a layer.
else if (m_state == STATE_MOVING_LAYER &&
m_clk_part == A_PART_LAYER_TEXT &&
m_hot_part == A_PART_LAYER_TEXT &&
m_clk_layer != m_hot_layer) {
if (m_layers[m_clk_layer]->isBackground())
jmouse_set_cursor(kForbiddenCursor);
// Moving.
else if (m_state == STATE_MOVING_RANGE) {
if (key[KEY_LCONTROL] || key[KEY_RCONTROL])
jmouse_set_cursor(kArrowPlusCursor);
else
jmouse_set_cursor(kMoveCursor);
}
// Moving a cel.
else if (m_state == STATE_MOVING_CEL &&
m_clk_part == A_PART_CEL &&
m_hot_part == A_PART_CEL &&
(m_clk_frame != m_hot_frame ||
m_clk_layer != m_hot_layer)) {
jmouse_set_cursor(kMoveCursor);
}
// Normal state.
else if (mx > m_separator_x-2 && mx < m_separator_x+2) {
// Is the mouse in the separator.
jmouse_set_cursor(kSizeLCursor);
}
else if (m_hot_part == A_PART_HEADER_ONIONSKIN_RANGE_LEFT
|| m_state == STATE_MOVING_ONIONSKIN_RANGE_LEFT) {
jmouse_set_cursor(kSizeLCursor);
@ -1107,6 +984,10 @@ void Timeline::setCursor(int x, int y)
else if (m_hot_part == A_PART_RANGE_OUTLINE) {
jmouse_set_cursor(kMoveCursor);
}
else if (mx > m_separator_x-2 && mx < m_separator_x+2) {
// Is the mouse in the separator.
jmouse_set_cursor(kSizeLCursor);
}
else {
jmouse_set_cursor(kArrowCursor);
}
@ -1244,10 +1125,8 @@ void Timeline::drawCel(ui::Graphics* g, int layer_index, FrameNumber frame, Cel*
if (!clip)
return;
if (m_range.inRange(layer_index, frame)
|| (isLayerActive(layer_index) && isFrameActive(frame))) {
if (layer_index == getLayerIndex(m_layer) && frame == m_frame)
drawPart(g, bounds, NULL, m_timelineSelectedCelStyle, false, false, true);
}
else
drawPart(g, bounds, NULL, m_timelineBoxStyle, is_active, is_hover);
@ -1284,13 +1163,52 @@ void Timeline::drawCel(ui::Graphics* g, int layer_index, FrameNumber frame, Cel*
void Timeline::drawRangeOutline(ui::Graphics* g)
{
gfx::Rect bounds = getPartBounds(A_PART_RANGE_OUTLINE);
gfx::Rect clipBounds;
switch (m_range.type()) {
case Range::kCels: clipBounds = getCelsBounds(); break;
case Range::kFrames: clipBounds = getFrameHeadersBounds(); break;
case Range::kLayers: clipBounds = getLayerHeadersBounds(); break;
}
IntersectClip clip(g, clipBounds);
if (!clip)
return;
Style::State state;
if (m_range.enabled()) state += Style::active();
if (m_hot_part == A_PART_RANGE_OUTLINE) state += Style::hover();
gfx::Rect bounds = getPartBounds(A_PART_RANGE_OUTLINE);
m_timelineRangeOutlineStyle->paint(g, bounds, NULL, state);
Range drop = getDropRange();
switch (drop.type()) {
case Range::kCels:
bounds =
getPartBounds(A_PART_CEL, drop.layerBegin(), drop.frameBegin()).createUnion(
getPartBounds(A_PART_CEL, drop.layerEnd(), drop.frameEnd()))
// TODO theme specific
.enlarge(2*jguiscale());
m_timelineRangeOutlineStyle->paint(g, bounds, NULL, Style::active());
break;
case Range::kFrames:
bounds = getPartBounds(A_PART_HEADER_FRAME, 0, drop.frameBegin());
bounds.w = 5 * jguiscale(); // TODO get height from the skin info
bounds.x -= bounds.w/2;
m_timelineDropFrameDecoStyle->paint(g, bounds, NULL, Style::State());
break;
case Range::kLayers:
bounds = getPartBounds(A_PART_LAYER, drop.layerBegin());
bounds.h = 5 * jguiscale(); // TODO get height from the skin info
bounds.y -= bounds.h/2;
m_timelineDropLayerDecoStyle->paint(g, bounds, NULL, Style::State());
break;
}
}
void Timeline::drawPaddings(ui::Graphics* g)
@ -1451,16 +1369,18 @@ gfx::Rect Timeline::getPartBounds(int part, int layer, FrameNumber frame) const
case A_PART_RANGE_OUTLINE:
switch (m_range.type()) {
case Range::None: break; // Return empty rectangle
case Range::Cels:
case Range::kNone: break; // Return empty rectangle
case Range::kCels:
return
getPartBounds(A_PART_CEL, m_range.layerBegin(), m_range.frameBegin()).createUnion(
getPartBounds(A_PART_CEL, m_range.layerEnd(), m_range.frameEnd())).enlarge(2*jguiscale());
case Range::Frames:
getPartBounds(A_PART_CEL, m_range.layerEnd(), m_range.frameEnd()));
// TODO theme specific
//.enlarge(2*jguiscale());
case Range::kFrames:
return
getPartBounds(A_PART_HEADER_FRAME, 0, m_range.frameBegin()).createUnion(
getPartBounds(A_PART_HEADER_FRAME, 0, m_range.frameEnd()));
case Range::Layers:
case Range::kLayers:
return
getPartBounds(A_PART_LAYER, m_range.layerBegin()).createUnion(
getPartBounds(A_PART_LAYER, m_range.layerEnd()));
@ -1499,23 +1419,26 @@ void Timeline::hotThis(int hot_part, int hot_layer, FrameNumber hot_frame)
if (hot_part != m_hot_part ||
hot_layer != m_hot_layer ||
hot_frame != m_hot_frame) {
// Clean the old 'hot' thing.
invalidatePart(m_hot_part,
m_hot_layer,
m_hot_frame);
// Invalidate the whole control.
if (m_state == STATE_MOVING_RANGE ||
hot_part == A_PART_RANGE_OUTLINE ||
m_hot_part == A_PART_RANGE_OUTLINE) {
invalidate();
}
// Invalidate the old and new 'hot' thing.
else {
invalidatePart(m_hot_part,
m_hot_layer,
m_hot_frame);
invalidatePart(m_hot_part,
m_hot_layer,
m_hot_frame);
}
// Draw the new 'hot' thing.
m_hot_part = hot_part;
m_hot_layer = hot_layer;
m_hot_frame = hot_frame;
invalidatePart(m_hot_part,
m_hot_layer,
m_hot_frame);
#if 0 // TODO remove these lines
if (getPartBounds(hot_part, hot_layer, hot_frame).isEmpty())
m_hot_part = A_PART_NOTHING;
#endif
updateStatusBar();
}
@ -1699,6 +1622,279 @@ bool Timeline::isFrameActive(FrameNumber frame) const
return m_range.inRange(frame);
}
void Timeline::dropRange(DropOp op)
{
// "Do nothing" cases. The user drops in the same place. (We don't
// even add the undo information.)
Range drop = getDropRange();
switch (drop.type()) {
case Range::kCels:
if (drop == m_range)
return;
break;
case Range::kFrames:
if (op == Timeline::kMove && drop.frameBegin() == m_range.frameBegin())
return;
break;
case Range::kLayers:
if (op == Timeline::kMove && drop.layerBegin() == m_range.layerBegin())
return;
break;
}
const char* undoLabel = NULL;
switch (op) {
case Timeline::kMove: undoLabel = "Move Range"; break;
case Timeline::kCopy: undoLabel = "Copy Range"; break;
}
const ContextReader reader(m_context);
ContextWriter writer(reader);
UndoTransaction undo(writer.context(), undoLabel, undo::ModifyDocument);
int activeRelativeLayer = getLayerIndex(m_layer) - m_range.layerBegin();
FrameNumber activeRelativeFrame = m_frame - m_range.frameBegin();
switch (drop.type()) {
case Range::kCels: dropCels(op, drop); break;
case Range::kFrames: dropFrames(op, drop); break;
case Range::kLayers: dropLayers(op, drop); break;
}
undo.commit();
regenerateLayers();
// Adjust "drop" range so we can select the same selected range that
// the user had selected.
switch (drop.type()) {
case Range::kFrames:
if (op == Timeline::kMove && m_range.frameBegin() < drop.frameBegin()) {
drop.displace(0, FrameNumber(-m_range.frames()));
}
drop.setFrames(m_range.frames());
break;
case Range::kLayers:
if (op == Timeline::kMove && m_range.layerBegin() < drop.layerBegin()) {
drop.displace(-m_range.layers(), FrameNumber(0));
}
drop.setLayers(m_range.layers());
break;
}
setLayer(m_layers[drop.layerBegin() + activeRelativeLayer]);
setFrame(drop.frameBegin() + activeRelativeFrame);
m_range = drop;
invalidate();
}
void Timeline::dropCels(DropOp op, const Range& drop)
{
ASSERT(drop.layerBegin() >= 0 && drop.layerBegin() < (int)m_layers.size());
ASSERT(drop.layerEnd() >= 0 && drop.layerEnd() < (int)m_layers.size());
ASSERT(drop.frameBegin() >= FrameNumber(0) && drop.frameBegin() < m_sprite->getTotalFrames());
ASSERT(drop.frameEnd() >= FrameNumber(0) && drop.frameEnd() < m_sprite->getTotalFrames());
int srcLayerBegin, srcLayerStep, srcLayerEnd;
int dstLayerBegin, dstLayerStep;
FrameNumber srcFrameBegin, srcFrameStep, srcFrameEnd;
FrameNumber dstFrameBegin, dstFrameStep;
if (drop.layerBegin() <= m_range.layerBegin()) {
srcLayerBegin = m_range.layerBegin();
srcLayerStep = 1;
srcLayerEnd = m_range.layerEnd()+1;
dstLayerBegin = drop.layerBegin();
dstLayerStep = 1;
}
else {
srcLayerBegin = m_range.layerEnd();
srcLayerStep = -1;
srcLayerEnd = m_range.layerBegin()-1;
dstLayerBegin = drop.layerEnd();
dstLayerStep = -1;
}
if (drop.frameBegin() <= m_range.frameBegin()) {
srcFrameBegin = m_range.frameBegin();
srcFrameStep = FrameNumber(1);
srcFrameEnd = m_range.frameEnd().next();
dstFrameBegin = drop.frameBegin();
dstFrameStep = FrameNumber(1);
}
else {
srcFrameBegin = m_range.frameEnd();
srcFrameStep = FrameNumber(-1);
srcFrameEnd = m_range.frameBegin().previous();
dstFrameBegin = drop.frameEnd();
dstFrameStep = FrameNumber(-1);
}
DocumentApi& api = m_document->getApi();
for (int srcLayerIdx = srcLayerBegin,
dstLayerIdx = dstLayerBegin; srcLayerIdx != srcLayerEnd; ) {
for (FrameNumber srcFrame = srcFrameBegin,
dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) {
LayerImage* srcLayer = static_cast<LayerImage*>(m_layers[srcLayerIdx]);
LayerImage* dstLayer = static_cast<LayerImage*>(m_layers[dstLayerIdx]);
color_t bgcolor = app_get_color_to_clear_layer(dstLayer);
switch (op) {
case Timeline::kMove: api.moveCel(m_sprite, srcLayer, dstLayer, srcFrame, dstFrame, bgcolor); break;
case Timeline::kCopy: api.copyCel(m_sprite, srcLayer, dstLayer, srcFrame, dstFrame, bgcolor); break;
}
srcFrame += srcFrameStep;
dstFrame += dstFrameStep;
}
srcLayerIdx += srcLayerStep;
dstLayerIdx += dstLayerStep;
}
}
void Timeline::dropFrames(DropOp op, const Range& drop)
{
FrameNumber srcFrameBegin, srcFrameStep, srcFrameEnd;
FrameNumber dstFrameBegin, dstFrameStep;
// TODO Try to add the range with just one call to DocumentApi
// methods, to avoid generating a lot of SetCelFrame undoers (see
// DocumentApi::setCelFramePosition).
switch (op) {
case Timeline::kMove:
if (drop.frameBegin() <= m_range.frameBegin()) {
srcFrameBegin = m_range.frameBegin();
srcFrameStep = FrameNumber(1);
srcFrameEnd = m_range.frameEnd().next();
dstFrameBegin = drop.frameBegin();
dstFrameStep = FrameNumber(1);
}
else {
srcFrameBegin = m_range.frameEnd();
srcFrameStep = FrameNumber(-1);
srcFrameEnd = m_range.frameBegin().previous();
dstFrameBegin = drop.frameEnd();
dstFrameStep = FrameNumber(-1);
}
break;
case Timeline::kCopy:
if (drop.frameBegin() <= m_range.frameBegin()) {
srcFrameBegin = m_range.frameBegin();
srcFrameStep = FrameNumber(2);
srcFrameEnd = m_range.frameBegin().next(2*m_range.frames());
dstFrameBegin = drop.frameBegin();
dstFrameStep = FrameNumber(1);
}
else {
srcFrameBegin = m_range.frameEnd();
srcFrameStep = FrameNumber(-1);
srcFrameEnd = m_range.frameBegin().previous();
dstFrameBegin = drop.frameBegin();
dstFrameStep = FrameNumber(0);
}
break;
}
DocumentApi api = m_document->getApi();
for (FrameNumber srcFrame = srcFrameBegin,
dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) {
switch (op) {
case Timeline::kMove: api.moveFrame(m_sprite, srcFrame, dstFrame); break;
case Timeline::kCopy: api.copyFrame(m_sprite, srcFrame, dstFrame); break;
}
srcFrame += srcFrameStep;
dstFrame += dstFrameStep;
}
}
void Timeline::dropLayers(DropOp op, const Range& drop)
{
if (m_layers[m_clk_layer]->isBackground()) {
Alert::show(PACKAGE "<<You can't move the `Background' layer.||&OK");
return;
}
Layer* firstLayer = m_layers[m_range.layerBegin()];
Layer* lastLayer = m_layers[m_range.layerEnd()];
std::vector<Layer*> layers = m_layers;
switch (op) {
case Timeline::kMove:
for (int i = m_range.layerBegin(); i <= m_range.layerEnd(); ++i) {
m_document->getApi().restackLayerAfter(
layers[i], layers[drop.layerBegin()]);
}
break;
case Timeline::kCopy:
for (int i = m_range.layerBegin(); i <= m_range.layerEnd(); ++i) {
m_document->getApi().duplicateLayer(
layers[i], layers[drop.layerBegin()]);
}
break;
}
}
Timeline::Range Timeline::getDropRange() const
{
Range drop;
if (m_state != STATE_MOVING_RANGE)
return drop;
switch (m_range.type()) {
case Range::kCels: {
FrameNumber dx = m_hot_frame - m_clk_frame;
int dy = m_hot_layer - m_clk_layer;
int layerIdx;
FrameNumber frame;
layerIdx = dy+m_range.layerBegin();
layerIdx = MID(0, layerIdx, (int)m_layers.size() - m_range.layers());
frame = dx+m_range.frameBegin();
frame = MID(FrameNumber(0), frame, m_sprite->getTotalFrames() - m_range.frames());
drop.startRange(layerIdx, frame, m_range.type());
drop.endRange(
layerIdx+m_range.layers()-1,
(frame+m_range.frames()).previous());
break;
}
case Range::kFrames: {
FrameNumber frame = m_hot_frame;
if (frame > m_range.frameBegin() && frame <= m_range.frameEnd())
frame = m_range.frameBegin();
int layerIdx = getLayerIndex(m_layer);
drop.startRange(layerIdx, frame, m_range.type());
drop.endRange(layerIdx, frame);
break;
}
case Range::kLayers: {
int layer = m_hot_layer;
if (layer > m_range.layerBegin() && layer <= m_range.layerEnd())
layer = m_range.layerBegin();
drop.startRange(layer, m_frame, m_range.type());
drop.endRange(layer, m_frame);
break;
}
}
return drop;
}
void Timeline::Range::startRange(int layer, FrameNumber frame, Type type)
{
m_type = type;
@ -1715,7 +1911,7 @@ void Timeline::Range::endRange(int layer, FrameNumber frame)
void Timeline::Range::disableRange()
{
m_type = None;
m_type = kNone;
}
bool Timeline::Range::inRange(int layer) const
@ -1739,4 +1935,24 @@ bool Timeline::Range::inRange(int layer, FrameNumber frame) const
return inRange(layer) && inRange(frame);
}
void Timeline::Range::setLayers(int layers)
{
if (m_layerBegin <= m_layerEnd) m_layerEnd = m_layerBegin + layers - 1;
else m_layerBegin = m_layerEnd + layers - 1;
}
void Timeline::Range::setFrames(FrameNumber frames)
{
if (m_frameBegin <= m_frameEnd) m_frameEnd = (m_frameBegin + frames).previous();
else m_frameBegin = (m_frameEnd + frames).previous();
}
void Timeline::Range::displace(int layerDelta, FrameNumber frameDelta)
{
m_layerBegin += layerDelta;
m_layerEnd += layerDelta;
m_frameBegin += frameDelta;
m_frameEnd += frameDelta;
}
} // namespace app

View File

@ -33,6 +33,7 @@
namespace raster {
class Cel;
class Layer;
class LayerImage;
class Sprite;
}
@ -59,25 +60,29 @@ namespace app {
STATE_SELECTING_FRAMES,
STATE_SELECTING_CELS,
STATE_MOVING_SEPARATOR,
STATE_MOVING_LAYER,
STATE_MOVING_CEL,
STATE_MOVING_FRAME,
STATE_MOVING_RANGE,
STATE_MOVING_ONIONSKIN_RANGE_LEFT,
STATE_MOVING_ONIONSKIN_RANGE_RIGHT
};
struct Range {
enum Type { None, Cels, Frames, Layers };
enum Type { kNone, kCels, kFrames, kLayers };
Range() : m_type(None) { }
Range() : m_type(kNone) { }
Type type() const { return m_type; }
bool enabled() const { return m_type != None; }
bool enabled() const { return m_type != kNone; }
int layerBegin() const { return MIN(m_layerBegin, m_layerEnd); }
int layerEnd() const { return MAX(m_layerBegin, m_layerEnd); }
FrameNumber frameBegin() const { return MIN(m_frameBegin, m_frameEnd); }
FrameNumber frameEnd() const { return MAX(m_frameBegin, m_frameEnd); }
int layers() const { return layerEnd() - layerBegin() + 1; }
FrameNumber frames() const { return (frameEnd() - frameBegin()).next(); }
void setLayers(int layers);
void setFrames(FrameNumber frames);
void displace(int layerDelta, FrameNumber frameDelta);
bool inRange(int layer) const;
bool inRange(FrameNumber frame) const;
bool inRange(int layer, FrameNumber frame) const;
@ -86,6 +91,12 @@ namespace app {
void endRange(int layer, FrameNumber frame);
void disableRange();
bool operator==(const Range& o) const {
return m_type == o.m_type &&
layerBegin() == o.layerBegin() && layerEnd() == o.layerEnd() &&
frameBegin() == o.frameBegin() && frameEnd() == o.frameEnd();
}
private:
Type m_type;
int m_layerBegin;
@ -94,6 +105,8 @@ namespace app {
FrameNumber m_frameEnd;
};
enum DropOp { kMove, kCopy };
Timeline();
~Timeline();
@ -111,6 +124,10 @@ namespace app {
Range range() const { return m_range; }
// Drag-and-drop operations. These actions are used by commands
// called from popup menus.
void dropRange(DropOp op);
protected:
bool onProcessMessage(ui::Message* msg) OVERRIDE;
void onPreferredSize(ui::PreferredSizeEvent& ev) OVERRIDE;
@ -165,6 +182,11 @@ namespace app {
bool isLayerActive(int layer_index) const;
bool isFrameActive(FrameNumber frame) const;
void updateStatusBar();
Range getDropRange() const;
void dropCels(DropOp op, const Range& drop);
void dropFrames(DropOp op, const Range& drop);
void dropLayers(DropOp op, const Range& drop);
skin::Style* m_timelineStyle;
skin::Style* m_timelineBoxStyle;
@ -187,6 +209,8 @@ namespace app {
skin::Style* m_timelinePaddingBrStyle;
skin::Style* m_timelineSelectedCelStyle;
skin::Style* m_timelineRangeOutlineStyle;
skin::Style* m_timelineDropLayerDecoStyle;
skin::Style* m_timelineDropFrameDecoStyle;
Context* m_context;
Editor* m_editor;
Document* m_document;
@ -209,7 +233,7 @@ namespace app {
int m_clk_part;
int m_clk_layer;
FrameNumber m_clk_frame;
// Old mouse position (for scrolling).
// Absolute mouse positions for scrolling.
gfx::Point m_oldPos;
};

View File

@ -23,6 +23,7 @@
#include "app/undoers/set_cel_frame.h"
#include "raster/cel.h"
#include "raster/layer.h"
#include "undo/objects_container.h"
#include "undo/undoers_collector.h"
@ -31,8 +32,9 @@ namespace undoers {
using namespace undo;
SetCelFrame::SetCelFrame(ObjectsContainer* objects, Cel* cel)
: m_celId(objects->addObject(cel))
SetCelFrame::SetCelFrame(ObjectsContainer* objects, LayerImage* layer, Cel* cel)
: m_layerId(objects->addObject(layer))
, m_celId(objects->addObject(cel))
, m_frame(cel->getFrame())
{
}
@ -44,12 +46,13 @@ void SetCelFrame::dispose()
void SetCelFrame::revert(ObjectsContainer* objects, UndoersCollector* redoers)
{
LayerImage* layer = objects->getObjectT<LayerImage>(m_layerId);
Cel* cel = objects->getObjectT<Cel>(m_celId);
// Push another SetCelFrame as redoer
redoers->pushUndoer(new SetCelFrame(objects, cel));
redoers->pushUndoer(new SetCelFrame(objects, layer, cel));
cel->setFrame(m_frame);
layer->moveCel(cel, m_frame);
}
} // namespace undoers

View File

@ -26,7 +26,7 @@
namespace raster {
class Cel;
class Layer;
class LayerImage;
}
namespace app {
@ -36,13 +36,14 @@ namespace app {
class SetCelFrame : public UndoerBase {
public:
SetCelFrame(ObjectsContainer* objects, Cel* cel);
SetCelFrame(ObjectsContainer* objects, LayerImage* layer, Cel* cel);
void dispose() OVERRIDE;
size_t getMemSize() const OVERRIDE { return sizeof(*this); }
void revert(ObjectsContainer* objects, UndoersCollector* redoers) OVERRIDE;
private:
ObjectId m_layerId;
ObjectId m_celId;
FrameNumber m_frame;
};

View File

@ -1,268 +0,0 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/util/celmove.h"
#include "app/app.h"
#include "app/color.h"
#include "app/console.h"
#include "app/context_access.h"
#include "app/modules/gui.h"
#include "app/undo_transaction.h"
#include "app/undoers/add_cel.h"
#include "app/undoers/add_image.h"
#include "app/undoers/remove_cel.h"
#include "app/undoers/remove_image.h"
#include "app/undoers/replace_image.h"
#include "app/undoers/set_cel_frame.h"
#include "app/undoers/set_cel_opacity.h"
#include "app/undoers/set_cel_position.h"
#include "raster/blend.h"
#include "raster/cel.h"
#include "raster/image.h"
#include "raster/layer.h"
#include "raster/primitives.h"
#include "raster/sprite.h"
#include "raster/stock.h"
namespace app {
// These variables indicate what cel to move (and the sprite's frame
// indicates where to move it).
static Layer* src_layer = NULL; // TODO warning not thread safe
static Layer* dst_layer = NULL;
static FrameNumber src_frame = FrameNumber(0);
static FrameNumber dst_frame = FrameNumber(0);
static void remove_cel(Sprite* sprite, UndoTransaction& undo, LayerImage* layer, Cel* cel);
void set_frame_to_handle(Layer* _src_layer, FrameNumber _src_frame,
Layer* _dst_layer, FrameNumber _dst_frame)
{
src_layer = _src_layer;
src_frame = _src_frame;
dst_layer = _dst_layer;
dst_frame = _dst_frame;
}
void move_cel(ContextWriter& writer)
{
Document* document = writer.document();
Sprite* sprite = writer.sprite();
Cel *src_cel, *dst_cel;
ASSERT(src_layer != NULL);
ASSERT(dst_layer != NULL);
ASSERT(src_frame >= 0 && src_frame < sprite->getTotalFrames());
ASSERT(dst_frame >= 0 && dst_frame < sprite->getTotalFrames());
if (src_layer->isBackground()) {
copy_cel(writer);
return;
}
src_cel = static_cast<LayerImage*>(src_layer)->getCel(src_frame);
dst_cel = static_cast<LayerImage*>(dst_layer)->getCel(dst_frame);
UndoTransaction undo(writer.context(), "Move Cel", undo::ModifyDocument);
/* remove the 'dst_cel' (if it exists) because it must be
replaced with 'src_cel' */
if ((dst_cel != NULL) && (!dst_layer->isBackground() || src_cel != NULL))
remove_cel(sprite, undo, static_cast<LayerImage*>(dst_layer), dst_cel);
/* move the cel in the same layer */
if (src_cel != NULL) {
if (src_layer == dst_layer) {
if (undo.isEnabled())
undo.pushUndoer(new undoers::SetCelFrame(undo.getObjects(), src_cel));
src_cel->setFrame(dst_frame);
}
/* move the cel in different layers */
else {
if (undo.isEnabled())
undo.pushUndoer(new undoers::RemoveCel(undo.getObjects(), src_layer, src_cel));
static_cast<LayerImage*>(src_layer)->removeCel(src_cel);
src_cel->setFrame(dst_frame);
/* if we are moving a cel from a transparent layer to the
background layer, we have to clear the background of the
image */
if (!src_layer->isBackground() &&
dst_layer->isBackground()) {
Image* src_image = sprite->getStock()->getImage(src_cel->getImage());
Image* dst_image = crop_image(src_image,
-src_cel->getX(),
-src_cel->getY(),
sprite->getWidth(),
sprite->getHeight(), 0);
if (undo.isEnabled()) {
undo.pushUndoer(new undoers::ReplaceImage(undo.getObjects(),
sprite->getStock(), src_cel->getImage()));
undo.pushUndoer(new undoers::SetCelPosition(undo.getObjects(), src_cel));
undo.pushUndoer(new undoers::SetCelOpacity(undo.getObjects(), src_cel));
}
clear_image(dst_image, app_get_color_to_clear_layer(dst_layer));
composite_image(dst_image, src_image, src_cel->getX(), src_cel->getY(), 255, BLEND_MODE_NORMAL);
src_cel->setPosition(0, 0);
src_cel->setOpacity(255);
sprite->getStock()->replaceImage(src_cel->getImage(), dst_image);
delete src_image;
}
if (undo.isEnabled())
undo.pushUndoer(new undoers::AddCel(undo.getObjects(), dst_layer, src_cel));
static_cast<LayerImage*>(dst_layer)->addCel(src_cel);
}
}
undo.commit();
document->notifyCelMoved(src_layer, src_frame, dst_layer, dst_frame);
set_frame_to_handle(NULL, FrameNumber(0), NULL, FrameNumber(0));
}
void copy_cel(ContextWriter& writer)
{
Document* document = writer.document();
Sprite* sprite = writer.sprite();
UndoTransaction undo(writer.context(), "Move Cel", undo::ModifyDocument);
Cel *src_cel, *dst_cel;
ASSERT(src_layer != NULL);
ASSERT(dst_layer != NULL);
ASSERT(src_frame >= 0 && src_frame < sprite->getTotalFrames());
ASSERT(dst_frame >= 0 && dst_frame < sprite->getTotalFrames());
src_cel = static_cast<LayerImage*>(src_layer)->getCel(src_frame);
dst_cel = static_cast<LayerImage*>(dst_layer)->getCel(dst_frame);
// Remove the 'dst_cel' (if it exists) because it must be replaced
// with 'src_cel'
if ((dst_cel != NULL) && (!dst_layer->isBackground() || src_cel != NULL))
remove_cel(sprite, undo, static_cast<LayerImage*>(dst_layer), dst_cel);
// Move the cel in the same layer.
if (src_cel != NULL) {
Image *src_image = sprite->getStock()->getImage(src_cel->getImage());
Image *dst_image;
int image_index;
int dst_cel_x;
int dst_cel_y;
int dst_cel_opacity;
// If we are moving a cel from a transparent layer to the
// background layer, we have to clear the background of the image.
if (!src_layer->isBackground() &&
dst_layer->isBackground()) {
dst_image = crop_image(src_image,
-src_cel->getX(),
-src_cel->getY(),
sprite->getWidth(),
sprite->getHeight(), 0);
clear_image(dst_image, app_get_color_to_clear_layer(dst_layer));
composite_image(dst_image, src_image, src_cel->getX(), src_cel->getY(), 255, BLEND_MODE_NORMAL);
dst_cel_x = 0;
dst_cel_y = 0;
dst_cel_opacity = 255;
}
else {
dst_image = Image::createCopy(src_image);
dst_cel_x = src_cel->getX();
dst_cel_y = src_cel->getY();
dst_cel_opacity = src_cel->getOpacity();
}
// Add the image in the stock
image_index = sprite->getStock()->addImage(dst_image);
if (undo.isEnabled())
undo.pushUndoer(new undoers::AddImage(undo.getObjects(),
sprite->getStock(), image_index));
// Create the new cel
dst_cel = new Cel(dst_frame, image_index);
dst_cel->setPosition(dst_cel_x, dst_cel_y);
dst_cel->setOpacity(dst_cel_opacity);
if (undo.isEnabled())
undo.pushUndoer(new undoers::AddCel(undo.getObjects(), dst_layer, dst_cel));
static_cast<LayerImage*>(dst_layer)->addCel(dst_cel);
}
undo.commit();
document->notifyCelCopied(src_layer, src_frame, dst_layer, dst_frame);
set_frame_to_handle(NULL, FrameNumber(0), NULL, FrameNumber(0));
}
static void remove_cel(Sprite* sprite, UndoTransaction& undo, LayerImage *layer, Cel *cel)
{
Image *image;
Cel *it;
bool used;
if (sprite != NULL && layer->isImage() && cel != NULL) {
/* find if the image that use the cel to remove, is used by
another cels */
used = false;
for (FrameNumber frame(0); frame<sprite->getTotalFrames(); ++frame) {
it = layer->getCel(frame);
if (it != NULL && it != cel && it->getImage() == cel->getImage()) {
used = true;
break;
}
}
if (!used) {
// If the image is only used by this cel, we can remove the
// image from the stock.
image = sprite->getStock()->getImage(cel->getImage());
if (undo.isEnabled())
undo.pushUndoer(new undoers::RemoveImage(undo.getObjects(),
sprite->getStock(), cel->getImage()));
sprite->getStock()->removeImage(image);
delete image;
}
if (undo.isEnabled()) {
undo.pushUndoer(new undoers::RemoveCel(undo.getObjects(), layer, cel));
}
// Remove the cel
layer->removeCel(cel);
delete cel;
}
}
} // namespace app

View File

@ -1,43 +0,0 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef APP_UTIL_CELMOVE_H_INCLUDED
#define APP_UTIL_CELMOVE_H_INCLUDED
#pragma once
#include "raster/frame_number.h"
namespace raster {
class Cel;
class Layer;
class Sprite;
}
namespace app {
class ContextWriter;
using namespace raster;
void set_frame_to_handle(Layer* src_layer, FrameNumber src_frame,
Layer* dst_layer, FrameNumber dst_frame);
void move_cel(ContextWriter& writer);
void copy_cel(ContextWriter& writer);
} // namespace app
#endif

View File

@ -39,6 +39,9 @@ namespace raster {
int getY() const { return m_y; }
int getOpacity() const { return m_opacity; }
// You should change the frame only if the cel isn't member of a
// layer. If the cel is already in a layer, you should use
// LayerImage::moveCel() member function.
void setFrame(FrameNumber frame) { m_frame = frame; }
void setImage(int image) { m_image = image; }
void setPosition(int x, int y) { m_x = x; m_y = y; }

View File

@ -38,6 +38,8 @@ namespace raster {
FrameNumber& operator--() { --m_value; return *this; }
FrameNumber operator++(int) { FrameNumber old(*this); ++m_value; return old; }
FrameNumber operator--(int) { FrameNumber old(*this); --m_value; return old; }
FrameNumber& operator+=(const FrameNumber& o) { m_value += o.m_value; return *this; }
FrameNumber& operator-=(const FrameNumber& o) { m_value -= o.m_value; return *this; }
bool operator<(const FrameNumber& o) const { return m_value < o.m_value; }
bool operator>(const FrameNumber& o) const { return m_value > o.m_value; }
bool operator<=(const FrameNumber& o) const { return m_value <= o.m_value; }

View File

@ -174,6 +174,13 @@ void LayerImage::removeCel(Cel *cel)
m_cels.erase(it);
}
void LayerImage::moveCel(Cel* cel, FrameNumber frame)
{
removeCel(cel);
cel->setFrame(frame);
addCel(cel);
}
const Cel* LayerImage::getCel(FrameNumber frame) const
{
CelConstIterator it = getCelBegin();

View File

@ -107,6 +107,7 @@ namespace raster {
void addCel(Cel *cel);
void removeCel(Cel *cel);
void moveCel(Cel *cel, FrameNumber frame);
const Cel* getCel(FrameNumber frame) const;
Cel* getCel(FrameNumber frame);