1
2
3 #include "toonz/fxcommand.h"
4
5 // TnzLib includes
6 #include "toonz/txsheet.h"
7 #include "toonz/tcolumnfx.h"
8 #include "toonz/fxdag.h"
9 #include "toonz/tcolumnfxset.h"
10 #include "toonz/txshzeraryfxcolumn.h"
11 #include "toonz/tstageobjecttree.h"
12 #include "toonz/txshlevelcolumn.h"
13 #include "toonz/txshpalettecolumn.h"
14 #include "toonz/toonzscene.h"
15 #include "toonz/txsheethandle.h"
16 #include "toonz/tfxhandle.h"
17 #include "toonz/tcolumnhandle.h"
18 #include "toonz/tscenehandle.h"
19 #include "historytypes.h"
20
21 // TnzBase includes
22 #include "tparamcontainer.h"
23 #include "tparamset.h"
24 #include "tfxattributes.h"
25 #include "tmacrofx.h"
26 #include "tpassivecachemanager.h"
27
28 // TnzCore includes
29 #include "tundo.h"
30 #include "tconst.h"
31
32 // Qt includes
33 #include <QMap>
34
35 // tcg includes
36 #include "tcg/tcg_macros.h"
37 #include "tcg/tcg_base.h"
38
39 #include <memory>
40
41 /*
42 Toonz currently has THREE different APIs to deal with scene objects commands:
43
44 1. From the xsheet, see columncommand.cpp
45 2. From the stage schematic
46 3. From the fx schematic
47
48 *This* is the one version that should 'rule them all' - we still have to
49 unify them, though.
50
51 TODO:
52
53 - Associated Stage Object copies when copying columns
54 - Stage schematic currently ignored (eg delete columns undo)
55 - Double-check macro fxs behavior
56 - Double-check group behavior
57 - Enforce dynamic link groups consistency
58 */
59
60 //**********************************************************************
61 // Local Namespace stuff
62 //**********************************************************************
63
64 namespace {
65
66 //======================================================
67
getActualIn(TFx * fx)68 inline TFx *getActualIn(TFx *fx) {
69 TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx);
70 return zcfx ? (assert(zcfx->getZeraryFx()), zcfx->getZeraryFx()) : fx;
71 }
72
73 //------------------------------------------------------
74
getActualOut(TFx * fx)75 inline TFx *getActualOut(TFx *fx) {
76 TZeraryFx *zfx = dynamic_cast<TZeraryFx *>(fx);
77 return (zfx && zfx->getColumnFx()) ? zfx->getColumnFx() : fx;
78 }
79
80 //------------------------------------------------------
81
inputPortIndex(TFx * fx,TFxPort * port)82 inline int inputPortIndex(TFx *fx, TFxPort *port) {
83 int p, pCount = fx->getInputPortCount();
84 for (p = 0; p != pCount; ++p)
85 if (fx->getInputPort(p) == port) break;
86 return p;
87 }
88
89 //------------------------------------------------------
90
91 /*!
92 Returns whether the specified fx is internal to a macro fx. Fxs
93 inside a macro should not be affected by most editing commands - the
94 macro is required to be exploded first.
95 */
isInsideAMacroFx(TFx * fx,TXsheet * xsh)96 bool isInsideAMacroFx(TFx *fx, TXsheet *xsh) {
97 if (!fx) return false;
98
99 TColumnFx *cfx = dynamic_cast<TColumnFx *>(fx);
100 TXsheetFx *xfx = dynamic_cast<TXsheetFx *>(fx);
101 TOutputFx *ofx = dynamic_cast<TOutputFx *>(fx);
102
103 return !cfx && !xfx && !ofx &&
104 !(xsh->getFxDag()->getInternalFxs()->containsFx(fx));
105 }
106
107 //------------------------------------------------------
108
109 template <typename ParamCont>
setParamsToCurrentScene(TXsheet * xsh,const ParamCont * cont)110 void setParamsToCurrentScene(TXsheet *xsh, const ParamCont *cont) {
111 for (int p = 0; p != cont->getParamCount(); ++p) {
112 TParam ¶m = *cont->getParam(p);
113
114 if (TDoubleParam *dp = dynamic_cast<TDoubleParam *>(¶m))
115 xsh->getStageObjectTree()->setGrammar(dp);
116 else if (TParamSet *paramSet = dynamic_cast<TParamSet *>(¶m))
117 setParamsToCurrentScene(xsh, paramSet);
118 }
119 }
120
121 //------------------------------------------------------
122
setFxParamToCurrentScene(TFx * fx,TXsheet * xsh)123 inline void setFxParamToCurrentScene(TFx *fx, TXsheet *xsh) {
124 setParamsToCurrentScene(xsh, fx->getParams());
125 }
126
127 //------------------------------------------------------
128
initializeFx(TXsheet * xsh,TFx * fx)129 void initializeFx(TXsheet *xsh, TFx *fx) {
130 if (TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx))
131 fx = zcfx->getZeraryFx();
132
133 xsh->getFxDag()->assignUniqueId(fx);
134 setFxParamToCurrentScene(fx, xsh);
135 }
136
137 //------------------------------------------------------
138
showFx(TXsheet * xsh,TFx * fx)139 void showFx(TXsheet *xsh, TFx *fx) {
140 fx->getAttributes()->setIsOpened(xsh->getFxDag()->getDagGridDimension() == 0);
141
142 if (TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx))
143 fx = zcfx->getZeraryFx();
144 fx->getAttributes()->passiveCacheDataIdx() = -1;
145 }
146
147 //------------------------------------------------------
148
hideFx(TXsheet * xsh,TFx * fx)149 void hideFx(TXsheet *xsh, TFx *fx) {
150 if (TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx))
151 fx = zcfx->getZeraryFx();
152 TPassiveCacheManager::instance()->disableCache(fx);
153 }
154
155 //------------------------------------------------------
156
addFxToCurrentScene(TFx * fx,TXsheet * xsh,bool isNewFx=true)157 void addFxToCurrentScene(TFx *fx, TXsheet *xsh, bool isNewFx = true) {
158 if (isNewFx) initializeFx(xsh, fx);
159
160 xsh->getFxDag()->getInternalFxs()->addFx(fx);
161
162 showFx(xsh, fx);
163 }
164
165 //------------------------------------------------------
166
removeFxFromCurrentScene(TFx * fx,TXsheet * xsh)167 void removeFxFromCurrentScene(TFx *fx, TXsheet *xsh) {
168 xsh->getFxDag()->getInternalFxs()->removeFx(fx);
169 xsh->getFxDag()->getTerminalFxs()->removeFx(fx);
170
171 hideFx(xsh, fx);
172 }
173
174 } // namespace
175
176 //**********************************************************************
177 // Filter Functors definition
178 //**********************************************************************
179
180 namespace {
181
182 struct FilterInsideAMacro {
183 TXsheet *m_xsh;
operator ()__anon13f77d830211::FilterInsideAMacro184 inline bool operator()(const TFxP &fx) {
185 return ::isInsideAMacroFx(fx.getPointer(), m_xsh);
186 }
187
operator ()__anon13f77d830211::FilterInsideAMacro188 inline bool operator()(const TFxCommand::Link &link) {
189 return ::isInsideAMacroFx(link.m_inputFx.getPointer(), m_xsh) ||
190 ::isInsideAMacroFx(link.m_outputFx.getPointer(), m_xsh);
191 }
192 };
193
194 struct FilterNonTerminalFxs {
195 TXsheet *xsh;
operator ()__anon13f77d830211::FilterNonTerminalFxs196 inline bool operator()(const TFxP &fx) {
197 return !xsh->getFxDag()->getTerminalFxs()->containsFx(fx.getPointer());
198 }
199 };
200
201 struct FilterTerminalFxs {
202 TXsheet *xsh;
operator ()__anon13f77d830211::FilterTerminalFxs203 inline bool operator()(const TFxP &fx) {
204 return xsh->getFxDag()->getTerminalFxs()->containsFx(fx.getPointer());
205 }
206 };
207
208 struct FilterColumnFxs {
operator ()__anon13f77d830211::FilterColumnFxs209 inline bool operator()(const TFxP &fx) {
210 return dynamic_cast<TLevelColumnFx *>(fx.getPointer());
211 }
212 };
213
214 } // namespace
215
216 //**********************************************************************
217 // CloneFxFunctor definition
218 //**********************************************************************
219
220 namespace {
221
222 struct CloneFxFunctor {
223 TFxP m_src;
224 bool m_ownsSrc;
operator ()__anon13f77d830311::CloneFxFunctor225 TFx *operator()() {
226 if (m_ownsSrc)
227 m_ownsSrc = false; // Transfer m_src and ownership if it
228 else // was surrendered
229 {
230 assert(m_src->getRefCount() >
231 1); // We'll be linking params to the cloned
232 // fx - so it MUST NOT be destroyed on release
233 TFx *src = m_src.getPointer();
234 m_src = m_src->clone(false); // Duplicate and link parameters all
235 m_src->linkParams(src); // the following times
236 }
237
238 return m_src.getPointer();
239 }
240 };
241
242 } // namespace
243
244 //**********************************************************************
245 // FxCommandUndo definition
246 //**********************************************************************
247
248 class FxCommandUndo : public TUndo {
249 public:
~FxCommandUndo()250 virtual ~FxCommandUndo() {}
251
252 virtual bool isConsistent() const = 0;
253
254 public:
255 template <typename Pred>
256 static TFx *leftmostConnectedFx(TFx *fx, Pred pred);
257 template <typename Pred>
258 static TFx *rightmostConnectedFx(TFx *fx, Pred pred);
259
260 static TFx *leftmostConnectedFx(TFx *fx);
261 static TFx *rightmostConnectedFx(TFx *fx);
262
263 static std::vector<TFxCommand::Link> inputLinks(TXsheet *xsh, TFx *fx);
264 static std::vector<TFxCommand::Link> outputLinks(TXsheet *xsh, TFx *fx);
265
getHistoryType()266 int getHistoryType() override { return HistoryType::Schematic; }
267
268 protected:
269 static TXshZeraryFxColumn *createZeraryFxColumn(TXsheet *xsh, TFx *zfx,
270 int row = 0);
271 static void cloneGroupStack(const QStack<int> &groupIds,
272 const QStack<std::wstring> &groupNames,
273 TFx *toFx);
274 static void cloneGroupStack(TFx *fromFx, TFx *toFx);
275 static void copyGroupEditLevel(int editGroupId, TFx *toFx);
276 static void copyGroupEditLevel(TFx *fromFx, TFx *toFx);
277 static void copyDagPosition(TFx *fromFx, TFx *toFx);
278 static void attach(TXsheet *xsh, TFx *inputFx, TFx *outputFx, int port,
279 bool copyGroupData);
280 static void attach(TXsheet *xsh, const TFxCommand::Link &link,
281 bool copyGroupData);
282 static void attachOutputs(TXsheet *xsh, TFx *insertedFx, TFx *inputFx);
283 static void detachFxs(TXsheet *xsh, TFx *fxLeft, TFx *fxRight,
284 bool detachLeft = true);
285 static void insertFxs(TXsheet *xsh, const TFxCommand::Link &link, TFx *fxLeft,
286 TFx *fxRight);
287 static void insertColumn(TXsheet *xsh, TXshColumn *column, int colIdx,
288 bool removeHole = false, bool autoTerminal = false);
289 static void removeFxOrColumn(TXsheet *xsh, TFx *fx, int colIdx,
290 bool insertHole = false,
291 bool unlinkParams = true);
292 static void linkParams(TFx *fx, TFx *linkedFx);
293 static void unlinkParams(TFx *fx);
294 static void makeNotCurrent(TFxHandle *fxHandle, TFx *fx);
295
296 private:
297 static void removeColumn(TXsheet *xsh, int colIdx, bool insertHole);
298 static void removeNormalFx(TXsheet *xsh, TFx *fx);
299 static void removeOutputFx(TXsheet *xsh, TOutputFx *outputFx);
300 };
301
302 //------------------------------------------------------
303
createZeraryFxColumn(TXsheet * xsh,TFx * zfx,int row)304 TXshZeraryFxColumn *FxCommandUndo::createZeraryFxColumn(TXsheet *xsh, TFx *zfx,
305 int row) {
306 int frameCount = xsh->getScene()->getFrameCount() - row;
307
308 TXshZeraryFxColumn *column =
309 new TXshZeraryFxColumn(frameCount > 0 ? frameCount : 100);
310 column->getZeraryColumnFx()->setZeraryFx(zfx);
311 column->insertEmptyCells(0, row);
312
313 return column;
314 }
315
316 //------------------------------------------------------
317
cloneGroupStack(const QStack<int> & groupIds,const QStack<std::wstring> & groupNames,TFx * toFx)318 void FxCommandUndo::cloneGroupStack(const QStack<int> &groupIds,
319 const QStack<std::wstring> &groupNames,
320 TFx *toFx) {
321 toFx->getAttributes()->removeFromAllGroup();
322
323 for (int i = 0; i < groupIds.size(); ++i) {
324 toFx->getAttributes()->setGroupId(groupIds[i]);
325 toFx->getAttributes()->setGroupName(groupNames[i]);
326 }
327 }
328
329 //------------------------------------------------------
330
cloneGroupStack(TFx * fromFx,TFx * toFx)331 void FxCommandUndo::cloneGroupStack(TFx *fromFx, TFx *toFx) {
332 if (fromFx->getAttributes()->isGrouped()) {
333 cloneGroupStack(fromFx->getAttributes()->getGroupIdStack(),
334 fromFx->getAttributes()->getGroupNameStack(), toFx);
335 }
336 }
337
338 //------------------------------------------------------
339
copyGroupEditLevel(int editGroupId,TFx * toFx)340 void FxCommandUndo::copyGroupEditLevel(int editGroupId, TFx *toFx) {
341 toFx->getAttributes()->closeAllGroups();
342 while (editGroupId != toFx->getAttributes()->getEditingGroupId() &&
343 toFx->getAttributes()->editGroup())
344 ;
345
346 assert(editGroupId == toFx->getAttributes()->getEditingGroupId());
347 }
348
349 //------------------------------------------------------
350
copyGroupEditLevel(TFx * fromFx,TFx * toFx)351 void FxCommandUndo::copyGroupEditLevel(TFx *fromFx, TFx *toFx) {
352 assert(toFx);
353 if (fromFx && fromFx->getAttributes()->isGrouped())
354 copyGroupEditLevel(fromFx->getAttributes()->getEditingGroupId(), toFx);
355 }
356
357 //------------------------------------------------------
358
copyDagPosition(TFx * fromFx,TFx * toFx)359 void FxCommandUndo::copyDagPosition(TFx *fromFx, TFx *toFx) {
360 assert(toFx);
361 if (fromFx)
362 toFx->getAttributes()->setDagNodePos(
363 fromFx->getAttributes()->getDagNodePos());
364 }
365
366 //------------------------------------------------------
367
attach(TXsheet * xsh,TFx * inputFx,TFx * outputFx,int link,bool copyGroupData)368 void FxCommandUndo::attach(TXsheet *xsh, TFx *inputFx, TFx *outputFx, int link,
369 bool copyGroupData) {
370 if (outputFx) {
371 FxDag *fxDag = xsh->getFxDag();
372
373 inputFx = ::getActualOut(inputFx);
374 outputFx = ::getActualIn(outputFx);
375
376 if (inputFx && link < 0) {
377 assert(dynamic_cast<TXsheetFx *>(outputFx));
378 fxDag->addToXsheet(inputFx);
379 } else {
380 int ipCount = outputFx->getInputPortCount();
381 if (ipCount > 0 && link < ipCount)
382 outputFx->getInputPort(link)->setFx(inputFx);
383
384 if (copyGroupData) copyGroupEditLevel(inputFx, outputFx);
385 }
386 }
387 }
388
389 //------------------------------------------------------
390
attach(TXsheet * xsh,const TFxCommand::Link & link,bool copyGroupData)391 void FxCommandUndo::attach(TXsheet *xsh, const TFxCommand::Link &link,
392 bool copyGroupData) {
393 attach(xsh, link.m_inputFx.getPointer(), link.m_outputFx.getPointer(),
394 link.m_index, copyGroupData);
395 }
396
397 //------------------------------------------------------
398
attachOutputs(TXsheet * xsh,TFx * insertedFx,TFx * inputFx)399 void FxCommandUndo::attachOutputs(TXsheet *xsh, TFx *insertedFx, TFx *inputFx) {
400 TCG_ASSERT(inputFx, return );
401
402 FxDag *fxDag = xsh->getFxDag();
403
404 insertedFx = ::getActualOut(insertedFx);
405 inputFx = ::getActualOut(inputFx);
406
407 int p, pCount = inputFx->getOutputConnectionCount();
408 for (p = pCount - 1; p >= 0;
409 --p) // Backward iteration on output connections -
410 { // it's necessary since TFxPort::setFx() REMOVES
411 TFxPort *port = inputFx->getOutputConnection(
412 p); // the corresponding port int the output connections
413 port->setFx(
414 insertedFx); // container - thus, it's better to start from the end
415 }
416
417 if (fxDag->getTerminalFxs()->containsFx(inputFx)) {
418 fxDag->removeFromXsheet(inputFx);
419 fxDag->addToXsheet(insertedFx);
420 }
421 }
422
423 //------------------------------------------------------
424
detachFxs(TXsheet * xsh,TFx * fxLeft,TFx * fxRight,bool detachLeft)425 void FxCommandUndo::detachFxs(TXsheet *xsh, TFx *fxLeft, TFx *fxRight,
426 bool detachLeft) {
427 assert(fxLeft && fxRight);
428
429 fxLeft = ::getActualIn(fxLeft);
430 fxRight = ::getActualOut(fxRight);
431
432 int ipCount = fxLeft->getInputPortCount();
433
434 // Redirect input/output ports
435 TFx *inputFx0 = (ipCount > 0) ? fxLeft->getInputPort(0)->getFx() : 0;
436
437 int p, opCount = fxRight->getOutputConnectionCount();
438 for (p = opCount - 1; p >= 0;
439 --p) // Backward iteration due to TFxPort::setFx()
440 {
441 TFxPort *outPort = fxRight->getOutputConnection(p);
442 assert(outPort && outPort->getFx() == fxRight);
443
444 outPort->setFx(inputFx0);
445 }
446
447 // Xsheet links redirection
448 FxDag *fxDag = xsh->getFxDag();
449 if (fxDag->getTerminalFxs()->containsFx(fxRight)) {
450 fxDag->removeFromXsheet(fxRight);
451
452 for (int p = 0; p != ipCount; ++p)
453 if (TFx *inputFx = fxLeft->getInputPort(p)->getFx())
454 fxDag->addToXsheet(inputFx);
455 }
456
457 if (detachLeft) fxLeft->disconnectAll();
458 }
459
460 //------------------------------------------------------
461
insertFxs(TXsheet * xsh,const TFxCommand::Link & link,TFx * fxLeft,TFx * fxRight)462 void FxCommandUndo::insertFxs(TXsheet *xsh, const TFxCommand::Link &link,
463 TFx *fxLeft, TFx *fxRight) {
464 assert(fxLeft && fxRight);
465
466 if (link.m_inputFx && link.m_outputFx) {
467 FxCommandUndo::attach(xsh, link.m_inputFx.getPointer(), fxLeft, 0, false);
468 FxCommandUndo::attach(xsh, fxRight, link.m_outputFx.getPointer(),
469 link.m_index, false);
470
471 if (link.m_index < 0)
472 xsh->getFxDag()->removeFromXsheet(
473 ::getActualOut(link.m_inputFx.getPointer()));
474 }
475 }
476
477 //------------------------------------------------------
478
insertColumn(TXsheet * xsh,TXshColumn * column,int col,bool removeHole,bool autoTerminal)479 void FxCommandUndo::insertColumn(TXsheet *xsh, TXshColumn *column, int col,
480 bool removeHole, bool autoTerminal) {
481 FxDag *fxDag = xsh->getFxDag();
482 TFx *fx = column->getFx();
483 bool terminal = false;
484
485 if (fx) {
486 ::showFx(xsh, fx);
487 terminal = fxDag->getTerminalFxs()->containsFx(fx);
488 }
489
490 if (removeHole) xsh->removeColumn(col);
491
492 xsh->insertColumn(col, column); // Attaches the fx to the xsheet, too -
493 // but not if the column is a palette one.
494 if (!autoTerminal) {
495 // Preserve the initial terminal state.
496 // This lets fxs to be linked to the xsheet while still hidden.
497
498 fxDag->removeFromXsheet(fx);
499 if (terminal) fxDag->addToXsheet(fx);
500 }
501
502 xsh->updateFrameCount();
503 }
504
505 //------------------------------------------------------
506
removeColumn(TXsheet * xsh,int col,bool insertHole)507 void FxCommandUndo::removeColumn(TXsheet *xsh, int col, bool insertHole) {
508 if (TFx *colFx = xsh->getColumn(col)->getFx()) {
509 detachFxs(xsh, colFx, colFx);
510 ::hideFx(xsh, colFx);
511 }
512
513 xsh->removeColumn(col); // Already detaches any fx in output,
514 if (insertHole) // including the terminal case
515 xsh->insertColumn(col); // Note that fxs in output are not
516 // removed - just detached.
517 xsh->updateFrameCount();
518 }
519
520 //------------------------------------------------------
521
removeNormalFx(TXsheet * xsh,TFx * fx)522 void FxCommandUndo::removeNormalFx(TXsheet *xsh, TFx *fx) {
523 detachFxs(xsh, fx, fx);
524 ::removeFxFromCurrentScene(fx, xsh); // Already hideFx()s
525 }
526
527 //------------------------------------------------------
528
removeOutputFx(TXsheet * xsh,TOutputFx * outputFx)529 void FxCommandUndo::removeOutputFx(TXsheet *xsh, TOutputFx *outputFx) {
530 detachFxs(xsh, outputFx, outputFx);
531 xsh->getFxDag()->removeOutputFx(outputFx);
532 }
533
534 //------------------------------------------------------
535
linkParams(TFx * fx,TFx * linkedFx)536 void FxCommandUndo::linkParams(TFx *fx, TFx *linkedFx) {
537 if (linkedFx) ::getActualIn(fx)->linkParams(::getActualIn(linkedFx));
538 }
539
540 //------------------------------------------------------
541
unlinkParams(TFx * fx)542 void FxCommandUndo::unlinkParams(TFx *fx) {
543 if (fx = ::getActualIn(fx), fx->getLinkedFx()) fx->unlinkParams();
544 }
545
546 //------------------------------------------------------
547
makeNotCurrent(TFxHandle * fxHandle,TFx * fx)548 void FxCommandUndo::makeNotCurrent(TFxHandle *fxHandle, TFx *fx) {
549 if (fx = ::getActualOut(fx), fx == fxHandle->getFx()) fxHandle->setFx(0);
550 }
551
552 //------------------------------------------------------
553
removeFxOrColumn(TXsheet * xsh,TFx * fx,int colIdx,bool insertHole,bool unlinkParams)554 void FxCommandUndo::removeFxOrColumn(TXsheet *xsh, TFx *fx, int colIdx,
555 bool insertHole, bool unlinkParams) {
556 assert(fx || colIdx >= 0);
557
558 if (!fx)
559 fx = xsh->getColumn(colIdx)->getFx();
560 else if (TColumnFx *colFx = dynamic_cast<TColumnFx *>(fx))
561 colIdx = colFx->getColumnIndex();
562 else if (TZeraryFx *zfx = dynamic_cast<TZeraryFx *>(fx)) {
563 if (zfx->getColumnFx())
564 fx = zfx->getColumnFx(),
565 colIdx = static_cast<TColumnFx *>(fx)->getColumnIndex();
566 }
567
568 if (fx) {
569 // Discriminate special fx types
570 if (TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx)) {
571 // Removed as a column
572 fx = zcfx->getZeraryFx();
573 } else if (TOutputFx *outputFx = dynamic_cast<TOutputFx *>(fx)) {
574 assert(xsh->getFxDag()->getOutputFxCount() > 1);
575 FxCommandUndo::removeOutputFx(xsh, outputFx);
576 } else if (colIdx < 0)
577 FxCommandUndo::removeNormalFx(xsh, fx);
578
579 if (unlinkParams) FxCommandUndo::unlinkParams(fx);
580 }
581
582 if (colIdx >= 0) FxCommandUndo::removeColumn(xsh, colIdx, insertHole);
583 }
584
585 //------------------------------------------------------
586
587 template <typename Pred>
leftmostConnectedFx(TFx * fx,Pred pred)588 TFx *FxCommandUndo::leftmostConnectedFx(TFx *fx, Pred pred) {
589 assert(fx);
590
591 fx = rightmostConnectedFx(fx, pred); // The rightmost fx should be discovered
592 // first, then, we'll descend from that
593 do {
594 fx = ::getActualIn(fx);
595
596 if (!((fx->getInputPortCount() > 0) && fx->getInputPort(0)->isConnected() &&
597 pred(fx->getInputPort(0)->getFx())))
598 break;
599
600 fx = fx->getInputPort(0)->getFx();
601 } while (true);
602
603 return fx;
604 }
605
606 //------------------------------------------------------
607
608 template <typename Pred>
rightmostConnectedFx(TFx * fx,Pred pred)609 TFx *FxCommandUndo::rightmostConnectedFx(TFx *fx, Pred pred) {
610 assert(fx);
611
612 do {
613 fx = ::getActualOut(fx);
614
615 if (!(fx->getOutputConnectionCount() > 0 &&
616 pred(fx->getOutputConnection(0)->getOwnerFx())))
617 break;
618
619 fx = fx->getOutputConnection(0)->getOwnerFx();
620 } while (true);
621
622 return fx;
623 }
624
625 //------------------------------------------------------
626
627 namespace {
628 struct True_pred {
operator ()__anon13f77d830411::True_pred629 bool operator()(TFx *fx) { return true; }
630 };
631 } // namespace
632
leftmostConnectedFx(TFx * fx)633 TFx *FxCommandUndo::leftmostConnectedFx(TFx *fx) {
634 return leftmostConnectedFx(fx, ::True_pred());
635 }
636
637 //------------------------------------------------------
638
rightmostConnectedFx(TFx * fx)639 TFx *FxCommandUndo::rightmostConnectedFx(TFx *fx) {
640 return rightmostConnectedFx(fx, ::True_pred());
641 }
642
643 //------------------------------------------------------
644
inputLinks(TXsheet * xsh,TFx * fx)645 std::vector<TFxCommand::Link> FxCommandUndo::inputLinks(TXsheet *xsh, TFx *fx) {
646 std::vector<TFxCommand::Link> result;
647
648 fx = ::getActualIn(fx);
649
650 int il, ilCount = fx->getInputPortCount();
651 for (il = 0; il != ilCount; ++il) {
652 TFxPort *port = fx->getInputPort(il);
653
654 assert(port);
655 if (port->isConnected())
656 result.push_back(TFxCommand::Link(port->getFx(), fx, il));
657 }
658
659 return result;
660 }
661
662 //------------------------------------------------------
663
outputLinks(TXsheet * xsh,TFx * fx)664 std::vector<TFxCommand::Link> FxCommandUndo::outputLinks(TXsheet *xsh,
665 TFx *fx) {
666 std::vector<TFxCommand::Link> result;
667
668 fx = ::getActualOut(fx);
669
670 int ol, olCount = fx->getOutputConnectionCount();
671 for (ol = 0; ol != olCount; ++ol) {
672 TFxPort *port = fx->getOutputConnection(ol);
673 TFx *ownerFx = port->getOwnerFx();
674 int portIndex = ::inputPortIndex(ownerFx, port);
675
676 result.push_back(TFxCommand::Link(fx, ownerFx, portIndex));
677 }
678
679 FxDag *fxDag = xsh->getFxDag();
680 if (fxDag->getTerminalFxs()->containsFx(fx))
681 result.push_back(TFxCommand::Link(fx, fxDag->getXsheetFx(), -1));
682
683 return result;
684 }
685
686 //**********************************************************************
687 // Insert Fx command
688 //**********************************************************************
689
690 class InsertFxUndo final : public FxCommandUndo {
691 QList<TFxP> m_selectedFxs;
692 QList<TFxCommand::Link> m_selectedLinks;
693
694 TApplication *m_app;
695
696 QList<TFxP> m_insertedFxs;
697 TXshZeraryFxColumnP m_insertedColumn;
698 int m_colIdx;
699 bool m_columnReplacesHole;
700 bool m_attachOutputs;
701
702 public:
InsertFxUndo(const TFxP & fx,int row,int col,const QList<TFxP> & selectedFxs,QList<TFxCommand::Link> selectedLinks,TApplication * app,bool attachOutputs=true)703 InsertFxUndo(const TFxP &fx, int row, int col, const QList<TFxP> &selectedFxs,
704 QList<TFxCommand::Link> selectedLinks, TApplication *app,
705 bool attachOutputs = true)
706 : m_selectedFxs(selectedFxs)
707 , m_selectedLinks(selectedLinks)
708 , m_insertedColumn(0)
709 , m_app(app)
710 , m_colIdx(col)
711 , m_columnReplacesHole(false)
712 , m_attachOutputs(attachOutputs) {
713 initialize(fx, row, col);
714 }
715
isConsistent() const716 bool isConsistent() const override { return !m_insertedFxs.isEmpty(); }
717
718 void redo() const override;
719 void undo() const override;
720
getSize() const721 int getSize() const override { return sizeof(*this); }
722
723 QString getHistoryString() override;
724
725 private:
726 void initialize(const TFxP &newFx, int row, int col);
727 };
728
729 //------------------------------------------------------
730
has_fx_column(TFx * fx)731 inline bool has_fx_column(TFx *fx) {
732 if (TPluginInterface *plgif = dynamic_cast<TPluginInterface *>(fx))
733 return plgif->isPluginZerary();
734 else if (TZeraryFx *zfx = dynamic_cast<TZeraryFx *>(fx))
735 return zfx->isZerary();
736 return false;
737 }
738
739 namespace {
740
containsInputFx(const QList<TFxP> & fxs,const TFxCommand::Link & link)741 bool containsInputFx(const QList<TFxP> &fxs, const TFxCommand::Link &link) {
742 return fxs.contains(link.m_inputFx);
743 }
744
745 } // namespace
746
initialize(const TFxP & newFx,int row,int col)747 void InsertFxUndo::initialize(const TFxP &newFx, int row, int col) {
748 struct Locals {
749 InsertFxUndo *m_this;
750 inline void storeFx(TXsheet *xsh, TFx *fx) {
751 ::initializeFx(xsh, fx);
752 m_this->m_insertedFxs.push_back(fx);
753 }
754
755 } locals = {this};
756
757 TXsheet *xsh = m_app->getCurrentXsheet()->getXsheet();
758 TFx *fx = newFx.getPointer();
759
760 assert(!dynamic_cast<TZeraryColumnFx *>(fx));
761
762 TZeraryFx *zfx = dynamic_cast<TZeraryFx *>(fx);
763 if (has_fx_column(fx)) {
764 m_insertedColumn = InsertFxUndo::createZeraryFxColumn(xsh, fx, row);
765
766 locals.storeFx(xsh, fx);
767
768 if (xsh->getColumn(col) && xsh->getColumn(col)->isEmpty())
769 m_columnReplacesHole = true;
770 } else {
771 if (m_selectedFxs.isEmpty() && m_selectedLinks.isEmpty()) {
772 // Attempt retrieval of current Fx from the fxHandle
773 if (TFx *currentFx = m_app->getCurrentFx()->getFx())
774 m_selectedFxs.push_back(currentFx);
775 else {
776 // Isolated case
777 locals.storeFx(xsh, fx);
778 return;
779 }
780 }
781
782 // Remove all unacceptable input fxs
783 ::FilterInsideAMacro filterInMacroFxs = {xsh};
784 m_selectedFxs.erase(std::remove_if(m_selectedFxs.begin(),
785 m_selectedFxs.end(), filterInMacroFxs),
786 m_selectedFxs.end());
787
788 // Remove all unacceptable links or links whose input fx was already
789 // selected
790 m_selectedLinks.erase(
791 std::remove_if(m_selectedLinks.begin(), m_selectedLinks.end(),
792 filterInMacroFxs),
793 m_selectedLinks.end());
794 m_selectedLinks.erase(
795 std::remove_if(m_selectedLinks.begin(), m_selectedLinks.end(),
796 [this](const TFxCommand::Link &link) {
797 return containsInputFx(m_selectedFxs, link);
798 }),
799 m_selectedLinks.end());
800
801 // Build an fx for each of the specified inputs
802 ::CloneFxFunctor cloneFx = {fx, true};
803
804 int f, fCount = m_selectedFxs.size();
805 for (f = 0; f != fCount; ++f) {
806 TFx *fx = cloneFx();
807 FxCommandUndo::cloneGroupStack(m_selectedFxs[f].getPointer(), fx);
808 locals.storeFx(xsh, fx);
809 }
810
811 fCount = m_selectedLinks.size();
812 for (f = 0; f != fCount; ++f) {
813 TFx *fx = cloneFx();
814 FxCommandUndo::cloneGroupStack(m_selectedLinks[f].m_inputFx.getPointer(),
815 fx);
816 locals.storeFx(xsh, fx);
817 }
818 }
819 }
820
821 //------------------------------------------------------
822
redo() const823 void InsertFxUndo::redo() const {
824 struct OnExit {
825 const InsertFxUndo *m_this;
826 ~OnExit() {
827 m_this->m_app->getCurrentFx()->setFx(
828 m_this->m_insertedFxs.back().getPointer());
829 m_this->m_app->getCurrentXsheet()->notifyXsheetChanged();
830 m_this->m_app->getCurrentScene()->setDirtyFlag(true);
831 }
832 } onExit = {this};
833
834 TXsheet *xsh = m_app->getCurrentXsheet()->getXsheet();
835
836 // Zerary case
837 if (m_insertedColumn) {
838 FxCommandUndo::insertColumn(xsh, m_insertedColumn.getPointer(), m_colIdx,
839 m_columnReplacesHole, true);
840 return;
841 }
842
843 // Isolated Fx case
844 if (m_selectedLinks.isEmpty() && m_selectedFxs.isEmpty()) {
845 assert(m_insertedFxs.size() == 1);
846 ::addFxToCurrentScene(m_insertedFxs.back().getPointer(), xsh,
847 false); // Already showFx()s
848 } else {
849 // Selected links
850 int i;
851 for (i = 0; i < m_selectedLinks.size(); ++i) {
852 const TFxCommand::Link &link = m_selectedLinks[i];
853 TFx *insertedFx = m_insertedFxs[i].getPointer();
854
855 ::addFxToCurrentScene(insertedFx, xsh, false);
856 FxCommandUndo::insertFxs(xsh, link, insertedFx, insertedFx);
857 FxCommandUndo::copyGroupEditLevel(link.m_inputFx.getPointer(),
858 insertedFx);
859 }
860
861 // Selected fxs
862 int j, t;
863 for (j = 0, t = 0; j < m_selectedFxs.size(); j++) {
864 TFx *fx = m_selectedFxs[j].getPointer();
865 assert(fx);
866
867 TFx *insertedFx = m_insertedFxs[i + t].getPointer();
868 t++;
869
870 assert(insertedFx);
871 ::addFxToCurrentScene(insertedFx, xsh, false);
872
873 if (m_attachOutputs) FxCommandUndo::attachOutputs(xsh, insertedFx, fx);
874
875 FxCommandUndo::attach(xsh, fx, insertedFx, 0, true);
876 }
877 }
878 }
879
880 //------------------------------------------------------
881
undo() const882 void InsertFxUndo::undo() const {
883 TXsheet *xsh = m_app->getCurrentXsheet()->getXsheet();
884
885 int i, iCount = m_insertedFxs.size();
886 for (i = 0; i != iCount; ++i) {
887 TFx *insertedFx = m_insertedFxs[i].getPointer();
888
889 FxCommandUndo::removeFxOrColumn(xsh, insertedFx, -1, m_columnReplacesHole,
890 false); // Skip parameter links removal
891 FxCommandUndo::makeNotCurrent(m_app->getCurrentFx(), insertedFx);
892 }
893
894 m_app->getCurrentFx()->setFx(0);
895 m_app->getCurrentXsheet()->notifyXsheetChanged();
896 m_app->getCurrentScene()->setDirtyFlag(true);
897 }
898
899 //------------------------------------------------------
900
getHistoryString()901 QString InsertFxUndo::getHistoryString() {
902 QString str = (m_selectedLinks.isEmpty()) ? QObject::tr("Add Fx : ")
903 : QObject::tr("Insert Fx : ");
904 QList<TFxP>::iterator it;
905 for (it = m_insertedFxs.begin(); it != m_insertedFxs.end(); it++) {
906 if (it != m_insertedFxs.begin()) str += QString(", ");
907
908 str += QString::fromStdWString((*it)->getFxId());
909 }
910 return str;
911 }
912
913 //=============================================================
914
insertFx(TFx * newFx,const QList<TFxP> & fxs,const QList<Link> & links,TApplication * app,int col,int row)915 void TFxCommand::insertFx(TFx *newFx, const QList<TFxP> &fxs,
916 const QList<Link> &links, TApplication *app, int col,
917 int row) {
918 if (!newFx) return;
919
920 if (col < 0)
921 col = 0; // Normally insert before. In case of camera, insert after
922
923 std::unique_ptr<FxCommandUndo> undo(
924 new InsertFxUndo(newFx, row, col, fxs, links, app));
925 if (undo->isConsistent()) {
926 undo->redo();
927 TUndoManager::manager()->add(undo.release());
928 }
929 }
930
931 //**********************************************************************
932 // Add Fx command
933 //**********************************************************************
934
addFx(TFx * newFx,const QList<TFxP> & fxs,TApplication * app,int col,int row)935 void TFxCommand::addFx(TFx *newFx, const QList<TFxP> &fxs, TApplication *app,
936 int col, int row) {
937 if (!newFx) return;
938
939 std::unique_ptr<FxCommandUndo> undo(
940 new InsertFxUndo(newFx, row, col, fxs, QList<Link>(), app, false));
941 if (!undo->isConsistent()) return;
942
943 undo->redo();
944 TUndoManager::manager()->add(undo.release());
945 }
946
947 //**********************************************************************
948 // Duplicate Fx command
949 //**********************************************************************
950
951 class DuplicateFxUndo final : public FxCommandUndo {
952 TFxP m_fx, m_dupFx;
953 TXshColumnP m_column;
954 int m_colIdx;
955
956 TXsheetHandle *m_xshHandle;
957 TFxHandle *m_fxHandle;
958
959 public:
DuplicateFxUndo(const TFxP & originalFx,TXsheetHandle * xshHandle,TFxHandle * fxHandle)960 DuplicateFxUndo(const TFxP &originalFx, TXsheetHandle *xshHandle,
961 TFxHandle *fxHandle)
962 : m_fx(originalFx)
963 , m_colIdx(-1)
964 , m_xshHandle(xshHandle)
965 , m_fxHandle(fxHandle) {
966 initialize();
967 }
968
isConsistent() const969 bool isConsistent() const override { return bool(m_dupFx); }
970
971 void redo() const override;
972 void undo() const override;
973
getSize() const974 int getSize() const override { return sizeof(*this); }
975
976 QString getHistoryString() override;
977
978 private:
979 void initialize();
980 };
981
982 //-------------------------------------------------------------
983
initialize()984 void DuplicateFxUndo::initialize() {
985 TXsheet *xsh = m_xshHandle->getXsheet();
986 TFx *fx = m_fx.getPointer();
987
988 fx = ::getActualOut(fx);
989
990 if (isInsideAMacroFx(fx, xsh) || dynamic_cast<TXsheetFx *>(fx) ||
991 dynamic_cast<TOutputFx *>(fx) ||
992 (dynamic_cast<TColumnFx *>(fx) && !dynamic_cast<TZeraryColumnFx *>(fx)))
993 return;
994
995 if (TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx)) {
996 m_column = new TXshZeraryFxColumn(*zcfx->getColumn());
997 m_colIdx = xsh->getFirstFreeColumnIndex();
998
999 TZeraryColumnFx *dupZcfx =
1000 static_cast<TZeraryColumnFx *>(m_column->getFx());
1001 ::initializeFx(xsh, dupZcfx->getZeraryFx());
1002
1003 FxCommandUndo::cloneGroupStack(zcfx, dupZcfx);
1004
1005 m_dupFx = dupZcfx;
1006 } else {
1007 fx = fx->clone(false);
1008 ::initializeFx(xsh, fx);
1009
1010 FxCommandUndo::cloneGroupStack(m_fx.getPointer(), fx);
1011
1012 m_dupFx = fx;
1013 }
1014 }
1015
1016 //-------------------------------------------------------------
1017
redo() const1018 void DuplicateFxUndo::redo() const {
1019 TXsheet *xsh = m_xshHandle->getXsheet();
1020
1021 if (m_column) {
1022 // Zerary Fx case
1023 TZeraryColumnFx *zcfx = static_cast<TZeraryColumnFx *>(m_fx.getPointer());
1024 TZeraryColumnFx *dupZcfx =
1025 static_cast<TZeraryColumnFx *>(m_dupFx.getPointer());
1026
1027 FxCommandUndo::insertColumn(xsh, m_column.getPointer(), m_colIdx, true,
1028 true);
1029 FxCommandUndo::copyGroupEditLevel(zcfx, dupZcfx);
1030
1031 dupZcfx->getZeraryFx()->linkParams(zcfx->getZeraryFx());
1032 } else {
1033 // Normal Fx case
1034 addFxToCurrentScene(m_dupFx.getPointer(), m_xshHandle->getXsheet(), false);
1035 FxCommandUndo::copyGroupEditLevel(m_fx.getPointer(), m_dupFx.getPointer());
1036
1037 m_dupFx->linkParams(m_fx.getPointer());
1038 }
1039
1040 m_fxHandle->setFx(m_dupFx.getPointer());
1041 m_xshHandle->notifyXsheetChanged();
1042 }
1043
1044 //-------------------------------------------------------------
1045
undo() const1046 void DuplicateFxUndo::undo() const {
1047 FxCommandUndo::removeFxOrColumn(m_xshHandle->getXsheet(),
1048 m_dupFx.getPointer(), -1, true, true);
1049
1050 m_fxHandle->setFx(0);
1051 m_xshHandle->notifyXsheetChanged();
1052 }
1053
1054 //-------------------------------------------------------------
1055
getHistoryString()1056 QString DuplicateFxUndo::getHistoryString() {
1057 if (TZeraryColumnFx *zDup =
1058 dynamic_cast<TZeraryColumnFx *>(m_dupFx.getPointer()))
1059 return QObject::tr("Create Linked Fx : %1")
1060 .arg(QString::fromStdWString(zDup->getZeraryFx()->getFxId()));
1061
1062 return QObject::tr("Create Linked Fx : %1")
1063 .arg(QString::fromStdWString(m_dupFx->getFxId()));
1064 }
1065
1066 //=============================================================
1067
duplicateFx(TFx * src,TXsheetHandle * xshHandle,TFxHandle * fxHandle)1068 void TFxCommand::duplicateFx(TFx *src, TXsheetHandle *xshHandle,
1069 TFxHandle *fxHandle) {
1070 std::unique_ptr<FxCommandUndo> undo(
1071 new DuplicateFxUndo(src, xshHandle, fxHandle));
1072 if (undo->isConsistent()) {
1073 undo->redo();
1074 TUndoManager::manager()->add(undo.release());
1075 }
1076 }
1077
1078 //**********************************************************************
1079 // Replace Fx command
1080 //**********************************************************************
1081
1082 class ReplaceFxUndo final : public FxCommandUndo {
1083 TFxP m_fx, m_repFx, m_linkedFx;
1084 TXshColumnP m_column, m_repColumn;
1085 int m_colIdx, m_repColIdx;
1086
1087 std::vector<std::pair<int, TFx *>> m_inputLinks;
1088
1089 TXsheetHandle *m_xshHandle;
1090 TFxHandle *m_fxHandle;
1091
1092 public:
ReplaceFxUndo(const TFxP & replacementFx,const TFxP & replacedFx,TXsheetHandle * xshHandle,TFxHandle * fxHandle)1093 ReplaceFxUndo(const TFxP &replacementFx, const TFxP &replacedFx,
1094 TXsheetHandle *xshHandle, TFxHandle *fxHandle)
1095 : m_fx(replacedFx)
1096 , m_repFx(replacementFx)
1097 , m_xshHandle(xshHandle)
1098 , m_fxHandle(fxHandle)
1099 , m_colIdx(-1)
1100 , m_repColIdx(-1) {
1101 initialize();
1102 }
1103
isConsistent() const1104 bool isConsistent() const override { return bool(m_repFx); }
1105
1106 void redo() const override;
1107 void undo() const override;
1108
getSize() const1109 int getSize() const override { return sizeof(*this); }
1110
1111 QString getHistoryString() override;
1112
1113 private:
1114 void initialize();
1115 static void replace(TXsheet *xsh, TFx *fx, TFx *repFx, TXshColumn *column,
1116 TXshColumn *repColumn, int colIdx, int repColIdx);
1117 };
1118
1119 //-------------------------------------------------------------
1120
initialize()1121 void ReplaceFxUndo::initialize() {
1122 TXsheet *xsh = m_xshHandle->getXsheet();
1123
1124 TFx *fx = m_fx.getPointer();
1125 TFx *repFx = m_repFx.getPointer();
1126
1127 fx = ::getActualOut(fx);
1128
1129 if (isInsideAMacroFx(fx, xsh) || dynamic_cast<TXsheetFx *>(fx) ||
1130 dynamic_cast<TOutputFx *>(fx) ||
1131 (dynamic_cast<TColumnFx *>(fx) && !dynamic_cast<TZeraryColumnFx *>(fx))) {
1132 m_repFx = TFxP();
1133 return;
1134 }
1135
1136 if (dynamic_cast<TXsheetFx *>(repFx) || dynamic_cast<TOutputFx *>(repFx) ||
1137 dynamic_cast<TColumnFx *>(repFx)) {
1138 m_repFx = TFxP();
1139 return;
1140 }
1141
1142 ::initializeFx(xsh, repFx);
1143
1144 TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx);
1145 if (zcfx) {
1146 TXshZeraryFxColumn *zfColumn = zcfx->getColumn();
1147
1148 m_column = zfColumn;
1149 m_colIdx = zfColumn->getIndex();
1150
1151 fx = zcfx->getZeraryFx();
1152 }
1153
1154 TZeraryColumnFx *zcrepfx = dynamic_cast<TZeraryColumnFx *>(repFx);
1155 if (zcrepfx) repFx = zcrepfx->getZeraryFx();
1156
1157 bool fxHasCol = has_fx_column(fx);
1158 bool repfxHasCol = has_fx_column(repFx);
1159
1160 if (fxHasCol && repfxHasCol) {
1161 if (zcfx) {
1162 // Build a column with the same source cells pattern
1163 m_repColumn = new TXshZeraryFxColumn(*zcfx->getColumn());
1164 m_repColIdx = m_colIdx;
1165
1166 // Substitute the column's zerary fx with the substitute one
1167 TZeraryColumnFx *repZcfx =
1168 static_cast<TZeraryColumnFx *>(m_repColumn->getFx());
1169 repZcfx->setZeraryFx(repFx);
1170
1171 FxCommandUndo::cloneGroupStack(zcfx, repZcfx);
1172 m_repFx = repZcfx;
1173 } else {
1174 m_repColumn = FxCommandUndo::createZeraryFxColumn(xsh, repFx);
1175 m_repColIdx = xsh->getFirstFreeColumnIndex();
1176 m_repFx = static_cast<TZeraryColumnFx *>(m_repColumn->getFx());
1177 }
1178 } else if (!fxHasCol && repfxHasCol) {
1179 m_repColumn = FxCommandUndo::createZeraryFxColumn(xsh, repFx);
1180 m_repColIdx = xsh->getFirstFreeColumnIndex();
1181 m_repFx = static_cast<TZeraryColumnFx *>(m_repColumn->getFx());
1182 }
1183
1184 FxCommandUndo::cloneGroupStack(fx, m_repFx.getPointer());
1185
1186 // Store fx's input links (depending on m_repFx, they could be not matched
1187 // in the replacement)
1188 int p, ipCount = fx->getInputPortCount();
1189 for (p = 0; p != ipCount; ++p) {
1190 TFxPort *port = fx->getInputPort(p);
1191 if (TFx *inputFx = port->getFx())
1192 m_inputLinks.push_back(std::make_pair(p, inputFx));
1193 }
1194
1195 // Store the fx's linked fx
1196 m_linkedFx = fx->getLinkedFx();
1197 }
1198
1199 //-------------------------------------------------------------
1200
replace(TXsheet * xsh,TFx * fx,TFx * repFx,TXshColumn * column,TXshColumn * repColumn,int colIdx,int repColIdx)1201 void ReplaceFxUndo::replace(TXsheet *xsh, TFx *fx, TFx *repFx,
1202 TXshColumn *column, TXshColumn *repColumn,
1203 int colIdx, int repColIdx) {
1204 FxDag *fxDag = xsh->getFxDag();
1205
1206 TZeraryColumnFx *zcfx = column ? static_cast<TZeraryColumnFx *>(fx) : 0;
1207 TZeraryColumnFx *repZcfx =
1208 repColumn ? static_cast<TZeraryColumnFx *>(repFx) : 0;
1209
1210 TFx *ifx = zcfx ? zcfx->getZeraryFx() : fx;
1211 TFx *irepFx = repZcfx ? repZcfx->getZeraryFx() : repFx;
1212
1213 // Copy links first
1214 int p, ipCount = ifx->getInputPortCount(),
1215 ripCount = irepFx->getInputPortCount();
1216 for (p = 0; p != ipCount && p != ripCount; ++p) {
1217 TFxPort *ifxPort = ifx->getInputPort(p);
1218 TFxPort *irepFxPort = irepFx->getInputPort(p);
1219
1220 FxCommandUndo::attach(xsh, ifxPort->getFx(), irepFx, p, true);
1221 }
1222
1223 int opCount = fx->getOutputConnectionCount();
1224 for (p = opCount - 1; p >= 0; --p) {
1225 TFxPort *port = fx->getOutputConnection(p);
1226 port->setFx(repFx);
1227 }
1228
1229 if (fxDag->getTerminalFxs()->containsFx(fx)) {
1230 fxDag->removeFromXsheet(fx);
1231 fxDag->addToXsheet(repFx);
1232 }
1233
1234 // Remove fx/column
1235 FxCommandUndo::removeFxOrColumn(xsh, fx, colIdx, bool(repColumn), false);
1236
1237 // Insert the new fx/column
1238 if (repColumn)
1239 FxCommandUndo::insertColumn(xsh, repColumn, repColIdx,
1240 column); // Not attached to the xsheet
1241 else
1242 ::addFxToCurrentScene(repFx, xsh, false);
1243
1244 FxCommandUndo::copyGroupEditLevel(fx, repFx);
1245 FxCommandUndo::copyDagPosition(fx, repFx);
1246 }
1247
1248 //-------------------------------------------------------------
1249
redo() const1250 void ReplaceFxUndo::redo() const {
1251 ReplaceFxUndo::replace(m_xshHandle->getXsheet(), m_fx.getPointer(),
1252 m_repFx.getPointer(), m_column.getPointer(),
1253 m_repColumn.getPointer(), m_colIdx, m_repColIdx);
1254 FxCommandUndo::unlinkParams(m_fx.getPointer());
1255
1256 m_fxHandle->setFx(0);
1257 m_xshHandle->notifyXsheetChanged();
1258 }
1259
1260 //-------------------------------------------------------------
1261
undo() const1262 void ReplaceFxUndo::undo() const {
1263 ReplaceFxUndo::replace(m_xshHandle->getXsheet(), m_repFx.getPointer(),
1264 m_fx.getPointer(), m_repColumn.getPointer(),
1265 m_column.getPointer(), m_repColIdx, m_colIdx);
1266
1267 // Repair original input links for m_fx
1268 m_fx->disconnectAll();
1269
1270 size_t l, lCount = m_inputLinks.size();
1271 for (l = 0; l != lCount; ++l)
1272 m_fx->getInputPort(m_inputLinks[l].first)->setFx(m_inputLinks[l].second);
1273
1274 // Repair parameter links
1275 FxCommandUndo::linkParams(m_fx.getPointer(), m_linkedFx.getPointer());
1276
1277 m_fxHandle->setFx(0);
1278 m_xshHandle->notifyXsheetChanged();
1279 }
1280
1281 //-------------------------------------------------------------
1282
getHistoryString()1283 QString ReplaceFxUndo::getHistoryString() {
1284 QString str = QObject::tr("Replace Fx : ");
1285 str += QString("%1 > %2")
1286 .arg(QString::fromStdWString(m_fx->getFxId()))
1287 .arg(QString::fromStdWString(m_repFx->getFxId()));
1288
1289 return str;
1290 }
1291
1292 //=============================================================
1293
replaceFx(TFx * newFx,const QList<TFxP> & fxs,TXsheetHandle * xshHandle,TFxHandle * fxHandle)1294 void TFxCommand::replaceFx(TFx *newFx, const QList<TFxP> &fxs,
1295 TXsheetHandle *xshHandle, TFxHandle *fxHandle) {
1296 if (!newFx) return;
1297
1298 TUndoManager *undoManager = TUndoManager::manager();
1299 ::CloneFxFunctor cloneFx = {newFx, true};
1300
1301 undoManager->beginBlock();
1302
1303 TFxP clonedFx;
1304
1305 int f, fCount = fxs.size();
1306 for (f = 0; f != fCount; ++f) {
1307 if (!clonedFx) clonedFx = cloneFx();
1308
1309 std::unique_ptr<FxCommandUndo> undo(
1310 new ReplaceFxUndo(clonedFx, fxs[f], xshHandle, fxHandle));
1311 if (undo->isConsistent()) {
1312 undo->redo();
1313 undoManager->add(undo.release());
1314
1315 clonedFx = TFxP();
1316 }
1317 }
1318
1319 undoManager->endBlock();
1320 }
1321
1322 //**********************************************************************
1323 // Unlink Fx command
1324 //**********************************************************************
1325
1326 class UnlinkFxUndo final : public FxCommandUndo {
1327 TFxP m_fx, m_linkedFx;
1328
1329 TXsheetHandle *m_xshHandle;
1330
1331 public:
UnlinkFxUndo(const TFxP & fx,TXsheetHandle * xshHandle)1332 UnlinkFxUndo(const TFxP &fx, TXsheetHandle *xshHandle)
1333 : m_fx(fx), m_linkedFx(fx->getLinkedFx()), m_xshHandle(xshHandle) {}
1334
isConsistent() const1335 bool isConsistent() const override { return bool(m_linkedFx); }
1336
undo() const1337 void undo() const override {
1338 FxCommandUndo::linkParams(m_fx.getPointer(), m_linkedFx.getPointer());
1339 m_xshHandle->notifyXsheetChanged();
1340 }
1341
redo() const1342 void redo() const override {
1343 FxCommandUndo::unlinkParams(m_fx.getPointer());
1344 m_xshHandle->notifyXsheetChanged();
1345 }
1346
getSize() const1347 int getSize() const override { return sizeof(*this); }
1348
getHistoryString()1349 QString getHistoryString() override {
1350 return QObject::tr("Unlink Fx : %1 - - %2")
1351 .arg(QString::fromStdWString(m_fx->getFxId()))
1352 .arg(QString::fromStdWString(m_linkedFx->getFxId()));
1353 }
1354 };
1355
1356 //=============================================================
1357
unlinkFx(TFx * fx,TFxHandle * fxHandle,TXsheetHandle * xshHandle)1358 void TFxCommand::unlinkFx(TFx *fx, TFxHandle *fxHandle,
1359 TXsheetHandle *xshHandle) {
1360 if (!fx) return;
1361
1362 std::unique_ptr<FxCommandUndo> undo(new UnlinkFxUndo(fx, xshHandle));
1363 if (undo->isConsistent()) {
1364 undo->redo();
1365 TUndoManager::manager()->add(undo.release());
1366 }
1367 }
1368
1369 //**********************************************************************
1370 // Make Macro Fx command
1371 //**********************************************************************
1372
1373 class MakeMacroUndo : public FxCommandUndo {
1374 protected:
1375 TFxP m_macroFx;
1376 TApplication *m_app;
1377
1378 public:
MakeMacroUndo(const std::vector<TFxP> & fxs,TApplication * app)1379 MakeMacroUndo(const std::vector<TFxP> &fxs, TApplication *app) : m_app(app) {
1380 initialize(fxs);
1381 }
1382
isConsistent() const1383 bool isConsistent() const override { return bool(m_macroFx); }
1384
1385 void redo() const override;
1386 void undo() const override;
1387
getSize() const1388 int getSize() const override { return sizeof(*this); }
1389
getHistoryString()1390 QString getHistoryString() override {
1391 return QObject::tr("Make Macro Fx : %1")
1392 .arg(QString::fromStdWString(m_macroFx->getFxId()));
1393 }
1394
1395 private:
1396 void initialize(const std::vector<TFxP> &fxs);
1397
1398 protected:
MakeMacroUndo(TMacroFx * macroFx,TApplication * app)1399 MakeMacroUndo(TMacroFx *macroFx, TApplication *app)
1400 : m_macroFx(macroFx), m_app(app) {}
1401 };
1402
1403 //-------------------------------------------------------------
1404
initialize(const std::vector<TFxP> & fxs)1405 void MakeMacroUndo::initialize(const std::vector<TFxP> &fxs) {
1406 TXsheet *xsh = m_app->getCurrentXsheet()->getXsheet();
1407
1408 size_t f, fCount = fxs.size();
1409 for (f = 0; f != fCount; ++f) {
1410 // Only normal Fxs can be added in a macro
1411 TFx *fx = fxs[f].getPointer();
1412
1413 if (isInsideAMacroFx(fx, xsh) || fx->isZerary() ||
1414 dynamic_cast<TZeraryColumnFx *>(fx) || dynamic_cast<TMacroFx *>(fx) ||
1415 dynamic_cast<TLevelColumnFx *>(fx) ||
1416 dynamic_cast<TPaletteColumnFx *>(fx) || dynamic_cast<TXsheetFx *>(fx) ||
1417 dynamic_cast<TOutputFx *>(fx))
1418 return;
1419 }
1420
1421 TMacroFx *macroFx = TMacroFx::create(fxs);
1422 if (!macroFx) return;
1423
1424 ::initializeFx(xsh, macroFx);
1425 m_macroFx = TFxP(macroFx);
1426
1427 // An old comment suggested there may be trouble in case the fx editor popup
1428 // is opened.
1429 // In any case, the new macro fx will be selected at the end - so, let's
1430 // disable it right now
1431 m_app->getCurrentFx()->setFx(0);
1432 }
1433
1434 //-------------------------------------------------------------
1435
redo() const1436 void MakeMacroUndo::redo() const {
1437 TXsheet *xsh = m_app->getCurrentXsheet()->getXsheet();
1438 FxDag *fxDag = xsh->getFxDag();
1439 TFxSet *terminalFxs = fxDag->getTerminalFxs();
1440 TMacroFx *macroFx = static_cast<TMacroFx *>(m_macroFx.getPointer());
1441
1442 ::addFxToCurrentScene(macroFx, xsh, false);
1443
1444 // Replace the macro's root and deal with output links
1445 TFx *rootFx = macroFx->getRoot();
1446 if (terminalFxs->containsFx(rootFx)) fxDag->addToXsheet(macroFx);
1447
1448 int p, opCount = rootFx->getOutputConnectionCount();
1449 for (p = opCount - 1; p >= 0; --p)
1450 rootFx->getOutputConnection(p)->setFx(macroFx);
1451
1452 // Remove the macro's internal fxs from the scene
1453 const std::vector<TFxP> &fxs = macroFx->getFxs();
1454
1455 size_t f, fCount = fxs.size();
1456 for (f = 0; f != fCount; ++f)
1457 ::removeFxFromCurrentScene(fxs[f].getPointer(), xsh);
1458
1459 // Hijack their ports (no actual redirection) - resetting the port ownership.
1460 // NOTE: Is this even legal? Not gonna touch it, but... o_o!
1461 int ipCount = macroFx->getInputPortCount();
1462 for (p = 0; p != ipCount; ++p) macroFx->getInputPort(p)->setOwnerFx(macroFx);
1463
1464 m_app->getCurrentFx()->setFx(macroFx);
1465 m_app->getCurrentXsheet()->notifyXsheetChanged();
1466 }
1467
1468 //-------------------------------------------------------------
1469
undo() const1470 void MakeMacroUndo::undo() const {
1471 TXsheet *xsh = m_app->getCurrentXsheet()->getXsheet();
1472 FxDag *fxDag = xsh->getFxDag();
1473 TFxSet *terminalFxs = fxDag->getTerminalFxs();
1474 TMacroFx *macroFx = static_cast<TMacroFx *>(m_macroFx.getPointer());
1475
1476 // Reattach the macro's root to the xsheet if necessary
1477 TFx *rootFx = macroFx->getRoot();
1478 if (terminalFxs->containsFx(macroFx)) fxDag->addToXsheet(rootFx);
1479
1480 // Restore the root's output connections
1481 int p, opCount = macroFx->getOutputConnectionCount();
1482 for (p = opCount - 1; p >= 0; --p)
1483 macroFx->getOutputConnection(p)->setFx(rootFx);
1484
1485 // Remove the macro
1486 ::removeFxFromCurrentScene(macroFx, xsh);
1487
1488 // Re-insert the macro's internal fxs and restore ports ownership
1489 const std::vector<TFxP> &fxs = macroFx->getFxs();
1490
1491 size_t f, fCount = fxs.size();
1492 for (f = 0; f != fCount; ++f) {
1493 TFx *fx = fxs[f].getPointer();
1494
1495 ::addFxToCurrentScene(fx, xsh, false);
1496
1497 int p, ipCount = fx->getInputPortCount();
1498 for (p = 0; p != ipCount; ++p) fx->getInputPort(p)->setOwnerFx(fx);
1499 }
1500
1501 m_app->getCurrentFx()->setFx(0);
1502 m_app->getCurrentXsheet()->notifyXsheetChanged();
1503 }
1504
1505 //=============================================================
1506
makeMacroFx(const std::vector<TFxP> & fxs,TApplication * app)1507 void TFxCommand::makeMacroFx(const std::vector<TFxP> &fxs, TApplication *app) {
1508 if (fxs.empty()) return;
1509
1510 std::unique_ptr<FxCommandUndo> undo(new MakeMacroUndo(fxs, app));
1511 if (undo->isConsistent()) {
1512 undo->redo();
1513 TUndoManager::manager()->add(undo.release());
1514 }
1515 }
1516
1517 //**********************************************************************
1518 // Explode Macro Fx command
1519 //**********************************************************************
1520
1521 class ExplodeMacroUndo final : public MakeMacroUndo {
1522 public:
ExplodeMacroUndo(TMacroFx * macro,TApplication * app)1523 ExplodeMacroUndo(TMacroFx *macro, TApplication *app)
1524 : MakeMacroUndo(macro, app) {
1525 initialize();
1526 }
1527
redo() const1528 void redo() const override { MakeMacroUndo::undo(); }
undo() const1529 void undo() const override { MakeMacroUndo::redo(); }
1530
getSize() const1531 int getSize() const override { return sizeof(*this); }
1532
getHistoryString()1533 QString getHistoryString() override {
1534 return QObject::tr("Explode Macro Fx : %1")
1535 .arg(QString::fromStdWString(m_macroFx->getFxId()));
1536 }
1537
1538 private:
1539 void initialize();
1540 };
1541
1542 //------------------------------------------------------
1543
initialize()1544 void ExplodeMacroUndo::initialize() {
1545 if (!static_cast<TMacroFx *>(m_macroFx.getPointer())->getRoot())
1546 m_macroFx = TFxP();
1547 }
1548
1549 //=============================================================
1550
explodeMacroFx(TMacroFx * macroFx,TApplication * app)1551 void TFxCommand::explodeMacroFx(TMacroFx *macroFx, TApplication *app) {
1552 if (!macroFx) return;
1553
1554 std::unique_ptr<FxCommandUndo> undo(new ExplodeMacroUndo(macroFx, app));
1555 if (undo->isConsistent()) {
1556 undo->redo();
1557 TUndoManager::manager()->add(undo.release());
1558 }
1559 }
1560
1561 //**********************************************************************
1562 // Create Output Fx command
1563 //**********************************************************************
1564
1565 class CreateOutputFxUndo final : public FxCommandUndo {
1566 TFxP m_outputFx;
1567 TXsheetHandle *m_xshHandle;
1568
1569 public:
CreateOutputFxUndo(TFx * fx,TXsheetHandle * xshHandle)1570 CreateOutputFxUndo(TFx *fx, TXsheetHandle *xshHandle)
1571 : m_outputFx(new TOutputFx), m_xshHandle(xshHandle) {
1572 initialize(fx);
1573 }
1574
isConsistent() const1575 bool isConsistent() const override { return true; }
1576
redo() const1577 void redo() const override {
1578 FxDag *fxDag = m_xshHandle->getXsheet()->getFxDag();
1579 TOutputFx *outputFx = static_cast<TOutputFx *>(m_outputFx.getPointer());
1580
1581 fxDag->addOutputFx(outputFx);
1582 fxDag->setCurrentOutputFx(outputFx);
1583
1584 m_xshHandle->notifyXsheetChanged();
1585 }
1586
undo() const1587 void undo() const override {
1588 TOutputFx *outputFx = static_cast<TOutputFx *>(m_outputFx.getPointer());
1589
1590 m_xshHandle->getXsheet()->getFxDag()->removeOutputFx(outputFx);
1591 m_xshHandle->notifyXsheetChanged();
1592 }
1593
getSize() const1594 int getSize() const override { return sizeof(*this); }
1595
getHistoryString()1596 QString getHistoryString() override {
1597 return QObject::tr("Create Output Fx");
1598 }
1599
1600 private:
initialize(TFx * fx)1601 void initialize(TFx *fx) {
1602 TXsheet *xsh = m_xshHandle->getXsheet();
1603 TOutputFx *outputFx = static_cast<TOutputFx *>(m_outputFx.getPointer());
1604
1605 if (fx && !dynamic_cast<TOutputFx *>(fx))
1606 outputFx->getInputPort(0)->setFx(fx);
1607 else {
1608 TOutputFx *currentOutputFx = xsh->getFxDag()->getCurrentOutputFx();
1609 const TPointD &pos = currentOutputFx->getAttributes()->getDagNodePos();
1610 if (pos != TConst::nowhere)
1611 outputFx->getAttributes()->setDagNodePos(pos + TPointD(20, 20));
1612 }
1613 }
1614 };
1615
1616 //=============================================================
1617
createOutputFx(TXsheetHandle * xshHandle,TFx * currentFx)1618 void TFxCommand::createOutputFx(TXsheetHandle *xshHandle, TFx *currentFx) {
1619 TUndo *undo = new CreateOutputFxUndo(currentFx, xshHandle);
1620
1621 undo->redo();
1622 TUndoManager::manager()->add(undo);
1623 }
1624
1625 //**********************************************************************
1626 // Make Output Fx Current command
1627 //**********************************************************************
1628
makeOutputFxCurrent(TFx * fx,TXsheetHandle * xshHandle)1629 void TFxCommand::makeOutputFxCurrent(TFx *fx, TXsheetHandle *xshHandle) {
1630 TOutputFx *outputFx = dynamic_cast<TOutputFx *>(fx);
1631 if (!outputFx) return;
1632
1633 TXsheet *xsh = xshHandle->getXsheet();
1634 if (xsh->getFxDag()->getCurrentOutputFx() == outputFx) return;
1635
1636 xsh->getFxDag()->setCurrentOutputFx(outputFx);
1637 xshHandle->notifyXsheetChanged();
1638 }
1639
1640 //**********************************************************************
1641 // Connect Nodes To Xsheet command
1642 //**********************************************************************
1643
1644 class ConnectNodesToXsheetUndo : public FxCommandUndo {
1645 protected:
1646 std::vector<TFxP> m_fxs;
1647 TXsheetHandle *m_xshHandle;
1648
1649 public:
ConnectNodesToXsheetUndo(const std::list<TFxP> & fxs,TXsheetHandle * xshHandle)1650 ConnectNodesToXsheetUndo(const std::list<TFxP> &fxs, TXsheetHandle *xshHandle)
1651 : m_fxs(fxs.begin(), fxs.end()), m_xshHandle(xshHandle) {
1652 initialize();
1653 }
1654
isConsistent() const1655 bool isConsistent() const override { return !m_fxs.empty(); }
1656
redo() const1657 void redo() const override {
1658 /*
1659 Due to compatibility issues from *schematicnode.cpp files, the "do" operation
1660 must be
1661 accessible without scene change notifications (see TFxCommand::setParent())
1662 */
1663
1664 redo_();
1665 m_xshHandle->notifyXsheetChanged();
1666 }
1667
redo_() const1668 void redo_() const {
1669 FxDag *fxDag = m_xshHandle->getXsheet()->getFxDag();
1670
1671 size_t f, fCount = m_fxs.size();
1672 for (f = 0; f != fCount; ++f) fxDag->addToXsheet(m_fxs[f].getPointer());
1673 }
1674
undo() const1675 void undo() const override {
1676 FxDag *fxDag = m_xshHandle->getXsheet()->getFxDag();
1677
1678 size_t f, fCount = m_fxs.size();
1679 for (f = 0; f != fCount; ++f)
1680 fxDag->removeFromXsheet(m_fxs[f].getPointer());
1681
1682 m_xshHandle->notifyXsheetChanged();
1683 }
1684
getSize() const1685 int getSize() const override { return sizeof(*this); }
1686
getHistoryString()1687 QString getHistoryString() override {
1688 QString str = QObject::tr("Connect to Xsheet : ");
1689 std::vector<TFxP>::iterator it;
1690 for (it = m_fxs.begin(); it != m_fxs.end(); it++) {
1691 if (it != m_fxs.begin()) str += QString(", ");
1692 str += QString::fromStdWString((*it)->getFxId());
1693 }
1694 return str;
1695 }
1696
1697 protected:
ConnectNodesToXsheetUndo(const std::list<TFxP> & fxs,TXsheetHandle * xshHandle,bool)1698 ConnectNodesToXsheetUndo(const std::list<TFxP> &fxs, TXsheetHandle *xshHandle,
1699 bool)
1700 : m_fxs(fxs.begin(), fxs.end()), m_xshHandle(xshHandle) {}
1701
1702 private:
1703 void initialize();
1704 };
1705
1706 //------------------------------------------------------
1707
initialize()1708 void ConnectNodesToXsheetUndo::initialize() {
1709 TXsheet *xsh = m_xshHandle->getXsheet();
1710
1711 ::FilterInsideAMacro filterInMacro = {xsh};
1712 m_fxs.erase(std::remove_if(m_fxs.begin(), m_fxs.end(), filterInMacro),
1713 m_fxs.end());
1714
1715 ::FilterTerminalFxs filterTerminalFxs = {xsh};
1716 m_fxs.erase(std::remove_if(m_fxs.begin(), m_fxs.end(), filterTerminalFxs),
1717 m_fxs.end());
1718 }
1719
1720 //=============================================================
1721
connectNodesToXsheet(const std::list<TFxP> & fxs,TXsheetHandle * xshHandle)1722 void TFxCommand::connectNodesToXsheet(const std::list<TFxP> &fxs,
1723 TXsheetHandle *xshHandle) {
1724 std::unique_ptr<FxCommandUndo> undo(
1725 new ConnectNodesToXsheetUndo(fxs, xshHandle));
1726 if (undo->isConsistent()) {
1727 undo->redo();
1728 TUndoManager::manager()->add(undo.release());
1729 }
1730 }
1731
1732 //**********************************************************************
1733 // Disconnect Nodes From Xsheet command
1734 //**********************************************************************
1735
1736 class DisconnectNodesFromXsheetUndo final : public ConnectNodesToXsheetUndo {
1737 public:
DisconnectNodesFromXsheetUndo(const std::list<TFxP> & fxs,TXsheetHandle * xshHandle)1738 DisconnectNodesFromXsheetUndo(const std::list<TFxP> &fxs,
1739 TXsheetHandle *xshHandle)
1740 : ConnectNodesToXsheetUndo(fxs, xshHandle, true) {
1741 initialize();
1742 }
1743
redo() const1744 void redo() const override { ConnectNodesToXsheetUndo::undo(); }
undo() const1745 void undo() const override { ConnectNodesToXsheetUndo::redo(); }
1746
getHistoryString()1747 QString getHistoryString() override {
1748 QString str = QObject::tr("Disconnect from Xsheet : ");
1749 std::vector<TFxP>::iterator it;
1750 for (it = m_fxs.begin(); it != m_fxs.end(); it++) {
1751 if (it != m_fxs.begin()) str += QString(", ");
1752 str += QString::fromStdWString((*it)->getFxId());
1753 }
1754 return str;
1755 }
1756
1757 private:
1758 void initialize();
1759 };
1760
1761 //------------------------------------------------------
1762
initialize()1763 void DisconnectNodesFromXsheetUndo::initialize() {
1764 TXsheet *xsh = m_xshHandle->getXsheet();
1765
1766 ::FilterInsideAMacro filterInMacro = {xsh};
1767 m_fxs.erase(std::remove_if(m_fxs.begin(), m_fxs.end(), filterInMacro),
1768 m_fxs.end());
1769
1770 ::FilterNonTerminalFxs filterNonTerminalFxs = {xsh};
1771 m_fxs.erase(std::remove_if(m_fxs.begin(), m_fxs.end(), filterNonTerminalFxs),
1772 m_fxs.end());
1773 }
1774
1775 //=============================================================
1776
disconnectNodesFromXsheet(const std::list<TFxP> & fxs,TXsheetHandle * xshHandle)1777 void TFxCommand::disconnectNodesFromXsheet(const std::list<TFxP> &fxs,
1778 TXsheetHandle *xshHandle) {
1779 std::unique_ptr<FxCommandUndo> undo(
1780 new DisconnectNodesFromXsheetUndo(fxs, xshHandle));
1781 if (undo->isConsistent()) {
1782 undo->redo();
1783 TUndoManager::manager()->add(undo.release());
1784 }
1785 }
1786
1787 //**********************************************************************
1788 // Delete Link command
1789 //**********************************************************************
1790
1791 class DeleteLinksUndo : public FxCommandUndo {
1792 struct DynamicLink {
1793 int m_groupIndex;
1794 std::string m_portName;
1795 TFx *m_inputFx;
1796 };
1797
1798 typedef std::vector<DynamicLink> DynamicLinksVector;
1799
1800 protected:
1801 std::list<TFxCommand::Link> m_links; //!< The input links to remove
1802
1803 private:
1804 std::list<TFxCommand::Link>
1805 m_normalLinks; //!< Actual *common* links from m_links
1806 std::list<TFx *> m_terminalFxs; //!< Fxs connected to the xsheet (represents
1807 //! xsheet input links)
1808 // Why SMART pointers? No fx is deleted with this command... hmm...
1809 std::map<TFx *, DynamicLinksVector>
1810 m_dynamicLinks; //!< Complete dynamic links configuration, per fx.
1811
1812 TXsheetHandle *m_xshHandle;
1813
1814 public:
DeleteLinksUndo(const std::list<TFxCommand::Link> & links,TXsheetHandle * xshHandle)1815 DeleteLinksUndo(const std::list<TFxCommand::Link> &links,
1816 TXsheetHandle *xshHandle)
1817 : m_links(links), m_xshHandle(xshHandle) {
1818 initialize();
1819 }
1820
isConsistent() const1821 bool isConsistent() const override { return !m_links.empty(); }
1822
1823 void redo() const override;
1824 void undo() const override;
1825
getSize() const1826 int getSize() const override { return 10 << 10; } // Say, around 10 kB
1827
1828 QString getHistoryString() override;
1829
1830 protected:
DeleteLinksUndo(TXsheetHandle * xshHandle)1831 DeleteLinksUndo(TXsheetHandle *xshHandle) : m_xshHandle(xshHandle) {}
1832
1833 void initialize();
1834 };
1835
1836 //------------------------------------------------------
1837
initialize()1838 void DeleteLinksUndo::initialize() {
1839 struct locals {
1840 static bool isInvalid(FxDag *fxDag, const TFxCommand::Link &link) {
1841 if (link.m_index < 0)
1842 return !fxDag->getTerminalFxs()->containsFx(
1843 link.m_inputFx.getPointer());
1844
1845 TFx *inFx = ::getActualOut(link.m_inputFx.getPointer());
1846 TFx *outFx = ::getActualIn(link.m_outputFx.getPointer());
1847
1848 return (link.m_index >= outFx->getInputPortCount())
1849 ? true
1850 : (outFx->getInputPort(link.m_index)->getFx() != inFx);
1851 }
1852 };
1853
1854 TXsheet *xsh = m_xshHandle->getXsheet();
1855 FxDag *fxDag = xsh->getFxDag();
1856
1857 // Forget links dealing with an open macro Fx. Note that this INCLUDES
1858 // inside/outside links.
1859 ::FilterInsideAMacro filterInMacro = {xsh};
1860 m_links.erase(std::remove_if(m_links.begin(), m_links.end(), filterInMacro),
1861 m_links.end());
1862
1863 // Remove invalid links
1864 m_links.erase(std::remove_if(m_links.begin(), m_links.end(),
1865 [fxDag](const TFxCommand::Link &link) {
1866 return locals::isInvalid(fxDag, link);
1867 }),
1868 m_links.end());
1869
1870 std::list<TFxCommand::Link>::iterator lt, lEnd(m_links.end());
1871 for (lt = m_links.begin(); lt != lEnd; ++lt) {
1872 const TFxCommand::Link &link = *lt;
1873
1874 if (TXsheetFx *xsheetFx =
1875 dynamic_cast<TXsheetFx *>(link.m_outputFx.getPointer())) {
1876 // The input fx is connected to an xsheet node - ie it's terminal
1877 m_terminalFxs.push_back(link.m_inputFx.getPointer());
1878 continue;
1879 }
1880
1881 TFx *outputFx = link.m_outputFx.getPointer();
1882
1883 // Zerary columns wrap the actual zerary fx - that is the fx holding input
1884 // ports
1885 if (TZeraryColumnFx *zfx = dynamic_cast<TZeraryColumnFx *>(outputFx))
1886 outputFx = zfx->getZeraryFx();
1887
1888 TFxPort *port = outputFx->getInputPort(link.m_index);
1889
1890 int portGroup = port->getGroupIndex();
1891 if (portGroup < 0) m_normalLinks.push_back(link);
1892
1893 if (outputFx->hasDynamicPortGroups())
1894 m_dynamicLinks.insert(std::make_pair(outputFx, DynamicLinksVector()));
1895 }
1896
1897 m_normalLinks.sort(); // Really necessary?
1898
1899 // Store the complete configuration of dynamic groups - not just the ones
1900 // touched by
1901 // link editing, ALL of them.
1902 std::map<TFx *, DynamicLinksVector>::iterator dlt,
1903 dlEnd(m_dynamicLinks.end());
1904 for (dlt = m_dynamicLinks.begin(); dlt != dlEnd; ++dlt) {
1905 TFx *outputFx = dlt->first;
1906 DynamicLinksVector &dynLinks = dlt->second;
1907
1908 int p, pCount = outputFx->getInputPortCount();
1909 for (p = 0; p != pCount; ++p) {
1910 TFxPort *port = outputFx->getInputPort(p);
1911
1912 int g = port->getGroupIndex();
1913 if (g >= 0) {
1914 DynamicLink dLink = {g, outputFx->getInputPortName(p), port->getFx()};
1915 dynLinks.push_back(dLink);
1916 }
1917 }
1918 }
1919 }
1920
1921 //------------------------------------------------------
1922
redo() const1923 void DeleteLinksUndo::redo() const {
1924 FxDag *fxDag = m_xshHandle->getXsheet()->getFxDag();
1925
1926 // Perform unlinking
1927 std::list<TFxCommand::Link>::const_iterator lt, lEnd(m_links.end());
1928 for (lt = m_links.begin(); lt != lEnd; ++lt) {
1929 const TFxCommand::Link &link = *lt;
1930
1931 TFx *outputFx = lt->m_outputFx.getPointer();
1932
1933 if (TXsheetFx *xsheetFx = dynamic_cast<TXsheetFx *>(outputFx)) {
1934 // Terminal fx link case
1935 fxDag->removeFromXsheet(link.m_inputFx.getPointer());
1936 continue;
1937 }
1938
1939 // Actual link case
1940 if (TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(outputFx))
1941 outputFx = zcfx->getZeraryFx();
1942
1943 int index = lt->m_index;
1944
1945 assert(index < outputFx->getInputPortCount());
1946 if (index < outputFx->getInputPortCount())
1947 outputFx->getInputPort(index)->setFx(0);
1948 }
1949
1950 if (m_isLastInRedoBlock) m_xshHandle->notifyXsheetChanged();
1951 }
1952
1953 //------------------------------------------------------
1954
undo() const1955 void DeleteLinksUndo::undo() const {
1956 FxDag *fxDag = m_xshHandle->getXsheet()->getFxDag();
1957
1958 // Re-attach terminal fxs
1959 std::list<TFx *>::const_iterator ft;
1960 for (ft = m_terminalFxs.begin(); ft != m_terminalFxs.end(); ++ft) {
1961 if (fxDag->checkLoop(*ft, fxDag->getXsheetFx())) {
1962 assert(fxDag->checkLoop(*ft, fxDag->getXsheetFx()));
1963 continue;
1964 }
1965
1966 fxDag->addToXsheet(*ft);
1967 }
1968
1969 // Restore common links
1970 std::list<TFxCommand::Link>::const_iterator lt, lEnd(m_normalLinks.end());
1971 for (lt = m_normalLinks.begin(); lt != lEnd; ++lt) {
1972 const TFxCommand::Link &link = *lt;
1973
1974 int index = link.m_index;
1975 TFx *inputFx = link.m_inputFx.getPointer();
1976 TFx *outputFx = link.m_outputFx.getPointer();
1977
1978 if (TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(outputFx))
1979 outputFx = zcfx->getZeraryFx();
1980
1981 if (fxDag->checkLoop(inputFx, outputFx)) {
1982 assert(fxDag->checkLoop(inputFx, outputFx));
1983 continue;
1984 }
1985
1986 assert(index < outputFx->getInputPortCount());
1987
1988 if (index < outputFx->getInputPortCount())
1989 outputFx->getInputPort(index)->setFx(inputFx);
1990 }
1991
1992 // Restore complete dynamic port groups configuration
1993 std::map<TFx *, DynamicLinksVector>::const_iterator dlt,
1994 dlEnd(m_dynamicLinks.end());
1995 for (dlt = m_dynamicLinks.begin(); dlt != dlEnd; ++dlt) {
1996 TFx *outputFx = dlt->first;
1997 const DynamicLinksVector &dynLinks = dlt->second;
1998
1999 {
2000 int g, gCount = outputFx->dynamicPortGroupsCount();
2001 for (g = 0; g != gCount; ++g) outputFx->clearDynamicPortGroup(g);
2002 }
2003
2004 size_t d, dCount = dynLinks.size();
2005 for (d = 0; d != dCount; ++d) {
2006 const DynamicLink &link = dynLinks[d];
2007
2008 TFxPort *port = new TRasterFxPort; // isAControlPort... semi-obsolete
2009 port->setFx(link.m_inputFx);
2010
2011 outputFx->addInputPort(link.m_portName, port, link.m_groupIndex);
2012 }
2013 }
2014
2015 if (m_isLastInBlock) m_xshHandle->notifyXsheetChanged();
2016 }
2017
2018 //------------------------------------------------------
2019
getHistoryString()2020 QString DeleteLinksUndo::getHistoryString() {
2021 QString str = QObject::tr("Delete Link");
2022 if (!m_normalLinks.empty()) {
2023 str += QString(" : ");
2024 std::list<TFxCommand::Link>::const_iterator it;
2025 for (it = m_normalLinks.begin(); it != m_normalLinks.end(); it++) {
2026 if (it != m_normalLinks.begin()) str += QString(", ");
2027 TFxCommand::Link boundingFxs = *it;
2028 str +=
2029 QString("%1- -%2")
2030 .arg(QString::fromStdWString(boundingFxs.m_inputFx->getName()))
2031 .arg(QString::fromStdWString(boundingFxs.m_outputFx->getName()));
2032 }
2033 }
2034
2035 if (!m_terminalFxs.empty()) {
2036 str += QString(" : ");
2037 std::list<TFx *>::const_iterator ft;
2038 for (ft = m_terminalFxs.begin(); ft != m_terminalFxs.end(); ++ft) {
2039 if (ft != m_terminalFxs.begin()) str += QString(", ");
2040 str +=
2041 QString("%1- -Xsheet").arg(QString::fromStdWString((*ft)->getName()));
2042 }
2043 }
2044
2045 return str;
2046 }
2047
2048 //=============================================================
2049
deleteLinks(const std::list<TFxCommand::Link> & links,TXsheetHandle * xshHandle)2050 static void deleteLinks(const std::list<TFxCommand::Link> &links,
2051 TXsheetHandle *xshHandle) {
2052 std::unique_ptr<FxCommandUndo> undo(new DeleteLinksUndo(links, xshHandle));
2053 if (undo->isConsistent()) {
2054 undo->m_isLastInRedoBlock = false;
2055 undo->redo();
2056 TUndoManager::manager()->add(undo.release());
2057 }
2058 }
2059
2060 //******************************************************
2061 // Delete Fx command
2062 //******************************************************
2063
2064 class DeleteFxOrColumnUndo final : public DeleteLinksUndo {
2065 protected:
2066 TFxP m_fx;
2067 TXshColumnP m_column;
2068 int m_colIdx;
2069
2070 TFxP m_linkedFx;
2071 std::vector<TFx *> m_nonTerminalInputs;
2072
2073 mutable std::unique_ptr<TStageObjectParams> m_columnData;
2074
2075 TXsheetHandle *m_xshHandle;
2076 TFxHandle *m_fxHandle;
2077
2078 using DeleteLinksUndo::m_links;
2079
2080 public:
2081 DeleteFxOrColumnUndo(const TFxP &fx, TXsheetHandle *xshHandle,
2082 TFxHandle *fxHandle);
2083 DeleteFxOrColumnUndo(int colIdx, TXsheetHandle *xshHandle,
2084 TFxHandle *fxHandle);
2085
2086 bool isConsistent() const override;
2087
2088 void redo() const override;
2089 void undo() const override;
2090
getSize() const2091 int getSize() const override { return sizeof(*this); }
2092
2093 QString getHistoryString() override;
2094
2095 private:
2096 void initialize();
2097 };
2098
2099 //-------------------------------------------------------------
2100
DeleteFxOrColumnUndo(const TFxP & fx,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2101 DeleteFxOrColumnUndo::DeleteFxOrColumnUndo(const TFxP &fx,
2102 TXsheetHandle *xshHandle,
2103 TFxHandle *fxHandle)
2104 : DeleteLinksUndo(xshHandle)
2105 , m_fx(fx)
2106 , m_colIdx(-1)
2107 , m_xshHandle(xshHandle)
2108 , m_fxHandle(fxHandle) {
2109 initialize();
2110 }
2111
2112 //-------------------------------------------------------------
2113
DeleteFxOrColumnUndo(int colIdx,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2114 DeleteFxOrColumnUndo::DeleteFxOrColumnUndo(int colIdx, TXsheetHandle *xshHandle,
2115 TFxHandle *fxHandle)
2116 : DeleteLinksUndo(xshHandle)
2117 , m_colIdx(colIdx)
2118 , m_xshHandle(xshHandle)
2119 , m_fxHandle(fxHandle) {
2120 initialize();
2121 }
2122
2123 //-------------------------------------------------------------
2124
initialize()2125 void DeleteFxOrColumnUndo::initialize() {
2126 struct {
2127 DeleteFxOrColumnUndo *m_this;
2128 inline void getActualData(TXsheet *xsh, TFx *&ifx, TFx *&ofx) {
2129 TFx *fx = m_this->m_fx.getPointer();
2130
2131 if (!fx) fx = xsh->getColumn(m_this->m_colIdx)->getFx();
2132
2133 if (fx) {
2134 ifx = ::getActualIn(fx);
2135 ofx = ::getActualOut(fx);
2136
2137 if (TColumnFx *colFx = dynamic_cast<TColumnFx *>(ofx))
2138 m_this->m_colIdx = colFx->getColumnIndex();
2139 }
2140
2141 m_this->m_fx = ofx;
2142 }
2143 } locals = {this};
2144
2145 assert(m_fx || m_colIdx >= 0);
2146
2147 TXsheet *xsh = m_xshHandle->getXsheet();
2148 TFx *ifx = 0, *ofx = 0;
2149
2150 locals.getActualData(xsh, ifx, ofx);
2151
2152 if (ofx && isInsideAMacroFx(ofx, xsh)) // Macros must be exploded first
2153 {
2154 m_fx = TFxP(), m_colIdx = -1;
2155 return;
2156 }
2157
2158 // Assume shared ownership of the associated column, if any
2159 if (m_colIdx >= 0) {
2160 m_column = xsh->getColumn(m_colIdx);
2161 assert(m_column);
2162
2163 // Currently disputed, but in the previous implementation there was code
2164 // suggesting
2165 // that the column could have already been removed from the xsheet.
2166 // Preventing that case...
2167 if (!m_column->inColumnsSet()) {
2168 m_fx = TFxP(), m_colIdx = -1; // Bail out as inconsistent op
2169 return; //
2170 }
2171 } else if (TOutputFx *outputFx = dynamic_cast<TOutputFx *>(ofx)) {
2172 if (xsh->getFxDag()->getOutputFxCount() <= 1) {
2173 // Cannot delete the last output fx
2174 m_fx = TFxP();
2175 assert(m_colIdx < 0);
2176 return;
2177 }
2178 }
2179
2180 // Store links to be re-established in the undo
2181 FxDag *fxDag = xsh->getFxDag();
2182
2183 if (ofx) {
2184 // Store the terminal output link, if any
2185 if (fxDag->getTerminalFxs()->containsFx(ofx))
2186 m_links.push_back(TFxCommand::Link(ofx, fxDag->getXsheetFx(), -1));
2187
2188 // Store output links
2189 int p, opCount = ofx->getOutputConnectionCount();
2190 for (p = 0; p != opCount; ++p) {
2191 if (TFx *outFx = ofx->getOutputConnection(p)->getOwnerFx()) {
2192 int ip, ipCount = outFx->getInputPortCount();
2193 for (ip = 0; ip != ipCount; ++ip)
2194 if (outFx->getInputPort(ip)->getFx() == ofx) break;
2195
2196 assert(ip < ipCount);
2197 if (ip < ipCount) m_links.push_back(TFxCommand::Link(m_fx, outFx, ip));
2198 }
2199 }
2200 }
2201
2202 if (ifx) {
2203 m_linkedFx = ifx->getLinkedFx();
2204
2205 // Store input links
2206 int p, ipCount = ifx->getInputPortCount();
2207 for (p = 0; p != ipCount; ++p) {
2208 if (TFx *inputFx = ifx->getInputPort(p)->getFx()) {
2209 m_links.push_back(TFxCommand::Link(inputFx, m_fx, p));
2210 if (!fxDag->getTerminalFxs()->containsFx(
2211 inputFx)) // Store input fxs which DID NOT have an
2212 m_nonTerminalInputs.push_back(
2213 inputFx); // xsheet link before the deletion
2214 }
2215 }
2216 }
2217
2218 DeleteLinksUndo::initialize();
2219 }
2220
2221 //-------------------------------------------------------------
2222
isConsistent() const2223 bool DeleteFxOrColumnUndo::isConsistent() const {
2224 return (bool(m_fx) || (m_colIdx >= 0));
2225
2226 // NOTE: DeleteLinksUndo::isConsistent() is not checked.
2227 // This is because there could be no link to remove, and yet
2228 // the operation IS consistent.
2229 }
2230
2231 //-------------------------------------------------------------
2232
redo() const2233 void DeleteFxOrColumnUndo::redo() const {
2234 TXsheet *xsh = m_xshHandle->getXsheet();
2235
2236 // Store data to be restored in the undo
2237 if (m_colIdx >= 0) {
2238 assert(!m_columnData.get());
2239
2240 m_columnData.reset(
2241 xsh->getStageObject(TStageObjectId::ColumnId(
2242 m_colIdx)) // Cloned, ownership acquired
2243 ->getParams()); // However, params stored there are NOT cloned.
2244 } // This is fine since we're deleting the column...
2245
2246 // Perform operation
2247 FxCommandUndo::removeFxOrColumn(xsh, m_fx.getPointer(), m_colIdx);
2248
2249 if (m_isLastInRedoBlock)
2250 m_xshHandle->notifyXsheetChanged(); // Add the rest...
2251 }
2252
2253 //-------------------------------------------------------------
2254
undo() const2255 void DeleteFxOrColumnUndo::undo() const {
2256 struct Locals {
2257 const DeleteFxOrColumnUndo *m_this;
2258
2259 void insertColumnIn(TXsheet *xsh) {
2260 m_this->insertColumn(xsh, m_this->m_column.getPointer(),
2261 m_this->m_colIdx);
2262
2263 // Restore column data
2264 TStageObject *sObj =
2265 xsh->getStageObject(TStageObjectId::ColumnId(m_this->m_colIdx));
2266 assert(sObj);
2267
2268 sObj->assignParams(m_this->m_columnData.get(), false);
2269 m_this->m_columnData.reset();
2270 }
2271
2272 } locals = {this};
2273
2274 TXsheet *xsh = m_xshHandle->getXsheet();
2275 FxDag *fxDag = xsh->getFxDag();
2276
2277 // Re-add the fx/column to the xsheet
2278 TFx *fx = m_fx.getPointer();
2279
2280 if (m_column)
2281 locals.insertColumnIn(xsh);
2282 else if (TOutputFx *outFx = dynamic_cast<TOutputFx *>(fx))
2283 xsh->getFxDag()->addOutputFx(outFx);
2284 else
2285 addFxToCurrentScene(fx, xsh, false);
2286
2287 if (fx) {
2288 // Remove xsheet connections that became terminal due to the fx
2289 // removal
2290 size_t ti, tiCount = m_nonTerminalInputs.size();
2291 for (ti = 0; ti != tiCount; ++ti)
2292 fxDag->removeFromXsheet(m_nonTerminalInputs[ti]);
2293
2294 // Re-link parameters if necessary
2295 TFx *ifx = ::getActualIn(fx);
2296
2297 if (m_linkedFx) ifx->linkParams(m_linkedFx.getPointer());
2298
2299 // Re-establish fx links
2300 DeleteLinksUndo::undo();
2301 } else if (m_isLastInBlock) // Already covered by DeleteLinksUndo::undo()
2302 m_xshHandle->notifyXsheetChanged(); // in the other branch
2303 }
2304
2305 //-------------------------------------------------------------
2306
getHistoryString()2307 QString DeleteFxOrColumnUndo::getHistoryString() {
2308 return QObject::tr("Delete Fx Node : %1")
2309 .arg(QString::fromStdWString(m_fx->getFxId()));
2310 }
2311
2312 //=============================================================
2313
deleteFxs(const std::list<TFxP> & fxs,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2314 static void deleteFxs(const std::list<TFxP> &fxs, TXsheetHandle *xshHandle,
2315 TFxHandle *fxHandle) {
2316 TUndoManager *undoManager = TUndoManager::manager();
2317 TXsheet *xsh = xshHandle->getXsheet();
2318
2319 undoManager->beginBlock();
2320
2321 std::list<TFxP>::const_iterator ft, fEnd = fxs.end();
2322 for (ft = fxs.begin(); ft != fEnd; ++ft) {
2323 // Skip levels, as they are effectively supplied in here, AND in the
2324 // deleteColumns() branch.
2325 // This should NOT be performed here, though. TO BE MOVED TO deleteSelection
2326 // or ABOVE, if any.
2327 if (dynamic_cast<TLevelColumnFx *>(ft->getPointer())) continue;
2328
2329 std::unique_ptr<FxCommandUndo> undo(
2330 new DeleteFxOrColumnUndo(*ft, xshHandle, fxHandle));
2331 if (undo->isConsistent()) {
2332 // prevent emiting xsheetChanged signal for every undos which will cause
2333 // multiple triggers of preview rendering
2334 undo->m_isLastInRedoBlock = false;
2335 undo->redo();
2336 TUndoManager::manager()->add(undo.release());
2337 }
2338 }
2339
2340 undoManager->endBlock();
2341 }
2342
2343 //**********************************************************************
2344 // Remove Output Fx command
2345 //**********************************************************************
2346
removeOutputFx(TFx * fx,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2347 void TFxCommand::removeOutputFx(TFx *fx, TXsheetHandle *xshHandle,
2348 TFxHandle *fxHandle) {
2349 TOutputFx *outputFx = dynamic_cast<TOutputFx *>(fx);
2350 if (!outputFx) return;
2351
2352 std::unique_ptr<FxCommandUndo> undo(
2353 new DeleteFxOrColumnUndo(fx, xshHandle, fxHandle));
2354 if (undo->isConsistent()) {
2355 undo->redo();
2356 TUndoManager::manager()->add(undo.release());
2357 }
2358 }
2359
2360 //**********************************************************************
2361 // Delete Columns command
2362 //**********************************************************************
2363
deleteColumns(const std::list<int> & columns,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2364 static void deleteColumns(const std::list<int> &columns,
2365 TXsheetHandle *xshHandle, TFxHandle *fxHandle) {
2366 TUndoManager *undoManager = TUndoManager::manager();
2367
2368 undoManager->beginBlock();
2369
2370 // As columns are deleted, their index changes. So, the easiest workaround is
2371 // to address the
2372 // columns directly, and then their (updated) column index.
2373 TXsheet *xsh = xshHandle->getXsheet();
2374
2375 std::vector<TXshColumn *> cols;
2376 for (auto const &c : columns) {
2377 cols.push_back(xsh->getColumn(c));
2378 }
2379
2380 size_t c, cCount = cols.size();
2381 for (c = 0; c != cCount; ++c) {
2382 std::unique_ptr<FxCommandUndo> undo(
2383 new DeleteFxOrColumnUndo(cols[c]->getIndex(), xshHandle, fxHandle));
2384 if (undo->isConsistent()) {
2385 // prevent emiting xsheetChanged signal for every undos which will cause
2386 // multiple triggers of preview rendering
2387 undo->m_isLastInRedoBlock = false;
2388 undo->redo();
2389 undoManager->add(undo.release());
2390 }
2391 }
2392
2393 undoManager->endBlock();
2394 }
2395
2396 //**********************************************************************
2397 // Delete Selection command
2398 //**********************************************************************
2399
deleteSelection(const std::list<TFxP> & fxs,const std::list<Link> & links,const std::list<int> & columns,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2400 void TFxCommand::deleteSelection(const std::list<TFxP> &fxs,
2401 const std::list<Link> &links,
2402 const std::list<int> &columns,
2403 TXsheetHandle *xshHandle,
2404 TFxHandle *fxHandle) {
2405 // Prepare selected fxs - column fxs would be done twice if the corresponding
2406 // columns have been supplied for deletion too
2407 ::FilterColumnFxs filterColumnFxs;
2408
2409 std::list<TFxP> filteredFxs(fxs);
2410 filteredFxs.erase(
2411 std::remove_if(filteredFxs.begin(), filteredFxs.end(), filterColumnFxs),
2412 filteredFxs.end());
2413
2414 // Perform deletions
2415 TUndoManager::manager()->beginBlock();
2416
2417 if (!columns.empty()) deleteColumns(columns, xshHandle, fxHandle);
2418 if (!filteredFxs.empty()) deleteFxs(filteredFxs, xshHandle, fxHandle);
2419 if (!links.empty()) deleteLinks(links, xshHandle);
2420
2421 TUndoManager::manager()->endBlock();
2422 // emit xsheetChanged once here
2423 xshHandle->notifyXsheetChanged();
2424 }
2425
2426 //**********************************************************************
2427 // Paste Fxs command
2428 //**********************************************************************
2429
2430 /*
2431 NOTE: Zerary fxs should not be in the fxs list - but rather in the columns
2432 list.
2433 This would allow us to forget about the zeraryColumnSize map.
2434
2435 This requires changing the *selection*, though. To be done, in a later step.
2436 */
2437
2438 class UndoPasteFxs : public FxCommandUndo {
2439 protected:
2440 std::list<TFxP> m_fxs; //!< Fxs to be pasted
2441 std::list<TXshColumnP> m_columns; //!< Columns to be pasted
2442 std::vector<TFxCommand::Link>
2443 m_links; //!< Links to be re-established at the redo
2444
2445 TXsheetHandle *m_xshHandle;
2446 TFxHandle *m_fxHandle;
2447
2448 public:
UndoPasteFxs(const std::list<TFxP> & fxs,const std::map<TFx *,int> & zeraryFxColumnSize,const std::list<TXshColumnP> & columns,const TPointD & pos,TXsheetHandle * xshHandle,TFxHandle * fxHandle,bool addOffset=true)2449 UndoPasteFxs(const std::list<TFxP> &fxs,
2450 const std::map<TFx *, int> &zeraryFxColumnSize,
2451 const std::list<TXshColumnP> &columns, const TPointD &pos,
2452 TXsheetHandle *xshHandle, TFxHandle *fxHandle,
2453 bool addOffset = true)
2454 : m_fxs(fxs)
2455 , m_columns(columns)
2456 , m_xshHandle(xshHandle)
2457 , m_fxHandle(fxHandle) {
2458 initialize(zeraryFxColumnSize, pos, addOffset);
2459 }
2460
isConsistent() const2461 bool isConsistent() const override {
2462 return !(m_fxs.empty() && m_columns.empty());
2463 }
2464
2465 void redo() const override;
2466 void undo() const override;
2467
getSize() const2468 int getSize() const override { return sizeof(*this); }
2469
2470 QString getHistoryString() override;
2471
2472 protected:
2473 template <typename Func>
2474 void for_each_fx(Func func) const;
2475
2476 private:
2477 void initialize(const std::map<TFx *, int> &zeraryFxColumnSize,
2478 const TPointD &pos, bool addOffset);
2479 };
2480
2481 //------------------------------------------------------
2482
initialize(const std::map<TFx *,int> & zeraryFxColumnSize,const TPointD & pos,bool addOffset)2483 void UndoPasteFxs::initialize(const std::map<TFx *, int> &zeraryFxColumnSize,
2484 const TPointD &pos, bool addOffset) {
2485 struct locals {
2486 static void buildDagPos(TFx *fromFx, TFx *toFx, bool copyPos,
2487 bool addOffset) {
2488 TPointD dagPos = TConst::nowhere;
2489 if (copyPos) {
2490 static const TPointD offset(30, 30);
2491
2492 dagPos = fromFx->getAttributes()->getDagNodePos();
2493 if (addOffset &&
2494 (dagPos != TConst::nowhere)) // Shift may only happen if the copied
2495 dagPos += offset; // position is well-formed.
2496 }
2497
2498 toFx->getAttributes()->setDagNodePos(dagPos);
2499 }
2500
2501 static void renamePort(TFx *fx, int p, const std::wstring &oldId,
2502 const std::wstring &newId) {
2503 const QString &qOldId = QString::fromStdWString(oldId);
2504 const QString &qNewId = QString::fromStdWString(newId);
2505 const QString &qPortName =
2506 QString::fromStdString(fx->getInputPortName(p));
2507
2508 if (qPortName.endsWith(qOldId)) {
2509 QString qNewPortName = qPortName;
2510 qNewPortName.replace(qOldId, qNewId);
2511 fx->renamePort(qPortName.toStdString(), qNewPortName.toStdString());
2512 }
2513 }
2514 };
2515
2516 TXsheet *xsh = m_xshHandle->getXsheet();
2517 bool copyDagPos = (pos != TConst::nowhere);
2518
2519 // Initialize fxs
2520 std::list<TFxP>::iterator ft, fEnd = m_fxs.end();
2521 for (ft = m_fxs.begin(); ft != fEnd;) {
2522 TFx *fx = ft->getPointer();
2523
2524 assert(!dynamic_cast<TZeraryColumnFx *>(fx));
2525
2526 if (has_fx_column(fx)) {
2527 // Zerary case
2528
2529 // Since we have no column available (WHICH IS WRONG), we'll build up a
2530 // column with
2531 // the specified column size
2532 std::map<TFx *, int>::const_iterator it = zeraryFxColumnSize.find(fx);
2533 int rows = (it == zeraryFxColumnSize.end()) ? 100 : it->second;
2534
2535 TXshZeraryFxColumn *column = new TXshZeraryFxColumn(rows);
2536 TZeraryColumnFx *zcfx = column->getZeraryColumnFx();
2537
2538 zcfx->setZeraryFx(fx);
2539
2540 int op, opCount = fx->getOutputConnectionCount();
2541 for (op = opCount - 1; op >= 0;
2542 --op) // Output links in the zerary case were
2543 { // assigned to the *actual* zerary
2544 TFxPort *outPort =
2545 fx->getOutputConnection(op); // and not to a related zerary column.
2546 outPort->setFx(zcfx); // This is an FxsData fault... BUGFIX REQUEST!
2547 }
2548
2549 zcfx->getAttributes()->setDagNodePos(
2550 fx->getAttributes()->getDagNodePos());
2551 m_columns.push_front(column);
2552
2553 ft = m_fxs.erase(ft);
2554 continue;
2555 }
2556
2557 // Macro case
2558 if (TMacroFx *macroFx = dynamic_cast<TMacroFx *>(fx)) {
2559 const std::vector<TFxP> &inMacroFxs = macroFx->getFxs();
2560
2561 size_t f, fCount = inMacroFxs.size();
2562 for (f = 0; f != fCount; ++f) {
2563 TFx *inFx = inMacroFxs[f].getPointer();
2564 const std::wstring &oldFxId = inFx->getFxId();
2565
2566 // Since we're pasting a macro, we're actually pasting all of its inner
2567 // fxs,
2568 // which must have a different name (id) from the originals. However,
2569 // such ids
2570 // are actually copied in the macro's *port names* - which must
2571 // therefore be
2572 // redirected to the new names.
2573
2574 ::initializeFx(xsh, inFx);
2575 const std::wstring &newFxId = inFx->getFxId();
2576
2577 int ip, ipCount = macroFx->getInputPortCount();
2578 for (ip = 0; ip != ipCount; ++ip)
2579 locals::renamePort(macroFx, ip, oldFxId, newFxId);
2580 // node position of the macrofx is defined by dag-pos of inner fxs.
2581 // so we need to reset them here or pasted node will be at the same
2582 // position as the copied one.
2583 locals::buildDagPos(inFx, inFx, copyDagPos, addOffset);
2584 }
2585 }
2586
2587 ::initializeFx(xsh, fx);
2588 locals::buildDagPos(fx, fx, copyDagPos, addOffset);
2589
2590 ++ft;
2591 }
2592
2593 // Filter columns
2594 auto const circularSubxsheet = [xsh](const TXshColumnP &col) -> bool {
2595 return xsh->checkCircularReferences(col.getPointer());
2596 };
2597 m_columns.erase(
2598 std::remove_if(m_columns.begin(), m_columns.end(), circularSubxsheet),
2599 m_columns.end());
2600
2601 // Initialize columns
2602 std::list<TXshColumnP>::const_iterator ct, cEnd(m_columns.end());
2603 for (ct = m_columns.begin(); ct != cEnd; ++ct) {
2604 if (TFx *cfx = (*ct)->getFx()) {
2605 ::initializeFx(xsh, cfx);
2606 locals::buildDagPos(cfx, cfx, copyDagPos, addOffset);
2607 }
2608 }
2609
2610 // Now, let's make a temporary container of all the stored fxs, both
2611 // normal and column-based
2612 std::vector<TFx *> fxs;
2613 fxs.reserve(m_fxs.size() + m_columns.size());
2614
2615 for_each_fx([&fxs](TFx *fx) { fxs.push_back(fx); });
2616
2617 // We need to store input links for these fxs
2618 size_t f, fCount = fxs.size();
2619 for (f = 0; f != fCount; ++f) {
2620 TFx *fx = fxs[f];
2621
2622 TFx *ofx = ::getActualIn(fx);
2623 fx = ::getActualOut(fx);
2624
2625 int il, ilCount = ofx->getInputPortCount();
2626 for (il = 0; il != ilCount; ++il) {
2627 if (TFx *ifx = ofx->getInputPort(il)->getFx())
2628 m_links.push_back(TFxCommand::Link(ifx, ofx, il));
2629 }
2630 }
2631
2632 // Apply the required position, if any
2633 if (pos != TConst::nowhere) {
2634 // Then, we'll take the mean difference from pos and add it to
2635 // each fx
2636 TPointD middlePos;
2637 int fxsCount = 0;
2638
2639 std::vector<TFx *>::const_iterator ft, fEnd = fxs.end();
2640 for (ft = fxs.begin(); ft != fEnd; ++ft) {
2641 TFx *fx = *ft;
2642
2643 const TPointD &fxPos = fx->getAttributes()->getDagNodePos();
2644 if (fxPos != TConst::nowhere) {
2645 middlePos += fxPos;
2646 ++fxsCount;
2647 }
2648 }
2649
2650 if (fxsCount > 0) {
2651 middlePos = TPointD(middlePos.x / fxsCount, middlePos.y / fxsCount);
2652 const TPointD &offset = pos - middlePos;
2653
2654 for (ft = fxs.begin(); ft != fEnd; ++ft) {
2655 TFx *fx = *ft;
2656
2657 const TPointD &fxPos = fx->getAttributes()->getDagNodePos();
2658 if (fxPos != TConst::nowhere)
2659 fx->getAttributes()->setDagNodePos(fxPos + offset);
2660 }
2661 }
2662 }
2663 }
2664
2665 //------------------------------------------------------
2666
redo() const2667 void UndoPasteFxs::redo() const {
2668 TXsheet *xsh = m_xshHandle->getXsheet();
2669
2670 // Iterate all normal fxs
2671 std::list<TFxP>::const_iterator ft, fEnd = m_fxs.end();
2672 for (ft = m_fxs.begin(); ft != fEnd; ++ft) {
2673 // Re-insert them in the scene
2674 addFxToCurrentScene(ft->getPointer(), xsh, false);
2675 }
2676
2677 // Iterate columns
2678 std::list<TXshColumnP>::const_iterator ct, cEnd = m_columns.end();
2679 for (ct = m_columns.begin(); ct != cEnd; ++ct) {
2680 // Insert them
2681 FxCommandUndo::insertColumn(xsh, ct->getPointer(),
2682 xsh->getFirstFreeColumnIndex(), true, false);
2683 }
2684
2685 // Restore links
2686 size_t l, lCount = m_links.size();
2687 for (l = 0; l != lCount; ++l) FxCommandUndo::attach(xsh, m_links[l], false);
2688
2689 m_xshHandle->notifyXsheetChanged();
2690 }
2691
2692 //------------------------------------------------------
2693
undo() const2694 void UndoPasteFxs::undo() const {
2695 TXsheet *xsh = m_xshHandle->getXsheet();
2696
2697 std::list<TFxP>::const_iterator ft, fEnd = m_fxs.end();
2698 for (ft = m_fxs.begin(); ft != fEnd; ++ft) {
2699 TFx *fx = ft->getPointer();
2700
2701 FxCommandUndo::removeFxOrColumn(xsh, fx, -1, true,
2702 false); // Skip parameter links removal
2703 FxCommandUndo::makeNotCurrent(m_fxHandle, fx);
2704 }
2705
2706 std::list<TXshColumnP>::const_iterator ct, cEnd = m_columns.end();
2707 for (ct = m_columns.begin(); ct != cEnd; ++ct) {
2708 FxCommandUndo::removeFxOrColumn(xsh, 0, (*ct)->getIndex(), true,
2709 false); // Skip parameter links removal
2710 FxCommandUndo::makeNotCurrent(m_fxHandle, (*ct)->getFx());
2711 }
2712
2713 m_xshHandle->notifyXsheetChanged();
2714 }
2715
2716 //------------------------------------------------------
2717
2718 template <typename Func>
for_each_fx(Func func) const2719 void UndoPasteFxs::for_each_fx(Func func) const {
2720 std::list<TFxP>::const_iterator ft, fEnd = m_fxs.end();
2721 for (ft = m_fxs.begin(); ft != fEnd; ++ft) func(ft->getPointer());
2722
2723 std::list<TXshColumnP>::const_iterator ct, cEnd = m_columns.end();
2724 for (ct = m_columns.begin(); ct != cEnd; ++ct)
2725 if (TFx *cfx = (*ct)->getFx()) func(cfx);
2726 }
2727
2728 //------------------------------------------------------
2729
getHistoryString()2730 QString UndoPasteFxs::getHistoryString() {
2731 QString str = QObject::tr("Paste Fx : ");
2732 std::list<TFxP>::const_iterator it;
2733 for (it = m_fxs.begin(); it != m_fxs.end(); it++) {
2734 if (it != m_fxs.begin()) str += QString(", ");
2735 str += QString("%1").arg(QString::fromStdWString((*it)->getName()));
2736 }
2737 return str;
2738 }
2739
2740 //=============================================================
2741
pasteFxs(const std::list<TFxP> & fxs,const std::map<TFx *,int> & zeraryFxColumnSize,const std::list<TXshColumnP> & columns,const TPointD & pos,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2742 void TFxCommand::pasteFxs(const std::list<TFxP> &fxs,
2743 const std::map<TFx *, int> &zeraryFxColumnSize,
2744 const std::list<TXshColumnP> &columns,
2745 const TPointD &pos, TXsheetHandle *xshHandle,
2746 TFxHandle *fxHandle) {
2747 std::unique_ptr<FxCommandUndo> undo(new UndoPasteFxs(
2748 fxs, zeraryFxColumnSize, columns, pos, xshHandle, fxHandle));
2749 if (undo->isConsistent()) {
2750 undo->redo();
2751 TUndoManager::manager()->add(undo.release());
2752 }
2753 }
2754
2755 //**********************************************************************
2756 // Add Paste Fxs command
2757 //**********************************************************************
2758
2759 class UndoAddPasteFxs : public UndoPasteFxs {
2760 protected:
2761 TFxCommand::Link m_linkIn; //!< Input link to be re-established on redo
2762
2763 public:
UndoAddPasteFxs(TFx * inFx,const std::list<TFxP> & fxs,const std::map<TFx *,int> & zeraryFxColumnSize,const std::list<TXshColumnP> & columns,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2764 UndoAddPasteFxs(TFx *inFx, const std::list<TFxP> &fxs,
2765 const std::map<TFx *, int> &zeraryFxColumnSize,
2766 const std::list<TXshColumnP> &columns,
2767 TXsheetHandle *xshHandle, TFxHandle *fxHandle)
2768 : UndoPasteFxs(fxs, zeraryFxColumnSize, columns, TConst::nowhere,
2769 xshHandle, fxHandle) {
2770 initialize(inFx);
2771 }
2772
2773 void redo() const override;
2774
getSize() const2775 int getSize() const override { return sizeof(*this); }
2776
2777 private:
2778 void initialize(TFx *inFx);
2779 };
2780
2781 //------------------------------------------------------
2782
initialize(TFx * inFx)2783 void UndoAddPasteFxs::initialize(TFx *inFx) {
2784 if (!(inFx && UndoPasteFxs::isConsistent())) return;
2785
2786 // NOTE: Any zerary (shouldn't be there anyway) has already been
2787 // moved to columns at this point
2788
2789 TXsheet *xsh = m_xshHandle->getXsheet();
2790
2791 // Ensure that inFx and outFx are not in a macro Fx
2792 if (::isInsideAMacroFx(inFx, xsh)) {
2793 m_fxs.clear(), m_columns.clear();
2794 return;
2795 }
2796
2797 // Get the first fx to be inserted, and follow links down
2798 // (until an empty input port at index 0 is found)
2799 TFx *ifx = FxCommandUndo::leftmostConnectedFx(m_fxs.front().getPointer());
2800
2801 // Then, we have the link to be established
2802 m_linkIn = TFxCommand::Link(inFx, ifx, 0);
2803
2804 // Furthermore, clone the group stack from inFx into each inserted fx
2805 auto const clone_fun =
2806 static_cast<void (*)(TFx *, TFx *)>(FxCommandUndo::cloneGroupStack);
2807 for_each_fx([inFx, clone_fun](TFx *toFx) { clone_fun(inFx, toFx); });
2808 }
2809
2810 //------------------------------------------------------
2811
redo() const2812 void UndoAddPasteFxs::redo() const {
2813 if (m_linkIn.m_inputFx) {
2814 TXsheet *xsh = m_xshHandle->getXsheet();
2815
2816 // Further apply the stored link
2817 FxCommandUndo::attach(xsh, m_linkIn, false);
2818
2819 // Copiare l'indice di gruppo dell'fx di input
2820 auto const copy_fun =
2821 static_cast<void (*)(TFx *, TFx *)>(FxCommandUndo::copyGroupEditLevel);
2822 for_each_fx([this, copy_fun](TFx *toFx) {
2823 copy_fun(m_linkIn.m_inputFx.getPointer(), toFx);
2824 });
2825 }
2826
2827 UndoPasteFxs::redo();
2828 }
2829
2830 //=============================================================
2831
addPasteFxs(TFx * inFx,const std::list<TFxP> & fxs,const std::map<TFx *,int> & zeraryFxColumnSize,const std::list<TXshColumnP> & columns,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2832 void TFxCommand::addPasteFxs(TFx *inFx, const std::list<TFxP> &fxs,
2833 const std::map<TFx *, int> &zeraryFxColumnSize,
2834 const std::list<TXshColumnP> &columns,
2835 TXsheetHandle *xshHandle, TFxHandle *fxHandle) {
2836 std::unique_ptr<FxCommandUndo> undo(new UndoAddPasteFxs(
2837 inFx, fxs, zeraryFxColumnSize, columns, xshHandle, fxHandle));
2838 if (undo->isConsistent()) {
2839 // NOTE: (inFx == 0) is considered consistent, as long as
2840 // UndoPasteFxs::isConsistent()
2841 undo->redo();
2842 TUndoManager::manager()->add(undo.release());
2843 }
2844 }
2845
2846 //**********************************************************************
2847 // Insert Paste Fxs command
2848 //**********************************************************************
2849
2850 class UndoInsertPasteFxs final : public UndoAddPasteFxs {
2851 TFxCommand::Link m_linkOut; //!< Output link to be re-established
2852 //!< on redo
2853 public:
UndoInsertPasteFxs(const TFxCommand::Link & link,const std::list<TFxP> & fxs,const std::map<TFx *,int> & zeraryFxColumnSize,const std::list<TXshColumnP> & columns,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2854 UndoInsertPasteFxs(const TFxCommand::Link &link, const std::list<TFxP> &fxs,
2855 const std::map<TFx *, int> &zeraryFxColumnSize,
2856 const std::list<TXshColumnP> &columns,
2857 TXsheetHandle *xshHandle, TFxHandle *fxHandle)
2858 : UndoAddPasteFxs(link.m_inputFx.getPointer(), fxs, zeraryFxColumnSize,
2859 columns, xshHandle, fxHandle) {
2860 initialize(link);
2861 }
2862
2863 void redo() const override;
2864 void undo() const override;
2865
getSize() const2866 int getSize() const override { return sizeof(*this); }
2867
2868 private:
2869 void initialize(const TFxCommand::Link &link);
2870 };
2871
2872 //------------------------------------------------------
2873
initialize(const TFxCommand::Link & link)2874 void UndoInsertPasteFxs::initialize(const TFxCommand::Link &link) {
2875 if (!UndoPasteFxs::isConsistent()) return;
2876
2877 TXsheet *xsh = m_xshHandle->getXsheet();
2878 TFx *inFx = link.m_inputFx.getPointer();
2879 TFx *outFx = link.m_outputFx.getPointer();
2880
2881 // Ensure consistency
2882 if (!(inFx && outFx) || ::isInsideAMacroFx(outFx, xsh)) {
2883 m_fxs.clear(), m_columns.clear();
2884 return;
2885 }
2886
2887 // Get the first fx to be inserted, and follow links up
2888 // (to a no output fx)
2889 TFx *ofx = FxCommandUndo::rightmostConnectedFx(m_fxs.front().getPointer());
2890
2891 // Now, store the appropriate output link
2892 m_linkOut = TFxCommand::Link(ofx, outFx, link.m_index);
2893 }
2894
2895 //------------------------------------------------------
2896
redo() const2897 void UndoInsertPasteFxs::redo() const {
2898 TXsheet *xsh = m_xshHandle->getXsheet();
2899
2900 // Further apply the stored link pair
2901 FxCommandUndo::attach(xsh, m_linkOut, false);
2902
2903 if (m_linkOut.m_index < 0)
2904 xsh->getFxDag()->removeFromXsheet(m_linkIn.m_inputFx.getPointer());
2905
2906 UndoAddPasteFxs::redo();
2907 }
2908
2909 //------------------------------------------------------
2910
undo() const2911 void UndoInsertPasteFxs::undo() const {
2912 TXsheet *xsh = m_xshHandle->getXsheet();
2913
2914 // Reattach the original configuration
2915 TFxCommand::Link orig(m_linkIn.m_inputFx, m_linkOut.m_outputFx,
2916 m_linkOut.m_index);
2917 FxCommandUndo::attach(xsh, orig, false);
2918
2919 UndoAddPasteFxs::undo();
2920 }
2921
2922 //=============================================================
2923
insertPasteFxs(const Link & link,const std::list<TFxP> & fxs,const std::map<TFx *,int> & zeraryFxColumnSize,const std::list<TXshColumnP> & columns,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2924 void TFxCommand::insertPasteFxs(const Link &link, const std::list<TFxP> &fxs,
2925 const std::map<TFx *, int> &zeraryFxColumnSize,
2926 const std::list<TXshColumnP> &columns,
2927 TXsheetHandle *xshHandle, TFxHandle *fxHandle) {
2928 std::unique_ptr<FxCommandUndo> undo(new UndoInsertPasteFxs(
2929 link, fxs, zeraryFxColumnSize, columns, xshHandle, fxHandle));
2930 if (undo->isConsistent()) {
2931 undo->redo();
2932 TUndoManager::manager()->add(undo.release());
2933 }
2934 }
2935
2936 //**********************************************************************
2937 // Replace Paste Fxs command
2938 //**********************************************************************
2939
2940 class UndoReplacePasteFxs final : public UndoAddPasteFxs {
2941 std::unique_ptr<DeleteFxOrColumnUndo> m_deleteFxUndo;
2942
2943 TFx *m_fx, *m_rightmostFx;
2944
2945 public:
UndoReplacePasteFxs(TFx * fx,const std::list<TFxP> & fxs,const std::map<TFx *,int> & zeraryFxColumnSize,const std::list<TXshColumnP> & columns,TXsheetHandle * xshHandle,TFxHandle * fxHandle)2946 UndoReplacePasteFxs(TFx *fx, const std::list<TFxP> &fxs,
2947 const std::map<TFx *, int> &zeraryFxColumnSize,
2948 const std::list<TXshColumnP> &columns,
2949 TXsheetHandle *xshHandle, TFxHandle *fxHandle)
2950 : UndoAddPasteFxs(inFx(fx), fxs, zeraryFxColumnSize, columns, xshHandle,
2951 fxHandle)
2952 , m_deleteFxUndo(new DeleteFxOrColumnUndo(fx, xshHandle, fxHandle))
2953 , m_fx(fx)
2954 , m_rightmostFx() {
2955 initialize();
2956 }
2957
isConsistent() const2958 bool isConsistent() const override {
2959 return UndoAddPasteFxs::isConsistent() && m_deleteFxUndo->isConsistent();
2960 }
2961 void redo() const override;
2962 void undo() const override;
2963
getSize() const2964 int getSize() const override { return sizeof(*this); }
2965
2966 private:
inFx(const TFx * fx)2967 static TFx *inFx(const TFx *fx) {
2968 return (fx && fx->getInputPortCount() > 0) ? fx->getInputPort(0)->getFx()
2969 : 0;
2970 }
2971
2972 void initialize();
2973 };
2974
2975 //------------------------------------------------------
2976
initialize()2977 void UndoReplacePasteFxs::initialize() {
2978 if (m_fxs.empty()) return;
2979
2980 TXsheet *xsh = m_xshHandle->getXsheet();
2981 FxDag *fxDag = xsh->getFxDag();
2982
2983 // Get the first fx to be inserted, and follow links up
2984 // (to a no output fx)
2985 m_rightmostFx =
2986 FxCommandUndo::rightmostConnectedFx(this->m_fxs.front().getPointer());
2987
2988 // Then, add to the list of links to be inserted upon redo
2989 int ol, olCount = m_fx->getOutputConnectionCount();
2990 for (ol = 0; ol != olCount; ++ol) {
2991 TFxPort *port = m_fx->getOutputConnection(ol);
2992 TFx *ownerFx = port->getOwnerFx();
2993
2994 TCG_ASSERT(port && ownerFx, continue);
2995
2996 int p = ::inputPortIndex(ownerFx, port);
2997 TCG_ASSERT(p < ownerFx->getInputPortCount(), continue);
2998
2999 this->m_links.push_back(TFxCommand::Link(m_rightmostFx, ownerFx, p));
3000 }
3001
3002 if (fxDag->getTerminalFxs()->containsFx(m_fx))
3003 this->m_links.push_back(
3004 TFxCommand::Link(m_rightmostFx, fxDag->getXsheetFx(), -1));
3005 }
3006
3007 //------------------------------------------------------
3008
redo() const3009 void UndoReplacePasteFxs::redo() const {
3010 TXsheet *xsh = m_xshHandle->getXsheet();
3011 FxDag *fxDag = xsh->getFxDag();
3012
3013 // Deleting m_fx would attach its input to the xsheet, if m_fx is terminal.
3014 // In our case, however, it needs to be attached to the replacement fx - so,
3015 // let's detach m_fx from the xsheet
3016 fxDag->removeFromXsheet(m_fx);
3017
3018 // Then, delete the fx and insert the replacement. Note that this order is
3019 // required to ensure the correct dag positions
3020 m_deleteFxUndo->redo();
3021 UndoAddPasteFxs::redo();
3022 }
3023
3024 //------------------------------------------------------
3025
undo() const3026 void UndoReplacePasteFxs::undo() const {
3027 TXsheet *xsh = m_xshHandle->getXsheet();
3028 FxDag *fxDag = xsh->getFxDag();
3029
3030 // Remove m_lastFx's output connections - UndoAddPasteFxs would try to
3031 // redirect them to the replaced fx's input (due to the 'blind' detach
3032 // command)
3033 if (m_rightmostFx) {
3034 int ol, olCount = m_rightmostFx->getOutputConnectionCount();
3035 for (ol = olCount - 1; ol >= 0; --ol)
3036 if (TFxPort *port = m_rightmostFx->getOutputConnection(ol))
3037 port->setFx(0);
3038
3039 fxDag->removeFromXsheet(m_rightmostFx);
3040 }
3041
3042 // Reverse the applied commands. Again, the order prevents 'bumped' dag
3043 // positions
3044
3045 UndoAddPasteFxs::undo(); // This would bridge the inserted fxs' inputs with
3046 // their outputs
3047 m_deleteFxUndo->undo(); // This also re-establishes the original output links
3048 }
3049
3050 //=============================================================
3051
replacePasteFxs(TFx * inFx,const std::list<TFxP> & fxs,const std::map<TFx *,int> & zeraryFxColumnSize,const std::list<TXshColumnP> & columns,TXsheetHandle * xshHandle,TFxHandle * fxHandle)3052 void TFxCommand::replacePasteFxs(TFx *inFx, const std::list<TFxP> &fxs,
3053 const std::map<TFx *, int> &zeraryFxColumnSize,
3054 const std::list<TXshColumnP> &columns,
3055 TXsheetHandle *xshHandle,
3056 TFxHandle *fxHandle) {
3057 std::unique_ptr<FxCommandUndo> undo(new UndoReplacePasteFxs(
3058 inFx, fxs, zeraryFxColumnSize, columns, xshHandle, fxHandle));
3059 if (undo->isConsistent()) {
3060 undo->redo();
3061 TUndoManager::manager()->add(undo.release());
3062 }
3063 }
3064
3065 //**********************************************************************
3066 // Disconnect Fxs command
3067 //**********************************************************************
3068
3069 class UndoDisconnectFxs : public FxCommandUndo {
3070 protected:
3071 std::list<TFxP> m_fxs;
3072 TFx *m_leftFx, *m_rightFx;
3073
3074 // NOTE: Although we'll detach only 1 input link, fxs with dynamic ports still
3075 // require us
3076 // to store the whole ports configuration
3077 std::vector<TFxCommand::Link> m_undoLinksIn, m_undoLinksOut,
3078 m_undoTerminalLinks;
3079 std::vector<QPair<TFxP, TPointD>> m_undoDagPos, m_redoDagPos;
3080
3081 TXsheetHandle *m_xshHandle;
3082
3083 public:
UndoDisconnectFxs(const std::list<TFxP> & fxs,const QList<QPair<TFxP,TPointD>> & oldFxPos,TXsheetHandle * xshHandle)3084 UndoDisconnectFxs(const std::list<TFxP> &fxs,
3085 const QList<QPair<TFxP, TPointD>> &oldFxPos,
3086 TXsheetHandle *xshHandle)
3087 : m_fxs(fxs)
3088 , m_xshHandle(xshHandle)
3089 , m_undoDagPos(oldFxPos.begin(), oldFxPos.end()) {
3090 initialize();
3091 }
3092
isConsistent() const3093 bool isConsistent() const override { return !m_fxs.empty(); }
3094
3095 void redo() const override;
3096 void undo() const override;
3097
getSize() const3098 int getSize() const override { return sizeof(*this); }
3099
getHistoryString()3100 QString getHistoryString() override { return QObject::tr("Disconnect Fx"); }
3101
3102 private:
3103 void initialize();
3104
applyPos(const QPair<TFxP,TPointD> & pair)3105 static void applyPos(const QPair<TFxP, TPointD> &pair) {
3106 pair.first->getAttributes()->setDagNodePos(pair.second);
3107 }
3108
attach(TXsheet * xsh,const TFxCommand::Link & link)3109 static void attach(TXsheet *xsh, const TFxCommand::Link &link) {
3110 FxCommandUndo::attach(xsh, link, false);
3111 }
detachXsh(TXsheet * xsh,const TFxCommand::Link & link)3112 static void detachXsh(TXsheet *xsh, const TFxCommand::Link &link) {
3113 xsh->getFxDag()->removeFromXsheet(link.m_inputFx.getPointer());
3114 }
3115 };
3116
3117 //======================================================
3118
initialize()3119 void UndoDisconnectFxs::initialize() {
3120 TXsheet *xsh = m_xshHandle->getXsheet();
3121 FxDag *fxDag = xsh->getFxDag();
3122
3123 // Don't deal with fxs inside a macro fx
3124 ::FilterInsideAMacro insideAMacro_fun = {xsh};
3125 if (std::find_if(m_fxs.begin(), m_fxs.end(), insideAMacro_fun) != m_fxs.end())
3126 m_fxs.clear();
3127
3128 if (m_fxs.empty()) return;
3129
3130 // Build fxs data
3131 auto const contains = [this](TFx const *fx) -> bool {
3132 return std::count_if(this->m_fxs.begin(), this->m_fxs.end(),
3133 [fx](TFxP &f) { return f.getPointer() == fx; }) > 0;
3134 };
3135
3136 m_leftFx =
3137 FxCommandUndo::leftmostConnectedFx(m_fxs.front().getPointer(), contains);
3138 m_rightFx =
3139 FxCommandUndo::rightmostConnectedFx(m_fxs.front().getPointer(), contains);
3140
3141 // Store sensible original data for the undo
3142 m_undoLinksIn = FxCommandUndo::inputLinks(xsh, m_leftFx);
3143 m_undoLinksOut = FxCommandUndo::outputLinks(xsh, m_rightFx);
3144
3145 std::vector<TFxCommand::Link>::const_iterator lt, lEnd = m_undoLinksIn.end();
3146 for (lt = m_undoLinksIn.begin(); lt != lEnd; ++lt)
3147 if (fxDag->getTerminalFxs()->containsFx(lt->m_inputFx.getPointer()))
3148 m_undoTerminalLinks.push_back(TFxCommand::Link(lt->m_inputFx.getPointer(),
3149 fxDag->getXsheetFx(), -1));
3150
3151 std::vector<QPair<TFxP, TPointD>> v;
3152 for (auto const &e : m_undoDagPos) {
3153 v.emplace_back(e.first, e.first->getAttributes()->getDagNodePos());
3154 }
3155 m_redoDagPos = std::move(v);
3156 m_redoDagPos.shrink_to_fit();
3157 }
3158
3159 //------------------------------------------------------
3160
redo() const3161 void UndoDisconnectFxs::redo() const {
3162 TXsheet *xsh = m_xshHandle->getXsheet();
3163
3164 // Detach the first port only - I'm not sure it should really be like this,
3165 // but it's
3166 // legacy, and altering it would require to revise the 'simulation' procedures
3167 // in fxschematicscene.cpp... (any command simulation should be done here,
3168 // btw)
3169 FxCommandUndo::detachFxs(xsh, m_leftFx, m_rightFx, false);
3170 if (m_leftFx->getInputPortCount() > 0) m_leftFx->getInputPort(0)->setFx(0);
3171
3172 // This is also convenient, since fxs with dynamic groups will maintain all
3173 // BUT 1
3174 // port - thus preventing us from dealing with that, since 1 port is always
3175 // available
3176 // on fxs with dynamic ports...
3177
3178 std::for_each(m_redoDagPos.begin(), m_redoDagPos.end(), applyPos);
3179
3180 m_xshHandle->notifyXsheetChanged();
3181 }
3182
3183 //------------------------------------------------------
3184
undo() const3185 void UndoDisconnectFxs::undo() const {
3186 typedef void (*LinkFun)(TXsheet * xsh, const TFxCommand::Link &);
3187
3188 TXsheet *xsh = m_xshHandle->getXsheet();
3189 FxDag *fxDag = xsh->getFxDag();
3190
3191 // Restore the old links
3192 auto const attacher = [xsh](const TFxCommand::Link &link) {
3193 return UndoDisconnectFxs::attach(xsh, link);
3194 };
3195 auto const xshDetacher = [xsh](const TFxCommand::Link &link) {
3196 return UndoDisconnectFxs::detachXsh(xsh, link);
3197 };
3198
3199 std::for_each(m_undoLinksIn.begin(), m_undoLinksIn.end(), attacher);
3200 std::for_each(m_undoLinksOut.begin(), m_undoLinksOut.end(), attacher);
3201
3202 std::for_each(m_undoLinksIn.begin(), m_undoLinksIn.end(), xshDetacher);
3203 std::for_each(m_undoTerminalLinks.begin(), m_undoTerminalLinks.end(),
3204 attacher);
3205
3206 // Restore old positions
3207 std::for_each(m_undoDagPos.begin(), m_undoDagPos.end(), applyPos);
3208
3209 m_xshHandle->notifyXsheetChanged();
3210 }
3211
3212 //======================================================
3213
disconnectFxs(const std::list<TFxP> & fxs,TXsheetHandle * xshHandle,const QList<QPair<TFxP,TPointD>> & fxPos)3214 void TFxCommand::disconnectFxs(const std::list<TFxP> &fxs,
3215 TXsheetHandle *xshHandle,
3216 const QList<QPair<TFxP, TPointD>> &fxPos) {
3217 std::unique_ptr<FxCommandUndo> undo(
3218 new UndoDisconnectFxs(fxs, fxPos, xshHandle));
3219 if (undo->isConsistent()) {
3220 undo->redo();
3221 TUndoManager::manager()->add(undo.release());
3222 }
3223 }
3224
3225 //**********************************************************************
3226 // Connect Fxs command
3227 //**********************************************************************
3228
3229 class UndoConnectFxs final : public UndoDisconnectFxs {
3230 struct GroupData;
3231
3232 private:
3233 TFxCommand::Link m_link;
3234 std::vector<GroupData> m_undoGroupDatas;
3235
3236 public:
UndoConnectFxs(const TFxCommand::Link & link,const std::list<TFxP> & fxs,const QList<QPair<TFxP,TPointD>> & fxPos,TXsheetHandle * xshHandle)3237 UndoConnectFxs(const TFxCommand::Link &link, const std::list<TFxP> &fxs,
3238 const QList<QPair<TFxP, TPointD>> &fxPos,
3239 TXsheetHandle *xshHandle)
3240 : UndoDisconnectFxs(fxs, fxPos, xshHandle), m_link(link) {
3241 initialize();
3242 }
3243
isConsistent() const3244 bool isConsistent() const override { return !m_fxs.empty(); }
3245
3246 void redo() const override;
3247 void undo() const override;
3248
getSize() const3249 int getSize() const override { return sizeof(*this); }
3250
3251 QString getHistoryString() override;
3252
3253 private:
3254 void initialize();
3255
applyPos(const QPair<TFxP,TPointD> & pair)3256 static void applyPos(const QPair<TFxP, TPointD> &pair) {
3257 pair.first->getAttributes()->setDagNodePos(pair.second);
3258 }
3259 };
3260
3261 //======================================================
3262
3263 struct UndoConnectFxs::GroupData {
3264 TFx *m_fx;
3265 QStack<int> m_groupIds;
3266 QStack<std::wstring> m_groupNames;
3267 int m_editingGroup;
3268
3269 public:
3270 GroupData(TFx *fx);
3271 void restore() const;
3272 };
3273
3274 //------------------------------------------------------
3275
GroupData(TFx * fx)3276 UndoConnectFxs::GroupData::GroupData(TFx *fx)
3277 : m_fx(fx)
3278 , m_groupIds(fx->getAttributes()->getGroupIdStack())
3279 , m_groupNames(fx->getAttributes()->getGroupNameStack())
3280 , m_editingGroup(fx->getAttributes()->getEditingGroupId()) {}
3281
3282 //------------------------------------------------------
3283
restore() const3284 void UndoConnectFxs::GroupData::restore() const {
3285 assert(!m_groupIds.empty());
3286
3287 FxCommandUndo::cloneGroupStack(m_groupIds, m_groupNames, m_fx);
3288 FxCommandUndo::copyGroupEditLevel(m_editingGroup, m_fx);
3289 }
3290
3291 //======================================================
3292
initialize()3293 void UndoConnectFxs::initialize() {
3294 if (!UndoDisconnectFxs::isConsistent()) return;
3295
3296 TCG_ASSERT(m_link.m_inputFx && m_link.m_outputFx, m_fxs.clear(); return );
3297
3298 // Store sensible original data for the undo
3299 m_undoGroupDatas.reserve(m_fxs.size());
3300
3301 std::list<TFxP>::const_iterator ft, fEnd = m_fxs.end();
3302 for (ft = m_fxs.begin(); ft != fEnd; ++ft) {
3303 if ((*ft)->getAttributes()->isGrouped())
3304 m_undoGroupDatas.push_back(GroupData((*ft).getPointer()));
3305 }
3306 }
3307
3308 //------------------------------------------------------
3309
redo() const3310 void UndoConnectFxs::redo() const {
3311 UndoDisconnectFxs::redo();
3312
3313 TXsheet *xsh = m_xshHandle->getXsheet();
3314
3315 // Apply the new links
3316 FxCommandUndo::insertFxs(xsh, m_link, m_leftFx, m_rightFx);
3317
3318 // Copy the input fx's group data
3319 TFx *inFx = m_link.m_inputFx.getPointer();
3320
3321 std::list<TFxP>::const_iterator ft, fEnd = m_fxs.end();
3322 for (ft = m_fxs.begin(); ft != fEnd; ++ft) {
3323 TFx *fx = (*ft).getPointer();
3324
3325 FxCommandUndo::cloneGroupStack(inFx, fx);
3326 FxCommandUndo::copyGroupEditLevel(inFx, fx);
3327 }
3328
3329 m_xshHandle->notifyXsheetChanged();
3330 }
3331
3332 //------------------------------------------------------
3333
undo() const3334 void UndoConnectFxs::undo() const {
3335 TXsheet *xsh = m_xshHandle->getXsheet();
3336
3337 // Undo the insert
3338 FxCommandUndo::detachFxs(xsh, m_leftFx, m_rightFx);
3339 FxCommandUndo::attach(xsh, m_link, false);
3340
3341 // Restore the old fxs' group data
3342 for (auto const &groupData : m_undoGroupDatas) {
3343 groupData.restore();
3344 }
3345
3346 UndoDisconnectFxs::undo();
3347 }
3348
3349 //------------------------------------------------------
3350
getHistoryString()3351 QString UndoConnectFxs::getHistoryString() {
3352 return QObject::tr("Connect Fx : %1 - %2")
3353 .arg(QString::fromStdWString(m_leftFx->getName()))
3354 .arg(QString::fromStdWString(m_rightFx->getName()));
3355 }
3356
3357 //======================================================
3358
connectFxs(const Link & link,const std::list<TFxP> & fxs,TXsheetHandle * xshHandle,const QList<QPair<TFxP,TPointD>> & fxPos)3359 void TFxCommand::connectFxs(const Link &link, const std::list<TFxP> &fxs,
3360 TXsheetHandle *xshHandle,
3361 const QList<QPair<TFxP, TPointD>> &fxPos) {
3362 std::unique_ptr<FxCommandUndo> undo(
3363 new UndoConnectFxs(link, fxs, fxPos, xshHandle));
3364 if (undo->isConsistent()) {
3365 undo->redo();
3366 TUndoManager::manager()->add(undo.release());
3367 }
3368 }
3369
3370 //**********************************************************************
3371 // Set Parent command
3372 //**********************************************************************
3373
3374 class SetParentUndo final : public FxCommandUndo {
3375 TFxP m_oldFx, m_newFx, m_parentFx;
3376 int m_parentPort;
3377
3378 bool m_removeFromXsheet;
3379
3380 TXsheetHandle *m_xshHandle;
3381
3382 public:
SetParentUndo(TFx * fx,TFx * parentFx,int parentFxPort,TXsheetHandle * xshHandle)3383 SetParentUndo(TFx *fx, TFx *parentFx, int parentFxPort,
3384 TXsheetHandle *xshHandle)
3385 : m_newFx(fx)
3386 , m_parentFx(parentFx)
3387 , m_parentPort(parentFxPort)
3388 , m_xshHandle(xshHandle) {
3389 initialize();
3390 }
3391
isConsistent() const3392 bool isConsistent() const override { return m_parentFx; }
3393
3394 void redo() const override;
3395 void redo_() const;
3396 void undo() const override;
3397
getSize() const3398 int getSize() const override { return sizeof(*this); }
3399
3400 private:
3401 void initialize();
3402 };
3403
3404 //------------------------------------------------------
3405
initialize()3406 void SetParentUndo::initialize() {
3407 if (!m_parentFx) return;
3408
3409 // NOTE: We cannot store this directly, since it's the actual out that owns
3410 // the actual in, not viceversa
3411 TFx *parentFx = ::getActualIn(m_parentFx.getPointer());
3412
3413 TXsheet *xsh = m_xshHandle->getXsheet();
3414 FxDag *fxDag = xsh->getFxDag();
3415
3416 assert(m_parentPort < parentFx->getInputPortCount());
3417 assert(m_parentPort >= 0);
3418
3419 m_oldFx = parentFx->getInputPort(m_parentPort)->getFx();
3420
3421 m_removeFromXsheet = // This is a bit odd. The legacy behavior of the
3422 (m_newFx && // setParent() (ie connect 2 fxs with a link, I know
3423 (m_newFx->getOutputConnectionCount() ==
3424 0) && // the name is bad) command is that of *removing terminal
3425 fxDag->getTerminalFxs()->containsFx(
3426 m_newFx.getPointer()) && // links* on the input fx (m_newFx in our
3427 // case).
3428 m_parentFx != fxDag->getXsheetFx()); // I've retained this behavior
3429 // since it can be handy
3430 // for users, but only if the xsheet link is the SOLE output
3431 if (::isInsideAMacroFx(m_parentFx.getPointer(), xsh) || // link.
3432 ::isInsideAMacroFx(m_oldFx.getPointer(), xsh) ||
3433 ::isInsideAMacroFx(m_newFx.getPointer(), xsh))
3434 m_parentFx = TFxP();
3435 }
3436
3437 //------------------------------------------------------
3438
redo() const3439 void SetParentUndo::redo() const {
3440 /*
3441 Due to compatibility issues from *schematicnode.cpp files, the "do" operation
3442 cannot
3443 signal changes to the scene/xsheet... but the *re*do must.
3444 */
3445
3446 redo_();
3447 m_xshHandle->notifyXsheetChanged();
3448 }
3449
3450 //------------------------------------------------------
3451
redo_() const3452 void SetParentUndo::redo_() const {
3453 TXsheet *xsh = m_xshHandle->getXsheet();
3454
3455 TFx *parentFx = ::getActualIn(m_parentFx.getPointer());
3456 FxCommandUndo::attach(xsh, m_newFx.getPointer(), parentFx, m_parentPort,
3457 false);
3458
3459 if (m_removeFromXsheet)
3460 xsh->getFxDag()->removeFromXsheet(m_newFx.getPointer());
3461 }
3462
3463 //------------------------------------------------------
3464
undo() const3465 void SetParentUndo::undo() const {
3466 TXsheet *xsh = m_xshHandle->getXsheet();
3467
3468 TFx *parentFx = ::getActualIn(m_parentFx.getPointer());
3469 FxCommandUndo::attach(xsh, m_oldFx.getPointer(), parentFx, m_parentPort,
3470 false);
3471
3472 if (m_removeFromXsheet) xsh->getFxDag()->addToXsheet(m_newFx.getPointer());
3473
3474 m_xshHandle->notifyXsheetChanged();
3475 }
3476
3477 //======================================================
3478
setParent(TFx * fx,TFx * parentFx,int parentFxPort,TXsheetHandle * xshHandle)3479 void TFxCommand::setParent(TFx *fx, TFx *parentFx, int parentFxPort,
3480 TXsheetHandle *xshHandle) {
3481 if (dynamic_cast<TXsheetFx *>(parentFx) || parentFxPort < 0) {
3482 std::unique_ptr<ConnectNodesToXsheetUndo> undo(
3483 new ConnectNodesToXsheetUndo(std::list<TFxP>(1, fx), xshHandle));
3484 if (undo->isConsistent()) {
3485 undo->redo_();
3486 TUndoManager::manager()->add(undo.release());
3487 }
3488 } else {
3489 std::unique_ptr<SetParentUndo> undo(
3490 new SetParentUndo(fx, parentFx, parentFxPort, xshHandle));
3491 if (undo->isConsistent()) {
3492 undo->redo_();
3493 TUndoManager::manager()->add(undo.release());
3494 }
3495 }
3496 }
3497
3498 //**********************************************************************
3499 // Rename Fx command
3500 //**********************************************************************
3501
3502 class UndoRenameFx final : public FxCommandUndo {
3503 TFxP m_fx;
3504 std::wstring m_newName, m_oldName;
3505
3506 TXsheetHandle *m_xshHandle;
3507
3508 public:
UndoRenameFx(TFx * fx,const std::wstring & newName,TXsheetHandle * xshHandle)3509 UndoRenameFx(TFx *fx, const std::wstring &newName, TXsheetHandle *xshHandle)
3510 : m_fx(fx)
3511 , m_newName(newName)
3512 , m_oldName(::getActualIn(fx)->getName())
3513 , m_xshHandle(xshHandle) {
3514 assert(fx);
3515 }
3516
isConsistent() const3517 bool isConsistent() const override { return true; }
3518
redo() const3519 void redo() const override {
3520 redo_();
3521 m_xshHandle->notifyXsheetChanged();
3522 }
3523
redo_() const3524 void redo_() const { ::getActualIn(m_fx.getPointer())->setName(m_newName); }
3525
undo() const3526 void undo() const override {
3527 ::getActualIn(m_fx.getPointer())->setName(m_oldName);
3528 m_xshHandle->notifyXsheetChanged();
3529 }
3530
getSize() const3531 int getSize() const override { return sizeof(*this); }
3532
getHistoryString()3533 QString getHistoryString() override {
3534 return QObject::tr("Rename Fx : %1 > %2")
3535 .arg(QString::fromStdWString(m_oldName))
3536 .arg(QString::fromStdWString(m_newName));
3537 }
3538 };
3539
3540 //======================================================
3541
renameFx(TFx * fx,const std::wstring & newName,TXsheetHandle * xshHandle)3542 void TFxCommand::renameFx(TFx *fx, const std::wstring &newName,
3543 TXsheetHandle *xshHandle) {
3544 if (!fx) return;
3545
3546 std::unique_ptr<UndoRenameFx> undo(new UndoRenameFx(fx, newName, xshHandle));
3547 if (undo->isConsistent()) {
3548 undo->redo_();
3549 TUndoManager::manager()->add(undo.release());
3550 }
3551 }
3552
3553 //**********************************************************************
3554 // Group Fxs command
3555 //**********************************************************************
3556
3557 class UndoGroupFxs : public FxCommandUndo {
3558 public:
3559 struct GroupData {
3560 TFxP m_fx;
3561 mutable int m_groupIndex; //! AKA group \a position (not \a id).
3562
GroupDataUndoGroupFxs::GroupData3563 GroupData(const TFxP &fx, int groupIdx = -1)
3564 : m_fx(fx), m_groupIndex(groupIdx) {}
3565 };
3566
3567 protected:
3568 std::vector<GroupData> m_groupData;
3569 int m_groupId;
3570
3571 TXsheetHandle *m_xshHandle;
3572
3573 public:
UndoGroupFxs(const std::list<TFxP> & fxs,TXsheetHandle * xshHandle)3574 UndoGroupFxs(const std::list<TFxP> &fxs, TXsheetHandle *xshHandle)
3575 : m_groupData(fxs.begin(), fxs.end()), m_xshHandle(xshHandle) {
3576 initialize();
3577 }
3578
isConsistent() const3579 bool isConsistent() const override { return !m_groupData.empty(); }
3580
3581 void redo() const override;
3582 void undo() const override;
3583
getSize() const3584 int getSize() const override { return sizeof(*this); }
3585
getHistoryString()3586 QString getHistoryString() override { return QObject::tr("Group Fx"); }
3587
3588 protected:
UndoGroupFxs(int groupId,TXsheetHandle * xshHandle)3589 UndoGroupFxs(int groupId, TXsheetHandle *xshHandle)
3590 : m_groupId(groupId), m_xshHandle(xshHandle) {}
3591
3592 private:
3593 void initialize();
3594 };
3595
3596 //------------------------------------------------------
3597
initialize()3598 void UndoGroupFxs::initialize() {
3599 struct locals {
3600 inline static bool isXsheetFx(const GroupData &gd) {
3601 return dynamic_cast<TXsheet *>(gd.m_fx.getPointer());
3602 }
3603 };
3604
3605 TXsheet *xsh = m_xshHandle->getXsheet();
3606 FxDag *fxDag = xsh->getFxDag();
3607
3608 // Build a group id for the new group
3609 m_groupId = fxDag->getNewGroupId();
3610
3611 // The xsheet fx must never be grouped
3612 m_groupData.erase(std::remove_if(m_groupData.begin(), m_groupData.end(),
3613 &locals::isXsheetFx),
3614 m_groupData.end());
3615
3616 // Scan for macro fxs. A macro's internal fxs must be added to the group data,
3617 // too
3618 // Yep, this is one of the few fx commands that do not require macro
3619 // explosion.
3620 size_t g, gCount = m_groupData.size();
3621 for (g = 0; g != gCount; ++g) {
3622 if (TMacroFx *macro =
3623 dynamic_cast<TMacroFx *>(m_groupData[g].m_fx.getPointer())) {
3624 const std::vector<TFxP> &internalFxs = macro->getFxs();
3625
3626 std::vector<TFxP>::const_iterator ft, fEnd = internalFxs.end();
3627 for (ft = internalFxs.begin(); ft != fEnd; ++ft)
3628 m_groupData.push_back(*ft);
3629 }
3630 }
3631 }
3632
3633 //------------------------------------------------------
3634
redo() const3635 void UndoGroupFxs::redo() const {
3636 const std::wstring groupName = L"Group " + std::to_wstring(m_groupId);
3637
3638 std::vector<GroupData>::const_iterator gt, gEnd = m_groupData.end();
3639 for (gt = m_groupData.begin(); gt != gEnd; ++gt) {
3640 // Insert the group id in the fx
3641 gt->m_groupIndex = gt->m_fx->getAttributes()->setGroupId(m_groupId);
3642 gt->m_fx->getAttributes()->setGroupName(groupName);
3643 }
3644
3645 m_xshHandle->notifyXsheetChanged();
3646 }
3647
3648 //------------------------------------------------------
3649
undo() const3650 void UndoGroupFxs::undo() const {
3651 std::vector<GroupData>::const_iterator gt, gEnd = m_groupData.end();
3652 for (gt = m_groupData.begin(); gt != gEnd; ++gt) {
3653 TCG_ASSERT(gt->m_groupIndex >= 0, continue);
3654
3655 // Insert the group id in the fx
3656 gt->m_fx->getAttributes()->removeGroupId(gt->m_groupIndex);
3657 gt->m_fx->getAttributes()->removeGroupName(gt->m_groupIndex);
3658
3659 gt->m_groupIndex = -1;
3660 }
3661
3662 m_xshHandle->notifyXsheetChanged();
3663 }
3664
3665 //======================================================
3666
groupFxs(const std::list<TFxP> & fxs,TXsheetHandle * xshHandle)3667 void TFxCommand::groupFxs(const std::list<TFxP> &fxs,
3668 TXsheetHandle *xshHandle) {
3669 std::unique_ptr<FxCommandUndo> undo(new UndoGroupFxs(fxs, xshHandle));
3670 if (undo->isConsistent()) {
3671 undo->redo();
3672 TUndoManager::manager()->add(undo.release());
3673 }
3674 }
3675
3676 //**********************************************************************
3677 // Ungroup Fxs command
3678 //**********************************************************************
3679
3680 class UndoUngroupFxs final : public UndoGroupFxs {
3681 public:
UndoUngroupFxs(int groupId,TXsheetHandle * xshHandle)3682 UndoUngroupFxs(int groupId, TXsheetHandle *xshHandle)
3683 : UndoGroupFxs(groupId, xshHandle) {
3684 initialize();
3685 }
3686
redo() const3687 void redo() const override { UndoGroupFxs::undo(); }
undo() const3688 void undo() const override { UndoGroupFxs::redo(); }
3689
getHistoryString()3690 QString getHistoryString() override { return QObject::tr("Ungroup Fx"); }
3691
3692 private:
3693 void initialize();
3694 };
3695
3696 //------------------------------------------------------
3697
initialize()3698 void UndoUngroupFxs::initialize() {
3699 struct {
3700 UndoUngroupFxs *m_this;
3701
3702 void scanFxForGroup(TFx *fx) {
3703 if (fx) {
3704 const QStack<int> &groupStack = fx->getAttributes()->getGroupIdStack();
3705
3706 int groupIdx =
3707 groupStack.indexOf(m_this->m_groupId); // Returns -1 if not found
3708 if (groupIdx >= 0)
3709 m_this->m_groupData.push_back(GroupData(fx, groupIdx));
3710 }
3711 }
3712
3713 } locals = {this};
3714
3715 TXsheet *xsh = m_xshHandle->getXsheet();
3716 FxDag *fxDag = xsh->getFxDag();
3717
3718 // We must iterate the xsheet's fxs pool and look for fxs with the specified
3719 // group id
3720
3721 // Search column fxs
3722 int c, cCount = xsh->getColumnCount();
3723 for (c = 0; c != cCount; ++c) {
3724 TXshColumn *column = xsh->getColumn(c);
3725 assert(column);
3726
3727 locals.scanFxForGroup(column->getFx());
3728 }
3729
3730 // Search normal fxs (not column ones)
3731 TFxSet *internalFxs = fxDag->getInternalFxs();
3732
3733 int f, fCount = internalFxs->getFxCount();
3734 for (f = 0; f != fCount; ++f) {
3735 TFx *fx = internalFxs->getFx(f);
3736 locals.scanFxForGroup(fx);
3737
3738 if (TMacroFx *macroFx = dynamic_cast<TMacroFx *>(fx)) {
3739 // Search internal macro fxs
3740 const std::vector<TFxP> &fxs = macroFx->getFxs();
3741
3742 std::vector<TFxP>::const_iterator ft, fEnd = fxs.end();
3743 for (ft = fxs.begin(); ft != fEnd; ++ft)
3744 locals.scanFxForGroup(ft->getPointer());
3745 }
3746 }
3747
3748 // Search output fxs
3749 int o, oCount = fxDag->getOutputFxCount();
3750 for (o = 0; o != oCount; ++o) locals.scanFxForGroup(fxDag->getOutputFx(o));
3751 }
3752
3753 //======================================================
3754
ungroupFxs(int groupId,TXsheetHandle * xshHandle)3755 void TFxCommand::ungroupFxs(int groupId, TXsheetHandle *xshHandle) {
3756 std::unique_ptr<FxCommandUndo> undo(new UndoUngroupFxs(groupId, xshHandle));
3757 if (undo->isConsistent()) {
3758 undo->redo();
3759 TUndoManager::manager()->add(undo.release());
3760 }
3761 }
3762
3763 //**********************************************************************
3764 // Rename Group command
3765 //**********************************************************************
3766
3767 class UndoRenameGroup final : public FxCommandUndo {
3768 std::vector<UndoGroupFxs::GroupData> m_groupData;
3769 std::wstring m_oldGroupName, m_newGroupName;
3770
3771 TXsheetHandle *m_xshHandle;
3772
3773 public:
UndoRenameGroup(const std::list<TFxP> & fxs,const std::wstring & newGroupName,bool fromEditor,TXsheetHandle * xshHandle)3774 UndoRenameGroup(const std::list<TFxP> &fxs, const std::wstring &newGroupName,
3775 bool fromEditor, TXsheetHandle *xshHandle)
3776 : m_groupData(fxs.begin(), fxs.end())
3777 , m_newGroupName(newGroupName)
3778 , m_xshHandle(xshHandle) {
3779 initialize(fromEditor);
3780 }
3781
isConsistent() const3782 bool isConsistent() const override { return !m_groupData.empty(); }
3783
3784 void redo() const override;
3785 void undo() const override;
3786
3787 void redo_() const;
3788
getSize() const3789 int getSize() const override { return sizeof(*this); }
3790
getHistoryString()3791 QString getHistoryString() override {
3792 return QObject::tr("Rename Group : %1 > %2")
3793 .arg(QString::fromStdWString(m_oldGroupName))
3794 .arg(QString::fromStdWString(m_newGroupName));
3795 }
3796
3797 private:
3798 void initialize(bool fromEditor);
3799 };
3800
3801 //------------------------------------------------------
3802
initialize(bool fromEditor)3803 void UndoRenameGroup::initialize(bool fromEditor) {
3804 struct locals {
3805 inline static bool isInvalid(const UndoGroupFxs::GroupData &gd) {
3806 return (gd.m_groupIndex < 0);
3807 }
3808 };
3809
3810 if (!m_groupData.empty()) {
3811 m_oldGroupName =
3812 m_groupData.front().m_fx->getAttributes()->getGroupName(fromEditor);
3813
3814 // Extract group indices
3815 std::vector<UndoGroupFxs::GroupData>::const_iterator gt,
3816 gEnd = m_groupData.end();
3817 for (gt = m_groupData.begin(); gt != gEnd; ++gt) {
3818 const QStack<std::wstring> &groupNamesStack =
3819 gt->m_fx->getAttributes()->getGroupNameStack();
3820
3821 gt->m_groupIndex =
3822 groupNamesStack.indexOf(m_oldGroupName); // Returns -1 if not found
3823 assert(gt->m_groupIndex >= 0);
3824 }
3825 }
3826
3827 m_groupData.erase(std::remove_if(m_groupData.begin(), m_groupData.end(),
3828 &locals::isInvalid),
3829 m_groupData.end());
3830 }
3831
3832 //------------------------------------------------------
3833
redo() const3834 void UndoRenameGroup::redo() const {
3835 redo_();
3836 m_xshHandle->notifyXsheetChanged();
3837 }
3838
3839 //------------------------------------------------------
3840
redo_() const3841 void UndoRenameGroup::redo_() const {
3842 std::vector<UndoGroupFxs::GroupData>::const_iterator gt,
3843 gEnd = m_groupData.end();
3844 for (gt = m_groupData.begin(); gt != gEnd; ++gt) {
3845 gt->m_fx->getAttributes()->removeGroupName(gt->m_groupIndex);
3846 gt->m_fx->getAttributes()->setGroupName(m_newGroupName, gt->m_groupIndex);
3847 }
3848 }
3849
3850 //------------------------------------------------------
3851
undo() const3852 void UndoRenameGroup::undo() const {
3853 std::vector<UndoGroupFxs::GroupData>::const_iterator gt,
3854 gEnd = m_groupData.end();
3855 for (gt = m_groupData.begin(); gt != gEnd; ++gt) {
3856 gt->m_fx->getAttributes()->removeGroupName(gt->m_groupIndex);
3857 gt->m_fx->getAttributes()->setGroupName(m_oldGroupName, gt->m_groupIndex);
3858 }
3859
3860 m_xshHandle->notifyXsheetChanged();
3861 }
3862
3863 //======================================================
3864
renameGroup(const std::list<TFxP> & fxs,const std::wstring & name,bool fromEditor,TXsheetHandle * xshHandle)3865 void TFxCommand::renameGroup(const std::list<TFxP> &fxs,
3866 const std::wstring &name, bool fromEditor,
3867 TXsheetHandle *xshHandle) {
3868 std::unique_ptr<UndoRenameGroup> undo(
3869 new UndoRenameGroup(fxs, name, fromEditor, xshHandle));
3870 if (undo->isConsistent()) {
3871 undo->redo_(); // Same schematic nodes problem as above... :(
3872 TUndoManager::manager()->add(undo.release());
3873 }
3874 }
3875