#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/txshchildlevel.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 "toonz/preferences.h" #include "trop.h" #include "tools/toolhandle.h" #include "tools/rasterselection.h" #include "tools/strokeselection.h" #include "toonzqt/gutil.h" #include "historytypes.h" #include #include //============================================================================= TFrameId operator+(const TFrameId &fid, int d) { return TFrameId(fid.getNumber() + d, fid.getLetter(), fid.getZeroPadding(), fid.getStartSeqInd()); } //----------------------------------------------------------------------------- /* void doUpdateXSheet(TXshSimpleLevel *sl, std::vector oldFids, std::vector newFids, TXsheet *xsh, std::vector &childLevels) { for (int c = 0; c < xsh->getColumnCount(); ++c) { int r0, r1; int n = xsh->getCellRange(c, r0, r1); if (n > 0) { bool changed = false; std::vector cells(n); xsh->getCells(r0, c, n, &cells[0]); for (int i = 0; i < n; i++) { TXshCell currCell = cells[i]; // check the sub xsheets too if (!cells[i].isEmpty() && cells[i].m_level->getType() == CHILD_XSHLEVEL) { TXshChildLevel *level = cells[i].m_level->getChildLevel(); // make sure we haven't already checked the level if (level && std::find(childLevels.begin(), childLevels.end(), level) == childLevels.end()) { childLevels.push_back(level); TXsheet *subXsh = level->getXsheet(); doUpdateXSheet(sl, oldFids, newFids, subXsh, childLevels); } } for (int j = 0; j < oldFids.size(); j++) { if (oldFids.at(j) == newFids.at(j)) continue; TXshCell tempCell(sl, oldFids.at(j)); bool sameSl = tempCell.getSimpleLevel() == currCell.getSimpleLevel(); bool sameFid = tempCell.getFrameId() == currCell.getFrameId(); if (sameSl && sameFid) { TXshCell newCell(sl, newFids.at(j)); cells[i] = newCell; changed = true; break; } } } if (changed) { xsh->setCells(r0, c, n, &cells[0]); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } } } } */ //----------------------------------------------------------------------------- static void updateXSheet(TXshSimpleLevel *sl, std::vector oldFids, std::vector newFids) { std::vector childLevels; TXsheet *xsh = TApp::instance()->getCurrentScene()->getScene()->getTopXsheet(); bool changed = ToolUtils::doUpdateXSheet(sl, oldFids, newFids, xsh, childLevels); if (changed) TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } //============================================================================= // 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 &framesToInsert) { std::vector fids; std::vector oldFids; sl->getFids(fids); sl->getFids(oldFids); std::set::const_iterator it; std::set touchedFids; for (it = framesToInsert.begin(); it != framesToInsert.end(); ++it) { // devo inserire fid TFrameId fid(*it); std::vector::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()) { if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) updateXSheet(sl, oldFids, fids); sl->renumber(fids); sl->setDirtyFlag(true); } } //============================================================================= namespace { //----------------------------------------------------------------------------- void copyFramesWithoutUndo(TXshSimpleLevel *sl, std::set &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 &frames, TTileSet **tileSet, std::map> &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(data)) { std::set::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 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(data)) { std::set::iterator it; for (it = frames.begin(); it != frames.end(); ++it) { if ((sl->getType() == PLI_XSHLEVEL || sl->getType() == TZP_XSHLEVEL) && dynamic_cast(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 rects; std::vector strokes; std::vector 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) { if (!ri->getPalette()) ri->setPalette(TApp::instance() ->getPaletteController() ->getDefaultPalette(sl->getType()) ->clone()); TRasterP ras; double dpiX = 0, dpiY = 0; double imgDpiX = 0, imgDpiY = 0; std::vector rects; std::vector strokes; std::vector 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 &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 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 clearFramesWithoutUndo( const TXshSimpleLevelP &sl, const std::set &frames) { std::map clearedFrames; if (!sl || frames.empty()) return clearedFrames; std::set::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; // empty frame must be created BEFORE erasing frame or it may initialize // palette. TImageP emptyFrame = sl->createEmptyFrame(); sl->eraseFrame(frameId); sl->setFrame(*it, emptyFrame); } 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 &frames) { if (!sl || frames.empty()) return; std::set::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 &frames) { std::map imageSet; HookSet *levelHooks = sl->getHookSet(); int currentFrameIndex = TApp::instance()->getCurrentFrame()->getFrameIndex(); std::set::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 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(); } //----------------------------------------------------------------------------- std::map deleteFramesWithoutUndo( TXshSimpleLevel *sl, std::set &frames) { std::map imageSet; if (!sl || frames.empty()) return imageSet; int currentFrameIndex = TApp::instance()->getCurrentFrame()->getFrameIndex(); std::set::const_iterator it; int i = 0; for (it = frames.begin(); it != frames.end(); ++it, i++) { TFrameId frameId = *it; QString id = "deleteFrames" + 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); for (it = frames.begin(); it != frames.end(); ++it, i++) { sl->eraseFrame(*it); } std::vector newFids; sl->getFids(newFids); TApp::instance()->getCurrentFrame()->setFrameIds(newFids); TApp::instance()->getCurrentFrame()->setFrameIndex(currentFrameIndex); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); sl->setDirtyFlag(true); TApp::instance()->getCurrentLevel()->notifyLevelChange(); return imageSet; } //----------------------------------------------------------------------------- void insertNotEmptyframes(const TXshSimpleLevelP &sl, const std::map &framesToInsert) { if (framesToInsert.empty() || !sl) return; std::vector fids; sl->getFids(fids); std::set frames; for (auto const &frame : framesToInsert) { frames.insert(frame.first); } makeSpaceForFids(sl.getPointer(), frames); for (auto const &frame : framesToInsert) { TImageP img = TImageCache::instance()->get(frame.second, false); TImageCache::instance()->remove(frame.second); assert(img); sl->setFrame(frame.first, img); } invalidateIcons(sl.getPointer(), frames); sl->setDirtyFlag(true); TApp::instance()->getCurrentLevel()->notifyLevelChange(); } //============================================================================= // PasteRasterAreasUndo //----------------------------------------------------------------------------- class PasteRasterAreasUndo final : public TUndo { TXshSimpleLevelP m_level; std::set m_frames; TTileSet *m_tiles; RasterImageData *m_data; TPaletteP m_oldPalette; TPaletteP m_newPalette; bool m_isFrameInserted; public: PasteRasterAreasUndo(TXshSimpleLevel *sl, const std::set &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() override { m_newPalette = m_level->getPalette()->clone(); } void undo() const override { if (!m_level || m_frames.empty()) return; std::set frames = m_frames; if (m_isFrameInserted) { // Faccio remove dei frame incollati removeFramesWithoutUndo(m_level, frames); } else { std::set::const_iterator it; int i = 0; TTileSetCM32 *tileSetCM = dynamic_cast(m_tiles); TTileSetFullColor *tileSetFC = dynamic_cast(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 override { 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 frames = m_frames; std::set::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 rects; std::vector strokes; std::vector 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) { if (!ri->getPalette()) ri->setPalette(TApp::instance() ->getPaletteController() ->getDefaultPalette(m_level->getType()) ->clone()); TRasterP ras; double dpiX, dpiY; std::vector rects; std::vector strokes; std::vector 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 = 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 override { return sizeof(*this) + sizeof(*m_data) + sizeof(*m_tiles); } QString getHistoryString() override { QString str = QObject::tr("Paste : Level %1 : Frame ") .arg(QString::fromStdWString(m_level->getName())); std::set::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() override { return HistoryType::FilmStrip; } }; //============================================================================= // PasteVectorAreasUndo //----------------------------------------------------------------------------- class PasteVectorAreasUndo final : public TUndo { TXshSimpleLevelP m_level; std::set m_frames; std::map> m_indices; StrokesData *m_data; TPaletteP m_oldPalette; TPaletteP m_newPalette; bool m_isFrameInserted; public: PasteVectorAreasUndo(TXshSimpleLevel *sl, const std::set &frames, std::map> &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() override { m_newPalette = m_level->getPalette()->clone(); } void undo() const override { if (!m_level || m_frames.empty()) return; std::set frames = m_frames; if (m_isFrameInserted) { // Faccio remove dei frame incollati removeFramesWithoutUndo(m_level, frames); } else { std::set::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>::const_iterator mapIt = m_indices.find(*it); if (mapIt == m_indices.end()) continue; std::set imageIndices = mapIt->second; ; std::set::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 override { 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 frames = m_frames; std::set::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 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 override { return sizeof(*this) + sizeof(*m_data); } QString getHistoryString() override { QString str = QObject::tr("Paste : Level %1 : Frame ") .arg(QString::fromStdWString(m_level->getName())); std::set::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() override { return HistoryType::FilmStrip; } }; /*//============================================================================= // PasteAreasUndo //----------------------------------------------------------------------------- class PasteAreasUndo final : public TUndo { TXshSimpleLevelP m_level; std::set m_frames; TPaletteP m_oldPalette; TPaletteP m_newPalette; bool m_isFrameInserted; public: PasteAreasUndo(TXshSimpleLevel *sl, const std::set &frames, bool isFrameInserted) : TUndo() , m_level(sl) , m_frames(frames) , m_isFrameInserted(isFrameInserted) { m_oldPalette = m_level->getPalette()->clone(); if(!m_isFrameInserted) { std::set::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::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::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 frames = m_frames; if(m_isFrameInserted) { // Faccio remove dei frame incollati removeFramesWithoutUndo(m_level, frames); } else { std::set::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 frames = m_frames; std::set::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 final : public TUndo { TXshSimpleLevelP m_sl; std::set m_frames; std::vector m_oldLevelFrameId; TPaletteP m_oldPalette; DrawingData *m_oldData; DrawingData *m_newData; DrawingData::ImageSetType m_setType; HookSet *m_oldLevelHooks; bool m_updateXSheet; bool m_keepOriginalPalette; public: PasteFramesUndo(TXshSimpleLevel *sl, const std::set &frames, const std::vector &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(data); assert(m_newData); m_updateXSheet = Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled(); } ~PasteFramesUndo() { if (m_oldData) m_oldData->releaseData(); if (m_newData) m_newData->releaseData(); } void undo() const override { TSelection *selection = TSelection::getCurrent(); if (selection) selection->selectNone(); std::set 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()); if (m_updateXSheet) { std::vector newFrames; m_sl->getFids(newFrames); updateXSheet(m_sl.getPointer(), newFrames, m_oldLevelFrameId); } m_sl->renumber(m_oldLevelFrameId); m_sl->setDirtyFlag(true); TApp::instance() ->getPaletteController() ->getCurrentLevelPalette() ->notifyPaletteChanged(); TApp::instance()->getCurrentLevel()->notifyLevelChange(); } // Reinserisco i vecchi frame che ho sovrascritto if (m_setType != DrawingData::INSERT) { std::set 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 sl_fids; m_sl.getPointer()->getFids(sl_fids); invalidateIcons(m_sl.getPointer(), sl_fids); *m_sl->getHookSet() = *m_oldLevelHooks; } void redo() const override { if (!m_sl || m_frames.empty()) return; TSelection *selection = TSelection::getCurrent(); if (selection) selection->selectNone(); std::set frames = m_frames; bool keepOriginalPalette = m_keepOriginalPalette; pasteFramesWithoutUndo(m_newData, m_sl.getPointer(), frames, m_setType, true, keepOriginalPalette, true); } int getSize() const override { return sizeof(*this); } QString getHistoryString() override { QString str = QObject::tr("Paste : Level %1 : Frame ") .arg(QString::fromStdWString(m_sl->getName())); std::set::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() override { return HistoryType::FilmStrip; } }; //============================================================================= // ClearFramesUndo //----------------------------------------------------------------------------- class ClearFramesUndo final : public TUndo { TXshSimpleLevel *m_sl; std::set m_frames; DrawingData *m_oldData; DrawingData *m_newData; public: ClearFramesUndo(TXshSimpleLevel *sl, std::set &frames, DrawingData *oldData, DrawingData *newData) : m_sl(sl), m_frames(frames), m_oldData(oldData), m_newData(newData) {} ~ClearFramesUndo() { if (m_oldData) m_oldData->releaseData(); if (m_newData) m_newData->releaseData(); } void pasteFramesFromData(const DrawingData *data) const { std::set frames = m_frames; bool dummy = true; // Incollo i frames sovrascrivendoli pasteFramesWithoutUndo(data, m_sl, frames, DrawingData::OVER_SELECTION, true, dummy); } void undo() const override { 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 override { pasteFramesFromData(m_newData); } int getSize() const override { return sizeof(*this); } QString getHistoryString() override { QString str = QObject::tr("Clear Frames : Level %1 : Frame ") .arg(QString::fromStdWString(m_sl->getName())); std::set::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() override { return HistoryType::FilmStrip; } }; //============================================================================= //----------------------------------------------------------------------------- class CutFramesUndo final : public TUndo { TXshSimpleLevel *m_sl; std::set m_framesCutted; std::vector m_oldFrames; DrawingData *m_newData; public: CutFramesUndo(TXshSimpleLevel *sl, std::set &framesCutted, std::vector &oldFrames) : m_sl(sl), m_framesCutted(framesCutted), m_oldFrames(oldFrames) { QClipboard *clipboard = QApplication::clipboard(); QMimeData *data = cloneData(clipboard->mimeData()); m_newData = dynamic_cast(data); assert(m_newData); } ~CutFramesUndo() { if (m_newData) m_newData->releaseData(); } void undo() const override { std::set frames = m_framesCutted; bool dummy = true; pasteFramesWithoutUndo(m_newData, m_sl, frames, DrawingData::OVER_SELECTION, true, dummy); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } void redo() const override { // Prendo il clipboard corrente. QClipboard *clipboard = QApplication::clipboard(); QMimeData *currentData = cloneData(clipboard->mimeData()); std::set frames = m_framesCutted; cutFramesWithoutUndo(m_sl, frames); // Setto il clipboard corrente clipboard->setMimeData(currentData, QClipboard::Clipboard); } int getSize() const override { return sizeof(*this); } QString getHistoryString() override { QString str = QObject::tr("Cut Frames : Level %1 : Frame ") .arg(QString::fromStdWString(m_sl->getName())); std::set::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() override { return HistoryType::FilmStrip; } }; //============================================================================= //----------------------------------------------------------------------------- class DeleteFramesUndo final : public TUndo { TXshSimpleLevel *m_sl; std::set m_framesDeleted; std::vector m_oldFrames; DrawingData *m_oldData; public: DeleteFramesUndo(TXshSimpleLevel *sl, std::set &framesCutted, std::vector &oldFrames, DrawingData *oldData) : m_sl(sl) , m_framesDeleted(framesCutted) , m_oldFrames(oldFrames) , m_oldData(oldData) {} ~DeleteFramesUndo() { if (m_oldData) m_oldData->releaseData(); } void undo() const override { std::set frames = m_framesDeleted; bool dummy = true; pasteFramesWithoutUndo(m_oldData, m_sl, frames, DrawingData::OVER_SELECTION, true, dummy); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } void redo() const override { std::set frames = m_framesDeleted; deleteFramesWithoutUndo(m_sl, frames); } int getSize() const override { return sizeof(*this); } QString getHistoryString() override { QString str = QObject::tr("Delete Frames : Level %1 : Frame ") .arg(QString::fromStdWString(m_sl->getName())); std::set::const_iterator it; for (it = m_framesDeleted.begin(); it != m_framesDeleted.end(); it++) { if (it != m_framesDeleted.begin()) str += QString(", "); str += QString::number((*it).getNumber()); } return str; } int getHistoryType() override { return HistoryType::FilmStrip; } }; } // namespace //============================================================================= //----------------------------------------------------------------------------- namespace { //----------------------------------------------------------------------------- class AddFramesUndo final : public TUndo { TXshSimpleLevelP m_level; std::set m_insertedFids; std::vector m_oldFids; bool m_updateXSheet; public: AddFramesUndo(const TXshSimpleLevelP &level, const std::set insertedFids, std::vector oldFids) : m_level(level), m_insertedFids(insertedFids), m_oldFids(oldFids) { m_updateXSheet = Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled(); } void undo() const override { removeFramesWithoutUndo(m_level, m_insertedFids); if (m_updateXSheet) { std::vector newFrames; m_level->getFids(newFrames); updateXSheet(m_level.getPointer(), newFrames, m_oldFids); } m_level->renumber(m_oldFids); m_level->setDirtyFlag(true); TApp *app = TApp::instance(); app->getCurrentScene()->setDirtyFlag(true); app->getCurrentLevel()->notifyLevelChange(); } void redo() const override { makeSpaceForFids(m_level.getPointer(), m_insertedFids); for (auto const &fid : m_insertedFids) { m_level->setFrame(fid, m_level->createEmptyFrame()); IconGenerator::instance()->invalidate(m_level.getPointer(), fid); } m_level->setDirtyFlag(true); TApp *app = TApp::instance(); app->getCurrentScene()->setDirtyFlag(true); app->getCurrentLevel()->notifyLevelChange(); } int getSize() const override { return sizeof(*this); } QString getHistoryString() override { QString str = QObject::tr("Add Frames : Level %1 : Frame ") .arg(QString::fromStdWString(m_level->getName())); std::set::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() override { return HistoryType::FilmStrip; } }; QString getNextLetter(const QString &letter) { // 空なら a を返す if (letter.isEmpty()) return QString('a'); // 1文字かつ z または Z ならEmptyを返す if (letter == 'z' || letter == 'Z') return QString(); QByteArray byteArray = letter.toUtf8(); // それ以外の場合、最後の文字をとにかく1進めて返す byteArray.data()[byteArray.size() - 1]++; return QString::fromUtf8(byteArray); }; } // 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 oldFids; sl->getFids(oldFids); TFrameId tmplFid; if (!oldFids.empty()) tmplFid = oldFids.front(); std::set fidsToInsert; int frame = 0; for (frame = start; frame <= end; frame += step) fidsToInsert.insert(TFrameId(frame, "", tmplFid.getZeroPadding(), tmplFid.getStartSeqInd())); makeSpaceForFids(sl, fidsToInsert); for (auto const &fid : fidsToInsert) { sl->setFrame(fid, sl->createEmptyFrame()); IconGenerator::instance()->invalidate(sl, fid); } 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 final : public TUndo { TXshSimpleLevelP m_level; std::vector m_fids; std::map m_mapOldFrameId; bool m_updateXSheet = false; public: RenumberUndo(const TXshSimpleLevelP &level, const std::vector &fids, bool forceCallUpdateXSheet = false) : m_level(level), m_fids(fids) { assert(m_level); std::vector 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]; } m_updateXSheet = Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled() || forceCallUpdateXSheet; } void renumber(std::vector fids) const { if (m_updateXSheet) { std::vector oldFrames; m_level->getFids(oldFrames); updateXSheet(m_level.getPointer(), oldFrames, fids); } m_level->renumber(fids); TSelection *selection = TSelection::getCurrent(); if (selection) selection->selectNone(); m_level->setDirtyFlag(true); TApp::instance()->getCurrentLevel()->notifyLevelChange(); } void undo() const override { std::vector 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::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 override { renumber(m_fids); } int getSize() const override { return sizeof(*this) + sizeof(TFrameId) * m_fids.size(); } QString getHistoryString() override { return QObject::tr("Renumber : Level %1") .arg(QString::fromStdWString(m_level->getName())); } int getHistoryType() override { return HistoryType::FilmStrip; } }; } // namespace //============================================================================= // Renumber //----------------------------------------------------------------------------- void FilmstripCmd::renumber( TXshSimpleLevel *sl, const std::vector> &table, bool forceCallUpdateXSheet) { 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>::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 fids, rfids, oldFrames; sl->getFids(fids); sl->getFids(oldFrames); std::set 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 { tarFid = TFrameId(tarFid.getNumber(), getNextLetter(tarFid.getLetter()), tarFid.getZeroPadding(), tarFid.getStartSeqInd()); } while (!tarFid.getLetter().isEmpty() && tmp.count(tarFid) > 0); if (tarFid.getLetter().isEmpty()) { // todo: error message return; } } tmp.insert(tarFid); fids[i] = tarFid; } } TUndoManager::manager()->add( new RenumberUndo(sl, fids, forceCallUpdateXSheet)); if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled() || forceCallUpdateXSheet) { updateXSheet(sl, oldFrames, fids); } sl->renumber(fids); TApp::instance()->getCurrentScene()->setDirtyFlag(true); TApp::instance()->getCurrentLevel()->notifyLevelChange(); /* int i; std::set::iterator it2; it2=frames.begin(); std::set newFrames; for (i=0; igetLetter())); assert(frames.size()==newFrames.size()); frames.swap(newFrames); */ } //----------------------------------------------------------------------------- void FilmstripCmd::renumber(TXshSimpleLevel *sl, std::set &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 fids; std::vector oldFrames; sl->getFids(oldFrames); sl->getFids(fids); std::set modifiedFids; // tmp contiene i frames del livello, meno quelli da renumerare std::set tmp(fids.begin(), fids.end()); std::set::const_iterator it; for (it = frames.begin(); it != frames.end(); ++it) tmp.erase(*it); int frame = startFrame; std::vector::iterator j = fids.begin(); for (it = frames.begin(); it != frames.end(); ++it) { TFrameId srcFid(*it); TFrameId dstFid(frame, "", srcFid.getZeroPadding(), srcFid.getStartSeqInd()); 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)); if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) { updateXSheet(sl, oldFrames, fids); } sl->renumber(fids); sl->setDirtyFlag(true); TApp::instance()->getCurrentScene()->setDirtyFlag(true); int i; std::set::iterator it2; it2 = frames.begin(); std::set newFrames; for (i = 0; i < frames.size(); i++, it2++) newFrames.insert(TFrameId(startFrame + (i * stepFrame), it2->getLetter(), it2->getZeroPadding(), it2->getStartSeqInd())); assert(frames.size() == newFrames.size()); frames.swap(newFrames); TApp::instance()->getCurrentLevel()->notifyLevelChange(); } //============================================================================= // copy //----------------------------------------------------------------------------- void FilmstripCmd::copy(TXshSimpleLevel *sl, std::set &frames) { if (!sl || frames.empty()) return; copyFramesWithoutUndo(sl, frames); } //============================================================================= // paste //----------------------------------------------------------------------------- void FilmstripCmd::paste(TXshSimpleLevel *sl, std::set &frames) { if (!sl || sl->isReadOnly() || frames.empty()) return; std::vector 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(data); if (drawingData) { if (sl->isSubsequence()) return; // keep the chosen 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> indices; TUndo *undo = 0; TPaletteP plt = sl->getPalette()->clone(); QImage clipImage = clipboard->image(); FullColorImageData *fullColorData = dynamic_cast(data); if ((!clipImage.isNull() || fullColorData) && sl->getType() != OVL_XSHLEVEL) { DVGui::error(QObject::tr( "Can't paste full raster data on a non full raster level.")); return; } TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); ToolHandle *toolHandle = TApp::instance()->getCurrentTool(); if (toolHandle->getTool()->getName() == "T_Selection") { TSelection *ts = toolHandle->getTool()->getSelection(); RasterSelection *rs = dynamic_cast(ts); StrokeSelection *ss = dynamic_cast(ts); if (rs) { toolHandle->getTool()->onActivate(); rs->pasteSelection(); return; } if (ss) { toolHandle->getTool()->onActivate(); ss->paste(); return; } } if (sl->getType() == OVL_XSHLEVEL && !clipImage.isNull()) { // This stuff is only if we have a pasted image from outside Tahoma if (sl->getResolution().lx < clipImage.width() || sl->getResolution().ly < clipImage.height()) { clipImage = clipImage.scaled(sl->getResolution().lx, sl->getResolution().ly, Qt::KeepAspectRatio); } // create variables to go into the Full Color Image data std::vector rects; const std::vector strokes; const std::vector originalStrokes; TAffine aff; TRasterP ras = rasterFromQImage(clipImage); rects.push_back(TRectD(0.0 - clipImage.width() / 2, 0.0 - clipImage.height() / 2, clipImage.width() / 2, clipImage.height() / 2)); FullColorImageData *qimageData = new FullColorImageData(); TDimension dim = sl->getResolution(); qimageData->setData(ras, plt, 120.0, 120.0, dim, rects, strokes, originalStrokes, aff); data = qimageData; // end of pasted from outside Tahoma stuff // rasterImageData holds all the info either way now. } bool isPaste = pasteAreasWithoutUndo(data, sl, frames, &tileSet, indices); RasterImageData *rasterImageData = dynamic_cast(data); StrokesData *strokesData = dynamic_cast(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 &frames) { if (!sl || sl->isReadOnly() || sl->isSubsequence()) return; std::vector oldLevelFrameId; sl->getFids(oldLevelFrameId); TPaletteP oldPalette = sl->getPalette()->clone(); std::set frameIdToChange; QClipboard *clipboard = QApplication::clipboard(); if (const DrawingData *drawingData = dynamic_cast(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 &frames) { if (!sl || sl->isReadOnly() || sl->isSubsequence()) return; std::vector oldLevelFrameId; sl->getFids(oldLevelFrameId); TPaletteP oldPalette; if (TPalette *pal = sl->getPalette()) oldPalette = pal->clone(); QClipboard *clipboard = QApplication::clipboard(); if (const DrawingData *drawingData = dynamic_cast(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 &frames) { if (!sl || frames.empty() || sl->isSubsequence() || sl->isReadOnly()) return; std::set framesToCut = frames; std::vector oldFrames; sl->getFids(oldFrames); cutFramesWithoutUndo(sl, frames); TUndoManager::manager()->add(new CutFramesUndo(sl, framesToCut, oldFrames)); } //============================================================================= // delete //----------------------------------------------------------------------------- void FilmstripCmd::deleteFrames(TXshSimpleLevel *sl, std::set &frames) { // make sure there are actual drawing frames to deal with. if (!sl || frames.empty() || sl->isSubsequence() || sl->isReadOnly()) return; // still checking that the level is editable. if (sl->isReadOnly()) { std::set editableFrames = sl->getEditableRange(); if (editableFrames.empty()) return; // Browse all the frames and return if some frames are not editable std::set::const_iterator it; for (it = frames.begin(); it != frames.end(); ++it) { TFrameId frameId = *it; if (editableFrames.count(frameId) == 0) return; } } std::set framesToDelete = frames; std::vector oldFrames; sl->getFids(oldFrames); // set up the drawing data that will be necessary to undo the delete. HookSet *levelHooks = sl->getHookSet(); std::map imageSet = deleteFramesWithoutUndo(sl, frames); DrawingData *oldData = new DrawingData(); oldData->setFrames(imageSet, sl, *levelHooks); TUndoManager::manager()->add( new DeleteFramesUndo(sl, framesToDelete, oldFrames, oldData)); } //============================================================================= // clear //----------------------------------------------------------------------------- void FilmstripCmd::clear(TXshSimpleLevel *sl, std::set &frames) { if (!sl || frames.empty()) return; if (sl->isReadOnly()) { std::set editableFrames = sl->getEditableRange(); if (editableFrames.empty()) return; // Browser all the frames and return if some frames are not editable std::set::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 oldFrames = frames; std::map 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 ClearFramesUndo(sl, oldFrames, oldData, newData)); } //----------------------------------------------------------------------------- namespace { //----------------------------------------------------------------------------- void insertEmptyFilmstripFrames(const TXshSimpleLevelP &sl, const std::set &frames) { if (!sl || frames.empty()) return; makeSpaceForFids(sl.getPointer(), frames); std::set::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 final : public TUndo { TXshSimpleLevelP m_level; std::vector m_oldFrames; std::set m_frames; bool m_updateXSheet; public: UndoInsertEmptyFrames(const TXshSimpleLevelP &level, std::set frames, std::vector oldFrames) : m_level(level), m_frames(frames), m_oldFrames(oldFrames) { if (m_level->getType() == TZP_XSHLEVEL) { std::set::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); } } m_updateXSheet = Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled(); } ~UndoInsertEmptyFrames() { // TImageCache::instance()->remove("UndoInsertEmptyFrames"+QString::number((UINT)this)); TImageCache::instance()->remove("UndoInsertEmptyFrames" + QString::number((uintptr_t) this)); } void undo() const override { removeFramesWithoutUndo(m_level, m_frames); assert(m_oldFrames.size() == m_level->getFrameCount()); if (m_updateXSheet) { std::vector newFrames; m_level->getFids(newFrames); updateXSheet(m_level.getPointer(), newFrames, m_oldFrames); } m_level->renumber(m_oldFrames); m_level->setDirtyFlag(true); TApp::instance()->getCurrentLevel()->notifyLevelChange(); } void redo() const override { 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::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 override { return sizeof(*this); } QString getHistoryString() override { return QObject::tr("Insert : Level %1") .arg(QString::fromStdWString(m_level->getName())); } int getHistoryType() override { return HistoryType::FilmStrip; } }; } // namespace //============================================================================= // insert //----------------------------------------------------------------------------- void FilmstripCmd::insert(TXshSimpleLevel *sl, const std::set &frames, bool withUndo) { if (frames.empty() || !sl || sl->isSubsequence() || sl->isReadOnly()) return; std::vector 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 &frames) { if (!sl || frames.empty()) return; std::vector fids; std::vector oldFrames; sl->getFids(oldFrames); 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; std::swap(fids[i], fids[j]); i++; j--; } if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) { updateXSheet(sl.getPointer(), oldFrames, fids); } sl->renumber(fids); sl->setDirtyFlag(true); // TApp::instance()->getCurrentScene()->setDirtyFlag(true); TApp::instance()->getCurrentLevel()->notifyLevelChange(); } //----------------------------------------------------------------------------- class FilmstripReverseUndo final : public TUndo { TXshSimpleLevelP m_level; std::set m_frames; public: FilmstripReverseUndo(TXshSimpleLevelP level, std::set frames) : m_level(level), m_frames(frames) {} void undo() const override { performReverse(m_level, m_frames); } void redo() const override { performReverse(m_level, m_frames); } int getSize() const override { return sizeof *this; } QString getHistoryString() override { return QObject::tr("Reverse : Level %1") .arg(QString::fromStdWString(m_level->getName())); } int getHistoryType() override { return HistoryType::FilmStrip; } }; } // namespace //============================================================================= // reverse //----------------------------------------------------------------------------- void FilmstripCmd::reverse(TXshSimpleLevel *sl, std::set &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 &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 framesToInsert; int i; for (i = 0; i < count; i++) framesToInsert.insert(insertPoint + i); std::vector clonedImages; std::set::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 final : public TUndo { TXshSimpleLevelP m_level; std::set m_frames; std::set m_newFrames; public: FilmstripSwingUndo(const TXshSimpleLevelP &level, const std::set &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 framesToInsert; int i; for (i = 0; i < count; i++) m_newFrames.insert(insertPoint + i); } void undo() const override { TSelection *selection = TSelection::getCurrent(); if (selection) selection->selectNone(); removeFramesWithoutUndo(m_level, m_newFrames); } void redo() const override { TSelection *selection = TSelection::getCurrent(); if (selection) selection->selectNone(); performSwing(m_level, m_frames); } int getSize() const override { return sizeof *this; } QString getHistoryString() override { return QObject::tr("Swing : Level %1") .arg(QString::fromStdWString(m_level->getName())); } int getHistoryType() override { return HistoryType::FilmStrip; } }; } // namespace //============================================================================= // swing //----------------------------------------------------------------------------- void FilmstripCmd::swing(TXshSimpleLevel *sl, std::set &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 &frames, int step = 2) { if (!sl || frames.empty() || step < 2) return; std::vector fids; std::set changedFids; std::vector insertIndices; std::vector oldFrames; sl->getFids(oldFrames); 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; } } if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) { updateXSheet(sl.getPointer(), oldFrames, fids); } 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); sl->setDirtyFlag(true); TApp::instance()->getCurrentLevel()->notifyLevelChange(); } //----------------------------------------------------------------------------- class StepFilmstripUndo final : public TUndo { TXshSimpleLevelP m_level; std::set m_insertedFrames; std::set m_frames; std::vector m_oldFrames; int m_step; bool m_updateXSheet; public: StepFilmstripUndo(const TXshSimpleLevelP &level, const std::set &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::const_iterator it; for (it = frames.begin(); it != frames.end(); ++it) for (int j = 1; j < step; j++) m_insertedFrames.insert(*it + (++d)); m_updateXSheet = Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled(); } void undo() const override { removeFramesWithoutUndo(m_level, m_insertedFrames); std::set::const_iterator it = m_frames.begin(); if (m_updateXSheet) { std::vector newFrames; m_level->getFids(newFrames); updateXSheet(m_level.getPointer(), newFrames, m_oldFrames); } m_level->renumber(m_oldFrames); TSelection *selection = TSelection::getCurrent(); if (selection) selection->selectNone(); m_level->setDirtyFlag(true); TApp::instance()->getCurrentLevel()->notifyLevelChange(); } void redo() const override { TSelection *selection = TSelection::getCurrent(); if (selection) selection->selectNone(); stepFilmstripFrames(m_level, m_frames, m_step); } int getSize() const override { return sizeof *this; } QString getHistoryString() override { return QObject::tr("Step %1 : Level %2") .arg(QString::number(m_step)) .arg(QString::fromStdWString(m_level->getName())); } int getHistoryType() override { return HistoryType::FilmStrip; } }; } // namespace //============================================================================= // step //----------------------------------------------------------------------------- void FilmstripCmd::step(TXshSimpleLevel *sl, std::set &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 eachFilmstripFrames( const TXshSimpleLevelP &sl, const std::set &frames, int each) { if (frames.empty() || !sl || each < 2) return std::map(); std::vector framesToDelete; std::set::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::reverse_iterator fit; std::map 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 final : public TUndo { TXshSimpleLevelP m_level; std::set m_frames; std::map m_cutFrames; int m_each; public: EachFilmstripUndo(const TXshSimpleLevelP &level, int each, const std::set &frames, std::map deletedFrames) : m_level(level) , m_cutFrames(deletedFrames) , m_each(each) , m_frames(frames) {} ~EachFilmstripUndo() { std::map::iterator it = m_cutFrames.begin(); for (; it != m_cutFrames.end(); ++it) TImageCache::instance()->remove(it->second); } void undo() const override { TSelection *selection = TSelection::getCurrent(); if (selection) selection->selectNone(); insertNotEmptyframes(m_level, m_cutFrames); } void redo() const override { TSelection *selection = TSelection::getCurrent(); if (selection) selection->selectNone(); eachFilmstripFrames(m_level, m_frames, m_each); } int getSize() const override { return sizeof *this; } QString getHistoryString() override { return QObject::tr("Each %1 : Level %2") .arg(QString::number(m_each)) .arg(QString::fromStdWString(m_level->getName())); } int getHistoryType() override { return HistoryType::FilmStrip; } }; } // namespace //============================================================================= // each //----------------------------------------------------------------------------- void FilmstripCmd::each(TXshSimpleLevel *sl, std::set &frames, int each) { if (!sl || sl->isSubsequence() || sl->isReadOnly()) return; std::map deletedFrames = eachFilmstripFrames(sl, frames, each); TUndoManager::manager()->add( new EachFilmstripUndo(sl, each, frames, deletedFrames)); frames.clear(); TApp::instance()->getCurrentScene()->setDirtyFlag(true); } //============================================================================= //----------------------------------------------------------------------------- namespace { //----------------------------------------------------------------------------- class UndoDuplicateDrawing final : public TUndo { TXshSimpleLevelP m_level; std::set m_frameInserted; std::vector m_oldFrames; std::set m_framesForRedo; bool m_updateXSheet; public: UndoDuplicateDrawing(const TXshSimpleLevelP &level, const std::vector oldFrames, std::set frameInserted, std::set framesForRedo) : m_level(level) , m_oldFrames(oldFrames) , m_frameInserted(frameInserted) , m_framesForRedo(framesForRedo) { m_updateXSheet = Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled(); } void undo() const override { assert(m_level); removeFramesWithoutUndo(m_level, m_frameInserted); if (m_updateXSheet) { std::vector newFrames; m_level->getFids(newFrames); updateXSheet(m_level.getPointer(), newFrames, m_oldFrames); } m_level->renumber(m_oldFrames); m_level->setDirtyFlag(true); TApp::instance()->getCurrentLevel()->notifyLevelChange(); } void redo() const override { std::set framesForRedo = m_framesForRedo; FilmstripCmd::duplicate(m_level.getPointer(), framesForRedo, false); } int getSize() const override { return sizeof(*this); } QString getHistoryString() override { return QObject::tr("Duplicate : Level %1") .arg(QString::fromStdWString(m_level->getName())); } int getHistoryType() override { return HistoryType::FilmStrip; } }; } // namespace //============================================================================= // duplicate //----------------------------------------------------------------------------- void FilmstripCmd::duplicateFrameWithoutUndo(TXshSimpleLevel *sl, TFrameId srcFrame, TFrameId targetFrame) { if (srcFrame.isNoFrame() || targetFrame.isNoFrame()) return; if (srcFrame.isEmptyFrame()) return; std::set frames; frames.insert(srcFrame); DrawingData *data = new DrawingData(); data->setLevelFrames(sl, frames); frames.clear(); frames.insert(targetFrame); bool keepOriginalPalette = true; pasteFramesWithoutUndo(data, sl, frames, DrawingData::OVER_SELECTION, true, keepOriginalPalette); } void FilmstripCmd::duplicate(TXshSimpleLevel *sl, std::set &frames, bool withUndo) { if (frames.empty() || !sl || sl->isSubsequence() || sl->isReadOnly()) return; TFrameId insertPoint = (*frames.rbegin()) + 1; std::map framesToInsert; std::set newFrames; int i = 0; for (auto const &fid : frames) { TImageP img = sl->getFrame(fid, false); TImageP imgClone = (img) ? img->cloneImage() : 0; QString id = "dupFrames" + QString::number((uintptr_t)sl) + "-" + QString::number(fid.getNumber()); TImageCache::instance()->add(id, imgClone); framesToInsert[insertPoint + i] = id; newFrames.insert(insertPoint + i); i++; } std::vector 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 &frames) { if (frames.empty() || !level) return; TXsheetHandle *xh = TApp::instance()->getCurrentXsheet(); TXsheet *xsh = xh->getXsheet(); int row = 0; int col = xsh->getFirstFreeColumnIndex(); std::set::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 final : public TUndo { std::wstring m_levelName; int m_col; std::set m_fids; public: MoveLevelToSceneUndo(std::wstring levelName, int col, std::set fids) : m_levelName(levelName), m_col(col), m_fids(fids) {} void undo() const override { 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 override { 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 override { return sizeof *this; } QString getHistoryString() override { return QObject::tr("Move Level to Scene : Level %1") .arg(QString::fromStdWString(m_levelName)); } int getHistoryType() override { return HistoryType::FilmStrip; } }; } // namespace //============================================================================= // moveToScene //----------------------------------------------------------------------------- void FilmstripCmd::moveToScene(TXshLevel *sl, std::set &frames) { if (frames.empty() || !sl) return; TXsheetHandle *xh = TApp::instance()->getCurrentXsheet(); TXsheet *xsh = xh->getXsheet(); int row = 0; int col = xsh->getFirstFreeColumnIndex(); std::set::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 fids; sl->getFids(fids); std::set fidsSet(fids.begin(), fids.end()); moveToScene(sl, fidsSet); } //----------------------------------------------------------------------------- void FilmstripCmd::moveToScene(TXshPaletteLevel *pl) { if (!pl) return; std::set 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::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 fids; sl->getFids(fids); std::set fidsSet(fids.begin(), fids.end()); moveToScene(sl, fidsSet); } //============================================================================= // UndoInbetween //----------------------------------------------------------------------------- namespace { class UndoInbetween final : public TUndo { TXshSimpleLevelP m_level; std::vector m_fids; std::vector m_images; FilmstripCmd::InbetweenInterpolation m_interpolation; public: UndoInbetween(TXshSimpleLevel *xl, std::vector fids, FilmstripCmd::InbetweenInterpolation interpolation) : m_level(xl), m_fids(fids), m_interpolation(interpolation) { std::vector::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 override { 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 override { TFrameId fid0 = *m_fids.begin(); TFrameId fid1 = *(--m_fids.end()); FilmstripCmd::inbetweenWithoutUndo(m_level.getPointer(), fid0, fid1, m_interpolation); } int getSize() const override { assert(!m_images.empty()); return m_images.size() * m_images.front()->getStrokeCount() * 100; } QString getHistoryString() override { QString str = QObject::tr("Inbetween : Level %1, ") .arg(QString::fromStdWString(m_level->getName())); switch (m_interpolation) { case FilmstripCmd::II_Linear: str += QString("Linear Interpolation"); break; case FilmstripCmd::II_EaseIn: str += QString("Ease In Interpolation"); break; case FilmstripCmd::II_EaseOut: str += QString("Ease Out Interpolation"); break; case FilmstripCmd::II_EaseInOut: str += QString("Ease In-Out Interpolation"); break; } return str; } int getHistoryType() override { return HistoryType::FilmStrip; } }; } // namespace //============================================================================= // inbetween //----------------------------------------------------------------------------- void FilmstripCmd::inbetweenWithoutUndo( TXshSimpleLevel *sl, const TFrameId &fid0, const TFrameId &fid1, FilmstripCmd::InbetweenInterpolation interpolation) { if (!sl) return; std::vector fids; sl->getFids(fids); std::vector::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; enum TInbetween::TweenAlgorithm algorithm; switch (interpolation) { case II_Linear: algorithm = TInbetween::LinearInterpolation; break; case II_EaseIn: algorithm = TInbetween::EaseInInterpolation; break; case II_EaseOut: algorithm = TInbetween::EaseOutInterpolation; break; case II_EaseInOut: algorithm = TInbetween::EaseInOutInterpolation; break; } TInbetween inbetween(img0, img1); int i; for (i = ia + 1; i < ib; i++) { double t = (double)(i - ia) / (double)(ib - ia); double s = TInbetween::interpolation(t, algorithm); 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 fids; std::vector levelFids; sl->getFids(levelFids); for (auto const &fid : levelFids) { int curFid = fid.getNumber(); if (fid0.getNumber() <= curFid && curFid <= fid1.getNumber()) fids.push_back(fid); } 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 fids; std::vector oldFrames; sl->getFids(oldFrames); sl->getFids(fids); std::vector::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()) { QString nextLetter = getNextLetter(newFid.getLetter()); if (nextLetter.isEmpty()) return; newFid = TFrameId(newFid.getNumber(), nextLetter); } *it = newFid; if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) { updateXSheet(sl, oldFrames, fids); } sl->renumber(fids); sl->setDirtyFlag(true); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); TApp::instance()->getCurrentLevel()->notifyLevelChange(); TApp::instance()->getCurrentScene()->setDirtyFlag(true); }