1 
2 
3 // TnzCore includes
4 #include "tstream.h"
5 
6 // TnzBase includes
7 #include "toutputproperties.h"
8 #include "tfx.h"
9 #include "tparamcontainer.h"
10 #include "tparamset.h"
11 #include "tfxattributes.h"
12 
13 // TnzLib includes
14 #include "toonz/fxdag.h"
15 #include "toonz/txshchildlevel.h"
16 #include "toonz/txshcell.h"
17 #include "toonz/observer.h"
18 #include "toonz/controlpointobserver.h"
19 #include "toonz/tcolumnfx.h"
20 #include "toonz/tcolumnfxset.h"
21 #include "toonz/txshlevelcolumn.h"
22 #include "toonz/txshpalettecolumn.h"
23 #include "toonz/txshzeraryfxcolumn.h"
24 #include "toonz/txshsoundcolumn.h"
25 #include "toonz/sceneproperties.h"
26 #include "toonz/toonzscene.h"
27 #include "toonz/columnfan.h"
28 #include "toonz/txshleveltypes.h"
29 #include "toonz/txshnoteset.h"
30 #include "toonz/txshsimplelevel.h"
31 #include "toonz/stage.h"
32 #include "toonz/textureutils.h"
33 #include "xshhandlemanager.h"
34 #include "orientation.h"
35 #include "toonz/expressionreferencemonitor.h"
36 
37 #include "toonz/txsheet.h"
38 #include "toonz/preferences.h"
39 
40 // STD includes
41 #include <set>
42 
43 using namespace std;
44 
45 DEFINE_CLASS_CODE(TXsheet, 18)
46 
47 //-----------------------------------------------------------------------------
48 namespace {
49 //-----------------------------------------------------------------------------
50 
getColumnDefaultName(TXsheet * xsh,int col,QString oldName)51 string getColumnDefaultName(TXsheet *xsh, int col, QString oldName) {
52   TXshColumn *column = xsh->getColumn(col);
53   if (column) {
54     TXshLevelColumn *lc = column->getLevelColumn();
55     if (lc) {
56       int r0, r1;
57       if (lc->getRange(r0, r1)) {
58         TXshCell cell = lc->getCell(r0);
59         assert(!cell.isEmpty());
60         TXshLevel *level = cell.m_level.getPointer();
61         if (level) {
62           bool isNumber = true;
63           oldName.right(oldName.size() - 3).toInt(&isNumber);
64           if (oldName.left(3) == "Col" && isNumber)
65             return ::to_string(level->getName());
66           else
67             return "";
68         }
69       }
70     }
71   }
72   return "Col" + std::to_string(col + 1);
73 }
74 
75 //-----------------------------------------------------------------------------
76 
setColumnName(TXsheet * xsh,int col)77 void setColumnName(TXsheet *xsh, int col) {
78   TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(col));
79   QString oldName   = QString::fromStdString(obj->getName());
80   string name       = getColumnDefaultName(xsh, col, oldName);
81   if (!name.empty()) obj->setName(name);
82 }
83 
84 }  // namespace
85 
86 //=============================================================================
87 // TXsheetImp
88 
89 struct TXsheet::TXsheetImp {
90   unsigned long m_id;  //!< The xsheet instance's unique identifier
91 
92   TColumnSetT<TXshColumn> m_columnSet;
93   TStageObjectTree *m_pegTree;
94   FxDag *m_fxDag;
95 
96   int m_frameCount;
97   int m_soloColumn;
98   int m_viewColumn;
99 
100   TSoundTrackP m_mixedSound;
101   ColumnFan m_columnFans[Orientations::COUNT];
102   XshHandleManager *m_handleManager;
103   ToonzScene *m_scene;
104 
105   ExpressionReferenceMonitor *m_expRefMonitor;
106 
107 public:
108   TXsheetImp();
109   ~TXsheetImp();
110 
newIdentifierTXsheet::TXsheetImp111   static inline unsigned long newIdentifier() {
112     static unsigned long currentId = 0;
113     return ++currentId;
114   }
115 
116   void copyFoldedState();
117 
118 private:
119   void initColumnFans();
120 
121   // not implemented
122   TXsheetImp(const TXsheetImp &);
123   TXsheetImp &operator=(const TXsheetImp &);
124 };
125 
126 //-----------------------------------------------------------------------------
127 
SoundProperties()128 TXsheet::SoundProperties::SoundProperties()
129     : m_fromFrame(-1), m_toFrame(-1), m_frameRate(-1), m_isPreview(false) {}
130 
131 //-----------------------------------------------------------------------------
132 
~SoundProperties()133 TXsheet::SoundProperties::~SoundProperties() {}
134 
135 //-----------------------------------------------------------------------------
136 
operator ==(const SoundProperties & c) const137 inline bool TXsheet::SoundProperties::operator==(
138     const SoundProperties &c) const {
139   return m_fromFrame == c.m_fromFrame && m_toFrame == c.m_toFrame &&
140          m_frameRate == c.m_frameRate && m_isPreview == c.m_isPreview;
141 }
142 
operator !=(const SoundProperties & c) const143 inline bool TXsheet::SoundProperties::operator!=(
144     const SoundProperties &c) const {
145   return !(*this == c);
146 }
147 
148 //-----------------------------------------------------------------------------
149 
TXsheetImp()150 TXsheet::TXsheetImp::TXsheetImp()
151     : m_id(newIdentifier())
152     , m_pegTree(new TStageObjectTree)
153     , m_handleManager(0)
154     , m_fxDag(new FxDag())
155     , m_frameCount(0)
156     , m_soloColumn(-1)
157     , m_viewColumn(-1)
158     , m_mixedSound(0)
159     , m_scene(0)
160     , m_expRefMonitor(new ExpressionReferenceMonitor()) {
161   initColumnFans();
162 }
163 
164 //-----------------------------------------------------------------------------
165 
~TXsheetImp()166 TXsheet::TXsheetImp::~TXsheetImp() {
167   assert(m_pegTree);
168   assert(m_fxDag);
169   assert(m_handleManager);
170   delete m_pegTree;
171   delete m_fxDag;
172   delete m_handleManager;
173 }
174 
175 //-----------------------------------------------------------------------------
176 
initColumnFans()177 void TXsheet::TXsheetImp::initColumnFans() {
178   for (auto o : Orientations::all()) {
179     int index = o->dimension(PredefinedDimension::INDEX);
180     m_columnFans[index].setDimensions(
181         o->dimension(PredefinedDimension::LAYER),
182         o->dimension(PredefinedDimension::CAMERA_LAYER));
183   }
184 }
185 
copyFoldedState()186 void TXsheet::TXsheetImp::copyFoldedState() {
187   for (int i = 1; i < Orientations::COUNT; i++)
188     m_columnFans[i].copyFoldedStateFrom(m_columnFans[0]);
189 }
190 
191 //=============================================================================
192 // TXsheet
193 
TXsheet()194 TXsheet::TXsheet()
195     : TSmartObject(m_classCode)
196     , m_player(0)
197     , m_imp(new TXsheet::TXsheetImp)
198     , m_notes(new TXshNoteSet())
199     , m_cameraColumnIndex(0)
200     , m_observer(nullptr) {
201   // extern TSyntax::Grammar *createXsheetGrammar(TXsheet*);
202   m_soundProperties      = new TXsheet::SoundProperties();
203   m_imp->m_handleManager = new XshHandleManager(this);
204   m_imp->m_pegTree->setHandleManager(m_imp->m_handleManager);
205   m_imp->m_pegTree->createGrammar(this);
206 
207   // Dummy camera column
208   m_cameraColumn          = TXshColumn::createEmpty(0);
209   m_cameraColumn->m_index = -1;
210   m_cameraColumn->setXsheet(this);
211 }
212 
213 //-----------------------------------------------------------------------------
214 
~TXsheet()215 TXsheet::~TXsheet() {
216   texture_utils::invalidateTextures(this);
217 
218   assert(m_imp);
219   if (m_notes) delete m_notes;
220   if (m_soundProperties) delete m_soundProperties;
221 }
222 
223 //-----------------------------------------------------------------------------
224 
id() const225 unsigned long TXsheet::id() const { return m_imp->m_id; }
226 
227 //-----------------------------------------------------------------------------
228 
getFrameCount() const229 int TXsheet::getFrameCount() const { return m_imp->m_frameCount; }
230 
231 //-----------------------------------------------------------------------------
232 
getCell(int row,int col) const233 const TXshCell &TXsheet::getCell(int row, int col) const {
234   return getCell(CellPosition(row, col));
235 }
236 
getCell(const CellPosition & pos) const237 const TXshCell &TXsheet::getCell(const CellPosition &pos) const {
238   static const TXshCell emptyCell;
239 
240   TXshColumnP column = m_imp->m_columnSet.getColumn(pos.layer());
241   if (!column) return emptyCell;
242   TXshCellColumn *xshColumn = column->getCellColumn();
243   if (!xshColumn) return emptyCell;
244   return xshColumn->getCell(pos.frame());
245 }
246 
247 //-----------------------------------------------------------------------------
248 
setCell(int row,int col,const TXshCell & cell)249 bool TXsheet::setCell(int row, int col, const TXshCell &cell) {
250   if (row < 0 || col < 0) return false;
251 
252   bool wasColumnEmpty = isColumnEmpty(col);
253   TXshCellColumn *cellColumn;
254 
255   if (!cell.isEmpty()) {
256     TXshLevel *level = cell.m_level.getPointer();
257     assert(level);
258 
259     int levelType               = level->getType();
260     TXshColumn::ColumnType type = TXshColumn::eLevelType;
261 
262     if (levelType == SND_XSHLEVEL)
263       type = TXshColumn::eSoundType;
264     else if (levelType == SND_TXT_XSHLEVEL)
265       type = TXshColumn::eSoundTextType;
266     else if (levelType == PLT_XSHLEVEL)
267       type = TXshColumn::ePaletteType;
268     else if (levelType == ZERARYFX_XSHLEVEL)
269       type = TXshColumn::eZeraryFxType;
270     else if (levelType == MESH_XSHLEVEL)
271       type = TXshColumn::eMeshType;
272 
273     cellColumn = touchColumn(col, type)->getCellColumn();
274   } else {
275     TXshColumn *column = getColumn(col);
276     cellColumn         = column ? column->getCellColumn() : 0;
277   }
278 
279   if (!cellColumn || cellColumn->isLocked()) return false;
280 
281   cellColumn->setXsheet(this);
282 
283   if (!cellColumn->setCell(row, cell)) {
284     if (wasColumnEmpty) {
285       removeColumn(col);
286       insertColumn(col);
287     }
288 
289     return false;
290   }
291 
292   TFx *fx = cellColumn->getFx();
293   if (wasColumnEmpty && fx && fx->getOutputConnectionCount() == 0 &&
294       cellColumn->getPaletteColumn() == 0)
295     getFxDag()->addToXsheet(fx);
296 
297   if (cell.isEmpty())
298     updateFrameCount();
299   else if (row >= m_imp->m_frameCount)
300     m_imp->m_frameCount = row + 1;
301 
302   TNotifier::instance()->notify(TXsheetChange());
303 
304   return true;
305 }
306 
307 //-----------------------------------------------------------------------------
308 
getCells(int row,int col,int rowCount,TXshCell cells[]) const309 void TXsheet::getCells(int row, int col, int rowCount, TXshCell cells[]) const {
310   static const TXshCell emptyCell;
311   int i;
312   TXshColumnP column = m_imp->m_columnSet.getColumn(col);
313   if (!column) {
314     for (i = 0; i < rowCount; i++) cells[i] = emptyCell;
315     return;
316   }
317   TXshCellColumn *xshColumn = column->getCellColumn();
318   if (!xshColumn) {
319     for (i = 0; i < rowCount; i++) cells[i] = emptyCell;
320     return;
321   }
322   xshColumn->getCells(row, rowCount, cells);
323 }
324 
325 //-----------------------------------------------------------------------------
326 
setCells(int row,int col,int rowCount,const TXshCell cells[])327 bool TXsheet::setCells(int row, int col, int rowCount, const TXshCell cells[]) {
328   static const TXshCell emptyCell;
329   int i = 0;
330   while (i < rowCount && cells[i].isEmpty()) i++;
331 
332   // inserito da Elisa verso novembre 2009.
333   // cosi' ha il difetto che se assegno celle vuote non fa nulla
334   // per ora lo commento. bisogna indagare se questo rompe qualcosa
335 
336   // ho modificato il seguito per gestire il caso in cui i>=rowCount
337   // => niente livelli dentro cells
338 
339   // if(i>=rowCount)
340   //  return false;
341 
342   TXshColumn::ColumnType type = TXshColumn::eLevelType;
343   if (i < rowCount) {
344     TXshLevel *level = cells[i].m_level.getPointer();
345     int levelType    = level->getType();
346 
347     if (levelType == SND_XSHLEVEL)
348       type = TXshColumn::eSoundType;
349     else if (levelType == SND_TXT_XSHLEVEL)
350       type = TXshColumn::eSoundTextType;
351     else if (levelType == PLT_XSHLEVEL)
352       type = TXshColumn::ePaletteType;
353     else if (levelType == ZERARYFX_XSHLEVEL)
354       type = TXshColumn::eZeraryFxType;
355     else if (levelType == MESH_XSHLEVEL)
356       type = TXshColumn::eMeshType;
357   }
358   bool wasColumnEmpty = isColumnEmpty(col);
359   if (col < 0) return false;
360   TXshCellColumn *column = touchColumn(col, type)->getCellColumn();
361   if (!column) return false;
362 
363   int oldColRowCount = column->getMaxFrame() + 1;
364   bool ret           = column->setCells(row, rowCount, cells);
365   if (!ret || column->isLocked()) {
366     if (wasColumnEmpty) {
367       removeColumn(col);
368       insertColumn(col);
369     }
370     return false;
371   }
372   int newColRowCount = column->getMaxFrame() + 1;
373 
374   TFx *fx = column->getFx();
375   if (wasColumnEmpty && fx && fx->getOutputConnectionCount() == 0)
376     getFxDag()->addToXsheet(fx);
377   column->setXsheet(this);
378 
379   if (newColRowCount > m_imp->m_frameCount)
380     m_imp->m_frameCount = newColRowCount;
381   else {
382     if (oldColRowCount == m_imp->m_frameCount &&
383         newColRowCount < m_imp->m_frameCount)
384       updateFrameCount();
385   }
386 
387   return true;
388 }
389 
390 //-----------------------------------------------------------------------------
391 
insertCells(int row,int col,int rowCount)392 void TXsheet::insertCells(int row, int col, int rowCount) {
393   TXshColumnP column = m_imp->m_columnSet.getColumn(col);
394   if (!column || column->isLocked()) return;
395   TXshCellColumn *xshColumn = column->getCellColumn();
396   if (!xshColumn) return;
397   xshColumn->insertEmptyCells(row, rowCount);
398   // aggiorno il frame count
399   int fc = xshColumn->getMaxFrame() + 1;
400   if (fc > m_imp->m_frameCount) m_imp->m_frameCount = fc;
401 }
402 
403 //-----------------------------------------------------------------------------
404 
removeCells(int row,int col,int rowCount)405 void TXsheet::removeCells(int row, int col, int rowCount) {
406   TXshColumnP column = m_imp->m_columnSet.getColumn(col);
407   if (!column || column->isLocked()) return;
408 
409   TXshCellColumn *xshCellColumn = column->getCellColumn();
410   if (!xshCellColumn) return;
411 
412   int oldColRowCount = xshCellColumn->getMaxFrame() + 1;
413   xshCellColumn->removeCells(row, rowCount);
414 
415   // aggiornamento framecount
416   if (oldColRowCount == m_imp->m_frameCount) updateFrameCount();
417 
418   TNotifier::instance()->notify(TXsheetChange());
419 }
420 
421 //-----------------------------------------------------------------------------
422 
clearCells(int row,int col,int rowCount)423 void TXsheet::clearCells(int row, int col, int rowCount) {
424   const TXshColumnP &column = m_imp->m_columnSet.getColumn(col);
425   if (!column || column->isLocked()) return;
426 
427   TXshCellColumn *xshCellColumn = column->getCellColumn();
428   if (!xshCellColumn) return;
429 
430   int oldColRowCount = xshCellColumn->getMaxFrame() + 1;
431   xshCellColumn->clearCells(row, rowCount);
432 
433   // aggiornamento framecount
434   if (oldColRowCount == m_imp->m_frameCount) updateFrameCount();
435 }
436 
437 //-----------------------------------------------------------------------------
438 
clearAll()439 void TXsheet::clearAll() {
440   int c0 = 0, c1 = m_imp->m_columnSet.getColumnCount() - 1;
441   int r0 = 0, r1 = getFrameCount() - 1;
442   m_imp->m_columnSet.clear();
443 
444   if (m_imp->m_pegTree) {
445     delete m_imp->m_pegTree;
446     m_imp->m_pegTree = new TStageObjectTree();
447     m_imp->m_pegTree->setHandleManager(m_imp->m_handleManager);
448     m_imp->m_pegTree->createGrammar(this);
449   }
450 
451   if (m_imp->m_fxDag) {
452     delete m_imp->m_fxDag;
453     m_imp->m_fxDag = new FxDag();
454   }
455 
456   m_imp->m_frameCount = 0;
457   m_imp->m_mixedSound = 0;
458 }
459 
460 //-----------------------------------------------------------------------------
461 
getCellRange(int col,int & r0,int & r1) const462 int TXsheet::getCellRange(int col, int &r0, int &r1) const {
463   r0                 = 0;
464   r1                 = -1;
465   TXshColumnP column = m_imp->m_columnSet.getColumn(col);
466   if (!column) return 0;
467   TXshCellColumn *cellColumn = column->getCellColumn();
468   if (!cellColumn) return 0;
469   return cellColumn->getRange(r0, r1);
470 }
471 
472 //-----------------------------------------------------------------------------
473 
getMaxFrame(int col) const474 int TXsheet::getMaxFrame(int col) const {
475   TXshColumnP column = m_imp->m_columnSet.getColumn(col);
476   if (!column) return 0;
477   return column->getMaxFrame();
478 }
479 
480 //-----------------------------------------------------------------------------
481 
isColumnEmpty(int col) const482 bool TXsheet::isColumnEmpty(int col) const {
483   TXshColumnP column = m_imp->m_columnSet.getColumn(col);
484   return column ? column->isEmpty() : true;
485 }
486 
487 //-----------------------------------------------------------------------------
488 
getUsedLevels(set<TXshLevel * > & levels) const489 void TXsheet::getUsedLevels(set<TXshLevel *> &levels) const {
490   set<const TXsheet *> visitedXshs;
491   vector<const TXsheet *> todoXshs;
492 
493   visitedXshs.insert(this);
494   todoXshs.push_back(this);
495 
496   while (!todoXshs.empty()) {
497     const TXsheet *xsh = todoXshs.back();
498     todoXshs.pop_back();
499 
500     int c0 = 0, c1 = xsh->getColumnCount() - 1;
501     for (int c = c0; c <= c1; ++c) {
502       TXshColumnP column = const_cast<TXsheet *>(xsh)->getColumn(c);
503       if (!column) continue;
504 
505       TXshCellColumn *cellColumn = column->getCellColumn();
506       if (!cellColumn) continue;
507 
508       int r0, r1;
509       if (!cellColumn->getRange(r0, r1)) continue;
510 
511       TXshLevel *level = 0;
512       for (int r = r0; r <= r1; r++) {
513         TXshCell cell = cellColumn->getCell(r);
514         if (cell.isEmpty() || !cell.m_level) continue;
515 
516         if (level != cell.m_level.getPointer()) {
517           level = cell.m_level.getPointer();
518           levels.insert(level);
519           if (level->getChildLevel()) {
520             TXsheet *childXsh = level->getChildLevel()->getXsheet();
521             if (visitedXshs.count(childXsh) == 0) {
522               visitedXshs.insert(childXsh);
523               todoXshs.push_back(childXsh);
524             }
525           }
526         }
527       }
528     }
529   }
530 }
531 
532 //-----------------------------------------------------------------------------
533 
isLevelUsed(TXshLevel * level) const534 bool TXsheet::isLevelUsed(TXshLevel *level) const {
535   set<TXshLevel *> levels;
536   getUsedLevels(levels);
537   return levels.count(level) > 0;
538 }
539 
540 //-----------------------------------------------------------------------------
541 
getStageObject(const TStageObjectId & id) const542 TStageObject *TXsheet::getStageObject(const TStageObjectId &id) const {
543   assert(id != TStageObjectId::NoneId);
544   return m_imp->m_pegTree->getStageObject(id);
545 }
546 
547 //-----------------------------------------------------------------------------
548 
getStageObjectTree() const549 TStageObjectTree *TXsheet::getStageObjectTree() const {
550   return m_imp->m_pegTree;
551 }
552 
553 //-----------------------------------------------------------------------------
554 
getPlacement(const TStageObjectId & id,int frame) const555 TAffine TXsheet::getPlacement(const TStageObjectId &id, int frame) const {
556   assert(id != TStageObjectId::NoneId);
557   return m_imp->m_pegTree->getStageObject(id)->getPlacement(frame);
558 }
559 
560 //-----------------------------------------------------------------------------
561 
getZ(const TStageObjectId & id,int frame) const562 double TXsheet::getZ(const TStageObjectId &id, int frame) const {
563   assert(id != TStageObjectId::NoneId);
564   return m_imp->m_pegTree->getStageObject(id)->getZ(frame);
565 }
566 
567 //-----------------------------------------------------------------------------
568 
getNoScaleZ(const TStageObjectId & id) const569 double TXsheet::getNoScaleZ(const TStageObjectId &id) const {
570   assert(id != TStageObjectId::NoneId);
571   return m_imp->m_pegTree->getStageObject(id)->getNoScaleZ();
572 }
573 
574 //-----------------------------------------------------------------------------
575 
getParentPlacement(const TStageObjectId & id,int frame) const576 TAffine TXsheet::getParentPlacement(const TStageObjectId &id, int frame) const {
577   assert(id != TStageObjectId::NoneId);
578   return m_imp->m_pegTree->getStageObject(id)->getParentPlacement(frame);
579 }
580 
581 //-----------------------------------------------------------------------------
582 
getCenter(const TStageObjectId & id,int frame) const583 TPointD TXsheet::getCenter(const TStageObjectId &id, int frame) const {
584   assert(id != TStageObjectId::NoneId);
585   return m_imp->m_pegTree->getStageObject(id)->getCenter(frame);
586 }
587 
588 //-----------------------------------------------------------------------------
589 
setCenter(const TStageObjectId & id,int frame,const TPointD & center)590 void TXsheet::setCenter(const TStageObjectId &id, int frame,
591                         const TPointD &center) {
592   assert(id != TStageObjectId::NoneId);
593   m_imp->m_pegTree->getStageObject(id)->setCenter(frame, center);
594 }
595 
596 //-----------------------------------------------------------------------------
597 
getStageObjectParent(const TStageObjectId & id)598 TStageObjectId TXsheet::getStageObjectParent(const TStageObjectId &id) {
599   assert(id != TStageObjectId::NoneId);
600   return m_imp->m_pegTree->getStageObject(id)->getParent();
601 }
602 
603 //-----------------------------------------------------------------------------
604 
setStageObjectParent(const TStageObjectId & id,const TStageObjectId & parentId)605 void TXsheet::setStageObjectParent(const TStageObjectId &id,
606                                    const TStageObjectId &parentId) {
607   assert(id != TStageObjectId::NoneId);
608   m_imp->m_pegTree->getStageObject(id)->setParent(parentId);
609 }
610 
611 //-----------------------------------------------------------------------------
612 
hasChildren(const TStageObjectId & id) const613 bool TXsheet::hasChildren(const TStageObjectId &id) const {
614   assert(id != TStageObjectId::NoneId);
615   return m_imp->m_pegTree->getStageObject(id)->hasChildren();
616 }
617 
618 //-----------------------------------------------------------------------------
619 
getCameraAff(int frame) const620 TAffine TXsheet::getCameraAff(int frame) const {
621   TStageObjectId cameraId = getStageObjectTree()->getCurrentCameraId();
622   TAffine cameraAff       = getPlacement(cameraId, frame);
623   double cameraZ          = getZ(cameraId, frame);
624   TAffine aff             = cameraAff * TScale((1000 + cameraZ) / 1000);
625   return aff;
626 }
627 
628 //-----------------------------------------------------------------------------
629 
reverseCells(int r0,int c0,int r1,int c1)630 void TXsheet::reverseCells(int r0, int c0, int r1, int c1) {
631   int rowCount = r1 - r0;
632   if (rowCount < 0 || c1 - c0 < 0) return;
633 
634   for (int j = c0; j <= c1; j++) {
635     int i1, i2;
636     for (i1 = r0, i2 = r1; i1 < i2; i1++, i2--) {
637       TXshCell app1 = getCell(CellPosition(i1, j));
638       TXshCell app2 = getCell(CellPosition(i2, j));
639       setCell(i1, j, app2);
640       setCell(i2, j, app1);
641     }
642   }
643 }
644 
645 //-----------------------------------------------------------------------------
646 
swingCells(int r0,int c0,int r1,int c1)647 void TXsheet::swingCells(int r0, int c0, int r1, int c1) {
648   int rowCount = r1 - r0;
649   if (rowCount < 0 || c1 - c0 < 0) return;
650   int r0Mod = r1 + 1;
651   for (int c = c0; c <= c1; ++c) insertCells(r0Mod, c, rowCount);
652 
653   for (int j = c0; j <= c1; j++) {
654     for (int i1 = r0Mod, i2 = r1 - 1; i2 >= r0; i1++, i2--) {
655       TXshCell cell = getCell(CellPosition(i2, j));
656       setCell(i1, j, cell);
657     }
658   }
659 }
660 
661 //-----------------------------------------------------------------------------
662 
incrementCells(int r0,int c0,int r1,int c1,vector<std::pair<TRect,TXshCell>> & forUndo)663 bool TXsheet::incrementCells(int r0, int c0, int r1, int c1,
664                              vector<std::pair<TRect, TXshCell>> &forUndo) {
665   for (int j = c0; j <= c1; j++) {
666     int i = r0;
667     while (getCell(CellPosition(i, j)).isEmpty() && i <= r1 - 1) i++;
668 
669     for (; i <= r1 - 1; i++) {
670       if (getCell(CellPosition(i + 1, j)).isEmpty()) break;
671       const TXshCell &ce1 = getCell(CellPosition(i, j)),
672                      &ce2 = getCell(CellPosition(i + 1, j));
673       if (ce2.getSimpleLevel() != ce1.getSimpleLevel() ||
674           ce2.getFrameId().getNumber() < ce1.getFrameId().getNumber())
675         return false;
676     }
677     i = r0;
678     while (getCell(CellPosition(i, j)).isEmpty() && i <= r1 - 1) i++;
679     int count;
680     for (; i <= r1 - 1; i++) {
681       count = 1;
682       if (getCell(CellPosition(i + 1, j)).isEmpty()) continue;
683 
684       int frame1 = getCell(CellPosition(i, j)).getFrameId().getNumber();
685       if (frame1 == -1) break;
686       while (!getCell(CellPosition(i + 1, j)).isEmpty() &&
687              getCell(CellPosition(i + 1, j)).getFrameId().getNumber() ==
688                  getCell(CellPosition(i, j)).getFrameId().getNumber())
689         i++, count++;
690 
691       int frame2 = getCell(CellPosition(i + 1, j)).getFrameId().getNumber();
692       if (frame2 == -1) break;
693 
694       if (frame1 + count == frame2)
695         continue;
696       else if (frame1 + count < frame2)  // add
697       {
698         int numCells = frame2 - frame1 - count;
699         insertCells(i + 1, j, numCells);
700         forUndo.push_back(std::pair<TRect, TXshCell>(
701             TRect(i + 1, j, i + 1 + numCells - 1, j), TXshCell()));
702         for (int k = 1; k <= numCells; k++)
703           setCell(i + k, j, getCell(CellPosition(i, j)));
704         i += numCells;
705         r1 += numCells;
706       } else  // remove
707       {
708         int numCells = count - frame2 + frame1;
709         i            = i - numCells;
710         forUndo.push_back(
711             std::pair<TRect, TXshCell>(TRect(i + 1, j, i + 1 + numCells - 1, j),
712                                        getCell(CellPosition(i + 1, j))));
713         removeCells(i + 1, j, numCells);
714         r1 -= numCells;
715       }
716     }
717   }
718   return true;
719 }
720 
721 //-----------------------------------------------------------------------------
722 
duplicateCells(int r0,int c0,int r1,int c1,int upTo)723 void TXsheet::duplicateCells(int r0, int c0, int r1, int c1, int upTo) {
724   assert(upTo >= r1 + 1);
725   int chunk = r1 - r0 + 1;
726 
727   for (int j = c0; j <= c1; j++) {
728     insertCells(r1 + 1, j, upTo - (r1 + 1) + 1);
729     for (int i = r1 + 1; i <= upTo; i++)
730       setCell(i, j, getCell(CellPosition(r0 + ((i - (r1 + 1)) % chunk), j)));
731   }
732 }
733 
734 //-----------------------------------------------------------------------------
735 
stepCells(int r0,int c0,int r1,int c1,int type)736 void TXsheet::stepCells(int r0, int c0, int r1, int c1, int type) {
737   int nr = r1 - r0 + 1;
738   int nc = c1 - c0 + 1;
739   if (nr < 1 || nc <= 0) return;
740   int size = nr * nc;
741   std::unique_ptr<TXshCell[]> cells(new TXshCell[size]);
742   if (!cells) return;
743   // salvo il contenuto delle celle in cells
744   int k = 0;
745   for (int r = r0; r <= r1; r++)
746     for (int c = c0; c <= c1; c++) {
747       cells[k++] = getCell(CellPosition(r, c));
748     }
749 
750   int nrows = nr * (type - 1);
751 
752   for (int c = c0; c <= c1; ++c) insertCells(r1 + 1, c, nrows);
753 
754   for (int j = c0; j <= c1; j++) {
755     int i, k;
756     for (i = r0, k = j - c0; k < size; k += nc) {
757       for (int i1 = 0; i1 < type; i1++) {
758         if (cells[k].isEmpty())
759           clearCells(i + i1, j);
760         else
761           setCell(i + i1, j, cells[k]);
762       }
763       i += type;  // dipende dal tipo di step (2 o 3 per ora)
764     }
765   }
766 }
767 
768 //-----------------------------------------------------------------------------
769 
increaseStepCells(int r0,int c0,int & r1,int c1)770 void TXsheet::increaseStepCells(int r0, int c0, int &r1, int c1) {
771   int c, size = r1 - r0 + 1;
772   QList<int> ends;
773   for (c = c0; c <= c1; c++) {
774     int r = r0, i = 0, rEnd = r1;
775     while (r <= rEnd) {
776       TXshCell cell = getCell(CellPosition(r, c));
777       if (!cell.isEmpty()) {
778         insertCells(r, c);
779         setCell(r, c, cell);
780         rEnd++;
781         r++;
782         while (cell == getCell(CellPosition(r, c)) && r <= rEnd) r++;
783       } else
784         r++;
785       i++;
786     }
787     ends.append(rEnd);
788   }
789   if (ends.isEmpty()) return;
790   // controllo se devo cambiare la selezione
791   bool allIncreaseIsEqual = true;
792   for (c = 0; c < ends.size() - 1 && allIncreaseIsEqual; c++)
793     allIncreaseIsEqual = allIncreaseIsEqual && ends[c] == ends[c + 1];
794   if (allIncreaseIsEqual) r1 = ends[0];
795 }
796 
797 //-----------------------------------------------------------------------------
798 
decreaseStepCells(int r0,int c0,int & r1,int c1)799 void TXsheet::decreaseStepCells(int r0, int c0, int &r1, int c1) {
800   int c, size = r1 - r0 + 1;
801   QList<int> ends;
802   for (c = c0; c <= c1; c++) {
803     int r = r0, i = 0, rEnd = r1;
804     while (r <= rEnd) {
805       TXshCell cell = getCell(CellPosition(r, c));
806       if (!cell.isEmpty()) {
807         r++;
808         bool removed = false;
809         while (cell == getCell(CellPosition(r, c)) && r <= rEnd) {
810           if (!removed) {
811             removed = true;
812             removeCells(r, c);
813             rEnd--;
814           } else
815             r++;
816         }
817       } else
818         r++;
819       i++;
820     }
821     ends.append(rEnd);
822   }
823   if (ends.isEmpty()) return;
824   // controllo se devo cambiare la selezione
825   bool allDecreaseIsEqual = true;
826   for (c = 0; c < ends.size() - 1 && allDecreaseIsEqual; c++)
827     allDecreaseIsEqual = allDecreaseIsEqual && ends[c] == ends[c + 1];
828   if (allDecreaseIsEqual) r1 = ends[0];
829 }
830 
831 //-----------------------------------------------------------------------------
832 
eachCells(int r0,int c0,int r1,int c1,int type)833 void TXsheet::eachCells(int r0, int c0, int r1, int c1, int type) {
834   int nr = r1 - r0 + 1;
835   int nc = c1 - c0 + 1;
836   if (nr < type || nc <= 0) return;
837 
838   int newRows = nr % type ? nr / type + 1 : nr / type;
839 
840   int size = newRows * nc;
841   assert(size > 0);
842   std::unique_ptr<TXshCell[]> cells(new TXshCell[size]);
843   assert(cells);
844 
845   int i, j, k;
846   for (j = r0, i = 0; i < size;
847        j += type)  // in cells copio il contenuto delle celle che mi interessano
848   {
849     for (k = c0; k <= c1; k++, i++) cells[i] = getCell(CellPosition(j, k));
850   }
851 
852   int c;
853   for (c = c0; c <= c1; ++c) removeCells(r0 + newRows, c, nr - newRows);
854 
855   for (i = r0, k = 0; i < r0 + newRows && k < size; i++)
856     for (j = c0; j <= c1; j++) {
857       //----110523 iwasawa
858       // Eachでできた空きセルに、操作前のセルの中身が残ってしまう不具合を修正
859       if (cells[k].isEmpty())
860         clearCells(i, j);
861       else
862         setCell(i, j, cells[k]);
863       k++;
864     }
865 }
866 
867 //-----------------------------------------------------------------------------
868 /*! force cells order in n-steps. returns the row amount after process
869  */
reframeCells(int r0,int r1,int col,int type,int withBlank)870 int TXsheet::reframeCells(int r0, int r1, int col, int type, int withBlank) {
871   // Row amount in the selection
872   int nr = r1 - r0 + 1;
873 
874   if (nr < 1) return 0;
875 
876   QVector<TXshCell> cells;
877 
878   cells.clear();
879 
880   for (int r = r0; r <= r1; r++) {
881     if (cells.size() == 0 || cells.last() != getCell(CellPosition(r, col)))
882       cells.push_back(getCell(CellPosition(r, col)));
883   }
884 
885   // if withBlank is greater than -1, remove empty cells from cell order
886   if (withBlank >= 0) {
887     auto itr = cells.begin();
888     while (itr != cells.end()) {
889       if ((*itr).isEmpty())
890         itr = cells.erase(itr);
891       else
892         itr++;
893     }
894   }
895 
896   if (cells.empty()) return 0;
897 
898   // row amount after n-step
899   int nrows = cells.size() * type;
900 
901   if (withBlank > 0) {
902     nrows += cells.size() * withBlank * type;
903   }
904 
905   // if needed, insert cells
906   if (nr < nrows) {
907     insertCells(r1 + 1, col, nrows - nr);
908   }
909   // if needed, remove cells
910   else if (nr > nrows) {
911     removeCells(r0 + nrows, col, nr - nrows);
912   }
913 
914   for (int i = r0, k = 0; i < r0 + nrows; k++) {
915     for (int i1 = 0; i1 < type; i1++) {
916       if (cells[k].isEmpty())
917         clearCells(i + i1, col);
918       else
919         setCell(i + i1, col, cells[k]);
920     }
921     i += type;  // dipende dal tipo di step (2 o 3 per ora)
922 
923     if (withBlank > 0) {
924       for (int i1 = 0; i1 < withBlank * type; i1++) {
925         clearCells(i + i1, col);
926       }
927       i += withBlank * type;
928     }
929   }
930 
931   return nrows;  // return row amount after process
932 }
933 
934 //-----------------------------------------------------------------------------
935 
resetStepCells(int r0,int c0,int r1,int c1)936 void TXsheet::resetStepCells(int r0, int c0, int r1, int c1) {
937   int c, size = r1 - r0 + 1;
938   for (c = c0; c <= c1; c++) {
939     int r = r0, i = 0;
940     TXshCell *cells = new TXshCell[size];
941     while (r <= r1) {
942       // mi prendo le celle che mi servono
943       cells[i] = getCell(CellPosition(r, c));
944       r++;
945       while (cells[i] == getCell(CellPosition(r, c)) && r <= r1) r++;
946       i++;
947     }
948 
949     size = i;
950     removeCells(r0, c, r1 - r0 + 1);
951     insertCells(r0, c, i);
952     i = 0;
953     r = r0;
954     for (i = 0; i < size; i++, r++) setCell(r, c, cells[i]);
955   }
956 }
957 
958 //-----------------------------------------------------------------------------
959 /*! Roll first cells of rect r0,c0,r1,c1. Move cells contained in first row to
960  * last row.
961  */
rollupCells(int r0,int c0,int r1,int c1)962 void TXsheet::rollupCells(int r0, int c0, int r1, int c1) {
963   int nc   = c1 - c0 + 1;
964   int size = 1 * nc;
965   assert(size > 0);
966   std::unique_ptr<TXshCell[]> cells(new TXshCell[size]);
967   assert(cells);
968 
969   // in cells copio il contenuto delle celle che mi interessano
970   int k;
971   for (k = c0; k <= c1; k++) cells[k - c0] = getCell(CellPosition(r0, k));
972 
973   for (k = c0; k <= c1; k++) removeCells(r0, k, 1);
974 
975   for (k = c0; k <= c1; k++) {
976     insertCells(r1, k, 1);
977     setCell(r1, k, cells[k - c0]);  // setto le celle
978   }
979 }
980 
981 //-----------------------------------------------------------------------------
982 /*! Roll last cells of rect r0,c0,r1,c1. Move cells contained in last row to
983  * first row.
984  */
rolldownCells(int r0,int c0,int r1,int c1)985 void TXsheet::rolldownCells(int r0, int c0, int r1, int c1) {
986   int nc   = c1 - c0 + 1;
987   int size = 1 * nc;
988   assert(size > 0);
989   std::unique_ptr<TXshCell[]> cells(new TXshCell[size]);
990   assert(cells);
991 
992   // in cells copio il contenuto delle celle che mi interessano
993   int k;
994   for (k = c0; k <= c1; k++) cells[k - c0] = getCell(CellPosition(r1, k));
995 
996   for (k = c0; k <= c1; k++) removeCells(r1, k, 1);
997 
998   for (k = c0; k <= c1; k++) {
999     insertCells(r0, k, 1);
1000     setCell(r0, k, cells[k - c0]);  // setto le celle
1001   }
1002 }
1003 
1004 //-----------------------------------------------------------------------------
1005 /*! Stretch cells contained in rect r0,c0,r1,c1, from r1-r0+1 to nr.
1006                 If nr>r1-r0+1 add cells, otherwise remove cells. */
timeStretch(int r0,int c0,int r1,int c1,int nr)1007 void TXsheet::timeStretch(int r0, int c0, int r1, int c1, int nr) {
1008   int oldNr = r1 - r0 + 1;
1009   if (nr > oldNr) /* ingrandisce */
1010   {
1011     int c;
1012     for (c = c0; c <= c1; c++) {
1013       int dn = nr - oldNr;
1014       assert(oldNr > 0);
1015       std::unique_ptr<TXshCell[]> cells(new TXshCell[oldNr]);
1016       assert(cells);
1017       getCells(r0, c, oldNr, cells.get());
1018       insertCells(r0 + 1, c, dn);
1019       int i;
1020       for (i = nr - 1; i >= 0; i--) {
1021         int j = i * double(oldNr) / double(nr);
1022         if (j < i) setCell(i + r0, c, cells[j]);
1023       }
1024     }
1025   } else /* rimpicciolisce */
1026   {
1027     int c;
1028     for (c = c0; c <= c1; c++) {
1029       int dn = oldNr - nr;
1030       std::unique_ptr<TXshCell[]> cells(new TXshCell[oldNr]);
1031       assert(cells);
1032       getCells(r0, c, oldNr, cells.get());
1033       int i;
1034       for (i = 0; i < nr; i++) {
1035         int j = i * double(oldNr) / double(nr);
1036         if (j > i) setCell(i + r0, c, cells[j]);
1037       }
1038       removeCells(r1 - dn + 1, c, dn);
1039     }
1040   }
1041 }
1042 
1043 //-----------------------------------------------------------------------------
1044 
exposeLevel(int row,int col,TXshLevel * xl,bool overwrite)1045 int TXsheet::exposeLevel(int row, int col, TXshLevel *xl, bool overwrite) {
1046   if (!xl) return 0;
1047   std::vector<TFrameId> fids;
1048   xl->getFids(fids);
1049   int frameCount = 1;
1050   if (fids.empty()) {
1051     setCell(row, col, TXshCell(xl, TFrameId(1)));
1052     updateFrameCount();
1053     return frameCount;
1054   }
1055   exposeLevel(row, col, xl, fids, overwrite);
1056   return (int)fids.size();
1057 }
1058 
1059 //-----------------------------------------------------------------------------
1060 // customized version for load level popup
exposeLevel(int row,int col,TXshLevel * xl,std::vector<TFrameId> & fIds_,int xFrom,int xTo,int step,int inc,int frameCount,bool doesFileActuallyExist)1061 int TXsheet::exposeLevel(int row, int col, TXshLevel *xl,
1062                          std::vector<TFrameId> &fIds_, int xFrom, int xTo,
1063                          int step, int inc, int frameCount,
1064                          bool doesFileActuallyExist) {
1065   if (!xl) return 0;
1066   std::vector<TFrameId> fids;
1067 
1068   if (doesFileActuallyExist)
1069     xl->getFids(fids);
1070   else {
1071     for (int i = 0; i < (int)fIds_.size(); i++) {
1072       fids.push_back(fIds_[i]);
1073     }
1074   }
1075 
1076   // multiple exposing
1077   if (frameCount < 0 || xFrom < 0 || xTo < 0 || step < 0 || inc < 0) {
1078     insertCells(row, col, xl->getFrameCount());
1079 
1080     frameCount = 1;
1081     if (fids.empty())
1082       setCell(row, col, TXshCell(xl, TFrameId(1)));
1083     else {
1084       frameCount = (int)fids.size();
1085       insertCells(row, col, frameCount);
1086       std::vector<TFrameId>::iterator it;
1087       for (it = fids.begin(); it != fids.end(); ++it)
1088         setCell(row++, col, TXshCell(xl, *it));
1089     }
1090     updateFrameCount();
1091     return frameCount;
1092   }
1093 
1094   // single exposing
1095 
1096   insertCells(row, col, frameCount);
1097 
1098   if (fids.empty()) {
1099     setCell(row, col, TXshCell(xl, TFrameId(1)));
1100   } else {
1101     if (inc == 0)  // inc = Auto
1102     {
1103       std::vector<TFrameId>::iterator it;
1104       it = fids.begin();
1105       while (it->getNumber() < xFrom) it++;
1106 
1107       if (step == 0)  // Step = Auto
1108       {
1109         std::vector<TFrameId>::iterator next_it;
1110         next_it = it;
1111         next_it++;
1112 
1113         int startFrame = it->getNumber();
1114 
1115         for (int f = startFrame; f < startFrame + frameCount; f++) {
1116           if (next_it != fids.end() && f >= next_it->getNumber()) {
1117             it++;
1118             next_it++;
1119           }
1120           setCell(row++, col, TXshCell(xl, *it));
1121         }
1122       }
1123 
1124       else  // Step != Auto
1125       {
1126         int loopCount = frameCount / step;
1127         for (int loop = 0; loop < loopCount; loop++) {
1128           for (int s = 0; s < step; s++) {
1129             setCell(row++, col, TXshCell(xl, *it));
1130           }
1131           it++;
1132         }
1133       }
1134 
1135     } else  // inc != Auto
1136     {
1137       int loopCount;
1138       if (step == 0)  // Step = Auto
1139         step = inc;
1140 
1141       loopCount = frameCount / step;
1142 
1143       for (int loop = 0; loop < loopCount; loop++) {
1144         TFrameId id(xFrom + loop * inc, fids.begin()->getLetter());
1145         for (int s = 0; s < step; s++) {
1146           setCell(row++, col, TXshCell(xl, id));
1147         }
1148       }
1149     }
1150   }
1151   updateFrameCount();
1152   return frameCount;
1153 }
1154 
1155 //-----------------------------------------------------------------------------
1156 
exposeLevel(int row,int col,TXshLevel * xl,std::vector<TFrameId> fids,bool overwrite)1157 void TXsheet::exposeLevel(int row, int col, TXshLevel *xl,
1158                           std::vector<TFrameId> fids, bool overwrite) {
1159   int frameCount = (int)fids.size();
1160   if (!overwrite) insertCells(row, col, frameCount);
1161   std::vector<TFrameId>::iterator it;
1162   for (it = fids.begin(); it != fids.end(); ++it)
1163     setCell(row++, col, TXshCell(xl, *it));
1164   updateFrameCount();
1165 }
1166 
1167 //-----------------------------------------------------------------------------
1168 
updateFrameCount()1169 void TXsheet::updateFrameCount() {
1170   m_imp->m_frameCount = 0;
1171   for (int i = 0; i < m_imp->m_columnSet.getColumnCount(); ++i) {
1172     TXshColumnP cc = m_imp->m_columnSet.getColumn(i);
1173     if (cc && !cc->isEmpty())
1174       m_imp->m_frameCount =
1175           std::max(m_imp->m_frameCount, cc->getMaxFrame() + 1);
1176   }
1177 }
1178 
1179 //-----------------------------------------------------------------------------
1180 
loadData(TIStream & is)1181 void TXsheet::loadData(TIStream &is) {
1182   clearAll();
1183   TStageObjectId cameraId   = TStageObjectId::CameraId(0);
1184   TStageObject *firstCamera = getStageObject(cameraId);
1185   m_imp->m_pegTree->removeStageObject(cameraId);
1186 
1187   int col = 0;
1188   string tagName;
1189   while (is.openChild(tagName)) {
1190     if (tagName == "columns") {
1191       while (!is.eos()) {
1192         TPersist *p = 0;
1193         is >> p;
1194         TXshColumn *column = dynamic_cast<TXshColumn *>(p);
1195         if (!column) throw TException("expected xsheet column");
1196         m_imp->m_columnSet.insertColumn(col++, column);
1197         column->setXsheet(this);
1198         if (TXshZeraryFxColumn *zc =
1199                 dynamic_cast<TXshZeraryFxColumn *>(column)) {
1200           TFx *fx         = zc->getZeraryColumnFx()->getZeraryFx();
1201           int fxTypeCount = m_imp->m_fxDag->getFxTypeCount(fx);
1202           int maxFxTypeId = std::max(fxTypeCount, fx->getAttributes()->getId());
1203           m_imp->m_fxDag->updateFxTypeTable(fx, maxFxTypeId);
1204           m_imp->m_fxDag->updateFxIdTable(fx);
1205           for (int j = 0; j < fx->getParams()->getParamCount(); j++) {
1206             TParam *param = fx->getParams()->getParam(j);
1207             if (TDoubleParam *dp = dynamic_cast<TDoubleParam *>(param))
1208               getStageObjectTree()->setGrammar(dp);
1209             else if (dynamic_cast<TPointParam *>(param) ||
1210                      dynamic_cast<TRangeParam *>(param) ||
1211                      dynamic_cast<TPixelParam *>(param)) {
1212               TParamSet *paramSet = dynamic_cast<TParamSet *>(param);
1213               assert(paramSet);
1214               int f;
1215               for (f = 0; f < paramSet->getParamCount(); f++) {
1216                 TDoubleParam *dp = dynamic_cast<TDoubleParam *>(
1217                     paramSet->getParam(f).getPointer());
1218                 if (!dp) continue;
1219                 getStageObjectTree()->setGrammar(dp);
1220               }
1221             }
1222           }
1223         }
1224       }
1225     } else if (tagName == "pegbars") {
1226       TPersist *p = m_imp->m_pegTree;
1227       m_imp->m_pegTree->loadData(is, this);
1228     } else if (tagName == "fxnodes") {
1229       m_imp->m_fxDag->loadData(is);
1230       std::vector<TFx *> fxs;
1231       m_imp->m_fxDag->getFxs(fxs);
1232       for (int i = 0; i < (int)fxs.size(); i++) {
1233         TFx *fx = fxs[i];
1234         for (int j = 0; j < fx->getParams()->getParamCount(); j++) {
1235           TParam *param = fx->getParams()->getParam(j);
1236           if (TDoubleParam *dp = dynamic_cast<TDoubleParam *>(param))
1237             getStageObjectTree()->setGrammar(dp);
1238           else if (dynamic_cast<TPointParam *>(param) ||
1239                    dynamic_cast<TRangeParam *>(param) ||
1240                    dynamic_cast<TPixelParam *>(param)) {
1241             TParamSet *paramSet = dynamic_cast<TParamSet *>(param);
1242             assert(paramSet);
1243             int f;
1244             for (f = 0; f < paramSet->getParamCount(); f++) {
1245               TDoubleParam *dp = dynamic_cast<TDoubleParam *>(
1246                   paramSet->getParam(f).getPointer());
1247               if (!dp) continue;
1248               getStageObjectTree()->setGrammar(dp);
1249             }
1250           }
1251         }
1252       }
1253 
1254       if (is.matchEndTag()) continue;
1255 
1256       // was ist dass?
1257       TFxSet fxSet;
1258       fxSet.loadData(is);
1259     } else if (tagName == "columnFan") {
1260       m_imp->m_columnFans[0].loadData(is);
1261       m_imp->copyFoldedState();
1262     } else if (tagName == "noteSet") {
1263       m_notes->loadData(is);
1264     } else {
1265       throw TException("xsheet, unknown tag: " + tagName);
1266     }
1267     is.closeChild();
1268   }
1269   updateFrameCount();
1270 }
1271 
1272 //-----------------------------------------------------------------------------
1273 
saveData(TOStream & os)1274 void TXsheet::saveData(TOStream &os) {
1275   os.openChild("columns");
1276   for (int c = 0; c < m_imp->m_columnSet.getColumnCount(); ++c) {
1277     TXshColumnP column = m_imp->m_columnSet.getColumn(c);
1278     if (column && c < getFirstFreeColumnIndex()) os << column.getPointer();
1279   }
1280   os.closeChild();
1281   os.openChild("pegbars");
1282   m_imp->m_pegTree->saveData(os, getFirstFreeColumnIndex(), this);
1283   // os << *(m_imp->m_pegTree);
1284   os.closeChild();
1285 
1286   FxDag *fxDag = getFxDag();
1287   os.openChild("fxnodes");
1288   fxDag->saveData(os, getFirstFreeColumnIndex());
1289   os.closeChild();
1290 
1291   // does not matter which Orientation to take, as all fans share folded data
1292   ColumnFan *columnFan = getColumnFan(Orientations::topToBottom());
1293   if (!columnFan->isEmpty()) {
1294     os.openChild("columnFan");
1295     columnFan->saveData(os);
1296     os.closeChild();
1297   }
1298 
1299   TXshNoteSet *notes = getNotes();
1300   if (notes->getCount() > 0) {
1301     os.openChild("noteSet");
1302     notes->saveData(os);
1303     os.closeChild();
1304   }
1305 }
1306 
1307 //-----------------------------------------------------------------------------
1308 
1309 PERSIST_IDENTIFIER(TXsheet, "xsheet")
1310 
1311 //-----------------------------------------------------------------------------
1312 
insertColumn(int col,TXshColumn::ColumnType type)1313 void TXsheet::insertColumn(int col, TXshColumn::ColumnType type) {
1314   insertColumn(col, TXshColumn::createEmpty(type));
1315 }
1316 
1317 //-----------------------------------------------------------------------------
1318 
insertColumn(int col,TXshColumn * column)1319 void TXsheet::insertColumn(int col, TXshColumn *column) {
1320   if (col < 0) col = 0;
1321   column->setXsheet(this);
1322   m_imp->m_columnSet.insertColumn(col, column);
1323   m_imp->m_pegTree->insertColumn(col);
1324   if (column->getPaletteColumn() ==
1325       0)  // palette column are not connected to the xsheet fx node
1326   {
1327     TFx *fx = column->getFx();
1328     if (fx) getFxDag()->addToXsheet(fx);
1329   }
1330 
1331   for (ColumnFan &columnFan : m_imp->m_columnFans) {
1332     columnFan.rollRightFoldedState(col,
1333                                    m_imp->m_columnSet.getColumnCount() - col);
1334   }
1335 
1336   notify(TXsheetColumnChange(TXsheetColumnChange::Insert, col));
1337 }
1338 
1339 //-----------------------------------------------------------------------------
1340 
removeColumn(int col)1341 void TXsheet::removeColumn(int col) {
1342   TXshColumn *column = getColumn(col);
1343   if (column) {
1344     TFx *fx = column->getFx();
1345     if (fx) {
1346       getFxDag()->removeFromXsheet(fx);
1347 
1348       // disconnetto dal columnFx tutti gli effetti connessi in uscita
1349       TFxPort *outPort = 0;
1350       while ((outPort = fx->getOutputConnection(0))) outPort->setFx(0);
1351     }
1352   }
1353   m_imp->m_columnSet.removeColumn(col);
1354   m_imp->m_pegTree->removeColumn(col);
1355 
1356   for (ColumnFan &columnFan : m_imp->m_columnFans) {
1357     columnFan.rollLeftFoldedState(col,
1358                                   m_imp->m_columnSet.getColumnCount() - col);
1359   }
1360 
1361   notify(TXsheetColumnChange(TXsheetColumnChange::Remove, col));
1362 }
1363 
1364 //-----------------------------------------------------------------------------
1365 
moveColumn(int srcIndex,int dstIndex)1366 void TXsheet::moveColumn(int srcIndex, int dstIndex) {
1367   if (srcIndex == dstIndex) return;
1368   assert(srcIndex >= 0);
1369   assert(dstIndex >= 0);
1370   int col = std::max(srcIndex, dstIndex);
1371   if (col >= m_imp->m_columnSet.getColumnCount()) {
1372     int n = m_imp->m_columnSet.getColumnCount();
1373     touchColumn(col, TXshColumn::eLevelType);
1374     while (n <= col) {
1375       TXshColumn *column = getColumn(n);
1376       assert(column);
1377       column->setXsheet(this);
1378       n++;
1379     }
1380   }
1381   assert(m_imp->m_columnSet.getColumnCount() > srcIndex);
1382   assert(m_imp->m_columnSet.getColumnCount() > dstIndex);
1383 
1384   if (srcIndex < dstIndex) {
1385     int c0 = srcIndex;
1386     int c1 = dstIndex;
1387     assert(c0 < c1);
1388     m_imp->m_columnSet.rollLeft(c0, c1 - c0 + 1);
1389     for (ColumnFan &columnFan : m_imp->m_columnFans)
1390       columnFan.rollLeftFoldedState(c0, c1 - c0 + 1);
1391     for (int c = c0; c < c1; ++c) m_imp->m_pegTree->swapColumns(c, c + 1);
1392   } else {
1393     int c0 = dstIndex;
1394     int c1 = srcIndex;
1395     assert(c0 < c1);
1396     m_imp->m_columnSet.rollRight(c0, c1 - c0 + 1);
1397     for (ColumnFan &columnFan : m_imp->m_columnFans)
1398       columnFan.rollRightFoldedState(c0, c1 - c0 + 1);
1399     for (int c = c1 - 1; c >= c0; --c) m_imp->m_pegTree->swapColumns(c, c + 1);
1400   }
1401 
1402   notify(TXsheetColumnChange(TXsheetColumnChange::Move, srcIndex, dstIndex));
1403 }
1404 
1405 //-----------------------------------------------------------------------------
1406 
getColumn(int col) const1407 TXshColumn *TXsheet::getColumn(int col) const {
1408   if (col < 0) return m_cameraColumn;
1409   return m_imp->m_columnSet.getColumn(col).getPointer();
1410 }
1411 
1412 //-----------------------------------------------------------------------------
1413 
getColumnCount() const1414 int TXsheet::getColumnCount() const {
1415   return m_imp->m_columnSet.getColumnCount();
1416 }
1417 
1418 //-----------------------------------------------------------------------------
1419 
getFirstFreeColumnIndex() const1420 int TXsheet::getFirstFreeColumnIndex() const {
1421   int i = getColumnCount();
1422   while (i > 0 && isColumnEmpty(i - 1)) --i;
1423   return i;
1424 }
1425 
1426 //-----------------------------------------------------------------------------
1427 
touchColumn(int index,TXshColumn::ColumnType type)1428 TXshColumn *TXsheet::touchColumn(int index, TXshColumn::ColumnType type) {
1429   TXshColumn *column = m_imp->m_columnSet.touchColumn(index, type).getPointer();
1430   if (index < 0 || !column) return 0;
1431 
1432   // NOTE (Daniele): The following && should be a bug... but I fear I'd break
1433   // something changing it.
1434   // Observe that the implied behavior is that of REPLACING AN EXISTING
1435   // LEGITIMATE COLUMN!
1436   // Please, Inquire further if you're not upon release!
1437 
1438   if (column->isEmpty() && column->getColumnType() != type) {
1439     removeColumn(index);
1440     insertColumn(index, type);
1441     column = getColumn(index);
1442   }
1443 
1444   return column;
1445 }
1446 
1447 //=============================================================================
1448 namespace {  // Utility function
1449 //-----------------------------------------------------------------------------
1450 
searchAudioColumn(TXsheet * xsh,std::vector<TXshSoundColumn * > & sounds,bool isPreview=true)1451 void searchAudioColumn(TXsheet *xsh, std::vector<TXshSoundColumn *> &sounds,
1452                        bool isPreview = true) {
1453   int i       = 0;
1454   int columns = xsh->getColumnCount();
1455 
1456   for (; i < columns; ++i) {
1457     TXshColumn *column = xsh->getColumn(i);
1458     if (column) {
1459       TXshSoundColumn *soundCol = column->getSoundColumn();
1460       if (soundCol && !soundCol->isEmpty() &&
1461           ((isPreview && soundCol->isCamstandVisible()) ||
1462            (!isPreview && soundCol->isPreviewVisible()))) {
1463         sounds.push_back(soundCol);
1464         continue;
1465       }
1466     }
1467   }
1468 }
1469 //-----------------------------------------------------------------------------
1470 }  // namespace
1471 //-----------------------------------------------------------------------------
1472 
makeSound(SoundProperties * properties)1473 TSoundTrack *TXsheet::makeSound(SoundProperties *properties) {
1474   std::vector<TXshSoundColumn *> sounds;
1475   searchAudioColumn(this, sounds, properties->m_isPreview);
1476   if (!m_imp->m_mixedSound || *properties != *m_soundProperties) {
1477     if (!sounds.empty() && properties->m_fromFrame <= properties->m_toFrame)
1478       m_imp->m_mixedSound = sounds[0]->mixingTogether(
1479           sounds, properties->m_fromFrame, properties->m_toFrame,
1480           properties->m_frameRate);
1481     else
1482       m_imp->m_mixedSound = 0;
1483     delete m_soundProperties;
1484     m_soundProperties = properties;
1485   } else
1486     delete properties;
1487   return m_imp->m_mixedSound.getPointer();
1488 }
1489 
1490 //-----------------------------------------------------------------------------
1491 
scrub(int frame,bool isPreview)1492 void TXsheet::scrub(int frame, bool isPreview) {
1493   try {
1494     double fps =
1495         getScene()->getProperties()->getOutputProperties()->getFrameRate();
1496 
1497     TXsheet::SoundProperties *prop = new TXsheet::SoundProperties();
1498     prop->m_isPreview              = isPreview;
1499 
1500     TSoundTrack *st = makeSound(prop);  // Absorbs prop's ownership
1501     if (!st) return;
1502 
1503     double samplePerFrame = st->getSampleRate() / fps;
1504 
1505     double s0 = frame * samplePerFrame, s1 = s0 + samplePerFrame;
1506     // if (m_player && m_player->isPlaying()) {
1507     //    try {
1508     //        m_player->stop();
1509     //    }
1510     //    catch (const std::runtime_error& e) {
1511     //        int i = 0;
1512     //    }
1513     //    catch (const std::exception& e) {
1514     //        int i = 0;
1515     //    }
1516     //    catch (...) {
1517     //        int i = 0;
1518     //    }
1519     //}
1520     play(st, s0, s1, false);
1521   } catch (TSoundDeviceException &e) {
1522     if (e.getType() == TSoundDeviceException::NoDevice) {
1523       std::cout << ::to_string(e.getMessage()) << std::endl;
1524     } else {
1525       throw TSoundDeviceException(e.getType(), e.getMessage());
1526     }
1527   }
1528 }
1529 
1530 //-----------------------------------------------------------------------------
1531 
stopScrub()1532 void TXsheet::stopScrub() {
1533   if (m_player) m_player->stop();
1534 }
1535 
1536 //-----------------------------------------------------------------------------
1537 
play(TSoundTrackP soundtrack,int s0,int s1,bool loop)1538 void TXsheet::play(TSoundTrackP soundtrack, int s0, int s1, bool loop) {
1539   if (!TSoundOutputDevice::installed()) return;
1540 
1541   if (!m_player) m_player = new TSoundOutputDevice();
1542 
1543   if (m_player) {
1544     try {
1545       m_player->play(soundtrack, s0, s1, loop);
1546     } catch (TSoundDeviceException &) {
1547     }
1548   }
1549 }
1550 
1551 //-----------------------------------------------------------------------------
1552 
getFxDag() const1553 FxDag *TXsheet::getFxDag() const { return m_imp->m_fxDag; }
1554 
1555 //-----------------------------------------------------------------------------
1556 
getColumnFan(const Orientation * o) const1557 ColumnFan *TXsheet::getColumnFan(const Orientation *o) const {
1558   int index = o->dimension(PredefinedDimension::INDEX);
1559   return &m_imp->m_columnFans[index];
1560 }
1561 
1562 //-----------------------------------------------------------------------------
1563 
getScene() const1564 ToonzScene *TXsheet::getScene() const { return m_imp->m_scene; }
1565 
1566 //-----------------------------------------------------------------------------
1567 
setScene(ToonzScene * scene)1568 void TXsheet::setScene(ToonzScene *scene) { m_imp->m_scene = scene; }
1569 
1570 //-----------------------------------------------------------------------------
1571 
checkCircularReferences(const TXshCell & cellCandidate)1572 bool TXsheet::checkCircularReferences(const TXshCell &cellCandidate) {
1573   if (cellCandidate.isEmpty() || !cellCandidate.m_level->getChildLevel())
1574     return false;
1575   TXsheet *childCandidate = cellCandidate.m_level->getChildLevel()->getXsheet();
1576   return checkCircularReferences(childCandidate);
1577 }
1578 
1579 //-----------------------------------------------------------------------------
1580 
checkCircularReferences(TXshColumn * columnCandidate)1581 bool TXsheet::checkCircularReferences(TXshColumn *columnCandidate) {
1582   if (!columnCandidate || !columnCandidate->getLevelColumn()) return false;
1583   TXshLevelColumn *lc = columnCandidate->getLevelColumn();
1584   int r0 = 0, r1 = -1;
1585   if (lc->getRange(r0, r1) <= 0) return false;
1586   int r;
1587   TXshCell oldCell;
1588   for (r = r0; r <= r1; r++) {
1589     TXshCell cell = lc->getCell(r);
1590     // to speed up:
1591     if (cell.m_level.getPointer() == oldCell.m_level.getPointer()) continue;
1592     if (checkCircularReferences(cell)) return true;
1593     oldCell = cell;
1594   }
1595   return false;
1596 }
1597 
1598 //-----------------------------------------------------------------------------
1599 
invalidateSound()1600 void TXsheet::invalidateSound() { m_imp->m_mixedSound = TSoundTrackP(); }
1601 
1602 //-----------------------------------------------------------------------------
1603 
checkCircularReferences(TXsheet * childCandidate)1604 bool TXsheet::checkCircularReferences(TXsheet *childCandidate) {
1605   if (this == childCandidate) return true;
1606   if (childCandidate == 0) return false;
1607   int i;
1608   for (i = 0; i < childCandidate->getColumnCount(); i++)
1609     if (checkCircularReferences(childCandidate->getColumn(i))) return true;
1610   return false;
1611 }
1612 
1613 //-----------------------------------------------------------------------------
1614 
1615 // Builds the camstand bbox associated to the specified xsheet
getBBox(int r) const1616 TRectD TXsheet::getBBox(int r) const {
1617   static const double maxDouble = (std::numeric_limits<double>::max)();
1618   static const TRectD voidRect(maxDouble, maxDouble, -maxDouble, -maxDouble);
1619 
1620   //-----------------------------------------------------------------------
1621 
1622   struct locals {
1623     static TRectD getBBox(const TXsheet *xsh, int r, int c) {
1624       // Discriminate cell content
1625       const TXshCell &cell = xsh->getCell(CellPosition(r, c));
1626       if (cell.isEmpty()) return voidRect;
1627 
1628       if (TXshChildLevel *cl = cell.getChildLevel())
1629         return cl->getXsheet()->getBBox(cell.getFrameId().getNumber() - 1);
1630 
1631       TXshSimpleLevel *sl = cell.getSimpleLevel();
1632       if (!sl ||
1633           !(sl->getType() &
1634             LEVELCOLUMN_XSHLEVEL))  // Avoid other mesh levels - which could
1635         return voidRect;            // be deformed too...
1636 
1637       // Retrieve column affine
1638       TAffine columnZaff;
1639       {
1640         TStageObject *colObj = xsh->getStageObject(TStageObjectId::ColumnId(c));
1641 
1642         const TAffine &columnAff = colObj->getPlacement(r);  // ...
1643         double columnZ           = colObj->getZ(r);          // ...
1644         double columnNoScaleZ    = colObj->getGlobalNoScaleZ();
1645 
1646         TStageObjectId cameraId =
1647             xsh->getStageObjectTree()->getCurrentCameraId();
1648         TStageObject *camera = xsh->getStageObject(cameraId);
1649 
1650         const TAffine &cameraAff = camera->getPlacement(r);  // ...
1651         double cameraZ           = camera->getZ(r);          // ...
1652 
1653         if (!TStageObject::perspective(columnZaff, cameraAff, cameraZ,
1654                                        columnAff, columnZ, columnNoScaleZ))
1655           return voidRect;
1656       }
1657 
1658       const TRectD &bbox = sl->getBBox(cell.getFrameId());
1659       if (bbox.getLx() <= 0.0 || bbox.getLy() <= 0.0) return voidRect;
1660 
1661       return columnZaff * TScale(Stage::inch, Stage::inch) * bbox;
1662     }
1663   };
1664 
1665   //-----------------------------------------------------------------------
1666 
1667   // Initialize a union-neutral rect
1668   TRectD bbox(voidRect);
1669 
1670   // Traverse the xsheet's columns, adding the bbox of each
1671   int c, cCount = getColumnCount();
1672   for (c = 0; c != cCount; ++c) {
1673     // Skip empty or invisible columns
1674     TXshColumn *column = getColumn(c);
1675     if (column->isEmpty() || !column->isCamstandVisible()) continue;
1676 
1677     const TRectD &colBBox = locals::getBBox(this, r, c);
1678 
1679     // Make the union
1680     bbox.x0 = std::min(bbox.x0, colBBox.x0);
1681     bbox.y0 = std::min(bbox.y0, colBBox.y0);
1682     bbox.x1 = std::max(bbox.x1, colBBox.x1);
1683     bbox.y1 = std::max(bbox.y1, colBBox.y1);
1684   }
1685 
1686   return bbox;
1687 }
1688 
1689 //-----------------------------------------------------------------------
1690 
isRectEmpty(const CellPosition & pos0,const CellPosition & pos1) const1691 bool TXsheet::isRectEmpty(const CellPosition &pos0,
1692                           const CellPosition &pos1) const {
1693   for (int frame = pos0.frame(); frame <= pos1.frame(); frame++)
1694     for (int layer = pos0.layer(); layer <= pos1.layer(); layer++)
1695       if (!getCell(CellPosition(frame, layer)).isEmpty()) return false;
1696   return true;
1697 }
1698 
1699 //-----------------------------------------------------------------------
1700 // Function triggered by AutoInputCellNumberPopup.
1701 // executing this on column selection, set r1 = -1.
1702 // Here are the expected behaviors
1703 // 1. Cell Selection + Overwrite
1704 //    Cells will be input from the top of the selected range.
1705 //    New arrangement CANNOT run over the selected range.
1706 //    If the new arrangement is shorter than the selected range,
1707 //    excess cells will not be cleared but keep their contents.
1708 //    (It is the same behavior as Celsys' QuickChecker)
1709 // 2. Cell Selection + Insert
1710 //    New arrangement will be inserted before the selected range.
1711 //    If the selected range has multiple columns, then the inserted
1712 //    cells will be expanded to the longest arrangement with empty cells.
1713 // 3. Column Selection + Overwrite
1714 //    Cells will be input from the top of the columns.
1715 //    New arrangement CAN run over the existing column range.
1716 //    If the new arrangement is shorter than the selected range,
1717 //    excess cells will not be cleared but keep their contents.
1718 // 4. Column Selection + Insert
1719 //    New arrangement will be inserted at the top of the columns.
1720 //    If multiple columns are selected, then the inserted cells
1721 //    will be expanded to the longest arrangement with empty cells.
1722 //
autoInputCellNumbers(int increment,int interval,int step,int repeat,int from,int to,int r0,int r1,bool isOverwrite,std::vector<int> columnIndices,std::vector<TXshLevelP> levels,int rowsCount)1723 void TXsheet::autoInputCellNumbers(int increment, int interval, int step,
1724                                    int repeat, int from, int to, int r0, int r1,
1725                                    bool isOverwrite,
1726                                    std::vector<int> columnIndices,
1727                                    std::vector<TXshLevelP> levels,
1728                                    int rowsCount) {
1729   int rowUpTo = (r1 == -1) ? rowsCount - 1
1730                            : ((isOverwrite) ? std::min(r1, r0 + rowsCount - 1)
1731                                             : r0 + rowsCount - 1);
1732   // for each column
1733   for (int c = 0; c < columnIndices.size(); c++) {
1734     int columnIndex  = columnIndices.at(c);
1735     TXshLevelP level = levels.at(c);
1736 
1737     // on insertion, insert empty cells first
1738     if (!isOverwrite) insertCells(r0, columnIndex, rowsCount);
1739 
1740     // obtain fids to be input
1741     std::vector<TFrameId> fids;
1742     if (increment == 0) {
1743       std::vector<TFrameId> wholeFids;
1744       level->getFids(wholeFids);
1745       if (from <= to) {
1746         for (auto itr = wholeFids.begin(); itr != wholeFids.end(); ++itr) {
1747           if ((*itr).getNumber() >= from && (*itr).getNumber() <= to)
1748             fids.push_back(*itr);
1749           else if ((*itr).getNumber() > to)
1750             break;
1751         }
1752       } else {  // from > to
1753         for (auto itr = wholeFids.rbegin(); itr != wholeFids.rend(); ++itr) {
1754           if ((*itr).getNumber() <= from && (*itr).getNumber() >= to)
1755             fids.push_back(*itr);
1756           else if ((*itr).getNumber() < to)
1757             break;
1758         }
1759       }
1760     } else {  // increment != 0
1761       int f = from;
1762       if (from <= to) {
1763         while (f <= to) {
1764           fids.push_back(TFrameId(f));
1765           f += increment;
1766         }
1767       } else {  // from > to
1768         while (f >= to) {
1769           fids.push_back(TFrameId(f));
1770           f -= increment;
1771         }
1772       }
1773     }
1774 
1775     // input cells
1776     int row             = r0;
1777     int repeat_itr      = 0;
1778     int fid_itr         = 0;
1779     int step_interv_itr = 0;
1780     while (row <= rowUpTo) {
1781       // input cell
1782       if (step_interv_itr < step)
1783         setCell(row, columnIndex, TXshCell(level, fids.at(fid_itr)));
1784       // .. or set empty cell as interval
1785       else
1786         setCell(row, columnIndex, TXshCell());
1787 
1788       // increment
1789       step_interv_itr++;
1790       // next frame
1791       if (step_interv_itr == step + interval) {
1792         fid_itr++;
1793         step_interv_itr = 0;
1794       }
1795       // next repeat cycle
1796       if (fid_itr == fids.size()) {
1797         repeat_itr++;
1798         fid_itr = 0;
1799       }
1800       if (repeat_itr == repeat) break;
1801 
1802       row++;
1803     }
1804   }
1805 }
1806 
1807 //---------------------------------------------------------
1808 
setObserver(TXsheetColumnChangeObserver * observer)1809 void TXsheet::setObserver(TXsheetColumnChangeObserver *observer) {
1810   m_observer = observer;
1811 }
1812 
1813 //---------------------------------------------------------
1814 
notify(const TXsheetColumnChange & change)1815 void TXsheet::notify(const TXsheetColumnChange &change) {
1816   if (m_observer) m_observer->onChange(change);
1817 }
notifyFxAdded(const std::vector<TFx * > & fxs)1818 void TXsheet::notifyFxAdded(const std::vector<TFx *> &fxs) {
1819   if (m_observer) m_observer->onFxAdded(fxs);
1820 }
notifyStageObjectAdded(const TStageObjectId id)1821 void TXsheet::notifyStageObjectAdded(const TStageObjectId id) {
1822   if (m_observer) m_observer->onStageObjectAdded(id);
1823 }
isReferenceManagementIgnored(TDoubleParam * param)1824 bool TXsheet::isReferenceManagementIgnored(TDoubleParam *param) {
1825   if (m_observer) return m_observer->isIgnored(param);
1826   return false;
1827 }
getExpRefMonitor() const1828 ExpressionReferenceMonitor *TXsheet::getExpRefMonitor() const {
1829   return m_imp->m_expRefMonitor;
1830 }
1831 //---------------------------------------------------------
1832