diff --git a/stuff/config/loc/日本語/toonz.qm b/stuff/config/loc/日本語/toonz.qm index 1cb3d38..114ccaa 100644 Binary files a/stuff/config/loc/日本語/toonz.qm and b/stuff/config/loc/日本語/toonz.qm differ diff --git a/stuff/profiles/layouts/rooms/Default/menubar_template.xml b/stuff/profiles/layouts/rooms/Default/menubar_template.xml index 8867a81..56c209b 100644 --- a/stuff/profiles/layouts/rooms/Default/menubar_template.xml +++ b/stuff/profiles/layouts/rooms/Default/menubar_template.xml @@ -169,6 +169,7 @@ MI_Rollup MI_Rolldown MI_TimeStretch + MI_AutoInputCellNumber MI_DrawingSubForward MI_DrawingSubBackward diff --git a/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml b/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml index b78b58c..9f4037d 100644 --- a/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml +++ b/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml @@ -159,6 +159,7 @@ MI_Rollup MI_Rolldown MI_TimeStretch + MI_AutoInputCellNumber MI_Autorenumber MI_Duplicate diff --git a/stuff/profiles/layouts/rooms/StudioGhibli/room4_menubar.xml b/stuff/profiles/layouts/rooms/StudioGhibli/room4_menubar.xml index e869102..86d788e 100644 --- a/stuff/profiles/layouts/rooms/StudioGhibli/room4_menubar.xml +++ b/stuff/profiles/layouts/rooms/StudioGhibli/room4_menubar.xml @@ -61,6 +61,7 @@ MI_Swing MI_Random MI_TimeStretch + MI_AutoInputCellNumber MI_Reframe1 diff --git a/toonz/sources/include/toonz/txsheet.h b/toonz/sources/include/toonz/txsheet.h index 0886596..1427371 100644 --- a/toonz/sources/include/toonz/txsheet.h +++ b/toonz/sources/include/toonz/txsheet.h @@ -12,6 +12,7 @@ // TnzLib includes #include "toonz/txshcolumn.h" +#include "toonz/txshlevel.h" #include "cellposition.h" @@ -549,6 +550,11 @@ in TXsheetImp. //! Returns the xsheet content's \a camstand bbox at the specified row. TRectD getBBox(int row) const; + void autoInputCellNumbers(int increment, int interval, int step, int repeat, + int from, int to, int r0, int r1, bool isOverwrite, + std::vector columnIndices, + std::vector levels, int rowsCount); + protected: bool checkCircularReferences(TXsheet *childCandidate); diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt index 2d960c1..315bf71 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 + autoinputcellnumberpopup.h # Tracker file dummyprocessor.h metnum.h @@ -317,6 +318,7 @@ set(SOURCES locatorpopup.cpp styleshortcutswitchablepanel.cpp cameracapturelevelcontrol.cpp + autoinputcellnumberpopup.cpp # Tracker file dummyprocessor.cpp metnum.cpp diff --git a/toonz/sources/toonz/autoinputcellnumberpopup.cpp b/toonz/sources/toonz/autoinputcellnumberpopup.cpp new file mode 100644 index 0000000..6b64e09 --- /dev/null +++ b/toonz/sources/toonz/autoinputcellnumberpopup.cpp @@ -0,0 +1,480 @@ +#include "autoinputcellnumberpopup.h" + +// Tnz includes +#include "tapp.h" +#include "menubarcommandids.h" +#include "cellselection.h" +#include "columnselection.h" +#include "penciltestpopup.h" // for FrameNumberLineEdit + +// TnzQt includes +#include "toonzqt/intfield.h" +#include "toonzqt/tselectionhandle.h" +#include "toonzqt/selection.h" +#include "toonzqt/dvdialog.h" + +// TnzLib includes +#include "toonz/txsheethandle.h" +#include "toonz/txshcell.h" +#include "toonz/txshsimplelevel.h" +#include "toonz/txshchildlevel.h" +#include "toonz/preferences.h" + +#include "tundo.h" +#include "historytypes.h" + +#include +#include +#include +#include + +#include + +namespace { + +class AutoInputCellNumberUndo final : public TUndo { + int m_increment, m_interval, m_step, m_repeat; + int m_from, m_to; + int m_r0, m_r1; + bool m_isOverwrite; + std::vector m_columnIndices; + std::vector m_levels; + + int m_rowsCount; + std::unique_ptr m_beforeCells; + +public: + AutoInputCellNumberUndo(int increment, int interval, int step, int repeat, + int from, int to, int r0, int r1, bool isOverwrite, + std::vector columnIndices, + std::vector levels); + + ~AutoInputCellNumberUndo() {} + + void undo() const override; + void redo() const override; + int getSize() const override { return sizeof(*this); } + + QString getHistoryString() override { + return QObject::tr("Auto Input Cell Numbers : %1") + .arg((m_isOverwrite) ? QObject::tr("Overwrite") + : QObject::tr("Insert")); + } + int getHistoryType() override { return HistoryType::Xsheet; } + + int rowsCount() { return m_rowsCount; } +}; +}; + +//----------------------------------------------------------------------------- +// executing this on column selection, set r1 = -1. +// Here are the expected behaviors +// 1. Cell Selection + Overwrite +// Cells will be input from the top of the selected range. +// New arrangement CANNOT run over the selected range. +// If the new arrangement is shorter than the selected range, +// excess cells will not be cleared but keep their contents. +// (It is the same behavior as Celsys' QuickChecker) +// 2. Cell Selection + Insert +// New arrangement will be inserted before the selected range. +// If the selected range has multiple columns, then the inserted +// cells will be expanded to the longest arrangement with empty cells. +// 3. Column Selection + Overwrite +// Cells will be input from the top of the columns. +// New arrangement CAN run over the existing column range. +// If the new arrangement is shorter than the selected range, +// excess cells will not be cleared but keep their contents. +// 4. Column Selection + Insert +// New arrangement will be inserted at the top of the columns. +// If multiple columns are selected, then the inserted cells +// will be expanded to the longest arrangement with empty cells. +// + +AutoInputCellNumberUndo::AutoInputCellNumberUndo(int increment, int interval, + int step, int repeat, int from, + int to, int r0, int r1, + bool isOverwrite, + std::vector columnIndices, + std::vector levels) + : m_increment(increment) + , m_interval(interval) + , m_step(step) + , m_repeat(repeat) + , m_from(from) + , m_to(to) + , m_r0(r0) + , m_r1(r1) + , m_isOverwrite(isOverwrite) + , m_columnIndices(columnIndices) + , m_levels(levels) { + if (Preferences::instance()->isShowFrameNumberWithLettersEnabled()) + m_increment *= 10; + + int maxInputFrameAmount = 0; + int frameRangeMin = std::min(m_from, m_to); + int frameRangeMax = std::max(m_from, m_to); + + if (m_increment == 0) { + // obtain the maximum frame amount to be input + for (int lv = 0; lv < m_levels.size(); lv++) { + TXshLevelP level = m_levels.at(lv); + std::vector fids; + level->getFids(fids); + int fCount = 0; + for (auto itr = fids.begin(); itr != fids.end(); ++itr) { + if ((*itr).getNumber() >= frameRangeMin && + (*itr).getNumber() <= frameRangeMax) { + fCount++; + } else if ((*itr).getNumber() > frameRangeMax) + break; + } + if (maxInputFrameAmount < fCount) maxInputFrameAmount = fCount; + } + } else + maxInputFrameAmount = (int)std::ceil( + (float)(frameRangeMax - frameRangeMin + 1) / (float)m_increment); + + maxInputFrameAmount *= m_repeat; + + // compute maximum row amount to be arranged + if (maxInputFrameAmount == 0) { + m_rowsCount = 0; + return; + } else + m_rowsCount = + maxInputFrameAmount * m_step + (maxInputFrameAmount - 1) * m_interval; + + // on overwrite case, keep existing cells for undo + if (m_isOverwrite) { + int rowUpTo = + (m_r1 == -1) ? m_rowsCount - 1 : std::min(m_r1, m_r0 + m_rowsCount - 1); + m_beforeCells.reset( + new TXshCell[m_columnIndices.size() * (rowUpTo - m_r0 + 1)]); + int k = 0; + for (int c = 0; c < m_columnIndices.size(); ++c) { + for (int r = m_r0; r <= rowUpTo; ++r) + m_beforeCells[k++] = + TApp::instance()->getCurrentXsheet()->getXsheet()->getCell( + r, m_columnIndices.at(c)); + } + } +} + +void AutoInputCellNumberUndo::undo() const { + TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); + // on overwrite case, retrieve cells + if (m_isOverwrite) { + int rowUpTo = + (m_r1 == -1) ? m_rowsCount - 1 : std::min(m_r1, m_r0 + m_rowsCount - 1); + int k = 0; + for (int c = 0; c < m_columnIndices.size(); ++c) { + for (int r = m_r0; r <= rowUpTo; ++r) { + if (m_beforeCells[k].isEmpty()) + xsh->clearCells(r, m_columnIndices.at(c)); + else + xsh->setCell(r, m_columnIndices.at(c), m_beforeCells[k]); + k++; + } + } + } else { // on insert case, remove inserted cells + for (int c = 0; c < m_columnIndices.size(); ++c) + xsh->removeCells(m_r0, m_columnIndices.at(c), m_rowsCount); + } + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); +} + +void AutoInputCellNumberUndo::redo() const { + TApp::instance()->getCurrentXsheet()->getXsheet()->autoInputCellNumbers( + m_increment, m_interval, m_step, m_repeat, m_from, m_to, m_r0, m_r1, + m_isOverwrite, m_columnIndices, m_levels, m_rowsCount); + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); +} + +//============================================================================= + +AutoInputCellNumberPopup::AutoInputCellNumberPopup() + : Dialog(TApp::instance()->getMainWindow(), false, true, + "AutoInputCellNumberPopup") { + setWindowTitle(tr("Auto Input Cell Number")); + + m_from = new FrameNumberLineEdit(this); + m_increment = new DVGui::IntLineEdit(this, 0, 0); + m_to = new FrameNumberLineEdit(this); + m_interval = new DVGui::IntLineEdit(this, 0, 0); + m_step = new DVGui::IntLineEdit(this, 1, 1); + m_repeat = new DVGui::IntLineEdit(this, 1, 1); + + m_overwriteBtn = new QPushButton(tr("Overwrite"), this); + m_insertBtn = new QPushButton(tr("Insert"), this); + QPushButton *cancelBtn = new QPushButton(tr("Cancel"), this); + + m_increment->setToolTip( + tr("Setting this value 0 will automatically \npick up all frames in the " + "selected level.")); + + // layout + + QGridLayout *mainLay = new QGridLayout(); + mainLay->setHorizontalSpacing(5); + mainLay->setVerticalSpacing(10); + { + mainLay->addWidget(new QLabel(tr("From frame"), this), 0, 0, + Qt::AlignRight | Qt::AlignVCenter); + mainLay->addWidget(m_from, 0, 1); + mainLay->addWidget(new QLabel(tr(" ", "from frame"), this), 0, 2, 1, 2); + + mainLay->addWidget(new QLabel(tr("with"), this), 1, 0, 1, 2, + Qt::AlignRight | Qt::AlignVCenter); + mainLay->addWidget(m_increment, 1, 2); + mainLay->addWidget(new QLabel(tr("frames increment"), this), 1, 3); + + mainLay->addWidget(new QLabel(tr("To frame"), this), 2, 0, + Qt::AlignRight | Qt::AlignVCenter); + mainLay->addWidget(m_to, 2, 1); + mainLay->addWidget(new QLabel(tr(" ", "to frame"), this), 2, 2, 1, 2); + + mainLay->addWidget(new QLabel(tr("inserting"), this), 3, 0, 1, 2, + Qt::AlignRight | Qt::AlignVCenter); + mainLay->addWidget(m_interval, 3, 2); + mainLay->addWidget(new QLabel(tr("empty cell intervals"), this), 3, 3); + + mainLay->addWidget(new QLabel(tr("with"), this), 4, 0, + Qt::AlignRight | Qt::AlignVCenter); + mainLay->addWidget(m_step, 4, 1); + mainLay->addWidget(new QLabel(tr("cell steps"), this), 4, 2, 1, 2); + + mainLay->addWidget(new QLabel(tr("Repeat"), this), 5, 0, + Qt::AlignRight | Qt::AlignVCenter); + mainLay->addWidget(m_repeat, 5, 1); + mainLay->addWidget(new QLabel(tr("times"), this), 5, 2, 1, 2); + } + m_topLayout->addLayout(mainLay); + + m_buttonFrame = new QFrame(this); + m_buttonFrame->setObjectName("dialogButtonFrame"); + m_buttonFrame->setFrameStyle(QFrame::StyledPanel); + m_buttonFrame->setFixedHeight(45); + + m_buttonLayout = new QHBoxLayout; + m_buttonLayout->setMargin(0); + m_buttonLayout->setSpacing(20); + m_buttonLayout->setAlignment(Qt::AlignHCenter); + { + m_buttonLayout->addSpacing(20); + m_buttonLayout->addWidget(m_overwriteBtn, 0); + m_buttonLayout->addWidget(m_insertBtn, 0); + m_buttonLayout->addWidget(cancelBtn, 0); + m_buttonLayout->addSpacing(20); + } + m_buttonFrame->setLayout(m_buttonLayout); + layout()->addWidget(m_buttonFrame); + + // signal-slot connections + bool ret = true; + ret = ret && connect(m_overwriteBtn, SIGNAL(clicked()), this, + SLOT(onOverwritePressed())); + ret = ret && + connect(m_insertBtn, SIGNAL(clicked()), this, SLOT(onInsertPressed())); + ret = ret && connect(cancelBtn, SIGNAL(clicked()), this, SLOT(close())); + assert(ret); +} + +//----------------------------------------------------------------------------- + +void AutoInputCellNumberPopup::onOverwritePressed() { doExecute(true); } + +//----------------------------------------------------------------------------- + +void AutoInputCellNumberPopup::onInsertPressed() { doExecute(false); } + +//----------------------------------------------------------------------------- + +void AutoInputCellNumberPopup::doExecute(bool overwrite) { + std::vector columnIndices; + std::vector levels; + int r0, r1; + bool found = getTarget(columnIndices, levels, r0, r1); + // just in case the execution is done to empty column + if (!found) { + DVGui::MsgBox(DVGui::WARNING, + tr("No available cells or columns are selected.")); + return; + } + AutoInputCellNumberUndo *undo = new AutoInputCellNumberUndo( + m_increment->getValue(), m_interval->getValue(), m_step->getValue(), + m_repeat->getValue(), m_from->getValue(), m_to->getValue(), r0, r1, + overwrite, columnIndices, levels); + // if no cells will be arranged, then return + if (undo->rowsCount() == 0) { + DVGui::MsgBox(DVGui::WARNING, + tr("Selected level has no frames between From and To.")); + delete undo; + return; + } + undo->redo(); + TUndoManager::manager()->add(undo); + // on cell selection & insert case, select the inserted cell range + if (!overwrite && r1 != -1) { + TSelection *selection = + TApp::instance()->getCurrentSelection()->getSelection(); + if (!selection) return; + TCellSelection *cellSelection = dynamic_cast(selection); + if (!cellSelection) return; + int c0, c1; + cellSelection->getSelectedCells(r0, c0, r1, c1); + cellSelection->selectCells(r0, c0, r0 + undo->rowsCount() - 1, c1); + TApp::instance()->getCurrentSelection()->notifySelectionChanged(); + } + // If exection is properly completed, then close this popup + close(); +} + +//----------------------------------------------------------------------------- + +void AutoInputCellNumberPopup::onSelectionChanged() { + // enable buttons only if they will work + m_overwriteBtn->setEnabled(false); + m_insertBtn->setEnabled(false); + + std::vector columnIndices; + std::vector levels; + int r0, r1; + bool found = getTarget(columnIndices, levels, r0, r1, true); + + // if no reference level is found, return + if (!found) return; + + int from, to; + std::vector fids; + levels.front()->getFids(fids); + from = fids.front().getNumber(); + to = fids.back().getNumber(); + + m_from->setValue(from); + m_to->setValue(to); + + // enable buttons + m_overwriteBtn->setEnabled(true); + m_insertBtn->setEnabled(true); +} + +//----------------------------------------------------------------------------- + +void AutoInputCellNumberPopup::showEvent(QShowEvent *) { + bool ret = connect(TApp::instance()->getCurrentSelection(), + SIGNAL(selectionChanged(TSelection *)), this, + SLOT(onSelectionChanged())); + assert(ret); + onSelectionChanged(); +} + +//----------------------------------------------------------------------------- + +void AutoInputCellNumberPopup::hideEvent(QHideEvent *) { + bool ret = disconnect(TApp::instance()->getCurrentSelection(), + SIGNAL(selectionChanged(TSelection *)), this, + SLOT(onSelectionChanged())); + assert(ret); +} + +//----------------------------------------------------------------------------- + +bool AutoInputCellNumberPopup::getTarget(std::vector &columnIndices, + std::vector &levels, + int &r0, int &r1, bool forCheck) { + TSelection *selection = + TApp::instance()->getCurrentSelection()->getSelection(); + if (!selection) return false; + + // something must be selected + if (selection->isEmpty()) return false; + + // selection must be cells or collumns + TCellSelection *cellSelection = dynamic_cast(selection); + TColumnSelection *columnSelection = + dynamic_cast(selection); + if (!cellSelection && !columnSelection) return false; + + TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); + // TXshLevelP refLevel; + + if (cellSelection) { + // if cells are selected, selected range must contain at least one available + // level + int c0, c1; + cellSelection->getSelectedCells(r0, c0, r1, c1); + for (int col = c0; col <= c1; col++) { + // column should not be empty or locked + if (xsh->isColumnEmpty(col)) continue; + TXshColumn *column = xsh->getColumn(col); + if (column->isLocked()) continue; + // column type should be "level" + if (column->getColumnType() != TXshColumn::eLevelType) continue; + // try to find the topmost available level in the column + for (int row = r0; row <= r1; row++) { + TXshCell cell = xsh->getCell(row, col); + if (cell.isEmpty()) continue; + TXshSimpleLevel *simpleLevel = cell.getSimpleLevel(); + TXshChildLevel *childLevel = cell.getChildLevel(); + if (!simpleLevel && !childLevel) continue; + if (simpleLevel) { + TFrameId fid = simpleLevel->getFirstFid(); + // the level should not be single-framed + if (fid.getNumber() == TFrameId::EMPTY_FRAME || + fid.getNumber() == TFrameId::NO_FRAME) + continue; + } + levels.push_back(cell.m_level); + columnIndices.push_back(col); + break; + } + // if some level found, break the loop for check + if (forCheck && !levels.empty()) break; + } + } + + else { // columnSelection case + r0 = 0; + r1 = -1; + std::set indices = columnSelection->getIndices(); + // try to find the leftmost available column + for (auto id = indices.begin(); id != indices.end(); ++id) { + TXshColumn *column = xsh->getColumn(*id); + if (!column) continue; + if (column->isEmpty() || column->isLocked()) continue; + // column type should be "level" + if (column->getColumnType() != TXshColumn::eLevelType) continue; + // try to find the topmost available level in the column + TXshCellColumn *xshColumn = column->getCellColumn(); + if (!xshColumn) continue; + int tmpR0, tmpR1; + column->getRange(tmpR0, tmpR1); + for (int row = tmpR0; row <= tmpR1; row++) { + TXshCell cell = xshColumn->getCell(row); + if (cell.isEmpty()) continue; + TXshSimpleLevel *simpleLevel = cell.getSimpleLevel(); + TXshChildLevel *childLevel = cell.getChildLevel(); + if (!simpleLevel && !childLevel) continue; + if (simpleLevel) { + TFrameId fid = simpleLevel->getFirstFid(); + // the level should not be single-framed + if (fid.getNumber() == TFrameId::EMPTY_FRAME || + fid.getNumber() == TFrameId::NO_FRAME) + continue; + } + levels.push_back(cell.m_level); + columnIndices.push_back(*id); + break; + } + // if some level found, break the loop for check + if (forCheck && !levels.empty()) break; + } + } + + return !levels.empty(); +} + +//----------------------------------------------------------------------------- + +OpenPopupCommandHandler openAutoInputCellNumberPopup( + MI_AutoInputCellNumber); diff --git a/toonz/sources/toonz/autoinputcellnumberpopup.h b/toonz/sources/toonz/autoinputcellnumberpopup.h new file mode 100644 index 0000000..bfdc202 --- /dev/null +++ b/toonz/sources/toonz/autoinputcellnumberpopup.h @@ -0,0 +1,44 @@ +#pragma once + +#ifndef AUTOINPUTCELLNUMBERPOPUP_H +#define AUTOINPUTCELLNUMBERPOPUP_H + +#include "toonzqt/dvdialog.h" +#include "toonz/txshlevel.h" + +class TSelection; +class QPushButton; +class FrameNumberLineEdit; +namespace DVGui { +class IntLineEdit; +} +//============================================================================= +// AutoInputCellNumberPopup +//----------------------------------------------------------------------------- + +class AutoInputCellNumberPopup final : public DVGui::Dialog { + Q_OBJECT + + DVGui::IntLineEdit *m_increment, *m_interval, *m_step, *m_repeat; + FrameNumberLineEdit *m_from, *m_to; + QPushButton *m_overwriteBtn, *m_insertBtn; + + bool getTarget(std::vector &columnIndices, + std::vector &levels, int &r0, int &r1, + bool forCheck = false); + void doExecute(bool overwrite); + +public: + AutoInputCellNumberPopup(); + +public slots: + void onOverwritePressed(); + void onInsertPressed(); + void onSelectionChanged(); + +protected: + void showEvent(QShowEvent *) override; + void hideEvent(QHideEvent *) override; +}; + +#endif // AUTOINPUTCELLNUMBERPOPUP_H \ No newline at end of file diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index 6d44d82..c61f1c0 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -1855,6 +1855,9 @@ void MainWindow::defineActions() { createMenuCellsAction(MI_Reframe4, tr("4's"), ""); + createMenuCellsAction(MI_AutoInputCellNumber, tr("Auto Input Cell Number..."), + ""); + createRightClickMenuAction(MI_SetKeyframes, tr("&Set Key"), "Z"); createToggle(MI_ViewCamera, tr("&Camera Box"), "", @@ -2344,9 +2347,9 @@ RecentFiles::~RecentFiles() {} void RecentFiles::addFilePath(QString path, FileType fileType) { QList files = - (fileType == Scene) ? m_recentScenes : (fileType == Level) - ? m_recentLevels - : m_recentFlipbookImages; + (fileType == Scene) + ? m_recentScenes + : (fileType == Level) ? m_recentLevels : m_recentFlipbookImages; int i; for (i = 0; i < files.size(); i++) if (files.at(i) == path) files.removeAt(i); @@ -2471,9 +2474,9 @@ void RecentFiles::saveRecentFiles() { QList RecentFiles::getFilesNameList(FileType fileType) { QList files = - (fileType == Scene) ? m_recentScenes : (fileType == Level) - ? m_recentLevels - : m_recentFlipbookImages; + (fileType == Scene) + ? m_recentScenes + : (fileType == Level) ? m_recentLevels : m_recentFlipbookImages; QList names; int i; for (i = 0; i < files.size(); i++) { @@ -2500,9 +2503,9 @@ void RecentFiles::refreshRecentFilesMenu(FileType fileType) { menu->setEnabled(false); else { CommandId clearActionId = - (fileType == Scene) ? MI_ClearRecentScene : (fileType == Level) - ? MI_ClearRecentLevel - : MI_ClearRecentImage; + (fileType == Scene) + ? MI_ClearRecentScene + : (fileType == Level) ? MI_ClearRecentLevel : MI_ClearRecentImage; menu->setActions(names); menu->addSeparator(); QAction *clearAction = CommandManager::instance()->getAction(clearActionId); diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h index 912ca8c..6b02bce 100644 --- a/toonz/sources/toonz/menubarcommandids.h +++ b/toonz/sources/toonz/menubarcommandids.h @@ -319,4 +319,5 @@ #define MI_StartupPopup "MI_StartupPopup" #define MI_PencilTest "MI_PencilTest" #define MI_AudioRecording "MI_AudioRecording" +#define MI_AutoInputCellNumber "MI_AutoInputCellNumber" #endif diff --git a/toonz/sources/toonz/xshcellviewer.cpp b/toonz/sources/toonz/xshcellviewer.cpp index 7545db3..a99a279 100644 --- a/toonz/sources/toonz/xshcellviewer.cpp +++ b/toonz/sources/toonz/xshcellviewer.cpp @@ -2615,6 +2615,7 @@ void CellArea::createCellMenu(QMenu &menu, bool isCellSelected) { menu.addAction(cmdManager->getAction(MI_Rollup)); menu.addAction(cmdManager->getAction(MI_Rolldown)); menu.addAction(cmdManager->getAction(MI_TimeStretch)); + menu.addAction(cmdManager->getAction(MI_AutoInputCellNumber)); menu.addSeparator(); menu.addAction(cmdManager->getAction(MI_Autorenumber)); } diff --git a/toonz/sources/toonz/xshcolumnviewer.cpp b/toonz/sources/toonz/xshcolumnviewer.cpp index 9a01fe0..383f186 100644 --- a/toonz/sources/toonz/xshcolumnviewer.cpp +++ b/toonz/sources/toonz/xshcolumnviewer.cpp @@ -2053,6 +2053,7 @@ void ColumnArea::contextMenuEvent(QContextMenuEvent *event) { reframeSubMenu->addAction(cmdManager->getAction(MI_Reframe4)); } menu.addMenu(reframeSubMenu); + menu.addAction(cmdManager->getAction(MI_AutoInputCellNumber)); } if (containsRasterLevel(m_viewer->getColumnSelection())) { diff --git a/toonz/sources/toonzlib/txsheet.cpp b/toonz/sources/toonzlib/txsheet.cpp index 3377cb7..4462812 100644 --- a/toonz/sources/toonzlib/txsheet.cpp +++ b/toonz/sources/toonzlib/txsheet.cpp @@ -1610,4 +1610,112 @@ bool TXsheet::isRectEmpty(const CellPosition &pos0, for (int layer = pos0.layer(); layer <= pos1.layer(); layer++) if (!getCell(CellPosition(frame, layer)).isEmpty()) return false; return true; +} + +//----------------------------------------------------------------------- +// Function triggered by AutoInputCellNumberPopup. +// executing this on column selection, set r1 = -1. +// Here are the expected behaviors +// 1. Cell Selection + Overwrite +// Cells will be input from the top of the selected range. +// New arrangement CANNOT run over the selected range. +// If the new arrangement is shorter than the selected range, +// excess cells will not be cleared but keep their contents. +// (It is the same behavior as Celsys' QuickChecker) +// 2. Cell Selection + Insert +// New arrangement will be inserted before the selected range. +// If the selected range has multiple columns, then the inserted +// cells will be expanded to the longest arrangement with empty cells. +// 3. Column Selection + Overwrite +// Cells will be input from the top of the columns. +// New arrangement CAN run over the existing column range. +// If the new arrangement is shorter than the selected range, +// excess cells will not be cleared but keep their contents. +// 4. Column Selection + Insert +// New arrangement will be inserted at the top of the columns. +// If multiple columns are selected, then the inserted cells +// will be expanded to the longest arrangement with empty cells. +// +void TXsheet::autoInputCellNumbers(int increment, int interval, int step, + int repeat, int from, int to, int r0, int r1, + bool isOverwrite, + std::vector columnIndices, + std::vector levels, + int rowsCount) { + int rowUpTo = (r1 == -1) ? rowsCount - 1 + : ((isOverwrite) ? std::min(r1, r0 + rowsCount - 1) + : r0 + rowsCount - 1); + // for each column + for (int c = 0; c < columnIndices.size(); c++) { + int columnIndex = columnIndices.at(c); + TXshLevelP level = levels.at(c); + + // on insertion, insert empty cells first + if (!isOverwrite) insertCells(r0, columnIndex, rowsCount); + + // obtain fids to be input + std::vector fids; + if (increment == 0) { + std::vector wholeFids; + level->getFids(wholeFids); + if (from <= to) { + for (auto itr = wholeFids.begin(); itr != wholeFids.end(); ++itr) { + if ((*itr).getNumber() >= from && (*itr).getNumber() <= to) + fids.push_back(*itr); + else if ((*itr).getNumber() > to) + break; + } + } else { // from > to + for (auto itr = wholeFids.rbegin(); itr != wholeFids.rend(); ++itr) { + if ((*itr).getNumber() <= from && (*itr).getNumber() >= to) + fids.push_back(*itr); + else if ((*itr).getNumber() < to) + break; + } + } + } else { // increment != 0 + int f = from; + if (from <= to) { + while (f <= to) { + fids.push_back(TFrameId(f)); + f += increment; + } + } else { // from > to + while (f >= to) { + fids.push_back(TFrameId(f)); + f -= increment; + } + } + } + + // input cells + int row = r0; + int repeat_itr = 0; + int fid_itr = 0; + int step_interv_itr = 0; + while (row <= rowUpTo) { + // input cell + if (step_interv_itr < step) + setCell(row, columnIndex, TXshCell(level, fids.at(fid_itr))); + // .. or set empty cell as interval + else + setCell(row, columnIndex, TXshCell()); + + // increment + step_interv_itr++; + // next frame + if (step_interv_itr == step + interval) { + fid_itr++; + step_interv_itr = 0; + } + // next repeat cycle + if (fid_itr == fids.size()) { + repeat_itr++; + fid_itr = 0; + } + if (repeat_itr == repeat) break; + + row++; + } + } } \ No newline at end of file diff --git a/toonz/sources/translations/japanese/toonz.ts b/toonz/sources/translations/japanese/toonz.ts index 9ff1a70..33aeea5 100644 --- a/toonz/sources/translations/japanese/toonz.ts +++ b/toonz/sources/translations/japanese/toonz.ts @@ -143,6 +143,85 @@ Please select a different device or check the microphone. + AutoInputCellNumberPopup + + Auto Input Cell Number + セルの自動入力 + + + Overwrite + 上書き + + + Insert + 挿入 + + + Cancel + キャンセル + + + with + + + + frames increment + 番ごとに + + + inserting + + + + empty cell intervals + フレームおいて + + + cell steps + フレームずつ配置 + + + Repeat + + + + times + 回繰り返す + + + No available cells or columns are selected. + 入力可能なコマまたは列が選択されていません。 + + + Selected level has no frames between From and To. + 指定されたフレーム範囲に動画がありません。 + + + Setting this value 0 will automatically +pick up all frames in the selected level. + この値を0にすると、選択されたレベルの +全てのフレームを自動的に配置します。 + + + From frame + セルの + + + + from frame + 番から + + + To frame + + + + + to frame + 番までを + + + AutocenterPopup Autocenter @@ -4632,6 +4711,10 @@ Do you want to create it? 新規ラスターレベル + Auto Input Cell Number... + セルの自動入力... + + Alpha Channel アルファチャンネル @@ -6518,6 +6601,22 @@ Is it OK to release these shortcuts? Enable OpenToonz Commands' Shortcut Keys While Renaming Cell タイムシートのコマ入力時にOpenToonzコマンドのショートカットキーを有効にする + + Life is too short for Comic Sans + + + + Good luck. You're on your own from here. + + + + Font *: + フォント *: + + + Font Weight *: + 文字の太さ *: + PreferencesPopup::FormatProperties @@ -8232,6 +8331,14 @@ Are you sure? レイヤー名 + Auto Input Cell Numbers : %1 + セルの自動入力: + + + Insert + 挿入 + + Command Bar コマンドバー