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 &param = *cont->getParam(p);
938     if (TDoubleParam *dp = dynamic_cast<TDoubleParam *>(&param))
939       dp->setGrammar(grammer);
940     else if (TParamSet *paramSet = dynamic_cast<TParamSet *>(&param))
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