#include "filmstripcommand.h"
#include "tapp.h"
#include "toonz/palettecontroller.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/txsheethandle.h"
#include "toonz/tscenehandle.h"
#include "toonz/tpalettehandle.h"
#include "toonz/tframehandle.h"
#include "tinbetween.h"
#include "tvectorimage.h"
#include "ttoonzimage.h"
#include "toonzqt/selection.h"
#include "toonzqt/dvdialog.h"
#include "drawingdata.h"
#include "toonzqt/strokesdata.h"
#include "toonzqt/rasterimagedata.h"
#include "timagecache.h"
#include "tools/toolutils.h"
#include "toonzqt/icongenerator.h"
#include "tundo.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/txshsoundlevel.h"
#include "toonz/txshpalettelevel.h"
#include "toonz/txshpalettecolumn.h"
#include "toonz/txshsoundcolumn.h"
#include "toonz/txsheet.h"
#include "toonz/txshcell.h"
#include "toonz/toonzscene.h"
#include "toonz/levelset.h"
#include "toonz/txshleveltypes.h"
#include "toonz/toonzimageutils.h"
#include "toonz/trasterimageutils.h"
#include "toonz/tcamera.h"
#include "trop.h"
#include "toonzqt/gutil.h"
#include "historytypes.h"
#include <QApplication>
#include <QClipboard>
//=============================================================================
TFrameId operator+(const TFrameId &fid, int d)
{
return TFrameId(fid.getNumber() + d, fid.getLetter());
}
//-----------------------------------------------------------------------------
//=============================================================================
// makeSpaceForFid
// se necessario renumera gli altri fid del livello in modo che
// framesToInsert siano liberi
// n.b. modifica
//-----------------------------------------------------------------------------
void makeSpaceForFids(TXshSimpleLevel *sl, const std::set<TFrameId> &framesToInsert)
{
std::vector<TFrameId> fids;
sl->getFids(fids);
std::set<TFrameId>::const_iterator it;
std::set<TFrameId> touchedFids;
for (it = framesToInsert.begin(); it != framesToInsert.end(); ++it) {
// devo inserire fid
TFrameId fid(*it);
std::vector<TFrameId>::iterator j;
// controllo che non ci sia gia'
j = fids.begin();
while (j = std::find(j, fids.end(), fid), j != fids.end()) {
// c'e' gia' un fid. faccio fid -> fid + 1
touchedFids.insert(fid);
fid = fid + 1;
touchedFids.insert(fid);
*j = fid;
// adesso devo controllare che il nuovo fid non ci sia gia' nella
// parte restante dell'array fids
++j;
}
}
if (!touchedFids.empty()) {
sl->renumber(fids);
invalidateIcons(sl, touchedFids);
sl->setDirtyFlag(true);
}
}
//=============================================================================
namespace
{
//-----------------------------------------------------------------------------
void copyFramesWithoutUndo(TXshSimpleLevel *sl, std::set<TFrameId> &frames)
{
QClipboard *clipboard = QApplication::clipboard();
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
DrawingData *data = new DrawingData();
data->setLevelFrames(sl, frames);
clipboard->setMimeData(data, QClipboard::Clipboard);
}
//-----------------------------------------------------------------------------
// frames is a selected frames in the film strip
bool pasteAreasWithoutUndo(const QMimeData *data, TXshSimpleLevel *sl, std::set<TFrameId> &frames,
TTileSet **tileSet, std::map<TFrameId, std::set<int>> &indices)
{
//paste between the same level must keep the palette unchanged
//Not emitting PaletteChanged signal can avoid the update of color model
bool paletteHasChanged = true;
if (const StrokesData *strokesData = dynamic_cast<const StrokesData *>(data)) {
std::set<TFrameId>::iterator it;
for (it = frames.begin(); it != frames.end(); ++it) {
TImageP img = sl->getFrame(*it, true);
if (!img) {
img = sl->createEmptyFrame();
sl->setFrame(*it, img);
}
TVectorImageP vi = img;
TToonzImageP ti = img;
TRasterImageP ri = img;
if (vi) {
std::set<int> imageIndices;
strokesData->getImage(vi, imageIndices, true);
indices[*it] = imageIndices;
} else if (ti) {
ToonzImageData *toonzImageData = strokesData->toToonzImageData(ti);
return pasteAreasWithoutUndo(toonzImageData, sl, frames, tileSet, indices);
} else if (ri) {
double dpix, dpiy;
ri->getDpi(dpix, dpiy);
if (dpix == 0 || dpiy == 0) {
TPointD dpi = sl->getScene()->getCurrentCamera()->getDpi();
dpix = dpi.x;
dpiy = dpi.y;
ri->setDpi(dpix, dpiy);
}
FullColorImageData *fullColorImageData = strokesData->toFullColorImageData(ri);
return pasteAreasWithoutUndo(fullColorImageData, sl, frames, tileSet, indices);
}
}
}
// when pasting the copied area selected with the selection tool
else if (const RasterImageData *rasterImageData = dynamic_cast<const RasterImageData *>(data)) {
std::set<TFrameId>::iterator it;
for (it = frames.begin(); it != frames.end(); ++it) {
if ((sl->getType() == PLI_XSHLEVEL || sl->getType() == TZP_XSHLEVEL) && dynamic_cast<const FullColorImageData *>(rasterImageData)) {
DVGui::error(QObject::tr("The copied selection cannot be pasted in the current drawing."));
return false;
}
//obtain the image data to be pasted
TImageP img = sl->getFrame(*it, true);
if (!img) {
img = sl->createEmptyFrame();
sl->setFrame(*it, img);
}
TToonzImageP ti = img;
TRasterImageP ri = img;
TVectorImageP vi = img;
//pasting TLV
if (ti) {
TRasterP ras;
double dpiX, dpiY;
std::vector<TRectD> rects;
std::vector<TStroke> strokes;
std::vector<TStroke> originalStrokes;
TAffine affine;
// style will be merged in getData() if the palettes are different
int styleCountBeforePasteImage = ti->getPalette()->getStyleCount();
rasterImageData->getData(ras, dpiX, dpiY, rects, strokes, originalStrokes, affine, ti->getPalette());
if (styleCountBeforePasteImage == ti->getPalette()->getStyleCount())
paletteHasChanged = false;
double imgDpiX, imgDpiY;
ti->getDpi(imgDpiX, imgDpiY);
TScale sc(imgDpiX / dpiX, imgDpiY / dpiY);
affine *= sc;
int i;
TRectD boxD;
if (rects.size() > 0)
boxD = rects[0];
if (strokes.size() > 0)
boxD = strokes[0].getBBox();
for (i = 0; i < rects.size(); i++)
boxD += rects[i];
for (i = 0; i < strokes.size(); i++)
boxD += strokes[i].getBBox();
boxD = affine * boxD;
TRect box = ToonzImageUtils::convertWorldToRaster(boxD, ti);
TPoint pos = box.getP00();
if (pos.x < 0)
pos.x = 0;
if (pos.y < 0)
pos.y = 0;
if (*tileSet == 0)
*tileSet = new TTileSetCM32(ti->getRaster()->getSize());
if (box.overlaps(ti->getRaster()->getBounds()))
(*tileSet)->add(ti->getRaster(), box);
else
(*tileSet)->add(0);
TRasterCM32P app = ras;
if (app) {
TRop::over(ti->getRaster(), app, pos, affine);
ToolUtils::updateSaveBox(sl, *it);
}
} else if (ri) {
TRasterP ras;
double dpiX = 0, dpiY = 0;
double imgDpiX = 0, imgDpiY = 0;
std::vector<TRectD> rects;
std::vector<TStroke> strokes;
std::vector<TStroke> originalStrokes;
TAffine affine;
TPointD cameraDpi;
ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
if (scene) {
TCamera *camera = scene->getCurrentCamera();
cameraDpi = camera->getDpi();
if (cameraDpi.x == 0.0 || cameraDpi.y == 0.0) // it should never happen. just in case...
return false;
} else
return false;
rasterImageData->getData(ras, dpiX, dpiY, rects, strokes, originalStrokes, affine, ri->getPalette());
if (dpiX == 0 || dpiY == 0) {
dpiX = cameraDpi.x;
dpiY = cameraDpi.y;
}
ri->getDpi(imgDpiX, imgDpiY);
if (imgDpiX == 0 || imgDpiY == 0) {
imgDpiX = cameraDpi.x;
imgDpiY = cameraDpi.y;
}
TScale sc(imgDpiX / dpiX, imgDpiY / dpiY);
affine *= sc;
int i;
TRectD boxD;
if (rects.size() > 0)
boxD = rects[0];
if (strokes.size() > 0)
boxD = strokes[0].getBBox();
for (i = 0; i < rects.size(); i++)
boxD += rects[i];
for (i = 0; i < strokes.size(); i++)
boxD += strokes[i].getBBox();
boxD = affine * boxD;
TRect box = TRasterImageUtils::convertWorldToRaster(boxD, ri);
TPoint pos = box.getP00();
if (*tileSet == 0)
*tileSet = new TTileSetFullColor(ri->getRaster()->getSize());
if (box.overlaps(ri->getRaster()->getBounds()))
(*tileSet)->add(ri->getRaster(), box);
else
(*tileSet)->add(0);
TRasterCM32P app = ras;
if (app)
TRop::over(ri->getRaster(), app, ri->getPalette(), pos, affine);
else
TRop::over(ri->getRaster(), ras, pos, affine);
} else if (vi) {
StrokesData *strokesData = rasterImageData->toStrokesData(sl->getScene());
return pasteAreasWithoutUndo(strokesData, sl, frames, tileSet, indices);
}
}
} else
return false;
if (paletteHasChanged)
TApp::instance()->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
invalidateIcons(sl, frames);
sl->setDirtyFlag(true);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
return true;
}
//-----------------------------------------------------------------------------
// Se insert == true incolla i frames nel livello inserendoli, se necessario spostando
// verso il basso i preesistenti. Altrimenti incolla i frames sostituendoli.
//il parametro clone images si mette a false quando questa funzione viene usata per un undo/redo.
bool pasteFramesWithoutUndo(const DrawingData *data, TXshSimpleLevel *sl,
std::set<TFrameId> &frames,
DrawingData::ImageSetType setType, bool cloneImages,
bool &keepOriginalPalette,
bool isRedo = false)
{
if (!data || (frames.empty() && setType != DrawingData::OVER_FRAMEID))
return false;
bool isPaste = data->getLevelFrames(sl, frames, setType, cloneImages, keepOriginalPalette, isRedo);
if (!isPaste)
return false;
if (keepOriginalPalette)
invalidateIcons(sl, frames);
else {
std::vector<TFrameId> sl_fids;
sl->getFids(sl_fids);
invalidateIcons(sl, sl_fids);
}
sl->setDirtyFlag(true);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
TApp::instance()->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
return true;
}
//-----------------------------------------------------------------------------
// "Svuota" i frames: i frames vengono buttati e al loro posto
// vengono inseriti frames vuoti.
std::map<TFrameId, QString> clearFramesWithoutUndo(const TXshSimpleLevelP &sl,
const std::set<TFrameId> &frames)
{
std::map<TFrameId, QString> clearedFrames;
if (!sl || frames.empty())
return clearedFrames;
std::set<TFrameId>::const_iterator it;
for (it = frames.begin(); it != frames.end(); ++it) {
TFrameId frameId = *it;
/* UINT にキャストしたらだめだろ */
//QString id = "clearFrames"+QString::number((UINT)sl.getPointer())+"-"+QString::number(it->getNumber());
QString id = "clearFrames" + QString::number((uintptr_t)sl.getPointer()) + "-" + QString::number(it->getNumber());
TImageCache::instance()->add(id, sl->getFrame(frameId, false));
clearedFrames[frameId] = id;
sl->eraseFrame(frameId);
sl->setFrame(*it, sl->createEmptyFrame());
}
invalidateIcons(sl.getPointer(), frames);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
sl->setDirtyFlag(true);
return clearedFrames;
}
//-----------------------------------------------------------------------------
// Rimuove i frames dal livello
void removeFramesWithoutUndo(const TXshSimpleLevelP &sl,
const std::set<TFrameId> &frames)
{
if (!sl || frames.empty())
return;
std::set<TFrameId>::const_iterator it;
for (it = frames.begin(); it != frames.end(); ++it)
sl->eraseFrame(*it);
removeIcons(sl.getPointer(), frames);
sl->setDirtyFlag(true);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
//-----------------------------------------------------------------------------
void cutFramesWithoutUndo(TXshSimpleLevel *sl, std::set<TFrameId> &frames)
{
std::map<TFrameId, QString> imageSet;
HookSet *levelHooks = sl->getHookSet();
int currentFrameIndex = TApp::instance()->getCurrentFrame()->getFrameIndex();
std::set<TFrameId>::const_iterator it;
int i = 0;
for (it = frames.begin(); it != frames.end(); ++it, i++) {
TFrameId frameId = *it;
//QString id = "cutFrames"+QString::number((UINT)sl)+"-"+QString::number(it->getNumber());
QString id = "cutFrames" + QString::number((uintptr_t)sl) + "-" + QString::number(it->getNumber());
TImageCache::instance()->add(id, sl->getFrame(frameId, false));
imageSet[frameId] = id;
}
removeIcons(sl, frames);
sl->setDirtyFlag(true);
QClipboard *clipboard = QApplication::clipboard();
DrawingData *data = new DrawingData();
data->setFrames(imageSet, sl, *levelHooks);
clipboard->setMimeData(data, QClipboard::Clipboard);
for (it = frames.begin(); it != frames.end(); ++it, i++) {
sl->eraseFrame(*it);
}
std::vector<TFrameId> newFids;
sl->getFids(newFids);
//Devo settare i nuovi fids al frame handle prima di mandare la notifica di cambiamento del livello
TApp::instance()->getCurrentFrame()->setFrameIds(newFids);
TApp::instance()->getCurrentFrame()->setFrameIndex(currentFrameIndex);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
sl->setDirtyFlag(true);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
//-----------------------------------------------------------------------------
void insertNotEmptyframes(const TXshSimpleLevelP &sl,
const std::map<TFrameId, QString> &framesToInsert)
{
if (framesToInsert.empty() || !sl)
return;
std::vector<TFrameId> fids;
sl->getFids(fids);
std::set<TFrameId> frames;
std::map<TFrameId, QString>::const_iterator it = framesToInsert.begin();
for (it; it != framesToInsert.end(); it++)
frames.insert(it->first);
makeSpaceForFids(sl.getPointer(), frames);
for (it = framesToInsert.begin(); it != framesToInsert.end(); ++it) {
//QString cacheId = "UndoImage"+QString::number(id)+"-"+QString::number(it->first.getNumber());
TImageP img = TImageCache::instance()->get(it->second, false);
TImageCache::instance()->remove(it->second);
assert(img);
sl->setFrame(it->first, img);
}
invalidateIcons(sl.getPointer(), frames);
sl->setDirtyFlag(true);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
//=============================================================================
// PasteRasterAreasUndo
//-----------------------------------------------------------------------------
class PasteRasterAreasUndo : public TUndo
{
TXshSimpleLevelP m_level;
std::set<TFrameId> m_frames;
TTileSet *m_tiles;
RasterImageData *m_data;
TPaletteP m_oldPalette;
TPaletteP m_newPalette;
bool m_isFrameInserted;
public:
PasteRasterAreasUndo(TXshSimpleLevel *sl, const std::set<TFrameId> &frames, TTileSet *tiles, RasterImageData *data,
TPalette *plt, bool isFrameInserted)
: TUndo(), m_level(sl), m_frames(frames), m_tiles(tiles), m_oldPalette(plt->clone()), m_isFrameInserted(isFrameInserted)
{
m_data = data->clone();
assert(m_tiles->getTileCount() == m_frames.size());
}
~PasteRasterAreasUndo()
{
if (m_tiles)
delete m_tiles;
if (m_data)
delete m_data;
}
void onAdd()
{
m_newPalette = m_level->getPalette()->clone();
}
void undo() const
{
if (!m_level || m_frames.empty())
return;
std::set<TFrameId> frames = m_frames;
if (m_isFrameInserted) {
// Faccio remove dei frame incollati
removeFramesWithoutUndo(m_level, frames);
} else {
std::set<TFrameId>::const_iterator it;
int i = 0;
TTileSetCM32 *tileSetCM = dynamic_cast<TTileSetCM32 *>(m_tiles);
TTileSetFullColor *tileSetFC = dynamic_cast<TTileSetFullColor *>(m_tiles);
for (it = m_frames.begin(); it != m_frames.end(); it++, i++) {
TImageP image = m_level->getFrame(*it, true);
if (!image)
continue;
TRasterImageP ri(image);
TToonzImageP ti(image);
if (tileSetCM) {
const TTileSetCM32::Tile *tile = tileSetCM->getTile(i);
if (!tile)
continue;
TRasterCM32P tileRas;
tile->getRaster(tileRas);
assert(ti);
ti->getRaster()->copy(tileRas, tile->m_rasterBounds.getP00());
ToolUtils::updateSaveBox(m_level, *it);
} else if (tileSetFC) {
const TTileSetFullColor::Tile *tile = tileSetFC->getTile(i);
if (!tile)
continue;
TRasterP tileRas;
tile->getRaster(tileRas);
assert(ri);
ri->getRaster()->copy(tileRas, tile->m_rasterBounds.getP00());
}
}
}
//Setto la vecchia paletta al livello
if (m_oldPalette.getPointer()) {
m_level->getPalette()->assign(m_oldPalette->clone());
TApp::instance()->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
}
invalidateIcons(m_level.getPointer(), m_frames);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
void redo() const
{
if (!m_level || m_frames.empty())
return;
if (m_isFrameInserted) {
assert(m_frames.size() == 1);
TImageP img = m_level->createEmptyFrame();
m_level->setFrame(*m_frames.begin(), img);
}
std::set<TFrameId> frames = m_frames;
std::set<TFrameId>::const_iterator it;
for (it = m_frames.begin(); it != m_frames.end(); it++) {
TImageP image = m_level->getFrame(*it, true);
TRasterImageP ri = image;
TToonzImageP ti = image;
if (ti) {
TRasterP ras;
double dpiX, dpiY;
std::vector<TRectD> rects;
std::vector<TStroke> strokes;
std::vector<TStroke> originalStrokes;
TAffine affine;
m_data->getData(ras, dpiX, dpiY, rects, strokes, originalStrokes, affine, ti->getPalette());
double imgDpiX, imgDpiY;
ti->getDpi(imgDpiX, imgDpiY);
TScale sc(imgDpiX / dpiX, imgDpiY / dpiY);
affine *= sc;
int i;
TRectD boxD;
if (rects.size() > 0)
boxD = rects[0];
if (strokes.size() > 0)
boxD = strokes[0].getBBox();
for (i = 0; i < rects.size(); i++)
boxD += rects[i];
for (i = 0; i < strokes.size(); i++)
boxD += strokes[i].getBBox();
boxD = affine * boxD;
TRect box = ToonzImageUtils::convertWorldToRaster(boxD, ti);
TPoint pos = box.getP00();
TRasterCM32P app = ras;
TRop::over(ti->getRaster(), app, pos, affine);
ToolUtils::updateSaveBox(m_level, *it);
} else if (ri) {
TRasterP ras;
double dpiX, dpiY;
std::vector<TRectD> rects;
std::vector<TStroke> strokes;
std::vector<TStroke> originalStrokes;
TAffine affine;
m_data->getData(ras, dpiX, dpiY, rects, strokes, originalStrokes, affine, ri->getPalette());
double imgDpiX, imgDpiY;
if (dpiX == 0 && dpiY == 0) {
ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
if (scene) {
TCamera *camera = scene->getCurrentCamera();
TPointD dpi = dpi = camera->getDpi();
dpiX = dpi.x;
dpiY = dpi.y;
} else
return;
}
ri->getDpi(imgDpiX, imgDpiY);
TScale sc(imgDpiX / dpiX, imgDpiY / dpiY);
affine *= sc;
int i;
TRectD boxD;
if (rects.size() > 0)
boxD = rects[0];
if (strokes.size() > 0)
boxD = strokes[0].getBBox();
for (i = 0; i < rects.size(); i++)
boxD += rects[i];
for (i = 0; i < strokes.size(); i++)
boxD += strokes[i].getBBox();
boxD = affine * boxD;
TRect box = TRasterImageUtils::convertWorldToRaster(boxD, ri);
TPoint pos = box.getP00();
TRasterCM32P app = ras;
if (app)
TRop::over(ri->getRaster(), app, ri->getPalette(), pos, affine);
else
TRop::over(ri->getRaster(), ras, pos, affine);
}
}
if (m_newPalette.getPointer()) {
m_level->getPalette()->assign(m_newPalette->clone());
TApp::instance()->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
}
invalidateIcons(m_level.getPointer(), m_frames);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
int getSize() const
{
return sizeof(*this) + sizeof(*m_data) + sizeof(*m_tiles);
}
QString getHistoryString()
{
QString str = QObject::tr("Paste : Level %1 : Frame ")
.arg(QString::fromStdWString(m_level->getName()));
std::set<TFrameId>::const_iterator it;
for (it = m_frames.begin(); it != m_frames.end(); it++) {
if (it != m_frames.begin())
str += QString(", ");
str += QString::number((*it).getNumber());
}
return str;
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
//=============================================================================
// PasteVectorAreasUndo
//-----------------------------------------------------------------------------
class PasteVectorAreasUndo : public TUndo
{
TXshSimpleLevelP m_level;
std::set<TFrameId> m_frames;
std::map<TFrameId, std::set<int>> m_indices;
StrokesData *m_data;
TPaletteP m_oldPalette;
TPaletteP m_newPalette;
bool m_isFrameInserted;
public:
PasteVectorAreasUndo(TXshSimpleLevel *sl, const std::set<TFrameId> &frames, std::map<TFrameId, std::set<int>> &indices,
StrokesData *data, TPalette *plt, bool isFrameInserted)
: TUndo(), m_level(sl), m_frames(frames), m_indices(indices), m_oldPalette(plt->clone()), m_isFrameInserted(isFrameInserted)
{
m_data = data->clone();
}
~PasteVectorAreasUndo()
{
if (m_data)
delete m_data;
}
void onAdd()
{
m_newPalette = m_level->getPalette()->clone();
}
void undo() const
{
if (!m_level || m_frames.empty())
return;
std::set<TFrameId> frames = m_frames;
if (m_isFrameInserted) {
// Faccio remove dei frame incollati
removeFramesWithoutUndo(m_level, frames);
} else {
std::set<TFrameId>::const_iterator it;
for (it = m_frames.begin(); it != m_frames.end(); it++) {
TVectorImageP img = m_level->getFrame(*it, true);
assert(img);
if (!img)
continue;
std::map<TFrameId, std::set<int>>::const_iterator mapIt = m_indices.find(*it);
if (mapIt == m_indices.end())
continue;
std::set<int> imageIndices = mapIt->second;
;
std::set<int>::const_iterator it2 = imageIndices.end();
while (it2 != imageIndices.begin()) {
it2--;
img->removeStroke(*it2);
}
}
}
//Setto la vecchia paletta al livello
if (m_oldPalette.getPointer()) {
m_level->getPalette()->assign(m_oldPalette->clone());
TApp::instance()->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
}
invalidateIcons(m_level.getPointer(), m_frames);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
void redo() const
{
if (!m_level || m_frames.empty())
return;
if (m_isFrameInserted) {
assert(m_frames.size() == 1);
TVectorImageP img = m_level->createEmptyFrame();
m_level->setFrame(*m_frames.begin(), img);
}
std::set<TFrameId> frames = m_frames;
std::set<TFrameId>::const_iterator it;
for (it = m_frames.begin(); it != m_frames.end(); it++) {
TVectorImageP img = m_level->getFrame(*it, true);
assert(img);
if (!img)
continue;
std::set<int> app;
m_data->getImage(img, app, true);
}
if (m_newPalette.getPointer()) {
m_level->getPalette()->assign(m_newPalette->clone());
TApp::instance()->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
}
invalidateIcons(m_level.getPointer(), m_frames);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
int getSize() const
{
return sizeof(*this) + sizeof(*m_data);
}
QString getHistoryString()
{
QString str = QObject::tr("Paste : Level %1 : Frame ")
.arg(QString::fromStdWString(m_level->getName()));
std::set<TFrameId>::const_iterator it;
for (it = m_frames.begin(); it != m_frames.end(); it++) {
if (it != m_frames.begin())
str += QString(", ");
str += QString::number((*it).getNumber());
}
return str;
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
/*//=============================================================================
// PasteAreasUndo
//-----------------------------------------------------------------------------
class PasteAreasUndo : public TUndo
{
TXshSimpleLevelP m_level;
std::set<TFrameId> m_frames;
TPaletteP m_oldPalette;
TPaletteP m_newPalette;
bool m_isFrameInserted;
public:
PasteAreasUndo(TXshSimpleLevel *sl, const std::set<TFrameId> &frames, bool isFrameInserted)
: TUndo()
, m_level(sl)
, m_frames(frames)
, m_isFrameInserted(isFrameInserted)
{
m_oldPalette = m_level->getPalette()->clone();
if(!m_isFrameInserted)
{
std::set<TFrameId>::iterator it;
for(it=m_frames.begin(); it!=m_frames.end(); it++)
{
TImageP img = m_level->getFrame(*it,true);
TImageCache::instance()->add("PasteAreasUndoOld"+QString::number((UINT)this)+QString::number((*it).getNumber()), img->cloneImage());
}
}
}
void onAdd()
{
m_newPalette = m_level->getPalette()->clone();
std::set<TFrameId>::iterator it;
for(it=m_frames.begin(); it!=m_frames.end(); it++)
{
TImageP img = m_level->getFrame(*it,true);
TImageCache::instance()->add("PasteAreasUndoNew"+QString::number((UINT)this)+QString::number((*it).getNumber()), img->cloneImage());
}
}
~PasteAreasUndo() {
std::set<TFrameId>::const_iterator it;
if(!m_isFrameInserted)
{
for(it=m_frames.begin(); it!=m_frames.end(); it++)
TImageCache::instance()->remove("PasteAreasUndoOld"+QString::number((UINT)this)+QString::number((*it).getNumber()));
}
for(it=m_frames.begin(); it!=m_frames.end(); it++)
TImageCache::instance()->remove("PasteAreasUndoNew"+QString::number((UINT)this)+QString::number((*it).getNumber()));
}
void undo() const
{
std::set<TFrameId> frames = m_frames;
if(m_isFrameInserted)
{
// Faccio remove dei frame incollati
removeFramesWithoutUndo(m_level, frames);
}
else
{
std::set<TFrameId>::const_iterator it;
for(it=m_frames.begin(); it!=m_frames.end(); it++)
{
TImageP img = TImageCache::instance()->get("PasteAreasUndoOld"+QString::number((UINT)this)+QString::number((*it).getNumber()),true);
assert(img);
m_level->setFrame(*it,img);
}
}
//Setto la vecchia paletta al livello
if(m_oldPalette.getPointer())
{
m_level->getPalette()->assign(m_oldPalette->clone());
TApp::instance()->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
}
invalidateIcons(m_level.getPointer(), m_frames);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
void redo() const
{
if(!m_level || m_frames.empty()) return;
std::set<TFrameId> frames = m_frames;
std::set<TFrameId>::const_iterator it;
for(it=m_frames.begin(); it!=m_frames.end(); it++)
{
TImageP img = TImageCache::instance()->get("PasteAreasUndoNew"+QString::number((UINT)this)+QString::number((*it).getNumber()),true);
if(!img) continue;
m_level->setFrame(*it, img, true);
}
if(m_newPalette.getPointer())
{
m_level->getPalette()->assign(m_newPalette->clone());
TApp::instance()->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
}
invalidateIcons(m_level.getPointer(), m_frames);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
int getSize() const{
return sizeof(*this);
}
};*/
//=============================================================================
// PasteFramesUndo
//-----------------------------------------------------------------------------
class PasteFramesUndo : public TUndo
{
TXshSimpleLevelP m_sl;
std::set<TFrameId> m_frames;
std::vector<TFrameId> m_oldLevelFrameId;
TPaletteP m_oldPalette;
DrawingData *m_oldData;
DrawingData *m_newData;
DrawingData::ImageSetType m_setType;
HookSet *m_oldLevelHooks;
bool m_keepOriginalPalette;
public:
PasteFramesUndo(TXshSimpleLevel *sl,
const std::set<TFrameId> &frames,
const std::vector<TFrameId> &oldLevelFrameId,
TPaletteP oldPalette,
DrawingData::ImageSetType setType,
HookSet *oldLevelHooks,
bool keepOriginalPalette,
DrawingData *oldData = 0)
: m_sl(sl), m_frames(frames), m_oldLevelFrameId(oldLevelFrameId), m_oldPalette(oldPalette), m_setType(setType), m_keepOriginalPalette(keepOriginalPalette), m_oldData(oldData), m_oldLevelHooks(oldLevelHooks)
{
QClipboard *clipboard = QApplication::clipboard();
QMimeData *data = cloneData(clipboard->mimeData());
m_newData = dynamic_cast<DrawingData *>(data);
assert(m_newData);
}
~PasteFramesUndo()
{
if (m_oldData)
m_oldData->releaseData();
if (m_newData)
m_newData->releaseData();
}
void undo() const
{
TSelection *selection = TSelection::getCurrent();
if (selection)
selection->selectNone();
std::set<TFrameId> frames = m_frames;
// Faccio remove dei frame incollati
if (m_setType != DrawingData::OVER_SELECTION)
removeFramesWithoutUndo(m_sl, frames);
// Renumero i frame con i vecchi fids
if (m_setType == DrawingData::INSERT) {
assert(m_sl->getFrameCount() == m_oldLevelFrameId.size());
m_sl->renumber(m_oldLevelFrameId);
TApp::instance()->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
//Reinserisco i vecchi frame che ho sovrascritto
if (m_setType != DrawingData::INSERT) {
std::set<TFrameId> framesToModify;
m_oldData->getFrames(framesToModify);
bool dummy = true;
// Incollo i frames sovrascrivendoli
pasteFramesWithoutUndo(m_oldData, m_sl.getPointer(), framesToModify, DrawingData::OVER_SELECTION, true, dummy);
}
//Setto la vecchia paletta al livello
if (m_oldPalette.getPointer()) {
TPalette *levelPalette = m_sl->getPalette();
if (levelPalette)
levelPalette->assign(m_oldPalette.getPointer());
TApp *app = TApp::instance();
if (app->getCurrentLevel()->getLevel() == m_sl.getPointer())
app->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
}
//update all icons
std::vector<TFrameId> sl_fids;
m_sl.getPointer()->getFids(sl_fids);
invalidateIcons(m_sl.getPointer(), sl_fids);
*m_sl->getHookSet() = *m_oldLevelHooks;
}
void redo() const
{
if (!m_sl || m_frames.empty())
return;
TSelection *selection = TSelection::getCurrent();
if (selection)
selection->selectNone();
std::set<TFrameId> frames = m_frames;
bool keepOriginalPalette = m_keepOriginalPalette;
pasteFramesWithoutUndo(m_newData, m_sl.getPointer(), frames, m_setType, true, keepOriginalPalette, true);
}
int getSize() const
{
return sizeof(*this);
}
QString getHistoryString()
{
QString str = QObject::tr("Paste : Level %1 : Frame ")
.arg(QString::fromStdWString(m_sl->getName()));
std::set<TFrameId>::const_iterator it;
for (it = m_frames.begin(); it != m_frames.end(); it++) {
if (it != m_frames.begin())
str += QString(", ");
str += QString::number((*it).getNumber());
}
return str;
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
//=============================================================================
// DeleteFramesUndo
//-----------------------------------------------------------------------------
class DeleteFramesUndo : public TUndo
{
TXshSimpleLevel *m_sl;
std::set<TFrameId> m_frames;
DrawingData *m_oldData;
DrawingData *m_newData;
public:
DeleteFramesUndo(TXshSimpleLevel *sl,
std::set<TFrameId> &frames,
DrawingData *oldData,
DrawingData *newData)
: m_sl(sl), m_frames(frames), m_oldData(oldData), m_newData(newData)
{
}
~DeleteFramesUndo()
{
if (m_oldData)
m_oldData->releaseData();
if (m_newData)
m_newData->releaseData();
}
void pasteFramesFromData(const DrawingData *data) const
{
std::set<TFrameId> frames = m_frames;
bool dummy = true;
// Incollo i frames sovrascrivendoli
pasteFramesWithoutUndo(data, m_sl, frames, DrawingData::OVER_SELECTION, true, dummy);
}
void undo() const
{
pasteFramesFromData(m_oldData);
}
//OSS.: Non posso usare il metodo "clearFramesWithoutUndo(...)" perche'
// genera un NUOVO frame vuoto, perdendo quello precedente e le eventuali
// modifiche che ad esso possono essere state fatte successivamente.
void redo() const
{
pasteFramesFromData(m_newData);
}
int getSize() const
{
return sizeof(*this);
}
QString getHistoryString()
{
QString str = QObject::tr("Delete Frames : Level %1 : Frame ")
.arg(QString::fromStdWString(m_sl->getName()));
std::set<TFrameId>::const_iterator it;
for (it = m_frames.begin(); it != m_frames.end(); it++) {
if (it != m_frames.begin())
str += QString(", ");
str += QString::number((*it).getNumber());
}
return str;
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
//=============================================================================
//-----------------------------------------------------------------------------
class CutFramesUndo : public TUndo
{
TXshSimpleLevel *m_sl;
std::set<TFrameId> m_framesCutted;
std::vector<TFrameId> m_oldFrames;
DrawingData *m_newData;
public:
CutFramesUndo(TXshSimpleLevel *sl,
std::set<TFrameId> &framesCutted,
std::vector<TFrameId> &oldFrames)
: m_sl(sl), m_framesCutted(framesCutted), m_oldFrames(oldFrames)
{
QClipboard *clipboard = QApplication::clipboard();
QMimeData *data = cloneData(clipboard->mimeData());
m_newData = dynamic_cast<DrawingData *>(data);
assert(m_newData);
}
~CutFramesUndo()
{
if (m_newData)
m_newData->releaseData();
}
void undo() const
{
std::set<TFrameId> frames = m_framesCutted;
bool dummy = true;
pasteFramesWithoutUndo(m_newData, m_sl, frames, DrawingData::OVER_SELECTION, true, dummy);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const
{
//Prendo il clipboard corrente.
QClipboard *clipboard = QApplication::clipboard();
QMimeData *currentData = cloneData(clipboard->mimeData());
std::set<TFrameId> frames = m_framesCutted;
cutFramesWithoutUndo(m_sl, frames);
// Setto il clipboard corrente
clipboard->setMimeData(currentData, QClipboard::Clipboard);
}
int getSize() const
{
return sizeof(*this);
}
QString getHistoryString()
{
QString str = QObject::tr("Cut Frames : Level %1 : Frame ")
.arg(QString::fromStdWString(m_sl->getName()));
std::set<TFrameId>::const_iterator it;
for (it = m_framesCutted.begin(); it != m_framesCutted.end(); it++) {
if (it != m_framesCutted.begin())
str += QString(", ");
str += QString::number((*it).getNumber());
}
return str;
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
} //namespace
//=============================================================================
//-----------------------------------------------------------------------------
namespace
{
//-----------------------------------------------------------------------------
class AddFramesUndo : public TUndo
{
TXshSimpleLevelP m_level;
std::set<TFrameId> m_insertedFids;
std::vector<TFrameId> m_oldFids;
public:
AddFramesUndo(const TXshSimpleLevelP &level,
const std::set<TFrameId> insertedFids,
std::vector<TFrameId> oldFids)
: m_level(level), m_insertedFids(insertedFids), m_oldFids(oldFids)
{
}
void undo() const
{
removeFramesWithoutUndo(m_level, m_insertedFids);
m_level->renumber(m_oldFids);
invalidateIcons(m_level.getPointer(), m_oldFids);
m_level->setDirtyFlag(true);
TApp *app = TApp::instance();
app->getCurrentScene()->setDirtyFlag(true);
app->getCurrentLevel()->notifyLevelChange();
}
void redo() const
{
makeSpaceForFids(m_level.getPointer(), m_insertedFids);
std::set<TFrameId>::const_iterator it = m_insertedFids.begin();
for (it; it != m_insertedFids.end(); it++) {
m_level->setFrame(*it, m_level->createEmptyFrame());
IconGenerator::instance()->invalidate(m_level.getPointer(), *it);
}
m_level->setDirtyFlag(true);
TApp *app = TApp::instance();
app->getCurrentScene()->setDirtyFlag(true);
app->getCurrentLevel()->notifyLevelChange();
}
int getSize() const
{
return sizeof(*this);
}
QString getHistoryString()
{
QString str = QObject::tr("Add Frames : Level %1 : Frame ")
.arg(QString::fromStdWString(m_level->getName()));
std::set<TFrameId>::const_iterator it;
for (it = m_insertedFids.begin(); it != m_insertedFids.end(); it++) {
if (it != m_insertedFids.begin())
str += QString(", ");
str += QString::number((*it).getNumber());
}
return str;
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
} //namespace
//=============================================================================
// AddFrames
//-----------------------------------------------------------------------------
void FilmstripCmd::addFrames(TXshSimpleLevel *sl, int start, int end, int step)
{
if (start < 1 || step < 1 || start > end || !sl || sl->isSubsequence() || sl->isReadOnly())
return;
std::vector<TFrameId> oldFids;
sl->getFids(oldFids);
std::set<TFrameId> fidsToInsert;
int frame = 0;
for (frame = start; frame <= end; frame += step)
fidsToInsert.insert(TFrameId(frame));
makeSpaceForFids(sl, fidsToInsert);
std::set<TFrameId>::iterator it = fidsToInsert.begin();
for (it; it != fidsToInsert.end(); it++) {
sl->setFrame(*it, sl->createEmptyFrame());
IconGenerator::instance()->invalidate(sl, *it);
}
sl->setDirtyFlag(true);
AddFramesUndo *undo = new AddFramesUndo(sl, fidsToInsert, oldFids);
TUndoManager::manager()->add(undo);
TApp *app = TApp::instance();
app->getCurrentScene()->setDirtyFlag(true);
app->getCurrentLevel()->notifyLevelChange();
}
//=============================================================================
// RenumberUndo
//-----------------------------------------------------------------------------
namespace
{
class RenumberUndo : public TUndo
{
TXshSimpleLevelP m_level;
std::vector<TFrameId> m_fids;
std::map<TFrameId, TFrameId> m_mapOldFrameId;
public:
RenumberUndo(const TXshSimpleLevelP &level, const std::vector<TFrameId> &fids)
: m_level(level), m_fids(fids)
{
assert(m_level);
std::vector<TFrameId> oldFids;
m_level->getFids(oldFids);
assert(oldFids.size() == m_fids.size());
int i;
for (i = 0; i < m_fids.size(); i++) {
if (m_fids[i] != oldFids[i])
m_mapOldFrameId[m_fids[i]] = oldFids[i];
}
}
void renumber(std::vector<TFrameId> fids) const
{
m_level->renumber(fids);
TSelection *selection = TSelection::getCurrent();
if (selection)
selection->selectNone();
invalidateIcons(m_level.getPointer(), fids);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
void undo() const
{
std::vector<TFrameId> fids;
m_level->getFids(fids);
assert(fids.size() == m_fids.size());
int i;
for (i = 0; i < fids.size(); i++) {
if (m_mapOldFrameId.count(fids[i]) > 0) {
std::map<TFrameId, TFrameId>::const_iterator it;
it = m_mapOldFrameId.find(fids[i]);
assert(it != m_mapOldFrameId.end());
if (it != m_mapOldFrameId.end())
fids[i] = TFrameId(it->second);
}
}
renumber(fids);
}
void redo() const { renumber(m_fids); }
int getSize() const
{
return sizeof(*this) + sizeof(TFrameId) * m_fids.size();
}
QString getHistoryString()
{
return QObject::tr("Renumber : Level %1")
.arg(QString::fromStdWString(m_level->getName()));
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
} //namespace
//=============================================================================
// Renumber
//-----------------------------------------------------------------------------
void FilmstripCmd::renumber(TXshSimpleLevel *sl,
const std::vector<std::pair<TFrameId, TFrameId>> &table)
{
if (!sl || sl->isSubsequence() || sl->isReadOnly())
return;
if (table.empty())
return;
// table:src->dst; check that src is a fid of the level
std::vector<std::pair<TFrameId, TFrameId>>::const_iterator it;
for (it = table.begin(); it != table.end(); ++it) {
TFrameId srcFid = it->first;
if (!sl->isFid(srcFid)) {
// todo: error messages
return;
}
}
// tmp contains all the level fids that are not affected by the renumbering
std::vector<TFrameId> fids, rfids;
sl->getFids(fids);
std::set<TFrameId> tmp;
for (int i = 0; i < (int)fids.size(); i++)
tmp.insert(fids[i]);
for (it = table.begin(); it != table.end(); ++it)
tmp.erase(it->first);
// fids contain the new numbering of all the level drawings
// (note: fids can be not ordered)
for (int i = 0; i < (int)fids.size(); i++) {
TFrameId srcFid = fids[i];
for (it = table.begin(); it != table.end() && it->first != srcFid; ++it) {
}
if (it != table.end()) {
// srcFid is affected by the renumbering
TFrameId tarFid = it->second;
// make sure that srcFid has not been used. add a letter if this is needed
if (tmp.count(tarFid) > 0) {
do {
char letter = tarFid.getLetter();
tarFid = TFrameId(tarFid.getNumber(), letter == 0 ? 'a' : letter + 1);
} while (tarFid.getLetter() <= 'z' && tmp.count(tarFid) > 0);
if (tarFid.getLetter() > 'z') {
// todo: error message
return;
}
}
tmp.insert(tarFid);
fids[i] = tarFid;
}
}
TUndoManager::manager()->add(new RenumberUndo(sl, fids));
sl->renumber(fids);
sl->setDirtyFlag(true);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
invalidateIcons(sl, fids);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
/*
int i;
std::set<TFrameId>::iterator it2;
it2=frames.begin();
std::set<TFrameId> newFrames;
for (i=0; i<frames.size(); i++, it2++)
newFrames.insert(TFrameId(startFrame+(i*stepFrame),it2->getLetter()));
assert(frames.size()==newFrames.size());
frames.swap(newFrames);
*/
}
//-----------------------------------------------------------------------------
void FilmstripCmd::renumber(TXshSimpleLevel *sl,
std::set<TFrameId> &frames,
int startFrame, int stepFrame)
{
if (!sl || sl->isSubsequence() || sl->isReadOnly())
return;
assert(startFrame > 0 && stepFrame > 0);
if (startFrame <= 0 || stepFrame <= 0 || frames.empty())
return;
std::vector<TFrameId> fids;
sl->getFids(fids);
std::set<TFrameId> modifiedFids;
// tmp contiene i frames del livello, meno quelli da renumerare
std::set<TFrameId> tmp(fids.begin(), fids.end());
std::set<TFrameId>::const_iterator it;
for (it = frames.begin(); it != frames.end(); ++it)
tmp.erase(*it);
int frame = startFrame;
std::vector<TFrameId>::iterator j = fids.begin();
for (it = frames.begin(); it != frames.end(); ++it) {
TFrameId srcFid(*it);
TFrameId dstFid(frame);
frame += stepFrame;
// faccio il controllo su tmp e non su fids. considera:
// fids = [1,2,3,4], renumber = [2->3,3->5]
if (tmp.count(dstFid) > 0) {
DVGui::error(("can't renumber: frame conflict"));
return;
}
j = std::find(j, fids.end(), srcFid);
// i frames selezionati fanno parte del livello sl. Quindi:
assert(j != fids.end());
assert(srcFid == *j);
if (j == fids.end())
continue; // per sicurezza
int k = std::distance(fids.begin(), j);
if (srcFid != dstFid) {
modifiedFids.insert(srcFid);
modifiedFids.insert(dstFid);
*j = dstFid;
++j;
}
}
TUndoManager::manager()->add(new RenumberUndo(sl, fids));
sl->renumber(fids);
sl->setDirtyFlag(true);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
int i;
std::set<TFrameId>::iterator it2;
it2 = frames.begin();
std::set<TFrameId> newFrames;
for (i = 0; i < frames.size(); i++, it2++)
newFrames.insert(TFrameId(startFrame + (i * stepFrame), it2->getLetter()));
assert(frames.size() == newFrames.size());
frames.swap(newFrames);
invalidateIcons(sl, fids);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
//=============================================================================
// copy
//-----------------------------------------------------------------------------
void FilmstripCmd::copy(TXshSimpleLevel *sl, std::set<TFrameId> &frames)
{
if (!sl || frames.empty())
return;
copyFramesWithoutUndo(sl, frames);
}
//=============================================================================
// paste
//-----------------------------------------------------------------------------
void FilmstripCmd::paste(TXshSimpleLevel *sl, std::set<TFrameId> &frames)
{
if (!sl || sl->isReadOnly() || frames.empty())
return;
std::vector<TFrameId> oldLevelFrameId;
sl->getFids(oldLevelFrameId);
TPaletteP oldPalette;
if (TPalette *pal = sl->getPalette())
oldPalette = pal->clone();
QClipboard *clipboard = QApplication::clipboard();
QMimeData *data = cloneData(clipboard->mimeData());
//when pasting the filmstrip frames
DrawingData *drawingData = dynamic_cast<DrawingData *>(data);
if (drawingData) {
if (sl->isSubsequence())
return;
// keep the choosed option of "Keep Original Palette" and reproduce it in undo
bool keepOriginalPalette;
HookSet *oldLevelHooks = new HookSet();
*oldLevelHooks = *sl->getHookSet();
bool isPaste = pasteFramesWithoutUndo(drawingData, sl, frames, DrawingData::INSERT, true, keepOriginalPalette);
if (!isPaste)
return;
TUndoManager::manager()->add(new PasteFramesUndo(sl, frames, oldLevelFrameId, oldPalette, DrawingData::INSERT, oldLevelHooks, keepOriginalPalette));
}
// when pasting the copied part of the image which is selected with the selection tool
else {
bool isFrameToInsert = (frames.size() == 1) ? !sl->isFid((*frames.begin())) : false;
TTileSet *tileSet = 0;
std::map<TFrameId, std::set<int>> indices;
TUndo *undo = 0;
TPaletteP plt = sl->getPalette()->clone();
bool isPaste = pasteAreasWithoutUndo(data, sl, frames, &tileSet, indices);
RasterImageData *rasterImageData = dynamic_cast<RasterImageData *>(data);
StrokesData *strokesData = dynamic_cast<StrokesData *>(data);
if (rasterImageData && tileSet)
undo = new PasteRasterAreasUndo(sl, frames, tileSet, rasterImageData,
plt.getPointer(), isFrameToInsert);
if (strokesData && tileSet) {
TImageP img = sl->getFrame(*frames.begin(), false);
TRasterImageP ri = img;
TToonzImageP ti = img;
assert(img);
if (ti)
undo = new PasteRasterAreasUndo(sl, frames, tileSet, strokesData->toToonzImageData(ti),
plt.getPointer(), isFrameToInsert);
else if (ri) {
double dpix, dpiy;
ri->getDpi(dpix, dpiy);
if (dpix == 0 || dpiy == 0) {
TPointD dpi = sl->getScene()->getCurrentCamera()->getDpi();
dpix = dpi.x;
dpiy = dpi.y;
ri->setDpi(dpix, dpiy);
}
undo = new PasteRasterAreasUndo(sl, frames, tileSet, strokesData->toFullColorImageData(ri),
plt.getPointer(), isFrameToInsert);
}
}
if (strokesData && !indices.empty())
undo = new PasteVectorAreasUndo(sl, frames, indices, strokesData,
plt.getPointer(), isFrameToInsert);
if (rasterImageData && !indices.empty())
undo = new PasteVectorAreasUndo(sl, frames, indices, rasterImageData->toStrokesData(sl->getScene()),
plt.getPointer(), isFrameToInsert);
if (!isPaste)
return;
if (undo)
TUndoManager::manager()->add(undo);
}
}
//=============================================================================
// merge
//-----------------------------------------------------------------------------
void FilmstripCmd::merge(TXshSimpleLevel *sl, std::set<TFrameId> &frames)
{
if (!sl || sl->isReadOnly() || sl->isSubsequence())
return;
std::vector<TFrameId> oldLevelFrameId;
sl->getFids(oldLevelFrameId);
TPaletteP oldPalette = sl->getPalette()->clone();
std::set<TFrameId> frameIdToChange;
QClipboard *clipboard = QApplication::clipboard();
if (const DrawingData *drawingData = dynamic_cast<const DrawingData *>(clipboard->mimeData())) {
drawingData->getFrames(frameIdToChange);
DrawingData *data = new DrawingData();
data->setLevelFrames(sl, frameIdToChange);
HookSet *oldLevelHooks = new HookSet();
*oldLevelHooks = *sl->getHookSet();
bool keepOriginalPalette = true;
bool isPaste = pasteFramesWithoutUndo(drawingData, sl, frames, DrawingData::OVER_FRAMEID, true, keepOriginalPalette);
if (!isPaste)
return;
TUndoManager::manager()->add(new PasteFramesUndo(sl, frames, oldLevelFrameId, oldPalette, DrawingData::OVER_FRAMEID, oldLevelHooks, keepOriginalPalette, data));
}
}
//=============================================================================
// pasteInto
//-----------------------------------------------------------------------------
void FilmstripCmd::pasteInto(TXshSimpleLevel *sl, std::set<TFrameId> &frames)
{
if (!sl || sl->isReadOnly() || sl->isSubsequence())
return;
std::vector<TFrameId> oldLevelFrameId;
sl->getFids(oldLevelFrameId);
TPaletteP oldPalette;
if (TPalette *pal = sl->getPalette())
oldPalette = pal->clone();
QClipboard *clipboard = QApplication::clipboard();
if (const DrawingData *drawingData = dynamic_cast<const DrawingData *>(clipboard->mimeData())) {
DrawingData *data = new DrawingData();
data->setLevelFrames(sl, frames);
HookSet *oldLevelHooks = new HookSet();
*oldLevelHooks = *sl->getHookSet();
bool keepOriginalPalette = true;
bool isPaste = pasteFramesWithoutUndo(drawingData, sl, frames, DrawingData::OVER_SELECTION, true, keepOriginalPalette);
if (!isPaste)
return;
TUndoManager::manager()->add(new PasteFramesUndo(sl, frames, oldLevelFrameId, oldPalette, DrawingData::OVER_SELECTION, oldLevelHooks, keepOriginalPalette, data));
}
}
//=============================================================================
// cut
//-----------------------------------------------------------------------------
void FilmstripCmd::cut(TXshSimpleLevel *sl, std::set<TFrameId> &frames)
{
if (!sl || frames.empty() || sl->isSubsequence() || sl->isReadOnly())
return;
std::set<TFrameId> framesToCut = frames;
std::vector<TFrameId> oldFrames;
sl->getFids(oldFrames);
cutFramesWithoutUndo(sl, frames);
TUndoManager::manager()->add(new CutFramesUndo(sl, framesToCut, oldFrames));
}
//=============================================================================
// clear
//-----------------------------------------------------------------------------
void FilmstripCmd::clear(TXshSimpleLevel *sl, std::set<TFrameId> &frames)
{
if (!sl || frames.empty())
return;
if (sl->isReadOnly()) {
std::set<TFrameId> editableFrames = sl->getEditableRange();
if (editableFrames.empty())
return;
// Browser all the frames and return if some frames are not editable
std::set<TFrameId>::const_iterator it;
for (it = frames.begin(); it != frames.end(); ++it) {
TFrameId frameId = *it;
if (editableFrames.count(frameId) == 0)
return;
}
}
HookSet *levelHooks = sl->getHookSet();
std::set<TFrameId> oldFrames = frames;
std::map<TFrameId, QString> clearedFrames = clearFramesWithoutUndo(sl, frames);
DrawingData *oldData = new DrawingData();
oldData->setFrames(clearedFrames, sl, *levelHooks);
DrawingData *newData = new DrawingData();
newData->setLevelFrames(sl, frames);
frames.clear();
TUndoManager::manager()->add(new DeleteFramesUndo(sl, oldFrames, oldData, newData));
}
//-----------------------------------------------------------------------------
namespace
{
//-----------------------------------------------------------------------------
void insertEmptyFilmstripFrames(const TXshSimpleLevelP &sl, const std::set<TFrameId> &frames)
{
if (!sl || frames.empty())
return;
makeSpaceForFids(sl.getPointer(), frames);
std::set<TFrameId>::const_iterator it;
for (it = frames.begin(); it != frames.end(); ++it)
sl->setFrame(*it, sl->createEmptyFrame());
invalidateIcons(sl.getPointer(), frames);
sl->setDirtyFlag(true);
// TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
//-----------------------------------------------------------------------------
class UndoInsertEmptyFrames : public TUndo
{
TXshSimpleLevelP m_level;
std::vector<TFrameId> m_oldFrames;
std::set<TFrameId> m_frames;
public:
UndoInsertEmptyFrames(
const TXshSimpleLevelP &level,
std::set<TFrameId> frames,
std::vector<TFrameId> oldFrames)
: m_level(level), m_frames(frames), m_oldFrames(oldFrames)
{
if (m_level->getType() == TZP_XSHLEVEL) {
std::set<TFrameId>::iterator it;
for (it = m_frames.begin(); it != m_frames.end(); it++) {
TToonzImageP img = m_level->getFrame(*it, true);
//TImageCache::instance()->add("UndoInsertEmptyFrames"+QString::number((UINT)this), img);
TImageCache::instance()->add("UndoInsertEmptyFrames" + QString::number((uintptr_t) this), img);
}
}
}
~UndoInsertEmptyFrames()
{
//TImageCache::instance()->remove("UndoInsertEmptyFrames"+QString::number((UINT)this));
TImageCache::instance()->remove("UndoInsertEmptyFrames" + QString::number((uintptr_t) this));
}
void undo() const
{
removeFramesWithoutUndo(m_level, m_frames);
assert(m_oldFrames.size() == m_level->getFrameCount());
m_level->renumber(m_oldFrames);
invalidateIcons(m_level.getPointer(), m_oldFrames);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
void redo() const
{
if (!m_level || m_frames.empty())
return;
if (m_level->getType() == PLI_XSHLEVEL)
FilmstripCmd::insert(m_level.getPointer(), m_frames, false);
else if (m_level->getType() == TZP_XSHLEVEL) {
makeSpaceForFids(m_level.getPointer(), m_frames);
std::set<TFrameId>::const_iterator it;
//TToonzImageP image = (TToonzImageP)TImageCache::instance()->get("UndoInsertEmptyFrames"+QString::number((UINT)this), true);
TToonzImageP image = (TToonzImageP)TImageCache::instance()->get("UndoInsertEmptyFrames" + QString::number((uintptr_t) this), true);
if (!image)
return;
for (it = m_frames.begin(); it != m_frames.end(); ++it)
m_level->setFrame(*it, image);
invalidateIcons(m_level.getPointer(), m_frames);
m_level->setDirtyFlag(true);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
}
int getSize() const
{
return sizeof(*this);
}
QString getHistoryString()
{
return QObject::tr("Insert : Level %1")
.arg(QString::fromStdWString(m_level->getName()));
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
} //namespace
//=============================================================================
// insert
//-----------------------------------------------------------------------------
void FilmstripCmd::insert(TXshSimpleLevel *sl, const std::set<TFrameId> &frames, bool withUndo)
{
if (frames.empty() || !sl || sl->isSubsequence() || sl->isReadOnly())
return;
std::vector<TFrameId> oldFrames;
if (withUndo)
sl->getFids(oldFrames);
insertEmptyFilmstripFrames(sl, frames);
if (withUndo)
TUndoManager::manager()->add(new UndoInsertEmptyFrames(sl, frames, oldFrames));
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//=============================================================================
//-----------------------------------------------------------------------------
namespace
{
//-----------------------------------------------------------------------------
void performReverse(const TXshSimpleLevelP &sl, const std::set<TFrameId> &frames)
{
if (!sl || frames.empty())
return;
std::vector<TFrameId> fids;
sl->getFids(fids);
int i = 0, j = (int)fids.size() - 1;
for (;;) {
while (i < j && frames.count(fids[i]) == 0)
i++;
while (i < j && frames.count(fids[j]) == 0)
j--;
if (i >= j)
break;
tswap(fids[i], fids[j]);
i++;
j--;
}
sl->renumber(fids);
sl->setDirtyFlag(true);
// TApp::instance()->getCurrentScene()->setDirtyFlag(true);
invalidateIcons(sl.getPointer(), frames);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
//-----------------------------------------------------------------------------
class FilmstripReverseUndo : public TUndo
{
TXshSimpleLevelP m_level;
std::set<TFrameId> m_frames;
public:
FilmstripReverseUndo(TXshSimpleLevelP level, std::set<TFrameId> frames)
: m_level(level), m_frames(frames)
{
}
void undo() const
{
performReverse(m_level, m_frames);
}
void redo() const
{
performReverse(m_level, m_frames);
}
int getSize() const
{
return sizeof *this;
}
QString getHistoryString()
{
return QObject::tr("Reverse : Level %1")
.arg(QString::fromStdWString(m_level->getName()));
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
} //namespace
//=============================================================================
// reverse
//-----------------------------------------------------------------------------
void FilmstripCmd::reverse(TXshSimpleLevel *sl, std::set<TFrameId> &frames)
{
if (!sl || sl->isSubsequence() || sl->isReadOnly())
return;
performReverse(sl, frames);
TUndoManager::manager()->add(new FilmstripReverseUndo(sl, frames));
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//=============================================================================
//-----------------------------------------------------------------------------
namespace
{
//-----------------------------------------------------------------------------
void performSwing(const TXshSimpleLevelP &sl, const std::set<TFrameId> &frames)
{
if (!sl)
return;
int count = frames.size() - 1;
if (count <= 0)
return; // niente swing con un solo frame
TFrameId lastFid = *frames.rbegin();
TFrameId insertPoint = lastFid + 1;
std::set<TFrameId> framesToInsert;
int i;
for (i = 0; i < count; i++)
framesToInsert.insert(insertPoint + i);
std::vector<TImage *> clonedImages;
std::set<TFrameId>::const_reverse_iterator k;
k = frames.rbegin();
for (++k; k != frames.rend(); ++k) {
TImageP img = sl->getFrame(*k, false);
clonedImages.push_back(img ? img->cloneImage() : 0);
}
makeSpaceForFids(sl.getPointer(), framesToInsert);
assert(count == (int)clonedImages.size());
for (i = 0; i < count && (int)i < (int)clonedImages.size(); i++) {
TImage *img = clonedImages[i];
if (img)
sl->setFrame(insertPoint + i, img);
}
invalidateIcons(sl.getPointer(), framesToInsert);
sl->setDirtyFlag(true);
// TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
//-----------------------------------------------------------------------------
class FilmstripSwingUndo : public TUndo
{
TXshSimpleLevelP m_level;
std::set<TFrameId> m_frames;
std::set<TFrameId> m_newFrames;
public:
FilmstripSwingUndo(const TXshSimpleLevelP &level, const std::set<TFrameId> &frames)
: m_level(level), m_frames(frames)
{
int count = frames.size() - 1;
if (count <= 0)
return; // niente swing con un solo frame
TFrameId lastFid = *frames.rbegin();
TFrameId insertPoint = lastFid + 1;
std::set<TFrameId> framesToInsert;
int i;
for (i = 0; i < count; i++)
m_newFrames.insert(insertPoint + i);
}
void undo() const
{
TSelection *selection = TSelection::getCurrent();
if (selection)
selection->selectNone();
removeFramesWithoutUndo(m_level, m_newFrames);
}
void redo() const
{
TSelection *selection = TSelection::getCurrent();
if (selection)
selection->selectNone();
performSwing(m_level, m_frames);
}
int getSize() const
{
return sizeof *this;
}
QString getHistoryString()
{
return QObject::tr("Swing : Level %1")
.arg(QString::fromStdWString(m_level->getName()));
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
} //namespace
//=============================================================================
// swing
//-----------------------------------------------------------------------------
void FilmstripCmd::swing(TXshSimpleLevel *sl, std::set<TFrameId> &frames)
{
if (!sl || sl->isSubsequence() || sl->isReadOnly())
return;
performSwing(sl, frames);
TUndoManager::manager()->add(new FilmstripSwingUndo(sl, frames));
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//=============================================================================
//-----------------------------------------------------------------------------
namespace
{
//-----------------------------------------------------------------------------
void stepFilmstripFrames(const TXshSimpleLevelP &sl,
const std::set<TFrameId> &frames,
int step = 2)
{
if (!sl || frames.empty() || step < 2)
return;
std::vector<TFrameId> fids;
std::set<TFrameId> changedFids;
std::vector<int> insertIndices;
sl->getFids(fids);
int i, offset = 0;
for (i = 0; i < (int)fids.size(); i++) {
bool frameToStep = (frames.count(fids[i]) > 0);
if (offset > 0) {
changedFids.insert(fids[i]);
fids[i] = fids[i] + offset;
changedFids.insert(fids[i]);
}
if (frameToStep) {
insertIndices.push_back(i);
offset += step - 1;
}
}
sl->renumber(fids);
for (i = 0; i < (int)insertIndices.size(); i++) {
int j = insertIndices[i];
TFrameId fid = fids[j];
TImageP img = sl->getFrame(fid, false);
if (img) {
int h;
for (h = 1; h < step; h++) {
sl->setFrame(fid + h, img->cloneImage());
changedFids.insert(fid + h);
}
}
}
invalidateIcons(sl.getPointer(), changedFids);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
//-----------------------------------------------------------------------------
class StepFilmstripUndo : public TUndo
{
TXshSimpleLevelP m_level;
std::set<TFrameId> m_insertedFrames;
std::set<TFrameId> m_frames;
std::vector<TFrameId> m_oldFrames;
int m_step;
public:
StepFilmstripUndo(const TXshSimpleLevelP &level, const std::set<TFrameId> &frames, int step)
: m_level(level), m_frames(frames), m_step(step)
{
assert(m_level);
m_level->getFids(m_oldFrames);
int d = 0;
std::set<TFrameId>::const_iterator it;
for (it = frames.begin(); it != frames.end(); ++it)
for (int j = 1; j < step; j++)
m_insertedFrames.insert(*it + (++d));
}
void undo() const
{
removeFramesWithoutUndo(m_level, m_insertedFrames);
std::set<TFrameId>::const_iterator it = m_frames.begin();
m_level->renumber(m_oldFrames);
TSelection *selection = TSelection::getCurrent();
if (selection)
selection->selectNone();
invalidateIcons(m_level.getPointer(), m_oldFrames);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
void redo() const
{
TSelection *selection = TSelection::getCurrent();
if (selection)
selection->selectNone();
stepFilmstripFrames(m_level, m_frames, m_step);
}
int getSize() const
{
return sizeof *this;
}
QString getHistoryString()
{
return QObject::tr("Step %1 : Level %2")
.arg(QString::number(m_step))
.arg(QString::fromStdWString(m_level->getName()));
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
} //namespace
//=============================================================================
// step
//-----------------------------------------------------------------------------
void FilmstripCmd::step(TXshSimpleLevel *sl, std::set<TFrameId> &frames, int step)
{
if (!sl || sl->isSubsequence() || sl->isReadOnly())
return;
QApplication::setOverrideCursor(Qt::WaitCursor);
StepFilmstripUndo *undo = new StepFilmstripUndo(sl, frames, step);
stepFilmstripFrames(sl, frames, step);
TUndoManager::manager()->add(undo);
frames.clear();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
QApplication::restoreOverrideCursor();
}
//=============================================================================
//-----------------------------------------------------------------------------
namespace
{
//-----------------------------------------------------------------------------
std::map<TFrameId, QString> eachFilmstripFrames(const TXshSimpleLevelP &sl,
const std::set<TFrameId> &frames, int each)
{
if (frames.empty() || !sl || each < 2)
return std::map<TFrameId, QString>();
std::vector<TFrameId> framesToDelete;
std::set<TFrameId>::const_iterator it;
int k = 0;
for (it = frames.begin(); it != frames.end(); ++it, ++k)
if ((k % each) > 0)
framesToDelete.push_back(*it);
int i = 0;
std::vector<TFrameId>::reverse_iterator fit;
std::map<TFrameId, QString> cutFrames;
for (fit = framesToDelete.rbegin(); fit != framesToDelete.rend(); ++fit) {
TImageP img = sl->getFrame(*fit, false);
if (img) {
//QString id = "eachFrames"+QString::number((UINT)sl.getPointer())+"-"+QString::number(fit->getNumber());
QString id = "eachFrames" + QString::number((uintptr_t)sl.getPointer()) + "-" + QString::number(fit->getNumber());
TImageCache::instance()->add(id, img);
cutFrames[*fit] = id;
}
sl->eraseFrame(*fit); //toglie da cache?
IconGenerator::instance()->remove(sl.getPointer(), *fit);
}
TApp::instance()->getCurrentLevel()->notifyLevelChange();
return cutFrames;
}
//-----------------------------------------------------------------------------
class EachFilmstripUndo : public TUndo
{
TXshSimpleLevelP m_level;
std::set<TFrameId> m_frames;
std::map<TFrameId, QString> m_cutFrames;
int m_each;
public:
EachFilmstripUndo(const TXshSimpleLevelP &level, int each, const std::set<TFrameId> &frames, std::map<TFrameId, QString> deletedFrames)
: m_level(level), m_cutFrames(deletedFrames), m_each(each), m_frames(frames)
{
}
~EachFilmstripUndo()
{
std::map<TFrameId, QString>::iterator it = m_cutFrames.begin();
for (; it != m_cutFrames.end(); ++it)
TImageCache::instance()->remove(it->second);
}
void undo() const
{
TSelection *selection = TSelection::getCurrent();
if (selection)
selection->selectNone();
insertNotEmptyframes(m_level, m_cutFrames);
}
void redo() const
{
TSelection *selection = TSelection::getCurrent();
if (selection)
selection->selectNone();
eachFilmstripFrames(m_level, m_frames, m_each);
}
int getSize() const
{
return sizeof *this;
}
QString getHistoryString()
{
return QObject::tr("Each %1 : Level %2")
.arg(QString::number(m_each))
.arg(QString::fromStdWString(m_level->getName()));
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
} //namespace
//=============================================================================
// each
//-----------------------------------------------------------------------------
void FilmstripCmd::each(TXshSimpleLevel *sl, std::set<TFrameId> &frames, int each)
{
if (!sl || sl->isSubsequence() || sl->isReadOnly())
return;
std::map<TFrameId, QString> deletedFrames = eachFilmstripFrames(sl, frames, each);
TUndoManager::manager()->add(new EachFilmstripUndo(sl, each, frames, deletedFrames));
frames.clear();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//=============================================================================
//-----------------------------------------------------------------------------
namespace
{
//-----------------------------------------------------------------------------
class UndoDuplicateDrawing : public TUndo
{
TXshSimpleLevelP m_level;
std::set<TFrameId> m_frameInserted;
std::vector<TFrameId> m_oldFrames;
std::set<TFrameId> m_framesForRedo;
public:
UndoDuplicateDrawing(const TXshSimpleLevelP &level,
const std::vector<TFrameId> oldFrames,
std::set<TFrameId> frameInserted,
std::set<TFrameId> framesForRedo)
: m_level(level), m_oldFrames(oldFrames), m_frameInserted(frameInserted), m_framesForRedo(framesForRedo)
{
}
void undo() const
{
assert(m_level);
removeFramesWithoutUndo(m_level, m_frameInserted);
m_level->renumber(m_oldFrames);
invalidateIcons(m_level.getPointer(), m_oldFrames);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
void redo() const
{
std::set<TFrameId> framesForRedo = m_framesForRedo;
FilmstripCmd::duplicate(m_level.getPointer(), framesForRedo, false);
}
int getSize() const
{
return sizeof(*this);
}
QString getHistoryString()
{
return QObject::tr("Duplicate : Level %1")
.arg(QString::fromStdWString(m_level->getName()));
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
} //namespace
//=============================================================================
// duplicate
//-----------------------------------------------------------------------------
void FilmstripCmd::duplicate(TXshSimpleLevel *sl, std::set<TFrameId> &frames, bool withUndo)
{
if (frames.empty() || !sl || sl->isSubsequence() || sl->isReadOnly())
return;
TFrameId insertPoint = (*frames.rbegin()) + 1;
std::map<TFrameId, QString> framesToInsert;
std::set<TFrameId> newFrames;
std::set<TFrameId>::iterator it = frames.begin();
int i = 0;
for (it; it != frames.end(); it++) {
TFrameId fid = *it;
TImageP img = sl->getFrame(*it, false);
TImageP imgClone = (img) ? img->cloneImage() : 0;
//QString id = "dupFrames"+QString::number((UINT)sl)+"-"+QString::number(it->getNumber());
QString id = "dupFrames" + QString::number((uintptr_t)sl) + "-" + QString::number(it->getNumber());
//TImageCache::instance()->add(id, sl->getFrame(*it, false));
TImageCache::instance()->add(id, imgClone);
framesToInsert[insertPoint + i] = id;
newFrames.insert(insertPoint + i);
i++;
}
std::vector<TFrameId> oldFrames;
sl->getFids(oldFrames);
insertNotEmptyframes(sl, framesToInsert);
if (withUndo)
TUndoManager::manager()->add(new UndoDuplicateDrawing(sl, oldFrames, newFrames, frames));
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//=============================================================================
//-----------------------------------------------------------------------------
namespace
{
//-----------------------------------------------------------------------------
void moveToSceneFrames(TXshLevel *level, const std::set<TFrameId> &frames)
{
if (frames.empty() || !level)
return;
TXsheetHandle *xh = TApp::instance()->getCurrentXsheet();
TXsheet *xsh = xh->getXsheet();
int row = 0;
int col = xsh->getFirstFreeColumnIndex();
std::set<TFrameId>::const_iterator it;
if (level->getPaletteLevel()) {
TXshPaletteColumn *column = new TXshPaletteColumn;
xsh->insertColumn(col, column);
}
for (it = frames.begin(); it != frames.end(); ++it) {
xsh->setCell(row, col, TXshCell(level, *it));
++row;
}
xh->notifyXsheetChanged();
}
//-----------------------------------------------------------------------------
class MoveLevelToSceneUndo : public TUndo
{
std::wstring m_levelName;
int m_col;
std::set<TFrameId> m_fids;
public:
MoveLevelToSceneUndo(std::wstring levelName, int col,
std::set<TFrameId> fids)
: m_levelName(levelName), m_col(col), m_fids(fids)
{
}
void undo() const
{
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXshLevel *xl = scene->getLevelSet()->getLevel(m_levelName);
if (xl->getPaletteLevel())
xsh->removeColumn(m_col);
xsh->clearCells(0, m_col, m_fids.size());
app->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const
{
TApp *app = TApp::instance();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXshLevel *xl = scene->getLevelSet()->getLevel(m_levelName);
if (!xl)
return;
moveToSceneFrames(xl, m_fids);
}
int getSize() const
{
return sizeof *this;
}
QString getHistoryString()
{
return QObject::tr("Move Level to Scene : Level %1")
.arg(QString::fromStdWString(m_levelName));
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
} // namespace
//=============================================================================
// moveToScene
//-----------------------------------------------------------------------------
void FilmstripCmd::moveToScene(TXshLevel *sl, std::set<TFrameId> &frames)
{
if (frames.empty() || !sl)
return;
TXsheetHandle *xh = TApp::instance()->getCurrentXsheet();
TXsheet *xsh = xh->getXsheet();
int row = 0;
int col = xsh->getFirstFreeColumnIndex();
std::set<TFrameId>::const_iterator it;
for (it = frames.begin(); it != frames.end(); ++it) {
xsh->setCell(row, col, TXshCell(sl, *it));
++row;
}
xh->notifyXsheetChanged();
TUndoManager::manager()->add(new MoveLevelToSceneUndo(sl->getName(), col, frames));
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//-----------------------------------------------------------------------------
void FilmstripCmd::moveToScene(TXshSimpleLevel *sl)
{
std::vector<TFrameId> fids;
sl->getFids(fids);
std::set<TFrameId> fidsSet(fids.begin(), fids.end());
moveToScene(sl, fidsSet);
}
//-----------------------------------------------------------------------------
void FilmstripCmd::moveToScene(TXshPaletteLevel *pl)
{
if (!pl)
return;
std::set<TFrameId> fidsSet;
fidsSet.insert(TFrameId(1));
TXsheetHandle *xh = TApp::instance()->getCurrentXsheet();
TXsheet *xsh = xh->getXsheet();
int row = 0;
int col = xsh->getFirstFreeColumnIndex();
TXshPaletteColumn *column = new TXshPaletteColumn;
xsh->insertColumn(col, column);
std::set<TFrameId>::const_iterator it;
for (it = fidsSet.begin(); it != fidsSet.end(); ++it) {
xsh->setCell(row, col, TXshCell(pl, *it));
++row;
}
xh->notifyXsheetChanged();
TUndoManager::manager()->add(new MoveLevelToSceneUndo(pl->getName(), col, fidsSet));
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//-----------------------------------------------------------------------------
void FilmstripCmd::moveToScene(TXshSoundLevel *sl)
{
std::vector<TFrameId> fids;
sl->getFids(fids);
std::set<TFrameId> fidsSet(fids.begin(), fids.end());
moveToScene(sl, fidsSet);
}
//=============================================================================
// UndoInbetween
//-----------------------------------------------------------------------------
namespace
{
class UndoInbetween : public TUndo
{
TXshSimpleLevelP m_level;
std::vector<TFrameId> m_fids;
std::vector<TVectorImageP> m_images;
FilmstripCmd::InbetweenInterpolation m_interpolation;
public:
UndoInbetween(
TXshSimpleLevel *xl,
std::vector<TFrameId> fids,
FilmstripCmd::InbetweenInterpolation interpolation)
: m_level(xl), m_fids(fids), m_interpolation(interpolation)
{
std::vector<TFrameId>::iterator it = fids.begin();
//mi salvo tutte le immagine
for (; it != fids.end(); ++it)
m_images.push_back(m_level->getFrame(*it, false)); //non si fa clone perche' il livello subito dopo rilascia queste immagini a causa dell'inbetweener
}
void undo() const
{
UINT levelSize = m_fids.size() - 1;
for (UINT count = 1; count != levelSize; count++) {
TVectorImageP vImage = m_images[count];
m_level->setFrame(m_fids[count], vImage);
IconGenerator::instance()->invalidate(m_level.getPointer(), m_fids[count]);
}
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
void redo() const
{
TFrameId fid0 = *m_fids.begin();
TFrameId fid1 = *(--m_fids.end());
FilmstripCmd::inbetweenWithoutUndo(m_level.getPointer(), fid0, fid1, m_interpolation);
}
int getSize() const
{
assert(!m_images.empty());
return m_images.size() * m_images.front()->getStrokeCount() * 100;
}
QString getHistoryString()
{
QString str = QObject::tr("Inbetween : Level %1, ")
.arg(QString::fromStdWString(m_level->getName()));
switch (m_interpolation) {
case FilmstripCmd::II_Linear:
str += QString("Linear Interporation");
break;
case FilmstripCmd::II_EaseIn:
str += QString("Ease In Interporation");
break;
case FilmstripCmd::II_EaseOut:
str += QString("Ease Out Interporation");
break;
case FilmstripCmd::II_EaseInOut:
str += QString("Ease In-Out Interporation");
break;
}
return str;
}
int getHistoryType()
{
return HistoryType::FilmStrip;
}
};
} //namespace
//=============================================================================
// inbetween
//-----------------------------------------------------------------------------
void FilmstripCmd::inbetweenWithoutUndo(TXshSimpleLevel *sl,
const TFrameId &fid0,
const TFrameId &fid1,
FilmstripCmd::InbetweenInterpolation interpolation)
{
if (!sl)
return;
std::vector<TFrameId> fids;
sl->getFids(fids);
std::vector<TFrameId>::iterator it;
it = std::find(fids.begin(), fids.end(), fid0);
if (it == fids.end())
return;
int ia = std::distance(fids.begin(), it);
it = std::find(fids.begin(), fids.end(), fid1);
if (it == fids.end())
return;
int ib = std::distance(fids.begin(), it);
if (ib - ia < 2)
return;
TVectorImageP img0 = sl->getFrame(fid0, false);
TVectorImageP img1 = sl->getFrame(fid1, false);
if (!img0 || !img1)
return;
TInbetween inbetween(img0, img1);
int i;
for (i = ia + 1; i < ib; i++) {
double t = (double)(i - ia) / (double)(ib - ia);
double s = t;
// in tutte le interpolazioni : s(0) = 0, s(1) = 1
switch (interpolation) {
case II_Linear:
break;
case II_EaseIn:
s = t * t;
break; // s'(0) = 0
case II_EaseOut:
s = t * (2 - t);
break; // s'(1) = 0
case II_EaseInOut:
s = t * t * (3 - 2 * t);
break; // s'(0) = s'(1) = 0
}
TVectorImageP vi = inbetween.tween(s);
sl->setFrame(fids[i], vi);
IconGenerator::instance()->invalidate(sl, fids[i]);
}
sl->setDirtyFlag(true);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
//-----------------------------------------------------------------------------
void FilmstripCmd::inbetween(TXshSimpleLevel *sl,
const TFrameId &fid0,
const TFrameId &fid1,
FilmstripCmd::InbetweenInterpolation interpolation)
{
std::vector<TFrameId> fids;
std::vector<TFrameId> levelFids;
sl->getFids(levelFids);
std::vector<TFrameId>::iterator it = levelFids.begin();
for (it; it != levelFids.end(); it++) {
int curFid = it->getNumber();
if (fid0.getNumber() <= curFid && curFid <= fid1.getNumber())
fids.push_back(*it);
}
TUndoManager::manager()->add(new UndoInbetween(sl, fids, interpolation));
inbetweenWithoutUndo(sl, fid0, fid1, interpolation);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//-----------------------------------------------------------------------------
void FilmstripCmd::renumberDrawing(TXshSimpleLevel *sl, const TFrameId &oldFid, const TFrameId &desiredNewFid)
{
if (oldFid == desiredNewFid)
return;
std::vector<TFrameId> fids;
sl->getFids(fids);
std::vector<TFrameId>::iterator it = std::find(fids.begin(), fids.end(), oldFid);
if (it == fids.end())
return;
TFrameId newFid = desiredNewFid;
while (std::find(fids.begin(), fids.end(), newFid) != fids.end()) {
char letter = newFid.getLetter();
if (letter == 'z')
return;
if (letter == 0)
letter = 'a';
else
letter++;
newFid = TFrameId(newFid.getNumber(), letter);
}
*it = newFid;
sl->renumber(fids);
TXshCell oldCell(sl, oldFid);
TXshCell newCell(sl, newFid);
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
for (int c = 0; c < xsh->getColumnCount(); c++) {
int r0, r1;
int n = xsh->getCellRange(c, r0, r1);
if (n > 0) {
std::vector<TXshCell> cells(n);
xsh->getCells(r0, c, n, &cells[0]);
bool changed = false;
for (int i = 0; i < n; i++) {
if (cells[i] == oldCell) {
changed = true;
cells[i] = newCell;
}
}
if (changed)
xsh->setCells(r0, c, n, &cells[0]);
}
}
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentLevel()->notifyLevelChange();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}