diff --git a/toonz/sources/toonz/cellselection.cpp b/toonz/sources/toonz/cellselection.cpp index 4fc08e2..15a0e5d 100644 --- a/toonz/sources/toonz/cellselection.cpp +++ b/toonz/sources/toonz/cellselection.cpp @@ -1253,6 +1253,62 @@ public: //----------------------------------------------------------------------------- }; +//============================================================================= +// Duplicate in XSheet Undo +//----------------------------------------------------------------------------- + +class DuplicateInXSheetUndo final : public TUndo { + int m_r0, m_r1, m_c; + TXshCell m_oldCell; + TXshCell m_newCell; + bool m_goForward; + +public: + DuplicateInXSheetUndo(int r0, int r1, int c, TXshCell oldCell, + TXshCell newCell, bool goForward) + : m_r0(r0) + , m_r1(r1) + , m_c(c) + , m_oldCell(oldCell) + , m_newCell(newCell) + , m_goForward(goForward) {} + + ~DuplicateInXSheetUndo() {} + + void undo() const override { + TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); + if (m_r1 > m_r0) { + for (int i = m_r0; i <= m_r1; i++) { + xsh->setCell(i, m_c, m_oldCell); + } + } else + xsh->setCell(m_r0, m_c, m_oldCell); + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); + if (m_goForward) CommandManager::instance()->execute(MI_PrevFrame); + } + + void redo() const override { + TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); + if (m_r1 > m_r0) { + for (int i = m_r0; i <= m_r1; i++) { + xsh->setCell(i, m_c, m_newCell); + } + } else + xsh->setCell(m_r0, m_c, m_newCell); + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); + if (m_goForward) CommandManager::instance()->execute(MI_NextFrame); + } + + int getSize() const override { return sizeof(*this); } + + QString getHistoryString() override { + return QObject::tr("Duplicate Frame in XSheet"); + } + + int getHistoryType() override { return HistoryType::Xsheet; } + //----------------------------------------------------------------------------- +}; + } // namespace //----------------------------------------------------------------------------- @@ -1337,6 +1393,7 @@ void TCellSelection::enableCommands() { &TCellSelection::reframeWithEmptyInbetweens); enableCommand(this, MI_PasteNumbers, &TCellSelection::overwritePasteNumbers); + enableCommand(this, MI_Duplicate, &TCellSelection::duplicateFrame); } //----------------------------------------------------------------------------- // Used in RenameCellField::eventFilter() @@ -1853,6 +1910,96 @@ void TCellSelection::pasteKeyframesInto() { //----------------------------------------------------------------------------- +void TCellSelection::duplicateFrame() { + if (!Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) return; + + TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); + // TApp::instance()->getCurrentScene()->getScene()->g + int r0, c0, r1, c1; + getSelectedCells(r0, c0, r1, c1); + // check for cases that won't work + if (c1 > c0) { + DVGui::error("Please select only one layer to duplicate a frame."); + return; + } + if (r1 > r0) { + DVGui::error("Please select only one frame to duplicate."); + return; + } + + r0 = TTool::getApplication()->getCurrentFrame()->getFrame(); + c0 = TTool::getApplication()->getCurrentColumn()->getColumnIndex(); + + TXshCell cell = xsh->getCell(r0, c0); + TXshCell prevCell = xsh->getCell(r0 - 1, c0); + TXshCell nextCell = xsh->getCell(r0 + 1, c0); + TFrameId selectedFrameId = cell.getFrameId(); + TXshSimpleLevel *sl = cell.getSimpleLevel(); + bool usePreviousCell = false; + bool goForward = false; + + // check if we use the current cell to duplicate or the previous cell + if (cell.isEmpty()) { + if (prevCell.isEmpty() || !(prevCell.m_level->getSimpleLevel())) { + return; + } + usePreviousCell = true; + selectedFrameId = prevCell.getFrameId(); + sl = prevCell.getSimpleLevel(); + } + if (!sl || sl->isSubsequence() || sl->isReadOnly()) return; + + // check whether to change the next cell or replace the current cell if it is + // a hold from a previous frame + if (!usePreviousCell) { + // use the current cell + if (prevCell.m_level == cell.m_level && + prevCell.m_frameId == cell.m_frameId) { + // the current frame is a hold from a previous frame + // change this frame and any sequential frames part of the same hold + bool hold = true; + r1 = r0 + 1; + while (hold) { + TXshCell testCell = xsh->getCell(r1, c0); + if (testCell.m_frameId == cell.m_frameId && + testCell.m_level == cell.m_level) { + r1 += 1; + } else { + r1 -= 1; + hold = false; + } + } + + } else { + // This is not part of a hold, use the next cell. + // make sure we are not going to overwrite a non-empty cell + if (nextCell.isEmpty()) { + r0 += 1; + cell = nextCell; + goForward = true; + } else + return; + } + } else { + // cell = prevCell; + } + + std::set frames; + frames.insert(selectedFrameId); + TUndoManager::manager()->beginBlock(); + FilmstripCmd::duplicate(sl, frames, true); + TXshCell newCell; + newCell.m_level = sl; + newCell.m_frameId = selectedFrameId + 1; + DuplicateInXSheetUndo *undo = + new DuplicateInXSheetUndo(r0, r1, c0, cell, newCell, goForward); + TUndoManager::manager()->add(undo); + TUndoManager::manager()->endBlock(); + undo->redo(); +} + +//----------------------------------------------------------------------------- + void TCellSelection::openTimeStretchPopup() { if (!m_timeStretchPopup) m_timeStretchPopup = new TimeStretchPopup(); m_timeStretchPopup->show(); diff --git a/toonz/sources/toonz/cellselection.h b/toonz/sources/toonz/cellselection.h index 6adf8d2..fcc03e0 100644 --- a/toonz/sources/toonz/cellselection.h +++ b/toonz/sources/toonz/cellselection.h @@ -109,6 +109,7 @@ public: void convertVectortoVector(); void reframeWithEmptyInbetweens(); + void duplicateFrame(); void renameCells(TXshCell &cell); // rename cells for each columns with correspondent item in the list diff --git a/toonz/sources/toonz/xshcellviewer.cpp b/toonz/sources/toonz/xshcellviewer.cpp index 107f177..4f678d8 100644 --- a/toonz/sources/toonz/xshcellviewer.cpp +++ b/toonz/sources/toonz/xshcellviewer.cpp @@ -3266,6 +3266,7 @@ void CellArea::createCellMenu(QMenu &menu, bool isCellSelected, TXshCell cell) { menu.addAction(cmdManager->getAction(MI_Clear)); menu.addAction(cmdManager->getAction(MI_Insert)); + menu.addAction(cmdManager->getAction(MI_Duplicate)); menu.addSeparator(); TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();