// TnzCore includes
#include "tconst.h"
#include "tundo.h"
// TnzBase includes
#include "tfx.h"
#include "tfxattributes.h"
#include "tparamcontainer.h"
#include "tparamset.h"
// TnzLib includes
#include "toonz/tframehandle.h"
#include "toonz/tcolumnhandle.h"
#include "toonz/txsheethandle.h"
#include "toonz/tobjecthandle.h"
#include "toonz/tscenehandle.h"
#include "toonz/txshcell.h"
#include "toonz/txsheet.h"
#include "toonz/toonzscene.h"
#include "toonz/childstack.h"
#include "toonz/txshleveltypes.h"
#include "toonz/txshchildlevel.h"
#include "toonz/tstageobject.h"
#include "toonz/tcolumnfx.h"
#include "toonz/fxcommand.h"
#include "toonz/tcolumnfxset.h"
#include "toonz/fxdag.h"
#include "toonz/tstageobjecttree.h"
#include "toonz/tstageobjectspline.h"
#include "toonz/tcamera.h"
#include "toonz/expressionreferencemonitor.h"
// TnzQt includes
#include "toonzqt/menubarcommand.h"
#include "toonzqt/icongenerator.h"
#include "toonzqt/tselectionhandle.h"
#include "toonzqt/selection.h"
#include "toonzqt/dvdialog.h"
#include "toonzqt/stageobjectsdata.h"
#include "historytypes.h"
// Toonz includes
#include "columncommand.h"
#include "menubarcommandids.h"
#include "celldata.h"
#include "tapp.h"
#include "columnselection.h"
#include "cellselection.h"
#include "expressionreferencemanager.h"
#include "subscenecommand.h"
//*****************************************************************************
// Local namespace
//*****************************************************************************
namespace {
struct GroupData {
public:
QStack<int> m_groupIds;
QStack<std::wstring> m_groupNames;
int m_editingGroup;
GroupData(const QStack<int> &groupIds, const QStack<std::wstring> &groupNames,
int editingGroup)
: m_groupIds(groupIds)
, m_groupNames(groupNames)
, m_editingGroup(editingGroup) {}
};
//-----------------------------------------------------------------------------
// Zerary fxs and zerary COLUMN fxs are separate, and fx port connections
// are stored in the actual zerary fx.
TFx *getActualFx(TFx *fx) {
TZeraryColumnFx *zeraryColumnFx = dynamic_cast<TZeraryColumnFx *>(fx);
return zeraryColumnFx ? zeraryColumnFx->getZeraryFx() : fx;
}
//-----------------------------------------------------------------------------
void setFxParamToCurrentScene(TFx *fx, TXsheet *xsh) {
for (int i = 0; i < fx->getParams()->getParamCount(); i++) {
TParam *param = fx->getParams()->getParam(i);
if (TDoubleParam *dp = dynamic_cast<TDoubleParam *>(param))
xsh->getStageObjectTree()->setGrammar(dp);
else if (dynamic_cast<TPointParam *>(param) ||
dynamic_cast<TRangeParam *>(param) ||
dynamic_cast<TPixelParam *>(param)) {
TParamSet *paramSet = dynamic_cast<TParamSet *>(param);
assert(paramSet);
int f;
for (f = 0; f < paramSet->getParamCount(); f++) {
TDoubleParam *dp =
dynamic_cast<TDoubleParam *>(paramSet->getParam(f).getPointer());
if (!dp) continue;
xsh->getStageObjectTree()->setGrammar(dp);
}
}
}
}
//-----------------------------------------------------------------------------
std::vector<TStageObjectId> getRoots(const QList<TStageObjectId> &objIds,
TXsheetHandle *xshHandle) {
std::vector<TStageObjectId> roots;
std::map<TStageObjectId, std::string> parentHandles;
TStageObjectTree *pegTree = xshHandle->getXsheet()->getStageObjectTree();
for (int i = 0; i < objIds.size(); i++) {
TStageObject *obj = pegTree->getStageObject(objIds.at(i), false);
TStageObjectId parentId = obj->getParent();
bool parentIsColumn = parentId.isColumn() && !objIds.contains(parentId);
std::string parentHandle = obj->getParentHandle();
if (!parentIsColumn && !objIds.contains(parentId) &&
(parentHandles.count(parentId) == 0 ||
parentHandles[parentId] != parentHandle)) {
parentHandles[parentId] = parentHandle;
roots.push_back(parentId);
}
}
return roots;
}
//-----------------------------------------------------------------------------
std::vector<TStageObjectId> isConnected(
const std::set<int> &indices, const std::set<TStageObjectId> &pegbarIds,
TXsheetHandle *xshHandle) {
std::vector<TStageObjectId> roots;
std::map<TStageObjectId, std::string> parentHandles;
TStageObjectTree *pegTree = xshHandle->getXsheet()->getStageObjectTree();
std::set<int>::const_iterator it;
for (it = indices.begin(); it != indices.end(); it++) {
TStageObjectId id = TStageObjectId::ColumnId(*it);
TStageObject *obj = pegTree->getStageObject(id, false);
TStageObjectId parentId = obj->getParent();
std::string parentHandle = obj->getParentHandle();
bool parentIsColumn = parentId.isColumn() &&
indices.find(parentId.getIndex()) != indices.end();
if (!parentIsColumn && pegbarIds.find(parentId) == pegbarIds.end() &&
(parentHandles.count(parentId) == 0 ||
parentHandles[parentId] != parentHandle)) {
parentHandles[parentId] = parentHandle;
roots.push_back(parentId);
}
}
std::set<TStageObjectId>::const_iterator it2;
for (it2 = pegbarIds.begin(); it2 != pegbarIds.end(); it2++) {
TStageObject *obj = pegTree->getStageObject(*it2, false);
TStageObjectId parentId = obj->getParent();
bool parentIsColumn = parentId.isColumn() &&
indices.find(parentId.getIndex()) != indices.end();
std::string parentHandle = obj->getParentHandle();
if (!parentIsColumn && pegbarIds.find(parentId) == pegbarIds.end() &&
(parentHandles.count(parentId) == 0 ||
parentHandles[parentId] != parentHandle)) {
parentHandles[parentId] = parentHandle;
roots.push_back(parentId);
}
}
return roots;
}
//-----------------------------------------------------------------------------
std::map<TFx *, std::vector<TFxPort *>> isConnected(
const std::set<int> &indices, const std::set<TFx *> &internalFxs,
TXsheetHandle *xshHandle) {
TXsheet *xsh = xshHandle->getXsheet();
std::set<int>::const_iterator it;
std::map<TFx *, std::vector<TFxPort *>> roots;
for (it = indices.begin(); it != indices.end(); it++) {
TFx *fx = xsh->getColumn(*it)->getFx();
int i, outputCount = fx->getOutputConnectionCount();
for (i = 0; i < outputCount; i++) {
TFx *outFx = fx->getOutputConnection(i)->getOwnerFx();
if (internalFxs.find(outFx) == internalFxs.end())
roots[fx].push_back(fx->getOutputConnection(i));
}
}
std::set<TFx *>::const_iterator it2;
for (it2 = internalFxs.begin(); it2 != internalFxs.end(); it2++) {
int i, outputCount = (*it2)->getOutputConnectionCount();
for (i = 0; i < outputCount; i++) {
TFx *outFx = (*it2)->getOutputConnection(i)->getOwnerFx();
if (internalFxs.find(outFx) == internalFxs.end())
roots[*it2].push_back((*it2)->getOutputConnection(i));
}
}
return roots;
}
//-----------------------------------------------------------------------------
// returns true if the column indexed with col contains only the childLevel.
// if not, false is returned and in from and to is put the frame range contained
// the frame indexed with row.
bool mustRemoveColumn(int &from, int &to, TXshChildLevel *childLevel,
TXsheet *xsh, int col, int row) {
bool removeColumn = true;
bool rangeFound = false;
from = -1;
to = -1;
int i, r0, r1;
xsh->getColumn(col)->getRange(r0, r1);
for (i = r0; i <= r1; i++) {
TXshCell cell = xsh->getCell(i, col);
TXshChildLevel *app = cell.getChildLevel();
if (app != childLevel) {
removeColumn = false;
if (from != -1 && to != -1) {
rangeFound = from <= row && row <= to;
if (!rangeFound) from = to = -1;
}
continue;
}
if (from == -1 && !rangeFound) {
from = to = i;
} else if (from != -1 && !rangeFound) {
to = i;
}
}
return removeColumn;
}
//-----------------------------------------------------------------------------
class FxConnections {
bool m_isTerminal;
QMap<int, TFx *> m_inputLinks;
QMap<TFx *, int> m_outputLinks;
QList<TFx *> m_notTerminalInputFxs;
public:
FxConnections() {}
~FxConnections() {}
void setIsTerminal(bool isTerminal) { m_isTerminal = isTerminal; }
void setInputLink(int portIndex, TFx *inputFx) {
m_inputLinks[portIndex] = inputFx;
}
void setOutputLink(TFx *outputFx, int portIndex) {
m_outputLinks[outputFx] = portIndex;
}
void addNotTerminalInputFx(TFx *fx) { m_notTerminalInputFxs.append(fx); }
QMap<int, TFx *> getInputLinks() { return m_inputLinks; }
QMap<TFx *, int> getOutputLinks() { return m_outputLinks; }
QList<TFx *> getNotTerminalInputFxs() { return m_notTerminalInputFxs; }
bool isTerminal() { return m_isTerminal; }
};
//-----------------------------------------------------------------------------
void getFxConnections(QMap<TFx *, FxConnections> &fxConnetcions,
const std::set<TFx *> &fxs, TXsheet *xsh) {
TFxSet *terminalFxs = xsh->getFxDag()->getTerminalFxs();
for (auto const &fx : fxs) {
FxConnections connections;
connections.setIsTerminal(terminalFxs->containsFx(fx));
int i;
for (i = 0; i < fx->getInputPortCount(); i++) {
TFx *inputFx = fx->getInputPort(i)->getFx();
connections.setInputLink(i, inputFx);
if (connections.isTerminal()) connections.addNotTerminalInputFx(inputFx);
}
for (i = 0; i < fx->getOutputConnectionCount(); i++) {
TFx *outputFx = fx->getOutputConnection(i)->getOwnerFx();
int j, inputCount = outputFx->getInputPortCount();
if (inputCount == 0) continue;
for (j = 0; j < inputCount; j++) {
TFx *inputFx = outputFx->getInputPort(j)->getFx();
if (inputFx == fx) break;
}
connections.setOutputLink(outputFx, j);
}
fxConnetcions[fx] = connections;
}
}
//-----------------------------------------------------------------------------
void changeSaveSubXsheetAsCommand() {
ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
bool isSubxsheet = scene->getChildStack()->getAncestorCount() > 0;
CommandManager::instance()->enable(MI_SaveSubxsheetAs, isSubxsheet);
}
//-----------------------------------------------------------------------------
void getColumnOutputConnections(
const std::set<int> &indices,
QMap<TFx *, QList<TFxPort *>> &columnOutputConnections) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
std::set<int>::const_iterator it;
for (it = indices.begin(); it != indices.end(); it++) {
int i = *it;
TXshColumn *column = xsh->getColumn(i);
if (!column) continue;
TFx *columnFx = column->getFx();
if (!columnFx) continue;
QList<TFxPort *> ports;
int j;
for (j = 0; j < columnFx->getOutputConnectionCount(); j++)
ports.append(columnFx->getOutputConnection(j));
columnOutputConnections[columnFx] = ports;
}
}
//-----------------------------------------------------------------------------
void getChildren(const std::set<int> &indices,
QMap<TStageObjectId, QList<TStageObjectId>> &children) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
std::set<int>::const_iterator it;
for (it = indices.begin(); it != indices.end(); it++) {
TStageObjectId id = TStageObjectId::ColumnId(*it);
TStageObject *obj = xsh->getStageObjectTree()->getStageObject(id, false);
assert(obj);
if (obj && !obj->getChildren().empty()) {
std::list<TStageObject *> childrenObj = obj->getChildren();
std::list<TStageObject *>::iterator it2;
for (it2 = childrenObj.begin(); it2 != childrenObj.end(); it2++) {
TStageObjectId childId = (*it2)->getId();
children[id].append(childId);
}
}
}
}
//-----------------------------------------------------------------------------
void getParents(const std::set<int> &indices,
QMap<TStageObjectId, TStageObjectId> &parents) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
std::set<int>::const_iterator it;
for (it = indices.begin(); it != indices.end(); it++) {
TStageObjectId id = TStageObjectId::ColumnId(*it);
TStageObject *obj = xsh->getStageObjectTree()->getStageObject(id, false);
assert(obj);
if (obj) parents[id] = obj->getParent();
}
}
//-----------------------------------------------------------------------------
void setColumnOutputConnections(
const QMap<TFx *, QList<TFxPort *>> &columnOutputConnections) {
QMap<TFx *, QList<TFxPort *>>::const_iterator it;
for (it = columnOutputConnections.begin();
it != columnOutputConnections.end(); it++) {
TFx *columnFx = it.key();
QList<TFxPort *> ports = it.value();
int i;
for (i = 0; i < ports.size(); i++) ports.at(i)->setFx(columnFx);
}
}
//-----------------------------------------------------------------------------
void setChildren(const QMap<TStageObjectId, QList<TStageObjectId>> &children) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
QMap<TStageObjectId, QList<TStageObjectId>>::const_iterator it;
for (it = children.begin(); it != children.end(); it++) {
TStageObjectId id = it.key();
QList<TStageObjectId> childrenIds = it.value();
QList<TStageObjectId>::iterator it2;
for (it2 = childrenIds.begin(); it2 != childrenIds.end(); it2++)
xsh->setStageObjectParent(*it2, id);
}
}
//-----------------------------------------------------------------------------
void setParents(const QMap<TStageObjectId, TStageObjectId> &parents) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
QMap<TStageObjectId, TStageObjectId>::const_iterator it;
for (it = parents.begin(); it != parents.end(); it++)
xsh->setStageObjectParent(it.key(), it.value());
}
//-----------------------------------------------------------------------------
bool isConnectedToXsheet(TFx *fx) {
if (!fx) return false;
int i, count = fx->getInputPortCount();
bool xsheetConnected = false;
for (i = 0; i < count; i++) {
TFx *inputFx = fx->getInputPort(i)->getFx();
if (dynamic_cast<TXsheetFx *>(inputFx)) return true;
xsheetConnected = xsheetConnected || isConnectedToXsheet(inputFx);
}
return xsheetConnected;
}
//-----------------------------------------------------------------------------
// clones in outerDag fx and all effects contained in the subtree with root in
// fx
void bringFxOut(TFx *fx, QMap<TFx *, QPair<TFx *, int>> &fxs, FxDag *outerDag,
const GroupData &fxGroupData) {
if (!fx) return;
TFx *actualFx = getActualFx(fx);
if (fx != actualFx) {
// Zerary Column case
TFx *outerFx = getActualFx(fxs[fx].first);
int i, inputPortsCount = actualFx->getInputPortCount();
for (i = 0; i < inputPortsCount; ++i) {
TFx *inputFx = actualFx->getInputPort(i)->getFx();
if (!inputFx) continue;
bringFxOut(inputFx, fxs, outerDag, fxGroupData);
outerFx->getInputPort(i)->setFx(fxs[inputFx].first);
}
return;
}
// Common case
if (fxs.contains(fx)) return;
TFx *outerFx = fx->clone(false);
TOutputFx *outFx = dynamic_cast<TOutputFx *>(outerFx);
if (!outFx) {
outerDag->getInternalFxs()->addFx(outerFx);
outerDag->assignUniqueId(outerFx);
} else
outerDag->addOutputFx(outFx);
TFxAttributes *attr = outerFx->getAttributes();
attr->setDagNodePos(fx->getAttributes()->getDagNodePos());
// Put in the right Fx group if needed
attr->removeFromAllGroup();
if (!fxGroupData.m_groupIds.empty()) {
int i;
for (i = 0; i < fxGroupData.m_groupIds.size(); i++) {
attr->setGroupId(fxGroupData.m_groupIds[i]);
attr->setGroupName(fxGroupData.m_groupNames[i]);
}
for (i = 0;
i < fxGroupData.m_groupIds.size() && fxGroupData.m_editingGroup >= 0;
i++)
attr->editGroup();
}
int columnIndex = -1;
bool firstIndex = true;
int i, inputPortsCount = fx->getInputPortCount();
for (i = 0; i < inputPortsCount; ++i) {
TFx *inputFx = fx->getInputPort(i)->getFx();
if (!inputFx) continue;
bringFxOut(inputFx, fxs, outerDag, fxGroupData);
outerFx->getInputPort(i)->setFx(fxs[inputFx].first);
if (firstIndex) {
columnIndex = fxs[inputFx].second;
firstIndex = false;
}
}
fxs[fx] = QPair<TFx *, int>(outerFx, columnIndex);
}
//-----------------------------------------------------------------------------
TFx *explodeFxSubTree(TFx *innerFx, QMap<TFx *, QPair<TFx *, int>> &fxs,
FxDag *outerDag, TXsheet *outerXsheet, FxDag *innerDag,
const GroupData &fxGroupData,
const std::vector<TFxPort *> &outPorts) {
TXsheetFx *xsheetFx = dynamic_cast<TXsheetFx *>(innerFx);
if (!xsheetFx) {
if (innerDag->getCurrentOutputFx() == innerFx)
innerFx = innerFx->getInputPort(0)->getFx();
if (!innerFx) return nullptr;
bringFxOut(innerFx, fxs, outerDag, fxGroupData);
TOutputFx *outFx = dynamic_cast<TOutputFx *>(innerFx);
if (outFx)
return fxs[outFx->getInputPort(0)->getFx()].first;
else
return fxs[innerFx].first;
} else {
TFxSet *innerTerminals = innerDag->getTerminalFxs();
int i, terminalCount = innerTerminals->getFxCount();
QMultiMap<int, TFx *> sortedFx;
for (i = 0; i < terminalCount; i++) {
TFx *terminalFx = innerTerminals->getFx(i);
bringFxOut(terminalFx, fxs, outerDag, fxGroupData);
sortedFx.insert(fxs[terminalFx].second, fxs[terminalFx].first);
}
// Xsheet nodes can be "merged" if:
// a) the subxsheet node is directly connected to the Xsheet node in the
// parent fxdag, AND b) only the active output node is connected to the
// Xsheet node in the child fxdag
if (outPorts.empty() && xsheetFx->getOutputConnectionCount() == 1) {
if (innerDag->getCurrentOutputFx() ==
xsheetFx->getOutputConnection(0)->getOwnerFx())
return nullptr;
}
// in case no nodes connected to the xsheet the xsheet node will not be
// merged, but will just be removed
if (terminalCount == 0) {
fxs[innerFx] = QPair<TFx *, int>(nullptr, -1);
return innerFx; // just to return non-zero value
}
TFx *root = sortedFx.begin().value();
// If only one node is connected to the Xsheet node, then skip bringing it
// out.
if (terminalCount == 1) {
fxs[innerFx] = QPair<TFx *, int>(root, sortedFx.begin().key());
return root;
}
// Replace the child Xsheet node by the Over Fx node
TFx *overFx = TFx::create("overFx");
outerDag->assignUniqueId(overFx);
outerDag->getInternalFxs()->addFx(overFx);
setFxParamToCurrentScene(overFx, outerXsheet);
TPointD pos = root->getAttributes()->getDagNodePos();
overFx->getAttributes()->setDagNodePos((pos == TConst::nowhere)
? TConst::nowhere
: TPointD(pos.x + 150, pos.y));
const TFxPortDG *group = overFx->dynamicPortGroup(0);
for (int i = 0; i < sortedFx.size(); i++) {
TFxPort *port = new TRasterFxPort;
if (!overFx->addInputPort(
group->portsPrefix() + QString::number(i + 1).toStdString(), port,
0))
delete port;
}
int portId = sortedFx.size() - 1;
int columnIndex = -1;
for (auto it = sortedFx.begin(); it != sortedFx.end(); ++it, --portId) {
TFx *fx = it.value();
assert(fx);
overFx->getInputPort(portId)->setFx(fx);
outerDag->removeFromXsheet(fx);
// set the firstly-found column index
if (columnIndex == -1) columnIndex = it.key();
}
// register fx
fxs[innerFx] = QPair<TFx *, int>(overFx, columnIndex);
return overFx;
}
}
//-----------------------------------------------------------------------------
// brings in xsh obj and all objects contained in the subtree with root in obj
void bringObjectOut(TStageObject *obj, TXsheet *xsh,
QMap<TStageObjectId, TStageObjectId> &ids,
QMap<TStageObjectSpline *, TStageObjectSpline *> &splines,
QList<TStageObject *> &pegObjects, int &pegbarIndex,
const GroupData &objGroupData, int groupId) {
if (!obj->hasChildren()) return;
std::list<TStageObject *> children = obj->getChildren();
std::list<TStageObject *>::iterator it;
for (it = children.begin(); it != children.end(); it++) {
TStageObjectId id = (*it)->getId();
if (id.isColumn()) continue;
assert(id.isPegbar());
pegbarIndex++;
TStageObjectId outerId = TStageObjectId::PegbarId(pegbarIndex);
// find the first available pegbar id
while (xsh->getStageObjectTree()->getStageObject(outerId, false)) {
pegbarIndex++;
outerId = TStageObjectId::PegbarId(pegbarIndex);
}
TStageObject *outerObj =
xsh->getStageObjectTree()->getStageObject(outerId, true);
outerObj->setDagNodePos((*it)->getDagNodePos());
ids[id] = outerId;
pegObjects.append(outerObj);
outerObj->addRef(); // undo make release!!!
TStageObjectParams *params = (*it)->getParams();
if (params->m_spline) {
if (splines.contains(params->m_spline))
params->m_spline = splines[params->m_spline];
else {
TStageObjectSpline *spline = params->m_spline->clone();
splines[params->m_spline] = spline;
xsh->getStageObjectTree()->assignUniqueSplineId(spline);
xsh->getStageObjectTree()->insertSpline(spline);
params->m_spline = spline;
}
}
outerObj->assignParams(params);
delete params;
outerObj->setParent(ids[obj->getId()]);
outerObj->removeFromAllGroup();
if (groupId != -1) {
outerObj->setGroupId(groupId);
outerObj->setGroupName(L"Group " + std::to_wstring(groupId));
}
if (!objGroupData.m_groupIds.empty()) {
int i;
for (i = 0; i < objGroupData.m_groupIds.size(); i++) {
outerObj->setGroupId(objGroupData.m_groupIds[i]);
outerObj->setGroupName(objGroupData.m_groupNames[i]);
}
for (i = 0; i < objGroupData.m_groupIds.size() &&
objGroupData.m_editingGroup >= 0;
i++)
outerObj->editGroup();
}
bringObjectOut(*it, xsh, ids, splines, pegObjects, pegbarIndex,
objGroupData, groupId);
}
}
//-----------------------------------------------------------------------------
std::set<int> explodeStageObjects(
TXsheet *xsh, TXsheet *subXsh, int index, const TStageObjectId &parentId,
const GroupData &objGroupData, const TPointD &subPos,
const GroupData &fxGroupData, QList<TStageObject *> &pegObjects,
QMap<TFx *, QPair<TFx *, int>> &fxs,
QMap<TStageObjectSpline *, TStageObjectSpline *> &splines,
QMap<TStageObjectId, TStageObjectId> &ids, bool onlyColumn) {
/*- SubXsheet, 親Xsheet両方のツリーを取得 -*/
TStageObjectTree *innerTree = subXsh->getStageObjectTree();
TStageObjectTree *outerTree = xsh->getStageObjectTree();
// innerSpline->outerSpline
int groupId = -1; // outerTree->getNewGroupId();
/*- Pegbarも持ち出す場合 -*/
if (!onlyColumn) {
// add a pegbar to represent the table
TStageObject *table = subXsh->getStageObject(TStageObjectId::TableId);
// find the first available pegbar index
int pegbarIndex = 0;
while (
outerTree->getStageObject(TStageObjectId::PegbarId(pegbarIndex), false))
pegbarIndex++;
/*- 空いてるIndexのPegbarに、SubXsheetのTableを対応させる -*/
TStageObjectId id = TStageObjectId::PegbarId(pegbarIndex);
TStageObject *obj = outerTree->getStageObject(id, true);
/*- 対応表に追加 -*/
obj->setDagNodePos(table->getDagNodePos());
ids[TStageObjectId::TableId] = id;
pegObjects.append(obj);
obj->addRef(); // undo make release!!!!
/*- SubのTableの情報を、今作ったPegbarにコピーする -*/
TStageObjectParams *params = table->getParams();
if (params->m_spline) {
if (splines.contains(params->m_spline))
params->m_spline = splines[params->m_spline];
else {
TStageObjectSpline *spline = params->m_spline->clone();
splines[params->m_spline] = spline;
outerTree->assignUniqueSplineId(spline);
outerTree->insertSpline(spline);
params->m_spline = spline;
}
}
obj->assignParams(params);
delete params;
// a pegbar cannot be a child of column
if (parentId.isColumn())
obj->setParent(TStageObjectId::TableId);
else
obj->setParent(parentId);
// Put in the right StageObject group if needed
obj->removeFromAllGroup();
groupId = outerTree->getNewGroupId();
obj->setGroupId(groupId);
obj->setGroupName(L"Group " + std::to_wstring(groupId));
if (!objGroupData.m_groupIds.empty()) {
int i;
for (i = 0; i < objGroupData.m_groupIds.size(); i++) {
obj->setGroupId(objGroupData.m_groupIds[i]);
obj->setGroupName(objGroupData.m_groupNames[i]);
}
for (i = 0; i < objGroupData.m_groupIds.size() &&
objGroupData.m_editingGroup >= 0;
i++)
obj->editGroup();
}
// add all pegbar
bringObjectOut(table, xsh, ids, splines, pegObjects, pegbarIndex,
objGroupData, groupId);
}
// add columns;
FxDag *innerDag = subXsh->getFxDag();
FxDag *outerDag = xsh->getFxDag();
TStageObjectId tmpParentId = parentId;
std::set<int> indexes;
int i;
for (i = 0; i < subXsh->getColumnCount(); i++) {
TXshColumn *innerColumn = subXsh->getColumn(i);
TXshColumn *outerColumn = innerColumn->clone();
TFx *innerFx = innerColumn->getFx();
TFx *outerFx = outerColumn->getFx();
xsh->insertColumn(index, outerColumn);
// the above insertion operation may increment the parentId, in case that
// 1, the parent object is column, and
// 2, the parent column is placed on the right side of the inserted column
// ( i.e. index of the parent column is equal to or higher than "index")
if (onlyColumn && tmpParentId.isColumn() && tmpParentId.getIndex() >= index)
tmpParentId = TStageObjectId::ColumnId(tmpParentId.getIndex() + 1);
if (innerFx && outerFx) {
outerFx->getAttributes()->setDagNodePos(
innerFx->getAttributes()->getDagNodePos());
fxs[innerColumn->getFx()] =
QPair<TFx *, int>(outerColumn->getFx(), outerColumn->getIndex());
if (!innerDag->getTerminalFxs()->containsFx(innerColumn->getFx()))
outerDag->getTerminalFxs()->removeFx(outerColumn->getFx());
}
TStageObjectId innerId = TStageObjectId::ColumnId(i);
TStageObjectId outerId = TStageObjectId::ColumnId(index);
TStageObject *innerCol = innerTree->getStageObject(innerId, false);
TStageObject *outerCol = outerTree->getStageObject(outerId, false);
TStageObjectParams *params = innerCol->getParams();
if (params->m_spline) {
if (splines.contains(params->m_spline))
params->m_spline = splines[params->m_spline];
else {
TStageObjectSpline *spline = params->m_spline->clone();
splines[params->m_spline] = spline;
outerTree->assignUniqueSplineId(spline);
outerTree->insertSpline(spline);
params->m_spline = spline;
}
}
outerCol->assignParams(params);
outerCol->setDagNodePos(innerCol->getDagNodePos());
delete params;
assert(outerCol && innerCol);
ids[innerId] = outerId;
outerCol->removeFromAllGroup();
if (groupId != -1) {
outerCol->setGroupId(groupId);
outerCol->setGroupName(L"Group " + std::to_wstring(groupId));
}
if (onlyColumn) outerCol->setParent(tmpParentId);
// Put in the right StageObject group if needed
if (!objGroupData.m_groupIds.empty()) {
int j;
for (j = 0; j < objGroupData.m_groupIds.size(); j++) {
outerCol->setGroupId(objGroupData.m_groupIds[j]);
outerCol->setGroupName(objGroupData.m_groupNames[j]);
}
for (j = 0; j < objGroupData.m_groupIds.size() &&
objGroupData.m_editingGroup >= 0;
j++)
outerCol->editGroup();
}
// Put in the right Fx group if needed
if (outerFx && !fxGroupData.m_groupIds.empty()) {
int j;
for (j = 0; j < fxGroupData.m_groupIds.size(); j++) {
outerColumn->getFx()->getAttributes()->setGroupId(
fxGroupData.m_groupIds[j]);
outerColumn->getFx()->getAttributes()->setGroupName(
fxGroupData.m_groupNames[j]);
}
for (j = 0;
j < fxGroupData.m_groupIds.size() && fxGroupData.m_editingGroup >= 0;
j++)
outerColumn->getFx()->getAttributes()->editGroup();
}
indexes.insert(index);
index++;
}
// setting column parents
for (i = 0; i < subXsh->getColumnCount() && !onlyColumn; i++) {
TStageObjectId innerId = TStageObjectId::ColumnId(i);
TStageObject *innerCol = innerTree->getStageObject(innerId, false);
xsh->setStageObjectParent(ids[innerId], ids[innerCol->getParent()]);
}
TPointD middlePoint;
int objCount = 0;
QMap<TStageObjectId, TStageObjectId>::iterator it;
for (it = ids.begin(); it != ids.end(); it++) {
TStageObject *innerObj = innerTree->getStageObject(it.key(), false);
if (!innerObj) continue;
const TPointD &pos = innerObj->getDagNodePos();
if (pos == TConst::nowhere) continue;
middlePoint = middlePoint + pos;
++objCount;
}
middlePoint = TPointD(middlePoint.x / objCount, middlePoint.y / objCount);
// faccio in modo che tutti i nodi estratti siano centrati in middlePoint
// Li metto poi in un gruppo
TPointD offset = middlePoint - subPos;
for (it = ids.begin(); it != ids.end(); it++) {
TStageObject *outerObj = outerTree->getStageObject(it.value(), false);
if (!outerObj) continue;
/*outerObj->setGroupId(groupId);
outerObj->setGroupName(L"Group "+toWideString(groupId));*/
TPointD outerPos = outerObj->getDagNodePos();
if (outerPos != TConst::nowhere) outerObj->setDagNodePos(outerPos - offset);
}
return indexes;
}
//-----------------------------------------------------------------------------
void explodeFxs(TXsheet *xsh, TXsheet *subXsh, const GroupData &fxGroupData,
QMap<TFx *, QPair<TFx *, int>> &fxs, const TPointD &subPos,
const std::vector<TFxPort *> &outPorts, bool linkToXsheet) {
FxDag *innerDag = subXsh->getFxDag();
FxDag *outerDag = xsh->getFxDag();
bool explosionLinked = false;
// taking out all the effects that start from the xsheet.
// xsheet node will be replaced by the over fx node if necessary.
// root will be null if the xsheet node will not bring out to the parent
// fxdag.
TFx *root = explodeFxSubTree(innerDag->getXsheetFx(), fxs, outerDag, xsh,
innerDag, fxGroupData, outPorts);
// in case the child and parent Xsheet nodes will be "merged"
if (!root) {
TFxSet *internals = innerDag->getTerminalFxs();
for (int j = 0; j < internals->getFxCount(); j++) {
TFx *fx = internals->getFx(j);
outerDag->addToXsheet(fxs[fx].first);
}
explosionLinked = true;
}
// taking out all the effects that start from output nodes
for (int i = 0; i < innerDag->getOutputFxCount(); i++) {
TOutputFx *outFx = innerDag->getOutputFx(i);
bool isCurrent = (outFx == innerDag->getCurrentOutputFx());
// the link is done before tracing from the current out put node.
// it means that all the fxs before the output node are already exploded and
// connected.
if (isCurrent && explosionLinked) continue;
TFx *root = explodeFxSubTree(outFx, fxs, outerDag, xsh, innerDag,
fxGroupData, outPorts);
// If the output node is not connected to any other node
if (!root) continue;
if (isCurrent) {
// link the root node to the xsheet node if:
// a) the subxsheet column is connected to the xsheet node, OR
// b) the original subxsheet column will not be deleted and the exploded
// column will be inserted.
// (this case happens when the subxsheet column contains multiple
// levels. outPorts is empty in such case)
if (linkToXsheet)
outerDag->addToXsheet(root); // connect to the xsheet node
for (int j = 0; j < outPorts.size(); j++) outPorts[j]->setFx(root);
explosionLinked = true;
}
}
// taking out all the other effects!
TFxSet *innerInternals = innerDag->getInternalFxs();
for (int i = 0; i < innerInternals->getFxCount(); i++) {
TFx *fx = innerInternals->getFx(i);
if (fxs.contains(fx)) continue;
explodeFxSubTree(fx, fxs, outerDag, xsh, innerDag, fxGroupData, outPorts);
}
assert(explosionLinked);
// cerco il punto medio tra tutti i nodi
TPointD middlePoint(0.0, 0.0);
int fxsCount = 0;
QMap<TFx *, QPair<TFx *, int>>::iterator it;
for (it = fxs.begin(); it != fxs.end(); it++) {
TFx *innerFx = it.key();
if (!innerFx) continue;
assert(innerFx->getAttributes());
const TPointD &pos = innerFx->getAttributes()->getDagNodePos();
if (pos == TConst::nowhere) continue;
middlePoint = middlePoint + pos;
++fxsCount;
}
if (fxsCount > 0)
middlePoint = TPointD(middlePoint.x / fxsCount, middlePoint.y / fxsCount);
else
middlePoint = TPointD(25000, 25000); // center of the scene
// faccio in modo che tutti i nodi estratti siano centrati in middlePoint
// Li metto poi in un gruppo
TPointD offset = middlePoint - subPos;
int groupId = outerDag->getNewGroupId();
for (it = fxs.begin(); it != fxs.end(); it++) {
QPair<TFx *, int> pair = it.value();
TFx *outerFx = pair.first;
// skip redundant item. in case when only one node is input to the xsheet
// node in the inner dag
if (!outerFx) continue;
if (outerFx->getAttributes()->getGroupId() == groupId) continue;
outerFx->getAttributes()->setGroupId(groupId);
outerFx->getAttributes()->setGroupName(L"Group " +
std::to_wstring(groupId));
TPointD outerFxPos = outerFx->getAttributes()->getDagNodePos();
if (outerFxPos != TConst::nowhere)
outerFx->getAttributes()->setDagNodePos(outerFxPos - offset);
}
}
//-----------------------------------------------------------------------------
template <typename ParamCont>
void setGrammerToParams(const ParamCont *cont,
const TSyntax::Grammar *grammer) {
for (int p = 0; p != cont->getParamCount(); ++p) {
TParam ¶m = *cont->getParam(p);
if (TDoubleParam *dp = dynamic_cast<TDoubleParam *>(¶m))
dp->setGrammar(grammer);
else if (TParamSet *paramSet = dynamic_cast<TParamSet *>(¶m))
setGrammerToParams(paramSet, grammer);
}
}
//-----------------------------------------------------------------------------
std::set<int> explode(TXsheet *xsh, TXsheet *subXsh, int index,
const TStageObjectId &parentId,
const GroupData &objGroupData, const TPointD &stageSubPos,
const GroupData &fxGroupData, const TPointD &fxSubPos,
QList<TStageObject *> &pegObjects,
QMap<TStageObjectSpline *, TStageObjectSpline *> &splines,
const std::vector<TFxPort *> &outPorts, bool onlyColumn,
bool linkToXsheet) {
// innerFx->outerFxs
QMap<TFx *, QPair<TFx *, int>> fxs;
// inner id->outer id
QMap<TStageObjectId, TStageObjectId> objIds;
std::set<int> indexes = explodeStageObjects(
xsh, subXsh, index, parentId, objGroupData, stageSubPos, fxGroupData,
pegObjects, fxs, splines, objIds, onlyColumn);
explodeFxs(xsh, subXsh, fxGroupData, fxs, fxSubPos, outPorts, linkToXsheet);
assert(TApp::instance()->getCurrentXsheet()->getXsheet() == xsh);
// reset grammers for all parameters brought out to the parent xsheet
TSyntax::Grammar *grammer = xsh->getStageObjectTree()->getGrammar();
for (auto id : objIds.values()) {
TStageObject *obj = xsh->getStageObject(id);
for (int c = 0; c != TStageObject::T_ChannelCount; ++c)
obj->getParam((TStageObject::Channel)c)->setGrammar(grammer);
if (const PlasticSkeletonDeformationP &sd =
obj->getPlasticSkeletonDeformation())
sd->setGrammar(grammer);
}
QMap<TFx *, TFx *> fxMap;
for (auto it = fxs.constBegin(); it != fxs.constEnd(); ++it) {
if (it.value().first == nullptr) continue;
setGrammerToParams(it.value().first->getParams(), grammer);
fxMap.insert(it.key(), it.value().first);
}
ExpressionReferenceManager::instance()->transferReference(subXsh, xsh, objIds,
fxMap);
return indexes;
}
//=============================================================================
// OpenChildUndo
//-----------------------------------------------------------------------------
class OpenChildUndo final : public TUndo {
int m_row, m_col;
public:
OpenChildUndo() {
TApp *app = TApp::instance();
m_row = app->getCurrentFrame()->getFrame();
m_col = app->getCurrentColumn()->getColumnIndex();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
TXshCell cell = xsh->getCell(m_row, m_col);
}
void undo() const override {
TApp *app = TApp::instance();
ToonzScene *scene = app->getCurrentScene()->getScene();
int row, col;
scene->getChildStack()->closeChild(row, col);
app->getCurrentXsheet()->setXsheet(scene->getXsheet());
changeSaveSubXsheetAsCommand();
}
void redo() const override {
TApp *app = TApp::instance();
ToonzScene *scene = app->getCurrentScene()->getScene();
scene->getChildStack()->openChild(m_row, m_col);
app->getCurrentXsheet()->setXsheet(scene->getXsheet());
changeSaveSubXsheetAsCommand();
}
int getSize() const override { return sizeof(*this); }
};
//=============================================================================
// CloseChildUndo
//-----------------------------------------------------------------------------
class CloseChildUndo final : public TUndo {
std::vector<std::pair<int, int>> m_cells;
public:
CloseChildUndo(const std::vector<std::pair<int, int>> &cells)
: m_cells(cells) {}
void undo() const override {
TApp *app = TApp::instance();
ToonzScene *scene = app->getCurrentScene()->getScene();
for (int i = m_cells.size() - 1; i >= 0; i--) {
std::pair<int, int> rowCol = m_cells[i];
scene->getChildStack()->openChild(rowCol.first, rowCol.second);
}
app->getCurrentXsheet()->setXsheet(scene->getXsheet());
changeSaveSubXsheetAsCommand();
}
void redo() const override {
TApp *app = TApp::instance();
ToonzScene *scene = app->getCurrentScene()->getScene();
for (int i = 0; i < (int)m_cells.size(); i++) {
int row, col;
scene->getChildStack()->closeChild(row, col);
}
app->getCurrentXsheet()->setXsheet(scene->getXsheet());
changeSaveSubXsheetAsCommand();
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override { return QObject::tr("Close SubXsheet"); }
int getHistoryType() override { return HistoryType::Xsheet; }
};
//=============================================================================
void openSubXsheet() {
TApp *app = TApp::instance();
/*- Enter only when ChildLevel exists in selected cell or selected column -*/
TCellSelection *cellSelection =
dynamic_cast<TCellSelection *>(TSelection::getCurrent());
TColumnSelection *columnSelection =
dynamic_cast<TColumnSelection *>(TSelection::getCurrent());
bool ret = false;
ToonzScene *scene = app->getCurrentScene()->getScene();
int row = app->getCurrentFrame()->getFrame();
int col = app->getCurrentColumn()->getColumnIndex();
TXsheet *currentXsheet = app->getCurrentXsheet()->getXsheet();
TXshCell targetCell;
/*- For column selection -*/
if (columnSelection && !columnSelection->isEmpty()) {
int sceneLength = currentXsheet->getFrameCount();
std::set<int> columnIndices = columnSelection->getIndices();
/*- Try openChild on each cell for each Column -*/
for (auto const &c : columnIndices) {
// See if the current row indicator is on an exposed sub-xsheet frame
// If so, use that.
targetCell = currentXsheet->getCell(row, c);
if (!targetCell.isEmpty() &&
(ret = scene->getChildStack()->openChild(row, c)))
break;
/*- For each Cell in the Column, if contents are found break -*/
for (int r = 0; r < sceneLength; r++) {
ret = scene->getChildStack()->openChild(r, c);
if (ret) {
targetCell = currentXsheet->getCell(r, c);
break;
}
}
if (ret) break;
}
}
/*- In other cases (cell selection or other) -*/
else {
TRect selectedArea;
/*- If it is not cell selection, see current frame / column -*/
if (!cellSelection || cellSelection->isEmpty()) {
/*- When it is not cell selection, 1 × 1 selection range -*/
selectedArea = TRect(col, row, col, row);
}
/*- In case of cell selection -*/
else {
int r0, c0, r1, c1;
cellSelection->getSelectedCells(r0, c0, r1, c1);
selectedArea = TRect(c0, r0, c1, r1);
}
/*- Try openChild on each cell in Rect -*/
for (int c = selectedArea.x0; c <= selectedArea.x1; c++) {
for (int r = selectedArea.y0; r <= selectedArea.y1; r++) {
ret = scene->getChildStack()->openChild(r, c);
if (ret) {
// When opening based on cell selection use the 1st
// exposed frame in the sub-xsheet it finds
targetCell = currentXsheet->getCell(r, c);
break;
}
}
if (ret) break;
}
}
/*- When subXsheet Level is found -*/
if (ret) {
int subXsheetFrame = 0;
if (!targetCell.isEmpty())
subXsheetFrame = targetCell.getFrameId().getNumber() - 1;
if (TSelection::getCurrent()) TSelection::getCurrent()->selectNone();
TUndoManager::manager()->add(new OpenChildUndo());
app->getCurrentXsheet()->setXsheet(scene->getXsheet());
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentColumn()->setColumnIndex(0);
app->getCurrentFrame()->setFrameIndex(subXsheetFrame);
changeSaveSubXsheetAsCommand();
} else
DVGui::error(QObject::tr("Select a sub-xsheet cell."));
}
//=============================================================================
void closeSubXsheet(int dlevel) {
if (dlevel < 1) return;
TApp *app = TApp::instance();
TSelection *selection =
TApp::instance()->getCurrentSelection()->getSelection();
if (selection) selection->selectNone();
ToonzScene *scene = app->getCurrentScene()->getScene();
int ancestorCount = scene->getChildStack()->getAncestorCount();
if (ancestorCount == 0) return;
if (dlevel > ancestorCount) dlevel = ancestorCount;
std::vector<std::pair<int, int>> cells;
for (int i = 0; i < dlevel; i++) {
std::pair<int, int> rowCol;
scene->getChildStack()->closeChild(rowCol.first, rowCol.second);
TXsheet *xsh = scene->getXsheet();
IconGenerator::instance()->invalidate(
xsh->getCell(rowCol.first, rowCol.second).m_level.getPointer(),
TFrameId(1));
cells.push_back(rowCol);
}
if (cells.empty()) return;
TUndoManager::manager()->add(new CloseChildUndo(cells));
app->getCurrentXsheet()->setXsheet(scene->getXsheet());
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentColumn()->setColumnIndex(cells[0].second);
app->getCurrentFrame()->setFrameIndex(cells[0].first);
changeSaveSubXsheetAsCommand();
}
//=============================================================================
// returns true if there is at least one pegbar to be brought inside subxsheet
// on collase in order to see if the confirmation dialog is needed
bool hasPegbarsToBringInsideChildXsheet(TXsheet *xsh,
const std::set<int> &indices) {
for (auto itr = indices.cbegin(); itr != indices.cend(); itr++) {
TStageObjectId id =
xsh->getStageObjectParent(TStageObjectId::ColumnId(*itr));
// check the parent node
if (id.isPegbar() || id.isCamera()) return true;
}
return false;
}
//-----------------------------------------------------------------------------
void bringPegbarsInsideChildXsheet(
TXsheet *xsh, TXsheet *childXsh, std::set<int> indices,
std::set<int> newIndices, QMap<TStageObjectId, TStageObjectId> &idTable) {
// columns in the child xsheet are all connected to the table for now.
// so we need to take parental connection information from the parent xsheet.
// retrieve all pegbars used from copied columns
std::set<TStageObjectId> pegbarIds;
std::set<int>::iterator itr = indices.begin();
std::set<int>::iterator new_itr = newIndices.begin();
while (itr != indices.end()) {
TStageObjectId id =
xsh->getStageObjectParent(TStageObjectId::ColumnId(*itr));
TStageObjectId newCol = TStageObjectId::ColumnId(*new_itr);
if (id.isPegbar() || id.isCamera())
childXsh->setStageObjectParent(newCol, id);
/*- Columnの上流のPegbar/Cameraを格納していく -*/
while (id.isPegbar() || id.isCamera()) {
pegbarIds.insert(id);
id = xsh->getStageObjectParent(id);
}
itr++;
new_itr++;
}
std::set<TStageObjectId>::iterator pegbarIt;
for (pegbarIt = pegbarIds.begin(); pegbarIt != pegbarIds.end(); ++pegbarIt) {
TStageObjectId id = *pegbarIt;
TStageObjectParams *data = xsh->getStageObject(id)->getParams();
TStageObject *obj = childXsh->getStageObject(id);
obj->assignParams(data);
delete data;
obj->setParent(xsh->getStageObjectParent(id));
// reset grammers of all parameters or they fails to refer to other
// parameters via expression
for (int c = 0; c != TStageObject::T_ChannelCount; ++c)
childXsh->getStageObjectTree()->setGrammar(
obj->getParam((TStageObject::Channel)c));
// register pegbars to the table
idTable.insert(id, id);
}
}
//-----------------------------------------------------------------------------
void removeFx(TXsheet *xsh, TFx *fx) {
TOutputFx *outFx = dynamic_cast<TOutputFx *>(fx);
if (outFx) {
xsh->getFxDag()->removeOutputFx(outFx);
return;
}
TFxSet *internalFx = xsh->getFxDag()->getInternalFxs();
TFxSet *terminalFx = xsh->getFxDag()->getTerminalFxs();
int j;
for (j = 0; j < fx->getInputPortCount(); j++) {
TFxPort *inputPort = fx->getInputPort(j);
TFx *inputFx = inputPort->getFx();
if (inputFx && j == 0) {
int k;
for (k = fx->getOutputConnectionCount() - 1; k >= 0; k--) {
TFxPort *outputPort = fx->getOutputConnection(k);
outputPort->setFx(inputFx);
}
if (terminalFx->containsFx(fx)) {
terminalFx->removeFx(fx);
terminalFx->addFx(inputFx);
}
}
int i;
for (i = fx->getOutputConnectionCount() - 1; i >= 0; i--)
fx->getOutputConnection(i)->setFx(inputPort->getFx());
inputPort->setFx(0);
}
internalFx->removeFx(fx);
}
//-----------------------------------------------------------------------------
void collapseColumns(std::set<int> indices, bool columnsOnly) {
// return if there is no selected columns
if (indices.empty()) return;
int index = *indices.begin();
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
std::set<int> oldIndices = indices;
StageObjectsData *data = new StageObjectsData();
// store xsheet data to be collapsed
data->storeColumns(indices, xsh, StageObjectsData::eDoClone);
data->storeColumnFxs(indices, xsh, StageObjectsData::eDoClone);
// ExpressionReferenceMonitor *monitor = xsh->getExpRefMonitor()->clone();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXshLevel *xl = scene->createNewLevel(CHILD_XSHLEVEL);
assert(xl);
TXshChildLevel *childLevel = xl->getChildLevel();
assert(childLevel);
TXsheet *childXsh = childLevel->getXsheet();
std::set<int> newIndices;
std::list<int> restoredSplineIds;
QMap<TStageObjectId, TStageObjectId> idTable;
QMap<TFx *, TFx *> fxTable;
// restore data into sub xsheet
data->restoreObjects(newIndices, restoredSplineIds, childXsh, 0, idTable,
fxTable);
// bring pegbars into sub xsheet
if (!columnsOnly)
bringPegbarsInsideChildXsheet(xsh, childXsh, indices, newIndices, idTable);
ExpressionReferenceManager::instance()->refreshXsheetRefInfo(childXsh);
ExpressionReferenceManager::instance()->transferReference(xsh, childXsh,
idTable, fxTable);
childXsh->updateFrameCount();
app->getCurrentXsheet()->blockSignals(true);
app->getCurrentObject()->blockSignals(true);
// remove columns in the parent xsheet
ColumnCmd::deleteColumns(indices, false, true);
app->getCurrentXsheet()->blockSignals(false);
app->getCurrentObject()->blockSignals(false);
// insert subxsheet column at the leftmost of the deleted columns
xsh->insertColumn(index);
// set subxsheet cells in the parent xhseet
int r, rowCount = childXsh->getFrameCount();
for (r = 0; r < rowCount; ++r)
xsh->setCell(r, index, TXshCell(xl, TFrameId(r + 1)));
// the subxsheet node will always be connected to the table
// regardless of the "columns only" option
xsh->getStageObject(TStageObjectId::ColumnId(index))
->setParent(TStageObjectId::TableId);
xsh->updateFrameCount();
// copy camera info
// xsh -> childXsh
TStageObjectTree *parentTree = xsh->getStageObjectTree();
TStageObjectTree *childTree = childXsh->getStageObjectTree();
int tmpCamId = 0;
for (int cam = 0; cam < parentTree->getCameraCount();) {
TStageObject *parentCamera =
parentTree->getStageObject(TStageObjectId::CameraId(tmpCamId), false);
// skip the deleted camera
if (!parentCamera) {
tmpCamId++;
continue;
}
// if the camera exists
if (parentCamera->getCamera()) {
// obtain the correspondent camera in subxsheet. create it if it does not
// exist
TCamera *childCamera =
childTree->getStageObject(TStageObjectId::CameraId(tmpCamId))
->getCamera();
if (parentCamera && childCamera) {
childCamera->setRes(parentCamera->getCamera()->getRes());
childCamera->setSize(parentCamera->getCamera()->getSize());
}
}
tmpCamId++;
cam++;
}
// sync the current camera
childTree->setCurrentCameraId(parentTree->getCurrentCameraId());
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentScene()->setDirtyFlag(true);
app->getCurrentObject()->notifyObjectIdSwitched();
}
//-----------------------------------------------------------------------------
void collapseColumns(std::set<int> indices,
const QList<TStageObjectId> &objIds) {
if (indices.empty()) return;
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
std::set<int> oldIndices = indices;
int index = *indices.begin();
std::vector<TStageObjectId> roots = getRoots(objIds, app->getCurrentXsheet());
TStageObject *rootObj = 0;
if (roots.size() == 1) {
rootObj = xsh->getStageObjectTree()->getStageObject(roots[0], false);
assert(rootObj);
}
StageObjectsData *data = new StageObjectsData();
data->storeObjects(objIds.toVector().toStdVector(), xsh,
StageObjectsData::eDoClone);
data->storeColumnFxs(indices, xsh, StageObjectsData::eDoClone);
// ExpressionReferenceMonitor *monitor = xsh->getExpRefMonitor()->clone();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXshLevel *xl = scene->createNewLevel(CHILD_XSHLEVEL);
assert(xl);
TXshChildLevel *childLevel = xl->getChildLevel();
assert(childLevel);
TXsheet *childXsh = childLevel->getXsheet();
std::set<int> newIndices;
std::list<int> restoredSplineIds;
QMap<TStageObjectId, TStageObjectId> idTable;
QMap<TFx *, TFx *> fxTable;
data->restoreObjects(newIndices, restoredSplineIds, childXsh, 0, idTable,
fxTable);
childXsh->updateFrameCount();
ExpressionReferenceManager::instance()->refreshXsheetRefInfo(childXsh);
ExpressionReferenceManager::instance()->transferReference(xsh, childXsh,
idTable, fxTable);
app->getCurrentXsheet()->blockSignals(true);
app->getCurrentObject()->blockSignals(true);
ColumnCmd::deleteColumns(indices, false, true);
app->getCurrentXsheet()->blockSignals(false);
app->getCurrentObject()->blockSignals(false);
xsh->insertColumn(index);
int r, rowCount = childXsh->getFrameCount();
for (r = 0; r < rowCount; r++)
xsh->setCell(r, index, TXshCell(xl, TFrameId(r + 1)));
if (roots.size() == 1 && rootObj)
xsh->getStageObject(TStageObjectId::ColumnId(index))
->setParent(rootObj->getId());
else
xsh->getStageObject(TStageObjectId::ColumnId(index))
->setParent(TStageObjectId::TableId);
xsh->updateFrameCount();
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentScene()->setDirtyFlag(true);
app->getCurrentObject()->notifyObjectIdSwitched();
}
//-----------------------------------------------------------------------------
void collapseColumns(std::set<int> indices, const std::set<TFx *> &fxs,
bool columnsOnly) {
if (indices.empty()) return;
int index = *indices.begin();
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
std::set<int> oldIndices = indices;
//++++++++++++++++++++++++++++++
StageObjectsData *data = new StageObjectsData();
data->storeColumns(indices, xsh, StageObjectsData::eDoClone);
data->storeFxs(fxs, xsh, StageObjectsData::eDoClone);
// ExpressionReferenceMonitor *monitor = xsh->getExpRefMonitor()->clone();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXshLevel *xl = scene->createNewLevel(CHILD_XSHLEVEL);
assert(xl);
TXshChildLevel *childLevel = xl->getChildLevel();
assert(childLevel);
TXsheet *childXsh = childLevel->getXsheet();
std::set<int> newIndices;
std::list<int> restoredSplineIds;
QMap<TStageObjectId, TStageObjectId> idTable;
QMap<TFx *, TFx *> fxTable;
data->restoreObjects(newIndices, restoredSplineIds, childXsh, 0, idTable,
fxTable);
if (!columnsOnly)
bringPegbarsInsideChildXsheet(xsh, childXsh, indices, newIndices, idTable);
ExpressionReferenceManager::instance()->refreshXsheetRefInfo(childXsh);
ExpressionReferenceManager::instance()->transferReference(xsh, childXsh,
idTable, fxTable);
childXsh->updateFrameCount();
std::map<TFx *, std::vector<TFxPort *>> roots =
isConnected(indices, fxs, app->getCurrentXsheet());
app->getCurrentXsheet()->blockSignals(true);
app->getCurrentObject()->blockSignals(true);
ColumnCmd::deleteColumns(indices, true, true);
app->getCurrentXsheet()->blockSignals(false);
app->getCurrentObject()->blockSignals(false);
xsh->insertColumn(index);
std::set<TFx *>::const_iterator it;
for (it = fxs.begin(); it != fxs.end(); it++) {
TOutputFx *output = dynamic_cast<TOutputFx *>(*it);
if (output) xsh->getFxDag()->removeOutputFx(output);
}
int rowCount = childXsh->getFrameCount();
int r;
for (r = 0; r < rowCount; r++)
xsh->setCell(r, index, TXshCell(xl, TFrameId(r + 1)));
//++++++++++++++++++++++++++++++
// Rimuovo gli effetti che sono in fxs dall'xsheet
std::set<TFx *>::const_iterator it2;
for (it2 = fxs.begin(); it2 != fxs.end(); it2++) removeFx(xsh, *it2);
xsh->getStageObject(TStageObjectId::ColumnId(index))
->setParent(TStageObjectId::TableId);
if (roots.size() == 1) {
TFx *fx = xsh->getColumn(index)->getFx();
std::vector<TFxPort *> rootPorts = roots.begin()->second;
int i;
for (i = 0; i < rootPorts.size(); i++) rootPorts[i]->setFx(fx);
xsh->getFxDag()->getTerminalFxs()->removeFx(fx);
}
xsh->updateFrameCount();
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentScene()->setDirtyFlag(true);
app->getCurrentObject()->notifyObjectIdSwitched();
}
//-----------------------------------------------------------------------------
void getColumnIndexes(const QList<TStageObjectId> &objects,
std::set<int> &indeces) {
int i;
for (i = 0; i < objects.size(); i++) {
if (objects[i].isColumn()) indeces.insert(objects[i].getIndex());
}
}
//-----------------------------------------------------------------------------
void getColumnIndexesAndPegbarIds(const QList<TStageObjectId> &objects,
std::set<int> &indeces,
std::set<TStageObjectId> &pegbarIds) {
int i;
for (i = 0; i < objects.size(); i++) {
if (objects[i].isColumn()) indeces.insert(objects[i].getIndex());
if (objects[i].isPegbar()) pegbarIds.insert(objects[i]);
}
}
//-----------------------------------------------------------------------------
void getColumnIndexesAndInternalFxs(const QList<TFxP> &fxs,
std::set<int> &indices,
std::set<TFx *> &internalFx) {
int i;
for (i = 0; i < fxs.size(); i++) {
TFx *fx = fxs[i].getPointer();
TColumnFx *cFx = dynamic_cast<TColumnFx *>(fx);
if (cFx)
indices.insert(cFx->getColumnIndex());
else {
TXsheetFx *xshFx = dynamic_cast<TXsheetFx *>(fx);
TOutputFx *outFx = dynamic_cast<TOutputFx *>(fx);
if (xshFx) continue;
if (outFx) {
TXsheetFx *xshFx =
dynamic_cast<TXsheetFx *>(outFx->getInputPort(0)->getFx());
if (xshFx) continue;
}
internalFx.insert(fx);
fx->addRef();
}
}
}
//=============================================================================
// CollapseUndo
//-----------------------------------------------------------------------------
class CollapseUndo : public TUndo {
protected:
std::set<int> m_indices;
StageObjectsData *m_data;
StageObjectsData *m_newData;
int m_columnIndex;
QMap<TFx *, QList<TFxPort *>> m_columnOutputConnections;
QMap<TStageObjectId, QList<TStageObjectId>> m_children;
// id->parentId
QMap<TStageObjectId, TStageObjectId> m_parents;
void doUndo() const {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
xsh->removeColumn(m_columnIndex);
std::set<int> indices = m_indices;
std::list<int> restoredSplineIds;
m_data->restoreObjects(indices, restoredSplineIds, xsh, 0);
setColumnOutputConnections(m_columnOutputConnections);
setChildren(m_children);
setParents(m_parents);
TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
app->getCurrentSelection()->getSelection());
if (selection) {
selection->selectNone();
std::set<int> selectIndices = m_indices;
std::set<int>::const_iterator indicesIt = selectIndices.begin();
while (indicesIt != selectIndices.end())
selection->selectColumn(*indicesIt++);
}
}
void doRedo(bool deleteOnlyColumns) const {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
std::set<int> indicesToRemove = m_indices;
QMap<TFx *, QList<TFxPort *>> columnOutputConnections;
getColumnOutputConnections(m_indices, columnOutputConnections);
app->getCurrentXsheet()->blockSignals(true);
app->getCurrentObject()->blockSignals(true);
ColumnCmd::deleteColumns(indicesToRemove, deleteOnlyColumns, true);
app->getCurrentXsheet()->blockSignals(false);
app->getCurrentObject()->blockSignals(false);
setColumnOutputConnections(columnOutputConnections);
std::set<int> indices;
indices.insert(m_columnIndex);
std::list<int> restoredSplineIds;
m_newData->restoreObjects(indices, restoredSplineIds, xsh, 0);
TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
app->getCurrentSelection()->getSelection());
if (selection) {
selection->selectNone();
selection->selectColumn(m_columnIndex);
}
}
public:
CollapseUndo(const std::set<int> indices, int c0, StageObjectsData *data,
StageObjectsData *newData,
const QMap<TFx *, QList<TFxPort *>> &columnOutputConnections,
const QMap<TStageObjectId, QList<TStageObjectId>> &children,
const QMap<TStageObjectId, TStageObjectId> &parents)
: m_indices(indices)
, m_columnIndex(c0)
, m_data(data)
, m_newData(newData)
, m_columnOutputConnections(columnOutputConnections)
, m_children(children)
, m_parents(parents) {}
~CollapseUndo() {
delete m_data;
delete m_newData;
}
void undo() const override {
doUndo();
TApp *app = TApp::instance();
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentObject()->notifyObjectIdSwitched();
changeSaveSubXsheetAsCommand();
}
void redo() const override {
doRedo(false);
TApp *app = TApp::instance();
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentObject()->notifyObjectIdSwitched();
changeSaveSubXsheetAsCommand();
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override { return QObject::tr("Collapse"); }
int getHistoryType() override { return HistoryType::Xsheet; }
};
//=============================================================================
// CollapseFxUndo
//-----------------------------------------------------------------------------
class CollapseFxUndo final : public CollapseUndo {
std::set<TFx *> m_fxs;
QMap<TFx *, FxConnections> m_fxConnections;
public:
CollapseFxUndo(const std::set<int> indices, int c0, StageObjectsData *data,
StageObjectsData *newData,
const QMap<TFx *, QList<TFxPort *>> &columnOutputConnections,
const QMap<TStageObjectId, QList<TStageObjectId>> children,
const QMap<TStageObjectId, TStageObjectId> &parents,
const std::set<TFx *> &fxs,
const QMap<TFx *, FxConnections> fxConnections)
: CollapseUndo(indices, c0, data, newData, columnOutputConnections,
children, parents)
, m_fxs(fxs)
, m_fxConnections(fxConnections) {}
~CollapseFxUndo() {
for (auto const &e : m_fxs) e->release();
}
void undo() const override {
doUndo();
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
TFxSet *internalFxs = xsh->getFxDag()->getInternalFxs();
TFxSet *terminalFxs = xsh->getFxDag()->getTerminalFxs();
for (auto const &e : m_fxs)
if (!internalFxs->containsFx(e)) {
TOutputFx *outFx = dynamic_cast<TOutputFx *>(e);
if (outFx)
xsh->getFxDag()->addOutputFx(outFx);
else
internalFxs->addFx(e);
}
QMap<TFx *, FxConnections>::const_iterator it2;
for (it2 = m_fxConnections.begin(); it2 != m_fxConnections.end(); it2++) {
TFx *fx = it2.key();
FxConnections connections = it2.value();
QMap<int, TFx *> inputLinks = connections.getInputLinks();
QMap<int, TFx *>::const_iterator it3;
for (it3 = inputLinks.begin(); it3 != inputLinks.end(); it3++)
fx->getInputPort(it3.key())->setFx(it3.value());
if (connections.isTerminal()) {
terminalFxs->addFx(fx);
QList<TFx *> noTerminalInputFxs = connections.getNotTerminalInputFxs();
int i;
for (i = 0; i < noTerminalInputFxs.size(); i++)
if (terminalFxs->containsFx(noTerminalInputFxs[i]))
terminalFxs->removeFx(noTerminalInputFxs[i]);
}
QMap<TFx *, int> outputLinks = connections.getOutputLinks();
QMap<TFx *, int>::const_iterator it4;
for (it4 = outputLinks.begin(); it4 != outputLinks.end(); it4++)
it4.key()->getInputPort(it4.value())->setFx(fx);
}
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentObject()->notifyObjectIdSwitched();
changeSaveSubXsheetAsCommand();
}
void redo() const override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
std::map<TFx *, std::vector<TFxPort *>> roots =
isConnected(m_indices, m_fxs, app->getCurrentXsheet());
doRedo(true);
std::set<TFx *>::const_iterator it2;
for (it2 = m_fxs.begin(); it2 != m_fxs.end(); it2++) removeFx(xsh, *it2);
if (roots.size() == 1) {
TFx *fx = xsh->getColumn(m_columnIndex)->getFx();
std::vector<TFxPort *> rootPorts = roots.begin()->second;
int i;
for (i = 0; i < rootPorts.size(); i++) rootPorts[i]->setFx(fx);
xsh->getFxDag()->getTerminalFxs()->removeFx(fx);
}
app->getCurrentXsheet()->notifyXsheetChanged();
app->getCurrentObject()->notifyObjectIdSwitched();
changeSaveSubXsheetAsCommand();
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override { return QObject::tr("Collapse (Fx)"); }
};
//=============================================================================
// ExplodeChildUndoRemovingColumn
//-----------------------------------------------------------------------------
class ExplodeChildUndoRemovingColumn final : public TUndo {
std::set<int> m_newIndexs;
int m_index;
StageObjectsData *m_oldData;
StageObjectsData *m_newData;
QMap<TFx *, QList<TFxPort *>> m_oldColumnOutputConnections;
QMap<TFx *, QList<TFxPort *>> m_newColumnOutputConnections;
// objId->parentObjId
QMap<TStageObjectId, TStageObjectId> m_parentIds;
QList<TStageObject *> m_pegObjects;
QMap<TStageObjectSpline *, TStageObjectSpline *> m_splines;
TFx *m_root;
std::set<TFx *> m_oldInternalFxs;
std::set<TOutputFx *> m_oldOutFxs;
std::set<TOutputFx *> m_newOutFxs;
// to handle grouping for the subxsheet
QStack<int> m_objGroupIds;
QStack<std::wstring> m_objGroupNames;
public:
ExplodeChildUndoRemovingColumn(
const std::set<int> &newIndexs, int index, StageObjectsData *oldData,
StageObjectsData *newData,
const QMap<TFx *, QList<TFxPort *>> &columnOutputConnections,
const QList<TStageObject *> &pegObjects,
const QMap<TStageObjectSpline *, TStageObjectSpline *> &splines,
const std::set<TFx *> &oldInternalFxs,
const std::set<TOutputFx *> oldOutFxs, TFx *root,
const QStack<int> &objGroupIds, const QStack<std::wstring> &objGroupNames)
: m_newIndexs(newIndexs)
, m_index(index)
, m_oldData(oldData)
, m_newData(newData)
, m_oldColumnOutputConnections(columnOutputConnections)
, m_pegObjects(pegObjects)
, m_splines(splines)
, m_root(root)
, m_oldInternalFxs(oldInternalFxs)
, m_oldOutFxs(oldOutFxs)
, m_objGroupIds(objGroupIds)
, m_objGroupNames(objGroupNames) {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
std::set<int>::iterator it;
for (it = m_newIndexs.begin(); it != m_newIndexs.end(); it++) {
TXshColumn *column = xsh->getColumn(*it);
TStageObjectId colId = TStageObjectId::ColumnId(*it);
m_parentIds[colId] = xsh->getStageObjectParent(colId);
TFx *columnFx = column->getFx();
if (!columnFx) continue;
QList<TFxPort *> outputConnections;
int i;
for (i = 0; i < columnFx->getOutputConnectionCount(); i++)
outputConnections.append(columnFx->getOutputConnection(i));
m_newColumnOutputConnections[columnFx] = outputConnections;
}
std::set<TOutputFx *>::iterator it2;
for (it2 = m_oldOutFxs.begin(); it2 != m_oldOutFxs.end(); it2++)
(*it2)->addRef();
int i, outFxCount = xsh->getFxDag()->getOutputFxCount();
for (i = 0; i < outFxCount; i++) {
TOutputFx *outFx = xsh->getFxDag()->getOutputFx(i);
m_newOutFxs.insert(outFx);
outFx->addRef();
}
for (int i = 0; i < m_pegObjects.size(); i++)
m_parentIds[m_pegObjects[i]->getId()] = m_pegObjects[i]->getParent();
QMap<TStageObjectSpline *, TStageObjectSpline *>::iterator it3;
for (it3 = m_splines.begin(); it3 != m_splines.end(); it3++)
it3.value()->addRef();
}
~ExplodeChildUndoRemovingColumn() {
delete m_oldData;
delete m_newData;
int i;
for (i = m_pegObjects.size() - 1; i >= 0; i--) m_pegObjects[i]->release();
std::set<TOutputFx *>::iterator it2;
for (it2 = m_oldOutFxs.begin(); it2 != m_oldOutFxs.end(); it2++)
(*it2)->release();
for (it2 = m_newOutFxs.begin(); it2 != m_newOutFxs.end(); it2++)
(*it2)->release();
QMap<TStageObjectSpline *, TStageObjectSpline *>::iterator it3;
for (it3 = m_splines.begin(); it3 != m_splines.end(); it3++)
it3.value()->release();
}
void setEditingFxGroup(TFx *fx, int editingGroup,
const QStack<int> &fxGroupIds) const {
fx->getAttributes()->closeEditingGroup(fxGroupIds.top());
while (fx->getAttributes()->getEditingGroupId() != editingGroup)
fx->getAttributes()->editGroup();
for (int i = 0; i < fx->getInputPortCount(); i++) {
TFx *inputFx = fx->getInputPort(i)->getFx();
if (inputFx) setEditingFxGroup(inputFx, editingGroup, fxGroupIds);
}
}
void setEditingObjGroup(TStageObject *obj, int editingGroup,
const QStack<int> &objGroupIds) const {
obj->closeEditingGroup(objGroupIds.top());
while (obj->getEditingGroupId() != editingGroup) obj->editGroup();
std::list<TStageObject *> children = obj->getChildren();
std::list<TStageObject *>::iterator it;
for (it = children.begin(); it != children.end(); it++) {
TStageObject *childeObj = *it;
if (childeObj) setEditingObjGroup(childeObj, editingGroup, objGroupIds);
}
}
void undo() const override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
int editingGroup = -1;
TStageObjectId parentId = TStageObjectId::NoneId;
if (m_root && m_root->getOutputConnectionCount() > 0)
editingGroup = m_root->getOutputConnection(0)
->getOwnerFx()
->getAttributes()
->getEditingGroupId();
std::set<int> indexesToRemove = m_newIndexs;
app->getCurrentXsheet()->blockSignals(true);
app->getCurrentObject()->blockSignals(true);
ColumnCmd::deleteColumns(indexesToRemove, false, true);
app->getCurrentXsheet()->blockSignals(false);
app->getCurrentObject()->blockSignals(false);
int i;
for (i = m_pegObjects.size() - 1; i >= 0; i--) {
TStageObjectId pegObjectId = m_pegObjects[i]->getId();
TStageObjectId _parentId = xsh->getStageObjectParent(pegObjectId);
if (!m_pegObjects.contains(xsh->getStageObject(_parentId)))
parentId = _parentId;
if (app->getCurrentObject()->getObjectId() == pegObjectId)
app->getCurrentObject()->setObjectId(TStageObjectId::TableId);
xsh->getStageObjectTree()->removeStageObject(pegObjectId);
}
QMap<TStageObjectSpline *, TStageObjectSpline *>::const_iterator it;
for (it = m_splines.begin(); it != m_splines.end(); it++) {
TStageObjectSpline *spline = it.value();
xsh->getStageObjectTree()->removeSpline(spline);
}
std::set<int> indexes;
indexes.insert(m_index);
std::list<int> restoredSplineIds;
m_oldData->restoreObjects(indexes, restoredSplineIds, xsh, 0);
setColumnOutputConnections(m_oldColumnOutputConnections);
TFxSet *internals = xsh->getFxDag()->getInternalFxs();
for (i = internals->getFxCount() - 1; i >= 0; i--) {
TFx *fx = internals->getFx(i);
if (m_oldInternalFxs.find(fx) == m_oldInternalFxs.end())
internals->removeFx(fx);
}
std::set<TOutputFx *>::const_iterator it2;
for (it2 = m_newOutFxs.begin(); it2 != m_newOutFxs.end(); it2++) {
if (m_oldOutFxs.find(*it2) == m_oldOutFxs.end())
xsh->getFxDag()->removeOutputFx(*it2);
}
TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
app->getCurrentSelection()->getSelection());
if (selection) {
selection->selectNone();
selection->selectColumn(m_index);
}
// reinsert in groups
TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(m_index));
if (parentId != TStageObjectId::NoneId) obj->setParent(parentId);
if (!m_objGroupIds.empty()) {
TStageObjectId parentId = obj->getParent();
TStageObject *parentObj = xsh->getStageObject(parentId);
int i;
for (i = 0; i < m_objGroupIds.size(); i++) {
obj->setGroupId(m_objGroupIds[i]);
obj->setGroupName(m_objGroupNames[i]);
}
for (i = 0;
i < m_objGroupIds.size() && parentObj->getEditingGroupId() >= 0; i++)
obj->editGroup();
}
QStack<int> fxGroupIds;
if (m_root) fxGroupIds = m_root->getAttributes()->getGroupIdStack();
if (!fxGroupIds.empty()) {
// recupero l'id del gruppo che si sta editando!
TFx *colFx = xsh->getColumn(m_index)->getFx();
assert(colFx);
colFx->getAttributes()->closeEditingGroup(fxGroupIds.top());
while (colFx->getAttributes()->getEditingGroupId() != editingGroup)
colFx->getAttributes()->editGroup();
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(m_index));
TStageObjectId parentId = obj->getParent();
TStageObject *parentObj = xsh->getStageObject(parentId);
int objEditingGroup = -1;
if (parentObj->isGrouped())
objEditingGroup = parentObj->getEditingGroupId();
TXshColumn *column = xsh->getColumn(m_index);
assert(column);
TFx *columnFx = column->getFx();
assert(columnFx);
int i;
std::vector<TFxPort *> outPorts;
for (i = 0; i < columnFx->getOutputConnectionCount(); i++)
outPorts.push_back(columnFx->getOutputConnection(i));
xsh->removeColumn(m_index);
std::set<int> indexes = m_newIndexs;
for (i = m_pegObjects.size() - 1; i >= 0; i--)
xsh->getStageObjectTree()->insertStageObject(m_pegObjects[i]);
QMap<TStageObjectSpline *, TStageObjectSpline *>::const_iterator it3;
for (it3 = m_splines.begin(); it3 != m_splines.end(); it3++)
xsh->getStageObjectTree()->insertSpline(it3.value());
std::list<int> restoredSplineIds;
m_newData->restoreObjects(indexes, restoredSplineIds, xsh, 0);
for (i = 0; i < m_pegObjects.size(); i++)
xsh->setStageObjectParent(m_pegObjects[i]->getId(),
m_parentIds[m_pegObjects[i]->getId()]);
std::set<int>::const_iterator it;
for (it = m_newIndexs.begin(); it != m_newIndexs.end(); it++) {
TStageObjectId colId = TStageObjectId::ColumnId(*it);
TStageObjectId parentId = m_parentIds[colId];
xsh->setStageObjectParent(colId, m_parentIds[colId]);
TStageObject *obj = xsh->getStageObject(colId);
TStageObject *parentObj = xsh->getStageObject(parentId);
if (parentObj->isGrouped()) {
QStack<int> idStack = parentObj->getGroupIdStack();
QStack<std::wstring> groupstack = parentObj->getGroupNameStack();
for (int i = 0; i < idStack.size(); i++) {
obj->setGroupId(idStack[i]);
obj->setGroupName(groupstack[i]);
}
int editedGroup = parentObj->getEditingGroupId();
while (editedGroup != -1 && obj->getEditingGroupId() != editedGroup)
obj->editGroup();
}
}
setColumnOutputConnections(m_newColumnOutputConnections);
for (i = 0; i < outPorts.size() && m_root; i++) outPorts[i]->setFx(m_root);
std::set<TOutputFx *>::const_iterator it2;
for (it2 = m_newOutFxs.begin(); it2 != m_newOutFxs.end(); it2++) {
if (m_oldOutFxs.find(*it2) == m_oldOutFxs.end())
xsh->getFxDag()->addOutputFx(*it2);
}
TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
app->getCurrentSelection()->getSelection());
if (selection) {
selection->selectNone();
std::set<int> selectIndices = m_newIndexs;
std::set<int>::const_iterator indicesIt = selectIndices.begin();
while (indicesIt != selectIndices.end())
selection->selectColumn(*indicesIt++);
}
QStack<int> fxGroupIds;
if (m_root) fxGroupIds = m_root->getAttributes()->getGroupIdStack();
if (!fxGroupIds.empty()) {
// recupero l'id del gruppo che si sta editando!
int editingGroup = -1;
if (m_root->getOutputConnectionCount() > 0)
editingGroup = m_root->getOutputConnection(0)
->getOwnerFx()
->getAttributes()
->getEditingGroupId();
setEditingFxGroup(m_root, editingGroup, fxGroupIds);
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override { return QObject::tr("Explode"); }
int getHistoryType() override { return HistoryType::Xsheet; }
};
//=============================================================================
// ExplodeChildUndo
//-----------------------------------------------------------------------------
class ExplodeChildUndoWithoutRemovingColumn final : public TUndo {
std::set<int> m_newIndexs;
int m_index, m_from, m_to;
TCellData *m_cellData;
StageObjectsData *m_newData;
QMap<TFx *, QList<TFxPort *>> m_newColumnOutputConnections;
QList<TStageObject *> m_pegObjects;
QMap<TStageObjectSpline *, TStageObjectSpline *> m_splines;
std::set<TFx *> m_oldInternalFxs;
std::set<TOutputFx *> m_oldOutFxs;
std::set<TOutputFx *> m_newOutFxs;
// to handle grouping for the subxsheet
QStack<int> m_objGroupIds;
QStack<std::wstring> m_objGroupNames;
public:
ExplodeChildUndoWithoutRemovingColumn(
const std::set<int> &newIndexs, int index, int from, int to,
TCellData *cellData, StageObjectsData *newData,
QList<TStageObject *> pegObjects,
const QMap<TStageObjectSpline *, TStageObjectSpline *> &splines,
const std::set<TFx *> &oldInternalFxs,
const std::set<TOutputFx *> oldOutFxs, const QStack<int> &objGroupIds,
const QStack<std::wstring> &objGroupNames)
: m_newIndexs(newIndexs)
, m_index(index)
, m_from(from)
, m_to(to)
, m_cellData(cellData)
, m_newData(newData)
, m_pegObjects(pegObjects)
, m_splines(splines)
, m_oldInternalFxs(oldInternalFxs)
, m_oldOutFxs(oldOutFxs)
, m_objGroupIds(objGroupIds)
, m_objGroupNames(objGroupNames) {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
std::set<int>::iterator it;
for (it = m_newIndexs.begin(); it != m_newIndexs.end(); it++) {
TXshColumn *column = xsh->getColumn(*it);
TFx *columnFx = column->getFx();
QList<TFxPort *> outputConnections;
int i;
for (i = 0; i < columnFx->getOutputConnectionCount(); i++)
outputConnections.append(columnFx->getOutputConnection(i));
m_newColumnOutputConnections[columnFx] = outputConnections;
}
std::set<TOutputFx *>::iterator it2;
for (it2 = m_oldOutFxs.begin(); it2 != m_oldOutFxs.end(); it2++)
(*it2)->addRef();
int i, outFxCount = xsh->getFxDag()->getOutputFxCount();
for (i = 0; i < outFxCount; i++) {
TOutputFx *outFx = xsh->getFxDag()->getOutputFx(i);
m_newOutFxs.insert(outFx);
outFx->addRef();
}
}
~ExplodeChildUndoWithoutRemovingColumn() {
delete m_cellData;
delete m_newData;
int i;
for (i = m_pegObjects.size() - 1; i >= 0; i--) m_pegObjects[i]->release();
std::set<TOutputFx *>::iterator it2;
for (it2 = m_oldOutFxs.begin(); it2 != m_oldOutFxs.end(); it2++)
(*it2)->release();
for (it2 = m_newOutFxs.begin(); it2 != m_newOutFxs.end(); it2++)
(*it2)->release();
}
void undo() const override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
std::set<int> indexesToRemove = m_newIndexs;
app->getCurrentXsheet()->blockSignals(true);
app->getCurrentObject()->blockSignals(true);
ColumnCmd::deleteColumns(indexesToRemove, false, true);
app->getCurrentXsheet()->blockSignals(false);
app->getCurrentObject()->blockSignals(false);
int i;
for (i = m_pegObjects.size() - 1; i >= 0; i--)
xsh->getStageObjectTree()->removeStageObject(m_pegObjects[i]->getId());
std::set<int> indexes;
indexes.insert(m_index);
int to = m_to;
int index = m_index;
m_cellData->getCells(xsh, m_from, m_index, to, index, false, false);
TFxSet *internals = xsh->getFxDag()->getInternalFxs();
for (i = internals->getFxCount() - 1; i >= 0; i--) {
TFx *fx = internals->getFx(i);
if (m_oldInternalFxs.find(fx) == m_oldInternalFxs.end())
internals->removeFx(fx);
}
std::set<TOutputFx *>::const_iterator it;
for (it = m_newOutFxs.begin(); it != m_newOutFxs.end(); it++) {
if (m_oldOutFxs.find(*it) == m_oldOutFxs.end())
xsh->getFxDag()->removeOutputFx(*it);
}
TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
app->getCurrentSelection()->getSelection());
if (selection) {
selection->selectNone();
selection->selectColumn(m_index);
}
// reinsert in groups
if (!m_objGroupIds.empty()) {
TStageObject *obj =
xsh->getStageObject(TStageObjectId::ColumnId(m_index));
TStageObjectId parentId = obj->getParent();
TStageObject *parentObj = xsh->getStageObject(parentId);
int i;
for (i = 0; i < m_objGroupIds.size(); i++) {
obj->setGroupId(m_objGroupIds[i]);
obj->setGroupName(m_objGroupNames[i]);
}
for (i = 0;
i < m_objGroupIds.size() && parentObj->getEditingGroupId() >= 0; i++)
obj->editGroup();
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const override {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
xsh->clearCells(m_from, m_index, m_to - m_from + 1);
std::set<int> indexes = m_newIndexs;
int i;
for (i = m_pegObjects.size() - 1; i >= 0; i--)
xsh->getStageObjectTree()->insertStageObject(m_pegObjects[i]);
std::list<int> restoredSplineIds;
m_newData->restoreObjects(indexes, restoredSplineIds, xsh, 0);
setColumnOutputConnections(m_newColumnOutputConnections);
std::set<TOutputFx *>::const_iterator it;
for (it = m_newOutFxs.begin(); it != m_newOutFxs.end(); it++) {
if (m_oldOutFxs.find(*it) == m_oldOutFxs.end())
xsh->getFxDag()->addOutputFx(*it);
}
TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
app->getCurrentSelection()->getSelection());
if (selection) {
selection->selectNone();
std::set<int> selectIndices = m_newIndexs;
std::set<int>::const_iterator indicesIt = selectIndices.begin();
while (indicesIt != selectIndices.end())
selection->selectColumn(*indicesIt++);
}
// reinsert in groups
if (!m_objGroupIds.empty()) {
for (auto const &e : indexes) {
TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(e));
TStageObjectId parentId = obj->getParent();
TStageObject *parentObj = xsh->getStageObject(parentId);
int i;
for (i = 0; i < m_objGroupIds.size(); i++) {
obj->setGroupId(m_objGroupIds[i]);
obj->setGroupName(m_objGroupNames[i]);
}
for (i = 0;
i < m_objGroupIds.size() && parentObj->getEditingGroupId() >= 0;
i++)
obj->editGroup();
}
}
app->getCurrentXsheet()->notifyXsheetChanged();
}
int getSize() const override { return sizeof(*this); }
QString getHistoryString() override { return QObject::tr("Explode"); }
int getHistoryType() override { return HistoryType::Xsheet; }
};
} // namespace
//=============================================================================
// OpenChildCommand
//-----------------------------------------------------------------------------
class OpenChildCommand final : public MenuItemHandler {
public:
OpenChildCommand() : MenuItemHandler(MI_OpenChild) {}
void execute() override { openSubXsheet(); }
} openChildCommand;
//=============================================================================
// CloseChildCommand
//-----------------------------------------------------------------------------
class CloseChildCommand final : public MenuItemHandler {
public:
CloseChildCommand() : MenuItemHandler(MI_CloseChild) {}
void execute() override { closeSubXsheet(1); }
} closeChildCommand;
//=============================================================================
// collapseColumns
//-----------------------------------------------------------------------------
//! Collapses the specified column indices in current XSheet.
void SubsceneCmd::collapse(std::set<int> &indices) {
if (indices.empty()) return;
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
bool onlyColumns = true;
if (hasPegbarsToBringInsideChildXsheet(xsh, indices)) {
// User must decide if pegbars must be collapsed too
QString question(QObject::tr("Collapsing columns: what you want to do?"));
QList<QString> list;
list.append(QObject::tr(
"Maintain parenting relationships in the sub-xsheet as well."));
list.append(
QObject::tr("Include the selected columns in the sub-xsheet without "
"parenting info."));
int ret = DVGui::RadioButtonMsgBox(DVGui::WARNING, question, list);
if (ret == 0) return;
onlyColumns = (ret == 2);
}
if (!ColumnCmd::checkExpressionReferences(indices, onlyColumns, true)) return;
std::set<int> oldIndices = indices;
int index = *indices.begin();
// Retrieve current status to backup it in the UNDO
StageObjectsData *oldData = new StageObjectsData();
oldData->storeColumns(indices, xsh, 0);
oldData->storeColumnFxs(indices, xsh, 0);
QMap<TFx *, QList<TFxPort *>> columnOutputConnections;
getColumnOutputConnections(indices, columnOutputConnections);
QMap<TStageObjectId, QList<TStageObjectId>> children;
getChildren(indices, children);
QMap<TStageObjectId, TStageObjectId> parents;
getParents(indices, parents);
// Perform the collapse
collapseColumns(indices, onlyColumns);
setColumnOutputConnections(columnOutputConnections);
// Retrieve current status to backup it in the REDO
indices.clear();
indices.insert(index);
StageObjectsData *newData = new StageObjectsData();
newData->storeColumns(indices, xsh, 0);
newData->storeColumnFxs(indices, xsh, 0);
// Build the undo
CollapseUndo *undo =
new CollapseUndo(oldIndices, index, oldData, newData,
columnOutputConnections, children, parents);
TUndoManager::manager()->add(undo);
changeSaveSubXsheetAsCommand();
}
//-----------------------------------------------------------------------------
void SubsceneCmd::collapse(const QList<TStageObjectId> &objects) {
if (objects.isEmpty()) return;
std::set<int> indices;
getColumnIndexes(objects, indices);
if (!ColumnCmd::checkExpressionReferences(objects)) return;
std::set<int> oldIndices = indices;
int index = *indices.begin();
StageObjectsData *oldData = new StageObjectsData();
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
oldData->storeColumns(indices, xsh, 0);
oldData->storeColumnFxs(indices, xsh, 0);
QMap<TFx *, QList<TFxPort *>> columnOutputConnections;
getColumnOutputConnections(indices, columnOutputConnections);
QMap<TStageObjectId, QList<TStageObjectId>> children;
getChildren(indices, children);
QMap<TStageObjectId, TStageObjectId> parents;
getParents(indices, parents);
collapseColumns(indices, objects);
setColumnOutputConnections(columnOutputConnections);
indices.clear();
indices.insert(index);
StageObjectsData *newData = new StageObjectsData();
newData->storeColumns(indices, xsh, 0);
newData->storeColumnFxs(indices, xsh, 0);
CollapseUndo *undo =
new CollapseUndo(oldIndices, index, oldData, newData,
columnOutputConnections, children, parents);
TUndoManager::manager()->add(undo);
changeSaveSubXsheetAsCommand();
}
//-----------------------------------------------------------------------------
void SubsceneCmd::collapse(const QList<TFxP> &fxs) {
if (fxs.isEmpty()) return;
std::set<int> indices;
std::set<TFx *> internalFx;
getColumnIndexesAndInternalFxs(fxs, indices, internalFx);
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
bool onlyColumns = true;
if (hasPegbarsToBringInsideChildXsheet(xsh, indices)) {
// User must decide if pegbars must be collapsed too
QString question(QObject::tr("Collapsing columns: what you want to do?"));
QList<QString> list;
list.append(QObject::tr(
"Maintain parenting relationships in the sub-xsheet as well."));
list.append(
QObject::tr("Include the selected columns in the sub-xsheet without "
"parenting info."));
int ret = DVGui::RadioButtonMsgBox(DVGui::WARNING, question, list);
if (ret == 0) return;
onlyColumns = (ret == 2);
}
if (!ColumnCmd::checkExpressionReferences(indices, internalFx, onlyColumns,
true))
return;
std::set<int> oldIndices = indices;
int index = *indices.begin();
StageObjectsData *oldData = new StageObjectsData();
oldData->storeColumns(indices, xsh, 0);
oldData->storeColumnFxs(indices, xsh, 0);
QMap<TFx *, QList<TFxPort *>> columnOutputConnections;
getColumnOutputConnections(indices, columnOutputConnections);
QMap<TStageObjectId, QList<TStageObjectId>> children;
getChildren(indices, children);
QMap<TStageObjectId, TStageObjectId> parents;
getParents(indices, parents);
QMap<TFx *, FxConnections> fxConnections;
getFxConnections(fxConnections, internalFx, xsh);
collapseColumns(indices, internalFx, onlyColumns);
indices.clear();
indices.insert(index);
StageObjectsData *newData = new StageObjectsData();
newData->storeColumns(indices, xsh, 0);
newData->storeColumnFxs(indices, xsh, 0);
CollapseFxUndo *undo = new CollapseFxUndo(oldIndices, index, oldData, newData,
columnOutputConnections, children,
parents, internalFx, fxConnections);
TUndoManager::manager()->add(undo);
changeSaveSubXsheetAsCommand();
}
//-----------------------------------------------------------------------------
void SubsceneCmd::explode(int index) {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
TFrameHandle *frameHandle = app->getCurrentFrame();
assert(frameHandle->getFrameType() == TFrameHandle::SceneFrame);
int frameIndex = app->getCurrentFrame()->getFrame();
/*- これからExplodeするセルを取得 -*/
TXshCell cell = xsh->getCell(frameIndex, index);
TXshChildLevel *childLevel = cell.getChildLevel();
if (!childLevel) return;
// Cannot remove the column if it contains frames of a TXshSimpleLevel.
int from, to;
// removeColumn is true if the column contains only one subXsheetLevel (i.e.
// the column will be removed) removeColumn is false if there is another level
// in the same column (i.e. the column will remain)
bool removeColumn =
mustRemoveColumn(from, to, childLevel, xsh, index, frameIndex);
/*- Pegbarを親Sheetに持って出るか?の質問ダイアログ -*/
QString question(QObject::tr("Exploding Sub-xsheet: what you want to do?"));
QList<QString> list;
list.append(
QObject::tr("Maintain parenting relationships in the main xsheet."));
list.append(
QObject::tr("Bring columns in the main xsheet without parenting."));
int ret = DVGui::RadioButtonMsgBox(DVGui::WARNING, question, list);
if (ret == 0) return;
if (!ExpressionReferenceManager::instance()->checkExplode(
childLevel->getXsheet(), index, removeColumn, ret == 2))
return;
// Collect column stage object information
TStageObjectId colId = TStageObjectId::ColumnId(index);
TStageObjectId parentId = xsh->getStageObjectParent(colId);
TStageObject *obj = xsh->getStageObjectTree()->getStageObject(colId, false);
assert(obj);
// Collect StageObjects group information
QStack<int> objGroupIds;
QStack<std::wstring> objGroupNames;
int objEditingGroup = obj->getEditingGroupId();
if (obj->isGrouped()) {
int i = 0;
objGroupIds = obj->getGroupIdStack();
objGroupNames = obj->getGroupNameStack();
while (objGroupIds.empty() && objEditingGroup >= 0 &&
objGroupIds[i] != objEditingGroup) {
objGroupIds.remove(i);
objGroupNames.remove(i);
i++;
}
}
GroupData objGroupData(objGroupIds, objGroupNames, objEditingGroup);
// Collect column fx information
/*- FxのGroupの管理 -*/
TXshColumn *column = xsh->getColumn(index);
TFx *columnFx = column->getFx();
TFxAttributes *attr = columnFx->getAttributes();
// Collect Fx group information
QStack<int> fxGroupIds;
QStack<std::wstring> fxGroupNames;
int fxEditingGroup = attr->getEditingGroupId();
if (attr->isGrouped()) {
int i = 0;
fxGroupIds = attr->getGroupIdStack();
fxGroupNames = attr->getGroupNameStack();
while (!fxGroupIds.empty() && fxEditingGroup >= 0 &&
fxGroupIds[i] != fxEditingGroup) {
fxGroupIds.remove(i);
fxGroupNames.remove(i);
i++;
}
}
GroupData fxGroupData(fxGroupIds, fxGroupNames, fxEditingGroup);
/*- Explode前のOutputFxのリストを取得 (oldOutFxs) -*/
std::set<TOutputFx *> oldOutFxs;
int i, outFxCount = xsh->getFxDag()->getOutputFxCount();
for (i = 0; i < outFxCount; i++)
oldOutFxs.insert(xsh->getFxDag()->getOutputFx(i));
std::vector<TFxPort *> outPorts;
QList<TStageObject *> pegObjects;
QMap<TStageObjectSpline *, TStageObjectSpline *> splines;
TPointD fxSubPos = attr->getDagNodePos();
TPointD stageSubPos = obj->getDagNodePos();
if (removeColumn) {
/*- SubXsheetカラムノードから繋がっているFxPortのリストを取得 (outPorts) -*/
for (i = 0; i < columnFx->getOutputConnectionCount(); i++)
outPorts.push_back(columnFx->getOutputConnection(i));
bool wasLinkedToXsheet =
xsh->getFxDag()->getTerminalFxs()->containsFx(columnFx);
// Collect data for undo
std::set<int> indexes;
indexes.insert(index);
/*- Undoのためのデータの取得 -*/
StageObjectsData *oldData = new StageObjectsData();
oldData->storeColumns(indexes, xsh, 0);
oldData->storeColumnFxs(indexes, xsh, 0);
QMap<TFx *, QList<TFxPort *>> columnOutputConnections;
getColumnOutputConnections(indexes, columnOutputConnections);
TFxSet *internals = xsh->getFxDag()->getInternalFxs();
std::set<TFx *> oldInternalFxs;
internals->getFxs(oldInternalFxs);
// Remove column
xsh->removeColumn(index);
// The above removing operation may decrement the parentId, in case that
// 1, the parent object is column, and
// 2, the parent column is placed on the right side of the removed column
// ( i.e. index of the parent column is higher than "index")
if (parentId.isColumn() && parentId.getIndex() > index)
parentId = TStageObjectId::ColumnId(parentId.getIndex() - 1);
// Explode
std::set<int> newIndexes =
::explode(xsh, childLevel->getXsheet(), index, parentId, objGroupData,
stageSubPos, fxGroupData, fxSubPos, pegObjects, splines,
outPorts, ret == 2, wasLinkedToXsheet);
/*- Redoのためのデータの取得 -*/
StageObjectsData *newData = new StageObjectsData();
newData->storeColumns(newIndexes, xsh, 0);
newData->storeColumnFxs(newIndexes, xsh, 0);
TFx *root = 0;
assert(!columnOutputConnections.empty());
QList<TFxPort *> ports = columnOutputConnections.begin().value();
if (!ports.empty()) root = (*ports.begin())->getFx();
ExplodeChildUndoRemovingColumn *undo = new ExplodeChildUndoRemovingColumn(
newIndexes, index, oldData, newData, columnOutputConnections,
pegObjects, splines, oldInternalFxs, oldOutFxs, root, objGroupIds,
objGroupNames);
TUndoManager::manager()->add(undo);
} else {
// keep outPorts empty since the exploded node will be re-connected to the
// xsheet node
// Collect information for undo
TCellData *cellData = new TCellData();
cellData->setCells(xsh, from, index, to, index);
TFxSet *internals = xsh->getFxDag()->getInternalFxs();
std::set<TFx *> oldInternalFxs;
internals->getFxs(oldInternalFxs);
// Remove cells
xsh->clearCells(from, index, to - from + 1);
// Explode
std::set<int> newIndexes = ::explode(
xsh, childLevel->getXsheet(), index + 1, parentId, objGroupData,
stageSubPos + TPointD(10, 10), fxGroupData, fxSubPos + TPointD(10, 10),
pegObjects, splines, outPorts, ret == 2, true);
StageObjectsData *newData = new StageObjectsData();
newData->storeColumns(newIndexes, xsh, 0);
newData->storeColumnFxs(newIndexes, xsh, 0);
ExplodeChildUndoWithoutRemovingColumn *undo =
new ExplodeChildUndoWithoutRemovingColumn(
newIndexes, index, from, to, cellData, newData, pegObjects, splines,
oldInternalFxs, oldOutFxs, objGroupIds, objGroupNames);
TUndoManager::manager()->add(undo);
}
app->getCurrentXsheet()->notifyXsheetChanged();
}