diff --git a/toonz/sources/include/toonzqt/fxselection.h b/toonz/sources/include/toonzqt/fxselection.h index 81adeee..45b3278 100644 --- a/toonz/sources/include/toonzqt/fxselection.h +++ b/toonz/sources/include/toonzqt/fxselection.h @@ -156,6 +156,7 @@ signals: void doCollapse(const QList &); void doExplodeChild(const QList &); void doDelete(); + void columnPasted(const QList &); }; #endif diff --git a/toonz/sources/include/toonzqt/schematicviewer.h b/toonz/sources/include/toonzqt/schematicviewer.h index 3e5c552..14e013d 100644 --- a/toonz/sources/include/toonzqt/schematicviewer.h +++ b/toonz/sources/include/toonzqt/schematicviewer.h @@ -5,6 +5,7 @@ // TnzLib includes #include "toonz/tstageobjectid.h" +#include "toonz/txshcolumn.h" // TnzBase includes #include "tfx.h" @@ -569,6 +570,7 @@ signals: void doDeleteFxs(const FxSelection *); void doDeleteStageObjects(const StageObjectSelection *); + void columnPasted(const QList &); protected slots: void onSceneChanged(); diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt index 7b9b27c..a338383 100644 --- a/toonz/sources/toonz/CMakeLists.txt +++ b/toonz/sources/toonz/CMakeLists.txt @@ -185,6 +185,7 @@ set(HEADERS ../include/orientation.h ../include/saveloadqsettings.h xdtsio.h + levelcommand.h # Tracker file ObjectTracker.h dummyprocessor.h diff --git a/toonz/sources/toonz/cellselection.cpp b/toonz/sources/toonz/cellselection.cpp index 643180d..e0ed921 100644 --- a/toonz/sources/toonz/cellselection.cpp +++ b/toonz/sources/toonz/cellselection.cpp @@ -13,6 +13,7 @@ #include "timestretchpopup.h" #include "tapp.h" #include "xsheetviewer.h" +#include "levelcommand.h" // TnzTools includes #include "tools/toolutils.h" @@ -1391,6 +1392,26 @@ public: //----------------------------------------------------------------------------- }; +//----------------------------------------------------------------------------- +// obtain level set contained in the cellData +// it is used for checking and updating the scene cast when pasting +void getLevelSetFromData(const TCellData *cellData, + std::set &levelSet) { + for (int c = 0; c < cellData->getCellCount(); c++) { + TXshCell cell = cellData->getCell(c); + if (cell.isEmpty()) continue; + TXshLevelP tmpLevel = cell.m_level; + if (levelSet.count(tmpLevel.getPointer()) == 0) { + // gather levels in subxsheet + if (tmpLevel->getChildLevel()) { + TXsheet *childXsh = tmpLevel->getChildLevel()->getXsheet(); + childXsh->getUsedLevels(levelSet); + } + levelSet.insert(tmpLevel.getPointer()); + } + } +} + } // namespace //----------------------------------------------------------------------------- @@ -1749,8 +1770,16 @@ void TCellSelection::pasteCells() { (newCr0 == r0 && newCr1 == r1)); } if (!isPaste) return; + initUndo = true; TUndoManager::manager()->beginBlock(); + + // make sure that the pasting levels are registered in the scene cast + // it may rename the level if there is another level with the same name + std::set pastedLevels; + getLevelSetFromData(cellData, pastedLevels); + LevelCmd::addMissingLevelsToCast(pastedLevels); + TUndoManager::manager()->add(new PasteCellsUndo( r0, c0, r1, c1, oldR0, oldC0, oldR1, oldC1, areColumnsEmpty)); TApp::instance()->getCurrentScene()->setDirtyFlag(true); @@ -2159,6 +2188,12 @@ void TCellSelection::pasteDuplicateCells() { return; } + // make sure that the pasting levels are registered in the scene cast + // it may rename the level if there is another level with the same name + std::set pastedLevels; + getLevelSetFromData(newCellData, pastedLevels); + LevelCmd::addMissingLevelsToCast(pastedLevels); + TUndoManager::manager()->add(new PasteCellsUndo( r0, c0, r1, c1, oldR0, oldC0, oldR1, oldC1, areColumnsEmpty)); TApp::instance()->getCurrentScene()->setDirtyFlag(true); @@ -2994,10 +3029,19 @@ void TCellSelection::overWritePasteCells() { areColumnsEmpty.push_back(!column || column->isEmpty() || (newCr0 == r0 && newCr1 == r1)); } + TUndoManager::manager()->beginBlock(); + + // make sure that the pasting levels are registered in the scene cast + // it may rename the level if there is another level with the same name + std::set pastedLevels; + getLevelSetFromData(cellData, pastedLevels); + LevelCmd::addMissingLevelsToCast(pastedLevels); + /*-- r0,c0,r1,c1はペーストされた範囲 old付きはペースト前の選択範囲 --*/ TUndoManager::manager()->add( new OverwritePasteCellsUndo(r0, c0, r1, c1, oldR0, oldC0, oldR1, oldC1, areColumnsEmpty, beforeData)); + TUndoManager::manager()->endBlock(); TApp::instance()->getCurrentScene()->setDirtyFlag(true); delete beforeData; diff --git a/toonz/sources/toonz/columnselection.cpp b/toonz/sources/toonz/columnselection.cpp index 0c9cb82..b84616a 100644 --- a/toonz/sources/toonz/columnselection.cpp +++ b/toonz/sources/toonz/columnselection.cpp @@ -7,6 +7,7 @@ #include "menubarcommandids.h" #include "columncommand.h" #include "tapp.h" +#include "levelcommand.h" // TnzLib includes #include "toonz/txsheethandle.h" @@ -19,10 +20,48 @@ #include "toonz/levelproperties.h" #include "orientation.h" #include "toonz/preferences.h" +#include "toonz/txshchildlevel.h" // TnzCore includes #include "tvectorimage.h" #include "ttoonzimage.h" +#include "tundo.h" + +namespace { +// obtain level set contained in the column specified by indices +// it is used for checking and updating the scene cast when pasting +// based on TXsheet::getUsedLevels +void getLevelSetFromColumnIndices(const std::set& indices, + std::set& levelSet) { + TXsheet* xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); + for (auto c : indices) { + TXshColumnP column = const_cast(xsh)->getColumn(c); + if (!column) continue; + + TXshCellColumn* cellColumn = column->getCellColumn(); + if (!cellColumn) continue; + + int r0, r1; + if (!cellColumn->getRange(r0, r1)) continue; + + TXshLevel* level = 0; + for (int r = r0; r <= r1; r++) { + TXshCell cell = cellColumn->getCell(r); + if (cell.isEmpty() || !cell.m_level) continue; + + if (level != cell.m_level.getPointer()) { + level = cell.m_level.getPointer(); + levelSet.insert(level); + if (level->getChildLevel()) { + TXsheet* childXsh = level->getChildLevel()->getXsheet(); + childXsh->getUsedLevels(levelSet); + } + } + } + } +} + +} // namespace //============================================================================= // TColumnSelection @@ -78,7 +117,21 @@ void TColumnSelection::pasteColumns() { return; else indices.insert(*m_indices.begin()); + + TUndoManager::manager()->beginBlock(); + ColumnCmd::pasteColumns(indices); + + if (!indices.empty()) { + // make sure that the levels contained in the pasted columns are registered + // in the scene cast it may rename the level if there is another level with + // the same name + std::set pastedLevels; + getLevelSetFromColumnIndices(indices, pastedLevels); + LevelCmd::addMissingLevelsToCast(pastedLevels); + } + + TUndoManager::manager()->endBlock(); } //----------------------------------------------------------------------------- @@ -86,11 +139,25 @@ void TColumnSelection::pasteColumns() { void TColumnSelection::pasteColumnsAbove() { std::set indices; if (isEmpty()) { // in case that no columns are selected - TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); + TXsheet* xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); indices.insert(xsh->getFirstFreeColumnIndex()); } else indices.insert(*m_indices.rbegin() + 1); + + TUndoManager::manager()->beginBlock(); + ColumnCmd::pasteColumns(indices); + + if (!indices.empty()) { + // make sure that the levels contained in the pasted columns are registered + // in the scene cast it may rename the level if there is another level with + // the same name + std::set pastedLevels; + getLevelSetFromColumnIndices(indices, pastedLevels); + LevelCmd::addMissingLevelsToCast(pastedLevels); + } + + TUndoManager::manager()->endBlock(); } //----------------------------------------------------------------------------- @@ -136,7 +203,7 @@ void TColumnSelection::explodeChild() { //----------------------------------------------------------------------------- static bool canMergeColumns(int column, int mColumn, bool forMatchlines) { - TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); + TXsheet* xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); if (column < 0 || mColumn < 0) return false; @@ -256,9 +323,9 @@ void TColumnSelection::cloneChild() { //----------------------------------------------------------------------------- void TColumnSelection::hideColumns() { - TApp *app = TApp::instance(); + TApp* app = TApp::instance(); for (auto o : Orientations::all()) { - ColumnFan *columnFan = + ColumnFan* columnFan = app->getCurrentXsheet()->getXsheet()->getColumnFan(o); std::set::iterator it = m_indices.begin(); for (; it != m_indices.end(); ++it) columnFan->deactivate(*it); diff --git a/toonz/sources/toonz/levelcommand.cpp b/toonz/sources/toonz/levelcommand.cpp index 9abd2a4..0e764cb 100644 --- a/toonz/sources/toonz/levelcommand.cpp +++ b/toonz/sources/toonz/levelcommand.cpp @@ -1,5 +1,5 @@ - +#include "levelcommand.h" #include "toonzqt/menubarcommand.h" #include "menubarcommandids.h" #include "tapp.h" @@ -19,6 +19,7 @@ #include "toonz/levelset.h" #include "toonz/txshcell.h" #include "toonz/childstack.h" +#include "toonz/txshchildlevel.h" #include "toonzqt/dvdialog.h" #include "toonzqt/icongenerator.h" @@ -30,6 +31,7 @@ #include "tsystem.h" #include "toonzqt/gutil.h" +#include "toonz/namebuilder.h" namespace { @@ -426,3 +428,119 @@ public: void execute() override { revertTo(false); } } revertToLastSaveCommand; + +//----------------------------------------------------------------------------- +namespace { +class addLevelToCastUndo final : public TUndo { + TXshLevelP m_xl; + std::wstring m_newName, m_oldName; + +public: + addLevelToCastUndo(TXshLevel *xl, const std::wstring newName = L"", + const std::wstring oldName = L"") + : m_xl(xl), m_newName(newName), m_oldName(oldName) {} + + void undo() const override { + TLevelSet *levelSet = + TApp::instance()->getCurrentScene()->getScene()->getLevelSet(); + levelSet->removeLevel(m_xl.getPointer()); + if (m_oldName != L"") m_xl->setName(m_oldName); + if (m_isLastInBlock) + TApp::instance()->getCurrentScene()->notifyCastChange(); + } + void redo() const override { + TLevelSet *levelSet = + TApp::instance()->getCurrentScene()->getScene()->getLevelSet(); + if (m_newName != L"") m_xl->setName(m_newName); + levelSet->insertLevel(m_xl.getPointer()); + if (m_isLastInRedoBlock) + TApp::instance()->getCurrentScene()->notifyCastChange(); + } + + int getSize() const override { return sizeof *this + 100; } + + QString getHistoryString() override { + return QObject::tr("Add Level to Scene Cast : %1") + .arg(QString::fromStdWString(m_xl->getName())); + } +}; + +}; // namespace + +void LevelCmd::addMissingLevelsToCast(const QList &columns) { + // make sure that the levels contained in the pasted columns are registered in + // the scene cast it may rename the level if there is another level with the + // same name + std::set levels; + // obtain level set contained in the specified columns + // it is used for checking and updating the scene cast when pasting + // based on TXsheet::getUsedLevels + for (auto column : columns) { + if (!column) continue; + + TXshCellColumn *cellColumn = column->getCellColumn(); + if (!cellColumn) continue; + + int r0, r1; + if (!cellColumn->getRange(r0, r1)) continue; + + TXshLevel *level = 0; + for (int r = r0; r <= r1; r++) { + TXshCell cell = cellColumn->getCell(r); + if (cell.isEmpty() || !cell.m_level) continue; + + if (level != cell.m_level.getPointer()) { + level = cell.m_level.getPointer(); + levels.insert(level); + if (level->getChildLevel()) { + TXsheet *childXsh = level->getChildLevel()->getXsheet(); + childXsh->getUsedLevels(levels); + } + } + } + } + LevelCmd::addMissingLevelsToCast(levels); +} + +void LevelCmd::addMissingLevelsToCast(std::set &levels) { + if (levels.empty()) return; + TUndoManager::manager()->beginBlock(); + TLevelSet *levelSet = + TApp::instance()->getCurrentScene()->getScene()->getLevelSet(); + bool castChanged = false; + // for each level + for (auto level : levels) { + std::wstring levelName = level->getName(); + + // search by level name + if (TXshLevel *levelInCast = levelSet->getLevel(levelName)) { + // continue if it is the same level. This should be in most cases + if (level == levelInCast) continue; + + // if the the name is occupied by another level, then rename and register + // it + std::wstring oldName = levelName; + NameModifier nm(levelName); + levelName = nm.getNext(); + while (levelSet->hasLevel(levelName)) levelName = nm.getNext(); + addLevelToCastUndo *undo = + new addLevelToCastUndo(level, levelName, oldName); + undo->m_isLastInRedoBlock = false; // prevent to emit signal + undo->redo(); + TUndoManager::manager()->add(undo); + castChanged = true; + } + // if not found + else { + // register the level + addLevelToCastUndo *undo = new addLevelToCastUndo(level); + undo->redo(); + TUndoManager::manager()->add(undo); + castChanged = true; + } + } + + TUndoManager::manager()->endBlock(); + + if (castChanged) TApp::instance()->getCurrentScene()->notifyCastChange(); +} \ No newline at end of file diff --git a/toonz/sources/toonz/levelcommand.h b/toonz/sources/toonz/levelcommand.h new file mode 100644 index 0000000..d14132f --- /dev/null +++ b/toonz/sources/toonz/levelcommand.h @@ -0,0 +1,18 @@ +#pragma once + +#ifndef LEVELCOMMAND_H +#define LEVELCOMMAND_H + +#include "toonz/txshcolumn.h" + +#include +#include + +class TXshLevel; + +namespace LevelCmd { +void addMissingLevelsToCast(const QList& columns); +void addMissingLevelsToCast(std::set& levels); +} // namespace LevelCmd + +#endif diff --git a/toonz/sources/toonz/tpanels.cpp b/toonz/sources/toonz/tpanels.cpp index fb5b582..356b686 100644 --- a/toonz/sources/toonz/tpanels.cpp +++ b/toonz/sources/toonz/tpanels.cpp @@ -41,6 +41,7 @@ #include "tapp.h" #include "mainwindow.h" #include "columncommand.h" +#include "levelcommand.h" // TnzTools includes #include "tools/tooloptions.h" @@ -257,6 +258,12 @@ void SchematicScenePanel::onDeleteStageObjects( //----------------------------------------------------------------------------- +void SchematicScenePanel::onColumnPaste(const QList &columns) { + LevelCmd::addMissingLevelsToCast(columns); +} + +//----------------------------------------------------------------------------- + void SchematicScenePanel::showEvent(QShowEvent *e) { if (m_schematicViewer->isStageSchematicViewed()) setWindowTitle(QObject::tr("Stage Schematic")); @@ -290,6 +297,8 @@ void SchematicScenePanel::showEvent(QShowEvent *e) { SLOT(updateSchematic())); connect(app->getCurrentScene(), SIGNAL(sceneSwitched()), m_schematicViewer, SLOT(onSceneSwitched())); + connect(m_schematicViewer, SIGNAL(columnPasted(const QList &)), + this, SLOT(onColumnPaste(const QList &))); m_schematicViewer->updateSchematic(); } @@ -319,6 +328,9 @@ void SchematicScenePanel::hideEvent(QHideEvent *e) { m_schematicViewer, SLOT(updateSchematic())); disconnect(app->getCurrentScene(), SIGNAL(sceneSwitched()), m_schematicViewer, SLOT(onSceneSwitched())); + disconnect(m_schematicViewer, + SIGNAL(columnPasted(const QList &)), this, + SLOT(onColumnPaste(const QList &))); } //============================================================================= diff --git a/toonz/sources/toonz/tpanels.h b/toonz/sources/toonz/tpanels.h index 910a37c..d28041e 100644 --- a/toonz/sources/toonz/tpanels.h +++ b/toonz/sources/toonz/tpanels.h @@ -191,6 +191,7 @@ protected slots: void onEditObject(); void onDeleteFxs(const FxSelection *); void onDeleteStageObjects(const StageObjectSelection *); + void onColumnPaste(const QList &); }; //========================================================= diff --git a/toonz/sources/toonzqt/fxselection.cpp b/toonz/sources/toonzqt/fxselection.cpp index 873c608..99926a0 100644 --- a/toonz/sources/toonzqt/fxselection.cpp +++ b/toonz/sources/toonzqt/fxselection.cpp @@ -211,10 +211,22 @@ void FxSelection::pasteSelection() { TPointD(ssv->getOldScenePos().x(), ssv->getOldScenePos().y()); } + if (!columns.isEmpty()) { + // make sure that the levels contained in the pasted column nodes are + // registered in the scene cast it may rename the level if there is + // another level with the same name + TUndoManager::manager()->beginBlock(); + emit columnPasted(columns); + } + TFxCommand::pasteFxs(fxs.toStdList(), zeraryFxColumnSize.toStdMap(), columns.toStdList(), m_pastePosition, m_xshHandle, m_fxHandle); + if (!columns.isEmpty()) { + TUndoManager::manager()->endBlock(); + } + if (m_schematicScene) { selectNone(); for (int i = 0; i < (int)fxs.size(); i++) select(fxs[i]); @@ -263,6 +275,10 @@ bool FxSelection::insertPasteSelection() { if (!auto_.m_destruct) { auto_.m_destruct = true; TUndoManager::manager()->beginBlock(); + // make sure that the levels contained in the pasted column nodes are + // registered in the scene cast it may rename the level if there is + // another level with the same name + emit columnPasted(columns); } TFxCommand::insertPasteFxs(selectedLinks[i], fxs.toStdList(), @@ -349,8 +365,13 @@ bool FxSelection::replacePasteSelection() { if (fxs.empty() && columns.empty()) return true; // auto ends the undo block in its destructor - if (!auto_.m_destruct) + if (!auto_.m_destruct) { auto_.m_destruct = true, TUndoManager::manager()->beginBlock(); + // make sure that the levels contained in the pasted column nodes are + // registered in the scene cast it may rename the level if there is + // another level with the same name + emit columnPasted(columns); + } TFx *inFx = m_selectedFxs[i].getPointer(); TFxCommand::replacePasteFxs(inFx, fxs.toStdList(), diff --git a/toonz/sources/toonzqt/schematicviewer.cpp b/toonz/sources/toonzqt/schematicviewer.cpp index 063987c..56e471e 100644 --- a/toonz/sources/toonzqt/schematicviewer.cpp +++ b/toonz/sources/toonzqt/schematicviewer.cpp @@ -783,6 +783,13 @@ SchematicViewer::SchematicViewer(QWidget *parent) connect(m_stageScene->getStageSelection(), SIGNAL(doDelete()), this, SLOT(deleteStageObjects())); + connect(m_fxScene->getFxSelection(), + SIGNAL(columnPasted(const QList &)), this, + SIGNAL(columnPasted(const QList &))); + connect(m_stageScene->getStageSelection(), + SIGNAL(columnPasted(const QList &)), this, + SIGNAL(columnPasted(const QList &))); + m_viewer->setScene(m_stageScene); m_fxToolbar->hide(); diff --git a/toonz/sources/toonzqt/stageobjectselection.cpp b/toonz/sources/toonzqt/stageobjectselection.cpp index 6763ac8..9d66576 100644 --- a/toonz/sources/toonzqt/stageobjectselection.cpp +++ b/toonz/sources/toonzqt/stageobjectselection.cpp @@ -365,6 +365,21 @@ void StageObjectSelection::pasteSelection() { std::vector ids = objData->restoreObjects( indexes, restoredSplineIds, m_xshHandle->getXsheet(), StageObjectsData::eDoClone, m_pastePosition); + + // make sure that the levels contained in the pasted column nodes are + // registered in the scene cast it may rename the level if there is another + // level with the same name + QList pastedColumns; + for (auto c : indexes) { + TXshColumnP column = m_xshHandle->getXsheet()->getColumn(c); + if (!column || column->isEmpty()) continue; + pastedColumns.append(column); + } + if (!pastedColumns.isEmpty()) { + TUndoManager::manager()->beginBlock(); + emit columnPasted(pastedColumns); + } + StageObjectsData *undoData = new StageObjectsData(); undoData->storeObjects(ids, m_xshHandle->getXsheet(), 0); undoData->storeColumnFxs(indexes, m_xshHandle->getXsheet(), 0); @@ -374,6 +389,8 @@ void StageObjectSelection::pasteSelection() { m_objHandle, m_fxHandle)); m_xshHandle->notifyXsheetChanged(); m_pastePosition = TConst::nowhere; + + if (!pastedColumns.isEmpty()) TUndoManager::manager()->endBlock(); } //------------------------------------------------------- diff --git a/toonz/sources/toonzqt/stageobjectselection.h b/toonz/sources/toonzqt/stageobjectselection.h index 2063c75..c18bd71 100644 --- a/toonz/sources/toonzqt/stageobjectselection.h +++ b/toonz/sources/toonzqt/stageobjectselection.h @@ -5,6 +5,7 @@ #include "toonzqt/selection.h" #include "toonz/tstageobjectid.h" +#include "toonz/txshcolumn.h" #include "tgeometry.h" #include #include @@ -98,6 +99,7 @@ signals: void doCollapse(QList); void doExplodeChild(QList); void doDelete(); + void columnPasted(const QList &); }; #endif