tahoma2d/toonz/sources/toonz/xsheetcmd.cpp
2023-10-12 08:23:29 -04:00

2864 lines
94 KiB
C++

// TnzCore includes
#include "tundo.h"
#include "trandom.h"
#include "tvectorimage.h"
#include "ttoonzimage.h"
#include "trasterimage.h"
#include "tpalette.h"
#include "tsystem.h"
#include "tstream.h"
#include "tconvert.h"
#include "tfilepath_io.h"
// TnzBase includes
#include "tunit.h"
// TnzLib includes
#include "toonz/txsheethandle.h"
#include "toonz/tscenehandle.h"
#include "toonz/tcolumnhandle.h"
#include "toonz/tobjecthandle.h"
#include "toonz/tframehandle.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/txsheet.h"
#include "toonz/txshcell.h"
#include "toonz/txshcolumn.h"
#include "toonz/toonzscene.h"
#include "toonz/levelset.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/txshleveltypes.h"
#include "toonz/txshsoundtextcolumn.h"
#include "toonz/txshsoundtextlevel.h"
#include "toonz/tstageobjecttree.h"
#include "toonz/tstageobjectkeyframe.h"
#include "toonz/stageobjectutil.h"
#include "toonz/toonzfolders.h"
#include "toonz/txshchildlevel.h"
#include "toonz/childstack.h"
#include "toonz/tproject.h"
#include "toonz/fxcommand.h"
#include "toonz/tfxhandle.h"
#include "toonz/scenefx.h"
#include "toonz/preferences.h"
#include "toonz/txshlevelcolumn.h"
#include "toonz/navigationtags.h"
// TnzQt includes
#include "toonzqt/tselectionhandle.h"
#include "toonzqt/gutil.h"
#include "toonzqt/menubarcommand.h"
#include "toonzqt/stageobjectsdata.h"
#include "historytypes.h"
#include "xsheetdragtool.h"
#include "xsheetviewer.h"
// Tnz6 includes
#include "cellselection.h"
#include "columnselection.h"
#include "keyframeselection.h"
#include "celldata.h"
#include "tapp.h"
#include "duplicatepopup.h"
#include "menubarcommandids.h"
#include "columncommand.h"
#include "xshcellviewer.h" // SetCellMarkUndo
#include "navtageditorpopup.h"
// Qt includes
#include <QClipboard>
// tcg includes
#include "tcg/boost/range_utility.h"
// boost includes
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/transformed.hpp>
namespace ba = boost::adaptors;
using namespace std; // Please, remove
//*****************************************************************************
// Local Namespace stuff
//*****************************************************************************
namespace {
void getColumns(std::vector<int> &columns) {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
app->getCurrentSelection()->getSelection());
if (selection && selection->isEmpty()) selection = 0;
if (!selection) // Se non c'e' selezione inserisco la colonna di camera
columns.push_back(-1);
for (int c = 0; c < xsh->getColumnCount(); ++c)
if (!selection || selection->isColumnSelected(c)) columns.push_back(c);
}
bool isKeyframe(int r, int c) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
TStageObjectId objectId =
(c == -1) ? TStageObjectId::CameraId(xsh->getCameraColumnIndex())
: TStageObjectId::ColumnId(c);
TStageObject *object = xsh->getStageObject(objectId);
assert(object);
return object->isKeyframe(r);
}
} // namespace
//*****************************************************************************
// InsertSceneFrame command
//*****************************************************************************
namespace XshCmd {
class InsertSceneFrameUndo : public TUndo {
protected:
int m_frame;
public:
InsertSceneFrameUndo(int frame) : m_frame(frame) {}
void undo() const override {
doRemoveSceneFrame(m_frame);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const override {
doInsertSceneFrame(m_frame);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override {
return QObject::tr("Insert Frame at Frame %1")
.arg(QString::number(m_frame + 1));
}
int getHistoryType() override { return HistoryType::Xsheet; }
protected:
static void doInsertSceneFrame(int frame);
static void doRemoveSceneFrame(int frame);
};
//-----------------------------------------------------------------------------
void InsertSceneFrameUndo::doInsertSceneFrame(int frame) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int c, colsCount = xsh->getColumnCount();
for (c = -1; c < colsCount; ++c) {
TStageObjectId objectId;
if (c == -1)
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
else {
objectId = TStageObjectId::ColumnId(c);
xsh->insertCells(frame, c);
TXshCell cell;
if (!Preferences::instance()->isImplicitHoldEnabled() && frame > 0)
cell = xsh->getCell(frame - 1, c);
xsh->setCell(frame, c, cell);
}
if (!xsh->getColumn(c) || xsh->getColumn(c)->isLocked()) continue;
if (TStageObject *obj = xsh->getStageObject(objectId))
insertFrame(obj, frame);
}
xsh->getNavigationTags()->shiftTags(frame, 1);
}
//-----------------------------------------------------------------------------
void InsertSceneFrameUndo::doRemoveSceneFrame(int frame) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
for (int c = -1; c != xsh->getColumnCount(); ++c) {
TStageObjectId objectId;
if (c == -1)
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
else {
objectId = TStageObjectId::ColumnId(c);
xsh->removeCells(frame, c);
}
if (!xsh->getColumn(c) || xsh->getColumn(c)->isLocked()) continue;
if (TStageObject *pegbar = xsh->getStageObject(objectId))
removeFrame(pegbar, frame);
}
if (xsh->isFrameTagged(frame)) xsh->getNavigationTags()->removeTag(frame);
xsh->getNavigationTags()->shiftTags(frame, -1);
}
//-----------------------------------------------------------------------------
static void insertSceneFrame(int frame) {
InsertSceneFrameUndo *undo = new InsertSceneFrameUndo(frame);
TUndoManager::manager()->add(undo);
undo->redo();
}
//=============================================================================
class ToggleCurrentTimeIndicatorCommand final : public MenuItemHandler {
public:
ToggleCurrentTimeIndicatorCommand()
: MenuItemHandler(MI_ToggleCurrentTimeIndicator) {}
void execute() override {
bool currentTimeIndEnabled =
Preferences::instance()->isCurrentTimelineIndicatorEnabled();
Preferences::instance()->setValue(currentTimelineEnabled,
!currentTimeIndEnabled);
}
} toggleCurrentTimeIndicatorComman;
//=============================================================================
class InsertSceneFrameCommand final : public MenuItemHandler {
public:
InsertSceneFrameCommand() : MenuItemHandler(MI_InsertSceneFrame) {}
void execute() override {
int frame = TApp::instance()->getCurrentFrame()->getFrame();
XshCmd::insertSceneFrame(frame);
}
} insertSceneFrameCommand;
//=============================================================================
class ToggleAutoCreateCommand final : public MenuItemHandler {
public:
ToggleAutoCreateCommand() : MenuItemHandler(MI_ToggleAutoCreate) {}
void execute() override {
bool currentAutoCreateEnabled =
Preferences::instance()->isAutoCreateEnabled();
if (CommandManager::instance()
->getAction(MI_ToggleAutoCreate)
->isChecked() == currentAutoCreateEnabled)
return;
Preferences::instance()->setValue(EnableAutocreation,
!currentAutoCreateEnabled);
}
} ToggleAutoCreateCommand;
//=============================================================================
class ToggleCreationInHoldCellsCommand final : public MenuItemHandler {
public:
ToggleCreationInHoldCellsCommand()
: MenuItemHandler(MI_ToggleCreationInHoldCells) {}
void execute() override {
bool currentCreationInHoldCells =
Preferences::instance()->isCreationInHoldCellsEnabled();
if (CommandManager::instance()
->getAction(MI_ToggleCreationInHoldCells)
->isChecked() == currentCreationInHoldCells)
return;
Preferences::instance()->setValue(EnableCreationInHoldCells,
!currentCreationInHoldCells);
}
} ToggleCreationInHoldCellsCommand;
//=============================================================================
class ToggleAutoStretchCommand final : public MenuItemHandler {
public:
ToggleAutoStretchCommand() : MenuItemHandler(MI_ToggleAutoStretch) {}
void execute() override {
bool currentAutoStretchEnabled =
Preferences::instance()->isAutoStretchEnabled();
if (CommandManager::instance()
->getAction(MI_ToggleAutoStretch)
->isChecked() == currentAutoStretchEnabled)
return;
Preferences::instance()->setValue(EnableAutoStretch,
!currentAutoStretchEnabled);
}
} ToggleAutoStretchCommand;
//=============================================================================
class ToggleImplicitHoldCommand final : public MenuItemHandler {
public:
ToggleImplicitHoldCommand() : MenuItemHandler(MI_ToggleImplicitHold) {}
void execute() override {
bool currentImplicitHoldEnabled =
Preferences::instance()->isImplicitHoldEnabled();
if (CommandManager::instance()
->getAction(MI_ToggleImplicitHold)
->isChecked() == currentImplicitHoldEnabled)
return;
Preferences::instance()->setValue(EnableImplicitHold,
!currentImplicitHoldEnabled);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
} ToggleImplicitHoldCommand;
//*****************************************************************************
// RemoveSceneFrame command
//*****************************************************************************
class RemoveSceneFrameUndo final : public InsertSceneFrameUndo {
std::vector<TXshCell> m_cells;
std::vector<TStageObject::Keyframe> m_keyframes;
NavigationTags::Tag m_tag;
public:
RemoveSceneFrameUndo(int frame) : InsertSceneFrameUndo(frame) {
// Store cells and TStageObject::Keyframe that will be canceled
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int colsCount = xsh->getColumnCount();
m_cells.resize(colsCount);
m_keyframes.resize(colsCount + 1);
m_tag = xsh->getNavigationTags()->getTag(frame);
// Inserting the eventual camera keyframe at the end
TStageObject *cameraObj = xsh->getStageObject(
TStageObjectId::CameraId(xsh->getCameraColumnIndex()));
if (cameraObj->isKeyframe(m_frame))
m_keyframes[colsCount] = cameraObj->getKeyframe(m_frame);
for (int c = 0; c != colsCount; ++c) {
// Store cell
const TXshCell &cell = xsh->getCell(m_frame, c, false);
m_cells[c] = cell;
// Store stage object keyframes
TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(c));
if (obj->isKeyframe(m_frame)) m_keyframes[c] = obj->getKeyframe(m_frame);
}
}
void redo() const override { InsertSceneFrameUndo::undo(); }
void undo() const override {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
// Insert an empty frame, need space for our stored stuff
doInsertSceneFrame(m_frame);
// Insert cells
int cellsCount = m_cells.size();
// Deal with the eventual camera keyframe
if (m_keyframes[cellsCount].m_isKeyframe) {
TStageObject *cameraObj = xsh->getStageObject(
TStageObjectId::CameraId(xsh->getCameraColumnIndex()));
cameraObj->setKeyframeWithoutUndo(m_frame, m_keyframes[cellsCount]);
}
for (int c = 0; c != cellsCount; ++c) {
xsh->setCell(m_frame, c, m_cells[c]);
if (m_keyframes[c].m_isKeyframe) {
TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(c));
obj->setKeyframeWithoutUndo(m_frame, m_keyframes[c]);
}
}
// Restore tag if there was one
if (m_tag.m_frame != -1)
xsh->getNavigationTags()->addTag(m_tag.m_frame, m_tag.m_label);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
int getSize() const override {
return 10 << 10;
} // Gave up exact calculation. Say ~ 10 kB?
QString getHistoryString() override {
return QObject::tr("Remove Frame at Frame %1")
.arg(QString::number(m_frame + 1));
}
int getHistoryType() override { return HistoryType::Xsheet; }
};
//-----------------------------------------------------------------------------
static void removeSceneFrame(int frame) {
RemoveSceneFrameUndo *undo = new RemoveSceneFrameUndo(frame);
TUndoManager::manager()->add(undo);
undo->redo();
}
//=============================================================================
class RemoveSceneFrameCommand final : public MenuItemHandler {
public:
RemoveSceneFrameCommand() : MenuItemHandler(MI_RemoveSceneFrame) {}
void execute() override {
int frame = TApp::instance()->getCurrentFrame()->getFrame();
XshCmd::removeSceneFrame(frame);
}
} removeSceneFrameCommand;
//*****************************************************************************
// GlobalKeyframeUndo definition
//*****************************************************************************
class GlobalKeyframeUndo : public TUndo {
public:
GlobalKeyframeUndo(int frame) : m_frame(frame) {}
int getSize() const override {
return sizeof(*this) + m_columns.size() * sizeof(int);
}
int getHistoryType() override { return HistoryType::Xsheet; }
protected:
std::vector<int> m_columns;
int m_frame;
protected:
static void doInsertGlobalKeyframes(int frame,
const std::vector<int> &columns);
static void doRemoveGlobalKeyframes(int frame,
const std::vector<int> &columns);
};
//-----------------------------------------------------------------------------
void GlobalKeyframeUndo::doInsertGlobalKeyframes(
int frame, const std::vector<int> &columns) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
for (const int c : columns) {
TStageObjectId objectId;
TXshColumn *column = xsh->getColumn(c);
if (column && column->getSoundColumn()) continue;
if (c == -1)
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
else
objectId = TStageObjectId::ColumnId(c);
TXshColumn *xshColumn = xsh->getColumn(c);
if (!xshColumn || xshColumn->isLocked() ||
(xshColumn->isCellEmpty(frame) && !objectId.isCamera()))
continue;
TStageObject *obj = xsh->getStageObject(objectId);
obj->setKeyframeWithoutUndo(frame);
}
}
//-----------------------------------------------------------------------------
void GlobalKeyframeUndo::doRemoveGlobalKeyframes(
int frame, const std::vector<int> &columns) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
for (const int c : columns) {
TStageObjectId objectId;
TXshColumn *column = xsh->getColumn(c);
if (column && column->getSoundColumn()) continue;
if (c == -1)
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
else
objectId = TStageObjectId::ColumnId(c);
if (xsh->getColumn(c) && xsh->getColumn(c)->isLocked()) continue;
TStageObject *obj = xsh->getStageObject(objectId);
obj->removeKeyframeWithoutUndo(frame);
}
}
//*****************************************************************************
// InsertGlobalKeyframe command
//*****************************************************************************
class InsertGlobalKeyframeUndo final : public GlobalKeyframeUndo {
public:
InsertGlobalKeyframeUndo(int frame, const std::vector<int> &columns)
: GlobalKeyframeUndo(frame) {
tcg::substitute(
m_columns,
columns | ba::filtered([frame](int c){ return !isKeyframe(frame, c); }));
}
void redo() const override {
doInsertGlobalKeyframes(m_frame, m_columns);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
void undo() const override {
doRemoveGlobalKeyframes(m_frame, m_columns);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
QString getHistoryString() override {
return QObject::tr("Insert Multiple Keys at Frame %1")
.arg(QString::number(m_frame + 1));
}
};
//-----------------------------------------------------------------------------
static void insertGlobalKeyframe(int frame) {
std::vector<int> columns;
::getColumns(columns);
if (columns.empty()) return;
TUndo *undo = new InsertGlobalKeyframeUndo(frame, columns);
TUndoManager::manager()->add(undo);
undo->redo();
}
//=============================================================================
class InsertGlobalKeyframeCommand final : public MenuItemHandler {
public:
InsertGlobalKeyframeCommand() : MenuItemHandler(MI_InsertGlobalKeyframe) {}
void execute() override {
int frame = TApp::instance()->getCurrentFrame()->getFrame();
XshCmd::insertGlobalKeyframe(frame);
}
} insertGlobalKeyframeCommand;
//*****************************************************************************
// RemoveGlobalKeyframe command
//*****************************************************************************
class RemoveGlobalKeyframeUndo final : public GlobalKeyframeUndo {
std::vector<TStageObject::Keyframe> m_keyframes;
public:
RemoveGlobalKeyframeUndo(int frame, const std::vector<int> &columns)
: GlobalKeyframeUndo(frame) {
struct locals {
static TStageObject::Keyframe getKeyframe(int r, int c) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
TStageObjectId objectId =
(c == -1) ? TStageObjectId::CameraId(xsh->getCameraColumnIndex())
: TStageObjectId::ColumnId(c);
TStageObject *object = xsh->getStageObject(objectId);
assert(object);
return object->getKeyframe(r);
}
}; // locals
tcg::substitute(m_columns,
columns | ba::filtered([frame](int c){ return isKeyframe(frame, c); }));
tcg::substitute(m_keyframes,
m_columns | ba::transformed([frame](int c){ return locals::getKeyframe(frame, c); }));
}
void redo() const override {
doRemoveGlobalKeyframes(m_frame, m_columns);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
void undo() const override {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int c, cCount = int(m_columns.size());
for (c = 0; c != cCount; ++c) {
int col = m_columns[c];
TStageObjectId objectId =
(col == -1) ? TStageObjectId::CameraId(xsh->getCameraColumnIndex())
: TStageObjectId::ColumnId(col);
TStageObject *object = xsh->getStageObject(objectId);
object->setKeyframeWithoutUndo(m_frame, m_keyframes[c]);
}
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
int getSize() const override {
return GlobalKeyframeUndo::getSize() +
m_keyframes.size() * sizeof(TStageObject::Keyframe);
}
QString getHistoryString() override {
return QObject::tr("Remove Multiple Keys at Frame %1")
.arg(QString::number(m_frame + 1));
}
};
//-----------------------------------------------------------------------------
static void removeGlobalKeyframe(int frame) {
std::vector<int> columns;
::getColumns(columns);
if (columns.empty()) return;
TUndo *undo = new RemoveGlobalKeyframeUndo(frame, columns);
TUndoManager::manager()->add(undo);
undo->redo();
}
//=============================================================================
class RemoveGlobalKeyframeCommand final : public MenuItemHandler {
public:
RemoveGlobalKeyframeCommand() : MenuItemHandler(MI_RemoveGlobalKeyframe) {}
void execute() override {
int frame = TApp::instance()->getCurrentFrame()->getFrame();
XshCmd::removeGlobalKeyframe(frame);
}
} removeGlobalKeyframeCommand;
//*****************************************************************************
// SetGlobalStopframe command
//*****************************************************************************
class SetGlobalStopframeUndo final : public TUndo {
std::vector<std::pair<int, TXshCell>> m_oldCells;
std::vector<int> m_columns;
int m_frame;
public:
SetGlobalStopframeUndo(int frame, const std::vector<int> &columns);
~SetGlobalStopframeUndo() {}
void undo() const override {
if (m_frame < 0 || !m_oldCells.size()) return;
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
for (int i = 0; i < m_oldCells.size(); i++) {
std::pair<int, TXshCell> cellData = m_oldCells[i];
TXshColumn *xshColumn = xsh->getColumn(cellData.first);
if (!xshColumn) continue;
TXshCellColumn *cellColumn = xshColumn->getCellColumn();
if (!cellColumn) continue;
std::vector<TXshCell> cells;
cells.push_back(cellData.second);
cellColumn->setCells(m_frame, 1, &cells[0]);
}
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const override;
int getSize() const override { return m_oldCells.size(); }
QString getHistoryString() override {
return QObject::tr("Set Multiple Stop Frames at Frame %1")
.arg(QString::number(m_frame + 1));
}
};
//-----------------------------------------------------------------------------
SetGlobalStopframeUndo::SetGlobalStopframeUndo(int frame,
const std::vector<int> &columns)
: m_frame(frame), m_columns(columns) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
m_oldCells.clear();
for (int c : m_columns) {
if (c < 0) continue;
TXshColumn *xshColumn = xsh->getColumn(c);
if (!xshColumn || xshColumn->getSoundColumn() ||
xshColumn->getSoundTextColumn() || xshColumn->isLocked() ||
xshColumn->isEmpty())
continue;
TXshCellColumn *cellColumn = xshColumn->getCellColumn();
if (!cellColumn || cellColumn->isEmpty()) continue;
TXshCell cell = cellColumn->getCell(m_frame, false);
if (!cell.isEmpty()) continue;
m_oldCells.push_back(std::make_pair(c, cell));
}
}
//-----------------------------------------------------------------------------
void SetGlobalStopframeUndo::redo() const {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
for (int c : m_columns) {
if (c < 0) continue;
TXshColumn *xshColumn = xsh->getColumn(c);
if (!xshColumn || xshColumn->getSoundColumn() ||
xshColumn->getSoundTextColumn() || xshColumn->isLocked() ||
xshColumn->isEmpty())
continue;
TXshCellColumn *cellColumn = xshColumn->getCellColumn();
if (!cellColumn || cellColumn->isEmpty()) continue;
TXshCell cell = cellColumn->getCell(m_frame);
if (!cell.isEmpty() && !cellColumn->isCellImplicit(m_frame)) continue;
if (cell.isEmpty()) { // Might have hit a stop frame
for (int r = m_frame - 1; r >= 0; r--) {
cell = cellColumn->getCell(r, false);
if (cell.isEmpty()) continue;
break;
}
if (cell.isEmpty()) continue;
}
cellColumn->setCell(
m_frame, TXshCell(cell.m_level.getPointer(), TFrameId::STOP_FRAME));
}
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
//-----------------------------------------------------------------------------
static void setGlobalStopframe(int frame) {
std::vector<int> columns;
::getColumns(columns);
if (columns.empty()) return;
TUndo *undo = new SetGlobalStopframeUndo(frame, columns);
TUndoManager::manager()->add(undo);
undo->redo();
}
//=============================================================================
class SetGlobalStopframeCommand final : public MenuItemHandler {
public:
SetGlobalStopframeCommand() : MenuItemHandler(MI_SetGlobalStopframe) {}
void execute() override {
int frame = TApp::instance()->getCurrentFrame()->getFrame();
XshCmd::setGlobalStopframe(frame);
}
} setGlobalStopframeCommand;
//*****************************************************************************
// RemoveGlobalStopframe command
//*****************************************************************************
class RemoveGlobalStopframeUndo final : public TUndo {
std::vector<std::pair<int, TXshCell>> m_oldCells;
std::vector<int> m_columns;
int m_frame;
public:
RemoveGlobalStopframeUndo(int frame, const std::vector<int> &columns);
~RemoveGlobalStopframeUndo() {}
void undo() const override {
if (m_frame < 0 || !m_oldCells.size()) return;
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
for (int i = 0; i < m_oldCells.size(); i++) {
std::pair<int, TXshCell> cellData = m_oldCells[i];
TXshColumn *xshColumn = xsh->getColumn(cellData.first);
if (!xshColumn) continue;
TXshCellColumn *cellColumn = xshColumn->getCellColumn();
if (!cellColumn) continue;
std::vector<TXshCell> cells;
cells.push_back(cellData.second);
cellColumn->setCells(m_frame, 1, &cells[0]);
}
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const override;
int getSize() const override { return m_oldCells.size(); }
QString getHistoryString() override {
return QObject::tr("Remove Multiple Stop Frames at Frame %1")
.arg(QString::number(m_frame + 1));
}
};
//-----------------------------------------------------------------------------
RemoveGlobalStopframeUndo::RemoveGlobalStopframeUndo(
int frame, const std::vector<int> &columns)
: m_frame(frame), m_columns(columns) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
m_oldCells.clear();
for (int c : m_columns) {
if (c < 0) continue;
TXshColumn *xshColumn = xsh->getColumn(c);
if (!xshColumn || xshColumn->getSoundColumn() ||
xshColumn->getSoundTextColumn() || xshColumn->isLocked() ||
xshColumn->isEmpty())
continue;
TXshCellColumn *cellColumn = xshColumn->getCellColumn();
if (!cellColumn || cellColumn->isEmpty()) continue;
TXshCell cell = cellColumn->getCell(m_frame, false);
if (!cell.getFrameId().isStopFrame()) continue;
m_oldCells.push_back(std::make_pair(c, cell));
}
}
//-----------------------------------------------------------------------------
void RemoveGlobalStopframeUndo::redo() const {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
for (int c : m_columns) {
if (c < 0) continue;
TXshColumn *xshColumn = xsh->getColumn(c);
if (!xshColumn || xshColumn->getSoundColumn() ||
xshColumn->getSoundTextColumn() || xshColumn->isLocked() ||
xshColumn->isEmpty())
continue;
TXshCellColumn *cellColumn = xshColumn->getCellColumn();
if (!cellColumn || cellColumn->isEmpty()) continue;
TXshCell cell = cellColumn->getCell(m_frame, false);
if (!cell.getFrameId().isStopFrame()) continue;
cellColumn->clearCells(m_frame, 1);
}
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
//-----------------------------------------------------------------------------
static void removeGlobalStopframe(int frame) {
std::vector<int> columns;
::getColumns(columns);
if (columns.empty()) return;
TUndo *undo = new RemoveGlobalStopframeUndo(frame, columns);
TUndoManager::manager()->add(undo);
undo->redo();
}
//=============================================================================
class RemoveGlobalStopframeCommand final : public MenuItemHandler {
public:
RemoveGlobalStopframeCommand() : MenuItemHandler(MI_RemoveGlobalStopframe) {}
void execute() override {
int frame = TApp::instance()->getCurrentFrame()->getFrame();
XshCmd::removeGlobalStopframe(frame);
}
} RemoveGlobalStopframeCommand;
//============================================================
// Drawing Substitution
//============================================================
class DrawingSubtitutionUndo final : public TUndo {
private:
int m_direction, m_row, m_col;
TCellSelection::Range m_range;
bool m_selected;
std::vector<std::pair<int, int>> emptyCells;
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) {
TXsheet *xsh = TApp::instance()->getCurrentScene()->getScene()->getXsheet();
int tempCol, tempRow;
int c = m_range.m_c0;
int r = m_range.m_r0;
while (c <= m_range.m_c1) {
tempCol = c;
while (r <= m_range.m_r1 + 1) {
tempRow = r;
if (xsh->getCell(tempRow, tempCol, false).isEmpty())
emptyCells.push_back(std::make_pair(tempRow, tempCol));
r++;
}
r = m_range.m_r0;
c++;
}
}
void undo() const override {
TXsheet *xsh = TApp::instance()->getCurrentScene()->getScene()->getXsheet();
if (!m_selected) {
changeDrawing(-m_direction, m_row, m_col);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
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 + 1) {
row = r;
bool found = false;
for (int i = 0; i < emptyCells.size(); i++) {
if (emptyCells[i].first == row && emptyCells[i].second == col) {
xsh->clearCells(row, col);
found = true;
}
}
if (found) {
r++;
continue;
}
if (r <= m_range.m_r1) changeDrawing(-m_direction, row, col);
r++;
}
r = m_range.m_r0;
c++;
}
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
void redo() const override {
if (!m_selected) {
changeDrawing(m_direction, m_row, m_col);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
return;
}
int col, row;
int c = m_range.m_c0;
int r = m_range.m_r0;
TXsheetP xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
while (c <= m_range.m_c1) {
col = c;
// Convert implicit cell at end of selected range into a populated cell
if (xsh->isImplicitCell((m_range.m_r1 + 1), col)) {
TXshCell origCell = xsh->getCell((m_range.m_r1 + 1), col);
xsh->setCell((m_range.m_r1 + 1), col, origCell);
}
while (r <= m_range.m_r1) {
row = r;
if (row == m_range.m_r0 || !xsh->isImplicitCell(row, col))
changeDrawing(m_direction, row, col);
r++;
}
r = m_range.m_r0;
c++;
}
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override {
return QObject::tr("Change current drawing %1")
.arg(QString::number(m_direction));
}
int getHistoryType() override { 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,
TXshLevel *level);
friend class DrawingSubtitutionGroupUndo;
};
//============================================================
class DrawingSubtitutionGroupUndo final : public TUndo {
private:
int m_direction;
int m_row;
int m_col;
int m_count;
bool m_selected;
TCellSelection::Range m_range;
std::vector<std::pair<int, int>> emptyCells;
typedef std::map<std::pair<int, int>, int> FramesMap;
FramesMap m_frameRanges;
public:
DrawingSubtitutionGroupUndo(int dir, int row, int col, bool selected,
TCellSelection::Range range)
: m_direction(dir)
, m_col(col)
, m_row(row)
, m_selected(selected)
, m_range(range) {
TXsheet *xsh =
TTool::getApplication()->getCurrentScene()->getScene()->getXsheet();
if (!selected) {
m_range.m_c0 = col;
m_range.m_r0 = row;
m_range.m_c1 = col;
m_range.m_r1 = row;
}
for (int c = m_range.m_c0; c <= m_range.m_c1; c++) {
for (int r = m_range.m_r0; r <= m_range.m_r1; r++) {
TXshCell baseCell = xsh->getCell(r, c);
// Find the 1st populated cell in the column
if (baseCell.isEmpty()) continue;
if (xsh->isImplicitCell(r, c))
emptyCells.push_back(std::make_pair(r, c));
FramesMap::key_type frameBaseKey(r, c);
int frameCount = 1;
TXshCell nextCell = xsh->getCell((r + frameCount), c);
while (nextCell == baseCell ||
(nextCell.isEmpty() && (r + frameCount) <= m_range.m_r1)) {
if ((r + frameCount) >= xsh->getFrameCount()) break;
if (nextCell.isEmpty() || xsh->isImplicitCell((r + frameCount), c))
emptyCells.push_back(std::make_pair((r + frameCount), c));
frameCount++;
nextCell = xsh->getCell((r + frameCount), c);
}
m_frameRanges.insert(std::make_pair(frameBaseKey, 0));
m_frameRanges[frameBaseKey] = frameCount;
r = r + (frameCount - 1); // Skip frames in range we've processed
}
}
}
void undo() const override {
TXsheet *xsh =
TTool::getApplication()->getCurrentScene()->getScene()->getXsheet();
FramesMap::const_iterator ct;
for (ct = m_frameRanges.begin(); ct != m_frameRanges.end(); ++ct) {
int n = 0;
while (n < ct->second) {
int row = ct->first.first + n;
int col = ct->first.second;
std::vector<std::pair<int, int>>::const_iterator it;
bool found = false;
for (it = emptyCells.begin(); it != emptyCells.end(); ++it) {
if (it->first == row && it->second == col) {
xsh->clearCells(row, col);
found = true;
}
}
if (!found)
DrawingSubtitutionUndo::changeDrawing(-m_direction, row, col);
n++;
}
}
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
void redo() const override {
FramesMap::const_iterator ct;
for (ct = m_frameRanges.begin(); ct != m_frameRanges.end(); ++ct) {
int n = 0;
while (n < ct->second) {
int row = ct->first.first + n;
int col = ct->first.second;
if (n == 0 ||
!TApp::instance()->getCurrentXsheet()->getXsheet()->isImplicitCell(
row, col))
DrawingSubtitutionUndo::changeDrawing(m_direction, row, col);
n++;
}
}
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override {
return QObject::tr("Change current drawing %1")
.arg(QString::number(m_direction));
}
int getHistoryType() override { 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);
bool usePrevCell = false;
if (cell.isEmpty()) {
TXshCell prevCell = xsh->getCell(row - 1, col);
if (prevCell.isEmpty() || !(prevCell.m_level->getSimpleLevel() ||
prevCell.m_level->getChildLevel() ||
prevCell.m_level->getSoundTextLevel()))
return false;
cell = prevCell;
usePrevCell = true;
} else if (cell.getFrameId().isStopFrame() || !cell.m_level ||
!(cell.m_level->getSimpleLevel() ||
cell.m_level->getChildLevel() ||
cell.m_level->getSoundTextLevel()))
return false;
TXshLevel *level = cell.m_level->getSimpleLevel();
if (!level) level = cell.m_level->getChildLevel();
if (!level) level = cell.m_level->getSoundTextLevel();
std::vector<TFrameId> fids;
int framesTextSize = 0;
int n, index;
bool usingSoundText = false;
TFrameId cellFrameId;
if (cell.m_level->getSimpleLevel())
cell.m_level->getSimpleLevel()->getFids(fids);
if (cell.m_level->getChildLevel())
cell.m_level->getChildLevel()->getFids(fids);
if (cell.m_level->getSoundTextLevel()) {
n = cell.m_level->getSoundTextLevel()->m_framesText.size();
usingSoundText = true;
} else
n = fids.size();
if (n < 2) return false;
if (!usingSoundText) {
std::vector<TFrameId>::iterator it;
it = std::find(fids.begin(), fids.end(), cell.m_frameId);
if (it == fids.end()) return false;
index = std::distance(fids.begin(), it);
} else
index = cell.getFrameId().getNumber();
if (usePrevCell) {
index -= delta;
cell = xsh->getCell(row, col);
}
// if negative direction, add the size to the direction to avoid a negative
// modulo
while (delta < 0) delta += n;
// the index is the remainder
index = (index + delta) % n;
assert(index < n);
// sound text levels can't have a 0 index frame id
// the index points to a qlist<qstring>
// reading 1 below the frameid number
// and you can't have a -1 index on a qlist
if (usingSoundText && index == 0) index = n;
if (!usingSoundText)
cellFrameId = fids[index];
else
cellFrameId = TFrameId(index);
setDrawing(cellFrameId, row, col, cell, level);
return true;
}
void DrawingSubtitutionUndo::setDrawing(const TFrameId &fid, int row, int col,
TXshCell cell, TXshLevel *level) {
TTool::Application *app = TTool::getApplication();
TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
cell.m_frameId = fid;
cell.m_level = level;
xsh->setCell(row, col, cell);
TStageObject *pegbar = xsh->getStageObject(TStageObjectId::ColumnId(col));
pegbar->setOffset(pegbar->getOffset());
}
//-----------------------------------------------------------------------------
static void drawingSubstituion(int dir) {
TCellSelection *selection = dynamic_cast<TCellSelection *>(
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();
}
static void drawingSubstituionGroup(int dir) {
TCellSelection *selection = dynamic_cast<TCellSelection *>(
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();
DrawingSubtitutionGroupUndo *undo =
new DrawingSubtitutionGroupUndo(dir, row, col, selected, range);
TUndoManager::manager()->add(undo);
undo->redo();
}
//=============================================================================
class DrawingSubstitutionForwardCommand final : public MenuItemHandler {
public:
DrawingSubstitutionForwardCommand() : MenuItemHandler(MI_DrawingSubForward) {}
void execute() override { XshCmd::drawingSubstituion(1); }
} DrawingSubstitutionForwardCommand;
//============================================================
class DrawingSubstitutionBackwardCommand final : public MenuItemHandler {
public:
DrawingSubstitutionBackwardCommand()
: MenuItemHandler(MI_DrawingSubBackward) {}
void execute() override { XshCmd::drawingSubstituion(-1); }
} DrawingSubstitutionBackwardCommand;
//=============================================================================
class DrawingSubstitutionGroupForwardCommand final : public MenuItemHandler {
public:
DrawingSubstitutionGroupForwardCommand()
: MenuItemHandler(MI_DrawingSubGroupForward) {}
void execute() override { XshCmd::drawingSubstituionGroup(1); }
} DrawingSubstitutionGroupForwardCommand;
//============================================================
class DrawingSubstitutionGroupBackwardCommand final : public MenuItemHandler {
public:
DrawingSubstitutionGroupBackwardCommand()
: MenuItemHandler(MI_DrawingSubGroupBackward) {}
void execute() override { XshCmd::drawingSubstituionGroup(-1); }
} DrawingSubstitutionGroupBackwardCommand;
//============================================================
class NewNoteLevelUndo final : public TUndo {
TXshSoundTextColumnP m_soundtextColumn;
int m_col;
QString m_columnName;
public:
NewNoteLevelUndo(TXshSoundTextColumn *soundtextColumn, int col,
QString columnName)
: m_soundtextColumn(soundtextColumn)
, m_col(col)
, m_columnName(columnName) {}
void undo() const override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
xsh->removeColumn(m_col);
app->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
xsh->insertColumn(m_col, m_soundtextColumn.getPointer());
TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(m_col));
std::string str = m_columnName.toStdString();
obj->setName(str);
app->getCurrentXsheet()->notifyXsheetChanged();
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override { return QObject::tr("New Note Level"); }
int getHistoryType() override { return HistoryType::Xsheet; }
};
//============================================================
static void newNoteLevel() {
TTool::Application *app = TTool::getApplication();
TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
int col = TTool::getApplication()->getCurrentColumn()->getColumnIndex();
if (col < 0)
col = 0; // Normally insert before. In case of camera, insert after
TXshSoundTextColumn *textSoundCol = new TXshSoundTextColumn();
textSoundCol->setXsheet(xsh);
QList<QString> textList;
textList << " ";
textSoundCol->createSoundTextLevel(0, textList);
xsh->insertColumn(col, textSoundCol);
// name the level a unique NoteLevel number
TStageObjectTree *objects = xsh->getStageObjectTree();
int objectCount = objects->getStageObjectCount();
int maxTextColumns = 1;
for (int i = 0; i < objectCount; i++) {
TStageObject *object = objects->getStageObject(i);
std::string objName = object->getName();
int pos = objName.find("NoteLevel");
if (pos != std::string::npos && pos == 0) {
std::string currStrCount = objName.substr(9);
bool ok;
int currCount = QString::fromStdString(currStrCount).toInt(&ok);
if (ok && currCount >= maxTextColumns) {
maxTextColumns = currCount + 1;
}
}
}
TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(col));
QString str = "NoteLevel" + QString::number(maxTextColumns);
obj->setName(str.toStdString());
TUndoManager::manager()->add(new NewNoteLevelUndo(textSoundCol, col, str));
TXsheetHandle *xshHandle = app->getCurrentXsheet();
xshHandle->notifyXsheetChanged();
}
//============================================================
class NewNoteLevelCommand final : public MenuItemHandler {
public:
NewNoteLevelCommand() : MenuItemHandler(MI_NewNoteLevel) {}
void execute() override { XshCmd::newNoteLevel(); }
} NewNoteLevelCommand;
//============================================================
static void removeEmptyColumns() {
TTool::Application *app = TTool::getApplication();
TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
std::set<int> indices;
for (int i = 0; i < xsh->getColumnCount(); i++) {
TXshColumn *column = xsh->getColumn(i);
if (!column || column->isEmpty()) indices.insert(i);
}
if (indices.empty()) return;
if (!ColumnCmd::checkExpressionReferences(indices)) return;
ColumnCmd::deleteColumns(indices, false, false);
app->getCurrentXsheet()->notifyXsheetChanged();
}
class RemoveEmptyColumnsCommand final : public MenuItemHandler {
public:
RemoveEmptyColumnsCommand() : MenuItemHandler(MI_RemoveEmptyColumns) {}
void execute() override { XshCmd::removeEmptyColumns(); }
} RemoveEmptyColumnsCommand;
//============================================================
static void convertHoldType(int holdType) {
TTool::Application *app = TTool::getApplication();
TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
if (!xsh) return;
int answer = DVGui::MsgBox(
QString(QObject::tr("Converting scene to use %1 Holds can only be undone "
"using 'Revert Scene'. Save before converting.\nDo "
"you want to continue?")
.arg(holdType == 0 ? QObject::tr("Implicit")
: QObject::tr("Explicit"))),
QObject::tr("Continue"), QObject::tr("Cancel"), 1);
if (answer == 0 || answer == 2) return;
QAction *action =
CommandManager::instance()->getAction(MI_ToggleImplicitHold);
if (holdType == 0) {
xsh->convertToImplicitHolds();
if (action && !action->isChecked()) action->trigger();
} else {
int r0, r1, step;
XsheetGUI::getPlayRange(r0, r1, step);
xsh->convertToExplicitHolds(r1);
if (action && action->isChecked()) action->trigger();
}
app->getCurrentScene()->setDirtyFlag();
app->getCurrentXsheet()->notifyXsheetChanged();
}
class ConvertToImplicitHoldsCommand final : public MenuItemHandler {
public:
ConvertToImplicitHoldsCommand()
: MenuItemHandler(MI_ConvertToImplicitHolds) {}
void execute() override { XshCmd::convertHoldType(0); }
} ConvertToImplicitHoldsCommand;
class ConvertToExplicitHoldsCommand final : public MenuItemHandler {
public:
ConvertToExplicitHoldsCommand()
: MenuItemHandler(MI_ConvertToExplicitHolds) {}
void execute() override { XshCmd::convertHoldType(1); }
} ConvertToExplicitHoldsCommand;
//============================================================
} // namespace XshCmd
//*****************************************************************************
// Selection commands
//*****************************************************************************
class SelectRowKeyframesCommand final : public MenuItemHandler {
public:
SelectRowKeyframesCommand() : MenuItemHandler(MI_SelectRowKeyframes) {}
void execute() override {
TApp *app = TApp::instance();
TKeyframeSelection *selection = dynamic_cast<TKeyframeSelection *>(
app->getCurrentSelection()->getSelection());
if (!selection) return;
int row = app->getCurrentFrame()->getFrame();
selection->selectNone();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = scene->getXsheet();
int col;
for (col = -1; col < xsh->getColumnCount(); col++) {
TStageObjectId objectId;
if (col == -1 && Preferences::instance()->isXsheetCameraColumnVisible())
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
else
objectId = TStageObjectId::ColumnId(col);
TStageObject *pegbar = xsh->getStageObject(objectId);
if (pegbar->isKeyframe(row)) selection->select(row, col);
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
} SelectRowKeyframesCommand;
//-----------------------------------------------------------------------------
class SelectColumnKeyframesCommand final : public MenuItemHandler {
public:
SelectColumnKeyframesCommand() : MenuItemHandler(MI_SelectColumnKeyframes) {}
void execute() override {
TApp *app = TApp::instance();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = scene->getXsheet();
TKeyframeSelection *selection = dynamic_cast<TKeyframeSelection *>(
app->getCurrentSelection()->getSelection());
if (!selection) return;
int col = app->getCurrentColumn()->getColumnIndex();
TStageObjectId objectId = app->getCurrentObject()->getObjectId();
if (app->getCurrentObject()->getObjectId() ==
TStageObjectId::CameraId(xsh->getCameraColumnIndex())) {
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
col = -1;
}
selection->selectNone();
TStageObject *pegbar = xsh->getStageObject(objectId);
TStageObject::KeyframeMap keyframes;
pegbar->getKeyframes(keyframes);
for (TStageObject::KeyframeMap::iterator it = keyframes.begin();
it != keyframes.end(); ++it)
selection->select(it->first, col);
app->getCurrentXsheet()->notifyXsheetChanged();
}
} SelectColumnKeyframesCommand;
//-----------------------------------------------------------------------------
class SelectAllKeyframesCommand final : public MenuItemHandler {
public:
SelectAllKeyframesCommand() : MenuItemHandler(MI_SelectAllKeyframes) {}
void execute() override {
TApp *app = TApp::instance();
TKeyframeSelection *selection = dynamic_cast<TKeyframeSelection *>(
app->getCurrentSelection()->getSelection());
if (!selection) return;
selection->selectNone();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = scene->getXsheet();
int col;
for (col = -1; col < xsh->getColumnCount(); col++) {
TStageObjectId objectId;
if (col == -1 && Preferences::instance()->isXsheetCameraColumnVisible())
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
else
objectId = TStageObjectId::ColumnId(col);
TStageObject *pegbar = xsh->getStageObject(objectId);
TStageObject::KeyframeMap keyframes;
pegbar->getKeyframes(keyframes);
for (TStageObject::KeyframeMap::iterator it = keyframes.begin();
it != keyframes.end(); ++it) {
int row = it->first;
assert(pegbar->isKeyframe(row));
selection->select(row, col);
}
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
} SelectAllKeyframesCommand;
//-----------------------------------------------------------------------------
class SelectAllKeyframesBeforeCommand final : public MenuItemHandler {
public:
SelectAllKeyframesBeforeCommand()
: MenuItemHandler(MI_SelectAllKeyframesNotBefore) {}
void execute() override {
TApp *app = TApp::instance();
TKeyframeSelection *selection = dynamic_cast<TKeyframeSelection *>(
app->getCurrentSelection()->getSelection());
if (!selection) return;
int currentRow = app->getCurrentFrame()->getFrame();
selection->selectNone();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = scene->getXsheet();
for (int col = -1; col < xsh->getColumnCount(); col++) {
TStageObjectId objectId;
if (col == -1 && Preferences::instance()->isXsheetCameraColumnVisible())
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
else
objectId = TStageObjectId::ColumnId(col);
TStageObject *pegbar = xsh->getStageObject(objectId);
TStageObject::KeyframeMap keyframes;
pegbar->getKeyframes(keyframes);
for (TStageObject::KeyframeMap::iterator it = keyframes.begin();
it != keyframes.end(); ++it) {
int row = it->first;
if (row < currentRow) continue;
assert(pegbar->isKeyframe(row));
selection->select(row, col);
}
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
} SelectAllKeyframesBeforeCommand;
//-----------------------------------------------------------------------------
class SelectAllKeyframesAfterCommand final : public MenuItemHandler {
public:
SelectAllKeyframesAfterCommand()
: MenuItemHandler(MI_SelectAllKeyframesNotAfter) {}
void execute() override {
TApp *app = TApp::instance();
TKeyframeSelection *selection = dynamic_cast<TKeyframeSelection *>(
app->getCurrentSelection()->getSelection());
if (!selection) return;
int currentRow = app->getCurrentFrame()->getFrame();
selection->selectNone();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = scene->getXsheet();
int col;
for (col = -1; col < xsh->getColumnCount(); col++) {
TStageObjectId objectId;
if (col == -1 && Preferences::instance()->isXsheetCameraColumnVisible())
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
else
objectId = TStageObjectId::ColumnId(col);
TStageObject *pegbar = xsh->getStageObject(objectId);
TStageObject::KeyframeMap keyframes;
pegbar->getKeyframes(keyframes);
for (TStageObject::KeyframeMap::iterator it = keyframes.begin();
it != keyframes.end(); ++it) {
int row = it->first;
if (row > currentRow) continue;
assert(pegbar->isKeyframe(row));
selection->select(row, col);
}
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
} SelectAllKeyframesAfterCommand;
//-----------------------------------------------------------------------------
class SelectPreviousKeysInColumnCommand final : public MenuItemHandler {
public:
SelectPreviousKeysInColumnCommand()
: MenuItemHandler(MI_SelectPreviousKeysInColumn) {}
void execute() override {
TApp *app = TApp::instance();
TKeyframeSelection *selection = dynamic_cast<TKeyframeSelection *>(
app->getCurrentSelection()->getSelection());
if (!selection) return;
int currentRow = app->getCurrentFrame()->getFrame();
int currentColumn = app->getCurrentColumn()->getColumnIndex();
selection->selectNone();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = scene->getXsheet();
TStageObjectId objectId =
TApp::instance()->getCurrentObject()->getObjectId();
if (objectId == TStageObjectId::CameraId(xsh->getCameraColumnIndex()) &&
Preferences::instance()->isXsheetCameraColumnVisible())
currentColumn = -1;
TStageObject *pegbar = xsh->getStageObject(objectId);
TStageObject::KeyframeMap keyframes;
pegbar->getKeyframes(keyframes);
for (TStageObject::KeyframeMap::iterator it = keyframes.begin();
it != keyframes.end(); ++it) {
int row = it->first;
if (row > currentRow) continue;
assert(pegbar->isKeyframe(row));
selection->select(row, currentColumn);
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
} SelectPreviousKeysInColumnCommand;
//-----------------------------------------------------------------------------
class SelectFollowingKeysInColumnCommand final : public MenuItemHandler {
public:
SelectFollowingKeysInColumnCommand()
: MenuItemHandler(MI_SelectFollowingKeysInColumn) {}
void execute() override {
TApp *app = TApp::instance();
TKeyframeSelection *selection = dynamic_cast<TKeyframeSelection *>(
app->getCurrentSelection()->getSelection());
if (!selection) return;
int currentRow = app->getCurrentFrame()->getFrame();
int currentColumn = app->getCurrentColumn()->getColumnIndex();
selection->selectNone();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = scene->getXsheet();
TStageObjectId objectId =
TApp::instance()->getCurrentObject()->getObjectId();
if (objectId == TStageObjectId::CameraId(xsh->getCameraColumnIndex()) &&
Preferences::instance()->isXsheetCameraColumnVisible())
currentColumn = -1;
TStageObject *pegbar = xsh->getStageObject(objectId);
TStageObject::KeyframeMap keyframes;
pegbar->getKeyframes(keyframes);
for (TStageObject::KeyframeMap::iterator it = keyframes.begin();
it != keyframes.end(); ++it) {
int row = it->first;
if (row < currentRow) continue;
assert(pegbar->isKeyframe(row));
selection->select(row, currentColumn);
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
} SelectFollowingKeysInColumnCommand;
//-----------------------------------------------------------------------------
class SelectPreviousKeysInRowCommand final : public MenuItemHandler {
public:
SelectPreviousKeysInRowCommand()
: MenuItemHandler(MI_SelectPreviousKeysInRow) {}
void execute() override {
TApp *app = TApp::instance();
TKeyframeSelection *selection = dynamic_cast<TKeyframeSelection *>(
app->getCurrentSelection()->getSelection());
if (!selection) return;
int currentRow = app->getCurrentFrame()->getFrame();
int currentColumn = app->getCurrentColumn()->getColumnIndex();
selection->selectNone();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = scene->getXsheet();
int col;
for (col = -1; col <= currentColumn; col++) {
TStageObjectId objectId;
if (col == -1 && Preferences::instance()->isXsheetCameraColumnVisible())
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
else
objectId = TStageObjectId::ColumnId(col);
TStageObject *pegbar = xsh->getStageObject(objectId);
TStageObject::KeyframeMap keyframes;
pegbar->getKeyframes(keyframes);
for (TStageObject::KeyframeMap::iterator it = keyframes.begin();
it != keyframes.end(); ++it) {
int row = it->first;
if (row != currentRow) continue;
assert(pegbar->isKeyframe(row));
selection->select(row, col);
}
}
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentXsheet()->notifyXsheetChanged();
}
} SelectPreviousKeysInRowCommand;
//-----------------------------------------------------------------------------
class SelectFollowingKeysInRowCommand final : public MenuItemHandler {
public:
SelectFollowingKeysInRowCommand()
: MenuItemHandler(MI_SelectFollowingKeysInRow) {}
void execute() override {
TApp *app = TApp::instance();
TKeyframeSelection *selection = dynamic_cast<TKeyframeSelection *>(
app->getCurrentSelection()->getSelection());
if (!selection) return;
int currentRow = app->getCurrentFrame()->getFrame();
int currentColumn = app->getCurrentColumn()->getColumnIndex();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = scene->getXsheet();
TStageObjectId objectId =
TApp::instance()->getCurrentObject()->getObjectId();
if (objectId == TStageObjectId::CameraId(xsh->getCameraColumnIndex()) &&
Preferences::instance()->isXsheetCameraColumnVisible())
currentColumn = -1;
selection->selectNone();
int col;
for (col = currentColumn; col < xsh->getColumnCount(); col++) {
TStageObjectId objectId;
if (col == -1)
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
else
objectId = TStageObjectId::ColumnId(col);
TStageObject *pegbar = xsh->getStageObject(objectId);
TStageObject::KeyframeMap keyframes;
pegbar->getKeyframes(keyframes);
for (TStageObject::KeyframeMap::iterator it = keyframes.begin();
it != keyframes.end(); ++it) {
int row = it->first;
if (row != currentRow) continue;
assert(pegbar->isKeyframe(row));
selection->select(row, col);
}
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
} SelectFollowingKeysInRowCommand;
//-----------------------------------------------------------------------------
class InvertKeyframeSelectionCommand final : public MenuItemHandler {
public:
InvertKeyframeSelectionCommand()
: MenuItemHandler(MI_InvertKeyframeSelection) {}
void execute() override {
TApp *app = TApp::instance();
TKeyframeSelection *selection = dynamic_cast<TKeyframeSelection *>(
app->getCurrentSelection()->getSelection());
if (!selection) return;
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = scene->getXsheet();
int col;
for (col = -1; col < xsh->getColumnCount(); col++) {
TStageObjectId objectId;
if (col == -1 && Preferences::instance()->isXsheetCameraColumnVisible())
objectId = TStageObjectId::CameraId(xsh->getCameraColumnIndex());
else
objectId = TStageObjectId::ColumnId(col);
TStageObject *pegbar = xsh->getStageObject(objectId);
TStageObject::KeyframeMap keyframes;
pegbar->getKeyframes(keyframes);
for (TStageObject::KeyframeMap::iterator it = keyframes.begin();
it != keyframes.end(); ++it) {
assert(pegbar->isKeyframe(it->first));
if (selection->isSelected(it->first, col))
selection->unselect(it->first, col);
else
selection->select(it->first, col);
}
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
} InvertKeyframeSelectionCommand;
//*****************************************************************************
// Kayframe Handles commands
//*****************************************************************************
namespace {
class KeyFrameHandleCommandUndo final : public TUndo {
TStageObjectId m_objId;
int m_rowFirst, m_rowSecond;
TStageObject::Keyframe m_oldKeyframeFirst, m_oldKeyframeSecond,
m_newKeyframeFirst, m_newKeyframeSecond;
public:
KeyFrameHandleCommandUndo(TStageObjectId id, int rowFirst, int rowSecond)
: m_objId(id), m_rowFirst(rowFirst), m_rowSecond(rowSecond) {
TStageObject *pegbar =
TApp::instance()->getCurrentXsheet()->getXsheet()->getStageObject(
m_objId);
assert(pegbar);
m_oldKeyframeFirst = pegbar->getKeyframe(m_rowFirst);
m_oldKeyframeSecond = pegbar->getKeyframe(m_rowSecond);
}
void onAdd() override {
TStageObject *pegbar =
TApp::instance()->getCurrentXsheet()->getXsheet()->getStageObject(
m_objId);
assert(pegbar);
m_newKeyframeFirst = pegbar->getKeyframe(m_rowFirst);
m_newKeyframeSecond = pegbar->getKeyframe(m_rowSecond);
}
void setKeyframes(const TStageObject::Keyframe &k0,
const TStageObject::Keyframe &k1) const {
TStageObject *pegbar =
TApp::instance()->getCurrentXsheet()->getXsheet()->getStageObject(
m_objId);
assert(pegbar);
pegbar->setKeyframeWithoutUndo(m_rowFirst, k0);
pegbar->setKeyframeWithoutUndo(m_rowSecond, k1);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentObject()->notifyObjectIdChanged(false);
}
void redo() const override {
setKeyframes(m_newKeyframeFirst, m_newKeyframeSecond);
}
void undo() const override {
setKeyframes(m_oldKeyframeFirst, m_oldKeyframeSecond);
}
int getSize() const override { return sizeof *this; }
QString getHistoryString() override {
return QObject::tr("Set Keyframe : %1")
.arg(QString::fromStdString(m_objId.toString()));
}
int getHistoryType() override { return HistoryType::Xsheet; }
};
} // namespace
//-----------------------------------------------------------------------------
class SetAccelerationCommand final : public MenuItemHandler {
public:
SetAccelerationCommand() : MenuItemHandler(MI_SetAcceleration) {}
void execute() override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
int row = app->getCurrentFrame()->getFrame();
TStageObjectId objectId = app->getCurrentObject()->getObjectId();
TStageObject *pegbar = xsh->getStageObject(objectId);
if (!pegbar) return;
int r0, r1;
double ease0, ease1;
pegbar->getKeyframeSpan(row, r0, ease0, r1, ease1);
std::unique_ptr<TUndo> undo(
new KeyFrameHandleCommandUndo(objectId, r0, r1));
TStageObject::Keyframe keyframe0 = pegbar->getKeyframe(r0);
TStageObject::Keyframe keyframe1 = pegbar->getKeyframe(r1);
int dr = std::max(r1 - r0, 0);
if (keyframe0.m_easeOut == dr) return;
keyframe0.m_easeOut = dr;
keyframe1.m_easeIn = 0;
// The following TStageObject::setKeyframeWithoutUndo()s could probably
// be left to the undo->redo(). I would have to inquire further. No big deal
// anyway.
pegbar->setKeyframeWithoutUndo(r0, keyframe0);
pegbar->setKeyframeWithoutUndo(r1, keyframe1);
TUndoManager::manager()->add(
undo.release()); // Stores set keyframes in the undo
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentObject()->notifyObjectIdChanged(false);
}
} SetAccelerationCommand;
//-----------------------------------------------------------------------------
class SetDecelerationCommand final : public MenuItemHandler {
public:
SetDecelerationCommand() : MenuItemHandler(MI_SetDeceleration) {}
void execute() override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
int row = app->getCurrentFrame()->getFrame();
TStageObjectId objectId = app->getCurrentObject()->getObjectId();
TStageObject *pegbar = xsh->getStageObject(objectId);
if (!pegbar) return;
int r0, r1;
double ease0, ease1;
pegbar->getKeyframeSpan(row, r0, ease0, r1, ease1);
std::unique_ptr<TUndo> undo(
new KeyFrameHandleCommandUndo(objectId, r0, r1));
TStageObject::Keyframe keyframe0 = pegbar->getKeyframe(r0);
TStageObject::Keyframe keyframe1 = pegbar->getKeyframe(r1);
int dr = std::max(r1 - r0, 0);
if (keyframe1.m_easeIn == dr) return;
keyframe0.m_easeOut = 0;
keyframe1.m_easeIn = dr;
pegbar->setKeyframeWithoutUndo(r0, keyframe0);
pegbar->setKeyframeWithoutUndo(r1, keyframe1);
TUndoManager::manager()->add(undo.release());
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentObject()->notifyObjectIdChanged(false);
}
} SetDecelerationCommand;
//-----------------------------------------------------------------------------
class SetConstantSpeedCommand final : public MenuItemHandler {
public:
SetConstantSpeedCommand() : MenuItemHandler(MI_SetConstantSpeed) {}
void execute() override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
int row = app->getCurrentFrame()->getFrame();
TStageObjectId objectId = app->getCurrentObject()->getObjectId();
TStageObject *pegbar = xsh->getStageObject(objectId);
if (!pegbar) return;
int r0, r1;
double ease0, ease1;
pegbar->getKeyframeSpan(row, r0, ease0, r1, ease1);
KeyFrameHandleCommandUndo *undo =
new KeyFrameHandleCommandUndo(objectId, r0, r1);
TStageObject::Keyframe keyframe0 = pegbar->getKeyframe(r0);
TStageObject::Keyframe keyframe1 = pegbar->getKeyframe(r1);
keyframe0.m_easeOut = 0;
keyframe1.m_easeIn = 0;
pegbar->setKeyframeWithoutUndo(r0, keyframe0);
pegbar->setKeyframeWithoutUndo(r1, keyframe1);
TUndoManager::manager()->add(undo);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentObject()->notifyObjectIdChanged(false);
}
} SetConstantSpeedCommand;
//-----------------------------------------------------------------------------
class ResetArrowCommand final : public MenuItemHandler {
public:
ResetArrowCommand() : MenuItemHandler(MI_ResetInterpolation) {}
void execute() override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
int row = app->getCurrentFrame()->getFrame();
TStageObjectId objectId = app->getCurrentObject()->getObjectId();
TStageObject *pegbar = xsh->getStageObject(objectId);
if (!pegbar) return;
int r0, r1;
double ease0, ease1;
pegbar->getKeyframeSpan(row, r0, ease0, r1, ease1);
KeyFrameHandleCommandUndo *undo =
new KeyFrameHandleCommandUndo(objectId, r0, r1);
TStageObject::Keyframe k0 = pegbar->getKeyframe(r0);
TStageObject::Keyframe k1 = pegbar->getKeyframe(r1);
k0.m_easeOut = (k0.m_easeOut != 0) ? 1 : k0.m_easeOut;
k0.m_easeIn = (k0.m_easeIn != 0) ? 1 : k0.m_easeIn;
k1.m_easeOut = (k1.m_easeOut != 0) ? 1 : k1.m_easeOut;
k1.m_easeIn = (k1.m_easeIn != 0) ? 1 : k1.m_easeIn;
pegbar->setKeyframeWithoutUndo(r0, k0);
pegbar->setKeyframeWithoutUndo(r1, k1);
TUndoManager::manager()->add(undo);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentObject()->notifyObjectIdChanged(false);
}
} ResetArrowCommand;
//-----------------------------------------------------------------------------
// Unify commands for all types of interpolation
class SetInterpolation final : public MenuItemHandler {
TDoubleKeyframe::Type m_type;
public:
SetInterpolation(CommandId cmdId, TDoubleKeyframe::Type type)
: MenuItemHandler(cmdId), m_type(type) {}
void execute() override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
int row = app->getCurrentFrame()->getFrame();
TStageObjectId objectId = app->getCurrentObject()->getObjectId();
TStageObject *pegbar = xsh->getStageObject(objectId);
if (!pegbar) return;
int r0, r1;
double ease0, ease1;
pegbar->getKeyframeSpan(row, r0, ease0, r1, ease1);
KeyFrameHandleCommandUndo *undo =
new KeyFrameHandleCommandUndo(objectId, r0, r1);
TStageObject::Keyframe k0 = pegbar->getKeyframe(r0);
TStageObject::Keyframe k1 = pegbar->getKeyframe(r1);
for (int i = 0; i < TStageObject::T_ChannelCount; i++) {
k0.m_channels[i].m_type = m_type;
k1.m_channels[i].m_prevType = m_type;
}
pegbar->setKeyframeWithoutUndo(r0, k0);
pegbar->setKeyframeWithoutUndo(r1, k1);
TUndoManager::manager()->add(undo);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentObject()->notifyObjectIdChanged(false);
}
} UseLinearInterpolation(MI_UseLinearInterpolation, TDoubleKeyframe::Linear),
UseSpeedInOutInterpolation(MI_UseSpeedInOutInterpolation,
TDoubleKeyframe::SpeedInOut),
UseEaseInOutInterpolation(MI_UseEaseInOutInterpolation,
TDoubleKeyframe::EaseInOut),
UseEaseInOutPctInterpolation(MI_UseEaseInOutPctInterpolation,
TDoubleKeyframe::EaseInOutPercentage),
UseExponentialInterpolation(MI_UseExponentialInterpolation,
TDoubleKeyframe::Exponential),
UseExpressionInterpolation(MI_UseExpressionInterpolation,
TDoubleKeyframe::Expression),
UseFileInterpolation(MI_UseFileInterpolation, TDoubleKeyframe::File),
UseConstantInterpolation(MI_UseConstantInterpolation,
TDoubleKeyframe::Constant);
//===========================================================
// To Be Reworked
//===========================================================
class NewOutputFx final : public MenuItemHandler {
public:
NewOutputFx() : MenuItemHandler(MI_NewOutputFx) {}
void execute() override {
TApp *app = TApp::instance();
TFxCommand::createOutputFx(app->getCurrentXsheet(),
app->getCurrentFx()->getFx());
}
} newOutputFx;
namespace {
int columnsPerPage = 10000;
int rowsPerPage = 10000;
std::vector<std::pair<std::string, string>> infos;
void readParameters() {
infos.clear();
const std::string name("xsheet_html.xml");
TFilePath fp = ToonzFolder::getModuleFile(name);
if (!TFileStatus(fp).doesExist()) return;
try {
TIStream is(fp);
std::string tagName;
if (!is.matchTag(tagName) || tagName != "xsheet_html") return;
while (is.matchTag(tagName)) {
if (tagName == "page") {
std::string s;
s = is.getTagAttribute("rows");
if (s != "" && isInt(s)) rowsPerPage = std::stoi(s);
s = is.getTagAttribute("columns");
if (s != "" && isInt(s)) columnsPerPage = std::stoi(s);
} else if (tagName == "info") {
std::string name = is.getTagAttribute("name");
std::string value = is.getTagAttribute("value");
infos.push_back(std::make_pair(name, value));
} else
return;
}
} catch (...) {
}
}
void copyCss(TFilePath fp) {
const std::string name("xsheet.css");
TFilePath cssFp = fp.getParentDir() + name;
if (TFileStatus(cssFp).doesExist()) return;
TFilePath src = ToonzFolder::getModuleFile(name);
if (TFileStatus(src).doesExist()) {
try {
TSystem::copyFile(cssFp, src);
} catch (...) {
}
}
}
void getAllChildLevels(std::vector<TXshChildLevel *> &levels,
ToonzScene *scene) {
std::set<TXsheet *> visited, toVisit;
TXsheet *xsh = scene->getChildStack()->getTopXsheet();
visited.insert(xsh);
toVisit.insert(xsh);
while (!toVisit.empty()) {
xsh = *toVisit.begin();
toVisit.erase(xsh);
for (int i = 0; i < xsh->getColumnCount(); i++) {
TXshColumn *column = xsh->getColumn(i);
if (!column) continue;
if (TXshCellColumn *cc = column->getCellColumn()) {
int r0 = 0, r1 = -1;
cc->getRange(r0, r1);
if (!cc->isEmpty() && r0 <= r1) {
for (int r = r0; r <= r1; r++) {
TXshCell cell = cc->getCell(r);
if (cell.m_level && cell.m_level->getChildLevel()) {
TXsheet *subxsh = cell.m_level->getChildLevel()->getXsheet();
if (visited.find(subxsh) == visited.end()) {
levels.push_back(cell.m_level->getChildLevel());
visited.insert(subxsh);
toVisit.insert(subxsh);
}
}
}
}
}
}
}
}
} // namespace
struct NumericColumn {
TStageObject *m_pegbar;
TDoubleParamP m_curve;
NumericColumn(TStageObject *pegbar, const TDoubleParamP &curve)
: m_pegbar(pegbar), m_curve(curve){};
};
class XsheetWriter {
TXsheet *m_xsh;
std::map<TXshChildLevel *, int> m_childTable;
std::vector<NumericColumn> m_numericColumns;
public:
XsheetWriter(ToonzScene *scene);
void setXsheet(TXsheet *xsh);
void getSubXsheets(std::vector<TXsheet *> &xsheets);
int getChildLevelIndex(TXshChildLevel *);
void buildNumericColumns();
void buildChildTable(ToonzScene *scene);
void tableCaption(ostream &os);
void columnHeader(ostream &os, int c);
void numericColumnHeader(ostream &os, int c);
void cell(ostream &os, int r, int c);
void numericCell(ostream &os, int r, int c);
void write(ostream &os);
};
XsheetWriter::XsheetWriter(ToonzScene *scene) : m_xsh(0) {
buildChildTable(scene);
setXsheet(scene->getXsheet());
}
void XsheetWriter::setXsheet(TXsheet *xsh) {
m_xsh = xsh;
buildNumericColumns();
}
void XsheetWriter::getSubXsheets(std::vector<TXsheet *> &xsheets) {
std::map<TXshChildLevel *, int>::iterator it;
for (it = m_childTable.begin(); it != m_childTable.end(); ++it)
xsheets.push_back(it->first->getXsheet());
}
int XsheetWriter::getChildLevelIndex(TXshChildLevel *cl) {
std::map<TXshChildLevel *, int>::iterator it;
it = m_childTable.find(cl);
return it == m_childTable.end() ? -1 : it->second;
}
void XsheetWriter::buildNumericColumns() {
m_numericColumns.clear();
TStageObjectTree *pegbarTree = m_xsh->getStageObjectTree();
for (int i = 0; i < pegbarTree->getStageObjectCount(); i++) {
TStageObject *pegbar = pegbarTree->getStageObject(i);
for (int j = 0; j < TStageObject::T_ChannelCount; j++) {
TDoubleParamP curve = pegbar->getParam((TStageObject::Channel)j);
if (curve->hasKeyframes()) {
m_numericColumns.push_back(NumericColumn(pegbar, curve));
}
}
}
}
void XsheetWriter::buildChildTable(ToonzScene *scene) {
std::vector<TXshChildLevel *> levels;
getAllChildLevels(levels, scene);
int i, k = 0;
for (i = 0; i < (int)levels.size(); i++) m_childTable[levels[i]] = k++;
}
void XsheetWriter::tableCaption(ostream &os) { os << "<p>&nbsp;</p>\n"; }
void XsheetWriter::columnHeader(ostream &os, int c) {
os << " <th>" << (c + 1) << "</th>" << endl;
}
void XsheetWriter::numericColumnHeader(ostream &os, int c) {
std::string pegbarName = m_numericColumns[c].m_pegbar->getName();
std::string curveName =
m_numericColumns[c]
.m_curve
->getName(); // toString(TStringTable::translate(m_numericColumns[c].m_curve->getName()));
os << " <th class='" << (c > 0 ? "numeric" : "first_numeric") << "'>";
os << pegbarName << "<br>" << curveName;
os << "</th>" << endl;
}
void XsheetWriter::cell(ostream &os, int r, int c) {
TXshCell prevCell;
if (r > 0) prevCell = m_xsh->getCell(r - 1, c);
TXshCell cell = m_xsh->getCell(r, c);
if (cell.isEmpty())
os << "<td class='emptycell'>&nbsp;</td>";
else {
TXshLevel *level = cell.m_level.getPointer();
std::string type = "levelcell";
if (level->getChildLevel())
type = "subxsheetcell";
else if (level->getZeraryFxLevel())
type = "fxcell";
os << "<td class='" << type << "'>";
bool newPage = r % rowsPerPage == 0;
if (cell.m_level.getPointer() == prevCell.m_level.getPointer() &&
!newPage) {
// stesso livello
if (cell.m_frameId == prevCell.m_frameId) {
// stesso frame
os << "|";
} else {
// frame diverso
os << cell.m_frameId.getNumber();
}
} else {
// livello diverso
std::string levelName;
if (level->getChildLevel()) {
int index = getChildLevelIndex(level->getChildLevel());
levelName = index >= 0 ? "Sub" + std::to_string(index + 1) : "";
} else
levelName = ::to_string(level->getName());
os << levelName << " " << cell.m_frameId.getNumber();
}
os << "</td>" << endl;
}
}
void XsheetWriter::numericCell(ostream &os, int r, int c) {
TDoubleParamP curve = m_numericColumns[c].m_curve;
double v = curve->getValue(r);
TMeasure *measure = curve->getMeasure();
if (measure) {
const TUnit *unit = measure->getCurrentUnit();
if (unit) v = unit->convertTo(v);
}
os << "<td class='" << (c > 0 ? "numeric" : "first_numeric") << "'>";
os << v << "</td>" << endl;
}
void XsheetWriter::write(ostream &os) {
int rowCount = m_xsh->getFrameCount();
int colCount = m_xsh->getColumnCount();
int totColCount = colCount + (int)m_numericColumns.size();
int c0, c1;
c0 = 0;
for (;;) {
c1 = std::min(totColCount, c0 + columnsPerPage) - 1;
int ca0 = 0, ca1 = -1, cb0 = 0, cb1 = -1;
if (c0 < colCount) {
ca0 = c0;
ca1 = std::min(colCount - 1, c1);
}
if (c1 >= colCount) {
cb0 = std::max(c0, colCount);
cb1 = c1;
}
int r0, r1, r, c;
r0 = 0;
for (;;) {
r1 = std::min(rowCount, r0 + rowsPerPage) - 1;
tableCaption(os);
os << "<table>" << endl << "<tr>" << endl;
if (c0 == 0) os << " <th>&nbsp;</th>" << endl;
for (c = c0; c <= c1; c++) {
if (c < colCount)
columnHeader(os, c);
else
numericColumnHeader(os, c - colCount);
}
os << "</tr>" << endl;
for (r = r0; r <= r1; r++) {
os << "<tr>" << endl;
os << " <th class='frame'>" << r + 1 << "</th>" << endl;
for (c = ca0; c <= ca1; c++) cell(os, r, c);
for (c = cb0; c <= cb1; c++) numericCell(os, r, c - colCount);
os << "</tr>" << endl;
}
os << "</table>" << endl;
r0 = r1 + 1;
if (r0 >= rowCount) break;
}
c0 = c1 + 1;
if (c0 >= totColCount) break;
}
}
static void makeHtml(TFilePath fp) {
TApp *app = TApp::instance();
ToonzScene *scene = app->getCurrentScene()->getScene();
std::string sceneName = scene->getScenePath().getName();
std::string projectName = ::to_string(scene->getProject()->getName());
Tofstream os(fp);
os << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" " << endl;
os << " \"http://www.w3.org/TR/html4/strict.dtd\">" << endl;
os << "<html><head>" << endl;
os << "<title>" << sceneName << "</title>" << endl;
os << "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">"
<< endl;
os << "<meta http-equiv=\"Content-Style-Type\" content=\"text/css\">" << endl;
os << "<meta name=\"Generator\" content=\"Toonz 5.2\">" << endl;
os << "<link rel=\"stylesheet\" type=\"text/css\" href=\"xsheet.css\">"
<< endl;
os << "</head><body>" << endl;
os << "<table class='header'>" << endl;
for (int k = 0; k < (int)infos.size(); k++)
os << "<tr><th>" << infos[k].first << ":</th><td>" << infos[k].second
<< "</td></tr>" << endl;
os << "<tr><th>Project:</th><td>" << projectName << "</td></tr>" << endl;
os << "<tr><th>Scene:</th><td>" << sceneName << "</td></tr>" << endl;
os << "<tr><th>Frames:</th><td>" << scene->getFrameCount() << "</td></tr>"
<< endl;
os << "</table>\n";
os << "<p>&nbsp;</p>\n";
os << "<h2>Main Xsheet</h2>\n";
XsheetWriter writer(scene);
writer.write(os);
std::vector<TXsheet *> subXsheets;
writer.getSubXsheets(subXsheets);
int i;
for (i = 0; i < (int)subXsheets.size(); i++) {
os << "<h2>Sub Xsheet " << i + 1 << "</h2>\n";
writer.setXsheet(subXsheets[i]);
writer.write(os);
}
os << "<h2>Levels</h2>\n";
std::vector<TXshLevel *> levels;
scene->getLevelSet()->listLevels(levels);
os << "<dl>" << endl;
for (i = 0; i < (int)levels.size(); i++) {
TXshLevel *level = levels[i];
if (!level->getSimpleLevel()) continue;
os << "<dt>" << ::to_string(level->getName()) << "</dt>" << endl;
os << "<dd>" << endl;
TFilePath fp = level->getPath();
os << ::to_string(fp);
TFilePath fp2 = scene->decodeFilePath(fp);
if (fp != fp2) os << "<br>" << ::to_string(fp2);
os << "</dd>" << endl;
}
os << "</dl>" << endl;
os << "</body></html>" << endl;
}
class PrintXsheetCommand final : public MenuItemHandler {
public:
PrintXsheetCommand() : MenuItemHandler(MI_PrintXsheet) {}
void execute() override;
} printXsheetCommand;
void PrintXsheetCommand::execute() {
ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
TFilePath fp = scene->getScenePath().withType("html");
readParameters();
makeHtml(fp);
copyCss(fp);
QString str =
QObject::tr("The %1 file has been generated").arg(toQString(fp));
DVGui::warning(str);
TSystem::showDocument(fp);
}
//-----------------------------------------------------------------------------
class ToggleXsheetCameraColumnCommand final : public MenuItemHandler {
public:
ToggleXsheetCameraColumnCommand()
: MenuItemHandler(MI_ToggleXsheetCameraColumn) {}
void execute() override {
Preferences *pref = Preferences::instance();
if (!pref->isShowKeyframesOnXsheetCellAreaEnabled()) {
DVGui::warning(
QObject::tr("Please enable \"Show Keyframes on Cell Area\" to show "
"or hide the camera column."));
return;
}
pref->setValue(showXsheetCameraColumn,
!pref->isXsheetCameraColumnVisible());
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
} ToggleXsheetCameraColumnCommand;
//-----------------------------------------------------------------------------
class SetCellMarkCommand final : public MenuItemHandler {
int m_markId;
public:
SetCellMarkCommand(int markId)
: MenuItemHandler(
((std::string)MI_SetCellMark + std::to_string(markId)).c_str())
, m_markId(markId) {}
void execute() override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
int currentRow = app->getCurrentFrame()->getFrame();
int currentColumn = app->getCurrentColumn()->getColumnIndex();
if (!xsh->getColumn(currentColumn)) return;
TXshCellColumn *cellColumn = xsh->getColumn(currentColumn)->getCellColumn();
if (!cellColumn) return;
XsheetGUI::SetCellMarkUndo *undo =
new XsheetGUI::SetCellMarkUndo(currentRow, currentColumn, m_markId);
undo->redo();
TUndoManager::manager()->add(undo);
}
};
SetCellMarkCommand CellMarkCommand0(0);
SetCellMarkCommand CellMarkCommand1(1);
SetCellMarkCommand CellMarkCommand2(2);
SetCellMarkCommand CellMarkCommand3(3);
SetCellMarkCommand CellMarkCommand4(4);
SetCellMarkCommand CellMarkCommand5(5);
SetCellMarkCommand CellMarkCommand6(6);
SetCellMarkCommand CellMarkCommand7(7);
SetCellMarkCommand CellMarkCommand8(8);
SetCellMarkCommand CellMarkCommand9(9);
SetCellMarkCommand CellMarkCommand10(10);
SetCellMarkCommand CellMarkCommand11(11);
//============================================================
class SetStartMarker final : public MenuItemHandler {
public:
SetStartMarker()
: MenuItemHandler(MI_SetStartMarker) {}
void execute() override {
int frame = TApp::instance()->getCurrentFrame()->getFrame();
assert(frame >= 0);
int r0, r1, step;
XsheetGUI::getPlayRange(r0, r1, step);
if (r0 > r1) {
r0 = 0;
r1 = TApp::instance()->getCurrentScene()->getScene()->getFrameCount() - 1;
if (r1 < 1) r1 = 1;
}
r0 = frame;
if (r1 < r0) r1 = r0;
XsheetGUI::setPlayRange(r0, r1, step);
TApp::instance()->getCurrentXsheetViewer()->update();
}
} SetStartMarker;
//============================================================
class SetStopMarker final : public MenuItemHandler {
public:
SetStopMarker()
: MenuItemHandler(MI_SetStopMarker) {}
void execute() override {
int frame = TApp::instance()->getCurrentFrame()->getFrame();
assert(frame >= 0);
int r0, r1, step;
XsheetGUI::getPlayRange(r0, r1, step);
if (r0 > r1) {
r0 = 0;
r1 = TApp::instance()->getCurrentScene()->getScene()->getFrameCount() - 1;
if (r1 < 1) r1 = 1;
}
r1 = frame;
if (r1 < r0) r0 = r1;
r1 -= (step == 0) ? (r1 - r0) : (r1 - r0) % step;
XsheetGUI::setPlayRange(r0, r1, step);
TApp::instance()->getCurrentXsheetViewer()->update();
}
} SetStopMarker;
//============================================================
class ClearMarkers final : public MenuItemHandler {
public:
ClearMarkers()
: MenuItemHandler(MI_ClearMarkers) {}
void execute() override {
int step, r0, r1;
XsheetGUI::getPlayRange(r0, r1, step);
XsheetGUI::setPlayRange(0, -1, step);
TApp::instance()->getCurrentXsheetViewer()->update();
}
} ClearMarkers;
//============================================================
class SetAutoMarkers final : public MenuItemHandler {
public:
SetAutoMarkers()
: MenuItemHandler(MI_SetAutoMarkers) {}
enum Direction { up = 0, down };
int getNonEmptyCell(int row, int column, int lastRow, Direction direction) {
int currentPos = row;
bool exit = false;
while (!exit) {
TXsheet *xsh = TApp::instance()->getCurrentXsheetViewer()->getXsheet();
TXshCell cell = xsh->getCell(currentPos, column);
if (cell.isEmpty() || cell.getFrameId().isStopFrame() ||
(direction == down && currentPos > lastRow)) {
if (direction == down && currentPos > lastRow) {
if (cell.getFrameId().isStopFrame())
currentPos = lastRow;
else if (xsh->isImplicitCell(currentPos, column))
currentPos = (row >= lastRow) ? (row + 1) : currentPos++;
}
(direction == up) ? currentPos++ : currentPos--;
exit = true;
} else
(direction == up) ? currentPos-- : currentPos++;
}
return currentPos;
}
void execute() override {
int col = TApp::instance()->getCurrentColumn()->getColumnIndex();
int row = TApp::instance()->getCurrentFrame()->getFrame();
TXsheet *xsh = TApp::instance()->getCurrentXsheetViewer()->getXsheet();
TXshCell cell = xsh->getCell(row, col);
if (cell.isEmpty() || cell.getFrameId().isStopFrame()) return;
int step, r0, r1;
xsh->getCellRange(col, r0, r1);
int top = getNonEmptyCell(row, col, r0, Direction::up);
int bottom = getNonEmptyCell(row, col, r1, Direction::down);
XsheetGUI::getPlayRange(r0, r1, step);
XsheetGUI::setPlayRange(top, bottom, step);
TApp::instance()->getCurrentXsheetViewer()->update();
}
} SetAutoMarkers;
//============================================================
class PreviewThis final : public MenuItemHandler {
public:
PreviewThis()
: MenuItemHandler(MI_PreviewThis) {}
void execute() override {
int row = TApp::instance()->getCurrentFrame()->getFrame();
assert(row >= 0);
int r0, r1, step;
XsheetGUI::getPlayRange(r0, r1, step);
XsheetGUI::setPlayRange(row, row, step);
TApp::instance()->getCurrentXsheetViewer()->update();
}
} PreviewThis;
//============================================================
class PreviewSelected final : public MenuItemHandler {
public:
PreviewSelected() : MenuItemHandler(MI_PreviewSelected) {}
void execute() override {
TApp *app = TApp::instance();
TSelection *selection = app->getCurrentSelection()->getSelection();
if (!selection) return;
TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(selection);
if (!cellSelection) return;
int row0, col0, row1, col1;
cellSelection->getSelectedCells(row0, col0, row1, col1);
int r0, r1, step;
XsheetGUI::getPlayRange(r0, r1, step);
XsheetGUI::setPlayRange(row0, row1, step);
TApp::instance()->getCurrentXsheetViewer()->update();
}
} PreviewSelected;
//============================================================
class ToggleTaggedFrame final : public MenuItemHandler {
public:
ToggleTaggedFrame() : MenuItemHandler(MI_ToggleTaggedFrame) {}
void execute() override {
TApp *app = TApp::instance();
int frame = app->getCurrentXsheetViewer()->getContextMenuRow();
if (frame < 0) frame = app->getCurrentFrame()->getFrame();
assert(frame >= 0);
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
xsh->toggleTaggedFrame(frame);
NavigationTags *navTags = xsh->getNavigationTags();
CommandManager::instance()->enable(MI_EditTaggedFrame,
navTags->isTagged(frame));
CommandManager::instance()->enable(MI_ClearTags, (navTags->getCount() > 0));
TApp::instance()->getCurrentXsheetViewer()->update();
}
} ToggleTaggedFrame;
//============================================================
class EditTaggedFrame final : public MenuItemHandler {
public:
EditTaggedFrame() : MenuItemHandler(MI_EditTaggedFrame) {}
void execute() override {
TApp *app = TApp::instance();
int frame = app->getCurrentXsheetViewer()->getContextMenuRow();
if (frame < 0) frame = app->getCurrentFrame()->getFrame();
assert(frame >= 0);
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
NavigationTags *tags = xsh->getNavigationTags();
QString label = tags->getTagLabel(frame);
QColor color = tags->getTagColor(frame);
NavTagEditorPopup navTagEditor(frame, label, color);
if (navTagEditor.exec() != QDialog::Accepted) return;
tags->setTagLabel(frame, navTagEditor.getLabel());
tags->setTagColor(frame, navTagEditor.getColor());
}
} EditTaggedFrame;
//============================================================
class NextTaggedFrame final : public MenuItemHandler {
public:
NextTaggedFrame() : MenuItemHandler(MI_NextTaggedFrame) {}
void execute() override {
TApp *app = TApp::instance();
int frame = app->getCurrentFrame()->getFrame();
int col = app->getCurrentColumn()->getColumnIndex();
assert(frame >= 0);
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
NavigationTags *navTags = xsh->getNavigationTags();
int nextFrame = navTags->getNextTag(frame);
if (nextFrame != -1) {
app->getCurrentXsheetViewer()->setCurrentRow(nextFrame);
TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
TApp::instance()->getCurrentSelection()->getSelection());
if (cellSelection)
cellSelection->selectCells(nextFrame, col, nextFrame, col);
}
}
} NextTaggedFrame;
//============================================================
class PrevTaggedFrame final : public MenuItemHandler {
public:
PrevTaggedFrame() : MenuItemHandler(MI_PrevTaggedFrame) {}
void execute() override {
TApp *app = TApp::instance();
int frame = app->getCurrentFrame()->getFrame();
int col = app->getCurrentColumn()->getColumnIndex();
assert(frame >= 0);
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
NavigationTags *navTags = xsh->getNavigationTags();
int prevFrame = navTags->getPrevTag(frame);
if (prevFrame != -1) {
app->getCurrentXsheetViewer()->setCurrentRow(prevFrame);
TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
TApp::instance()->getCurrentSelection()->getSelection());
if (cellSelection)
cellSelection->selectCells(prevFrame, col, prevFrame, col);
}
}
} PrevTaggedFrame;
//============================================================
class ClearTags final : public MenuItemHandler {
public:
ClearTags() : MenuItemHandler(MI_ClearTags) {}
void execute() override {
TApp *app = TApp::instance();
int frame = app->getCurrentFrame()->getFrame();
assert(frame >= 0);
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
NavigationTags *navTags = xsh->getNavigationTags();
navTags->clearTags();
CommandManager::instance()->enable(MI_NextTaggedFrame, false);
CommandManager::instance()->enable(MI_PrevTaggedFrame, false);
CommandManager::instance()->enable(MI_EditTaggedFrame, false);
CommandManager::instance()->enable(MI_ClearTags, false);
TApp::instance()->getCurrentXsheetViewer()->update();
}
} ClearTags;