diff --git a/toonz/sources/include/toonz/preferences.h b/toonz/sources/include/toonz/preferences.h index fb1d7a7..dfa3aec 100644 --- a/toonz/sources/include/toonz/preferences.h +++ b/toonz/sources/include/toonz/preferences.h @@ -365,6 +365,10 @@ public: return getBoolValue(xsheetAutopanEnabled); } //!< Returns whether xsheet pans during playback. int getDragCellsBehaviour() const { return getIntValue(DragCellsBehaviour); } + int getDeleteCommandBehaviour() const { + return getIntValue(deleteCommandBehavior); + } + bool isIgnoreAlphaonColumn1Enabled() const { return getBoolValue(ignoreAlphaonColumn1Enabled); } diff --git a/toonz/sources/include/toonz/preferencesitemids.h b/toonz/sources/include/toonz/preferencesitemids.h index 17407c3..8a2cfdf 100644 --- a/toonz/sources/include/toonz/preferencesitemids.h +++ b/toonz/sources/include/toonz/preferencesitemids.h @@ -121,6 +121,7 @@ enum PreferencesItemId { xsheetStep, xsheetAutopanEnabled, DragCellsBehaviour, + deleteCommandBehavior, ignoreAlphaonColumn1Enabled, showKeyframesOnXsheetCellArea, showXsheetCameraColumn, diff --git a/toonz/sources/toonz/cellkeyframeselection.cpp b/toonz/sources/toonz/cellkeyframeselection.cpp index 23844fa..5c021ec 100644 --- a/toonz/sources/toonz/cellkeyframeselection.cpp +++ b/toonz/sources/toonz/cellkeyframeselection.cpp @@ -91,7 +91,10 @@ void TCellKeyframeSelection::pasteCellsKeyframes() { void TCellKeyframeSelection::deleteCellsKeyframes() { TUndoManager::manager()->beginBlock(); - m_cellSelection->deleteCells(); + // clear cells without shifting + // TODO: behavior of deleting cell+keyframe should also follow the preference + // option. + m_cellSelection->deleteCells(false); m_keyframeSelection->deleteKeyframes(); TUndoManager::manager()->endBlock(); } diff --git a/toonz/sources/toonz/cellselection.cpp b/toonz/sources/toonz/cellselection.cpp index ae7d469..3b55ed1 100644 --- a/toonz/sources/toonz/cellselection.cpp +++ b/toonz/sources/toonz/cellselection.cpp @@ -110,7 +110,8 @@ bool pasteCellsWithoutUndo(const TCellData *cellData, int &r0, int &c0, int &r1, if (!cellData) return false; if (r0 < 0 || c0 < 0) return false; - bool ret = cellData->getCells(xsh, r0, c0, r1, c1, insert, doZeraryClone); + bool ret = + cellData->getCells(xsh, r0, c0, r1, c1, insert, doZeraryClone, false); if (!ret) return false; return true; @@ -118,13 +119,17 @@ bool pasteCellsWithoutUndo(const TCellData *cellData, int &r0, int &c0, int &r1, //----------------------------------------------------------------------------- -void deleteCellsWithoutUndo(int &r0, int &c0, int &r1, int &c1) { +void deleteCellsWithoutUndo(int &r0, int &c0, int &r1, int &c1, bool doShift) { try { TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); int c; for (c = c0; c <= c1; c++) { if (xsh->isColumnEmpty(c)) continue; - xsh->clearCells(r0, c, r1 - r0 + 1); + + if (doShift) + xsh->removeCells(r0, c, r1 - r0 + 1); + else + xsh->clearCells(r0, c, r1 - r0 + 1); // when the column becomes empty after deletion, // ColumnCmd::DeleteColumn() will take care of column related operations // like disconnecting from fx nodes etc. @@ -281,8 +286,11 @@ class DeleteCellsUndo final : public TUndo { TCellSelection *m_selection; QMimeData *m_data; + bool m_doShift; // whether clear cell or remove and shift cells up + public: - DeleteCellsUndo(TCellSelection *selection, QMimeData *data) : m_data(data) { + DeleteCellsUndo(TCellSelection *selection, QMimeData *data, bool doShift) + : m_data(data), m_doShift(doShift) { int r0, c0, r1, c1; selection->getSelectedCells(r0, c0, r1, c1); if (c0 < 0) c0 = 0; // Ignore camera column @@ -299,14 +307,15 @@ public: m_selection->getSelectedCells(r0, c0, r1, c1); const TCellData *cellData = dynamic_cast(m_data); - pasteCellsWithoutUndo(cellData, r0, c0, r1, c1, false, false); + // insert cells if the delete operation had shifted cells up + pasteCellsWithoutUndo(cellData, r0, c0, r1, c1, m_doShift, false); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } void redo() const override { int r0, c0, r1, c1; m_selection->getSelectedCells(r0, c0, r1, c1); - deleteCellsWithoutUndo(r0, c0, r1, c1); + deleteCellsWithoutUndo(r0, c0, r1, c1, m_doShift); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } @@ -2383,13 +2392,26 @@ void TCellSelection::pasteDuplicateCells() { //----------------------------------------------------------------------------- void TCellSelection::deleteCells() { + // choose clear or remove+shift behavior + // 0: Clear Cell / Frame + // 1: Remove and Shift Cells / Frames Up + bool withShift = Preferences::instance()->getDeleteCommandBehaviour() == 1; + deleteCells(withShift); +} + +//----------------------------------------------------------------------------- + +void TCellSelection::deleteCells(bool withShift) { if (isEmpty()) return; int r0, c0, r1, c1; getSelectedCells(r0, c0, r1, c1); if (c0 < 0) c0 = 0; // Ignore camera column TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); - // if all the selected cells are already empty, then do nothing - if (xsh->isRectEmpty(CellPosition(r0, c0), CellPosition(r1, c1))) return; + // if with "Clear Cell" behavior and all the selected cells are already empty, + // then do nothing + if (!withShift && + xsh->isRectEmpty(CellPosition(r0, c0), CellPosition(r1, c1))) + return; std::set removedColIds; // check if the operation may remove expression reference as column becomes @@ -2410,9 +2432,9 @@ void TCellSelection::deleteCells() { } DeleteCellsUndo *undo = - new DeleteCellsUndo(new TCellSelection(m_range), data); + new DeleteCellsUndo(new TCellSelection(m_range), data, withShift); - deleteCellsWithoutUndo(r0, c0, r1, c1); + deleteCellsWithoutUndo(r0, c0, r1, c1, withShift); TUndoManager::manager()->add(undo); @@ -2480,6 +2502,7 @@ void TCellSelection::cutCells(bool withoutCopy) { selectNone(); TApp::instance()->getCurrentScene()->setDirtyFlag(true); + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/cellselection.h b/toonz/sources/toonz/cellselection.h index 99842ab..13d8bc3 100644 --- a/toonz/sources/toonz/cellselection.h +++ b/toonz/sources/toonz/cellselection.h @@ -47,6 +47,7 @@ public: void pasteCells(); void pasteDuplicateCells(); void deleteCells(); + void deleteCells(bool withShift); void cutCells(); void cutCells(bool withoutCopy); diff --git a/toonz/sources/toonz/filmstripcommand.cpp b/toonz/sources/toonz/filmstripcommand.cpp index 3c00e94..f310685 100644 --- a/toonz/sources/toonz/filmstripcommand.cpp +++ b/toonz/sources/toonz/filmstripcommand.cpp @@ -1173,6 +1173,52 @@ public: int getHistoryType() override { return HistoryType::FilmStrip; } }; +//----------------------------------------------------------------------------- + +class RemoveFramesUndo final : public TUndo { + TXshSimpleLevel *m_sl; + std::set m_framesRemoved; + DrawingData *m_oldData; + +public: + RemoveFramesUndo(TXshSimpleLevel *sl, std::set &framesRemoved, + DrawingData *oldData) + : m_sl(sl), m_framesRemoved(framesRemoved), m_oldData(oldData) {} + + ~RemoveFramesUndo() { + if (m_oldData) m_oldData->releaseData(); + } + + void undo() const override { + std::set frames = m_framesRemoved; + 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_framesRemoved; + removeFramesWithoutUndo(m_sl, frames); + } + + int getSize() const override { return sizeof(*this); } + + QString getHistoryString() override { + QString str = QObject::tr("Remove Frames : Level %1 : Frame ") + .arg(QString::fromStdWString(m_sl->getName())); + + std::set::const_iterator it; + for (it = m_framesRemoved.begin(); it != m_framesRemoved.end(); it++) { + if (it != m_framesRemoved.begin()) str += QString(", "); + str += QString::number((*it).getNumber()); + } + + return str; + } + int getHistoryType() override { return HistoryType::FilmStrip; } +}; + } // namespace //============================================================================= @@ -1714,8 +1760,8 @@ void FilmstripCmd::pasteInto(TXshSimpleLevel *sl, std::set &frames) { bool keepOriginalPalette = true; bool isPaste = pasteFramesWithoutUndo(drawingData, sl, frames, - DrawingData::OVER_SELECTION, true, - keepOriginalPalette); + DrawingData::OVER_SELECTION, true, + keepOriginalPalette); if (!isPaste) return; TUndoManager::manager()->add(new PasteFramesUndo( @@ -1770,6 +1816,31 @@ void FilmstripCmd::clear(TXshSimpleLevel *sl, std::set &frames) { new DeleteFramesUndo(sl, oldFrames, oldData, newData)); } +//============================================================================= +// clear +//----------------------------------------------------------------------------- + +void FilmstripCmd::remove(TXshSimpleLevel *sl, std::set &frames) { + if (!sl || frames.empty() || sl->isSubsequence() || sl->isReadOnly()) return; + + std::map imageSet; + std::set::const_iterator it; + int i = 0; + for (it = frames.begin(); it != frames.end(); ++it, i++) { + TFrameId frameId = *it; + QString id = "removeFrames" + QString::number((uintptr_t)sl) + "-" + + QString::number(it->getNumber()); + TImageCache::instance()->add(id, sl->getFrame(frameId, false)); + imageSet[frameId] = id; + } + HookSet *levelHooks = sl->getHookSet(); + DrawingData *oldData = new DrawingData(); + oldData->setFrames(imageSet, sl, *levelHooks); + + removeFramesWithoutUndo(sl, frames); + TUndoManager::manager()->add(new RemoveFramesUndo(sl, frames, oldData)); +} + //----------------------------------------------------------------------------- namespace { //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/filmstripcommand.h b/toonz/sources/toonz/filmstripcommand.h index 25ecce0..a30b612 100644 --- a/toonz/sources/toonz/filmstripcommand.h +++ b/toonz/sources/toonz/filmstripcommand.h @@ -32,6 +32,7 @@ void merge(TXshSimpleLevel *sl, std::set &frames); void pasteInto(TXshSimpleLevel *sl, std::set &frames); void cut(TXshSimpleLevel *sl, std::set &frames); void clear(TXshSimpleLevel *sl, std::set &frames); +void remove(TXshSimpleLevel *sl, std::set &frames); void insert(TXshSimpleLevel *sl, const std::set &frames, bool withUndo); diff --git a/toonz/sources/toonz/filmstripselection.cpp b/toonz/sources/toonz/filmstripselection.cpp index 0cf7978..02c2379 100644 --- a/toonz/sources/toonz/filmstripselection.cpp +++ b/toonz/sources/toonz/filmstripselection.cpp @@ -237,7 +237,7 @@ void TFilmstripSelection::cutFrames() { TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel(); if (sl) { int firstSelectedIndex = sl->fid2index(*m_selectedFrames.begin()); - if(firstSelectedIndex < 0 || firstSelectedIndex > sl->getFrameCount()) { + if (firstSelectedIndex < 0 || firstSelectedIndex > sl->getFrameCount()) { selectNone(); return; } @@ -285,7 +285,12 @@ void TFilmstripSelection::pasteInto() { void TFilmstripSelection::deleteFrames() { TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel(); - if (sl) FilmstripCmd::clear(sl, m_selectedFrames); + if (sl) { + if (Preferences::instance()->getDeleteCommandBehaviour() == 0) // Clear + FilmstripCmd::clear(sl, m_selectedFrames); + else // Remove and Shift + FilmstripCmd::remove(sl, m_selectedFrames); + } updateInbetweenRange(); } diff --git a/toonz/sources/toonz/preferencespopup.cpp b/toonz/sources/toonz/preferencespopup.cpp index 0aebcbd..0c32fa3 100644 --- a/toonz/sources/toonz/preferencespopup.cpp +++ b/toonz/sources/toonz/preferencespopup.cpp @@ -1259,6 +1259,7 @@ QString PreferencesPopup::getUIString(PreferencesItemId id) { {xsheetStep, tr("Next/Previous Step Frames:")}, {xsheetAutopanEnabled, tr("Xsheet Autopan during Playback")}, {DragCellsBehaviour, tr("Cell-dragging Behaviour:")}, + {deleteCommandBehavior, tr("Delete Command Behaviour:")}, {ignoreAlphaonColumn1Enabled, tr("Ignore Alpha Channel on Levels in Column 1")}, {showKeyframesOnXsheetCellArea, tr("Show Keyframes on Cell Area")}, @@ -1427,6 +1428,9 @@ QList PreferencesPopup::getComboItemList( Preferences::ShowLevelNameOnColumnHeader}}}, {DragCellsBehaviour, {{tr("Cells Only"), 0}, {tr("Cells and Column Data"), 1}}}, + {deleteCommandBehavior, + {{tr("Clear Cell / Frame"), 0}, + {tr("Remove and Shift Cells / Frames Up"), 1}}}, {keyframeType, // note that the value starts from 1, not 0 {{tr("Constant"), 1}, {tr("Linear"), 2}, @@ -1987,6 +1991,7 @@ QWidget* PreferencesPopup::createXsheetPage() { insertUI(xsheetStep, lay); insertUI(xsheetAutopanEnabled, lay); insertUI(DragCellsBehaviour, lay, getComboItemList(DragCellsBehaviour)); + insertUI(deleteCommandBehavior, lay, getComboItemList(deleteCommandBehavior)); insertUI(ignoreAlphaonColumn1Enabled, lay); QGridLayout* showKeyLay = insertGroupBoxUI(showKeyframesOnXsheetCellArea, lay); diff --git a/toonz/sources/toonz/xshcellviewer.cpp b/toonz/sources/toonz/xshcellviewer.cpp index 25fe8ff..cc006f9 100644 --- a/toonz/sources/toonz/xshcellviewer.cpp +++ b/toonz/sources/toonz/xshcellviewer.cpp @@ -929,7 +929,8 @@ void RenameCellField::renameCell() { if (fid.getNumber() == 0 && !hasFrameZero) { TCellSelection::Range range = cellSelection->getSelectedCells(); - cellSelection->deleteCells(); + // clear cells without shifting + cellSelection->deleteCells(false); // revert cell selection cellSelection->selectCells(range.m_r0, range.m_c0, range.m_r1, range.m_c1); } else if (cells.size() == 1) diff --git a/toonz/sources/toonzlib/preferences.cpp b/toonz/sources/toonzlib/preferences.cpp index 7c63cc2..841536f 100644 --- a/toonz/sources/toonzlib/preferences.cpp +++ b/toonz/sources/toonzlib/preferences.cpp @@ -567,6 +567,8 @@ void Preferences::definePreferenceItems() { define(xsheetAutopanEnabled, "xsheetAutopanEnabled", QMetaType::Bool, true); define(DragCellsBehaviour, "DragCellsBehaviour", QMetaType::Int, 1); // Cells and Column Data + define(deleteCommandBehavior, "deleteCommandBehavior", QMetaType::Int, + 0); // Clear Cell / Frame define(ignoreAlphaonColumn1Enabled, "ignoreAlphaonColumn1Enabled", QMetaType::Bool, false); define(showKeyframesOnXsheetCellArea, "showKeyframesOnXsheetCellArea",