Auto Input Cell Number command (#1479)
* auto input cell number * from frame - to frame
This commit is contained in:
parent
20c9615add
commit
947ad3809f
14 changed files with 765 additions and 9 deletions
Binary file not shown.
|
@ -169,6 +169,7 @@
|
|||
<command>MI_Rollup</command>
|
||||
<command>MI_Rolldown</command>
|
||||
<command>MI_TimeStretch</command>
|
||||
<command>MI_AutoInputCellNumber</command>
|
||||
<separator/>
|
||||
<command>MI_DrawingSubForward</command>
|
||||
<command>MI_DrawingSubBackward</command>
|
||||
|
|
|
@ -159,6 +159,7 @@
|
|||
<command>MI_Rollup</command>
|
||||
<command>MI_Rolldown</command>
|
||||
<command>MI_TimeStretch</command>
|
||||
<command>MI_AutoInputCellNumber</command>
|
||||
<separator/>
|
||||
<command>MI_Autorenumber</command>
|
||||
<command>MI_Duplicate</command>
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
<command>MI_Swing</command>
|
||||
<command>MI_Random</command>
|
||||
<command>MI_TimeStretch</command>
|
||||
<command>MI_AutoInputCellNumber</command>
|
||||
<separator/>
|
||||
<menu title="Reframe">
|
||||
<command>MI_Reframe1</command>
|
||||
|
|
|
@ -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<int> columnIndices,
|
||||
std::vector<TXshLevelP> levels, int rowsCount);
|
||||
|
||||
protected:
|
||||
bool checkCircularReferences(TXsheet *childCandidate);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
480
toonz/sources/toonz/autoinputcellnumberpopup.cpp
Normal file
480
toonz/sources/toonz/autoinputcellnumberpopup.cpp
Normal file
|
@ -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 <QMainWindow>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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<int> m_columnIndices;
|
||||
std::vector<TXshLevelP> m_levels;
|
||||
|
||||
int m_rowsCount;
|
||||
std::unique_ptr<TXshCell[]> m_beforeCells;
|
||||
|
||||
public:
|
||||
AutoInputCellNumberUndo(int increment, int interval, int step, int repeat,
|
||||
int from, int to, int r0, int r1, bool isOverwrite,
|
||||
std::vector<int> columnIndices,
|
||||
std::vector<TXshLevelP> 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<int> columnIndices,
|
||||
std::vector<TXshLevelP> 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<TFrameId> 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<int> columnIndices;
|
||||
std::vector<TXshLevelP> 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<TCellSelection *>(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<int> columnIndices;
|
||||
std::vector<TXshLevelP> 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<TFrameId> 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<int> &columnIndices,
|
||||
std::vector<TXshLevelP> &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<TCellSelection *>(selection);
|
||||
TColumnSelection *columnSelection =
|
||||
dynamic_cast<TColumnSelection *>(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<int> 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<AutoInputCellNumberPopup> openAutoInputCellNumberPopup(
|
||||
MI_AutoInputCellNumber);
|
44
toonz/sources/toonz/autoinputcellnumberpopup.h
Normal file
44
toonz/sources/toonz/autoinputcellnumberpopup.h
Normal file
|
@ -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<int> &columnIndices,
|
||||
std::vector<TXshLevelP> &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
|
|
@ -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<QString> 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<QString> RecentFiles::getFilesNameList(FileType fileType) {
|
||||
QList<QString> files =
|
||||
(fileType == Scene) ? m_recentScenes : (fileType == Level)
|
||||
? m_recentLevels
|
||||
: m_recentFlipbookImages;
|
||||
(fileType == Scene)
|
||||
? m_recentScenes
|
||||
: (fileType == Level) ? m_recentLevels : m_recentFlipbookImages;
|
||||
QList<QString> 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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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())) {
|
||||
|
|
|
@ -1611,3 +1611,111 @@ bool TXsheet::isRectEmpty(const CellPosition &pos0,
|
|||
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<int> columnIndices,
|
||||
std::vector<TXshLevelP> 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<TFrameId> fids;
|
||||
if (increment == 0) {
|
||||
std::vector<TFrameId> 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++;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -142,6 +142,85 @@ Please select a different device or check the microphone.</source>
|
|||
別のデバイスを選択するか、マイクを確認して下さい。</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>AutoInputCellNumberPopup</name>
|
||||
<message>
|
||||
<source>Auto Input Cell Number</source>
|
||||
<translation>セルの自動入力</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Overwrite</source>
|
||||
<translation>上書き</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Insert</source>
|
||||
<translation>挿入</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
<translation>キャンセル</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>with</source>
|
||||
<translation> </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>frames increment</source>
|
||||
<translation>番ごとに</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>inserting</source>
|
||||
<translation> </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>empty cell intervals</source>
|
||||
<translation>フレームおいて</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>cell steps</source>
|
||||
<translation>フレームずつ配置</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>を</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>times</source>
|
||||
<translation>回繰り返す</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No available cells or columns are selected.</source>
|
||||
<translation>入力可能なコマまたは列が選択されていません。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Selected level has no frames between From and To.</source>
|
||||
<translation>指定されたフレーム範囲に動画がありません。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Setting this value 0 will automatically
|
||||
pick up all frames in the selected level.</source>
|
||||
<translation>この値を0にすると、選択されたレベルの
|
||||
全てのフレームを自動的に配置します。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>From frame</source>
|
||||
<translation>セルの</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> </source>
|
||||
<comment>from frame</comment>
|
||||
<translation>番から</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>To frame</source>
|
||||
<translation> </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> </source>
|
||||
<comment>to frame</comment>
|
||||
<translation>番までを</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>AutocenterPopup</name>
|
||||
<message>
|
||||
|
@ -4631,6 +4710,10 @@ Do you want to create it?</source>
|
|||
<source>New Raster Level</source>
|
||||
<translation>新規ラスターレベル</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto Input Cell Number...</source>
|
||||
<translation>セルの自動入力...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Alpha Channel</source>
|
||||
<translation>アルファチャンネル</translation>
|
||||
|
@ -6518,6 +6601,22 @@ Is it OK to release these shortcuts?</source>
|
|||
<source>Enable OpenToonz Commands' Shortcut Keys While Renaming Cell</source>
|
||||
<translation>タイムシートのコマ入力時にOpenToonzコマンドのショートカットキーを有効にする</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Life is too short for Comic Sans</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Good luck. You're on your own from here.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Font *:</source>
|
||||
<translation>フォント *:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Font Weight *:</source>
|
||||
<translation>文字の太さ *:</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PreferencesPopup::FormatProperties</name>
|
||||
|
@ -8231,6 +8330,14 @@ Are you sure?</source>
|
|||
<source>Layer name</source>
|
||||
<translation>レイヤー名</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto Input Cell Numbers : %1</source>
|
||||
<translation>セルの自動入力:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Insert</source>
|
||||
<translation>挿入</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Command Bar</source>
|
||||
<translation>コマンドバー</translation>
|
||||
|
|
Loading…
Reference in a new issue