tahoma2d/toonz/sources/toonz/columncommand.cpp
2023-10-21 09:16:25 -04:00

1575 lines
53 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "columncommand.h"
// Tnz6 includes
#include "menubarcommandids.h"
#include "columnselection.h"
#include "tapp.h"
#include "expressionreferencemanager.h"
// TnzQt includes
#include "toonzqt/tselectionhandle.h"
#include "toonzqt/stageobjectsdata.h"
#include "toonzqt/menubarcommand.h"
#include "toonzqt/dvdialog.h"
#include "historytypes.h"
// TnzLib includes
#include "toonz/txsheethandle.h"
#include "toonz/tfxhandle.h"
#include "toonz/tobjecthandle.h"
#include "toonz/tscenehandle.h"
#include "toonz/tcolumnhandle.h"
#include "toonz/txsheet.h"
#include "toonz/txshcolumn.h"
#include "toonz/tstageobject.h"
#include "toonz/fxdag.h"
#include "toonz/tcolumnfxset.h"
#include "toonz/tcolumnfx.h"
#include "toonz/tstageobjecttree.h"
#include "toonz/txshzeraryfxcolumn.h"
#include "toonz/txshlevelcolumn.h"
#include "toonz/txshchildlevel.h"
#include "toonz/txshcell.h"
#include "toonz/childstack.h"
#include "toonz/toonzscene.h"
#include "toonz/tcamera.h"
#include "toonz/tstageobjectspline.h"
#include "toonz/fxcommand.h"
#include "toonz/preferences.h"
#include "toonz/tstageobjectid.h"
#include "../toonz/xsheetviewer.h"
// TnzBase includes
#include "tfx.h"
#include "tfxattributes.h"
#include "tparamcontainer.h"
#include "tparamset.h"
// TnzCore includes
#include "tstroke.h"
#include "tundo.h"
#include "tools/toolhandle.h"
// Qt includes
#include <QApplication>
#include <QClipboard>
#include <QSet>
#include <memory>
//*************************************************************************
// Local Namespace stuff
//*************************************************************************
namespace {
bool canRemoveFx(const std::set<TFx *> &leaves, TFx *fx) {
bool removeFx = false;
for (int i = 0; i < fx->getInputPortCount(); i++) {
TFx *inputFx = fx->getInputPort(i)->getFx();
if (!inputFx) continue;
if (leaves.count(inputFx) > 0) {
removeFx = true;
continue;
}
if (!canRemoveFx(leaves, inputFx)) return false;
removeFx = true;
}
return removeFx;
}
//-----------------------------------------------------------------------------
void getColumnLinkedFxs(TFx *startFx, TFx *newStartFx,
QMap<TFx *, TFx *> &fxs) {
int i, outputNodeCount = 0;
for (i = 0; i < startFx->getOutputConnectionCount(); i++) {
TFx *outputFx = startFx->getOutputConnection(i)->getOwnerFx();
if (fxs.contains(outputFx)) continue;
if (dynamic_cast<TOutputFx *>(outputFx)) {
outputNodeCount++;
continue;
}
TFxPort *port = newStartFx->getOutputConnection(i - outputNodeCount);
if (!port) continue;
TFx *newOutputFx = port->getOwnerFx();
fxs[outputFx] = newOutputFx;
getColumnLinkedFxs(outputFx, newOutputFx, fxs);
}
}
//------------------------------------------------------
template <typename ParamCont>
void setGrammerToParams(const ParamCont *cont,
const TSyntax::Grammar *grammer) {
for (int p = 0; p != cont->getParamCount(); ++p) {
TParam &param = *cont->getParam(p);
if (TDoubleParam *dp = dynamic_cast<TDoubleParam *>(&param))
dp->setGrammar(grammer);
else if (TParamSet *paramSet = dynamic_cast<TParamSet *>(&param))
setGrammerToParams(paramSet, grammer);
}
}
//-----------------------------------------------------------------------------
void cloneNotColumnLinkedFxsAndOutputsFx(
TXsheet *xsh, TXsheet *newXsh, QMap<TFx *, TFx *> *fxTable = nullptr) {
int columnCount = xsh->getColumnCount();
assert(newXsh->getColumnCount() == columnCount);
// Riempio un mapping (fx del vecchio xsheet -> fx del nuovo xsheet)
QMap<TFx *, TFx *> clonedFxs;
int i;
for (i = 0; i < columnCount; i++) {
TXshColumn *xshColumn = xsh->getColumn(i);
TFx *columnFx = xshColumn->getFx();
TFx *newColumnFx = newXsh->getColumn(i)->getFx();
if (columnFx && newColumnFx)
getColumnLinkedFxs(columnFx, newColumnFx, clonedFxs);
}
FxDag *fxDag = xsh->getFxDag();
FxDag *newFxDag = newXsh->getFxDag();
// aggiungo nel mapping tutti gli effetti che non sono connessi da un cammino
// con una colonna
std::vector<TFx *> fxs, newFxs;
fxDag->getFxs(fxs);
newFxDag->getFxs(newFxs);
QList<TFx *> notColumnLinkedClonedFxs;
if (fxs.size() > newFxs.size()) {
for (i = 0; i < fxs.size(); i++) {
TFx *fx = fxs[i];
if (clonedFxs.contains(fx)) continue;
TFx *newFx = fx->clone(false);
newFxDag->getInternalFxs()->addFx(newFx);
if (fxDag->getTerminalFxs()->containsFx(fx))
newFxDag->getTerminalFxs()->addFx(newFx);
// if the fx has not unique name then let assignUniqueId() set the default
// name
if (newFx->getName() == newFx->getFxId()) newFx->setName(L"");
newFxDag->assignUniqueId(newFx);
clonedFxs[fx] = newFx;
notColumnLinkedClonedFxs.append(newFx);
}
}
// Aggiungo tutti gli outputFx mancanti
for (i = 0; i < fxDag->getOutputFxCount(); i++) {
if (i >= newFxDag->getOutputFxCount()) newFxDag->addOutputFx();
newFxDag->getOutputFx(i)->getAttributes()->setDagNodePos(
fxDag->getOutputFx(i)->getAttributes()->getDagNodePos());
}
// connetto tutti i nuovi effetti aggiunti
for (i = 0; i < notColumnLinkedClonedFxs.size(); i++) {
TFx *newFx = notColumnLinkedClonedFxs[i];
TFx *fx = clonedFxs.key(newFx);
assert(fx);
int j;
for (j = 0; j < fx->getInputPortCount(); j++) {
TFx *inputFx = fx->getInputPort(j)->getFx();
if (!inputFx) continue;
if (dynamic_cast<TXsheetFx *>(inputFx))
newFx->getInputPort(j)->setFx(newFxDag->getXsheetFx());
else {
TFx *newInputFx = clonedFxs[inputFx];
if (!newInputFx) continue;
newFx->getInputPort(j)->setFx(newInputFx);
}
}
for (j = 0; j < fx->getOutputConnectionCount(); j++) {
TFxPort *outputPort = fx->getOutputConnection(j);
TFx *outputFx = outputPort->getOwnerFx();
int k, index = 0;
if (TOutputFx *outFx = dynamic_cast<TOutputFx *>(outputFx)) continue;
index = 0;
for (k = 0; k < outputFx->getInputPortCount(); k++) {
if (outputFx->getInputPort(k) == outputPort) index = k;
}
TFx *newOutputFx = clonedFxs[outputFx];
newOutputFx->getInputPort(index)->setFx(newFx);
}
}
// Connetto tutti gli output
for (i = 0; i < fxDag->getOutputFxCount(); i++) {
TOutputFx *outputFx = fxDag->getOutputFx(i);
TOutputFx *newOutputFx = newFxDag->getOutputFx(i);
TFx *inputFx = outputFx->getInputPort(0)->getFx();
if (!inputFx) continue;
if (dynamic_cast<TXsheetFx *>(inputFx))
newOutputFx->getInputPort(0)->setFx(newFxDag->getXsheetFx());
else if (TLevelColumnFx *levelFx =
dynamic_cast<TLevelColumnFx *>(inputFx)) {
int index = levelFx->getColumnIndex();
TFx *newInputFx = newXsh->getColumn(index)->getFx();
newOutputFx->getInputPort(0)->setFx(newInputFx);
} else if (TZeraryColumnFx *zeraryFx =
dynamic_cast<TZeraryColumnFx *>(inputFx)) {
int index = zeraryFx->getColumnIndex();
TFx *newInputFx = newXsh->getColumn(index)->getFx();
newOutputFx->getInputPort(0)->setFx(newInputFx);
} else {
TFx *newInputFx = clonedFxs[inputFx];
newOutputFx->getInputPort(0)->setFx(newInputFx);
}
}
// reset grammers for all parameters of cloned fxs
// or they fails to refer to other parameters via expression
TSyntax::Grammar *grammer = newXsh->getStageObjectTree()->getGrammar();
QMap<TFx *, TFx *>::const_iterator it;
for (it = clonedFxs.constBegin(); it != clonedFxs.constEnd(); ++it) {
setGrammerToParams(it.value()->getParams(), grammer);
// register to the fx table for expression management
if (fxTable) fxTable->insert(it.key(), it.value());
}
}
//-----------------------------------------------------------------------------
void cloneXsheetTStageObjectTree(TXsheet *xsh, TXsheet *newXsh) {
std::set<TStageObjectId> pegbarIds;
TStageObjectTree *tree = xsh->getStageObjectTree();
TStageObjectTree *newTree = newXsh->getStageObjectTree();
// Ricostruisco l'intero albero
int i;
for (i = 0; i < tree->getStageObjectCount(); i++) {
TStageObject *stageObject = tree->getStageObject(i);
TStageObjectId id = stageObject->getId();
TStageObject *newStageObject = newXsh->getStageObject(id);
if (id.isCamera()) {
TCamera *camera = stageObject->getCamera();
*newStageObject->getCamera() = *camera;
}
// Gestisco le spline delle colonne in modo differente perche' sono state
// gia' settate.
TStageObjectSpline *spline = newStageObject->getSpline();
TStageObjectParams *data = stageObject->getParams();
newStageObject->assignParams(data);
delete data;
newStageObject->setParent(xsh->getStageObjectParent(id));
if (id.isColumn() && spline) newStageObject->setSpline(spline);
// Gestisco le spline che non sono di colonna (spline di camera)
TStageObjectSpline *oldSpline = stageObject->getSpline();
if (oldSpline && !id.isColumn()) {
TStageObjectSpline *newSpline = newTree->createSpline();
newSpline->addRef();
newSpline->setStroke(new TStroke(*oldSpline->getStroke()));
newStageObject->setSpline(newSpline);
}
}
}
//-----------------------------------------------------------------------------
bool pasteColumnsWithoutUndo(std::set<int> *indices, bool doClone,
const StageObjectsData *data) {
if (!data)
data = dynamic_cast<const StageObjectsData *>(
QApplication::clipboard()->mimeData());
if (!data) return false;
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
// Check Circular References
if (data->checkCircularReferences(xsh)) {
DVGui::error(
QObject::tr("It is not possible to paste the columns: there is a "
"circular reference."));
return false;
}
std::list<int> restoredSplineIds;
data->restoreObjects(*indices, restoredSplineIds, xsh,
doClone ? StageObjectsData::eDoClone : 0,
TConst::nowhere);
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentObject()->notifyObjectIdSwitched();
return true;
}
//-----------------------------------------------------------------------------
void deleteColumnsWithoutUndo(std::set<int> *indices,
bool onlyColumns = false) {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
// non posso buttare la colonna di camera
indices->erase(-1);
// cerco le fx da eliminare. prima trovo le foglie
/*-- leavesに消されるカラムのColumnFxを格納していく --*/
std::set<TFx *> leaves;
std::set<int>::iterator it2;
for (it2 = indices->begin(); it2 != indices->end(); ++it2) {
int index = *it2;
if (index < 0) continue;
TXshColumn *column = xsh->getColumn(index);
if (!column) continue;
TFx *fx = column->getFx();
if (fx) leaves.insert(fx);
}
// e poi ...
/*-- カラムを消した時、一緒に消してもよいFxを格納していく --*/
std::vector<TFx *> fxsToKill;
TFxSet *fxSet = xsh->getFxDag()->getInternalFxs();
int i;
for (i = 0; i < fxSet->getFxCount(); i++) {
TFx *fx = fxSet->getFx(i);
if (canRemoveFx(leaves, fx)) fxsToKill.push_back(fx);
}
bool soundColumnRemoved = false;
int firstIndex = *indices->begin();
std::set<int>::reverse_iterator it;
for (it = indices->rbegin(); it != indices->rend(); ++it) {
TXshColumn *column = xsh->getColumn(*it);
if (column && column->getSoundColumn()) soundColumnRemoved = true;
if (column && column->getFx()) app->getCurrentFx()->setFx(0);
xsh->removeColumn(*it);
}
for (i = 0; i < (int)fxsToKill.size() && !onlyColumns; i++) {
TFx *fx = fxsToKill[i];
if (fx == app->getCurrentFx()->getFx()) app->getCurrentFx()->setFx(0);
if (fx->getLinkedFx() != fx) fx->unlinkParams();
int j, outputPortCount = fx->getOutputConnectionCount();
for (j = outputPortCount - 1; j >= 0; j--) {
TFxPort *port = fx->getOutputConnection(j);
TFx *outFx = port->getOwnerFx();
if (TZeraryFx *zeraryFx = dynamic_cast<TZeraryFx *>(outFx))
outFx = zeraryFx->getColumnFx();
std::vector<TFx *>::iterator it =
std::find(fxsToKill.begin(), fxsToKill.end(), outFx);
std::set<TFx *>::iterator it2 =
std::find(leaves.begin(), leaves.end(), outFx);
if (it == fxsToKill.end() && it2 == leaves.end()) port->setFx(0);
}
fxSet->removeFx(fx);
xsh->getFxDag()->getTerminalFxs()->removeFx(fx);
}
xsh->updateFrameCount();
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentObject()->notifyObjectIdSwitched();
if (soundColumnRemoved) {
app->getCurrentScene()->notifyCastChange();
app->getCurrentXsheet()->notifyXsheetSoundChanged();
}
indices->clear();
}
//-----------------------------------------------------------------------------
void resetColumns(
const QMimeData *mimeData, std::set<int> *indices,
const QMap<TFxPort *, TFx *> &columnFxLinks,
const QMap<TStageObjectId, TStageObjectId> &columnObjParents,
const QMap<TStageObjectId, QList<TStageObjectId>> &columnObjChildren) {
const StageObjectsData *data =
dynamic_cast<const StageObjectsData *>(mimeData);
if (!data) return;
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
std::list<int> restoredSplineIds;
data->restoreObjects(*indices, restoredSplineIds, xsh, 0);
QMap<TFxPort *, TFx *>::const_iterator it;
for (it = columnFxLinks.begin(); it != columnFxLinks.end(); it++)
it.key()->setFx(it.value());
// Devo rimettere le stesse connessioni tra gli stage object
QMap<TStageObjectId, TStageObjectId>::const_iterator it2;
for (it2 = columnObjParents.begin(); it2 != columnObjParents.end();
it2++) { // Parents
TStageObject *obj = xsh->getStageObject(it2.key());
if (obj) {
obj->setParent(it2.value());
}
}
QMap<TStageObjectId, QList<TStageObjectId>>::const_iterator it3;
for (it3 = columnObjChildren.begin(); it3 != columnObjChildren.end();
it3++) { // Children
QList<TStageObjectId> children = it3.value();
int i;
for (i = 0; i < children.size(); i++) {
TStageObject *child = xsh->getStageObject(children[i]);
if (child) {
child->setParent(it3.key());
}
}
}
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentObject()->notifyObjectIdSwitched();
}
//-----------------------------------------------------------------------------
// Clones the TXshChildLevel, but NOT any further nested TXshChildLevel inside
// it.
TXshChildLevel *cloneChildLevel(TXshChildLevel *cl) {
TXshChildLevel *newLevel = new TXshChildLevel(cl->getName());
newLevel->setScene(cl->getScene());
TXsheet *childXsh = cl->getXsheet(), *newChildXsh = newLevel->getXsheet();
std::set<int> indices;
for (int i = 0; i < childXsh->getColumnCount(); i++) indices.insert(i);
StageObjectsData *data = new StageObjectsData();
data->storeColumns(indices, childXsh, 0);
data->storeColumnFxs(indices, childXsh, 0);
std::list<int> restoredSplineIds;
data->restoreObjects(indices, restoredSplineIds, newChildXsh,
StageObjectsData::eDoClone);
delete data;
cloneNotColumnLinkedFxsAndOutputsFx(childXsh, newChildXsh);
cloneXsheetTStageObjectTree(childXsh, newChildXsh);
return newLevel;
}
//-----------------------------------------------------------------------------
void cloneSubXsheets(TXsheet *xsh) {
std::map<TXsheet *, TXshChildLevel *> visited;
std::set<TXsheet *> toVisit;
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();
std::map<TXsheet *, TXshChildLevel *>::iterator it =
visited.find(subxsh);
if (it == visited.end()) {
it = visited
.insert(std::make_pair(
subxsh,
cloneChildLevel(cell.m_level->getChildLevel())))
.first;
toVisit.insert(subxsh);
}
assert(it != visited.end());
cell.m_level = it->second;
cc->setCell(r, cell);
}
}
}
}
}
}
//=============================================================================
// PasteColumnsUndo
//-----------------------------------------------------------------------------
class PasteColumnsUndo : public TUndo {
std::set<int> m_indices;
StageObjectsData *m_data;
QMap<TFxPort *, TFx *> m_columnLinks;
public:
PasteColumnsUndo(std::set<int> indices) : m_indices(indices) {
TApp *app = TApp::instance();
m_data = new StageObjectsData();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
m_data->storeColumns(indices, xsh, 0);
m_data->storeColumnFxs(indices, xsh, 0);
std::set<int>::iterator it;
for (it = m_indices.begin(); it != m_indices.end(); it++) {
TXshColumn *column = xsh->getColumn(*it);
if (!column || !column->getFx()) continue;
TFx *fx = column->getFx();
int i;
for (i = 0; i < fx->getOutputConnectionCount(); i++)
m_columnLinks[fx->getOutputConnection(i)] = fx;
}
}
~PasteColumnsUndo() { delete m_data; }
void undo() const override {
std::set<int> indices;
std::set<int>::const_iterator indicesIt = m_indices.begin();
while (indicesIt != m_indices.end()) indices.insert(*indicesIt++);
deleteColumnsWithoutUndo(&indices);
}
void redo() const override {
std::set<int> indices;
std::set<int>::const_iterator indicesIt = m_indices.begin();
while (indicesIt != m_indices.end()) indices.insert(*indicesIt++);
resetColumns(m_data, &indices, m_columnLinks,
QMap<TStageObjectId, TStageObjectId>(),
QMap<TStageObjectId, QList<TStageObjectId>>());
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override {
QString str = QObject::tr("Paste Column : ");
std::set<int>::iterator it;
for (it = m_indices.begin(); it != m_indices.end(); it++) {
if (it != m_indices.begin()) str += QString(", ");
str += QString("Col%1").arg(QString::number((*it) + 1));
}
return str;
}
int getHistoryType() override { return HistoryType::Xsheet; }
};
//=============================================================================
// DeleteColumnsUndo
//-----------------------------------------------------------------------------
class DeleteColumnsUndo final : public TUndo {
std::set<int> m_indices;
QMap<TFxPort *, TFx *> m_columnFxLinks;
QMap<TStageObjectId, QList<TStageObjectId>> m_columnObjChildren;
QMap<TStageObjectId, TStageObjectId> m_columnObjParents;
mutable std::unique_ptr<StageObjectsData> m_data;
bool m_onlyColumns;
public:
DeleteColumnsUndo(const std::set<int> &indices, bool onlyColumns)
: m_indices(indices)
, m_data(new StageObjectsData)
, m_onlyColumns(onlyColumns) {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
m_data->storeColumns(m_indices, xsh, 0);
m_data->storeColumnFxs(m_indices, xsh, 0);
std::set<int>::iterator it;
for (it = m_indices.begin(); it != m_indices.end(); ++it) {
TXshColumn *column = xsh->getColumn(*it);
if (!column) continue;
// Store output links
if (TFx *fx = column->getFx()) {
for (int i = 0; i < fx->getOutputConnectionCount(); ++i) {
// Only links whose output-connected fx is in the xsheet (ie not
// temporary)
// are stored. Temporaries may be erased without an undo
// notification...
TFx *outFx = fx->getOutputConnection(i)->getOwnerFx();
if (xsh->getFxDag()->getInternalFxs()->containsFx(outFx))
m_columnFxLinks[fx->getOutputConnection(i)] = fx;
}
}
// Store TStageObject children
int pegbarsCount = xsh->getStageObjectTree()->getStageObjectCount();
TStageObjectId id = TStageObjectId::ColumnId(*it);
TStageObject *pegbar = xsh->getStageObject(id);
for (int i = 0; i < pegbarsCount; ++i) {
TStageObject *other = xsh->getStageObjectTree()->getStageObject(i);
if (other == pegbar) continue;
if (other->getParent() == id) {
// other->setParent(pegbar->getParent());
m_columnObjChildren[id].append(other->getId());
}
}
// Mi salvo il parent
m_columnObjParents[id] = pegbar->getParent();
}
}
void redo() const override {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
assert(!m_data.get());
m_data.reset(new StageObjectsData);
m_data->storeColumns(m_indices, xsh, 0);
m_data->storeColumnFxs(m_indices, xsh, 0);
std::set<int> indices = m_indices;
deleteColumnsWithoutUndo(&indices, m_onlyColumns);
}
void undo() const override {
std::set<int> indices = m_indices;
resetColumns(m_data.get(), &indices, m_columnFxLinks, m_columnObjParents,
m_columnObjChildren);
m_data.reset();
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override {
QString str = QObject::tr("Delete Column : ");
std::set<int>::iterator it;
for (it = m_indices.begin(); it != m_indices.end(); it++) {
if (it != m_indices.begin()) str += QString(", ");
str += QString("Col%1").arg(QString::number((*it) + 1));
}
return str;
}
int getHistoryType() override { return HistoryType::Xsheet; }
};
} // namespace
//*************************************************************************
// Column Command Undo
//*************************************************************************
class ColumnCommandUndo : public TUndo {
public:
virtual ~ColumnCommandUndo() {}
virtual bool isConsistent() const = 0;
protected:
};
//*************************************************************************
// Insert Empty Columns Command
//*************************************************************************
class InsertEmptyColumnsUndo final : public ColumnCommandUndo {
std::vector<std::pair<int, int>> m_columnBlocks;
public:
InsertEmptyColumnsUndo(const std::vector<int> &indices, bool insertAfter) {
initialize(indices, insertAfter);
}
bool isConsistent() const override { return true; }
void redo() const override;
void undo() const override;
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override {
QString str = QObject::tr("Insert Column : ");
std::vector<std::pair<int, int>>::iterator it;
for (it = m_columnBlocks.begin(); it != m_columnBlocks.end(); it++) {
if (it != m_columnBlocks.begin()) str += QString(", ");
str += QString("Col%1").arg(QString::number((*it).first + 1));
}
return str;
}
int getHistoryType() override { return HistoryType::Xsheet; }
private:
void initialize(const std::vector<int> &indices, bool insertAfter = false);
};
//------------------------------------------------------
void InsertEmptyColumnsUndo::initialize(const std::vector<int> &indices,
bool insertAfter) {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
std::vector<int>::const_iterator cb, ce, cEnd = indices.end();
for (cb = indices.begin(); cb != cEnd; cb = ce) // As long as block end is ok
{
int c = *cb; // Find a corresponding block start
for (ce = cb, ++ce, ++c;
(ce != cEnd) && (*ce == c);) // by iterating as long as the next
++ce, ++c; // column index is the previous one + 1
int insertAt = (insertAfter ? c : *cb);
m_columnBlocks.push_back(std::make_pair(insertAt, c - *cb));
}
assert(!m_columnBlocks.empty());
}
//------------------------------------------------------
void InsertEmptyColumnsUndo::redo() const {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
// If this is the very first column, add one now since there is always
// 1 visible on the screen but its not actually there yet.
if (!xsh->getColumnCount()) xsh->insertColumn(0);
std::vector<std::pair<int, int>>::const_reverse_iterator bt,
bEnd = m_columnBlocks.rend();
for (bt = m_columnBlocks.rbegin(); bt != bEnd; ++bt)
for (int n = 0; n != bt->second; ++n) xsh->insertColumn(bt->first);
app->getCurrentScene()->setDirtyFlag(true);
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentObject()->notifyObjectIdSwitched();
}
//------------------------------------------------------
void InsertEmptyColumnsUndo::undo() const {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
std::vector<std::pair<int, int>>::const_iterator bt,
bEnd = m_columnBlocks.end();
for (bt = m_columnBlocks.begin(); bt != bEnd; ++bt)
for (int n = 0; n != bt->second; ++n) xsh->removeColumn(bt->first);
app->getCurrentScene()->setDirtyFlag(true);
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentObject()->notifyObjectIdSwitched();
}
//======================================================
void ColumnCmd::insertEmptyColumns(const std::set<int> &indices,
bool insertAfter) {
// Filter out all less than 0 indices (in particular, the 'camera' column
// in the Toonz derivative product "Tab")
std::vector<int> positiveIndices(indices.begin(), indices.end());
if (positiveIndices[0] < 0) {
if (!insertAfter) return;
// If inserting after on camera column, change it to insert before on column
// 1
positiveIndices[0] = 0;
insertAfter = false;
}
if (positiveIndices.empty()) return;
std::unique_ptr<ColumnCommandUndo> undo(
new InsertEmptyColumnsUndo(positiveIndices, insertAfter));
if (undo->isConsistent()) {
undo->redo();
TUndoManager::manager()->add(undo.release());
}
}
//======================================================
void ColumnCmd::insertEmptyColumn(int index) {
std::set<int> ii;
ii.insert(index);
ColumnCmd::insertEmptyColumns(ii);
}
//*************************************************************************
// Copy Columns Command
//*************************************************************************
static void copyColumns_internal(const std::set<int> &indices) {
assert(!indices.empty());
StageObjectsData *data = new StageObjectsData;
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
data->storeColumns(
indices, xsh,
StageObjectsData::eDoClone | StageObjectsData::eResetFxDagPositions);
data->storeColumnFxs(
indices, xsh,
StageObjectsData::eDoClone | StageObjectsData::eResetFxDagPositions);
QApplication::clipboard()->setMimeData(data);
}
//------------------------------------------------------
void ColumnCmd::copyColumns(const std::set<int> &indices) {
if (indices.empty()) return;
copyColumns_internal(indices);
}
//*************************************************************************
// Paste Columns Command
//*************************************************************************
void ColumnCmd::pasteColumns(std::set<int> &indices,
const StageObjectsData *data) {
// indices will be updated here by inserted column ids after pasting
bool isPaste = pasteColumnsWithoutUndo(&indices, true, data);
if (!isPaste) return;
TUndoManager::manager()->add(new PasteColumnsUndo(indices));
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//*************************************************************************
// Delete Columns Command
//*************************************************************************
void ColumnCmd::deleteColumns(std::set<int> &indices, bool onlyColumns,
bool withoutUndo) {
indices.erase(-1); // Ignore camera column
if (indices.empty()) return;
if (!withoutUndo)
TUndoManager::manager()->add(new DeleteColumnsUndo(indices, onlyColumns));
deleteColumnsWithoutUndo(&indices, onlyColumns);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//=============================================================================
// deleteColumn
//=============================================================================
void ColumnCmd::deleteColumn(int index, bool onlyColumns) {
std::set<int> ii;
ii.insert(index);
ColumnCmd::deleteColumns(ii, onlyColumns, false);
}
//=============================================================================
// cutColumns
//-----------------------------------------------------------------------------
static void cutColumnsWithoutUndo(std::set<int> *indices) {
copyColumns_internal(*indices);
deleteColumnsWithoutUndo(indices);
}
void ColumnCmd::cutColumns(std::set<int> &indices) {
if (indices.empty()) return;
TUndoManager::manager()->add(new DeleteColumnsUndo(indices, false));
cutColumnsWithoutUndo(&indices);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//=============================================================================
// Undo Resequence
//=============================================================================
class ResequenceUndo final : public TUndo {
int m_index;
int m_r0;
std::vector<TFrameId> m_oldFrames;
int m_newFramesCount;
public:
ResequenceUndo(int col, int count)
: m_index(col), m_r0(0), m_newFramesCount(count) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int r0, r1;
xsh->getCellRange(col, r0, r1);
m_r0 = r0;
assert(r0 <= r1);
if (r0 > r1) return;
for (int r = r0; r <= r1; r++) {
TXshCell cell = xsh->getCell(r, col);
assert(cell.isEmpty() || cell.m_level->getChildLevel());
m_oldFrames.push_back(cell.m_frameId);
}
}
void undo() const override {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int r0, r1;
xsh->getCellRange(m_index, r0, r1);
assert(r0 <= r1);
if (r0 > r1) return;
TXshCell cell = xsh->getCell(r0, m_index);
assert(!cell.isEmpty());
assert(cell.m_level->getChildLevel());
xsh->clearCells(r0, m_index, r1 - r0 + 1);
for (int i = 0; i < (int)m_oldFrames.size(); i++) {
TFrameId fid = m_oldFrames[i];
if (fid != TFrameId::EMPTY_FRAME) {
cell.m_frameId = fid;
xsh->setCell(m_r0 + i, m_index, cell);
}
}
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const override {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int r0, r1;
xsh->getCellRange(m_index, r0, r1);
assert(r0 <= r1);
if (r0 > r1) return;
TXshCell cell = xsh->getCell(r0, m_index);
assert(!cell.isEmpty());
assert(cell.m_level->getChildLevel());
xsh->clearCells(r0, m_index, r1 - r0 + 1);
for (int i = 0; i < m_newFramesCount; i++) {
cell.m_frameId = TFrameId(i + 1);
xsh->setCell(m_r0 + i, m_index, cell);
}
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
int getSize() const override {
return sizeof(*this) + m_oldFrames.size() * sizeof(TFrameId);
}
QString getHistoryString() override {
return QObject::tr("Resequence : Col%1").arg(QString::number(m_index + 1));
}
int getHistoryType() override { return HistoryType::Xsheet; }
};
//=============================================================================
// Resequence
//=============================================================================
bool ColumnCmd::canResequence(int index) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
TXshColumn *column = xsh->getColumn(index);
if (!column) return false;
TXshLevelColumn *lcolumn = column->getLevelColumn();
if (!lcolumn) return false;
int r0 = 0, r1 = -1;
if (lcolumn->getRange(r0, r1) == 0) return false;
assert(r0 <= r1);
TXshCell cell = lcolumn->getCell(r0);
assert(!cell.isEmpty());
TXshLevel *xl = cell.m_level->getChildLevel();
if (!xl) return false;
for (int r = r0 + 1; r <= r1; r++) {
cell = lcolumn->getCell(r);
if (cell.isEmpty()) continue;
if (cell.m_level.getPointer() != xl) return false;
}
return true;
}
//-----------------------------------------------------------------------------
void ColumnCmd::resequence(int index) {
if (!canResequence(index)) return;
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
TXshColumn *column = xsh->getColumn(index);
assert(column);
TXshLevelColumn *lcolumn = column->getLevelColumn();
assert(lcolumn);
int r0 = 0, r1 = -1;
lcolumn->getRange(r0, r1);
assert(r0 <= r1);
TXshCell cell = lcolumn->getCell(r0);
assert(!cell.isEmpty());
TXshChildLevel *xl = cell.m_level->getChildLevel();
assert(xl);
TXsheet *childXsh = xl->getXsheet();
int frameCount = childXsh->getFrameCount();
if (frameCount < 1) frameCount = 1;
TUndoManager::manager()->add(new ResequenceUndo(index, frameCount));
lcolumn->clearCells(r0, r1 - r0 + 1);
for (int i = 0; i < frameCount; i++) {
cell.m_frameId = TFrameId(i + 1);
lcolumn->setCell(r0 + i, cell);
}
xsh->updateFrameCount();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
//=============================================================================
// Undo cloneChild
//=============================================================================
class CloneChildUndo final : public TUndo {
TXshChildLevelP m_childLevel;
int m_columnIndex;
public:
CloneChildUndo(TXshChildLevel *childLevel, int columnIndex)
: m_childLevel(childLevel), m_columnIndex(columnIndex) {}
void undo() const override {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
xsh->removeColumn(m_columnIndex);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const override {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
xsh->insertColumn(m_columnIndex);
int frameCount = m_childLevel->getXsheet()->getFrameCount();
if (frameCount < 1) frameCount = 1;
for (int r = 0; r < frameCount; r++)
xsh->setCell(r, m_columnIndex,
TXshCell(m_childLevel.getPointer(), TFrameId(r + 1)));
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
int getSize() const override {
// bisognerebbe tener conto della dimensione del sottoxsheet
return sizeof(*this) + 100;
}
QString getHistoryString() override {
return QObject::tr("Clone Sub-xsheet : Col%1")
.arg(QString::number(m_columnIndex + 1));
}
int getHistoryType() override { return HistoryType::Xsheet; }
};
//=============================================================================
// cloneChild
//=============================================================================
void ColumnCmd::cloneChild(int index) {
if (!canResequence(index)) return;
/*-- カラムを取得 --*/
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
TXshColumn *column = xsh->getColumn(index);
assert(column);
// get the subxsheet to clone (childLevel, childXsh)
/*-- SubXsheetレベルを取得 --*/
TXshLevelColumn *lcolumn = column->getLevelColumn();
assert(lcolumn);
int r0 = 0, r1 = -1;
lcolumn->getRange(r0, r1);
assert(r0 <= r1);
/*-- SubXsheetの一番頭のセル --*/
TXshCell cell = lcolumn->getCell(r0);
assert(!cell.isEmpty());
/*- cell内に格納されているLevelを取得 -*/
TXshChildLevel *childLevel = cell.m_level->getChildLevel();
assert(childLevel);
/*- SubXsheetのXsheetを取得 -*/
TXsheet *childXsh = childLevel->getXsheet();
// insert a new empty column
/*- 隣に空きColumnをInsertしてCloneに備える -*/
int newColumnIndex = index + 1;
xsh->insertColumn(newColumnIndex);
// create a subxsheet (newChildLevel, newChildXsh)
ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
ChildStack *childStack = scene->getChildStack();
TXshChildLevel *newChildLevel = childStack->createChild(0, newColumnIndex);
TXsheet *newChildXsh = newChildLevel->getXsheet();
// copy columns.
std::set<int> indices;
for (int i = 0; i < childXsh->getColumnCount(); i++) indices.insert(i);
StageObjectsData *data = new StageObjectsData();
data->storeColumns(indices, childXsh, 0);
data->storeColumnFxs(indices, childXsh, 0);
std::list<int> restoredSplineIds;
QMap<TStageObjectId, TStageObjectId> idTable;
QMap<TFx *, TFx *> fxTable;
data->restoreObjects(indices, restoredSplineIds, newChildXsh,
StageObjectsData::eDoClone, idTable, fxTable);
delete data;
cloneNotColumnLinkedFxsAndOutputsFx(childXsh, newChildXsh, &fxTable);
cloneXsheetTStageObjectTree(childXsh, newChildXsh);
/*--以下は、Clone SubXsheet
するときに、SubXsheet内にある子SubXsheetをクローンする関数
クローンされた中にある子SubXsheetは、同じもので良いので、スキップする --*/
// cloneSubXsheets(newChildXsh);
/*-- XSheetードのFxSchematicでのDagNodePosを再現
FxやColumnードの位置の再現は上のsetColumnで行っている
--*/
newChildXsh->getFxDag()->getXsheetFx()->getAttributes()->setDagNodePos(
childXsh->getFxDag()->getXsheetFx()->getAttributes()->getDagNodePos());
ExpressionReferenceManager::instance()->refreshXsheetRefInfo(newChildXsh);
ExpressionReferenceManager::instance()->transferReference(
childXsh, newChildXsh, idTable, fxTable);
newChildXsh->updateFrameCount();
/*-- TXshChildLevel作成時にsetCellした1つ目のセルを消去 --*/
xsh->removeCells(0, newColumnIndex);
/*-- CloneしたColumnのセル番号順を再現 --*/
for (int r = r0; r <= r1; r++) {
TXshCell cell = lcolumn->getCell(r);
if (cell.isEmpty()) continue;
cell.m_level = newChildLevel;
xsh->setCell(r, newColumnIndex, cell);
}
TStageObjectId currentObjectId =
TApp::instance()->getCurrentObject()->getObjectId();
xsh->getStageObject(TStageObjectId::ColumnId(newColumnIndex))
->setParent(xsh->getStageObjectParent(currentObjectId));
xsh->updateFrameCount();
TUndoManager::manager()->add(
new CloneChildUndo(newChildLevel, newColumnIndex));
// notify changes
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
//=============================================================================
// copyColumn
//=============================================================================
void ColumnCmd::copyColumn(int dstIndex, int srcIndex) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
// columns[srcIndex] => data
StageObjectsData *data = new StageObjectsData();
std::set<int> ii;
ii.insert(srcIndex);
data->storeColumns(ii, xsh, StageObjectsData::eDoClone);
data->storeColumnFxs(ii, xsh, StageObjectsData::eDoClone);
// data => columns[dstIndex]
ii.clear();
ii.insert(dstIndex);
ColumnCmd::pasteColumns(ii, data);
delete data;
}
//=============================================================================
// Undo Clear Cells
//=============================================================================
class ClearColumnCellsUndo final : public TUndo {
int m_col;
int m_r0;
std::vector<TXshCell> m_oldFrames;
public:
ClearColumnCellsUndo(int col) : m_col(col), m_r0(0) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int r0, r1;
xsh->getCellRange(col, r0, r1);
m_r0 = r0;
if (r0 > r1) return;
for (int r = r0; r <= r1; r++) {
m_oldFrames.push_back(xsh->getCell(r, col));
}
}
void undo() const override {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
for (int i = 0; i < (int)m_oldFrames.size(); i++) {
xsh->setCell(m_r0 + i, m_col, m_oldFrames[i]);
}
xsh->updateFrameCount();
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const override {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int r0, r1;
xsh->getCellRange(m_col, r0, r1);
if (r0 <= r1) {
xsh->clearCells(r0, m_col, r1 - r0 + 1);
xsh->updateFrameCount();
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
}
int getSize() const override {
return sizeof(*this) + m_oldFrames.size() * sizeof(m_oldFrames[0]);
}
QString getHistoryString() override {
return QObject::tr("Clear Cells : Col%1").arg(QString::number(m_col + 1));
}
int getHistoryType() override { return HistoryType::Xsheet; }
};
//=============================================================================
// clearCells
//=============================================================================
void ColumnCmd::clearCells(int index) {
ClearColumnCellsUndo *undo = new ClearColumnCellsUndo(index);
undo->redo();
TUndoManager::manager()->add(undo);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
}
//=============================================================================
// checkExpressionReferences
//=============================================================================
// - If onlyColumns is true, it means that only columns with specified indices
// will be removed.
// - If onlyColumns is false, it means that the relevant pegbars will be removed
// as well (when collapsing columns).
// - Note that relevant Fxs will be removed / collapsed regardless of
// onlyColumns.
// - When checkInvert is true, check references both side from the main xsheet
// and the child xsheet when collapsing columns.
bool ColumnCmd::checkExpressionReferences(const std::set<int> &indices,
bool onlyColumns, bool checkInvert) {
if (!Preferences::instance()->isModifyExpressionOnMovingReferencesEnabled())
return true;
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
// check if fxs will be deleted
QSet<int> colIdsToBeDeleted;
QSet<TFx *> fxsToBeDeleted;
// store column fxs to be deleted
std::set<TFx *> leaves;
for (auto index : indices) {
if (index < 0) continue;
TXshColumn *column = xsh->getColumn(index);
if (!column) continue;
colIdsToBeDeleted.insert(index);
TFx *fx = column->getFx();
if (fx) {
leaves.insert(fx);
TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx);
if (zcfx) fxsToBeDeleted.insert(zcfx->getZeraryFx());
}
}
// store relevant fxs which will be deleted along with the columns
TFxSet *fxSet = xsh->getFxDag()->getInternalFxs();
for (int i = 0; i < fxSet->getFxCount(); i++) {
TFx *fx = fxSet->getFx(i);
if (canRemoveFx(leaves, fx)) fxsToBeDeleted.insert(fx);
}
// store object ids which will be duplicated in the child xsheet on collapse
QList<TStageObjectId> objIdsToBeDuplicated;
if (checkInvert && !onlyColumns) {
for (auto index : indices) {
TStageObjectId id =
xsh->getStageObjectParent(TStageObjectId::ColumnId(index));
// store pegbars/cameras connected to the columns
while (id.isPegbar() || id.isCamera()) {
if (!objIdsToBeDuplicated.contains(id)) objIdsToBeDuplicated.append(id);
id = xsh->getStageObjectParent(id);
}
}
}
return ExpressionReferenceManager::instance()->checkReferenceDeletion(
colIdsToBeDeleted, fxsToBeDeleted, objIdsToBeDuplicated, checkInvert);
}
//-----------------------------------------------------------------------------
bool ColumnCmd::checkExpressionReferences(const std::set<int> &indices,
const std::set<TFx *> &fxs,
bool onlyColumns, bool checkInvert) {
if (!Preferences::instance()->isModifyExpressionOnMovingReferencesEnabled())
return true;
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
// check if fxs will be deleted
QSet<int> colIdsToBeDeleted;
QSet<TFx *> fxsToBeDeleted;
for (auto index : indices) {
if (index < 0) continue;
TXshColumn *column = xsh->getColumn(index);
if (!column) continue;
colIdsToBeDeleted.insert(index);
TFx *fx = column->getFx();
if (fx) {
TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx);
if (zcfx) fxsToBeDeleted.insert(zcfx->getZeraryFx());
}
}
TFxSet *fxSet = xsh->getFxDag()->getInternalFxs();
for (auto fx : fxs) fxsToBeDeleted.insert(fx);
// store object ids which will be duplicated in the child xsheet on collapse
QList<TStageObjectId> objIdsToBeDuplicated;
if (checkInvert && !onlyColumns) {
for (auto index : indices) {
TStageObjectId id =
xsh->getStageObjectParent(TStageObjectId::ColumnId(index));
// store pegbars/cameras connected to the columns
while (id.isPegbar() || id.isCamera()) {
if (!objIdsToBeDuplicated.contains(id)) objIdsToBeDuplicated.append(id);
id = xsh->getStageObjectParent(id);
}
}
}
return ExpressionReferenceManager::instance()->checkReferenceDeletion(
colIdsToBeDeleted, fxsToBeDeleted, objIdsToBeDuplicated, checkInvert);
}
//-----------------------------------------------------------------------------
bool ColumnCmd::checkExpressionReferences(
const QList<TStageObjectId> &objects) {
if (!Preferences::instance()->isModifyExpressionOnMovingReferencesEnabled())
return true;
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
QSet<int> colIdsToBeDeleted;
QSet<TFx *> fxsToBeDeleted;
QList<TStageObjectId> objIdsToBeDuplicated;
// store column fxs to be deleted
std::set<TFx *> leaves;
for (auto objId : objects) {
if (objId.isColumn()) {
int index = objId.getIndex();
if (index < 0) continue;
TXshColumn *column = xsh->getColumn(index);
if (!column) continue;
colIdsToBeDeleted.insert(index);
TFx *fx = column->getFx();
if (fx) {
leaves.insert(fx);
TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx);
if (zcfx) fxsToBeDeleted.insert(zcfx->getZeraryFx());
}
} else
objIdsToBeDuplicated.append(objId);
}
// store relevant fxs which will be deleted along with the columns
TFxSet *fxSet = xsh->getFxDag()->getInternalFxs();
for (int i = 0; i < fxSet->getFxCount(); i++) {
TFx *fx = fxSet->getFx(i);
if (canRemoveFx(leaves, fx)) fxsToBeDeleted.insert(fx);
}
return ExpressionReferenceManager::instance()->checkReferenceDeletion(
colIdsToBeDeleted, fxsToBeDeleted, objIdsToBeDuplicated, true);
}
//=============================================================================
namespace {
enum {
CMD_LOCK = 0x0001,
CMD_UNLOCK = 0x0002,
CMD_TOGGLE_LOCK = 0x0004,
CMD_ENABLE_PREVIEW = 0x0008,
CMD_DISABLE_PREVIEW = 0x0010,
CMD_TOGGLE_PREVIEW = 0x0020,
CMD_ENABLE_CAMSTAND = 0x0040,
CMD_DISABLE_CAMSTAND = 0x0080,
CMD_TOGGLE_CAMSTAND = 0x0100
};
enum {
TARGET_ALL,
TARGET_SELECTED,
TARGET_CURRENT,
TARGET_OTHER,
TARGET_UPPER /*-- カレントカラムより右側にあるカラムを非表示にするコマンド
--*/
};
} // namespace
class ColumnsStatusCommand final : public MenuItemHandler {
int m_cmd, m_target;
public:
ColumnsStatusCommand(CommandId id, int cmd, int target)
: MenuItemHandler(id), m_cmd(cmd), m_target(target) {}
void execute() override {
TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
TApp::instance()->getCurrentSelection()->getSelection());
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
XsheetViewer *xviewer = TApp::instance()->getCurrentXsheetViewer();
int cc = TApp::instance()->getCurrentColumn()->getColumnIndex();
bool sound_changed = false;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
TTool::Viewer *viewer = tool ? tool->getViewer() : nullptr;
bool viewer_changed = false;
int startCol =
Preferences::instance()->isXsheetCameraColumnVisible() ? -1 : 0;
for (int i = startCol; i < xsh->getColumnCount(); i++) {
/*- Skip if empty column -*/
if (i >= 0 && xsh->isColumnEmpty(i)) continue;
/*- Skip if column cannot be obtained -*/
TXshColumn *column = xsh->getColumn(i);
if (!column) continue;
/*- Skip if target is in selected column mode and not selected -*/
bool isSelected = selection && !selection->isEmpty()
? selection->isColumnSelected(i)
: cc == i;
if (m_target == TARGET_SELECTED && !isSelected) continue;
/*-
* Skip if target is "right side of current column" mode and i is left of
* current column
* -*/
if (m_target == TARGET_UPPER && i < cc) continue;
bool negate = ((m_target == TARGET_CURRENT && cc != i) ||
(m_target == TARGET_OTHER && cc == i) ||
(m_target == TARGET_UPPER && cc == i));
int cmd = m_cmd;
if (cmd & (CMD_LOCK | CMD_UNLOCK | CMD_TOGGLE_LOCK)) {
if (cmd & CMD_LOCK)
column->lock(!negate);
else if (cmd & CMD_UNLOCK)
column->lock(negate);
else
column->lock(!column->isLocked());
viewer_changed = true;
}
if (cmd &
(CMD_ENABLE_PREVIEW | CMD_DISABLE_PREVIEW | CMD_TOGGLE_PREVIEW)) {
if (cmd & CMD_ENABLE_PREVIEW)
column->setPreviewVisible(!negate);
else if (cmd & CMD_DISABLE_PREVIEW)
column->setPreviewVisible(negate);
else
column->setPreviewVisible(!column->isPreviewVisible());
}
if (cmd &
(CMD_ENABLE_CAMSTAND | CMD_DISABLE_CAMSTAND | CMD_TOGGLE_CAMSTAND)) {
if (cmd & CMD_ENABLE_CAMSTAND)
column->setCamstandVisible(!negate);
else if (cmd & CMD_DISABLE_CAMSTAND)
column->setCamstandVisible(negate);
else
column->setCamstandVisible(!column->isCamstandVisible());
if (column->getSoundColumn()) sound_changed = true;
viewer_changed = true;
}
/*TAB
if(cmd & (CMD_ENABLE_PREVIEW|CMD_DISABLE_PREVIEW|CMD_TOGGLE_PREVIEW))
{ //In Tab preview e cameraStand vanno settati entrambi
if(cmd&CMD_ENABLE_PREVIEW)
{
column->setPreviewVisible(!negate);
column->setCamstandVisible(!negate);
}
else if(cmd&CMD_DISABLE_PREVIEW)
{
column->setPreviewVisible(negate);
column->setCamstandVisible(negate);
}
else
{
column->setPreviewVisible(!column->isPreviewVisible());
column->setCamstandVisible(!column->isCamstandVisible());
}
}
*/
}
if (sound_changed)
TApp::instance()->getCurrentXsheet()->notifyXsheetSoundChanged();
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
if (viewer && viewer_changed) viewer->invalidateToolStatus();
}
};
namespace {
ColumnsStatusCommand
c00(MI_ActivateAllColumns, CMD_ENABLE_CAMSTAND, TARGET_ALL),
c01(MI_DeactivateAllColumns, CMD_DISABLE_CAMSTAND, TARGET_ALL),
c02(MI_ActivateThisColumnOnly, CMD_ENABLE_CAMSTAND, TARGET_CURRENT),
c03(MI_ToggleColumnsActivation, CMD_TOGGLE_CAMSTAND, TARGET_ALL),
c04(MI_ActivateSelectedColumns, CMD_ENABLE_CAMSTAND, TARGET_SELECTED),
c05(MI_DeactivateSelectedColumns, CMD_DISABLE_CAMSTAND, TARGET_SELECTED),
c18(MI_DeactivateUpperColumns, CMD_DISABLE_CAMSTAND, TARGET_UPPER),
c06(MI_EnableAllColumns, CMD_ENABLE_PREVIEW, TARGET_ALL),
c07(MI_DisableAllColumns, CMD_DISABLE_PREVIEW, TARGET_ALL),
c08(MI_EnableThisColumnOnly, CMD_ENABLE_PREVIEW, TARGET_CURRENT),
c09(MI_SwapEnabledColumns, CMD_TOGGLE_PREVIEW, TARGET_ALL),
c10(MI_EnableSelectedColumns, CMD_ENABLE_PREVIEW, TARGET_SELECTED),
c11(MI_DisableSelectedColumns, CMD_DISABLE_PREVIEW, TARGET_SELECTED),
c12(MI_LockAllColumns, CMD_LOCK, TARGET_ALL),
c13(MI_UnlockAllColumns, CMD_UNLOCK, TARGET_ALL),
c14(MI_LockThisColumnOnly, CMD_LOCK, TARGET_CURRENT),
c15(MI_ToggleColumnLocks, CMD_TOGGLE_LOCK, TARGET_ALL),
c16(MI_LockSelectedColumns, CMD_LOCK, TARGET_SELECTED),
c17(MI_UnlockSelectedColumns, CMD_UNLOCK, TARGET_SELECTED);
} // namespace
//=============================================================================
// ConvertToVectorUndo
//-----------------------------------------------------------------------------
// Same in functionality to PasteColumnsUndo; think of it perhaps like
// pasting the newly created vector column.
class ConvertToVectorUndo final : public PasteColumnsUndo {
public:
ConvertToVectorUndo(std::set<int> indices) : PasteColumnsUndo(indices){};
QString getHistoryString() override {
return QObject::tr("Convert to Vectors");
}
};
void ColumnCmd::addConvertToVectorUndo(std::set<int> &newColumnIndices) {
TUndoManager::manager()->add(new ConvertToVectorUndo(newColumnIndices));
}