diff --git a/toonz/sources/toonz/cellselection.cpp b/toonz/sources/toonz/cellselection.cpp index 4ae8039..fa8a42b 100644 --- a/toonz/sources/toonz/cellselection.cpp +++ b/toonz/sources/toonz/cellselection.cpp @@ -1254,6 +1254,45 @@ public: //----------------------------------------------------------------------------- +class DuplicateDrawingUndo final : public ToolUtils::TToolUndo { + TFrameId origFrameId; + TFrameId dupFrameId; + +public: + DuplicateDrawingUndo(TXshSimpleLevel *level, const TFrameId &srcFrameId, + const TFrameId &tgtFrameId) + : TToolUndo(level, tgtFrameId, true) { + origFrameId = srcFrameId; + dupFrameId = tgtFrameId; + } + + ~DuplicateDrawingUndo() {} + + void undo() const override { + removeLevelAndFrameIfNeeded(); + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); + notifyImageChanged(); + } + + void redo() const override { + insertLevelAndFrameIfNeeded(); + + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); + notifyImageChanged(); + } + + int getSize() const override { return sizeof(*this); } + + QString getHistoryString() override { + return QObject::tr("Duplicate Drawing"); + } + + int getHistoryType() override { return HistoryType::Xsheet; } + //----------------------------------------------------------------------------- +}; + +//----------------------------------------------------------------------------- + class FillEmptyCellUndo final : public TUndo { TCellSelection *m_selection; TXshCell m_cell; @@ -1440,7 +1479,7 @@ void TCellSelection::enableCommands() { enableCommand(this, MI_PasteNumbers, &TCellSelection::overwritePasteNumbers); enableCommand(this, MI_CreateBlankDrawing, &TCellSelection::createBlankDrawings); - enableCommand(this, MI_Duplicate, &TCellSelection::duplicateFrame); + enableCommand(this, MI_Duplicate, &TCellSelection::duplicateFrames); } //----------------------------------------------------------------------------- // Used in RenameCellField::eventFilter() @@ -1726,7 +1765,7 @@ void TCellSelection::pasteCells() { } TKeyframeSelection selection; if (isEmpty() && TApp::instance()->getCurrentObject()->getObjectId() == - TStageObjectId::CameraId(xsh->getCameraColumnIndex())) + TStageObjectId::CameraId(xsh->getCameraColumnIndex())) // Se la selezione e' vuota e l'objectId e' quello della camera sono nella // colonna di camera quindi devo selezionare la row corrente e -1. { @@ -1938,7 +1977,7 @@ void TCellSelection::pasteKeyframesInto() { TKeyframeSelection selection; if (isEmpty() && TApp::instance()->getCurrentObject()->getObjectId() == - TStageObjectId::CameraId(xsh->getCameraColumnIndex())) + TStageObjectId::CameraId(xsh->getCameraColumnIndex())) // Se la selezione e' vuota e l'objectId e' quello della camera sono nella // colonna di camera quindi devo selezionare la row corrente e -1. { @@ -2079,99 +2118,139 @@ void TCellSelection::createBlankDrawings() { //----------------------------------------------------------------------------- -void TCellSelection::duplicateFrame() { - if (!Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) { - DVGui::warning( - QObject::tr("Please enable \"Sync Level Strip Drawing Number Changes " - "with the XSheet\" preference option\nto use the duplicate " - "command in the xsheet / timeline.")); - return; - } - +void TCellSelection::duplicateFrame(int row, int col, bool multiple) { 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( - QObject::tr("Please select only one layer to duplicate a frame.")); + + if (col < 0) { + if (!multiple) + DVGui::warning(QObject::tr( + "There are no drawings in the camera column to duplicate")); return; } - if (r1 > r0) { - DVGui::error(QObject::tr("Please select only one frame to duplicate.")); + + TXshColumn *column = xsh->getColumn(col); + if (column && column->isLocked()) { + if (!multiple) DVGui::warning(QObject::tr("The current column is locked")); 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; + TApp::instance()->getCurrentColumn()->setColumnIndex(col); + TApp::instance()->getCurrentFrame()->setCurrentFrame(row + 1); - // check if we use the current cell to duplicate or the previous cell - if (cell.isEmpty()) { - if (prevCell.isEmpty() || !(prevCell.m_level->getSimpleLevel())) { + TXshLevel *level = TApp::instance()->getCurrentLevel()->getLevel(); + if (!level && Preferences::instance()->isAutoCreateEnabled() && + Preferences::instance()->isAnimationSheetEnabled()) { + int r0, r1; + xsh->getCellRange(col, r0, r1); + for (int r = std::min(r1, row); r > r0; r--) { + TXshCell cell = xsh->getCell(r, col); + if (cell.isEmpty()) continue; + level = cell.m_level.getPointer(); + if (!level) continue; + break; + } + } + if (level) { + int levelType = level->getType(); + if (levelType == ZERARYFX_XSHLEVEL || levelType == PLT_XSHLEVEL || + levelType == SND_XSHLEVEL || levelType == SND_TXT_XSHLEVEL || + levelType == MESH_XSHLEVEL) { + if (!multiple) + DVGui::warning( + QObject::tr("Cannot duplicate a drawing in the current column")); + return; + } else if (level->getSimpleLevel() && + level->getSimpleLevel()->isReadOnly()) { + if (!multiple) + DVGui::warning(QObject::tr("The current level is not editable")); return; } - usePreviousCell = true; - selectedFrameId = prevCell.getFrameId(); - sl = prevCell.getSimpleLevel(); } + + TXshCell targetCell = xsh->getCell(row, col); + TXshCell prevCell = xsh->getCell(row - 1, col); + ; + + // check if we use the current cell to duplicate or the previous cell + if (!targetCell.isEmpty() && targetCell != prevCell) { + // Current cell has a drawing to duplicate and it's not a hold shift focus + // to next cell as if they selected that one to duplicate into + prevCell = targetCell; + TApp::instance()->getCurrentFrame()->setCurrentFrame(row + 2); + row++; + } + + if (prevCell.isEmpty() || !(prevCell.m_level->getSimpleLevel())) return; + + TXshSimpleLevel *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; - } - } + ToolHandle *toolHandle = TApp::instance()->getCurrentTool(); - } 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; + // If autocreate disabled, let's turn it on temporarily + bool isAutoCreateEnabled = Preferences::instance()->isAutoCreateEnabled(); + if (!isAutoCreateEnabled) + Preferences::instance()->setValue(AutocreationType, 1, false); + + TImage *img = toolHandle->getTool()->touchImage(); + if (!img) { + if (!isAutoCreateEnabled) + Preferences::instance()->setValue(AutocreationType, 0, false); + if (!multiple) + DVGui::warning( + QObject::tr("Unable to duplicate a drawing on the current column")); + return; } + bool frameCreated = toolHandle->getTool()->m_isFrameCreated; + if (!frameCreated) { + if (!multiple) + DVGui::warning(QObject::tr( + "Unable to replace the current or next drawing with a duplicate drawing")); + return; + } + + targetCell = xsh->getCell(row, col); + TPalette *palette = sl->getPalette(); + TFrameId srcFrame = prevCell.getFrameId(); + TFrameId targetFrame = targetCell.getFrameId(); 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); + + FilmstripCmd::duplicateFrameWithoutUndo(sl, srcFrame, targetFrame); + + TApp::instance()->getCurrentLevel()->notifyLevelChange(); + + DuplicateDrawingUndo *undo = + new DuplicateDrawingUndo(sl, srcFrame, targetFrame); TUndoManager::manager()->add(undo); + + if (!isAutoCreateEnabled) + Preferences::instance()->setValue(AutocreationType, 0, false); +} + +//----------------------------------------------------------------------------- + +void TCellSelection::duplicateFrames() { + int col = TApp::instance()->getCurrentColumn()->getColumnIndex(); + int row = TApp::instance()->getCurrentFrame()->getFrameIndex(); + + int r0, c0, r1, c1; + getSelectedCells(r0, c0, r1, c1); + + bool multiple = (r1 - r0 > 1) || (c1 - c0 > 1); + + TUndoManager::manager()->beginBlock(); + for (int c = c0; c <= c1; c++) { + for (int r = r0; r <= r1; r++) { + duplicateFrame(r, c, multiple); + } + } TUndoManager::manager()->endBlock(); - undo->redo(); + + if (multiple) { + TApp::instance()->getCurrentColumn()->setColumnIndex(col); + TApp::instance()->getCurrentFrame()->setCurrentFrame(row + 1); + } } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/cellselection.h b/toonz/sources/toonz/cellselection.h index f0597a5..cf002db 100644 --- a/toonz/sources/toonz/cellselection.h +++ b/toonz/sources/toonz/cellselection.h @@ -113,7 +113,8 @@ public: void convertVectortoVector(); void reframeWithEmptyInbetweens(); - void duplicateFrame(); + void duplicateFrame(int row, int col, bool multiple); + void duplicateFrames(); void renameCells(TXshCell &cell); // rename cells for each columns with correspondent item in the list diff --git a/toonz/sources/toonz/filmstripcommand.cpp b/toonz/sources/toonz/filmstripcommand.cpp index 1cf6965..c68c608 100644 --- a/toonz/sources/toonz/filmstripcommand.cpp +++ b/toonz/sources/toonz/filmstripcommand.cpp @@ -2226,6 +2226,27 @@ public: // 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; diff --git a/toonz/sources/toonz/filmstripcommand.h b/toonz/sources/toonz/filmstripcommand.h index b15355c..6d6e305 100644 --- a/toonz/sources/toonz/filmstripcommand.h +++ b/toonz/sources/toonz/filmstripcommand.h @@ -40,6 +40,8 @@ void swing(TXshSimpleLevel *sl, std::set &frames); void step(TXshSimpleLevel *sl, std::set &frames, int step); void each(TXshSimpleLevel *sl, std::set &frames, int each); +void duplicateFrameWithoutUndo(TXshSimpleLevel *sl, TFrameId srcFrame, + TFrameId targetFrame); void duplicate(TXshSimpleLevel *sl, std::set &frames, bool withUndo); // TODO vanno spostati in un altro posto