diff --git a/stuff/profiles/layouts/rooms/Default/menubar_template.xml b/stuff/profiles/layouts/rooms/Default/menubar_template.xml
index 7b9c58b..334bc16 100644
--- a/stuff/profiles/layouts/rooms/Default/menubar_template.xml
+++ b/stuff/profiles/layouts/rooms/Default/menubar_template.xml
@@ -156,6 +156,8 @@
MI_Rollup
MI_Rolldown
MI_TimeStretch
+ MI_DrawingSubForward
+ MI_DrawingSubBackward
MI_Autorenumber
MI_Duplicate
diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp
index 8147c32..9ecf9dd 100644
--- a/toonz/sources/toonz/mainwindow.cpp
+++ b/toonz/sources/toonz/mainwindow.cpp
@@ -1727,6 +1727,11 @@ void MainWindow::defineActions() {
createMenuCellsAction(MI_Duplicate, tr("&Duplicate Drawing "), "D");
createMenuCellsAction(MI_Autorenumber, tr("&Autorenumber"), "");
createMenuCellsAction(MI_CloneLevel, tr("&Clone"), "");
+ createMenuCellsAction(MI_DrawingSubForward, tr("Drawing Substitution Forward"), ".");
+ createMenuCellsAction(MI_DrawingSubBackward, tr("Drawing Substitution Backward"), ",");
+ createMenuCellsAction(MI_DrawingSubGroupForward, tr("Similar Drawing Substitution Forward"), "Ctrl+.");
+ createMenuCellsAction(MI_DrawingSubGroupBackward, tr("Similar Drawing Substitution Backward"), "Ctrl+,");
+
createMenuCellsAction(MI_Reframe1, tr("1's"), "");
createMenuCellsAction(MI_Reframe2, tr("2's"), "");
diff --git a/toonz/sources/toonz/menubar.cpp b/toonz/sources/toonz/menubar.cpp
index 13fafae..a73afef 100644
--- a/toonz/sources/toonz/menubar.cpp
+++ b/toonz/sources/toonz/menubar.cpp
@@ -1252,6 +1252,8 @@ QMenuBar *StackedMenuBar::createFullMenuBar() {
addMenuItem(cellsMenu, MI_Rollup);
addMenuItem(cellsMenu, MI_Rolldown);
addMenuItem(cellsMenu, MI_TimeStretch);
+ addMenuItem(xsheetMenu, MI_DrawingSubForward);
+ addMenuItem(xsheetMenu, MI_DrawingSubBackward);
cellsMenu->addSeparator();
addMenuItem(cellsMenu, MI_Autorenumber);
addMenuItem(cellsMenu, MI_Duplicate);
diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h
index 10b3c26..8daabe2 100644
--- a/toonz/sources/toonz/menubarcommandids.h
+++ b/toonz/sources/toonz/menubarcommandids.h
@@ -127,6 +127,10 @@
#define MI_InsertGlobalKeyframe "MI_InsertGlobalKeyframe"
#define MI_RemoveGlobalKeyframe "MI_RemoveGlobalKeyframe"
+#define MI_DrawingSubForward "MI_DrawingSubForward"
+#define MI_DrawingSubBackward "MI_DrawingSubBackward"
+#define MI_DrawingSubGroupForward "MI_DrawingSubGroupForward"
+#define MI_DrawingSubGroupBackward "MI_DrawingSubGroupBackward"
#define MI_InsertFx "MI_InsertFx"
#define MI_NewOutputFx "MI_NewOutputFx"
diff --git a/toonz/sources/toonz/xsheetcmd.cpp b/toonz/sources/toonz/xsheetcmd.cpp
index 3f385e3..9549d03 100644
--- a/toonz/sources/toonz/xsheetcmd.cpp
+++ b/toonz/sources/toonz/xsheetcmd.cpp
@@ -557,7 +557,259 @@ public:
}
} removeGlobalKeyframeCommand;
-} // namespace XshCmd
+//============================================================
+// Drawing Substitution
+//============================================================
+class DrawingSubtitutionUndo : public TUndo {
+
+private:
+ int m_direction, m_row, m_col;
+ TCellSelection::Range m_range;
+ bool m_selected;
+
+public:
+ DrawingSubtitutionUndo(int dir, TCellSelection::Range range, int row, int col, bool selected)
+ : m_direction(dir), m_range(range), m_row(row), m_col(col), m_selected(selected){}
+
+ void undo() const {
+ if (!m_selected) {
+ changeDrawing(-m_direction, m_row, m_col);
+ return;
+ }
+ int col, row;
+ int c = m_range.m_c0;
+ int r = m_range.m_r0;
+ while (c <= m_range.m_c1) {
+ col = c;
+ while (r <= m_range.m_r1) {
+ row = r;
+ changeDrawing(-m_direction, row, col);
+ r++;
+ }
+ r = m_range.m_r0;
+ c++;
+ }
+
+ }
+
+ void redo() const {
+ if (!m_selected) {
+ changeDrawing(m_direction, m_row, m_col);
+ return;
+ }
+
+ int col, row;
+ int c = m_range.m_c0;
+ int r = m_range.m_r0;
+ while (c <= m_range.m_c1) {
+ col = c;
+ while (r <= m_range.m_r1) {
+ row = r;
+ changeDrawing(m_direction, row, col);
+ r++;
+ }
+ r = m_range.m_r0;
+ c++;
+ }
+ }
+
+ int getSize() const {
+ return sizeof(*this);
+ }
+
+ QString getHistoryString() {
+ return QObject::tr("Change current drawing %1").arg(QString::number(m_direction));
+ }
+
+ int getHistoryType() {
+ return HistoryType::Xsheet;
+ }
+protected:
+
+ static bool changeDrawing(int delta, int row, int col);
+ static void setDrawing(const TFrameId &fid, int row, int col, TXshCell cell);
+ friend class DrawingSubtitutionGroupUndo;
+};
+
+//============================================================
+
+class DrawingSubtitutionGroupUndo : public TUndo {
+
+private:
+ int m_direction;
+ int m_row;
+ int m_col;
+ int m_count;
+
+public:
+ DrawingSubtitutionGroupUndo(int dir, int row, int col) : m_direction(dir), m_col(col), m_row(row)
+ {
+ m_count = 1;
+ TXshCell cell = TTool::getApplication()->getCurrentScene()->getScene()->getXsheet()->getCell(m_row, m_col);
+ if (!cell.m_level || !cell.m_level->getSimpleLevel()) return;
+
+ TFrameId id = cell.m_frameId;
+
+ TXshCell nextCell = TTool::getApplication()->getCurrentScene()->getScene()->getXsheet()->getCell(m_row + m_count, m_col);
+ if (!nextCell.m_level || !nextCell.m_level->getSimpleLevel()) return;
+
+ TFrameId nextId = nextCell.m_frameId;
+
+ while (id == nextId) {
+ m_count++;
+ nextCell = TTool::getApplication()->getCurrentScene()->getScene()->getXsheet()->getCell(m_row + m_count, m_col);
+ nextId = nextCell.m_frameId;
+ }
+ }
+
+ void undo() const {
+ int n = 1;
+ DrawingSubtitutionUndo::changeDrawing(-m_direction, m_row, m_col);
+ while (n < m_count) {
+ DrawingSubtitutionUndo::changeDrawing(-m_direction, m_row + n, m_col);
+ n++;
+ }
+ }
+
+ void redo() const {
+ int n = 1;
+ DrawingSubtitutionUndo::changeDrawing(m_direction, m_row, m_col);
+ while (n < m_count) {
+ DrawingSubtitutionUndo::changeDrawing(m_direction, m_row + n, m_col);
+ n++;
+ }
+ }
+
+ int getSize() const {
+ return sizeof(*this);
+ }
+
+ QString getHistoryString() {
+ return QObject::tr("Change current drawing %1").arg(QString::number(m_direction));
+ }
+
+ int getHistoryType() {
+ return HistoryType::Xsheet;
+ }
+
+};
+
+//============================================================
+
+bool DrawingSubtitutionUndo::changeDrawing(int delta, int row, int col) {
+ TTool::Application *app = TTool::getApplication();
+ TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
+ TXshCell cell = xsh->getCell(row, col);
+
+ if (!cell.m_level || !cell.m_level->getSimpleLevel()) return false;
+
+ std::vector fids;
+ cell.m_level->getSimpleLevel()->getFids(fids);
+ int n = fids.size();
+
+ if (n < 2) return false;
+
+ std::vector::iterator it;
+ it = std::find(fids.begin(), fids.end(), cell.m_frameId);
+
+ if (it == fids.end()) return false;
+
+ int index = std::distance(fids.begin(), it);
+ while (delta < 0) delta += n;
+ index = (index + delta) % n;
+
+ setDrawing(fids[index], row, col, cell);
+
+}
+
+void DrawingSubtitutionUndo::setDrawing(const TFrameId &fid, int row, int col, TXshCell cell) {
+ TTool::Application *app = TTool::getApplication();
+ TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
+ cell.m_frameId = fid;
+ xsh->setCell(row, col, cell);
+ TStageObject *pegbar = xsh->getStageObject(TStageObjectId::ColumnId(col));
+ pegbar->setOffset(pegbar->getOffset());
+
+ app->getCurrentXsheet()->notifyXsheetChanged();
+ TApp::instance()->getCurrentScene()->setDirtyFlag(true);
+}
+
+//-----------------------------------------------------------------------------
+
+void drawingSubstituion(int dir) {
+ //TTool::Application *app = TTool::getApplication();
+ //TCellSelection *selection = dynamic_cast(app->getCurrentSelection()->getSelection());
+ TCellSelection *selection = dynamic_cast(TTool::getApplication()->getCurrentSelection()->getSelection());
+ TCellSelection::Range range;
+ bool selected = false;
+ if (selection) {
+ range = selection->getSelectedCells();
+ if (!(range.isEmpty()))
+ selected = true;
+ }
+ int row = TTool::getApplication()->getCurrentFrame()->getFrame();
+ int col = TTool::getApplication()->getCurrentColumn()->getColumnIndex();
+
+ DrawingSubtitutionUndo *undo = new DrawingSubtitutionUndo(dir, range, row, col, selected);
+ TUndoManager::manager()->add(undo);
+
+ undo->redo();
+}
+
+void drawingSubstituionGroup(int dir) {
+ int row = TTool::getApplication()->getCurrentFrame()->getFrame();
+ int col = TTool::getApplication()->getCurrentColumn()->getColumnIndex();
+ DrawingSubtitutionGroupUndo *undo = new DrawingSubtitutionGroupUndo(dir, row, col);
+ TUndoManager::manager()->add(undo);
+ undo->redo();
+}
+
+//=============================================================================
+
+class DrawingSubstitutionForwardCommand : public MenuItemHandler {
+public:
+ DrawingSubstitutionForwardCommand() : MenuItemHandler(MI_DrawingSubForward) {}
+ void execute() {
+ XshCmd::drawingSubstituion(1);
+ }
+} DrawingSubstitutionForwardCommand;
+
+
+//============================================================
+
+class DrawingSubstitutionBackwardCommand : public MenuItemHandler {
+public:
+ DrawingSubstitutionBackwardCommand() : MenuItemHandler(MI_DrawingSubBackward) {}
+ void execute() {
+ XshCmd::drawingSubstituion(-1);
+ }
+} DrawingSubstitutionBackwardCommand;
+
+//=============================================================================
+
+class DrawingSubstitutionGroupForwardCommand : public MenuItemHandler {
+public:
+ DrawingSubstitutionGroupForwardCommand() : MenuItemHandler(MI_DrawingSubGroupForward) {}
+ void execute() {
+ XshCmd::drawingSubstituionGroup(1);
+ }
+} DrawingSubstitutionGroupForwardCommand;
+
+
+//============================================================
+
+class DrawingSubstitutionGroupBackwardCommand : public MenuItemHandler {
+public:
+ DrawingSubstitutionGroupBackwardCommand() : MenuItemHandler(MI_DrawingSubGroupBackward) {}
+ void execute() {
+ XshCmd::drawingSubstituionGroup(-1);
+ }
+} DrawingSubstitutionGroupBackwardCommand;
+
+
+//============================================================
+
+} // namespace XshCmd
//*****************************************************************************
// Selection commands
diff --git a/toonz/sources/toonz/xshrowviewer.cpp b/toonz/sources/toonz/xshrowviewer.cpp
index 7e86c38..eec21fa 100644
--- a/toonz/sources/toonz/xshrowviewer.cpp
+++ b/toonz/sources/toonz/xshrowviewer.cpp
@@ -707,7 +707,8 @@ void RowArea::contextMenuEvent(QContextMenuEvent *event) {
menu->addAction(cmdManager->getAction(MI_RemoveSceneFrame));
menu->addAction(cmdManager->getAction(MI_InsertGlobalKeyframe));
menu->addAction(cmdManager->getAction(MI_RemoveGlobalKeyframe));
-
+ menu->addAction(cmdManager->getAction(MI_DrawingSubForward));
+ menu->addAction(cmdManager->getAction(MI_DrawingSubBackward));
menu->addSeparator();
menu->addAction(cmdManager->getAction(MI_ShiftTrace));
menu->addAction(cmdManager->getAction(MI_EditShift));