1
2
3 // TnzCore includes
4 #include "tconst.h"
5 #include "tundo.h"
6
7 // TnzBase includes
8 #include "tfx.h"
9 #include "tfxattributes.h"
10 #include "tparamcontainer.h"
11 #include "tparamset.h"
12
13 // TnzLib includes
14 #include "toonz/tframehandle.h"
15 #include "toonz/tcolumnhandle.h"
16 #include "toonz/txsheethandle.h"
17 #include "toonz/tobjecthandle.h"
18 #include "toonz/tscenehandle.h"
19 #include "toonz/txshcell.h"
20 #include "toonz/txsheet.h"
21 #include "toonz/toonzscene.h"
22 #include "toonz/childstack.h"
23 #include "toonz/txshleveltypes.h"
24 #include "toonz/txshchildlevel.h"
25 #include "toonz/tstageobject.h"
26 #include "toonz/tcolumnfx.h"
27 #include "toonz/fxcommand.h"
28 #include "toonz/tcolumnfxset.h"
29 #include "toonz/fxdag.h"
30 #include "toonz/tstageobjecttree.h"
31 #include "toonz/tstageobjectspline.h"
32 #include "toonz/tcamera.h"
33 #include "toonz/expressionreferencemonitor.h"
34
35 // TnzQt includes
36 #include "toonzqt/menubarcommand.h"
37 #include "toonzqt/icongenerator.h"
38 #include "toonzqt/tselectionhandle.h"
39 #include "toonzqt/selection.h"
40 #include "toonzqt/dvdialog.h"
41 #include "toonzqt/stageobjectsdata.h"
42 #include "historytypes.h"
43
44 // Toonz includes
45 #include "columncommand.h"
46 #include "menubarcommandids.h"
47 #include "celldata.h"
48 #include "tapp.h"
49 #include "columnselection.h"
50 #include "cellselection.h"
51 #include "expressionreferencemanager.h"
52
53 #include "subscenecommand.h"
54
55 //*****************************************************************************
56 // Local namespace
57 //*****************************************************************************
58
59 namespace {
60
61 struct GroupData {
62 public:
63 QStack<int> m_groupIds;
64 QStack<std::wstring> m_groupNames;
65 int m_editingGroup;
66
GroupData__anon3e5639860111::GroupData67 GroupData(const QStack<int> &groupIds, const QStack<std::wstring> &groupNames,
68 int editingGroup)
69 : m_groupIds(groupIds)
70 , m_groupNames(groupNames)
71 , m_editingGroup(editingGroup) {}
72 };
73
74 //-----------------------------------------------------------------------------
75
76 // Zerary fxs and zerary COLUMN fxs are separate, and fx port connections
77 // are stored in the actual zerary fx.
getActualFx(TFx * fx)78 TFx *getActualFx(TFx *fx) {
79 TZeraryColumnFx *zeraryColumnFx = dynamic_cast<TZeraryColumnFx *>(fx);
80 return zeraryColumnFx ? zeraryColumnFx->getZeraryFx() : fx;
81 }
82
83 //-----------------------------------------------------------------------------
84
setFxParamToCurrentScene(TFx * fx,TXsheet * xsh)85 void setFxParamToCurrentScene(TFx *fx, TXsheet *xsh) {
86 for (int i = 0; i < fx->getParams()->getParamCount(); i++) {
87 TParam *param = fx->getParams()->getParam(i);
88 if (TDoubleParam *dp = dynamic_cast<TDoubleParam *>(param))
89 xsh->getStageObjectTree()->setGrammar(dp);
90 else if (dynamic_cast<TPointParam *>(param) ||
91 dynamic_cast<TRangeParam *>(param) ||
92 dynamic_cast<TPixelParam *>(param)) {
93 TParamSet *paramSet = dynamic_cast<TParamSet *>(param);
94 assert(paramSet);
95 int f;
96 for (f = 0; f < paramSet->getParamCount(); f++) {
97 TDoubleParam *dp =
98 dynamic_cast<TDoubleParam *>(paramSet->getParam(f).getPointer());
99 if (!dp) continue;
100 xsh->getStageObjectTree()->setGrammar(dp);
101 }
102 }
103 }
104 }
105
106 //-----------------------------------------------------------------------------
107
getRoots(const QList<TStageObjectId> & objIds,TXsheetHandle * xshHandle)108 std::vector<TStageObjectId> getRoots(const QList<TStageObjectId> &objIds,
109 TXsheetHandle *xshHandle) {
110 std::vector<TStageObjectId> roots;
111 std::map<TStageObjectId, std::string> parentHandles;
112 TStageObjectTree *pegTree = xshHandle->getXsheet()->getStageObjectTree();
113 for (int i = 0; i < objIds.size(); i++) {
114 TStageObject *obj = pegTree->getStageObject(objIds.at(i), false);
115 TStageObjectId parentId = obj->getParent();
116 bool parentIsColumn = parentId.isColumn() && !objIds.contains(parentId);
117 std::string parentHandle = obj->getParentHandle();
118 if (!parentIsColumn && !objIds.contains(parentId) &&
119 (parentHandles.count(parentId) == 0 ||
120 parentHandles[parentId] != parentHandle)) {
121 parentHandles[parentId] = parentHandle;
122 roots.push_back(parentId);
123 }
124 }
125 return roots;
126 }
127
128 //-----------------------------------------------------------------------------
129
isConnected(const std::set<int> & indices,const std::set<TStageObjectId> & pegbarIds,TXsheetHandle * xshHandle)130 std::vector<TStageObjectId> isConnected(
131 const std::set<int> &indices, const std::set<TStageObjectId> &pegbarIds,
132 TXsheetHandle *xshHandle) {
133 std::vector<TStageObjectId> roots;
134 std::map<TStageObjectId, std::string> parentHandles;
135 TStageObjectTree *pegTree = xshHandle->getXsheet()->getStageObjectTree();
136 std::set<int>::const_iterator it;
137 for (it = indices.begin(); it != indices.end(); it++) {
138 TStageObjectId id = TStageObjectId::ColumnId(*it);
139 TStageObject *obj = pegTree->getStageObject(id, false);
140 TStageObjectId parentId = obj->getParent();
141 std::string parentHandle = obj->getParentHandle();
142 bool parentIsColumn = parentId.isColumn() &&
143 indices.find(parentId.getIndex()) != indices.end();
144 if (!parentIsColumn && pegbarIds.find(parentId) == pegbarIds.end() &&
145 (parentHandles.count(parentId) == 0 ||
146 parentHandles[parentId] != parentHandle)) {
147 parentHandles[parentId] = parentHandle;
148 roots.push_back(parentId);
149 }
150 }
151 std::set<TStageObjectId>::const_iterator it2;
152 for (it2 = pegbarIds.begin(); it2 != pegbarIds.end(); it2++) {
153 TStageObject *obj = pegTree->getStageObject(*it2, false);
154 TStageObjectId parentId = obj->getParent();
155 bool parentIsColumn = parentId.isColumn() &&
156 indices.find(parentId.getIndex()) != indices.end();
157 std::string parentHandle = obj->getParentHandle();
158 if (!parentIsColumn && pegbarIds.find(parentId) == pegbarIds.end() &&
159 (parentHandles.count(parentId) == 0 ||
160 parentHandles[parentId] != parentHandle)) {
161 parentHandles[parentId] = parentHandle;
162 roots.push_back(parentId);
163 }
164 }
165 return roots;
166 }
167
168 //-----------------------------------------------------------------------------
169
isConnected(const std::set<int> & indices,const std::set<TFx * > & internalFxs,TXsheetHandle * xshHandle)170 std::map<TFx *, std::vector<TFxPort *>> isConnected(
171 const std::set<int> &indices, const std::set<TFx *> &internalFxs,
172 TXsheetHandle *xshHandle) {
173 TXsheet *xsh = xshHandle->getXsheet();
174 std::set<int>::const_iterator it;
175 std::map<TFx *, std::vector<TFxPort *>> roots;
176 for (it = indices.begin(); it != indices.end(); it++) {
177 TFx *fx = xsh->getColumn(*it)->getFx();
178 int i, outputCount = fx->getOutputConnectionCount();
179 for (i = 0; i < outputCount; i++) {
180 TFx *outFx = fx->getOutputConnection(i)->getOwnerFx();
181 if (internalFxs.find(outFx) == internalFxs.end())
182 roots[fx].push_back(fx->getOutputConnection(i));
183 }
184 }
185 std::set<TFx *>::const_iterator it2;
186 for (it2 = internalFxs.begin(); it2 != internalFxs.end(); it2++) {
187 int i, outputCount = (*it2)->getOutputConnectionCount();
188 for (i = 0; i < outputCount; i++) {
189 TFx *outFx = (*it2)->getOutputConnection(i)->getOwnerFx();
190 if (internalFxs.find(outFx) == internalFxs.end())
191 roots[*it2].push_back((*it2)->getOutputConnection(i));
192 }
193 }
194 return roots;
195 }
196
197 //-----------------------------------------------------------------------------
198
199 // returns true if the column indexed with col contains only the childLevel.
200 // if not, false is returned and in from and to is put the frame range contained
201 // the frame indexed with row.
mustRemoveColumn(int & from,int & to,TXshChildLevel * childLevel,TXsheet * xsh,int col,int row)202 bool mustRemoveColumn(int &from, int &to, TXshChildLevel *childLevel,
203 TXsheet *xsh, int col, int row) {
204 bool removeColumn = true;
205 bool rangeFound = false;
206 from = -1;
207 to = -1;
208 int i, r0, r1;
209 xsh->getColumn(col)->getRange(r0, r1);
210 for (i = r0; i <= r1; i++) {
211 TXshCell cell = xsh->getCell(i, col);
212 TXshChildLevel *app = cell.getChildLevel();
213 if (app != childLevel) {
214 removeColumn = false;
215 if (from != -1 && to != -1) {
216 rangeFound = from <= row && row <= to;
217 if (!rangeFound) from = to = -1;
218 }
219 continue;
220 }
221 if (from == -1 && !rangeFound) {
222 from = to = i;
223 } else if (from != -1 && !rangeFound) {
224 to = i;
225 }
226 }
227 return removeColumn;
228 }
229
230 //-----------------------------------------------------------------------------
231
232 class FxConnections {
233 bool m_isTerminal;
234 QMap<int, TFx *> m_inputLinks;
235 QMap<TFx *, int> m_outputLinks;
236 QList<TFx *> m_notTerminalInputFxs;
237
238 public:
FxConnections()239 FxConnections() {}
~FxConnections()240 ~FxConnections() {}
241
setIsTerminal(bool isTerminal)242 void setIsTerminal(bool isTerminal) { m_isTerminal = isTerminal; }
setInputLink(int portIndex,TFx * inputFx)243 void setInputLink(int portIndex, TFx *inputFx) {
244 m_inputLinks[portIndex] = inputFx;
245 }
setOutputLink(TFx * outputFx,int portIndex)246 void setOutputLink(TFx *outputFx, int portIndex) {
247 m_outputLinks[outputFx] = portIndex;
248 }
addNotTerminalInputFx(TFx * fx)249 void addNotTerminalInputFx(TFx *fx) { m_notTerminalInputFxs.append(fx); }
getInputLinks()250 QMap<int, TFx *> getInputLinks() { return m_inputLinks; }
getOutputLinks()251 QMap<TFx *, int> getOutputLinks() { return m_outputLinks; }
getNotTerminalInputFxs()252 QList<TFx *> getNotTerminalInputFxs() { return m_notTerminalInputFxs; }
isTerminal()253 bool isTerminal() { return m_isTerminal; }
254 };
255
256 //-----------------------------------------------------------------------------
257
getFxConnections(QMap<TFx *,FxConnections> & fxConnetcions,const std::set<TFx * > & fxs,TXsheet * xsh)258 void getFxConnections(QMap<TFx *, FxConnections> &fxConnetcions,
259 const std::set<TFx *> &fxs, TXsheet *xsh) {
260 TFxSet *terminalFxs = xsh->getFxDag()->getTerminalFxs();
261 for (auto const &fx : fxs) {
262 FxConnections connections;
263 connections.setIsTerminal(terminalFxs->containsFx(fx));
264 int i;
265 for (i = 0; i < fx->getInputPortCount(); i++) {
266 TFx *inputFx = fx->getInputPort(i)->getFx();
267 connections.setInputLink(i, inputFx);
268 if (connections.isTerminal()) connections.addNotTerminalInputFx(inputFx);
269 }
270 for (i = 0; i < fx->getOutputConnectionCount(); i++) {
271 TFx *outputFx = fx->getOutputConnection(i)->getOwnerFx();
272 int j, inputCount = outputFx->getInputPortCount();
273 if (inputCount == 0) continue;
274 for (j = 0; j < inputCount; j++) {
275 TFx *inputFx = outputFx->getInputPort(j)->getFx();
276 if (inputFx == fx) break;
277 }
278 connections.setOutputLink(outputFx, j);
279 }
280 fxConnetcions[fx] = connections;
281 }
282 }
283
284 //-----------------------------------------------------------------------------
285
changeSaveSubXsheetAsCommand()286 void changeSaveSubXsheetAsCommand() {
287 ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
288 bool isSubxsheet = scene->getChildStack()->getAncestorCount() > 0;
289 CommandManager::instance()->enable(MI_SaveSubxsheetAs, isSubxsheet);
290 }
291
292 //-----------------------------------------------------------------------------
293
getColumnOutputConnections(const std::set<int> & indices,QMap<TFx *,QList<TFxPort * >> & columnOutputConnections)294 void getColumnOutputConnections(
295 const std::set<int> &indices,
296 QMap<TFx *, QList<TFxPort *>> &columnOutputConnections) {
297 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
298 std::set<int>::const_iterator it;
299 for (it = indices.begin(); it != indices.end(); it++) {
300 int i = *it;
301 TXshColumn *column = xsh->getColumn(i);
302 if (!column) continue;
303 TFx *columnFx = column->getFx();
304 if (!columnFx) continue;
305 QList<TFxPort *> ports;
306 int j;
307 for (j = 0; j < columnFx->getOutputConnectionCount(); j++)
308 ports.append(columnFx->getOutputConnection(j));
309 columnOutputConnections[columnFx] = ports;
310 }
311 }
312
313 //-----------------------------------------------------------------------------
314
getChildren(const std::set<int> & indices,QMap<TStageObjectId,QList<TStageObjectId>> & children)315 void getChildren(const std::set<int> &indices,
316 QMap<TStageObjectId, QList<TStageObjectId>> &children) {
317 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
318 std::set<int>::const_iterator it;
319 for (it = indices.begin(); it != indices.end(); it++) {
320 TStageObjectId id = TStageObjectId::ColumnId(*it);
321 TStageObject *obj = xsh->getStageObjectTree()->getStageObject(id, false);
322 assert(obj);
323 if (obj && !obj->getChildren().empty()) {
324 std::list<TStageObject *> childrenObj = obj->getChildren();
325 std::list<TStageObject *>::iterator it2;
326 for (it2 = childrenObj.begin(); it2 != childrenObj.end(); it2++) {
327 TStageObjectId childId = (*it2)->getId();
328 children[id].append(childId);
329 }
330 }
331 }
332 }
333
334 //-----------------------------------------------------------------------------
335
getParents(const std::set<int> & indices,QMap<TStageObjectId,TStageObjectId> & parents)336 void getParents(const std::set<int> &indices,
337 QMap<TStageObjectId, TStageObjectId> &parents) {
338 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
339 std::set<int>::const_iterator it;
340 for (it = indices.begin(); it != indices.end(); it++) {
341 TStageObjectId id = TStageObjectId::ColumnId(*it);
342 TStageObject *obj = xsh->getStageObjectTree()->getStageObject(id, false);
343 assert(obj);
344 if (obj) parents[id] = obj->getParent();
345 }
346 }
347
348 //-----------------------------------------------------------------------------
349
setColumnOutputConnections(const QMap<TFx *,QList<TFxPort * >> & columnOutputConnections)350 void setColumnOutputConnections(
351 const QMap<TFx *, QList<TFxPort *>> &columnOutputConnections) {
352 QMap<TFx *, QList<TFxPort *>>::const_iterator it;
353 for (it = columnOutputConnections.begin();
354 it != columnOutputConnections.end(); it++) {
355 TFx *columnFx = it.key();
356 QList<TFxPort *> ports = it.value();
357 int i;
358 for (i = 0; i < ports.size(); i++) ports.at(i)->setFx(columnFx);
359 }
360 }
361
362 //-----------------------------------------------------------------------------
363
setChildren(const QMap<TStageObjectId,QList<TStageObjectId>> & children)364 void setChildren(const QMap<TStageObjectId, QList<TStageObjectId>> &children) {
365 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
366 QMap<TStageObjectId, QList<TStageObjectId>>::const_iterator it;
367 for (it = children.begin(); it != children.end(); it++) {
368 TStageObjectId id = it.key();
369 QList<TStageObjectId> childrenIds = it.value();
370 QList<TStageObjectId>::iterator it2;
371 for (it2 = childrenIds.begin(); it2 != childrenIds.end(); it2++)
372 xsh->setStageObjectParent(*it2, id);
373 }
374 }
375
376 //-----------------------------------------------------------------------------
377
setParents(const QMap<TStageObjectId,TStageObjectId> & parents)378 void setParents(const QMap<TStageObjectId, TStageObjectId> &parents) {
379 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
380 QMap<TStageObjectId, TStageObjectId>::const_iterator it;
381 for (it = parents.begin(); it != parents.end(); it++)
382 xsh->setStageObjectParent(it.key(), it.value());
383 }
384 //-----------------------------------------------------------------------------
385
isConnectedToXsheet(TFx * fx)386 bool isConnectedToXsheet(TFx *fx) {
387 if (!fx) return false;
388 int i, count = fx->getInputPortCount();
389 bool xsheetConnected = false;
390 for (i = 0; i < count; i++) {
391 TFx *inputFx = fx->getInputPort(i)->getFx();
392 if (dynamic_cast<TXsheetFx *>(inputFx)) return true;
393 xsheetConnected = xsheetConnected || isConnectedToXsheet(inputFx);
394 }
395 return xsheetConnected;
396 }
397
398 //-----------------------------------------------------------------------------
399
400 // clones in outerDag fx and all effects contained in the subtree with root in
401 // fx
bringFxOut(TFx * fx,QMap<TFx *,QPair<TFx *,int>> & fxs,FxDag * outerDag,const GroupData & fxGroupData)402 void bringFxOut(TFx *fx, QMap<TFx *, QPair<TFx *, int>> &fxs, FxDag *outerDag,
403 const GroupData &fxGroupData) {
404 if (!fx) return;
405
406 TFx *actualFx = getActualFx(fx);
407 if (fx != actualFx) {
408 // Zerary Column case
409 TFx *outerFx = getActualFx(fxs[fx].first);
410
411 int i, inputPortsCount = actualFx->getInputPortCount();
412 for (i = 0; i < inputPortsCount; ++i) {
413 TFx *inputFx = actualFx->getInputPort(i)->getFx();
414 if (!inputFx) continue;
415
416 bringFxOut(inputFx, fxs, outerDag, fxGroupData);
417 outerFx->getInputPort(i)->setFx(fxs[inputFx].first);
418 }
419
420 return;
421 }
422
423 // Common case
424 if (fxs.contains(fx)) return;
425
426 TFx *outerFx = fx->clone(false);
427 TOutputFx *outFx = dynamic_cast<TOutputFx *>(outerFx);
428 if (!outFx) {
429 outerDag->getInternalFxs()->addFx(outerFx);
430 outerDag->assignUniqueId(outerFx);
431 } else
432 outerDag->addOutputFx(outFx);
433
434 TFxAttributes *attr = outerFx->getAttributes();
435 attr->setDagNodePos(fx->getAttributes()->getDagNodePos());
436
437 // Put in the right Fx group if needed
438 attr->removeFromAllGroup();
439 if (!fxGroupData.m_groupIds.empty()) {
440 int i;
441 for (i = 0; i < fxGroupData.m_groupIds.size(); i++) {
442 attr->setGroupId(fxGroupData.m_groupIds[i]);
443 attr->setGroupName(fxGroupData.m_groupNames[i]);
444 }
445 for (i = 0;
446 i < fxGroupData.m_groupIds.size() && fxGroupData.m_editingGroup >= 0;
447 i++)
448 attr->editGroup();
449 }
450
451 int columnIndex = -1;
452 bool firstIndex = true;
453
454 int i, inputPortsCount = fx->getInputPortCount();
455 for (i = 0; i < inputPortsCount; ++i) {
456 TFx *inputFx = fx->getInputPort(i)->getFx();
457 if (!inputFx) continue;
458
459 bringFxOut(inputFx, fxs, outerDag, fxGroupData);
460 outerFx->getInputPort(i)->setFx(fxs[inputFx].first);
461
462 if (firstIndex) {
463 columnIndex = fxs[inputFx].second;
464 firstIndex = false;
465 }
466 }
467
468 fxs[fx] = QPair<TFx *, int>(outerFx, columnIndex);
469 }
470
471 //-----------------------------------------------------------------------------
472
explodeFxSubTree(TFx * innerFx,QMap<TFx *,QPair<TFx *,int>> & fxs,FxDag * outerDag,TXsheet * outerXsheet,FxDag * innerDag,const GroupData & fxGroupData,const std::vector<TFxPort * > & outPorts)473 TFx *explodeFxSubTree(TFx *innerFx, QMap<TFx *, QPair<TFx *, int>> &fxs,
474 FxDag *outerDag, TXsheet *outerXsheet, FxDag *innerDag,
475 const GroupData &fxGroupData,
476 const std::vector<TFxPort *> &outPorts) {
477 TXsheetFx *xsheetFx = dynamic_cast<TXsheetFx *>(innerFx);
478 if (!xsheetFx) {
479 if (innerDag->getCurrentOutputFx() == innerFx)
480 innerFx = innerFx->getInputPort(0)->getFx();
481 if (!innerFx) return nullptr;
482 bringFxOut(innerFx, fxs, outerDag, fxGroupData);
483 TOutputFx *outFx = dynamic_cast<TOutputFx *>(innerFx);
484 if (outFx)
485 return fxs[outFx->getInputPort(0)->getFx()].first;
486 else
487 return fxs[innerFx].first;
488 } else {
489 TFxSet *innerTerminals = innerDag->getTerminalFxs();
490 int i, terminalCount = innerTerminals->getFxCount();
491 QMultiMap<int, TFx *> sortedFx;
492 for (i = 0; i < terminalCount; i++) {
493 TFx *terminalFx = innerTerminals->getFx(i);
494 bringFxOut(terminalFx, fxs, outerDag, fxGroupData);
495 sortedFx.insert(fxs[terminalFx].second, fxs[terminalFx].first);
496 }
497 // Xsheet nodes can be "merged" if:
498 // a) the subxsheet node is directly connected to the Xsheet node in the
499 // parent fxdag, AND b) only the active output node is connected to the
500 // Xsheet node in the child fxdag
501 if (outPorts.empty() && xsheetFx->getOutputConnectionCount() == 1) {
502 if (innerDag->getCurrentOutputFx() ==
503 xsheetFx->getOutputConnection(0)->getOwnerFx())
504 return nullptr;
505 }
506
507 // in case no nodes connected to the xsheet the xsheet node will not be
508 // merged, but will just be removed
509 if (terminalCount == 0) {
510 fxs[innerFx] = QPair<TFx *, int>(nullptr, -1);
511 return innerFx; // just to return non-zero value
512 }
513
514 TFx *root = sortedFx.begin().value();
515
516 // If only one node is connected to the Xsheet node, then skip bringing it
517 // out.
518 if (terminalCount == 1) {
519 fxs[innerFx] = QPair<TFx *, int>(root, sortedFx.begin().key());
520 return root;
521 }
522
523 // Replace the child Xsheet node by the Over Fx node
524 TFx *overFx = TFx::create("overFx");
525 outerDag->assignUniqueId(overFx);
526 outerDag->getInternalFxs()->addFx(overFx);
527 setFxParamToCurrentScene(overFx, outerXsheet);
528 TPointD pos = root->getAttributes()->getDagNodePos();
529 overFx->getAttributes()->setDagNodePos((pos == TConst::nowhere)
530 ? TConst::nowhere
531 : TPointD(pos.x + 150, pos.y));
532
533 const TFxPortDG *group = overFx->dynamicPortGroup(0);
534 for (int i = 0; i < sortedFx.size(); i++) {
535 TFxPort *port = new TRasterFxPort;
536 if (!overFx->addInputPort(
537 group->portsPrefix() + QString::number(i + 1).toStdString(), port,
538 0))
539 delete port;
540 }
541
542 int portId = sortedFx.size() - 1;
543 int columnIndex = -1;
544 for (auto it = sortedFx.begin(); it != sortedFx.end(); ++it, --portId) {
545 TFx *fx = it.value();
546 assert(fx);
547
548 overFx->getInputPort(portId)->setFx(fx);
549 outerDag->removeFromXsheet(fx);
550 // set the firstly-found column index
551 if (columnIndex == -1) columnIndex = it.key();
552 }
553
554 // register fx
555 fxs[innerFx] = QPair<TFx *, int>(overFx, columnIndex);
556
557 return overFx;
558 }
559 }
560
561 //-----------------------------------------------------------------------------
562
563 // brings in xsh obj and all objects contained in the subtree with root in obj
bringObjectOut(TStageObject * obj,TXsheet * xsh,QMap<TStageObjectId,TStageObjectId> & ids,QMap<TStageObjectSpline *,TStageObjectSpline * > & splines,QList<TStageObject * > & pegObjects,int & pegbarIndex,const GroupData & objGroupData,int groupId)564 void bringObjectOut(TStageObject *obj, TXsheet *xsh,
565 QMap<TStageObjectId, TStageObjectId> &ids,
566 QMap<TStageObjectSpline *, TStageObjectSpline *> &splines,
567 QList<TStageObject *> &pegObjects, int &pegbarIndex,
568 const GroupData &objGroupData, int groupId) {
569 if (!obj->hasChildren()) return;
570 std::list<TStageObject *> children = obj->getChildren();
571 std::list<TStageObject *>::iterator it;
572 for (it = children.begin(); it != children.end(); it++) {
573 TStageObjectId id = (*it)->getId();
574 if (id.isColumn()) continue;
575 assert(id.isPegbar());
576 pegbarIndex++;
577 TStageObjectId outerId = TStageObjectId::PegbarId(pegbarIndex);
578 // find the first available pegbar id
579 while (xsh->getStageObjectTree()->getStageObject(outerId, false)) {
580 pegbarIndex++;
581 outerId = TStageObjectId::PegbarId(pegbarIndex);
582 }
583 TStageObject *outerObj =
584 xsh->getStageObjectTree()->getStageObject(outerId, true);
585 outerObj->setDagNodePos((*it)->getDagNodePos());
586 ids[id] = outerId;
587 pegObjects.append(outerObj);
588 outerObj->addRef(); // undo make release!!!
589 TStageObjectParams *params = (*it)->getParams();
590 if (params->m_spline) {
591 if (splines.contains(params->m_spline))
592 params->m_spline = splines[params->m_spline];
593 else {
594 TStageObjectSpline *spline = params->m_spline->clone();
595 splines[params->m_spline] = spline;
596 xsh->getStageObjectTree()->assignUniqueSplineId(spline);
597 xsh->getStageObjectTree()->insertSpline(spline);
598 params->m_spline = spline;
599 }
600 }
601 outerObj->assignParams(params);
602 delete params;
603 outerObj->setParent(ids[obj->getId()]);
604 outerObj->removeFromAllGroup();
605 if (groupId != -1) {
606 outerObj->setGroupId(groupId);
607 outerObj->setGroupName(L"Group " + std::to_wstring(groupId));
608 }
609 if (!objGroupData.m_groupIds.empty()) {
610 int i;
611 for (i = 0; i < objGroupData.m_groupIds.size(); i++) {
612 outerObj->setGroupId(objGroupData.m_groupIds[i]);
613 outerObj->setGroupName(objGroupData.m_groupNames[i]);
614 }
615 for (i = 0; i < objGroupData.m_groupIds.size() &&
616 objGroupData.m_editingGroup >= 0;
617 i++)
618 outerObj->editGroup();
619 }
620 bringObjectOut(*it, xsh, ids, splines, pegObjects, pegbarIndex,
621 objGroupData, groupId);
622 }
623 }
624
625 //-----------------------------------------------------------------------------
626
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)627 std::set<int> explodeStageObjects(
628 TXsheet *xsh, TXsheet *subXsh, int index, const TStageObjectId &parentId,
629 const GroupData &objGroupData, const TPointD &subPos,
630 const GroupData &fxGroupData, QList<TStageObject *> &pegObjects,
631 QMap<TFx *, QPair<TFx *, int>> &fxs,
632 QMap<TStageObjectSpline *, TStageObjectSpline *> &splines,
633 QMap<TStageObjectId, TStageObjectId> &ids, bool onlyColumn) {
634 /*- SubXsheet, 親Xsheet両方のツリーを取得 -*/
635 TStageObjectTree *innerTree = subXsh->getStageObjectTree();
636 TStageObjectTree *outerTree = xsh->getStageObjectTree();
637 // innerSpline->outerSpline
638 int groupId = -1; // outerTree->getNewGroupId();
639 /*- Pegbarも持ち出す場合 -*/
640 if (!onlyColumn) {
641 // add a pegbar to represent the table
642 TStageObject *table = subXsh->getStageObject(TStageObjectId::TableId);
643 // find the first available pegbar index
644 int pegbarIndex = 0;
645 while (
646 outerTree->getStageObject(TStageObjectId::PegbarId(pegbarIndex), false))
647 pegbarIndex++;
648 /*- 空いてるIndexのPegbarに、SubXsheetのTableを対応させる -*/
649 TStageObjectId id = TStageObjectId::PegbarId(pegbarIndex);
650 TStageObject *obj = outerTree->getStageObject(id, true);
651 /*- 対応表に追加 -*/
652 obj->setDagNodePos(table->getDagNodePos());
653 ids[TStageObjectId::TableId] = id;
654 pegObjects.append(obj);
655 obj->addRef(); // undo make release!!!!
656 /*- SubのTableの情報を、今作ったPegbarにコピーする -*/
657 TStageObjectParams *params = table->getParams();
658 if (params->m_spline) {
659 if (splines.contains(params->m_spline))
660 params->m_spline = splines[params->m_spline];
661 else {
662 TStageObjectSpline *spline = params->m_spline->clone();
663 splines[params->m_spline] = spline;
664 outerTree->assignUniqueSplineId(spline);
665 outerTree->insertSpline(spline);
666 params->m_spline = spline;
667 }
668 }
669 obj->assignParams(params);
670 delete params;
671 // a pegbar cannot be a child of column
672 if (parentId.isColumn())
673 obj->setParent(TStageObjectId::TableId);
674 else
675 obj->setParent(parentId);
676
677 // Put in the right StageObject group if needed
678 obj->removeFromAllGroup();
679 groupId = outerTree->getNewGroupId();
680 obj->setGroupId(groupId);
681 obj->setGroupName(L"Group " + std::to_wstring(groupId));
682 if (!objGroupData.m_groupIds.empty()) {
683 int i;
684 for (i = 0; i < objGroupData.m_groupIds.size(); i++) {
685 obj->setGroupId(objGroupData.m_groupIds[i]);
686 obj->setGroupName(objGroupData.m_groupNames[i]);
687 }
688 for (i = 0; i < objGroupData.m_groupIds.size() &&
689 objGroupData.m_editingGroup >= 0;
690 i++)
691 obj->editGroup();
692 }
693 // add all pegbar
694 bringObjectOut(table, xsh, ids, splines, pegObjects, pegbarIndex,
695 objGroupData, groupId);
696 }
697
698 // add colums;
699 FxDag *innerDag = subXsh->getFxDag();
700 FxDag *outerDag = xsh->getFxDag();
701 TStageObjectId tmpParentId = parentId;
702 std::set<int> indexes;
703 int i;
704 for (i = 0; i < subXsh->getColumnCount(); i++) {
705 TXshColumn *innerColumn = subXsh->getColumn(i);
706 TXshColumn *outerColumn = innerColumn->clone();
707
708 TFx *innerFx = innerColumn->getFx();
709 TFx *outerFx = outerColumn->getFx();
710
711 xsh->insertColumn(index, outerColumn);
712 // the above insertion operation may increment the parentId, in case that
713 // 1, the parent object is column, and
714 // 2, the parent column is placed on the right side of the inserted column
715 // ( i.e. index of the parent column is equal to or higher than "index")
716 if (onlyColumn && tmpParentId.isColumn() && tmpParentId.getIndex() >= index)
717 tmpParentId = TStageObjectId::ColumnId(tmpParentId.getIndex() + 1);
718
719 if (innerFx && outerFx) {
720 outerFx->getAttributes()->setDagNodePos(
721 innerFx->getAttributes()->getDagNodePos());
722 fxs[innerColumn->getFx()] =
723 QPair<TFx *, int>(outerColumn->getFx(), outerColumn->getIndex());
724 if (!innerDag->getTerminalFxs()->containsFx(innerColumn->getFx()))
725 outerDag->getTerminalFxs()->removeFx(outerColumn->getFx());
726 }
727
728 TStageObjectId innerId = TStageObjectId::ColumnId(i);
729 TStageObjectId outerId = TStageObjectId::ColumnId(index);
730 TStageObject *innerCol = innerTree->getStageObject(innerId, false);
731 TStageObject *outerCol = outerTree->getStageObject(outerId, false);
732 TStageObjectParams *params = innerCol->getParams();
733 if (params->m_spline) {
734 if (splines.contains(params->m_spline))
735 params->m_spline = splines[params->m_spline];
736 else {
737 TStageObjectSpline *spline = params->m_spline->clone();
738 splines[params->m_spline] = spline;
739 outerTree->assignUniqueSplineId(spline);
740 outerTree->insertSpline(spline);
741 params->m_spline = spline;
742 }
743 }
744 outerCol->assignParams(params);
745 outerCol->setDagNodePos(innerCol->getDagNodePos());
746 delete params;
747 assert(outerCol && innerCol);
748 ids[innerId] = outerId;
749 outerCol->removeFromAllGroup();
750 if (groupId != -1) {
751 outerCol->setGroupId(groupId);
752 outerCol->setGroupName(L"Group " + std::to_wstring(groupId));
753 }
754
755 if (onlyColumn) outerCol->setParent(tmpParentId);
756
757 // Put in the right StageObject group if needed
758 if (!objGroupData.m_groupIds.empty()) {
759 int j;
760 for (j = 0; j < objGroupData.m_groupIds.size(); j++) {
761 outerCol->setGroupId(objGroupData.m_groupIds[j]);
762 outerCol->setGroupName(objGroupData.m_groupNames[j]);
763 }
764 for (j = 0; j < objGroupData.m_groupIds.size() &&
765 objGroupData.m_editingGroup >= 0;
766 j++)
767 outerCol->editGroup();
768 }
769
770 // Put in the right Fx group if needed
771 if (outerFx && !fxGroupData.m_groupIds.empty()) {
772 int j;
773 for (j = 0; j < fxGroupData.m_groupIds.size(); j++) {
774 outerColumn->getFx()->getAttributes()->setGroupId(
775 fxGroupData.m_groupIds[j]);
776 outerColumn->getFx()->getAttributes()->setGroupName(
777 fxGroupData.m_groupNames[j]);
778 }
779 for (j = 0;
780 j < fxGroupData.m_groupIds.size() && fxGroupData.m_editingGroup >= 0;
781 j++)
782 outerColumn->getFx()->getAttributes()->editGroup();
783 }
784 indexes.insert(index);
785 index++;
786 }
787
788 // setting column parents
789 for (i = 0; i < subXsh->getColumnCount() && !onlyColumn; i++) {
790 TStageObjectId innerId = TStageObjectId::ColumnId(i);
791 TStageObject *innerCol = innerTree->getStageObject(innerId, false);
792 xsh->setStageObjectParent(ids[innerId], ids[innerCol->getParent()]);
793 }
794
795 TPointD middlePoint;
796 int objCount = 0;
797 QMap<TStageObjectId, TStageObjectId>::iterator it;
798 for (it = ids.begin(); it != ids.end(); it++) {
799 TStageObject *innerObj = innerTree->getStageObject(it.key(), false);
800 if (!innerObj) continue;
801
802 const TPointD &pos = innerObj->getDagNodePos();
803 if (pos == TConst::nowhere) continue;
804
805 middlePoint = middlePoint + pos;
806 ++objCount;
807 }
808 middlePoint = TPointD(middlePoint.x / objCount, middlePoint.y / objCount);
809
810 // faccio in modo che tutti i nodi estratti siano centrati in middlePoint
811 // Li metto poi in un gruppo
812 TPointD offset = middlePoint - subPos;
813 for (it = ids.begin(); it != ids.end(); it++) {
814 TStageObject *outerObj = outerTree->getStageObject(it.value(), false);
815 if (!outerObj) continue;
816 /*outerObj->setGroupId(groupId);
817 outerObj->setGroupName(L"Group "+toWideString(groupId));*/
818 TPointD outerPos = outerObj->getDagNodePos();
819 if (outerPos != TConst::nowhere) outerObj->setDagNodePos(outerPos - offset);
820 }
821
822 return indexes;
823 }
824
825 //-----------------------------------------------------------------------------
826
explodeFxs(TXsheet * xsh,TXsheet * subXsh,const GroupData & fxGroupData,QMap<TFx *,QPair<TFx *,int>> & fxs,const TPointD & subPos,const std::vector<TFxPort * > & outPorts,bool linkToXsheet)827 void explodeFxs(TXsheet *xsh, TXsheet *subXsh, const GroupData &fxGroupData,
828 QMap<TFx *, QPair<TFx *, int>> &fxs, const TPointD &subPos,
829 const std::vector<TFxPort *> &outPorts, bool linkToXsheet) {
830 FxDag *innerDag = subXsh->getFxDag();
831 FxDag *outerDag = xsh->getFxDag();
832 bool explosionLinked = false;
833
834 // taking out all the effects that start from the xsheet.
835 // xsheet node will be replaced by the over fx node if necessary.
836 // root will be null if the xsheet node will not bring out to the parent
837 // fxdag.
838 TFx *root = explodeFxSubTree(innerDag->getXsheetFx(), fxs, outerDag, xsh,
839 innerDag, fxGroupData, outPorts);
840
841 // in case the child and parent Xsheet nodes will be "merged"
842 if (!root) {
843 TFxSet *internals = innerDag->getTerminalFxs();
844 for (int j = 0; j < internals->getFxCount(); j++) {
845 TFx *fx = internals->getFx(j);
846 outerDag->addToXsheet(fxs[fx].first);
847 }
848 explosionLinked = true;
849 }
850
851 // taking out all the effects that start from output nodes
852 for (int i = 0; i < innerDag->getOutputFxCount(); i++) {
853 TOutputFx *outFx = innerDag->getOutputFx(i);
854 bool isCurrent = (outFx == innerDag->getCurrentOutputFx());
855 // the link is done before tracing from the current out put node.
856 // it means that all the fxs before the output node are already exploded and
857 // connected.
858 if (isCurrent && explosionLinked) continue;
859
860 TFx *root = explodeFxSubTree(outFx, fxs, outerDag, xsh, innerDag,
861 fxGroupData, outPorts);
862 // If the output node is not connected to any other node
863 if (!root) continue;
864
865 if (isCurrent) {
866 // link the root node to the xsheet node if:
867 // a) the subxsheet column is connected to the xsheet node, OR
868 // b) the original subxsheet column will not be deleted and the exploded
869 // column will be inserted.
870 // (this case happens when the subxsheet column contains multiple
871 // levels. outPorts is empty in such case)
872 if (linkToXsheet)
873 outerDag->addToXsheet(root); // connect to the xsheet node
874 for (int j = 0; j < outPorts.size(); j++) outPorts[j]->setFx(root);
875
876 explosionLinked = true;
877 }
878 }
879
880 // taking out all the other effects!
881 TFxSet *innerInternals = innerDag->getInternalFxs();
882 for (int i = 0; i < innerInternals->getFxCount(); i++) {
883 TFx *fx = innerInternals->getFx(i);
884 if (fxs.contains(fx)) continue;
885 explodeFxSubTree(fx, fxs, outerDag, xsh, innerDag, fxGroupData, outPorts);
886 }
887
888 assert(explosionLinked);
889
890 // cerco il punto medio tra tutti i nodi
891 TPointD middlePoint(0.0, 0.0);
892 int fxsCount = 0;
893
894 QMap<TFx *, QPair<TFx *, int>>::iterator it;
895 for (it = fxs.begin(); it != fxs.end(); it++) {
896 TFx *innerFx = it.key();
897 if (!innerFx) continue;
898
899 assert(innerFx->getAttributes());
900 const TPointD &pos = innerFx->getAttributes()->getDagNodePos();
901 if (pos == TConst::nowhere) continue;
902
903 middlePoint = middlePoint + pos;
904 ++fxsCount;
905 }
906 if (fxsCount > 0)
907 middlePoint = TPointD(middlePoint.x / fxsCount, middlePoint.y / fxsCount);
908 else
909 middlePoint = TPointD(25000, 25000); // center of the scene
910
911 // faccio in modo che tutti i nodi estratti siano centrati in middlePoint
912 // Li metto poi in un gruppo
913 TPointD offset = middlePoint - subPos;
914 int groupId = outerDag->getNewGroupId();
915 for (it = fxs.begin(); it != fxs.end(); it++) {
916 QPair<TFx *, int> pair = it.value();
917 TFx *outerFx = pair.first;
918 // skip redundant item. in case when only one node is input to the xsheet
919 // node in the inner dag
920 if (!outerFx) continue;
921 if (outerFx->getAttributes()->getGroupId() == groupId) continue;
922 outerFx->getAttributes()->setGroupId(groupId);
923 outerFx->getAttributes()->setGroupName(L"Group " +
924 std::to_wstring(groupId));
925 TPointD outerFxPos = outerFx->getAttributes()->getDagNodePos();
926 if (outerFxPos != TConst::nowhere)
927 outerFx->getAttributes()->setDagNodePos(outerFxPos - offset);
928 }
929 }
930
931 //-----------------------------------------------------------------------------
932
933 template <typename ParamCont>
setGrammerToParams(const ParamCont * cont,const TSyntax::Grammar * grammer)934 void setGrammerToParams(const ParamCont *cont,
935 const TSyntax::Grammar *grammer) {
936 for (int p = 0; p != cont->getParamCount(); ++p) {
937 TParam ¶m = *cont->getParam(p);
938 if (TDoubleParam *dp = dynamic_cast<TDoubleParam *>(¶m))
939 dp->setGrammar(grammer);
940 else if (TParamSet *paramSet = dynamic_cast<TParamSet *>(¶m))
941 setGrammerToParams(paramSet, grammer);
942 }
943 }
944
945 //-----------------------------------------------------------------------------
946
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)947 std::set<int> explode(TXsheet *xsh, TXsheet *subXsh, int index,
948 const TStageObjectId &parentId,
949 const GroupData &objGroupData, const TPointD &stageSubPos,
950 const GroupData &fxGroupData, const TPointD &fxSubPos,
951 QList<TStageObject *> &pegObjects,
952 QMap<TStageObjectSpline *, TStageObjectSpline *> &splines,
953 const std::vector<TFxPort *> &outPorts, bool onlyColumn,
954 bool linkToXsheet) {
955 // innerFx->outerFxs
956 QMap<TFx *, QPair<TFx *, int>> fxs;
957 // inner id->outer id
958 QMap<TStageObjectId, TStageObjectId> objIds;
959 std::set<int> indexes = explodeStageObjects(
960 xsh, subXsh, index, parentId, objGroupData, stageSubPos, fxGroupData,
961 pegObjects, fxs, splines, objIds, onlyColumn);
962 explodeFxs(xsh, subXsh, fxGroupData, fxs, fxSubPos, outPorts, linkToXsheet);
963
964 assert(TApp::instance()->getCurrentXsheet()->getXsheet() == xsh);
965
966 // reset grammers for all parameters brought out to the parent xsheet
967 TSyntax::Grammar *grammer = xsh->getStageObjectTree()->getGrammar();
968 for (auto id : objIds.values()) {
969 TStageObject *obj = xsh->getStageObject(id);
970 for (int c = 0; c != TStageObject::T_ChannelCount; ++c)
971 obj->getParam((TStageObject::Channel)c)->setGrammar(grammer);
972 if (const PlasticSkeletonDeformationP &sd =
973 obj->getPlasticSkeletonDeformation())
974 sd->setGrammar(grammer);
975 }
976
977 QMap<TFx *, TFx *> fxMap;
978 for (auto it = fxs.constBegin(); it != fxs.constEnd(); ++it) {
979 if (it.value().first == nullptr) continue;
980 setGrammerToParams(it.value().first->getParams(), grammer);
981 fxMap.insert(it.key(), it.value().first);
982 }
983
984 ExpressionReferenceManager::instance()->transferReference(subXsh, xsh, objIds,
985 fxMap);
986
987 return indexes;
988 }
989
990 //=============================================================================
991 // OpenChildUndo
992 //-----------------------------------------------------------------------------
993
994 class OpenChildUndo final : public TUndo {
995 int m_row, m_col;
996
997 public:
OpenChildUndo()998 OpenChildUndo() {
999 TApp *app = TApp::instance();
1000 m_row = app->getCurrentFrame()->getFrame();
1001 m_col = app->getCurrentColumn()->getColumnIndex();
1002 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1003 TXshCell cell = xsh->getCell(m_row, m_col);
1004 }
1005
undo() const1006 void undo() const override {
1007 TApp *app = TApp::instance();
1008 ToonzScene *scene = app->getCurrentScene()->getScene();
1009 int row, col;
1010 scene->getChildStack()->closeChild(row, col);
1011 app->getCurrentXsheet()->setXsheet(scene->getXsheet());
1012 changeSaveSubXsheetAsCommand();
1013 }
1014
redo() const1015 void redo() const override {
1016 TApp *app = TApp::instance();
1017 ToonzScene *scene = app->getCurrentScene()->getScene();
1018 scene->getChildStack()->openChild(m_row, m_col);
1019 app->getCurrentXsheet()->setXsheet(scene->getXsheet());
1020 changeSaveSubXsheetAsCommand();
1021 }
1022
getSize() const1023 int getSize() const override { return sizeof(*this); }
1024 };
1025
1026 //=============================================================================
1027 // CloseChildUndo
1028 //-----------------------------------------------------------------------------
1029
1030 class CloseChildUndo final : public TUndo {
1031 std::vector<std::pair<int, int>> m_cells;
1032
1033 public:
CloseChildUndo(const std::vector<std::pair<int,int>> & cells)1034 CloseChildUndo(const std::vector<std::pair<int, int>> &cells)
1035 : m_cells(cells) {}
1036
undo() const1037 void undo() const override {
1038 TApp *app = TApp::instance();
1039 ToonzScene *scene = app->getCurrentScene()->getScene();
1040 for (int i = m_cells.size() - 1; i >= 0; i--) {
1041 std::pair<int, int> rowCol = m_cells[i];
1042 scene->getChildStack()->openChild(rowCol.first, rowCol.second);
1043 }
1044 app->getCurrentXsheet()->setXsheet(scene->getXsheet());
1045 changeSaveSubXsheetAsCommand();
1046 }
1047
redo() const1048 void redo() const override {
1049 TApp *app = TApp::instance();
1050 ToonzScene *scene = app->getCurrentScene()->getScene();
1051 for (int i = 0; i < (int)m_cells.size(); i++) {
1052 int row, col;
1053 scene->getChildStack()->closeChild(row, col);
1054 }
1055 app->getCurrentXsheet()->setXsheet(scene->getXsheet());
1056 changeSaveSubXsheetAsCommand();
1057 }
1058
getSize() const1059 int getSize() const override { return sizeof(*this); }
1060
getHistoryString()1061 QString getHistoryString() override { return QObject::tr("Close SubXsheet"); }
getHistoryType()1062 int getHistoryType() override { return HistoryType::Xsheet; }
1063 };
1064
1065 //=============================================================================
1066
openSubXsheet()1067 void openSubXsheet() {
1068 TApp *app = TApp::instance();
1069 /*- Enter only when ChildLevel exists in selected cell or selected column -*/
1070 TCellSelection *cellSelection =
1071 dynamic_cast<TCellSelection *>(TSelection::getCurrent());
1072 TColumnSelection *columnSelection =
1073 dynamic_cast<TColumnSelection *>(TSelection::getCurrent());
1074
1075 bool ret = false;
1076 ToonzScene *scene = app->getCurrentScene()->getScene();
1077 int row = app->getCurrentFrame()->getFrame();
1078 int col = app->getCurrentColumn()->getColumnIndex();
1079 TXsheet *currentXsheet = app->getCurrentXsheet()->getXsheet();
1080 TXshCell targetCell;
1081
1082 /*- For column selection -*/
1083 if (columnSelection && !columnSelection->isEmpty()) {
1084 int sceneLength = currentXsheet->getFrameCount();
1085
1086 std::set<int> columnIndices = columnSelection->getIndices();
1087 /*- Try openChild on each cell for each Column -*/
1088 for (auto const &c : columnIndices) {
1089 // See if the current row indicator is on an exposed sub-xsheet frame
1090 // If so, use that.
1091 targetCell = currentXsheet->getCell(row, c);
1092 if (!targetCell.isEmpty() &&
1093 (ret = scene->getChildStack()->openChild(row, c)))
1094 break;
1095
1096 /*- For each Cell in the Column, if contents are found break -*/
1097 for (int r = 0; r < sceneLength; r++) {
1098 ret = scene->getChildStack()->openChild(r, c);
1099 if (ret) {
1100 targetCell = currentXsheet->getCell(r, c);
1101 break;
1102 }
1103 }
1104 if (ret) break;
1105 }
1106 }
1107
1108 /*- In other cases (cell selection or other) -*/
1109 else {
1110 TRect selectedArea;
1111 /*- If it is not cell selection, see current frame / column -*/
1112 if (!cellSelection || cellSelection->isEmpty()) {
1113 /*- When it is not cell selection, 1 × 1 selection range -*/
1114 selectedArea = TRect(col, row, col, row);
1115 }
1116 /*- In case of cell selection -*/
1117 else {
1118 int r0, c0, r1, c1;
1119 cellSelection->getSelectedCells(r0, c0, r1, c1);
1120 selectedArea = TRect(c0, r0, c1, r1);
1121 }
1122 /*- Try openChild on each cell in Rect -*/
1123 for (int c = selectedArea.x0; c <= selectedArea.x1; c++) {
1124 for (int r = selectedArea.y0; r <= selectedArea.y1; r++) {
1125 ret = scene->getChildStack()->openChild(r, c);
1126 if (ret) {
1127 // When opening based on cell selection use the 1st
1128 // exposed frame in the sub-xsheet it finds
1129 targetCell = currentXsheet->getCell(r, c);
1130 break;
1131 }
1132 }
1133 if (ret) break;
1134 }
1135 }
1136
1137 /*- When subXsheet Level is found -*/
1138 if (ret) {
1139 int subXsheetFrame = 0;
1140
1141 if (!targetCell.isEmpty())
1142 subXsheetFrame = targetCell.getFrameId().getNumber() - 1;
1143
1144 if (TSelection::getCurrent()) TSelection::getCurrent()->selectNone();
1145
1146 TUndoManager::manager()->add(new OpenChildUndo());
1147 app->getCurrentXsheet()->setXsheet(scene->getXsheet());
1148 app->getCurrentXsheet()->notifyXsheetChanged();
1149 app->getCurrentColumn()->setColumnIndex(0);
1150 app->getCurrentFrame()->setFrameIndex(subXsheetFrame);
1151 changeSaveSubXsheetAsCommand();
1152 } else
1153 DVGui::error(QObject::tr("Select a sub-xsheet cell."));
1154 }
1155
1156 //=============================================================================
1157
closeSubXsheet(int dlevel)1158 void closeSubXsheet(int dlevel) {
1159 if (dlevel < 1) return;
1160 TApp *app = TApp::instance();
1161 TSelection *selection =
1162 TApp::instance()->getCurrentSelection()->getSelection();
1163 if (selection) selection->selectNone();
1164 ToonzScene *scene = app->getCurrentScene()->getScene();
1165 int ancestorCount = scene->getChildStack()->getAncestorCount();
1166 if (ancestorCount == 0) return;
1167 if (dlevel > ancestorCount) dlevel = ancestorCount;
1168 std::vector<std::pair<int, int>> cells;
1169 for (int i = 0; i < dlevel; i++) {
1170 std::pair<int, int> rowCol;
1171 scene->getChildStack()->closeChild(rowCol.first, rowCol.second);
1172 TXsheet *xsh = scene->getXsheet();
1173 IconGenerator::instance()->invalidate(
1174 xsh->getCell(rowCol.first, rowCol.second).m_level.getPointer(),
1175 TFrameId(1));
1176 cells.push_back(rowCol);
1177 }
1178 if (cells.empty()) return;
1179 TUndoManager::manager()->add(new CloseChildUndo(cells));
1180 app->getCurrentXsheet()->setXsheet(scene->getXsheet());
1181 app->getCurrentXsheet()->notifyXsheetChanged();
1182 app->getCurrentColumn()->setColumnIndex(cells[0].second);
1183 app->getCurrentFrame()->setFrameIndex(cells[0].first);
1184 changeSaveSubXsheetAsCommand();
1185 }
1186
1187 //=============================================================================
1188
1189 // returns true if there is at least one pegbar to be brought inside subxsheet
1190 // on collase in order to see if the confirmation dialog is needed
hasPegbarsToBringInsideChildXsheet(TXsheet * xsh,const std::set<int> & indices)1191 bool hasPegbarsToBringInsideChildXsheet(TXsheet *xsh,
1192 const std::set<int> &indices) {
1193 for (auto itr = indices.cbegin(); itr != indices.cend(); itr++) {
1194 TStageObjectId id =
1195 xsh->getStageObjectParent(TStageObjectId::ColumnId(*itr));
1196 // check the parent node
1197 if (id.isPegbar() || id.isCamera()) return true;
1198 }
1199 return false;
1200 }
1201
1202 //-----------------------------------------------------------------------------
1203
bringPegbarsInsideChildXsheet(TXsheet * xsh,TXsheet * childXsh,std::set<int> indices,std::set<int> newIndices,QMap<TStageObjectId,TStageObjectId> & idTable)1204 void bringPegbarsInsideChildXsheet(
1205 TXsheet *xsh, TXsheet *childXsh, std::set<int> indices,
1206 std::set<int> newIndices, QMap<TStageObjectId, TStageObjectId> &idTable) {
1207 // columns in the child xsheet are all connected to the table for now.
1208 // so we need to take parental connection information from the parent xsheet.
1209
1210 // retrieve all pegbars used from copied columns
1211 std::set<TStageObjectId> pegbarIds;
1212
1213 std::set<int>::iterator itr = indices.begin();
1214 std::set<int>::iterator new_itr = newIndices.begin();
1215 while (itr != indices.end()) {
1216 TStageObjectId id =
1217 xsh->getStageObjectParent(TStageObjectId::ColumnId(*itr));
1218
1219 TStageObjectId newCol = TStageObjectId::ColumnId(*new_itr);
1220 if (id.isPegbar() || id.isCamera())
1221 childXsh->setStageObjectParent(newCol, id);
1222 /*- Columnの上流のPegbar/Cameraを格納していく -*/
1223 while (id.isPegbar() || id.isCamera()) {
1224 pegbarIds.insert(id);
1225 id = xsh->getStageObjectParent(id);
1226 }
1227 itr++;
1228 new_itr++;
1229 }
1230
1231 std::set<TStageObjectId>::iterator pegbarIt;
1232 for (pegbarIt = pegbarIds.begin(); pegbarIt != pegbarIds.end(); ++pegbarIt) {
1233 TStageObjectId id = *pegbarIt;
1234 TStageObjectParams *data = xsh->getStageObject(id)->getParams();
1235 TStageObject *obj = childXsh->getStageObject(id);
1236 obj->assignParams(data);
1237 delete data;
1238 obj->setParent(xsh->getStageObjectParent(id));
1239
1240 // reset grammers of all parameters or they fails to refer to other
1241 // parameters via expression
1242 for (int c = 0; c != TStageObject::T_ChannelCount; ++c)
1243 childXsh->getStageObjectTree()->setGrammar(
1244 obj->getParam((TStageObject::Channel)c));
1245
1246 // register pegbars to the table
1247 idTable.insert(id, id);
1248 }
1249 }
1250
1251 //-----------------------------------------------------------------------------
1252
removeFx(TXsheet * xsh,TFx * fx)1253 void removeFx(TXsheet *xsh, TFx *fx) {
1254 TOutputFx *outFx = dynamic_cast<TOutputFx *>(fx);
1255 if (outFx) {
1256 xsh->getFxDag()->removeOutputFx(outFx);
1257 return;
1258 }
1259
1260 TFxSet *internalFx = xsh->getFxDag()->getInternalFxs();
1261 TFxSet *terminalFx = xsh->getFxDag()->getTerminalFxs();
1262
1263 int j;
1264 for (j = 0; j < fx->getInputPortCount(); j++) {
1265 TFxPort *inputPort = fx->getInputPort(j);
1266 TFx *inputFx = inputPort->getFx();
1267 if (inputFx && j == 0) {
1268 int k;
1269 for (k = fx->getOutputConnectionCount() - 1; k >= 0; k--) {
1270 TFxPort *outputPort = fx->getOutputConnection(k);
1271 outputPort->setFx(inputFx);
1272 }
1273 if (terminalFx->containsFx(fx)) {
1274 terminalFx->removeFx(fx);
1275 terminalFx->addFx(inputFx);
1276 }
1277 }
1278 int i;
1279 for (i = fx->getOutputConnectionCount() - 1; i >= 0; i--)
1280 fx->getOutputConnection(i)->setFx(inputPort->getFx());
1281 inputPort->setFx(0);
1282 }
1283 internalFx->removeFx(fx);
1284 }
1285
1286 //-----------------------------------------------------------------------------
1287
collapseColumns(std::set<int> indices,bool columnsOnly)1288 void collapseColumns(std::set<int> indices, bool columnsOnly) {
1289 // return if there is no selected columns
1290 if (indices.empty()) return;
1291
1292 int index = *indices.begin();
1293 TApp *app = TApp::instance();
1294 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1295
1296 std::set<int> oldIndices = indices;
1297
1298 StageObjectsData *data = new StageObjectsData();
1299 // store xsheet data to be collapsed
1300 data->storeColumns(indices, xsh, StageObjectsData::eDoClone);
1301 data->storeColumnFxs(indices, xsh, StageObjectsData::eDoClone);
1302
1303 ExpressionReferenceMonitor *monitor = xsh->getExpRefMonitor()->clone();
1304
1305 ToonzScene *scene = app->getCurrentScene()->getScene();
1306 TXshLevel *xl = scene->createNewLevel(CHILD_XSHLEVEL);
1307 assert(xl);
1308
1309 TXshChildLevel *childLevel = xl->getChildLevel();
1310 assert(childLevel);
1311
1312 TXsheet *childXsh = childLevel->getXsheet();
1313
1314 std::set<int> newIndices;
1315 std::list<int> restoredSplineIds;
1316 QMap<TStageObjectId, TStageObjectId> idTable;
1317 QMap<TFx *, TFx *> fxTable;
1318 // restore data into sub xsheet
1319 data->restoreObjects(newIndices, restoredSplineIds, childXsh, 0, idTable,
1320 fxTable);
1321
1322 // bring pegbars into sub xsheet
1323 if (!columnsOnly)
1324 bringPegbarsInsideChildXsheet(xsh, childXsh, indices, newIndices, idTable);
1325
1326 ExpressionReferenceManager::instance()->transferReference(xsh, childXsh,
1327 idTable, fxTable);
1328
1329 childXsh->updateFrameCount();
1330
1331 app->getCurrentXsheet()->blockSignals(true);
1332 app->getCurrentObject()->blockSignals(true);
1333 // remove columns in the parent xsheet
1334 ColumnCmd::deleteColumns(indices, false, true);
1335 app->getCurrentXsheet()->blockSignals(false);
1336 app->getCurrentObject()->blockSignals(false);
1337
1338 // insert subxsheet column at the leftmost of the deleted columns
1339 xsh->insertColumn(index);
1340
1341 // set subxsheet cells in the parent xhseet
1342 int r, rowCount = childXsh->getFrameCount();
1343 for (r = 0; r < rowCount; ++r)
1344 xsh->setCell(r, index, TXshCell(xl, TFrameId(r + 1)));
1345
1346 // the subxsheet node will always be connected to the table
1347 // regardless of the "columns only" option
1348 xsh->getStageObject(TStageObjectId::ColumnId(index))
1349 ->setParent(TStageObjectId::TableId);
1350 xsh->updateFrameCount();
1351
1352 // copy camera info
1353 // xsh -> childXsh
1354 TStageObjectTree *parentTree = xsh->getStageObjectTree();
1355 TStageObjectTree *childTree = childXsh->getStageObjectTree();
1356
1357 int tmpCamId = 0;
1358 for (int cam = 0; cam < parentTree->getCameraCount();) {
1359 TStageObject *parentCamera =
1360 parentTree->getStageObject(TStageObjectId::CameraId(tmpCamId), false);
1361 // skip the deleted camera
1362 if (!parentCamera) {
1363 tmpCamId++;
1364 continue;
1365 }
1366
1367 // if the camera exists
1368 if (parentCamera->getCamera()) {
1369 // obtain the correspondent camera in subxsheet. create it if it does not
1370 // exist
1371 TCamera *childCamera =
1372 childTree->getStageObject(TStageObjectId::CameraId(tmpCamId))
1373 ->getCamera();
1374 if (parentCamera && childCamera) {
1375 childCamera->setRes(parentCamera->getCamera()->getRes());
1376 childCamera->setSize(parentCamera->getCamera()->getSize());
1377 }
1378 }
1379 tmpCamId++;
1380 cam++;
1381 }
1382 // sync the current camera
1383 childTree->setCurrentCameraId(parentTree->getCurrentCameraId());
1384
1385 app->getCurrentXsheet()->notifyXsheetChanged();
1386 app->getCurrentScene()->setDirtyFlag(true);
1387 app->getCurrentObject()->notifyObjectIdSwitched();
1388 }
1389
1390 //-----------------------------------------------------------------------------
1391
collapseColumns(std::set<int> indices,const QList<TStageObjectId> & objIds)1392 void collapseColumns(std::set<int> indices,
1393 const QList<TStageObjectId> &objIds) {
1394 if (indices.empty()) return;
1395
1396 TApp *app = TApp::instance();
1397 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1398 std::set<int> oldIndices = indices;
1399
1400 int index = *indices.begin();
1401
1402 std::vector<TStageObjectId> roots = getRoots(objIds, app->getCurrentXsheet());
1403 TStageObject *rootObj = 0;
1404 if (roots.size() == 1) {
1405 rootObj = xsh->getStageObjectTree()->getStageObject(roots[0], false);
1406 assert(rootObj);
1407 }
1408
1409 StageObjectsData *data = new StageObjectsData();
1410 data->storeObjects(objIds.toVector().toStdVector(), xsh,
1411 StageObjectsData::eDoClone);
1412 data->storeColumnFxs(indices, xsh, StageObjectsData::eDoClone);
1413
1414 ExpressionReferenceMonitor *monitor = xsh->getExpRefMonitor()->clone();
1415
1416 ToonzScene *scene = app->getCurrentScene()->getScene();
1417 TXshLevel *xl = scene->createNewLevel(CHILD_XSHLEVEL);
1418 assert(xl);
1419
1420 TXshChildLevel *childLevel = xl->getChildLevel();
1421 assert(childLevel);
1422
1423 TXsheet *childXsh = childLevel->getXsheet();
1424
1425 std::set<int> newIndices;
1426 std::list<int> restoredSplineIds;
1427 QMap<TStageObjectId, TStageObjectId> idTable;
1428 QMap<TFx *, TFx *> fxTable;
1429 data->restoreObjects(newIndices, restoredSplineIds, childXsh, 0, idTable,
1430 fxTable);
1431 childXsh->updateFrameCount();
1432
1433 ExpressionReferenceManager::instance()->transferReference(xsh, childXsh,
1434 idTable, fxTable);
1435
1436 app->getCurrentXsheet()->blockSignals(true);
1437 app->getCurrentObject()->blockSignals(true);
1438 ColumnCmd::deleteColumns(indices, false, true);
1439 app->getCurrentXsheet()->blockSignals(false);
1440 app->getCurrentObject()->blockSignals(false);
1441
1442 xsh->insertColumn(index);
1443
1444 int r, rowCount = childXsh->getFrameCount();
1445 for (r = 0; r < rowCount; r++)
1446 xsh->setCell(r, index, TXshCell(xl, TFrameId(r + 1)));
1447
1448 if (roots.size() == 1 && rootObj)
1449 xsh->getStageObject(TStageObjectId::ColumnId(index))
1450 ->setParent(rootObj->getId());
1451 else
1452 xsh->getStageObject(TStageObjectId::ColumnId(index))
1453 ->setParent(TStageObjectId::TableId);
1454
1455 xsh->updateFrameCount();
1456
1457 app->getCurrentXsheet()->notifyXsheetChanged();
1458 app->getCurrentScene()->setDirtyFlag(true);
1459 app->getCurrentObject()->notifyObjectIdSwitched();
1460 }
1461
1462 //-----------------------------------------------------------------------------
1463
collapseColumns(std::set<int> indices,const std::set<TFx * > & fxs,bool columnsOnly)1464 void collapseColumns(std::set<int> indices, const std::set<TFx *> &fxs,
1465 bool columnsOnly) {
1466 if (indices.empty()) return;
1467 int index = *indices.begin();
1468 TApp *app = TApp::instance();
1469 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1470
1471 std::set<int> oldIndices = indices;
1472 //++++++++++++++++++++++++++++++
1473
1474 StageObjectsData *data = new StageObjectsData();
1475 data->storeColumns(indices, xsh, StageObjectsData::eDoClone);
1476 data->storeFxs(fxs, xsh, StageObjectsData::eDoClone);
1477
1478 ExpressionReferenceMonitor *monitor = xsh->getExpRefMonitor()->clone();
1479
1480 ToonzScene *scene = app->getCurrentScene()->getScene();
1481 TXshLevel *xl = scene->createNewLevel(CHILD_XSHLEVEL);
1482 assert(xl);
1483 TXshChildLevel *childLevel = xl->getChildLevel();
1484 assert(childLevel);
1485 TXsheet *childXsh = childLevel->getXsheet();
1486
1487 std::set<int> newIndices;
1488 std::list<int> restoredSplineIds;
1489 QMap<TStageObjectId, TStageObjectId> idTable;
1490 QMap<TFx *, TFx *> fxTable;
1491 data->restoreObjects(newIndices, restoredSplineIds, childXsh, 0, idTable,
1492 fxTable);
1493
1494 if (!columnsOnly)
1495 bringPegbarsInsideChildXsheet(xsh, childXsh, indices, newIndices, idTable);
1496
1497 ExpressionReferenceManager::instance()->transferReference(xsh, childXsh,
1498 idTable, fxTable);
1499
1500 childXsh->updateFrameCount();
1501
1502 std::map<TFx *, std::vector<TFxPort *>> roots =
1503 isConnected(indices, fxs, app->getCurrentXsheet());
1504 app->getCurrentXsheet()->blockSignals(true);
1505 app->getCurrentObject()->blockSignals(true);
1506 ColumnCmd::deleteColumns(indices, true, true);
1507 app->getCurrentXsheet()->blockSignals(false);
1508 app->getCurrentObject()->blockSignals(false);
1509 xsh->insertColumn(index);
1510
1511 std::set<TFx *>::const_iterator it;
1512 for (it = fxs.begin(); it != fxs.end(); it++) {
1513 TOutputFx *output = dynamic_cast<TOutputFx *>(*it);
1514 if (output) xsh->getFxDag()->removeOutputFx(output);
1515 }
1516
1517 int rowCount = childXsh->getFrameCount();
1518 int r;
1519 for (r = 0; r < rowCount; r++)
1520 xsh->setCell(r, index, TXshCell(xl, TFrameId(r + 1)));
1521
1522 //++++++++++++++++++++++++++++++
1523
1524 // Rimuovo gli effetti che sono in fxs dall'xsheet
1525 std::set<TFx *>::const_iterator it2;
1526 for (it2 = fxs.begin(); it2 != fxs.end(); it2++) removeFx(xsh, *it2);
1527
1528 xsh->getStageObject(TStageObjectId::ColumnId(index))
1529 ->setParent(TStageObjectId::TableId);
1530 if (roots.size() == 1) {
1531 TFx *fx = xsh->getColumn(index)->getFx();
1532 std::vector<TFxPort *> rootPorts = roots.begin()->second;
1533 int i;
1534 for (i = 0; i < rootPorts.size(); i++) rootPorts[i]->setFx(fx);
1535 xsh->getFxDag()->getTerminalFxs()->removeFx(fx);
1536 }
1537
1538 xsh->updateFrameCount();
1539 app->getCurrentXsheet()->notifyXsheetChanged();
1540 app->getCurrentScene()->setDirtyFlag(true);
1541 app->getCurrentObject()->notifyObjectIdSwitched();
1542 }
1543
1544 //-----------------------------------------------------------------------------
1545
getColumnIndexes(const QList<TStageObjectId> & objects,std::set<int> & indeces)1546 void getColumnIndexes(const QList<TStageObjectId> &objects,
1547 std::set<int> &indeces) {
1548 int i;
1549 for (i = 0; i < objects.size(); i++) {
1550 if (objects[i].isColumn()) indeces.insert(objects[i].getIndex());
1551 }
1552 }
1553
1554 //-----------------------------------------------------------------------------
1555
getColumnIndexesAndPegbarIds(const QList<TStageObjectId> & objects,std::set<int> & indeces,std::set<TStageObjectId> & pegbarIds)1556 void getColumnIndexesAndPegbarIds(const QList<TStageObjectId> &objects,
1557 std::set<int> &indeces,
1558 std::set<TStageObjectId> &pegbarIds) {
1559 int i;
1560 for (i = 0; i < objects.size(); i++) {
1561 if (objects[i].isColumn()) indeces.insert(objects[i].getIndex());
1562 if (objects[i].isPegbar()) pegbarIds.insert(objects[i]);
1563 }
1564 }
1565
1566 //-----------------------------------------------------------------------------
1567
getColumnIndexesAndInternalFxs(const QList<TFxP> & fxs,std::set<int> & indices,std::set<TFx * > & internalFx)1568 void getColumnIndexesAndInternalFxs(const QList<TFxP> &fxs,
1569 std::set<int> &indices,
1570 std::set<TFx *> &internalFx) {
1571 int i;
1572 for (i = 0; i < fxs.size(); i++) {
1573 TFx *fx = fxs[i].getPointer();
1574 TColumnFx *cFx = dynamic_cast<TColumnFx *>(fx);
1575 if (cFx)
1576 indices.insert(cFx->getColumnIndex());
1577 else {
1578 TXsheetFx *xshFx = dynamic_cast<TXsheetFx *>(fx);
1579 TOutputFx *outFx = dynamic_cast<TOutputFx *>(fx);
1580 if (xshFx) continue;
1581 if (outFx) {
1582 TXsheetFx *xshFx =
1583 dynamic_cast<TXsheetFx *>(outFx->getInputPort(0)->getFx());
1584 if (xshFx) continue;
1585 }
1586 internalFx.insert(fx);
1587 fx->addRef();
1588 }
1589 }
1590 }
1591
1592 //=============================================================================
1593 // CollapseUndo
1594 //-----------------------------------------------------------------------------
1595
1596 class CollapseUndo : public TUndo {
1597 protected:
1598 std::set<int> m_indices;
1599 StageObjectsData *m_data;
1600 StageObjectsData *m_newData;
1601 int m_columnIndex;
1602 QMap<TFx *, QList<TFxPort *>> m_columnOutputConnections;
1603 QMap<TStageObjectId, QList<TStageObjectId>> m_children;
1604 // id->parentId
1605 QMap<TStageObjectId, TStageObjectId> m_parents;
1606
doUndo() const1607 void doUndo() const {
1608 TApp *app = TApp::instance();
1609 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1610 xsh->removeColumn(m_columnIndex);
1611 std::set<int> indices = m_indices;
1612 std::list<int> restoredSplineIds;
1613 m_data->restoreObjects(indices, restoredSplineIds, xsh, 0);
1614 setColumnOutputConnections(m_columnOutputConnections);
1615 setChildren(m_children);
1616 setParents(m_parents);
1617
1618 TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
1619 app->getCurrentSelection()->getSelection());
1620 if (selection) {
1621 selection->selectNone();
1622 std::set<int> selectIndices = m_indices;
1623 std::set<int>::const_iterator indicesIt = selectIndices.begin();
1624 while (indicesIt != selectIndices.end())
1625 selection->selectColumn(*indicesIt++);
1626 }
1627 }
1628
doRedo(bool deleteOnlyColumns) const1629 void doRedo(bool deleteOnlyColumns) const {
1630 TApp *app = TApp::instance();
1631 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1632 std::set<int> indicesToRemove = m_indices;
1633 QMap<TFx *, QList<TFxPort *>> columnOutputConnections;
1634 getColumnOutputConnections(m_indices, columnOutputConnections);
1635 app->getCurrentXsheet()->blockSignals(true);
1636 app->getCurrentObject()->blockSignals(true);
1637 ColumnCmd::deleteColumns(indicesToRemove, deleteOnlyColumns, true);
1638 app->getCurrentXsheet()->blockSignals(false);
1639 app->getCurrentObject()->blockSignals(false);
1640 setColumnOutputConnections(columnOutputConnections);
1641 std::set<int> indices;
1642 indices.insert(m_columnIndex);
1643 std::list<int> restoredSplineIds;
1644 m_newData->restoreObjects(indices, restoredSplineIds, xsh, 0);
1645 TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
1646 app->getCurrentSelection()->getSelection());
1647 if (selection) {
1648 selection->selectNone();
1649 selection->selectColumn(m_columnIndex);
1650 }
1651 }
1652
1653 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)1654 CollapseUndo(const std::set<int> indices, int c0, StageObjectsData *data,
1655 StageObjectsData *newData,
1656 const QMap<TFx *, QList<TFxPort *>> &columnOutputConnections,
1657 const QMap<TStageObjectId, QList<TStageObjectId>> &children,
1658 const QMap<TStageObjectId, TStageObjectId> &parents)
1659 : m_indices(indices)
1660 , m_columnIndex(c0)
1661 , m_data(data)
1662 , m_newData(newData)
1663 , m_columnOutputConnections(columnOutputConnections)
1664 , m_children(children)
1665 , m_parents(parents) {}
1666
~CollapseUndo()1667 ~CollapseUndo() {
1668 delete m_data;
1669 delete m_newData;
1670 }
1671
undo() const1672 void undo() const override {
1673 doUndo();
1674 TApp *app = TApp::instance();
1675 app->getCurrentXsheet()->notifyXsheetChanged();
1676 app->getCurrentObject()->notifyObjectIdSwitched();
1677 changeSaveSubXsheetAsCommand();
1678 }
1679
redo() const1680 void redo() const override {
1681 doRedo(false);
1682 TApp *app = TApp::instance();
1683 app->getCurrentXsheet()->notifyXsheetChanged();
1684 app->getCurrentObject()->notifyObjectIdSwitched();
1685 changeSaveSubXsheetAsCommand();
1686 }
getSize() const1687 int getSize() const override { return sizeof(*this); }
1688
getHistoryString()1689 QString getHistoryString() override { return QObject::tr("Collapse"); }
getHistoryType()1690 int getHistoryType() override { return HistoryType::Xsheet; }
1691 };
1692
1693 //=============================================================================
1694 // CollapseFxUndo
1695 //-----------------------------------------------------------------------------
1696
1697 class CollapseFxUndo final : public CollapseUndo {
1698 std::set<TFx *> m_fxs;
1699 QMap<TFx *, FxConnections> m_fxConnections;
1700
1701 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)1702 CollapseFxUndo(const std::set<int> indices, int c0, StageObjectsData *data,
1703 StageObjectsData *newData,
1704 const QMap<TFx *, QList<TFxPort *>> &columnOutputConnections,
1705 const QMap<TStageObjectId, QList<TStageObjectId>> children,
1706 const QMap<TStageObjectId, TStageObjectId> &parents,
1707 const std::set<TFx *> &fxs,
1708 const QMap<TFx *, FxConnections> fxConnections)
1709 : CollapseUndo(indices, c0, data, newData, columnOutputConnections,
1710 children, parents)
1711 , m_fxs(fxs)
1712 , m_fxConnections(fxConnections) {}
1713
~CollapseFxUndo()1714 ~CollapseFxUndo() {
1715 for (auto const &e : m_fxs) e->release();
1716 }
1717
undo() const1718 void undo() const override {
1719 doUndo();
1720 TApp *app = TApp::instance();
1721 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1722 TFxSet *internalFxs = xsh->getFxDag()->getInternalFxs();
1723 TFxSet *terminalFxs = xsh->getFxDag()->getTerminalFxs();
1724 for (auto const &e : m_fxs)
1725 if (!internalFxs->containsFx(e)) {
1726 TOutputFx *outFx = dynamic_cast<TOutputFx *>(e);
1727 if (outFx)
1728 xsh->getFxDag()->addOutputFx(outFx);
1729 else
1730 internalFxs->addFx(e);
1731 }
1732 QMap<TFx *, FxConnections>::const_iterator it2;
1733 for (it2 = m_fxConnections.begin(); it2 != m_fxConnections.end(); it2++) {
1734 TFx *fx = it2.key();
1735 FxConnections connections = it2.value();
1736 QMap<int, TFx *> inputLinks = connections.getInputLinks();
1737 QMap<int, TFx *>::const_iterator it3;
1738 for (it3 = inputLinks.begin(); it3 != inputLinks.end(); it3++)
1739 fx->getInputPort(it3.key())->setFx(it3.value());
1740 if (connections.isTerminal()) {
1741 terminalFxs->addFx(fx);
1742 QList<TFx *> noTerminalInputFxs = connections.getNotTerminalInputFxs();
1743 int i;
1744 for (i = 0; i < noTerminalInputFxs.size(); i++)
1745 if (terminalFxs->containsFx(noTerminalInputFxs[i]))
1746 terminalFxs->removeFx(noTerminalInputFxs[i]);
1747 }
1748 QMap<TFx *, int> outputLinks = connections.getOutputLinks();
1749 QMap<TFx *, int>::const_iterator it4;
1750 for (it4 = outputLinks.begin(); it4 != outputLinks.end(); it4++)
1751 it4.key()->getInputPort(it4.value())->setFx(fx);
1752 }
1753 app->getCurrentXsheet()->notifyXsheetChanged();
1754 app->getCurrentObject()->notifyObjectIdSwitched();
1755 changeSaveSubXsheetAsCommand();
1756 }
1757
redo() const1758 void redo() const override {
1759 TApp *app = TApp::instance();
1760 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1761 std::map<TFx *, std::vector<TFxPort *>> roots =
1762 isConnected(m_indices, m_fxs, app->getCurrentXsheet());
1763 doRedo(true);
1764 std::set<TFx *>::const_iterator it2;
1765 for (it2 = m_fxs.begin(); it2 != m_fxs.end(); it2++) removeFx(xsh, *it2);
1766 if (roots.size() == 1) {
1767 TFx *fx = xsh->getColumn(m_columnIndex)->getFx();
1768 std::vector<TFxPort *> rootPorts = roots.begin()->second;
1769 int i;
1770 for (i = 0; i < rootPorts.size(); i++) rootPorts[i]->setFx(fx);
1771 xsh->getFxDag()->getTerminalFxs()->removeFx(fx);
1772 }
1773
1774 app->getCurrentXsheet()->notifyXsheetChanged();
1775 app->getCurrentObject()->notifyObjectIdSwitched();
1776 changeSaveSubXsheetAsCommand();
1777 }
getSize() const1778 int getSize() const override { return sizeof(*this); }
1779
getHistoryString()1780 QString getHistoryString() override { return QObject::tr("Collapse (Fx)"); }
1781 };
1782
1783 //=============================================================================
1784 // ExplodeChildUndoRemovingColumn
1785 //-----------------------------------------------------------------------------
1786
1787 class ExplodeChildUndoRemovingColumn final : public TUndo {
1788 std::set<int> m_newIndexs;
1789 int m_index;
1790 StageObjectsData *m_oldData;
1791 StageObjectsData *m_newData;
1792 QMap<TFx *, QList<TFxPort *>> m_oldColumnOutputConnections;
1793 QMap<TFx *, QList<TFxPort *>> m_newColumnOutputConnections;
1794 // objId->parentObjId
1795 QMap<TStageObjectId, TStageObjectId> m_parentIds;
1796 QList<TStageObject *> m_pegObjects;
1797 QMap<TStageObjectSpline *, TStageObjectSpline *> m_splines;
1798 TFx *m_root;
1799 std::set<TFx *> m_oldInternalFxs;
1800 std::set<TOutputFx *> m_oldOutFxs;
1801 std::set<TOutputFx *> m_newOutFxs;
1802
1803 // to handle grouping for the subxsheet
1804 QStack<int> m_objGroupIds;
1805 QStack<std::wstring> m_objGroupNames;
1806
1807 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)1808 ExplodeChildUndoRemovingColumn(
1809 const std::set<int> &newIndexs, int index, StageObjectsData *oldData,
1810 StageObjectsData *newData,
1811 const QMap<TFx *, QList<TFxPort *>> &columnOutputConnections,
1812 const QList<TStageObject *> &pegObjects,
1813 const QMap<TStageObjectSpline *, TStageObjectSpline *> &splines,
1814 const std::set<TFx *> &oldInternalFxs,
1815 const std::set<TOutputFx *> oldOutFxs, TFx *root,
1816 const QStack<int> &objGroupIds, const QStack<std::wstring> &objGroupNames)
1817 : m_newIndexs(newIndexs)
1818 , m_index(index)
1819 , m_oldData(oldData)
1820 , m_newData(newData)
1821 , m_oldColumnOutputConnections(columnOutputConnections)
1822 , m_pegObjects(pegObjects)
1823 , m_splines(splines)
1824 , m_root(root)
1825 , m_oldInternalFxs(oldInternalFxs)
1826 , m_oldOutFxs(oldOutFxs)
1827 , m_objGroupIds(objGroupIds)
1828 , m_objGroupNames(objGroupNames) {
1829 TApp *app = TApp::instance();
1830 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1831 std::set<int>::iterator it;
1832 for (it = m_newIndexs.begin(); it != m_newIndexs.end(); it++) {
1833 TXshColumn *column = xsh->getColumn(*it);
1834 TStageObjectId colId = TStageObjectId::ColumnId(*it);
1835 m_parentIds[colId] = xsh->getStageObjectParent(colId);
1836
1837 TFx *columnFx = column->getFx();
1838 if (!columnFx) continue;
1839
1840 QList<TFxPort *> outputConnections;
1841 int i;
1842 for (i = 0; i < columnFx->getOutputConnectionCount(); i++)
1843 outputConnections.append(columnFx->getOutputConnection(i));
1844 m_newColumnOutputConnections[columnFx] = outputConnections;
1845 }
1846
1847 std::set<TOutputFx *>::iterator it2;
1848 for (it2 = m_oldOutFxs.begin(); it2 != m_oldOutFxs.end(); it2++)
1849 (*it2)->addRef();
1850 int i, outFxCount = xsh->getFxDag()->getOutputFxCount();
1851 for (i = 0; i < outFxCount; i++) {
1852 TOutputFx *outFx = xsh->getFxDag()->getOutputFx(i);
1853 m_newOutFxs.insert(outFx);
1854 outFx->addRef();
1855 }
1856
1857 for (int i = 0; i < m_pegObjects.size(); i++)
1858 m_parentIds[m_pegObjects[i]->getId()] = m_pegObjects[i]->getParent();
1859
1860 QMap<TStageObjectSpline *, TStageObjectSpline *>::iterator it3;
1861 for (it3 = m_splines.begin(); it3 != m_splines.end(); it3++)
1862 it3.value()->addRef();
1863 }
1864
~ExplodeChildUndoRemovingColumn()1865 ~ExplodeChildUndoRemovingColumn() {
1866 delete m_oldData;
1867 delete m_newData;
1868 int i;
1869 for (i = m_pegObjects.size() - 1; i >= 0; i--) m_pegObjects[i]->release();
1870 std::set<TOutputFx *>::iterator it2;
1871 for (it2 = m_oldOutFxs.begin(); it2 != m_oldOutFxs.end(); it2++)
1872 (*it2)->release();
1873 for (it2 = m_newOutFxs.begin(); it2 != m_newOutFxs.end(); it2++)
1874 (*it2)->release();
1875 QMap<TStageObjectSpline *, TStageObjectSpline *>::iterator it3;
1876 for (it3 = m_splines.begin(); it3 != m_splines.end(); it3++)
1877 it3.value()->release();
1878 }
1879
setEditingFxGroup(TFx * fx,int editingGroup,const QStack<int> & fxGroupIds) const1880 void setEditingFxGroup(TFx *fx, int editingGroup,
1881 const QStack<int> &fxGroupIds) const {
1882 fx->getAttributes()->closeEditingGroup(fxGroupIds.top());
1883 while (fx->getAttributes()->getEditingGroupId() != editingGroup)
1884 fx->getAttributes()->editGroup();
1885 for (int i = 0; i < fx->getInputPortCount(); i++) {
1886 TFx *inputFx = fx->getInputPort(i)->getFx();
1887 if (inputFx) setEditingFxGroup(inputFx, editingGroup, fxGroupIds);
1888 }
1889 }
1890
setEditingObjGroup(TStageObject * obj,int editingGroup,const QStack<int> & objGroupIds) const1891 void setEditingObjGroup(TStageObject *obj, int editingGroup,
1892 const QStack<int> &objGroupIds) const {
1893 obj->closeEditingGroup(objGroupIds.top());
1894 while (obj->getEditingGroupId() != editingGroup) obj->editGroup();
1895 std::list<TStageObject *> children = obj->getChildren();
1896 std::list<TStageObject *>::iterator it;
1897 for (it = children.begin(); it != children.end(); it++) {
1898 TStageObject *childeObj = *it;
1899 if (childeObj) setEditingObjGroup(childeObj, editingGroup, objGroupIds);
1900 }
1901 }
1902
undo() const1903 void undo() const override {
1904 TApp *app = TApp::instance();
1905 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1906 int editingGroup = -1;
1907 TStageObjectId parentId = TStageObjectId::NoneId;
1908 if (m_root && m_root->getOutputConnectionCount() > 0)
1909 editingGroup = m_root->getOutputConnection(0)
1910 ->getOwnerFx()
1911 ->getAttributes()
1912 ->getEditingGroupId();
1913
1914 std::set<int> indexesToRemove = m_newIndexs;
1915 app->getCurrentXsheet()->blockSignals(true);
1916 app->getCurrentObject()->blockSignals(true);
1917 ColumnCmd::deleteColumns(indexesToRemove, false, true);
1918 app->getCurrentXsheet()->blockSignals(false);
1919 app->getCurrentObject()->blockSignals(false);
1920 int i;
1921 for (i = m_pegObjects.size() - 1; i >= 0; i--) {
1922 TStageObjectId pegObjectId = m_pegObjects[i]->getId();
1923 TStageObjectId _parentId = xsh->getStageObjectParent(pegObjectId);
1924 if (!m_pegObjects.contains(xsh->getStageObject(_parentId)))
1925 parentId = _parentId;
1926 if (app->getCurrentObject()->getObjectId() == pegObjectId)
1927 app->getCurrentObject()->setObjectId(TStageObjectId::TableId);
1928 xsh->getStageObjectTree()->removeStageObject(pegObjectId);
1929 }
1930 QMap<TStageObjectSpline *, TStageObjectSpline *>::const_iterator it;
1931 for (it = m_splines.begin(); it != m_splines.end(); it++) {
1932 TStageObjectSpline *spline = it.value();
1933 xsh->getStageObjectTree()->removeSpline(spline);
1934 }
1935 std::set<int> indexes;
1936 indexes.insert(m_index);
1937 std::list<int> restoredSplineIds;
1938 m_oldData->restoreObjects(indexes, restoredSplineIds, xsh, 0);
1939 setColumnOutputConnections(m_oldColumnOutputConnections);
1940 TFxSet *internals = xsh->getFxDag()->getInternalFxs();
1941 for (i = internals->getFxCount() - 1; i >= 0; i--) {
1942 TFx *fx = internals->getFx(i);
1943 if (m_oldInternalFxs.find(fx) == m_oldInternalFxs.end())
1944 internals->removeFx(fx);
1945 }
1946 std::set<TOutputFx *>::const_iterator it2;
1947 for (it2 = m_newOutFxs.begin(); it2 != m_newOutFxs.end(); it2++) {
1948 if (m_oldOutFxs.find(*it2) == m_oldOutFxs.end())
1949 xsh->getFxDag()->removeOutputFx(*it2);
1950 }
1951 TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
1952 app->getCurrentSelection()->getSelection());
1953 if (selection) {
1954 selection->selectNone();
1955 selection->selectColumn(m_index);
1956 }
1957 // reinsert in groups
1958 TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(m_index));
1959 if (parentId != TStageObjectId::NoneId) obj->setParent(parentId);
1960 if (!m_objGroupIds.empty()) {
1961 TStageObjectId parentId = obj->getParent();
1962 TStageObject *parentObj = xsh->getStageObject(parentId);
1963 int i;
1964 for (i = 0; i < m_objGroupIds.size(); i++) {
1965 obj->setGroupId(m_objGroupIds[i]);
1966 obj->setGroupName(m_objGroupNames[i]);
1967 }
1968 for (i = 0;
1969 i < m_objGroupIds.size() && parentObj->getEditingGroupId() >= 0; i++)
1970 obj->editGroup();
1971 }
1972 QStack<int> fxGroupIds;
1973 if (m_root) fxGroupIds = m_root->getAttributes()->getGroupIdStack();
1974 if (!fxGroupIds.empty()) {
1975 // recupero l'id del gruppo che si sta editando!
1976 TFx *colFx = xsh->getColumn(m_index)->getFx();
1977 assert(colFx);
1978 colFx->getAttributes()->closeEditingGroup(fxGroupIds.top());
1979 while (colFx->getAttributes()->getEditingGroupId() != editingGroup)
1980 colFx->getAttributes()->editGroup();
1981 }
1982 app->getCurrentXsheet()->notifyXsheetChanged();
1983 }
1984
redo() const1985 void redo() const override {
1986 TApp *app = TApp::instance();
1987 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1988
1989 TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(m_index));
1990 TStageObjectId parentId = obj->getParent();
1991 TStageObject *parentObj = xsh->getStageObject(parentId);
1992
1993 int objEditingGroup = -1;
1994 if (parentObj->isGrouped())
1995 objEditingGroup = parentObj->getEditingGroupId();
1996
1997 TXshColumn *column = xsh->getColumn(m_index);
1998 assert(column);
1999 TFx *columnFx = column->getFx();
2000 assert(columnFx);
2001 int i;
2002 std::vector<TFxPort *> outPorts;
2003 for (i = 0; i < columnFx->getOutputConnectionCount(); i++)
2004 outPorts.push_back(columnFx->getOutputConnection(i));
2005 xsh->removeColumn(m_index);
2006 std::set<int> indexes = m_newIndexs;
2007 for (i = m_pegObjects.size() - 1; i >= 0; i--)
2008 xsh->getStageObjectTree()->insertStageObject(m_pegObjects[i]);
2009 QMap<TStageObjectSpline *, TStageObjectSpline *>::const_iterator it3;
2010 for (it3 = m_splines.begin(); it3 != m_splines.end(); it3++)
2011 xsh->getStageObjectTree()->insertSpline(it3.value());
2012 std::list<int> restoredSplineIds;
2013 m_newData->restoreObjects(indexes, restoredSplineIds, xsh, 0);
2014 for (i = 0; i < m_pegObjects.size(); i++)
2015 xsh->setStageObjectParent(m_pegObjects[i]->getId(),
2016 m_parentIds[m_pegObjects[i]->getId()]);
2017 std::set<int>::const_iterator it;
2018 for (it = m_newIndexs.begin(); it != m_newIndexs.end(); it++) {
2019 TStageObjectId colId = TStageObjectId::ColumnId(*it);
2020 TStageObjectId parentId = m_parentIds[colId];
2021 xsh->setStageObjectParent(colId, m_parentIds[colId]);
2022 TStageObject *obj = xsh->getStageObject(colId);
2023 TStageObject *parentObj = xsh->getStageObject(parentId);
2024 if (parentObj->isGrouped()) {
2025 QStack<int> idStack = parentObj->getGroupIdStack();
2026 QStack<std::wstring> groupstack = parentObj->getGroupNameStack();
2027 for (int i = 0; i < idStack.size(); i++) {
2028 obj->setGroupId(idStack[i]);
2029 obj->setGroupName(groupstack[i]);
2030 }
2031 int editedGroup = parentObj->getEditingGroupId();
2032 while (editedGroup != -1 && obj->getEditingGroupId() != editedGroup)
2033 obj->editGroup();
2034 }
2035 }
2036 setColumnOutputConnections(m_newColumnOutputConnections);
2037 for (i = 0; i < outPorts.size() && m_root; i++) outPorts[i]->setFx(m_root);
2038 std::set<TOutputFx *>::const_iterator it2;
2039 for (it2 = m_newOutFxs.begin(); it2 != m_newOutFxs.end(); it2++) {
2040 if (m_oldOutFxs.find(*it2) == m_oldOutFxs.end())
2041 xsh->getFxDag()->addOutputFx(*it2);
2042 }
2043 TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
2044 app->getCurrentSelection()->getSelection());
2045 if (selection) {
2046 selection->selectNone();
2047 std::set<int> selectIndices = m_newIndexs;
2048 std::set<int>::const_iterator indicesIt = selectIndices.begin();
2049 while (indicesIt != selectIndices.end())
2050 selection->selectColumn(*indicesIt++);
2051 }
2052 QStack<int> fxGroupIds;
2053 if (m_root) fxGroupIds = m_root->getAttributes()->getGroupIdStack();
2054 if (!fxGroupIds.empty()) {
2055 // recupero l'id del gruppo che si sta editando!
2056 int editingGroup = -1;
2057 if (m_root->getOutputConnectionCount() > 0)
2058 editingGroup = m_root->getOutputConnection(0)
2059 ->getOwnerFx()
2060 ->getAttributes()
2061 ->getEditingGroupId();
2062 setEditingFxGroup(m_root, editingGroup, fxGroupIds);
2063 }
2064 app->getCurrentXsheet()->notifyXsheetChanged();
2065 }
2066
getSize() const2067 int getSize() const override { return sizeof(*this); }
2068
getHistoryString()2069 QString getHistoryString() override { return QObject::tr("Explode"); }
getHistoryType()2070 int getHistoryType() override { return HistoryType::Xsheet; }
2071 };
2072
2073 //=============================================================================
2074 // ExplodeChildUndo
2075 //-----------------------------------------------------------------------------
2076
2077 class ExplodeChildUndoWithoutRemovingColumn final : public TUndo {
2078 std::set<int> m_newIndexs;
2079 int m_index, m_from, m_to;
2080
2081 TCellData *m_cellData;
2082 StageObjectsData *m_newData;
2083 QMap<TFx *, QList<TFxPort *>> m_newColumnOutputConnections;
2084 QList<TStageObject *> m_pegObjects;
2085 QMap<TStageObjectSpline *, TStageObjectSpline *> m_splines;
2086 std::set<TFx *> m_oldInternalFxs;
2087 std::set<TOutputFx *> m_oldOutFxs;
2088 std::set<TOutputFx *> m_newOutFxs;
2089
2090 // to handle grouping for the subxsheet
2091 QStack<int> m_objGroupIds;
2092 QStack<std::wstring> m_objGroupNames;
2093
2094 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)2095 ExplodeChildUndoWithoutRemovingColumn(
2096 const std::set<int> &newIndexs, int index, int from, int to,
2097 TCellData *cellData, StageObjectsData *newData,
2098 QList<TStageObject *> pegObjects,
2099 const QMap<TStageObjectSpline *, TStageObjectSpline *> &splines,
2100 const std::set<TFx *> &oldInternalFxs,
2101 const std::set<TOutputFx *> oldOutFxs, const QStack<int> &objGroupIds,
2102 const QStack<std::wstring> &objGroupNames)
2103 : m_newIndexs(newIndexs)
2104 , m_index(index)
2105 , m_from(from)
2106 , m_to(to)
2107 , m_cellData(cellData)
2108 , m_newData(newData)
2109 , m_pegObjects(pegObjects)
2110 , m_splines(splines)
2111 , m_oldInternalFxs(oldInternalFxs)
2112 , m_oldOutFxs(oldOutFxs)
2113 , m_objGroupIds(objGroupIds)
2114 , m_objGroupNames(objGroupNames) {
2115 TApp *app = TApp::instance();
2116 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
2117 std::set<int>::iterator it;
2118 for (it = m_newIndexs.begin(); it != m_newIndexs.end(); it++) {
2119 TXshColumn *column = xsh->getColumn(*it);
2120 TFx *columnFx = column->getFx();
2121 QList<TFxPort *> outputConnections;
2122 int i;
2123 for (i = 0; i < columnFx->getOutputConnectionCount(); i++)
2124 outputConnections.append(columnFx->getOutputConnection(i));
2125 m_newColumnOutputConnections[columnFx] = outputConnections;
2126 }
2127 std::set<TOutputFx *>::iterator it2;
2128 for (it2 = m_oldOutFxs.begin(); it2 != m_oldOutFxs.end(); it2++)
2129 (*it2)->addRef();
2130 int i, outFxCount = xsh->getFxDag()->getOutputFxCount();
2131 for (i = 0; i < outFxCount; i++) {
2132 TOutputFx *outFx = xsh->getFxDag()->getOutputFx(i);
2133 m_newOutFxs.insert(outFx);
2134 outFx->addRef();
2135 }
2136 }
2137
~ExplodeChildUndoWithoutRemovingColumn()2138 ~ExplodeChildUndoWithoutRemovingColumn() {
2139 delete m_cellData;
2140 delete m_newData;
2141 int i;
2142 for (i = m_pegObjects.size() - 1; i >= 0; i--) m_pegObjects[i]->release();
2143 std::set<TOutputFx *>::iterator it2;
2144 for (it2 = m_oldOutFxs.begin(); it2 != m_oldOutFxs.end(); it2++)
2145 (*it2)->release();
2146 for (it2 = m_newOutFxs.begin(); it2 != m_newOutFxs.end(); it2++)
2147 (*it2)->release();
2148 }
2149
undo() const2150 void undo() const override {
2151 TApp *app = TApp::instance();
2152 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
2153
2154 std::set<int> indexesToRemove = m_newIndexs;
2155 app->getCurrentXsheet()->blockSignals(true);
2156 app->getCurrentObject()->blockSignals(true);
2157 ColumnCmd::deleteColumns(indexesToRemove, false, true);
2158 app->getCurrentXsheet()->blockSignals(false);
2159 app->getCurrentObject()->blockSignals(false);
2160 int i;
2161 for (i = m_pegObjects.size() - 1; i >= 0; i--)
2162 xsh->getStageObjectTree()->removeStageObject(m_pegObjects[i]->getId());
2163 std::set<int> indexes;
2164 indexes.insert(m_index);
2165 int to = m_to;
2166 int index = m_index;
2167 m_cellData->getCells(xsh, m_from, m_index, to, index, false, false);
2168 TFxSet *internals = xsh->getFxDag()->getInternalFxs();
2169 for (i = internals->getFxCount() - 1; i >= 0; i--) {
2170 TFx *fx = internals->getFx(i);
2171 if (m_oldInternalFxs.find(fx) == m_oldInternalFxs.end())
2172 internals->removeFx(fx);
2173 }
2174 std::set<TOutputFx *>::const_iterator it;
2175 for (it = m_newOutFxs.begin(); it != m_newOutFxs.end(); it++) {
2176 if (m_oldOutFxs.find(*it) == m_oldOutFxs.end())
2177 xsh->getFxDag()->removeOutputFx(*it);
2178 }
2179 TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
2180 app->getCurrentSelection()->getSelection());
2181 if (selection) {
2182 selection->selectNone();
2183 selection->selectColumn(m_index);
2184 }
2185 // reinsert in groups
2186 if (!m_objGroupIds.empty()) {
2187 TStageObject *obj =
2188 xsh->getStageObject(TStageObjectId::ColumnId(m_index));
2189 TStageObjectId parentId = obj->getParent();
2190 TStageObject *parentObj = xsh->getStageObject(parentId);
2191 int i;
2192 for (i = 0; i < m_objGroupIds.size(); i++) {
2193 obj->setGroupId(m_objGroupIds[i]);
2194 obj->setGroupName(m_objGroupNames[i]);
2195 }
2196 for (i = 0;
2197 i < m_objGroupIds.size() && parentObj->getEditingGroupId() >= 0; i++)
2198 obj->editGroup();
2199 }
2200 app->getCurrentXsheet()->notifyXsheetChanged();
2201 }
2202
redo() const2203 void redo() const override {
2204 TApp *app = TApp::instance();
2205 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
2206 xsh->clearCells(m_from, m_index, m_to - m_from + 1);
2207 std::set<int> indexes = m_newIndexs;
2208 int i;
2209 for (i = m_pegObjects.size() - 1; i >= 0; i--)
2210 xsh->getStageObjectTree()->insertStageObject(m_pegObjects[i]);
2211 std::list<int> restoredSplineIds;
2212 m_newData->restoreObjects(indexes, restoredSplineIds, xsh, 0);
2213 setColumnOutputConnections(m_newColumnOutputConnections);
2214 std::set<TOutputFx *>::const_iterator it;
2215 for (it = m_newOutFxs.begin(); it != m_newOutFxs.end(); it++) {
2216 if (m_oldOutFxs.find(*it) == m_oldOutFxs.end())
2217 xsh->getFxDag()->addOutputFx(*it);
2218 }
2219 TColumnSelection *selection = dynamic_cast<TColumnSelection *>(
2220 app->getCurrentSelection()->getSelection());
2221 if (selection) {
2222 selection->selectNone();
2223 std::set<int> selectIndices = m_newIndexs;
2224 std::set<int>::const_iterator indicesIt = selectIndices.begin();
2225 while (indicesIt != selectIndices.end())
2226 selection->selectColumn(*indicesIt++);
2227 }
2228 // reinsert in groups
2229 if (!m_objGroupIds.empty()) {
2230 for (auto const &e : indexes) {
2231 TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(e));
2232 TStageObjectId parentId = obj->getParent();
2233 TStageObject *parentObj = xsh->getStageObject(parentId);
2234 int i;
2235 for (i = 0; i < m_objGroupIds.size(); i++) {
2236 obj->setGroupId(m_objGroupIds[i]);
2237 obj->setGroupName(m_objGroupNames[i]);
2238 }
2239 for (i = 0;
2240 i < m_objGroupIds.size() && parentObj->getEditingGroupId() >= 0;
2241 i++)
2242 obj->editGroup();
2243 }
2244 }
2245 app->getCurrentXsheet()->notifyXsheetChanged();
2246 }
2247
getSize() const2248 int getSize() const override { return sizeof(*this); }
2249
getHistoryString()2250 QString getHistoryString() override { return QObject::tr("Explode"); }
getHistoryType()2251 int getHistoryType() override { return HistoryType::Xsheet; }
2252 };
2253
2254 } // namespace
2255
2256 //=============================================================================
2257 // OpenChildCommand
2258 //-----------------------------------------------------------------------------
2259
2260 class OpenChildCommand final : public MenuItemHandler {
2261 public:
OpenChildCommand()2262 OpenChildCommand() : MenuItemHandler(MI_OpenChild) {}
execute()2263 void execute() override { openSubXsheet(); }
2264 } openChildCommand;
2265
2266 //=============================================================================
2267 // CloseChildCommand
2268 //-----------------------------------------------------------------------------
2269
2270 class CloseChildCommand final : public MenuItemHandler {
2271 public:
CloseChildCommand()2272 CloseChildCommand() : MenuItemHandler(MI_CloseChild) {}
execute()2273 void execute() override { closeSubXsheet(1); }
2274 } closeChildCommand;
2275
2276 //=============================================================================
2277 // collapseColumns
2278 //-----------------------------------------------------------------------------
2279
2280 //! Collapses the specified column indices in current XSheet.
collapse(std::set<int> & indices)2281 void SubsceneCmd::collapse(std::set<int> &indices) {
2282 if (indices.empty()) return;
2283
2284 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
2285 bool onlyColumns = true;
2286 if (hasPegbarsToBringInsideChildXsheet(xsh, indices)) {
2287 // User must decide if pegbars must be collapsed too
2288 QString question(QObject::tr("Collapsing columns: what you want to do?"));
2289
2290 QList<QString> list;
2291 list.append(QObject::tr(
2292 "Maintain parenting relationships in the sub-xsheet as well."));
2293 list.append(
2294 QObject::tr("Include the selected columns in the sub-xsheet without "
2295 "parenting info."));
2296
2297 int ret = DVGui::RadioButtonMsgBox(DVGui::WARNING, question, list);
2298 if (ret == 0) return;
2299 onlyColumns = (ret == 2);
2300 }
2301 if (!ColumnCmd::checkExpressionReferences(indices, onlyColumns, true)) return;
2302
2303 std::set<int> oldIndices = indices;
2304 int index = *indices.begin();
2305
2306 // Retrieve current status to backup it in the UNDO
2307 StageObjectsData *oldData = new StageObjectsData();
2308
2309 oldData->storeColumns(indices, xsh, 0);
2310 oldData->storeColumnFxs(indices, xsh, 0);
2311
2312 QMap<TFx *, QList<TFxPort *>> columnOutputConnections;
2313 getColumnOutputConnections(indices, columnOutputConnections);
2314
2315 QMap<TStageObjectId, QList<TStageObjectId>> children;
2316 getChildren(indices, children);
2317
2318 QMap<TStageObjectId, TStageObjectId> parents;
2319 getParents(indices, parents);
2320
2321 // Perform the collapse
2322 collapseColumns(indices, onlyColumns);
2323 setColumnOutputConnections(columnOutputConnections);
2324
2325 // Retrieve current status to backup it in the REDO
2326 indices.clear();
2327 indices.insert(index);
2328
2329 StageObjectsData *newData = new StageObjectsData();
2330 newData->storeColumns(indices, xsh, 0);
2331 newData->storeColumnFxs(indices, xsh, 0);
2332
2333 // Build the undo
2334 CollapseUndo *undo =
2335 new CollapseUndo(oldIndices, index, oldData, newData,
2336 columnOutputConnections, children, parents);
2337 TUndoManager::manager()->add(undo);
2338
2339 changeSaveSubXsheetAsCommand();
2340 }
2341
2342 //-----------------------------------------------------------------------------
2343
collapse(const QList<TStageObjectId> & objects)2344 void SubsceneCmd::collapse(const QList<TStageObjectId> &objects) {
2345 if (objects.isEmpty()) return;
2346
2347 std::set<int> indices;
2348 getColumnIndexes(objects, indices);
2349
2350 if (!ColumnCmd::checkExpressionReferences(objects)) return;
2351
2352 std::set<int> oldIndices = indices;
2353 int index = *indices.begin();
2354
2355 StageObjectsData *oldData = new StageObjectsData();
2356
2357 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
2358 oldData->storeColumns(indices, xsh, 0);
2359 oldData->storeColumnFxs(indices, xsh, 0);
2360
2361 QMap<TFx *, QList<TFxPort *>> columnOutputConnections;
2362 getColumnOutputConnections(indices, columnOutputConnections);
2363
2364 QMap<TStageObjectId, QList<TStageObjectId>> children;
2365 getChildren(indices, children);
2366
2367 QMap<TStageObjectId, TStageObjectId> parents;
2368 getParents(indices, parents);
2369
2370 collapseColumns(indices, objects);
2371 setColumnOutputConnections(columnOutputConnections);
2372
2373 indices.clear();
2374 indices.insert(index);
2375
2376 StageObjectsData *newData = new StageObjectsData();
2377 newData->storeColumns(indices, xsh, 0);
2378 newData->storeColumnFxs(indices, xsh, 0);
2379
2380 CollapseUndo *undo =
2381 new CollapseUndo(oldIndices, index, oldData, newData,
2382 columnOutputConnections, children, parents);
2383 TUndoManager::manager()->add(undo);
2384
2385 changeSaveSubXsheetAsCommand();
2386 }
2387
2388 //-----------------------------------------------------------------------------
2389
collapse(const QList<TFxP> & fxs)2390 void SubsceneCmd::collapse(const QList<TFxP> &fxs) {
2391 if (fxs.isEmpty()) return;
2392
2393 std::set<int> indices;
2394 std::set<TFx *> internalFx;
2395 getColumnIndexesAndInternalFxs(fxs, indices, internalFx);
2396
2397 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
2398 bool onlyColumns = true;
2399 if (hasPegbarsToBringInsideChildXsheet(xsh, indices)) {
2400 // User must decide if pegbars must be collapsed too
2401 QString question(QObject::tr("Collapsing columns: what you want to do?"));
2402 QList<QString> list;
2403 list.append(QObject::tr(
2404 "Maintain parenting relationships in the sub-xsheet as well."));
2405 list.append(
2406 QObject::tr("Include the selected columns in the sub-xsheet without "
2407 "parenting info."));
2408 int ret = DVGui::RadioButtonMsgBox(DVGui::WARNING, question, list);
2409 if (ret == 0) return;
2410 onlyColumns = (ret == 2);
2411 }
2412
2413 if (!ColumnCmd::checkExpressionReferences(indices, internalFx, onlyColumns,
2414 true))
2415 return;
2416
2417 std::set<int> oldIndices = indices;
2418 int index = *indices.begin();
2419
2420 StageObjectsData *oldData = new StageObjectsData();
2421
2422 oldData->storeColumns(indices, xsh, 0);
2423 oldData->storeColumnFxs(indices, xsh, 0);
2424
2425 QMap<TFx *, QList<TFxPort *>> columnOutputConnections;
2426 getColumnOutputConnections(indices, columnOutputConnections);
2427
2428 QMap<TStageObjectId, QList<TStageObjectId>> children;
2429 getChildren(indices, children);
2430
2431 QMap<TStageObjectId, TStageObjectId> parents;
2432 getParents(indices, parents);
2433
2434 QMap<TFx *, FxConnections> fxConnections;
2435 getFxConnections(fxConnections, internalFx, xsh);
2436
2437 collapseColumns(indices, internalFx, onlyColumns);
2438
2439 indices.clear();
2440 indices.insert(index);
2441
2442 StageObjectsData *newData = new StageObjectsData();
2443 newData->storeColumns(indices, xsh, 0);
2444 newData->storeColumnFxs(indices, xsh, 0);
2445
2446 CollapseFxUndo *undo = new CollapseFxUndo(oldIndices, index, oldData, newData,
2447 columnOutputConnections, children,
2448 parents, internalFx, fxConnections);
2449 TUndoManager::manager()->add(undo);
2450
2451 changeSaveSubXsheetAsCommand();
2452 }
2453
2454 //-----------------------------------------------------------------------------
2455
explode(int index)2456 void SubsceneCmd::explode(int index) {
2457 TApp *app = TApp::instance();
2458 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
2459
2460 TFrameHandle *frameHandle = app->getCurrentFrame();
2461 assert(frameHandle->getFrameType() == TFrameHandle::SceneFrame);
2462 int frameIndex = app->getCurrentFrame()->getFrame();
2463
2464 /*- これからExplodeするセルを取得 -*/
2465 TXshCell cell = xsh->getCell(frameIndex, index);
2466
2467 TXshChildLevel *childLevel = cell.getChildLevel();
2468 if (!childLevel) return;
2469
2470 // Cannot remove the column if it contains frames of a TXshSimpleLevel.
2471 int from, to;
2472
2473 // removeColumn is true if the column contains only one subXsheetLevel (i.e.
2474 // the column will be removed) removeColumn is false if there is another level
2475 // in the same column (i.e. the column will remain)
2476 bool removeColumn =
2477 mustRemoveColumn(from, to, childLevel, xsh, index, frameIndex);
2478
2479 /*- Pegbarを親Sheetに持って出るか?の質問ダイアログ -*/
2480 QString question(QObject::tr("Exploding Sub-xsheet: what you want to do?"));
2481 QList<QString> list;
2482 list.append(
2483 QObject::tr("Maintain parenting relationships in the main xsheet."));
2484 list.append(
2485 QObject::tr("Bring columns in the main xsheet without parenting."));
2486 int ret = DVGui::RadioButtonMsgBox(DVGui::WARNING, question, list);
2487 if (ret == 0) return;
2488
2489 if (!ExpressionReferenceManager::instance()->checkExplode(
2490 childLevel->getXsheet(), index, removeColumn, ret == 2))
2491 return;
2492
2493 // Collect column stage object informations
2494 TStageObjectId colId = TStageObjectId::ColumnId(index);
2495 TStageObjectId parentId = xsh->getStageObjectParent(colId);
2496 TStageObject *obj = xsh->getStageObjectTree()->getStageObject(colId, false);
2497 assert(obj);
2498
2499 // Collect StageObjects group informations
2500 QStack<int> objGroupIds;
2501 QStack<std::wstring> objGroupNames;
2502 int objEditingGroup = obj->getEditingGroupId();
2503 if (obj->isGrouped()) {
2504 int i = 0;
2505 objGroupIds = obj->getGroupIdStack();
2506 objGroupNames = obj->getGroupNameStack();
2507 while (objGroupIds.empty() && objEditingGroup >= 0 &&
2508 objGroupIds[i] != objEditingGroup) {
2509 objGroupIds.remove(i);
2510 objGroupNames.remove(i);
2511 i++;
2512 }
2513 }
2514
2515 GroupData objGroupData(objGroupIds, objGroupNames, objEditingGroup);
2516
2517 // Collect column fx information
2518 /*- FxのGroupの管理 -*/
2519 TXshColumn *column = xsh->getColumn(index);
2520 TFx *columnFx = column->getFx();
2521 TFxAttributes *attr = columnFx->getAttributes();
2522
2523 // Collect Fx group informations
2524 QStack<int> fxGroupIds;
2525 QStack<std::wstring> fxGroupNames;
2526 int fxEditingGroup = attr->getEditingGroupId();
2527 if (attr->isGrouped()) {
2528 int i = 0;
2529 fxGroupIds = attr->getGroupIdStack();
2530 fxGroupNames = attr->getGroupNameStack();
2531 while (!fxGroupIds.empty() && fxEditingGroup >= 0 &&
2532 fxGroupIds[i] != fxEditingGroup) {
2533 fxGroupIds.remove(i);
2534 fxGroupNames.remove(i);
2535 i++;
2536 }
2537 }
2538
2539 GroupData fxGroupData(fxGroupIds, fxGroupNames, fxEditingGroup);
2540
2541 /*- Explode前のOutputFxのリストを取得 (oldOutFxs) -*/
2542 std::set<TOutputFx *> oldOutFxs;
2543 int i, outFxCount = xsh->getFxDag()->getOutputFxCount();
2544 for (i = 0; i < outFxCount; i++)
2545 oldOutFxs.insert(xsh->getFxDag()->getOutputFx(i));
2546
2547 std::vector<TFxPort *> outPorts;
2548
2549 QList<TStageObject *> pegObjects;
2550 QMap<TStageObjectSpline *, TStageObjectSpline *> splines;
2551
2552 TPointD fxSubPos = attr->getDagNodePos();
2553 TPointD stageSubPos = obj->getDagNodePos();
2554
2555 if (removeColumn) {
2556 /*- SubXsheetカラムノードから繋がっているFxPortのリストを取得 (outPorts) -*/
2557 for (i = 0; i < columnFx->getOutputConnectionCount(); i++)
2558 outPorts.push_back(columnFx->getOutputConnection(i));
2559
2560 bool wasLinkedToXsheet =
2561 xsh->getFxDag()->getTerminalFxs()->containsFx(columnFx);
2562
2563 // Collect data for undo
2564 std::set<int> indexes;
2565 indexes.insert(index);
2566
2567 /*- Undoのためのデータの取得 -*/
2568 StageObjectsData *oldData = new StageObjectsData();
2569 oldData->storeColumns(indexes, xsh, 0);
2570 oldData->storeColumnFxs(indexes, xsh, 0);
2571
2572 QMap<TFx *, QList<TFxPort *>> columnOutputConnections;
2573 getColumnOutputConnections(indexes, columnOutputConnections);
2574
2575 TFxSet *internals = xsh->getFxDag()->getInternalFxs();
2576 std::set<TFx *> oldInternalFxs;
2577 internals->getFxs(oldInternalFxs);
2578
2579 // Remove column
2580 xsh->removeColumn(index);
2581 // The above removing operation may decrement the parentId, in case that
2582 // 1, the parent object is column, and
2583 // 2, the parent column is placed on the right side of the removed column
2584 // ( i.e. index of the parent column is higher than "index")
2585 if (parentId.isColumn() && parentId.getIndex() > index)
2586 parentId = TStageObjectId::ColumnId(parentId.getIndex() - 1);
2587
2588 // Explode
2589 std::set<int> newIndexes =
2590 ::explode(xsh, childLevel->getXsheet(), index, parentId, objGroupData,
2591 stageSubPos, fxGroupData, fxSubPos, pegObjects, splines,
2592 outPorts, ret == 2, wasLinkedToXsheet);
2593
2594 /*- Redoのためのデータの取得 -*/
2595 StageObjectsData *newData = new StageObjectsData();
2596 newData->storeColumns(newIndexes, xsh, 0);
2597 newData->storeColumnFxs(newIndexes, xsh, 0);
2598
2599 TFx *root = 0;
2600 assert(!columnOutputConnections.empty());
2601 QList<TFxPort *> ports = columnOutputConnections.begin().value();
2602 if (!ports.empty()) root = (*ports.begin())->getFx();
2603
2604 ExplodeChildUndoRemovingColumn *undo = new ExplodeChildUndoRemovingColumn(
2605 newIndexes, index, oldData, newData, columnOutputConnections,
2606 pegObjects, splines, oldInternalFxs, oldOutFxs, root, objGroupIds,
2607 objGroupNames);
2608 TUndoManager::manager()->add(undo);
2609 } else {
2610 // keep outPorts empty since the exploded node will be re-connected to the
2611 // xsheet node
2612
2613 // Collect information for undo
2614 TCellData *cellData = new TCellData();
2615 cellData->setCells(xsh, from, index, to, index);
2616
2617 TFxSet *internals = xsh->getFxDag()->getInternalFxs();
2618 std::set<TFx *> oldInternalFxs;
2619 internals->getFxs(oldInternalFxs);
2620
2621 // Remove cells
2622 xsh->clearCells(from, index, to - from + 1);
2623
2624 // Explode
2625 std::set<int> newIndexes = ::explode(
2626 xsh, childLevel->getXsheet(), index + 1, parentId, objGroupData,
2627 stageSubPos + TPointD(10, 10), fxGroupData, fxSubPos + TPointD(10, 10),
2628 pegObjects, splines, outPorts, ret == 2, true);
2629
2630 StageObjectsData *newData = new StageObjectsData();
2631 newData->storeColumns(newIndexes, xsh, 0);
2632 newData->storeColumnFxs(newIndexes, xsh, 0);
2633
2634 ExplodeChildUndoWithoutRemovingColumn *undo =
2635 new ExplodeChildUndoWithoutRemovingColumn(
2636 newIndexes, index, from, to, cellData, newData, pegObjects, splines,
2637 oldInternalFxs, oldOutFxs, objGroupIds, objGroupNames);
2638 TUndoManager::manager()->add(undo);
2639 }
2640
2641 app->getCurrentXsheet()->notifyXsheetChanged();
2642 }
2643