diff --git "a/stuff/config/loc/\346\227\245\346\234\254\350\252\236/toonz.qm" "b/stuff/config/loc/\346\227\245\346\234\254\350\252\236/toonz.qm" index 114ccaa..9fd4950 100644 Binary files "a/stuff/config/loc/\346\227\245\346\234\254\350\252\236/toonz.qm" and "b/stuff/config/loc/\346\227\245\346\234\254\350\252\236/toonz.qm" differ diff --git a/toonz/sources/include/toonz/txsheet.h b/toonz/sources/include/toonz/txsheet.h index 1427371..b7ee655 100644 --- a/toonz/sources/include/toonz/txsheet.h +++ b/toonz/sources/include/toonz/txsheet.h @@ -418,7 +418,9 @@ frame duplication. void timeStretch(int r0, int c0, int r1, int c1, int nr); // force cells order in n-steps. returns the row amount after process - int reframeCells(int r0, int r1, int col, int type); + // if withBlank is greater than -1, remove empty cell from its order and + // insert blank frames with type*withBlank length at each n-step. + int reframeCells(int r0, int r1, int col, int type, int withBlank = -1); /*! Exposes level \b \e xl in xsheet starting from cell identified by \b \e row and \b \e col. diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt index 315bf71..5de78c0 100644 --- a/toonz/sources/toonz/CMakeLists.txt +++ b/toonz/sources/toonz/CMakeLists.txt @@ -151,6 +151,7 @@ set(MOC_HEADERS locatorpopup.h styleshortcutswitchablepanel.h cameracapturelevelcontrol.h + reframepopup.h autoinputcellnumberpopup.h # Tracker file dummyprocessor.h @@ -318,6 +319,7 @@ set(SOURCES locatorpopup.cpp styleshortcutswitchablepanel.cpp cameracapturelevelcontrol.cpp + reframepopup.cpp autoinputcellnumberpopup.cpp # Tracker file dummyprocessor.cpp diff --git a/toonz/sources/toonz/cellselection.cpp b/toonz/sources/toonz/cellselection.cpp index 7d0b645..74ca928 100644 --- a/toonz/sources/toonz/cellselection.cpp +++ b/toonz/sources/toonz/cellselection.cpp @@ -1154,7 +1154,7 @@ int TCellSelection::Range::getColCount() const { return m_c1 - m_c0 + 1; } // TCellSelection //----------------------------------------------------------------------------- -TCellSelection::TCellSelection() : m_timeStretchPopup(0) {} +TCellSelection::TCellSelection() : m_timeStretchPopup(0), m_reframePopup(0) {} //----------------------------------------------------------------------------- @@ -1204,21 +1204,47 @@ void TCellSelection::enableCommands() { enableCommand(this, MI_Reframe2, &TCellSelection::reframe2Cells); enableCommand(this, MI_Reframe3, &TCellSelection::reframe3Cells); enableCommand(this, MI_Reframe4, &TCellSelection::reframe4Cells); + enableCommand(this, MI_ReframeWithEmptyInbetweens, + &TCellSelection::reframeWithEmptyInbetweens); } //----------------------------------------------------------------------------- // Used in RenameCellField::eventFilter() bool TCellSelection::isEnabledCommand( std::string commandId) { // static function - static QList commands = { - MI_Autorenumber, MI_Reverse, MI_Swing, MI_Random, - MI_Increment, MI_ResetStep, MI_IncreaseStep, MI_DecreaseStep, - MI_Step2, MI_Step3, MI_Step4, MI_Each2, - MI_Each3, MI_Each4, MI_Rollup, MI_Rolldown, - MI_TimeStretch, MI_CloneLevel, MI_SetKeyframes, MI_Copy, - MI_Paste, MI_PasteInto, MI_Cut, MI_Clear, - MI_Insert, MI_PasteInto, MI_Reframe1, MI_Reframe2, - MI_Reframe3, MI_Reframe4, MI_Undo, MI_Redo}; + static QList commands = {MI_Autorenumber, + MI_Reverse, + MI_Swing, + MI_Random, + MI_Increment, + MI_ResetStep, + MI_IncreaseStep, + MI_DecreaseStep, + MI_Step2, + MI_Step3, + MI_Step4, + MI_Each2, + MI_Each3, + MI_Each4, + MI_Rollup, + MI_Rolldown, + MI_TimeStretch, + MI_CloneLevel, + MI_SetKeyframes, + MI_Copy, + MI_Paste, + MI_PasteInto, + MI_Cut, + MI_Clear, + MI_Insert, + MI_PasteInto, + MI_Reframe1, + MI_Reframe2, + MI_Reframe3, + MI_Reframe4, + MI_ReframeWithEmptyInbetweens, + MI_Undo, + MI_Redo}; return commands.contains(commandId); } diff --git a/toonz/sources/toonz/cellselection.h b/toonz/sources/toonz/cellselection.h index a2df65f..10bffb1 100644 --- a/toonz/sources/toonz/cellselection.h +++ b/toonz/sources/toonz/cellselection.h @@ -8,6 +8,7 @@ #include class TimeStretchPopup; +class ReframePopup; class TXshCell; //============================================================================= @@ -16,6 +17,7 @@ class TXshCell; class TCellSelection final : public TSelection { TimeStretchPopup *m_timeStretchPopup; + ReframePopup *m_reframePopup; public: class Range { @@ -99,6 +101,8 @@ public: void reframe3Cells() { reframeCells(3); } void reframe4Cells() { reframeCells(4); } + void reframeWithEmptyInbetweens(); + void renameCells(TXshCell &cell); // rename cells for each columns with correspondent item in the list void renameMultiCells(QList &cells); diff --git a/toonz/sources/toonz/cellselectioncommand.cpp b/toonz/sources/toonz/cellselectioncommand.cpp index fa3c108..e91fa6c 100644 --- a/toonz/sources/toonz/cellselectioncommand.cpp +++ b/toonz/sources/toonz/cellselectioncommand.cpp @@ -8,6 +8,7 @@ #include "overwritepopup.h" #include "selectionutils.h" #include "columnselection.h" +#include "reframepopup.h" // TnzQt includes #include "toonzqt/tselectionhandle.h" @@ -553,6 +554,7 @@ class ReframeUndo final : public TUndo { int m_r0, m_r1; int m_type; int m_nr; + int m_withBlank; std::unique_ptr m_cells; public: @@ -560,7 +562,8 @@ public: std::vector m_columnIndeces; - ReframeUndo(int r0, int r1, std::vector columnIndeces, int type); + ReframeUndo(int r0, int r1, std::vector columnIndeces, int type, + int withBlank = -1); ~ReframeUndo(); void undo() const override; void redo() const override; @@ -569,7 +572,12 @@ public: int getSize() const override { return sizeof(*this); } QString getHistoryString() override { - return QObject::tr("Reframe to %1's").arg(QString::number(m_type)); + if (m_withBlank == -1) + return QObject::tr("Reframe to %1's").arg(QString::number(m_type)); + else + return QObject::tr("Reframe to %1's with %2 blanks") + .arg(QString::number(m_type)) + .arg(QString::number(m_withBlank)); } int getHistoryType() override { return HistoryType::Xsheet; } }; @@ -577,12 +585,13 @@ public: //----------------------------------------------------------------------------- ReframeUndo::ReframeUndo(int r0, int r1, std::vector columnIndeces, - int type) + int type, int withBlank) : m_r0(r0) , m_r1(r1) , m_type(type) , m_nr(0) - , m_columnIndeces(columnIndeces) { + , m_columnIndeces(columnIndeces) + , m_withBlank(withBlank) { m_nr = m_r1 - m_r0 + 1; assert(m_nr > 0); m_cells.reset(new TXshCell[m_nr * (int)m_columnIndeces.size()]); @@ -641,7 +650,7 @@ void ReframeUndo::redo() const { for (int c = 0; c < m_columnIndeces.size(); c++) app->getCurrentXsheet()->getXsheet()->reframeCells( - m_r0, m_r1, m_columnIndeces[c], m_type); + m_r0, m_r1, m_columnIndeces[c], m_type, m_withBlank); app->getCurrentXsheet()->notifyXsheetChanged(); } @@ -699,6 +708,86 @@ void TColumnSelection::reframeCells(int count) { TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } +//============================================================================= + +void TCellSelection::reframeWithEmptyInbetweens() { + if (isEmpty() || areAllColSelectedLocked()) return; + + std::vector colIndeces; + for (int c = m_range.m_c0; c <= m_range.m_c1; c++) colIndeces.push_back(c); + + // destruction of m_reframePopup will be done along with the main window + if (!m_reframePopup) m_reframePopup = new ReframePopup(); + int ret = m_reframePopup->exec(); + if (ret == QDialog::Rejected) return; + + int step, blank; + m_reframePopup->getValues(step, blank); + + ReframeUndo *undo = + new ReframeUndo(m_range.m_r0, m_range.m_r1, colIndeces, step, blank); + + int maximumRow = 0; + for (int c = m_range.m_c0; c <= m_range.m_c1; c++) { + int nrows = TApp::instance()->getCurrentXsheet()->getXsheet()->reframeCells( + m_range.m_r0, m_range.m_r1, c, step, blank); + undo->m_newRows.push_back(nrows); + if (maximumRow < nrows) maximumRow = nrows; + } + + if (maximumRow == 0) { + delete undo; + return; + } + + TUndoManager::manager()->add(undo); + + // select reframed range + selectCells(m_range.m_r0, m_range.m_c0, m_range.m_r0 + maximumRow - 1, + m_range.m_c1); + + TApp::instance()->getCurrentScene()->setDirtyFlag(true); + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); +} + +void TColumnSelection::reframeWithEmptyInbetweens() { + if (isEmpty()) return; + + int rowCount = + TApp::instance()->getCurrentXsheet()->getXsheet()->getFrameCount(); + std::vector colIndeces; + std::set::const_iterator it; + for (it = m_indices.begin(); it != m_indices.end(); it++) + colIndeces.push_back(*it); + + if (!m_reframePopup) m_reframePopup = new ReframePopup(); + int ret = m_reframePopup->exec(); + if (ret == QDialog::Rejected) return; + + int step, blank; + m_reframePopup->getValues(step, blank); + + ReframeUndo *undo = new ReframeUndo(0, rowCount - 1, colIndeces, step, blank); + + bool commandExecuted = false; + for (int c = 0; c < (int)colIndeces.size(); c++) { + int nrows = TApp::instance()->getCurrentXsheet()->getXsheet()->reframeCells( + 0, rowCount - 1, colIndeces[c], step, blank); + undo->m_newRows.push_back(nrows); + if (nrows > 0) commandExecuted = true; + } + + if (!commandExecuted) { + delete undo; + return; + } + + TUndoManager::manager()->add(undo); + + TApp::instance()->getCurrentScene()->setDirtyFlag(true); + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); +} + //********************************************************************************* // Reset Step Cells command //********************************************************************************* diff --git a/toonz/sources/toonz/columnselection.cpp b/toonz/sources/toonz/columnselection.cpp index ad905e0..cf1394a 100644 --- a/toonz/sources/toonz/columnselection.cpp +++ b/toonz/sources/toonz/columnselection.cpp @@ -26,7 +26,7 @@ // TColumnSelection //----------------------------------------------------------------------------- -TColumnSelection::TColumnSelection() {} +TColumnSelection::TColumnSelection() : m_reframePopup(0) {} //----------------------------------------------------------------------------- @@ -50,6 +50,8 @@ void TColumnSelection::enableCommands() { enableCommand(this, MI_Reframe2, &TColumnSelection::reframe2Cells); enableCommand(this, MI_Reframe3, &TColumnSelection::reframe3Cells); enableCommand(this, MI_Reframe4, &TColumnSelection::reframe4Cells); + enableCommand(this, MI_ReframeWithEmptyInbetweens, + &TColumnSelection::reframeWithEmptyInbetweens); } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/columnselection.h b/toonz/sources/toonz/columnselection.h index ab4aa71..4f32bd2 100644 --- a/toonz/sources/toonz/columnselection.h +++ b/toonz/sources/toonz/columnselection.h @@ -7,12 +7,15 @@ #include "tgeometry.h" #include +class ReframePopup; + //============================================================================= // TColumnSelection //----------------------------------------------------------------------------- class TColumnSelection final : public TSelection { std::set m_indices; + ReframePopup *m_reframePopup; public: TColumnSelection(); @@ -46,6 +49,8 @@ public: void reframe2Cells() { reframeCells(2); } void reframe3Cells() { reframeCells(3); } void reframe4Cells() { reframeCells(4); } + + void reframeWithEmptyInbetweens(); }; #endif // TCELLSELECTION_H diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index c61f1c0..95f3be5 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -1855,6 +1855,8 @@ void MainWindow::defineActions() { createMenuCellsAction(MI_Reframe4, tr("4's"), ""); + createMenuCellsAction(MI_ReframeWithEmptyInbetweens, + tr("Reframe with Empty Inbetweens..."), ""); createMenuCellsAction(MI_AutoInputCellNumber, tr("Auto Input Cell Number..."), ""); diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h index 6b02bce..45c6edc 100644 --- a/toonz/sources/toonz/menubarcommandids.h +++ b/toonz/sources/toonz/menubarcommandids.h @@ -305,6 +305,7 @@ #define MI_Reframe2 "MI_Reframe2" #define MI_Reframe3 "MI_Reframe3" #define MI_Reframe4 "MI_Reframe4" +#define MI_ReframeWithEmptyInbetweens "MI_ReframeWithEmptyInbetweens" #define MI_FillAreas "MI_FillAreas" #define MI_FillLines "MI_FillLines" diff --git a/toonz/sources/toonz/reframepopup.cpp b/toonz/sources/toonz/reframepopup.cpp new file mode 100644 index 0000000..7c23ef7 --- /dev/null +++ b/toonz/sources/toonz/reframepopup.cpp @@ -0,0 +1,83 @@ +#include "reframepopup.h" + +// Tnz includes +#include "tapp.h" +#include "menubarcommandids.h" +// TnzQt includes +#include "toonzqt/intfield.h" +#include "toonzqt/dvdialog.h" +#include "toonzqt/menubarcommand.h" + +#include +#include +#include +#include + +#include +//----------------------------------------------------------------------------- + +ReframePopup::ReframePopup() + : Dialog(TApp::instance()->getMainWindow(), true, true, "ReframePopup") { + setWindowTitle(tr("Reframe with Empty Inbetweens")); + m_step = new DVGui::IntLineEdit(this, 1, 1); + m_blank = new DVGui::IntLineEdit(this, 0, 0); + + m_blankCellCountLbl = new QLabel("0", this); + + QPushButton* okBtn = new QPushButton(tr("OK"), this); + QPushButton* cancelBtn = new QPushButton(tr("Cancel"), this); + + m_step->setObjectName("LargeSizedText"); + m_blank->setObjectName("LargeSizedText"); + // layout + QHBoxLayout* mainLay = new QHBoxLayout(); + mainLay->setMargin(0); + mainLay->setSpacing(5); + { + mainLay->addWidget(m_step); + mainLay->addWidget(new QLabel(tr("steps"), this)); + mainLay->addSpacing(10); + mainLay->addWidget(new QLabel(tr("with"), this)); + mainLay->addWidget(m_blank); + mainLay->addWidget(new QLabel(tr("empty inbetweens"), this)); + } + m_topLayout->addLayout(mainLay); + + QHBoxLayout* textLay = new QHBoxLayout(); + textLay->setMargin(0); + { + textLay->addStretch(1); + textLay->addWidget(new QLabel(tr("("), this)); + textLay->addWidget(m_blankCellCountLbl); + textLay->addWidget(new QLabel(tr(" blank cells will be inserted.)"), this)); + } + m_topLayout->addLayout(textLay); + + // buttons + m_buttonLayout->addWidget(okBtn); + m_buttonLayout->addWidget(cancelBtn); + + // signal-slot connections + bool ret = true; + ret = ret && connect(m_step, SIGNAL(editingFinished()), this, + SLOT(updateBlankCellCount())); + ret = ret && connect(m_blank, SIGNAL(editingFinished()), this, + SLOT(updateBlankCellCount())); + ret = ret && connect(okBtn, SIGNAL(clicked()), this, SLOT(accept())); + ret = ret && connect(cancelBtn, SIGNAL(clicked()), this, SLOT(reject())); + assert(ret); +} + +//----------------------------------------------------------------------------- + +void ReframePopup::updateBlankCellCount() { + int blankCellCount = m_step->getValue() * m_blank->getValue(); + m_blankCellCountLbl->setText(QString::number(blankCellCount)); +} + +//----------------------------------------------------------------------------- + +void ReframePopup::getValues(int& step, int& blank) { + step = m_step->getValue(); + blank = m_blank->getValue(); +} diff --git a/toonz/sources/toonz/reframepopup.h b/toonz/sources/toonz/reframepopup.h new file mode 100644 index 0000000..e02c1bc --- /dev/null +++ b/toonz/sources/toonz/reframepopup.h @@ -0,0 +1,39 @@ +#pragma once + +#ifndef REFRAMEPOPUP_H +#define REFRAMEPOPUP_H + +#include "toonzqt/dvdialog.h" + +#include + +class QLabel; + +namespace DVGui { +class IntLineEdit; +} + +//============================================================================= +// ReframePopup +// - Enables reframe cells with specified blank frames. +// - The amount of blank frames is specified by the quotient, divided by the +// reframe step, ( e.g. "3 steps - 2 blanks" will insert 6 blank cells.) +// considering that it is common usage in Japanese animation industry. +// - Can be used both to the cell and the column selections. +//----------------------------------------------------------------------------- + +class ReframePopup final : public DVGui::Dialog { + Q_OBJECT + + DVGui::IntLineEdit *m_step, *m_blank; + QLabel* m_blankCellCountLbl; + +public: + ReframePopup(); + void getValues(int& step, int& blank); + +public slots: + void updateBlankCellCount(); +}; + +#endif // REFRAMEPOPUP_H \ No newline at end of file diff --git a/toonz/sources/toonz/xshcellviewer.cpp b/toonz/sources/toonz/xshcellviewer.cpp index b50f5f0..f2a60f7 100644 --- a/toonz/sources/toonz/xshcellviewer.cpp +++ b/toonz/sources/toonz/xshcellviewer.cpp @@ -1430,7 +1430,7 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference) { #ifdef _WIN32 fontName = "Arial"; #else - fontName = "Helvetica"; + fontName = "Helvetica"; #endif } static QFont font(fontName, -1, QFont::Normal); @@ -2590,6 +2590,8 @@ void CellArea::createCellMenu(QMenu &menu, bool isCellSelected) { reframeSubMenu->addAction(cmdManager->getAction(MI_Reframe2)); reframeSubMenu->addAction(cmdManager->getAction(MI_Reframe3)); reframeSubMenu->addAction(cmdManager->getAction(MI_Reframe4)); + reframeSubMenu->addAction( + cmdManager->getAction(MI_ReframeWithEmptyInbetweens)); } menu.addMenu(reframeSubMenu); diff --git a/toonz/sources/toonz/xshcolumnviewer.cpp b/toonz/sources/toonz/xshcolumnviewer.cpp index acf9d46..7c81e99 100644 --- a/toonz/sources/toonz/xshcolumnviewer.cpp +++ b/toonz/sources/toonz/xshcolumnviewer.cpp @@ -2052,6 +2052,8 @@ void ColumnArea::contextMenuEvent(QContextMenuEvent *event) { reframeSubMenu->addAction(cmdManager->getAction(MI_Reframe2)); reframeSubMenu->addAction(cmdManager->getAction(MI_Reframe3)); reframeSubMenu->addAction(cmdManager->getAction(MI_Reframe4)); + reframeSubMenu->addAction( + cmdManager->getAction(MI_ReframeWithEmptyInbetweens)); } menu.addMenu(reframeSubMenu); menu.addAction(cmdManager->getAction(MI_AutoInputCellNumber)); diff --git a/toonz/sources/toonzlib/txsheet.cpp b/toonz/sources/toonzlib/txsheet.cpp index 54ff5b2..849cea3 100644 --- a/toonz/sources/toonzlib/txsheet.cpp +++ b/toonz/sources/toonzlib/txsheet.cpp @@ -849,7 +849,7 @@ void TXsheet::eachCells(int r0, int c0, int r1, int c1, int type) { //----------------------------------------------------------------------------- /*! force cells order in n-steps. returns the row amount after process */ -int TXsheet::reframeCells(int r0, int r1, int col, int type) { +int TXsheet::reframeCells(int r0, int r1, int col, int type, int withBlank) { // Row amount in the selection int nr = r1 - r0 + 1; @@ -864,11 +864,26 @@ int TXsheet::reframeCells(int r0, int r1, int col, int type) { cells.push_back(getCell(CellPosition(r, col))); } + // if withBlank is greater than -1, remove empty cells from cell order + if (withBlank >= 0) { + auto itr = cells.begin(); + while (itr != cells.end()) { + if ((*itr).isEmpty()) + itr = cells.erase(itr); + else + itr++; + } + } + if (cells.empty()) return 0; // row amount after n-step int nrows = cells.size() * type; + if (withBlank > 0) { + nrows += cells.size() * withBlank * type; + } + // if needed, insert cells if (nr < nrows) { insertCells(r1 + 1, col, nrows - nr); @@ -886,6 +901,13 @@ int TXsheet::reframeCells(int r0, int r1, int col, int type) { setCell(i + i1, col, cells[k]); } i += type; // dipende dal tipo di step (2 o 3 per ora) + + if (withBlank > 0) { + for (int i1 = 0; i1 < withBlank * type; i1++) { + clearCells(i + i1, col); + } + i += withBlank * type; + } } return nrows; // return row amount after process diff --git a/toonz/sources/translations/japanese/toonz.ts b/toonz/sources/translations/japanese/toonz.ts index 33aeea5..2110cd1 100644 --- a/toonz/sources/translations/japanese/toonz.ts +++ b/toonz/sources/translations/japanese/toonz.ts @@ -4722,6 +4722,10 @@ Do you want to create it? &Command Bar コマンドバー (&C) + + Reframe with Empty Inbetweens... + 空コマを入れてリフレーム... + MatchlinesDialog @@ -8342,6 +8346,45 @@ Are you sure? Command Bar コマンドバー + + Reframe to %1's with %2 blanks + リフレーム: %1 コマ 中 %2 + + + + ReframePopup + + OK + OK + + + Cancel + キャンセル + + + steps + コマ + + + with + + + + ( + ( + + + blank cells will be inserted.) + コマの空コマが挿入されます。) + + + Reframe with Empty Inbetweens + 空コマを入れてリフレーム + + + empty inbetweens + + RenameAsToonzPopup