1 
2 
3 #include "cellselection.h"
4 
5 // Tnz6 includes
6 #include "celldata.h"
7 #include "keyframeselection.h"
8 #include "keyframedata.h"
9 #include "drawingdata.h"
10 #include "cellkeyframedata.h"
11 #include "filmstripcommand.h"
12 #include "menubarcommandids.h"
13 #include "timestretchpopup.h"
14 #include "tapp.h"
15 #include "xsheetviewer.h"
16 #include "levelcommand.h"
17 #include "columncommand.h"
18 
19 // TnzTools includes
20 #include "tools/toolutils.h"
21 #include "tools/toolhandle.h"
22 #include "tools/toolcommandids.h"
23 
24 // TnzQt includes
25 #include "toonzqt/strokesdata.h"
26 #include "toonzqt/rasterimagedata.h"
27 #include "toonzqt/icongenerator.h"
28 #include "toonzqt/tselectionhandle.h"
29 #include "toonzqt/menubarcommand.h"
30 #include "toonzqt/dvdialog.h"
31 #include "toonzqt/gutil.h"
32 #include "historytypes.h"
33 
34 // TnzLib includes
35 #include "toonz/palettecontroller.h"
36 #include "toonz/preferences.h"
37 #include "toonz/tpalettehandle.h"
38 #include "toonz/txsheethandle.h"
39 #include "toonz/txshlevelhandle.h"
40 #include "toonz/tcolumnhandle.h"
41 #include "toonz/tscenehandle.h"
42 #include "toonz/tframehandle.h"
43 #include "toonz/tobjecthandle.h"
44 #include "toonz/txsheet.h"
45 #include "toonz/txshsimplelevel.h"
46 #include "toonz/txshchildlevel.h"
47 #include "toonz/toonzscene.h"
48 #include "toonz/txshleveltypes.h"
49 #include "toonz/tcamera.h"
50 #include "toonz/levelproperties.h"
51 #include "toonz/toonzimageutils.h"
52 #include "toonz/trasterimageutils.h"
53 #include "toonz/levelset.h"
54 #include "toonz/tstageobjecttree.h"
55 #include "toonz/stage.h"
56 #include "vectorizerpopup.h"
57 
58 // TnzCore includes
59 #include "timagecache.h"
60 #include "tundo.h"
61 #include "tropcm.h"
62 #include "tvectorimage.h"
63 #include "tsystem.h"
64 #include "filebrowsermodel.h"
65 
66 // Qt includes
67 #include <QApplication>
68 #include <QClipboard>
69 
70 //=============================================================================
71 namespace {
72 //-----------------------------------------------------------------------------
73 
containsOnlyOneRasterLevel(int r0,int c0,int r1,int c1)74 bool containsOnlyOneRasterLevel(int r0, int c0, int r1, int c1) {
75   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
76   int r, c;
77   TXshLevelP xl = xsh->getCell(r0, c0).m_level;
78   for (r = r0; r <= r1; r++) {
79     for (c = c0; c <= c1; c++)
80       if (xsh->getCell(r, c).m_level.getPointer() != xl.getPointer())
81         return false;
82   }
83   return xl && (xl->getType() == TZP_XSHLEVEL ||
84                 xl->getType() == OVL_XSHLEVEL || xl->getType() == TZI_XSHLEVEL);
85 }
86 
87 //-----------------------------------------------------------------------------
88 
copyCellsWithoutUndo(int r0,int c0,int r1,int c1)89 void copyCellsWithoutUndo(int r0, int c0, int r1, int c1) {
90   int colCount = c1 - c0 + 1;
91   int rowCount = r1 - r0 + 1;
92   if (colCount <= 0 || rowCount <= 0) return;
93   TXsheet *xsh    = TApp::instance()->getCurrentXsheet()->getXsheet();
94   TCellData *data = new TCellData();
95   data->setCells(xsh, r0, c0, r1, c1);
96   QClipboard *clipboard = QApplication::clipboard();
97   clipboard->setMimeData(data, QClipboard::Clipboard);
98 }
99 
100 //-----------------------------------------------------------------------------
101 
pasteCellsWithoutUndo(const TCellData * cellData,int & r0,int & c0,int & r1,int & c1,bool insert=true,bool doZeraryClone=true)102 bool pasteCellsWithoutUndo(const TCellData *cellData, int &r0, int &c0, int &r1,
103                            int &c1, bool insert = true,
104                            bool doZeraryClone = true) {
105   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
106   if (!cellData) return false;
107   if (r0 < 0 || c0 < 0) return false;
108 
109   bool ret = cellData->getCells(xsh, r0, c0, r1, c1, insert, doZeraryClone);
110   if (!ret) return false;
111 
112   return true;
113 }
114 
115 //-----------------------------------------------------------------------------
116 
deleteCellsWithoutUndo(int & r0,int & c0,int & r1,int & c1)117 void deleteCellsWithoutUndo(int &r0, int &c0, int &r1, int &c1) {
118   try {
119     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
120     int c;
121     for (c = c0; c <= c1; c++) {
122       if (xsh->isColumnEmpty(c)) continue;
123       xsh->clearCells(r0, c, r1 - r0 + 1);
124       // when the column becomes empty after deletion,
125       // ColumnCmd::DeleteColumn() will take care of column related operations
126       // like disconnecting from fx nodes etc.
127       /*
128       TXshColumn* column = xsh->getColumn(c);
129       if (column && column->isEmpty()) {
130         column->resetColumnProperties();
131         TFx *fx = column->getFx();
132         if (fx) {
133           int i;
134           for (i = fx->getOutputConnectionCount() - 1; i >= 0; i--) {
135             TFxPort *port = fx->getOutputConnection(i);
136             port->setFx(0);
137           }
138         }
139       }
140       */
141     }
142   } catch (...) {
143     DVGui::error(QObject::tr("It is not possible to delete the selection."));
144   }
145 }
146 
147 //-----------------------------------------------------------------------------
148 
cutCellsWithoutUndo(int & r0,int & c0,int & r1,int & c1)149 void cutCellsWithoutUndo(int &r0, int &c0, int &r1, int &c1) {
150   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
151   int c;
152   for (c = c0; c <= c1; c++) {
153     xsh->removeCells(r0, c, r1 - r0 + 1);
154     // when the column becomes empty after deletion,
155     // ColumnCmd::DeleteColumn() will take care of column related operations
156     // like disconnecting from fx nodes etc.
157     /*
158     TXshColumn* column = xsh->getColumn(c);
159     if (column && column->isEmpty()) {
160       column->resetColumnProperties();
161       TFx* fx = column->getFx();
162       if (!fx) continue;
163       int i;
164       for (i = fx->getOutputConnectionCount() - 1; i >= 0; i--) {
165         TFxPort* port = fx->getOutputConnection(i);
166         port->setFx(0);
167       }
168     }
169     */
170   }
171 
172   // Se la selezione corrente e' TCellSelection svuoto la selezione.
173   TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
174       TApp::instance()->getCurrentSelection()->getSelection());
175   if (cellSelection) cellSelection->selectNone();
176 }
177 
178 //-----------------------------------------------------------------------------
179 // check if the operation may remove expression reference as column becomes
180 // empty and deleted after the operation. return true to continue the operation.
181 
checkColumnRemoval(const int r0,const int c0,const int r1,const int c1,std::set<int> & removedColIds)182 bool checkColumnRemoval(const int r0, const int c0, const int r1, const int c1,
183                         std::set<int> &removedColIds) {
184   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
185   // std::set<int> colIndicesToBeRemoved;
186   for (int c = c0; c <= c1; c++) {
187     TXshColumnP column = xsh->getColumn(c);
188     if (!column || column->isEmpty() || column->isLocked()) continue;
189     int tmp_r0, tmp_r1;
190     xsh->getCellRange(c, tmp_r0, tmp_r1);
191     if (r0 <= tmp_r0 && r1 >= tmp_r1) removedColIds.insert(c);
192   }
193 
194   if (removedColIds.empty() ||
195       !Preferences::instance()->isModifyExpressionOnMovingReferencesEnabled())
196     return true;
197   std::set<TFx *> dummy_fxs;
198   return ColumnCmd::checkExpressionReferences(removedColIds, dummy_fxs, true);
199 }
200 
201 //=============================================================================
202 //  PasteCellsUndo
203 //-----------------------------------------------------------------------------
204 
205 class PasteCellsUndo final : public TUndo {
206   TCellSelection *m_oldSelection;
207   TCellSelection *m_newSelection;
208   TCellData *m_data;
209   std::vector<bool> m_areOldColumnsEmpty;
210 
211 public:
PasteCellsUndo(int r0,int c0,int r1,int c1,int oldR0,int oldC0,int oldR1,int oldC1,const std::vector<bool> & areColumnsEmpty)212   PasteCellsUndo(int r0, int c0, int r1, int c1, int oldR0, int oldC0,
213                  int oldR1, int oldC1, const std::vector<bool> &areColumnsEmpty)
214       : m_areOldColumnsEmpty(areColumnsEmpty) {
215     m_data       = new TCellData();
216     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
217     m_data->setCells(xsh, r0, c0, r1, c1);
218     m_newSelection = new TCellSelection();
219     m_newSelection->selectCells(r0, c0, r1, c1);
220     m_oldSelection = new TCellSelection();
221     m_oldSelection->selectCells(oldR0, oldC0, oldR1, oldC1);
222   }
223 
~PasteCellsUndo()224   ~PasteCellsUndo() {
225     delete m_newSelection;
226     delete m_oldSelection;
227     delete m_data;
228   }
229 
undo() const230   void undo() const override {
231     int r0, c0, r1, c1;
232     m_newSelection->getSelectedCells(r0, c0, r1, c1);
233 
234     int oldR0, oldC0, oldR1, oldC1;
235     m_oldSelection->getSelectedCells(oldR0, oldC0, oldR1, oldC1);
236 
237     int c0BeforeCut = c0;
238     int c1BeforeCut = c1;
239     // Cut delle celle che sono in newSelection
240     cutCellsWithoutUndo(r0, c0, r1, c1);
241     // Se le colonne erano vuote le resetto (e' necessario farlo per le colonne
242     // particolari, colonne sount o palette)
243     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
244     assert(c1BeforeCut - c0BeforeCut + 1 == (int)m_areOldColumnsEmpty.size());
245     int c;
246     for (c = c0BeforeCut; c <= c1BeforeCut; c++) {
247       if (!m_areOldColumnsEmpty[c - c0BeforeCut] || !xsh->getColumn(c))
248         continue;
249     }
250 
251     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
252   }
253 
redo() const254   void redo() const override {
255     int r0, c0, r1, c1;
256     m_newSelection->getSelectedCells(r0, c0, c1, r1);
257     // Cut delle celle che sono in newSelection
258     pasteCellsWithoutUndo(m_data, r0, c0, c1, r1, true, false);
259     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
260   }
261 
getSize() const262   int getSize() const override { return sizeof(*this); }
263 
getHistoryString()264   QString getHistoryString() override { return QObject::tr("Paste Cells"); }
getHistoryType()265   int getHistoryType() override { return HistoryType::Xsheet; }
266 };
267 
268 //=============================================================================
269 //  DeleteCellsUndo
270 //  Recovering the column information (such as reconnecting nodes) when
271 //  undoing deletion of entire column will be done by DeleteColumnsUndo which
272 //  will be called in the same undo block. So here we only need to recover the
273 //  cell arrangement.
274 //-----------------------------------------------------------------------------
275 
276 class DeleteCellsUndo final : public TUndo {
277   TCellSelection *m_selection;
278   QMimeData *m_data;
279 
280 public:
DeleteCellsUndo(TCellSelection * selection,QMimeData * data)281   DeleteCellsUndo(TCellSelection *selection, QMimeData *data) : m_data(data) {
282     int r0, c0, r1, c1;
283     selection->getSelectedCells(r0, c0, r1, c1);
284     if (c0 < 0) c0 = 0;  // Ignore camera column
285     m_selection = new TCellSelection();
286     m_selection->selectCells(r0, c0, r1, c1);
287   }
288 
~DeleteCellsUndo()289   ~DeleteCellsUndo() { delete m_selection; }
290 
undo() const291   void undo() const override {
292     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
293 
294     int r0, c0, r1, c1;
295     m_selection->getSelectedCells(r0, c0, r1, c1);
296 
297     const TCellData *cellData = dynamic_cast<const TCellData *>(m_data);
298     pasteCellsWithoutUndo(cellData, r0, c0, r1, c1, false, false);
299     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
300   }
301 
redo() const302   void redo() const override {
303     int r0, c0, r1, c1;
304     m_selection->getSelectedCells(r0, c0, r1, c1);
305     deleteCellsWithoutUndo(r0, c0, r1, c1);
306     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
307   }
308 
getSize() const309   int getSize() const override { return sizeof(*this); }
310 
getHistoryString()311   QString getHistoryString() override { return QObject::tr("Delete Cells"); }
getHistoryType()312   int getHistoryType() override { return HistoryType::Xsheet; }
313 };
314 
315 //=============================================================================
316 //  CutCellsUndo
317 //  Just like DeleteCellsUndo, recovering the column information (such as
318 //  reconnecting nodes) when undoing deletion of entire column will be done
319 //  by DeleteColumnsUndo which will be called in the same undo block.
320 //-----------------------------------------------------------------------------
321 
322 class CutCellsUndo final : public TUndo {
323   TCellSelection *m_selection;
324   TCellData *m_data;
325 
326 public:
CutCellsUndo(TCellSelection * selection)327   CutCellsUndo(TCellSelection *selection) : m_data() {
328     int r0, c0, r1, c1;
329     selection->getSelectedCells(r0, c0, r1, c1);
330     if (c0 < 0) c0 = 0;  // Ignore camera column
331     m_selection = new TCellSelection();
332     m_selection->selectCells(r0, c0, r1, c1);
333   }
334 
setCurrentData(int r0,int c0,int r1,int c1)335   void setCurrentData(int r0, int c0, int r1, int c1) {
336     m_data       = new TCellData();
337     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
338     m_data->setCells(xsh, r0, c0, r1, c1);
339   }
340 
~CutCellsUndo()341   ~CutCellsUndo() {
342     delete m_selection;
343     delete m_data;
344   }
345 
undo() const346   void undo() const override {
347     int r0, c0, r1, c1;
348     m_selection->getSelectedCells(r0, c0, r1, c1);
349 
350     pasteCellsWithoutUndo(m_data, r0, c0, r1, c1, true);
351     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
352   }
353 
redo() const354   void redo() const override {
355     QClipboard *clipboard  = QApplication::clipboard();
356     QMimeData *currentData = cloneData(clipboard->mimeData());
357 
358     int r0, c0, r1, c1;
359     m_selection->getSelectedCells(r0, c0, r1, c1);
360     cutCellsWithoutUndo(r0, c0, r1, c1);
361 
362     clipboard->setMimeData(currentData, QClipboard::Clipboard);
363     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
364   }
365 
getSize() const366   int getSize() const override { return sizeof(*this); }
367 
getHistoryString()368   QString getHistoryString() override { return QObject::tr("Cut Cells"); }
getHistoryType()369   int getHistoryType() override { return HistoryType::Xsheet; }
370 };
371 
372 //=============================================================================
373 //  InsertUndo
374 //-----------------------------------------------------------------------------
375 
376 class InsertUndo final : public TUndo {
377   TCellSelection::Range m_range;
378 
379 public:
InsertUndo(const TCellSelection::Range & range)380   InsertUndo(const TCellSelection::Range &range) : m_range(range) {}
381 
undo() const382   void undo() const override {
383     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
384     int rowCount = m_range.getRowCount();
385     int c;
386     for (c = m_range.m_c0; c <= m_range.m_c1; c++)
387       xsh->removeCells(m_range.m_r0, c, rowCount);
388     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
389   }
redo() const390   void redo() const override {
391     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
392     int rowCount = m_range.getRowCount();
393     int c;
394     for (c = m_range.m_c0; c <= m_range.m_c1; c++)
395       xsh->insertCells(m_range.m_r0, c, rowCount);
396     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
397   }
getSize() const398   int getSize() const override { return sizeof(*this); }
getHistoryString()399   QString getHistoryString() override { return QObject::tr("Insert Cells"); }
getHistoryType()400   int getHistoryType() override { return HistoryType::Xsheet; }
401 };
402 
403 //=============================================================================
404 //  RenumberUndo
405 //-----------------------------------------------------------------------------
406 
407 class RenumberUndo {
408 public:
409   class RedoNotifier;
410   class UndoNotifier;
411 };
412 
413 class RenumberUndo::RedoNotifier final : public TUndo {
undo() const414   void undo() const override {}
redo() const415   void redo() const override {
416     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
417     TApp::instance()->getCurrentScene()->setDirtyFlag(true);
418   }
419 
getSize() const420   int getSize() const override { return sizeof(*this); }
421 };
422 
423 class RenumberUndo::UndoNotifier final : public TUndo {
redo() const424   void redo() const override {}
undo() const425   void undo() const override {
426     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
427     TApp::instance()->getCurrentScene()->setDirtyFlag(true);
428   }
429 
getSize() const430   int getSize() const override { return sizeof(*this); }
431 };
432 
433 //=============================================================================
434 
pasteStrokesInCellWithoutUndo(int row,int col,const StrokesData * strokesData,std::vector<int> & indices)435 bool pasteStrokesInCellWithoutUndo(
436     int row, int col, const StrokesData *strokesData,
437     std::vector<int> &indices)  // in indices gli stroke aggiunti
438 {
439   TApp *app     = TApp::instance();
440   TXsheet *xsh  = app->getCurrentXsheet()->getXsheet();
441   TXshCell cell = xsh->getCell(row, col);
442   TVectorImageP vi;
443   TXshSimpleLevel *sl = 0;
444   TFrameId fid(1);
445   if (cell.isEmpty()) {
446     if (row > 0) cell = xsh->getCell(row - 1, col);
447     sl = cell.getSimpleLevel();
448     if (!sl || sl->getType() != PLI_XSHLEVEL) {
449       ToonzScene *scene = app->getCurrentScene()->getScene();
450       TXshLevel *xl     = scene->createNewLevel(PLI_XSHLEVEL);
451       if (!xl) return false;
452       sl = xl->getSimpleLevel();
453       assert(sl);
454       app->getCurrentScene()->notifyCastChange();
455     }
456     vi = sl->createEmptyFrame();
457     assert(vi);
458     std::vector<TFrameId> fids;
459     sl->getFids(fids);
460     if (fids.size() > 0) fid = TFrameId(fids.back().getNumber() + 1);
461     sl->setFrame(fid, vi);
462     app->getCurrentLevel()->setLevel(sl);
463     app->getCurrentLevel()->notifyLevelChange();
464     xsh->setCell(row, col, TXshCell(sl, fid));
465     app->getCurrentXsheet()->notifyXsheetChanged();
466   } else {
467     vi = cell.getImage(true);
468     sl = cell.getSimpleLevel();
469     if (sl->getType() == OVL_XSHLEVEL &&
470         (sl->getPath().getType() == "psd" || sl->getPath().getType() == "gif" ||
471          sl->getPath().getType() == "mp4" || sl->getPath().getType() == "webm"))
472       return false;
473     fid = cell.getFrameId();
474     if (!vi) {
475       DVGui::error(QObject::tr(
476           "It is not possible to paste vectors in the current cell."));
477       return false;
478     }
479   }
480   if (vi) {
481     std::set<int> indicesSet;
482     strokesData->getImage(vi, indicesSet, true);
483     for (int index : indicesSet) {
484       indices.push_back(index);
485     }
486     assert(sl);
487     app->getPaletteController()
488         ->getCurrentLevelPalette()
489         ->notifyPaletteChanged();
490     IconGenerator::instance()->invalidate(sl, fid);
491   }
492   return true;
493 }
494 
495 //=============================================================================
496 //  PasteStrokesInCellsUndo
497 //-----------------------------------------------------------------------------
498 
499 class PasteStrokesInCellUndo final : public TUndo {
500   int m_row, m_col;
501   StrokesData *m_strokesData;
502   TFrameId m_fid;
503   TVectorImageP m_image;
504   TPaletteP m_oldPalette;
505   TXshSimpleLevelP m_sl;
506   bool m_createdFrame;
507   bool m_isLevelCreated;
508   std::vector<int> m_indices;
509 
510 public:
PasteStrokesInCellUndo(int row,int col,const StrokesData * data)511   PasteStrokesInCellUndo(int row, int col, const StrokesData *data)
512       : m_row(row)
513       , m_col(col)
514       , m_strokesData(data->clone())
515       , m_image()
516       , m_oldPalette(0)
517       , m_createdFrame(false)
518       , m_isLevelCreated(false) {
519     TXsheet *xsh         = TApp::instance()->getCurrentXsheet()->getXsheet();
520     TXshCell cell        = xsh->getCell(row, col);
521     TPaletteP oldPalette = 0;
522     if (!cell.getSimpleLevel()) {
523       m_createdFrame      = true;
524       TXshSimpleLevel *sl = xsh->getCell(row - 1, col).getSimpleLevel();
525       if (row <= 0 || !sl || sl->getType() != PLI_XSHLEVEL)
526         m_isLevelCreated = true;
527       else
528         oldPalette = xsh->getCell(row - 1, col).getSimpleLevel()->getPalette();
529     } else
530       oldPalette = cell.getSimpleLevel()->getPalette();
531     if (oldPalette) m_oldPalette = oldPalette->clone();
532   }
533 
~PasteStrokesInCellUndo()534   ~PasteStrokesInCellUndo() { delete m_strokesData; }
535 
setIndices(const std::vector<int> & indices)536   void setIndices(const std::vector<int> &indices) { m_indices = indices; }
537 
onAdd()538   void onAdd() override {
539     TXsheet *xsh  = TApp::instance()->getCurrentXsheet()->getXsheet();
540     TXshCell cell = xsh->getCell(m_row, m_col);
541     m_sl          = cell.getSimpleLevel();
542     m_fid         = cell.m_frameId;
543     m_image       = cell.getImage(false);
544   }
545 
undo() const546   void undo() const override {
547     m_image->removeStrokes(m_indices, true, true);
548     if (m_createdFrame) {
549       TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
550       m_sl->eraseFrame(m_fid);
551       xsh->clearCells(m_row, m_col);
552       if (m_isLevelCreated) {
553         // butta il livello
554         TLevelSet *levelSet =
555             TApp::instance()->getCurrentScene()->getScene()->getLevelSet();
556         if (levelSet) {
557           levelSet->removeLevel(m_sl.getPointer());
558           TApp::instance()->getCurrentScene()->notifyCastChange();
559         }
560       }
561     }
562     if (m_oldPalette.getPointer()) {
563       m_sl->getPalette()->assign(m_oldPalette->clone());
564       TApp::instance()
565           ->getPaletteController()
566           ->getCurrentLevelPalette()
567           ->notifyPaletteChanged();
568     }
569     IconGenerator::instance()->invalidate(m_sl.getPointer(), m_fid);
570     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
571   }
redo() const572   void redo() const override {
573     if (m_isLevelCreated) {
574       TLevelSet *levelSet =
575           TApp::instance()->getCurrentScene()->getScene()->getLevelSet();
576       if (levelSet) {
577         levelSet->insertLevel(m_sl.getPointer());
578         TApp::instance()->getCurrentScene()->notifyCastChange();
579       }
580     }
581     if (m_createdFrame) {
582       TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
583       m_sl->setFrame(m_fid, m_image.getPointer());
584       TXshCell cell(m_sl.getPointer(), m_fid);
585       xsh->setCell(m_row, m_col, cell);
586     }
587     std::vector<int> indices = m_indices;
588     pasteStrokesInCellWithoutUndo(m_row, m_col, m_strokesData, indices);
589     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
590   }
591 
getSize() const592   int getSize() const override { return sizeof *this; }
getHistoryString()593   QString getHistoryString() override { return QObject::tr("Paste (Strokes)"); }
getHistoryType()594   int getHistoryType() override { return HistoryType::Xsheet; }
595 };
596 
597 //=============================================================================
598 
pasteRasterImageInCellWithoutUndo(int row,int col,const RasterImageData * rasterImageData,TTileSet ** tiles,bool & isLevelCreated)599 bool pasteRasterImageInCellWithoutUndo(int row, int col,
600                                        const RasterImageData *rasterImageData,
601                                        TTileSet **tiles, bool &isLevelCreated) {
602   isLevelCreated = false;
603   TApp *app      = TApp::instance();
604   TXsheet *xsh   = app->getCurrentXsheet()->getXsheet();
605   TXshCell cell  = xsh->getCell(row, col);
606   TImageP img;
607   TXshSimpleLevel *sl = 0;
608   TFrameId fid(1);
609   ToonzScene *scene = app->getCurrentScene()->getScene();
610   TCamera *camera   = scene->getCurrentCamera();
611   if (cell.isEmpty()) {
612     if (row > 0) cell = xsh->getCell(row - 1, col);
613     sl = cell.getSimpleLevel();
614     if (!sl || (sl->getType() == OVL_XSHLEVEL &&
615                 sl->getPath().getFrame() == TFrameId::NO_FRAME)) {
616       int levelType;
617       if (dynamic_cast<const ToonzImageData *>(rasterImageData))
618         levelType = TZP_XSHLEVEL;
619       else if (dynamic_cast<const FullColorImageData *>(rasterImageData))
620         levelType = OVL_XSHLEVEL;
621       TXshLevel *xl = 0;
622       if (levelType == TZP_XSHLEVEL)
623         xl = scene->createNewLevel(TZP_XSHLEVEL, L"", rasterImageData->getDim(),
624                                    rasterImageData->getDpi().x);
625       else if (levelType == OVL_XSHLEVEL)
626         xl = scene->createNewLevel(OVL_XSHLEVEL, L"", rasterImageData->getDim(),
627                                    rasterImageData->getDpi().x);
628       if (!xl) return false;
629       isLevelCreated = true;
630       sl             = xl->getSimpleLevel();
631       assert(sl);
632       app->getCurrentScene()->notifyCastChange();
633 
634       if (levelType == TZP_XSHLEVEL || levelType == OVL_XSHLEVEL) {
635         img = sl->createEmptyFrame();
636       } else
637         return false;
638       sl->setFrame(fid, img);
639       app->getCurrentLevel()->setLevel(sl);
640       app->getCurrentLevel()->notifyLevelChange();
641       sl->save();
642       // after saving you need to obtain the image again
643       img = sl->getFrame(fid, true);
644     } else {
645       img = sl->createEmptyFrame();
646       assert(img);
647       std::vector<TFrameId> fids;
648       sl->getFids(fids);
649       if (fids.size() > 0) fid = TFrameId(fids.back().getNumber() + 1);
650       sl->setFrame(fid, img);
651     }
652     xsh->setCell(row, col, TXshCell(sl, fid));
653     // to let the undo to know which frame is edited
654     TTool::m_cellsData.push_back({row, row, TTool::CellOps::BlankToNew});
655   } else {
656     sl  = cell.getSimpleLevel();
657     fid = cell.getFrameId();
658     img = cell.getImage(true);
659     if (!img) {
660       DVGui::error(QObject::tr(
661           "It is not possible to paste image on the current cell."));
662       return false;
663     }
664   }
665   if (img) {
666     TRasterP ras;
667     TRasterP outRas;
668     double imgDpiX, imgDpiY;
669     if (TToonzImageP ti = (TToonzImageP)(img)) {
670       outRas = ti->getRaster();
671       ti->getDpi(imgDpiX, imgDpiY);
672     }
673     if (TRasterImageP ri = (TRasterImageP)(img)) {
674       outRas = ri->getRaster();
675       ri->getDpi(imgDpiX, imgDpiY);
676     }
677     double dpiX, dpiY;
678     std::vector<TRectD> rects;
679     std::vector<TStroke> strokes;
680     std::vector<TStroke> originalStrokes;
681     TAffine affine;
682     rasterImageData->getData(ras, dpiX, dpiY, rects, strokes, originalStrokes,
683                              affine, img->getPalette());
684     if (dpiX == 0 || dpiY == 0) {
685       TPointD dpi = camera->getDpi();
686       dpiX        = dpi.x;
687       dpiY        = dpi.y;
688     }
689     if (imgDpiX == 0 || imgDpiY == 0) {
690       TPointD dpi = camera->getDpi();
691       imgDpiX     = dpi.x;
692       imgDpiY     = dpi.y;
693     }
694     TScale sc(imgDpiX / dpiX, imgDpiY / dpiY);
695     affine *= sc;
696     int i;
697     TRectD boxD;
698     if (rects.size() > 0) boxD = rects[0];
699     if (strokes.size() > 0) boxD = strokes[0].getBBox();
700     for (i = 0; i < rects.size(); i++) boxD += rects[i];
701     for (i = 0; i < strokes.size(); i++) boxD += strokes[i].getBBox();
702     boxD = affine * boxD;
703     TPoint pos;
704     if (sl->getType() == TZP_XSHLEVEL) {
705       TRect box = ToonzImageUtils::convertWorldToRaster(boxD, img);
706       pos       = box.getP00();
707       *tiles    = new TTileSetCM32(outRas->getSize());
708       (*tiles)->add(outRas, ras->getBounds() + pos);
709     } else if (sl->getType() == OVL_XSHLEVEL || sl->getType() == TZI_XSHLEVEL) {
710       TRect box = TRasterImageUtils::convertWorldToRaster(boxD, img);
711       pos       = box.getP00();
712       *tiles    = new TTileSetFullColor(outRas->getSize());
713       (*tiles)->add(outRas, ras->getBounds() + pos);
714     } else
715       return false;
716     TRasterCM32P rasCM    = ras;
717     TRasterCM32P outRasCM = outRas;
718     if (!outRasCM && rasCM)
719       TRop::over(outRas, rasCM, img->getPalette(), pos, affine);
720     else
721       TRop::over(outRas, ras, pos, affine);
722     assert(sl);
723     ToolUtils::updateSaveBox(sl, fid);
724     IconGenerator::instance()->invalidate(sl, fid);
725     sl->setDirtyFlag(true);
726     app->getPaletteController()
727         ->getCurrentLevelPalette()
728         ->notifyPaletteChanged();
729   }
730   return true;
731 }
732 
733 //=============================================================================
734 //  PasteToonzImageInCellsUndo
735 //-----------------------------------------------------------------------------
736 
737 class PasteToonzImageInCellsUndo final : public ToolUtils::TRasterUndo {
738   RasterImageData *m_rasterImageData;
739 
740 public:
PasteToonzImageInCellsUndo(int row,int col,const RasterImageData * data,TTileSetCM32 * tiles,TXshSimpleLevel * level,const TFrameId & id,TPaletteP oldPalette,bool createdFrame,bool isLevelCreated)741   PasteToonzImageInCellsUndo(int row, int col, const RasterImageData *data,
742                              TTileSetCM32 *tiles, TXshSimpleLevel *level,
743                              const TFrameId &id, TPaletteP oldPalette,
744                              bool createdFrame, bool isLevelCreated)
745       : ToolUtils::TRasterUndo(tiles, level, id, createdFrame, isLevelCreated,
746                                oldPalette)
747       , m_rasterImageData(data->clone()) {}
748 
~PasteToonzImageInCellsUndo()749   ~PasteToonzImageInCellsUndo() { delete m_rasterImageData; }
750 
redo() const751   void redo() const override {
752     insertLevelAndFrameIfNeeded();
753     TTileSet *tiles;
754     bool isLevelCreated;
755     pasteRasterImageInCellWithoutUndo(m_row, m_col, m_rasterImageData, &tiles,
756                                       isLevelCreated);
757     delete tiles;
758     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
759   }
760 
getSize() const761   int getSize() const override {
762     return m_rasterImageData->getMemorySize() + TRasterUndo::getSize();
763   }
getHistoryString()764   QString getHistoryString() override { return QObject::tr("Paste"); }
getHistoryType()765   int getHistoryType() override { return HistoryType::Xsheet; }
766 };
767 
768 //=============================================================================
769 //  PasteFullColorImageInCellsUndo
770 //-----------------------------------------------------------------------------
771 
772 class PasteFullColorImageInCellsUndo final
773     : public ToolUtils::TFullColorRasterUndo {
774   RasterImageData *m_rasterImageData;
775 
776 public:
PasteFullColorImageInCellsUndo(const RasterImageData * data,TTileSetFullColor * tiles,TXshSimpleLevel * level,const TFrameId & id,TPaletteP oldPalette,bool createdFrame,bool isLevelCreated)777   PasteFullColorImageInCellsUndo(const RasterImageData *data,
778                                  TTileSetFullColor *tiles,
779                                  TXshSimpleLevel *level, const TFrameId &id,
780                                  TPaletteP oldPalette, bool createdFrame,
781                                  bool isLevelCreated)
782       : ToolUtils::TFullColorRasterUndo(tiles, level, id, createdFrame,
783                                         isLevelCreated, oldPalette)
784       , m_rasterImageData(data->clone()) {}
785 
~PasteFullColorImageInCellsUndo()786   ~PasteFullColorImageInCellsUndo() { delete m_rasterImageData; }
787 
redo() const788   void redo() const override {
789     insertLevelAndFrameIfNeeded();
790     TTileSet *tiles = 0;
791     bool isLevelCreated;
792     pasteRasterImageInCellWithoutUndo(m_row, m_col, m_rasterImageData, &tiles,
793                                       isLevelCreated);
794     if (tiles) delete tiles;
795     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
796   }
797 
getSize() const798   int getSize() const override {
799     return m_rasterImageData->getMemorySize() + TFullColorRasterUndo::getSize();
800   }
getHistoryString()801   QString getHistoryString() override { return QObject::tr("Paste (Raster)"); }
getHistoryType()802   int getHistoryType() override { return HistoryType::Xsheet; }
803 };
804 
805 //=============================================================================
806 
pasteDrawingsInCellWithoutUndo(TXsheet * xsh,TXshSimpleLevel * level,const std::set<TFrameId> & frameIds,int r0,int c0)807 void pasteDrawingsInCellWithoutUndo(TXsheet *xsh, TXshSimpleLevel *level,
808                                     const std::set<TFrameId> &frameIds, int r0,
809                                     int c0) {
810   int frameToInsert = frameIds.size();
811   xsh->insertCells(r0, c0, frameToInsert);
812   std::set<TFrameId>::const_iterator it;
813   int r = r0;
814   for (it = frameIds.begin(); it != frameIds.end(); it++, r++) {
815     TXshCell cell(level, *it);
816     xsh->setCell(r, c0, cell);
817   }
818 }
819 
820 //=============================================================================
821 //  PasteDrawingsInCellUndo
822 //-----------------------------------------------------------------------------
823 
824 class PasteDrawingsInCellUndo final : public TUndo {
825   TXsheet *m_xsheet;
826   int m_r0, m_c0;
827   std::set<TFrameId> m_frameIds;
828   TXshSimpleLevelP m_level;
829 
830 public:
PasteDrawingsInCellUndo(TXshSimpleLevel * level,const std::set<TFrameId> & frameIds,int r0,int c0)831   PasteDrawingsInCellUndo(TXshSimpleLevel *level,
832                           const std::set<TFrameId> &frameIds, int r0, int c0)
833       : TUndo(), m_level(level), m_frameIds(frameIds), m_r0(r0), m_c0(c0) {
834     m_xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
835     m_xsheet->addRef();
836   }
837 
838   //--------------------------------
839 
~PasteDrawingsInCellUndo()840   ~PasteDrawingsInCellUndo() { m_xsheet->release(); }
841 
undo() const842   void undo() const override {
843     int cellsToRemove = m_frameIds.size();
844     m_xsheet->removeCells(m_r0, m_c0, cellsToRemove);
845     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
846   }
847 
redo() const848   void redo() const override {
849     pasteDrawingsInCellWithoutUndo(m_xsheet, m_level.getPointer(), m_frameIds,
850                                    m_r0, m_c0);
851     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
852   }
853 
getSize() const854   int getSize() const override { return sizeof *this; }
855 
getHistoryString()856   QString getHistoryString() override { return QObject::tr("Paste"); }
getHistoryType()857   int getHistoryType() override { return HistoryType::Xsheet; }
858 };
859 
860 //=============================================================================
861 
pasteCellsWithoutUndo(int & r0,int & c0,int & r1,int & c1,bool insert=true,bool doZeraryClone=true,bool skipEmptyCells=true)862 bool pasteCellsWithoutUndo(int &r0, int &c0, int &r1, int &c1,
863                            bool insert = true, bool doZeraryClone = true,
864                            bool skipEmptyCells = true) {
865   TXsheet *xsh              = TApp::instance()->getCurrentXsheet()->getXsheet();
866   QClipboard *clipboard     = QApplication::clipboard();
867   const QMimeData *mimeData = clipboard->mimeData();
868   const TCellData *cellData = dynamic_cast<const TCellData *>(mimeData);
869 
870   if (!cellData) return false;
871 
872   if (r0 < 0 || c0 < 0) return false;
873 
874   /*-- この中で、r1,c1はペースト範囲にあわせリサイズされる --*/
875   bool ret = cellData->getCells(xsh, r0, c0, r1, c1, insert, doZeraryClone,
876                                 skipEmptyCells);
877   if (!ret) return false;
878 
879   // Se la selezione corrente e' TCellSelection selezione le celle copiate
880   TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
881       TApp::instance()->getCurrentSelection()->getSelection());
882   if (cellSelection) cellSelection->selectCells(r0, c0, r1, c1);
883   return true;
884 }
885 
886 //=============================================================================
887 
888 class OverwritePasteCellsUndo final : public TUndo {
889   TCellSelection *m_oldSelection;
890   TCellSelection *m_newSelection;
891   QMimeData *m_data;
892   std::vector<bool> m_areOldColumnsEmpty;
893 
894   QMimeData *m_beforeData;
895 
896 public:
OverwritePasteCellsUndo(int r0,int c0,int r1,int c1,int oldR0,int oldC0,int oldR1,int oldC1,const std::vector<bool> & areColumnsEmpty,TCellData * beforeData)897   OverwritePasteCellsUndo(int r0, int c0, int r1, int c1, int oldR0, int oldC0,
898                           int oldR1, int oldC1,
899                           const std::vector<bool> &areColumnsEmpty,
900                           TCellData *beforeData)
901       : m_areOldColumnsEmpty(areColumnsEmpty) {
902     QClipboard *clipboard = QApplication::clipboard();
903     /*-- ペーストされたセルをdataに保持しておく --*/
904     TCellData *data = new TCellData();
905     TXsheet *xsh    = TApp::instance()->getCurrentXsheet()->getXsheet();
906     data->setCells(xsh, r0, c0, r1, c1);
907     m_data         = data->clone();
908     m_newSelection = new TCellSelection();
909     m_newSelection->selectCells(r0, c0, r1, c1);
910     m_oldSelection = new TCellSelection();
911     m_oldSelection->selectCells(oldR0, oldC0, oldR1, oldC1);
912 
913     /*-- さらに、ペースト前のセルも保持しておく --*/
914     m_beforeData = beforeData->clone();
915   }
916 
~OverwritePasteCellsUndo()917   ~OverwritePasteCellsUndo() {
918     delete m_newSelection;
919     delete m_oldSelection;
920     delete m_data;
921     delete m_beforeData;
922   }
923 
undo() const924   void undo() const override {
925     int r0, c0, r1, c1;
926     m_newSelection->getSelectedCells(r0, c0, r1, c1);
927 
928     int oldR0, oldC0, oldR1, oldC1;
929     m_oldSelection->getSelectedCells(oldR0, oldC0, oldR1, oldC1);
930 
931     QClipboard *clipboard = QApplication::clipboard();
932     int c0BeforeCut       = c0;
933     int c1BeforeCut       = c1;
934     cutCellsWithoutUndo(r0, c0, r1, c1);
935 
936     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
937     assert(c1BeforeCut - c0BeforeCut + 1 == (int)m_areOldColumnsEmpty.size());
938     int c;
939     for (c = c0BeforeCut; c <= c1BeforeCut; c++) {
940       if (!m_areOldColumnsEmpty[c - c0BeforeCut] || !xsh->getColumn(c))
941         continue;
942       xsh->removeColumn(c);
943       xsh->insertColumn(c);
944     }
945 
946     /*-- クリップボードの内容を取っておく --*/
947     QMimeData *mimeData = cloneData(clipboard->mimeData());
948 
949     /*--
950      * ペースト前にあったセル配列をcellDataとしていったんクリップボードに入れ、ペーストさせる
951      * --*/
952     clipboard->setMimeData(cloneData(m_beforeData), QClipboard::Clipboard);
953     pasteCellsWithoutUndo(r0, c0, r1, c1, true, false, false);
954 
955     /*-- クリップボードを元に戻す --*/
956     clipboard->setMimeData(mimeData, QClipboard::Clipboard);
957 
958     // Se le selezione corrente e' TCellSelection seleziono le celle che sono in
959     // oldSelection
960     TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
961         TApp::instance()->getCurrentSelection()->getSelection());
962     if (cellSelection && oldR0 != -1 && oldC0 != -1)
963       cellSelection->selectCells(oldR0, oldC0, oldR1, oldC1);
964 
965     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
966   }
967 
redo() const968   void redo() const override {
969     int r0, c0, r1, c1;
970     m_newSelection->getSelectedCells(r0, c0, c1, r1);
971     QClipboard *clipboard = QApplication::clipboard();
972     clipboard->setMimeData(cloneData(m_data), QClipboard::Clipboard);
973     // Cut delle celle che sono in newSelection
974     pasteCellsWithoutUndo(r0, c0, c1, r1, false, false);
975     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
976   }
977 
getSize() const978   int getSize() const override { return sizeof(*this); }
979 
getHistoryString()980   QString getHistoryString() override {
981     return QObject::tr("Overwrite Paste Cells");
982   }
getHistoryType()983   int getHistoryType() override { return HistoryType::Xsheet; }
984 };
985 
986 //=============================================================================
987 
pasteNumbersWithoutUndo(int & r0,int & c0,int & r1,int & c1)988 bool pasteNumbersWithoutUndo(int &r0, int &c0, int &r1, int &c1) {
989   TXsheet *xsh              = TApp::instance()->getCurrentXsheet()->getXsheet();
990   QClipboard *clipboard     = QApplication::clipboard();
991   const QMimeData *mimeData = clipboard->mimeData();
992   const TCellData *cellData = dynamic_cast<const TCellData *>(mimeData);
993 
994   if (!cellData) return false;
995 
996   if (r0 < 0 || c0 < 0) return false;
997 
998   bool ret = cellData->getNumbers(xsh, r0, c0, r1, c1);
999   if (!ret) return false;
1000 
1001   // Se la selezione corrente e' TCellSelection selezione le celle copiate
1002   TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
1003       TApp::instance()->getCurrentSelection()->getSelection());
1004   if (cellSelection) cellSelection->selectCells(r0, c0, r1, c1);
1005   return true;
1006 }
1007 
1008 //=============================================================================
1009 
1010 class OverwritePasteNumbersUndo final : public TUndo {
1011   TCellSelection *m_oldSelection;
1012   TCellSelection *m_newSelection;
1013   QMimeData *m_data;
1014   QMimeData *m_beforeData;
1015 
1016 public:
OverwritePasteNumbersUndo(int r0,int c0,int r1,int c1,int oldR0,int oldC0,int oldR1,int oldC1,TCellData * beforeData)1017   OverwritePasteNumbersUndo(int r0, int c0, int r1, int c1, int oldR0,
1018                             int oldC0, int oldR1, int oldC1,
1019                             TCellData *beforeData) {
1020     QClipboard *clipboard = QApplication::clipboard();
1021     // keep the pasted data
1022     TCellData *data = new TCellData();
1023     TXsheet *xsh    = TApp::instance()->getCurrentXsheet()->getXsheet();
1024     data->setCells(xsh, r0, c0, r1, c1);
1025     m_data         = data->clone();
1026     m_newSelection = new TCellSelection();
1027     m_newSelection->selectCells(r0, c0, r1, c1);
1028     m_oldSelection = new TCellSelection();
1029     m_oldSelection->selectCells(oldR0, oldC0, oldR1, oldC1);
1030     // keep the cells before pasting
1031     m_beforeData = beforeData->clone();
1032   }
1033 
~OverwritePasteNumbersUndo()1034   ~OverwritePasteNumbersUndo() {
1035     delete m_newSelection;
1036     delete m_oldSelection;
1037     delete m_data;
1038     delete m_beforeData;
1039   }
1040 
undo() const1041   void undo() const override {
1042     int r0, c0, r1, c1;
1043     m_newSelection->getSelectedCells(r0, c0, r1, c1);
1044 
1045     int oldR0, oldC0, oldR1, oldC1;
1046     m_oldSelection->getSelectedCells(oldR0, oldC0, oldR1, oldC1);
1047 
1048     QClipboard *clipboard = QApplication::clipboard();
1049     int c0BeforeCut       = c0;
1050     int c1BeforeCut       = c1;
1051     cutCellsWithoutUndo(r0, c0, r1, c1);
1052 
1053     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1054 
1055     // keep the clipboard content
1056     QMimeData *mimeData = cloneData(clipboard->mimeData());
1057 
1058     // execute pasting
1059     clipboard->setMimeData(cloneData(m_beforeData), QClipboard::Clipboard);
1060     pasteCellsWithoutUndo(r0, c0, r1, c1, true, false, false);
1061 
1062     // restore clipboard
1063     clipboard->setMimeData(mimeData, QClipboard::Clipboard);
1064 
1065     // revert old cell selection
1066     TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
1067         TApp::instance()->getCurrentSelection()->getSelection());
1068     if (cellSelection && oldR0 != -1 && oldC0 != -1)
1069       cellSelection->selectCells(oldR0, oldC0, oldR1, oldC1);
1070 
1071     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1072   }
1073 
redo() const1074   void redo() const override {
1075     int r0, c0, r1, c1;
1076     m_newSelection->getSelectedCells(r0, c0, c1, r1);
1077     QClipboard *clipboard = QApplication::clipboard();
1078 
1079     // keep the clipboard content
1080     QMimeData *mimeData = cloneData(clipboard->mimeData());
1081 
1082     clipboard->setMimeData(cloneData(m_data), QClipboard::Clipboard);
1083     // Cut delle celle che sono in newSelection
1084     pasteCellsWithoutUndo(r0, c0, c1, r1, false, false);
1085 
1086     // restore clipboard
1087     clipboard->setMimeData(mimeData, QClipboard::Clipboard);
1088 
1089     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1090   }
1091 
getSize() const1092   int getSize() const override { return sizeof(*this); }
1093 
getHistoryString()1094   QString getHistoryString() override { return QObject::tr("Paste Numbers"); }
getHistoryType()1095   int getHistoryType() override { return HistoryType::Xsheet; }
1096 };
1097 
1098 //-----------------------------------------------------------------------------
1099 // if at least one of the cell in the range, return false
checkIfCellsHaveTheSameContent(const int & r0,const int & c0,const int & r1,const int & c1,const TXshCell & cell)1100 bool checkIfCellsHaveTheSameContent(const int &r0, const int &c0, const int &r1,
1101                                     const int &c1, const TXshCell &cell) {
1102   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1103   for (int c = c0; c <= c1; c++) {
1104     for (int r = r0; r <= r1; r++) {
1105       if (cell != xsh->getCell(r, c)) return false;
1106     }
1107   }
1108   return true;
1109 }
1110 
renameCellsWithoutUndo(int & r0,int & c0,int & r1,int & c1,const TXshCell & cell)1111 void renameCellsWithoutUndo(int &r0, int &c0, int &r1, int &c1,
1112                             const TXshCell &cell) {
1113   TApp *app    = TApp::instance();
1114   TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1115   for (int c = c0; c <= c1; c++) {
1116     for (int r = r0; r <= r1; r++) {
1117       xsh->setCell(r, c, TXshCell(cell));
1118     }
1119   }
1120   app->getCurrentXsheet()->notifyXsheetChanged();
1121   int currentFrame = app->getCurrentFrame()->getFrame();
1122   if (r0 <= currentFrame && currentFrame <= r1) {
1123     app->getCurrentTool()->onImageChanged(
1124         (TImage::Type)app->getCurrentImageType());
1125     xsh->getStageObjectTree()->invalidateAll();
1126   }
1127 }
1128 
1129 class RenameCellsUndo final : public TUndo {
1130   TCellSelection *m_selection;
1131   QMimeData *m_data;
1132   TXshCell m_cell;
1133 
1134 public:
RenameCellsUndo(TCellSelection * selection,QMimeData * data,TXshCell & cell)1135   RenameCellsUndo(TCellSelection *selection, QMimeData *data, TXshCell &cell)
1136       : m_data(data), m_cell(cell) {
1137     int r0, c0, r1, c1;
1138     selection->getSelectedCells(r0, c0, r1, c1);
1139     m_selection = new TCellSelection();
1140     m_selection->selectCells(r0, c0, r1, c1);
1141   }
1142 
RenameCellsUndo(int r0,int c0,int r1,int c1,QMimeData * data,TXshCell & cell)1143   RenameCellsUndo(int r0, int c0, int r1, int c1, QMimeData *data,
1144                   TXshCell &cell)
1145       : m_data(data), m_cell(cell) {
1146     m_selection = new TCellSelection();
1147     m_selection->selectCells(r0, c0, r1, c1);
1148   }
1149 
~RenameCellsUndo()1150   ~RenameCellsUndo() {
1151     delete m_selection;
1152     delete m_data;
1153   }
1154 
undo() const1155   void undo() const override {
1156     int r0, c0, r1, c1;
1157     m_selection->getSelectedCells(r0, c0, r1, c1);
1158     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1159     for (int c = c0; c <= c1; c++) {
1160       xsh->clearCells(r0, c, r1 - r0 + 1);
1161     }
1162     const TCellData *cellData = dynamic_cast<const TCellData *>(m_data);
1163     pasteCellsWithoutUndo(cellData, r0, c0, r1, c1, false, false);
1164     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1165   }
1166 
redo() const1167   void redo() const override {
1168     int r0, c0, r1, c1;
1169     m_selection->getSelectedCells(r0, c0, r1, c1);
1170     renameCellsWithoutUndo(r0, c0, r1, c1, m_cell);
1171   }
1172 
getSize() const1173   int getSize() const override { return sizeof(*this); }
1174 
getHistoryString()1175   QString getHistoryString() override {
1176     int r0, c0, r1, c1;
1177     m_selection->getSelectedCells(r0, c0, r1, c1);
1178     QString colRange = QString::number(c0 + 1);
1179     if (c0 != c1)
1180       colRange.append(QString(" - %1").arg(QString::number(c1 + 1)));
1181     QString frameRange = QString::number(r0 + 1);
1182     if (r0 != r1)
1183       frameRange.append(QString(" - %1").arg(QString::number(r1 + 1)));
1184 
1185     return QObject::tr("Rename Cell  at Column %1  Frame %2")
1186         .arg(colRange)
1187         .arg(frameRange);
1188   }
1189 
getHistoryType()1190   int getHistoryType() override { return HistoryType::Xsheet; }
1191 };
1192 
1193 //-----------------------------------------------------------------------------
1194 
1195 class CreateBlankDrawingUndo final : public ToolUtils::TToolUndo {
1196   int row;
1197   int col;
1198 
1199 public:
CreateBlankDrawingUndo(TXshSimpleLevel * level,const TFrameId & frameId,bool levelCreated,const TPaletteP & oldPalette)1200   CreateBlankDrawingUndo(TXshSimpleLevel *level, const TFrameId &frameId,
1201                          bool levelCreated, const TPaletteP &oldPalette)
1202       : TToolUndo(level, frameId, true, levelCreated, oldPalette) {}
1203 
~CreateBlankDrawingUndo()1204   ~CreateBlankDrawingUndo() {}
1205 
undo() const1206   void undo() const override {
1207     removeLevelAndFrameIfNeeded();
1208     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1209     notifyImageChanged();
1210   }
1211 
redo() const1212   void redo() const override {
1213     insertLevelAndFrameIfNeeded();
1214 
1215     IconGenerator::instance()->invalidate(m_level.getPointer(), m_frameId);
1216 
1217     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1218     notifyImageChanged();
1219   }
1220 
getSize() const1221   int getSize() const override { return sizeof(*this); }
1222 
getHistoryString()1223   QString getHistoryString() override {
1224     return QObject::tr("Create Blank Drawing");
1225   }
1226 
getHistoryType()1227   int getHistoryType() override { return HistoryType::Xsheet; }
1228   //-----------------------------------------------------------------------------
1229 };
1230 
1231 //-----------------------------------------------------------------------------
1232 
1233 class DuplicateDrawingUndo final : public ToolUtils::TToolUndo {
1234   TFrameId origFrameId;
1235   TFrameId dupFrameId;
1236 
1237 public:
DuplicateDrawingUndo(TXshSimpleLevel * level,const TFrameId & srcFrameId,const TFrameId & tgtFrameId)1238   DuplicateDrawingUndo(TXshSimpleLevel *level, const TFrameId &srcFrameId,
1239                        const TFrameId &tgtFrameId)
1240       : TToolUndo(level, tgtFrameId, true) {
1241     origFrameId = srcFrameId;
1242     dupFrameId  = tgtFrameId;
1243   }
1244 
~DuplicateDrawingUndo()1245   ~DuplicateDrawingUndo() {}
1246 
undo() const1247   void undo() const override {
1248     removeLevelAndFrameIfNeeded();
1249     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1250     notifyImageChanged();
1251   }
1252 
redo() const1253   void redo() const override {
1254     insertLevelAndFrameIfNeeded();
1255 
1256     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1257     notifyImageChanged();
1258   }
1259 
getSize() const1260   int getSize() const override { return sizeof(*this); }
1261 
getHistoryString()1262   QString getHistoryString() override {
1263     return QObject::tr("Duplicate Drawing");
1264   }
1265 
getHistoryType()1266   int getHistoryType() override { return HistoryType::Xsheet; }
1267   //-----------------------------------------------------------------------------
1268 };
1269 
1270 //-----------------------------------------------------------------------------
1271 
1272 class FillEmptyCellUndo final : public TUndo {
1273   TCellSelection *m_selection;
1274   TXshCell m_cell;
1275 
1276 public:
FillEmptyCellUndo(int r0,int r1,int c,TXshCell & cell)1277   FillEmptyCellUndo(int r0, int r1, int c, TXshCell &cell) : m_cell(cell) {
1278     m_selection = new TCellSelection();
1279     m_selection->selectCells(r0, c, r1, c);
1280   }
1281 
~FillEmptyCellUndo()1282   ~FillEmptyCellUndo() { delete m_selection; }
1283 
undo() const1284   void undo() const override {
1285     int r0, c0, r1, c1;
1286     m_selection->getSelectedCells(r0, c0, r1, c1);
1287     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1288     for (int c = c0; c <= c1; c++) {
1289       xsh->clearCells(r0, c, r1 - r0 + 1);
1290     }
1291     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1292   }
1293 
redo() const1294   void redo() const override {
1295     int r0, c0, r1, c1;
1296     m_selection->getSelectedCells(r0, c0, r1, c1);
1297     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1298     for (int i = r0; i <= r1; i++) xsh->setCell(i, c0, m_cell);
1299     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1300   }
1301 
getSize() const1302   int getSize() const override { return sizeof(*this); }
1303 
getHistoryString()1304   QString getHistoryString() override {
1305     return QObject::tr("Fill In Empty Cells");
1306   }
1307 
getHistoryType()1308   int getHistoryType() override { return HistoryType::Xsheet; }
1309   //-----------------------------------------------------------------------------
1310 };
1311 
1312 //=============================================================================
1313 // Duplicate in XSheet Undo
1314 //-----------------------------------------------------------------------------
1315 
1316 class DuplicateInXSheetUndo final : public TUndo {
1317   int m_r0, m_r1, m_c;
1318   TXshCell m_oldCell;
1319   TXshCell m_newCell;
1320   bool m_goForward;
1321 
1322 public:
DuplicateInXSheetUndo(int r0,int r1,int c,TXshCell oldCell,TXshCell newCell,bool goForward)1323   DuplicateInXSheetUndo(int r0, int r1, int c, TXshCell oldCell,
1324                         TXshCell newCell, bool goForward)
1325       : m_r0(r0)
1326       , m_r1(r1)
1327       , m_c(c)
1328       , m_oldCell(oldCell)
1329       , m_newCell(newCell)
1330       , m_goForward(goForward) {}
1331 
~DuplicateInXSheetUndo()1332   ~DuplicateInXSheetUndo() {}
1333 
undo() const1334   void undo() const override {
1335     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1336     if (m_r1 > m_r0) {
1337       for (int i = m_r0; i <= m_r1; i++) {
1338         xsh->setCell(i, m_c, m_oldCell);
1339       }
1340     } else
1341       xsh->setCell(m_r0, m_c, m_oldCell);
1342     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1343     if (m_goForward) CommandManager::instance()->execute(MI_PrevFrame);
1344   }
1345 
redo() const1346   void redo() const override {
1347     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1348     if (m_r1 > m_r0) {
1349       for (int i = m_r0; i <= m_r1; i++) {
1350         xsh->setCell(i, m_c, m_newCell);
1351       }
1352     } else
1353       xsh->setCell(m_r0, m_c, m_newCell);
1354     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1355     if (m_goForward) CommandManager::instance()->execute(MI_NextFrame);
1356   }
1357 
getSize() const1358   int getSize() const override { return sizeof(*this); }
1359 
getHistoryString()1360   QString getHistoryString() override {
1361     return QObject::tr("Duplicate Frame in XSheet");
1362   }
1363 
getHistoryType()1364   int getHistoryType() override { return HistoryType::Xsheet; }
1365   //-----------------------------------------------------------------------------
1366 };
1367 
1368 //-----------------------------------------------------------------------------
1369 // obtain level set contained in the cellData
1370 // it is used for checking and updating the scene cast when pasting
getLevelSetFromData(const TCellData * cellData,std::set<TXshLevel * > & levelSet)1371 void getLevelSetFromData(const TCellData *cellData,
1372                          std::set<TXshLevel *> &levelSet) {
1373   for (int c = 0; c < cellData->getCellCount(); c++) {
1374     TXshCell cell = cellData->getCell(c);
1375     if (cell.isEmpty()) continue;
1376     TXshLevelP tmpLevel = cell.m_level;
1377     if (levelSet.count(tmpLevel.getPointer()) == 0) {
1378       // gather levels in subxsheet
1379       if (tmpLevel->getChildLevel()) {
1380         TXsheet *childXsh = tmpLevel->getChildLevel()->getXsheet();
1381         childXsh->getUsedLevels(levelSet);
1382       }
1383       levelSet.insert(tmpLevel.getPointer());
1384     }
1385   }
1386 }
1387 
1388 }  // namespace
1389 //-----------------------------------------------------------------------------
1390 
1391 //=============================================================================
1392 // TCellSelection::Range
1393 //-----------------------------------------------------------------------------
1394 
Range()1395 TCellSelection::Range::Range() : m_c0(0), m_r0(0), m_c1(-1), m_r1(-1) {}
1396 
isEmpty() const1397 bool TCellSelection::Range::isEmpty() const {
1398   return m_c0 > m_c1 || m_r0 > m_r1;
1399 }
1400 
contains(int r,int c) const1401 bool TCellSelection::Range::contains(int r, int c) const {
1402   return m_r0 <= r && r <= m_r1 && m_c0 <= c && c <= m_c1;
1403 }
1404 
getRowCount() const1405 int TCellSelection::Range::getRowCount() const { return m_r1 - m_r0 + 1; }
1406 
getColCount() const1407 int TCellSelection::Range::getColCount() const { return m_c1 - m_c0 + 1; }
1408 
1409 //=============================================================================
1410 // TCellSelection
1411 //-----------------------------------------------------------------------------
1412 
TCellSelection()1413 TCellSelection::TCellSelection() : m_timeStretchPopup(0), m_reframePopup(0) {}
1414 
1415 //-----------------------------------------------------------------------------
1416 
~TCellSelection()1417 TCellSelection::~TCellSelection() {}
1418 
1419 //-----------------------------------------------------------------------------
1420 
enableCommands()1421 void TCellSelection::enableCommands() {
1422   // enableCommand(this, MI_PasteNew,     &TCellSelection::dPasteCells);
1423   enableCommand(this, MI_Autorenumber, &TCellSelection::dRenumberCells);
1424 
1425   enableCommand(this, MI_Reverse, &TCellSelection::reverseCells);
1426   enableCommand(this, MI_Swing, &TCellSelection::swingCells);
1427   enableCommand(this, MI_Random, &TCellSelection::randomCells);
1428   enableCommand(this, MI_Increment, &TCellSelection::incrementCells);
1429   enableCommand(this, MI_ResetStep, &TCellSelection::resetStepCells);
1430   enableCommand(this, MI_IncreaseStep, &TCellSelection::increaseStepCells);
1431   enableCommand(this, MI_DecreaseStep, &TCellSelection::decreaseStepCells);
1432   enableCommand(this, MI_Step2, &TCellSelection::step2Cells);
1433   enableCommand(this, MI_Step3, &TCellSelection::step3Cells);
1434   enableCommand(this, MI_Step4, &TCellSelection::step4Cells);
1435   enableCommand(this, MI_Each2, &TCellSelection::each2Cells);
1436   enableCommand(this, MI_Each3, &TCellSelection::each3Cells);
1437   enableCommand(this, MI_Each4, &TCellSelection::each4Cells);
1438 
1439   enableCommand(this, MI_Rollup, &TCellSelection::rollupCells);
1440   enableCommand(this, MI_Rolldown, &TCellSelection::rolldownCells);
1441 
1442   enableCommand(this, MI_TimeStretch, &TCellSelection::openTimeStretchPopup);
1443   enableCommand(this, MI_CloneLevel, &TCellSelection::cloneLevel);
1444   enableCommand(this, MI_SetKeyframes, &TCellSelection::setKeyframes);
1445 
1446   enableCommand(this, MI_ShiftKeyframesDown,
1447                 &TCellSelection::shiftKeyframesDown);
1448   enableCommand(this, MI_ShiftKeyframesUp, &TCellSelection::shiftKeyframesUp);
1449 
1450   enableCommand(this, MI_Copy, &TCellSelection::copyCells);
1451   enableCommand(this, MI_Paste, &TCellSelection::pasteCells);
1452 
1453   if (dynamic_cast<const TKeyframeData *>(
1454           QApplication::clipboard()->mimeData()))
1455     enableCommand(this, MI_PasteInto, &TCellSelection::pasteKeyframesInto);
1456 
1457   enableCommand(this, MI_Cut, &TCellSelection::cutCells);
1458   enableCommand(this, MI_Clear, &TCellSelection::deleteCells);
1459   enableCommand(this, MI_Insert, &TCellSelection::insertCells);
1460 
1461   enableCommand(this, MI_PasteInto, &TCellSelection::overWritePasteCells);
1462 
1463   enableCommand(this, MI_FillEmptyCell, &TCellSelection::fillEmptyCell);
1464   enableCommand(this, MI_Reframe1, &TCellSelection::reframe1Cells);
1465   enableCommand(this, MI_Reframe2, &TCellSelection::reframe2Cells);
1466   enableCommand(this, MI_Reframe3, &TCellSelection::reframe3Cells);
1467   enableCommand(this, MI_Reframe4, &TCellSelection::reframe4Cells);
1468   enableCommand(this, MI_ConvertToToonzRaster,
1469                 &TCellSelection::convertToToonzRaster);
1470   enableCommand(this, MI_ConvertVectorToVector,
1471                 &TCellSelection::convertVectortoVector);
1472   enableCommand(this, MI_ReframeWithEmptyInbetweens,
1473                 &TCellSelection::reframeWithEmptyInbetweens);
1474 
1475   enableCommand(this, MI_PasteNumbers, &TCellSelection::overwritePasteNumbers);
1476   enableCommand(this, MI_CreateBlankDrawing,
1477                 &TCellSelection::createBlankDrawings);
1478   enableCommand(this, MI_Duplicate, &TCellSelection::duplicateFrames);
1479   enableCommand(this, MI_PasteDuplicate, &TCellSelection::pasteDuplicateCells);
1480 }
1481 //-----------------------------------------------------------------------------
1482 // Used in RenameCellField::eventFilter()
1483 
isEnabledCommand(std::string commandId)1484 bool TCellSelection::isEnabledCommand(
1485     std::string commandId) {  // static function
1486   static QList<std::string> commands = {MI_Autorenumber,
1487                                         MI_Reverse,
1488                                         MI_Swing,
1489                                         MI_Random,
1490                                         MI_Increment,
1491                                         MI_ResetStep,
1492                                         MI_IncreaseStep,
1493                                         MI_DecreaseStep,
1494                                         MI_Step2,
1495                                         MI_Step3,
1496                                         MI_Step4,
1497                                         MI_Each2,
1498                                         MI_Each3,
1499                                         MI_Each4,
1500                                         MI_Rollup,
1501                                         MI_Rolldown,
1502                                         MI_TimeStretch,
1503                                         MI_CloneLevel,
1504                                         MI_SetKeyframes,
1505                                         MI_ShiftKeyframesDown,
1506                                         MI_ShiftKeyframesUp,
1507                                         MI_Copy,
1508                                         MI_Paste,
1509                                         MI_PasteInto,
1510                                         MI_Cut,
1511                                         MI_Clear,
1512                                         MI_Insert,
1513                                         MI_Reframe1,
1514                                         MI_Reframe2,
1515                                         MI_Reframe3,
1516                                         MI_Reframe4,
1517                                         MI_ReframeWithEmptyInbetweens,
1518                                         MI_Undo,
1519                                         MI_Redo,
1520                                         MI_PasteNumbers,
1521                                         MI_ConvertToToonzRaster,
1522                                         MI_ConvertVectorToVector,
1523                                         MI_CreateBlankDrawing,
1524                                         MI_FillEmptyCell};
1525   return commands.contains(commandId);
1526 }
1527 
1528 //-----------------------------------------------------------------------------
1529 
isEmpty() const1530 bool TCellSelection::isEmpty() const { return m_range.isEmpty(); }
1531 
1532 //-----------------------------------------------------------------------------
1533 
selectCells(int r0,int c0,int r1,int c1)1534 void TCellSelection::selectCells(int r0, int c0, int r1, int c1) {
1535   if (r0 > r1) std::swap(r0, r1);
1536   if (c0 > c1) std::swap(c0, c1);
1537   m_range.m_r0            = r0;
1538   m_range.m_c0            = c0;
1539   m_range.m_r1            = r1;
1540   m_range.m_c1            = c1;
1541   bool onlyOneRasterLevel = containsOnlyOneRasterLevel(r0, c0, r1, c1);
1542   CommandManager::instance()->enable(MI_CanvasSize, onlyOneRasterLevel);
1543 }
1544 
1545 //-----------------------------------------------------------------------------
1546 
selectCell(int row,int col)1547 void TCellSelection::selectCell(int row, int col) {
1548   m_range.m_r0            = row;
1549   m_range.m_c0            = col;
1550   m_range.m_r1            = row;
1551   m_range.m_c1            = col;
1552   bool onlyOneRasterLevel = containsOnlyOneRasterLevel(row, col, row, col);
1553   CommandManager::instance()->enable(MI_CanvasSize, onlyOneRasterLevel);
1554 }
1555 
1556 //-----------------------------------------------------------------------------
1557 
selectNone()1558 void TCellSelection::selectNone() {
1559   m_range = Range();
1560   CommandManager::instance()->enable(MI_CanvasSize, false);
1561 }
1562 
1563 //-----------------------------------------------------------------------------
1564 
getSelectedCells(int & r0,int & c0,int & r1,int & c1) const1565 void TCellSelection::getSelectedCells(int &r0, int &c0, int &r1,
1566                                       int &c1) const {
1567   r0 = m_range.m_r0;
1568   r1 = m_range.m_r1;
1569   c0 = m_range.m_c0;
1570   c1 = m_range.m_c1;
1571 }
1572 
1573 //-----------------------------------------------------------------------------
1574 
getSelectedCells() const1575 TCellSelection::Range TCellSelection::getSelectedCells() const {
1576   return m_range;
1577 }
1578 
1579 //-----------------------------------------------------------------------------
1580 
isCellSelected(int r,int c) const1581 bool TCellSelection::isCellSelected(int r, int c) const {
1582   return m_range.contains(r, c);
1583 }
1584 
1585 //-----------------------------------------------------------------------------
1586 
isRowSelected(int r) const1587 bool TCellSelection::isRowSelected(int r) const {
1588   return m_range.m_r0 <= r && r <= m_range.m_r1;
1589 }
1590 
1591 //-----------------------------------------------------------------------------
1592 
isColSelected(int c) const1593 bool TCellSelection::isColSelected(int c) const {
1594   return m_range.m_c0 <= c && c >= m_range.m_c1;
1595 }
1596 //-----------------------------------------------------------------------------
1597 
areAllColSelectedLocked() const1598 bool TCellSelection::areAllColSelectedLocked() const {
1599   int c;
1600   for (c = m_range.m_c0; c <= m_range.m_c1; c++) {
1601     TXsheet *xsh       = TApp::instance()->getCurrentXsheet()->getXsheet();
1602     TXshColumn *column = xsh->getColumn(c);
1603     if (column && !xsh->getColumn(c)->isLocked()) return false;
1604   }
1605   return true;
1606 }
1607 
1608 //-----------------------------------------------------------------------------
1609 
copyCells()1610 void TCellSelection::copyCells() {
1611   int r0, c0, r1, c1;
1612   getSelectedCells(r0, c0, r1, c1);
1613   if (isEmpty()) return;
1614   copyCellsWithoutUndo(r0, c0, r1, c1);
1615 }
1616 
1617 //-----------------------------------------------------------------------------
1618 
pasteStrokesInCell(int row,int col,const StrokesData * strokesData)1619 static void pasteStrokesInCell(int row, int col,
1620                                const StrokesData *strokesData) {
1621   PasteStrokesInCellUndo *undo =
1622       new PasteStrokesInCellUndo(row, col, strokesData);
1623   std::vector<int> indices;
1624   bool isPaste = pasteStrokesInCellWithoutUndo(row, col, strokesData, indices);
1625   if (!isPaste) {
1626     delete undo;
1627     return;
1628   }
1629   undo->setIndices(indices);
1630   TUndoManager::manager()->add(undo);
1631 }
1632 
1633 //-----------------------------------------------------------------------------
1634 
pasteRasterImageInCell(int row,int col,const RasterImageData * rasterImageData)1635 static void pasteRasterImageInCell(int row, int col,
1636                                    const RasterImageData *rasterImageData) {
1637   // to let the undo to know which frame is edited
1638   TTool::m_cellsData.clear();
1639 
1640   TXsheet *xsh         = TApp::instance()->getCurrentXsheet()->getXsheet();
1641   TXshCell cell        = xsh->getCell(row, col);
1642   bool createdFrame    = false;
1643   bool isLevelCreated  = false;
1644   TPaletteP oldPalette = 0;
1645   if (!cell.getSimpleLevel()) {
1646     createdFrame        = true;
1647     TXshSimpleLevel *sl = xsh->getCell(row - 1, col).getSimpleLevel();
1648     if (sl) oldPalette = sl->getPalette();
1649   } else {
1650     TXshSimpleLevel *sl = cell.getSimpleLevel();
1651     if (sl->getType() == OVL_XSHLEVEL &&
1652         (sl->getPath().getType() == "psd" || sl->getPath().getType() == "gif" ||
1653          sl->getPath().getType() == "mp4" || sl->getPath().getType() == "webm"))
1654       return;
1655     oldPalette = sl->getPalette();
1656   }
1657   if (oldPalette) oldPalette = oldPalette->clone();
1658   TTileSet *tiles = 0;
1659   bool isPaste    = pasteRasterImageInCellWithoutUndo(row, col, rasterImageData,
1660                                                    &tiles, isLevelCreated);
1661   if (isLevelCreated && oldPalette.getPointer()) oldPalette = 0;
1662   if (!isPaste) return;
1663   cell = xsh->getCell(row, col);
1664 
1665   TTileSetCM32 *cm32Tiles           = dynamic_cast<TTileSetCM32 *>(tiles);
1666   TTileSetFullColor *fullColorTiles = dynamic_cast<TTileSetFullColor *>(tiles);
1667   if (cm32Tiles) {
1668     TUndoManager::manager()->add(new PasteToonzImageInCellsUndo(
1669         row, col, rasterImageData, cm32Tiles, cell.getSimpleLevel(),
1670         cell.getFrameId(), oldPalette, createdFrame, isLevelCreated));
1671   } else if (fullColorTiles) {
1672     TUndoManager::manager()->add(new PasteFullColorImageInCellsUndo(
1673         rasterImageData, fullColorTiles, cell.getSimpleLevel(),
1674         cell.getFrameId(), oldPalette, createdFrame, isLevelCreated));
1675   }
1676 }
1677 
1678 //-----------------------------------------------------------------------------
1679 
pasteCells()1680 void TCellSelection::pasteCells() {
1681   int r0, c0, r1, c1;
1682   getSelectedCells(r0, c0, r1, c1);
1683   QClipboard *clipboard     = QApplication::clipboard();
1684   const QMimeData *mimeData = clipboard->mimeData();
1685   TXsheet *xsh              = TApp::instance()->getCurrentXsheet()->getXsheet();
1686   XsheetViewer *viewer      = TApp::instance()->getCurrentXsheetViewer();
1687 
1688   bool initUndo = false;
1689   const TCellKeyframeData *cellKeyframeData =
1690       dynamic_cast<const TCellKeyframeData *>(mimeData);
1691 
1692   const TCellData *cellData = dynamic_cast<const TCellData *>(mimeData);
1693   if (!cellData && cellKeyframeData) cellData = cellKeyframeData->getCellData();
1694   if (cellData) {
1695     if (isEmpty())  // Se la selezione delle celle e' vuota ritorno.
1696       return;
1697 
1698     if (cellData->getCellCount() == 0) {
1699       DVGui::error(QObject::tr("No data to paste."));
1700       return;
1701     }
1702 
1703     if (viewer && !viewer->orientation()->isVerticalTimeline()) {
1704       int cAdj = cellData->getColCount() - 1;
1705       c0 -= cAdj;
1706       c1 -= cAdj;
1707     }
1708 
1709     int oldR0 = r0;
1710     int oldC0 = c0;
1711     int oldR1 = r1;
1712     int oldC1 = c1;
1713     if (!cellData->canChange(xsh, c0)) return;
1714 
1715     // Check Circular References
1716     int i;
1717     for (i = 0; i < cellData->getCellCount(); i++) {
1718       if (!xsh->checkCircularReferences(cellData->getCell(i))) continue;
1719       DVGui::error(
1720           QObject::tr("It is not possible to paste the cells: there is a "
1721                       "circular reference."));
1722       return;
1723     }
1724 
1725     bool isPaste = pasteCellsWithoutUndo(cellData, r0, c0, r1, c1);
1726 
1727     // Se la selezione corrente e' TCellSelection selezione le celle copiate
1728     TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
1729         TApp::instance()->getCurrentSelection()->getSelection());
1730     if (cellSelection) cellSelection->selectCells(r0, c0, r1, c1);
1731 
1732     // Controllo se le colonne erano vuote prima del paste
1733     // n.b. devo farlo dopo il paste perche' prima non ho il range delle colonne
1734     // incollate corrette.
1735     std::vector<bool> areColumnsEmpty;
1736     int c;
1737     for (c = c0; c <= c1; c++) {
1738       TXshColumn *column = xsh->getColumn(c);
1739       if (!column) {
1740         areColumnsEmpty.push_back(false);
1741         continue;
1742       }
1743       int newCr0, newCr1;
1744       column->getRange(newCr0, newCr1);
1745       areColumnsEmpty.push_back(!column || column->isEmpty() ||
1746                                 (newCr0 == r0 && newCr1 == r1));
1747     }
1748     if (!isPaste) return;
1749 
1750     initUndo = true;
1751     TUndoManager::manager()->beginBlock();
1752 
1753     // make sure that the pasting levels are registered in the scene cast
1754     // it may rename the level if there is another level with the same name
1755     std::set<TXshLevel *> pastedLevels;
1756     getLevelSetFromData(cellData, pastedLevels);
1757     LevelCmd::addMissingLevelsToCast(pastedLevels);
1758 
1759     TUndoManager::manager()->add(new PasteCellsUndo(
1760         r0, c0, r1, c1, oldR0, oldC0, oldR1, oldC1, areColumnsEmpty));
1761     TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1762   }
1763 
1764   const TKeyframeData *keyframeData =
1765       dynamic_cast<const TKeyframeData *>(mimeData);
1766   if (!keyframeData && cellKeyframeData)
1767     keyframeData = cellKeyframeData->getKeyframeData();
1768   if (keyframeData) {
1769     if (keyframeData->m_keyData.empty()) {
1770       DVGui::error(QObject::tr(
1771           "It is not possible to paste data: there is nothing to paste."));
1772       return;
1773     }
1774     TKeyframeSelection selection;
1775     if (isEmpty() && TApp::instance()->getCurrentObject()->getObjectId() ==
1776                          TStageObjectId::CameraId(xsh->getCameraColumnIndex()))
1777     // Se la selezione e' vuota e l'objectId e' quello della camera sono nella
1778     // colonna di camera quindi devo selezionare la row corrente e -1.
1779     {
1780       int row = TApp::instance()->getCurrentFrame()->getFrame();
1781       selection.select(row, -1);
1782     } else {
1783       // Retrieves all keyframe positions from mime data and translates them by
1784       // (r0,c0)
1785       std::set<TKeyframeSelection::Position> positions;
1786       int newC0 = c0;
1787       if (viewer && !viewer->orientation()->isVerticalTimeline() && !cellData)
1788         newC0 = c0 - keyframeData->getColumnSpanCount() + 1;
1789       TKeyframeSelection::Position offset(keyframeData->getKeyframesOffset());
1790       positions.insert(TKeyframeSelection::Position(r0 + offset.first,
1791                                                     newC0 + offset.second));
1792       keyframeData->getKeyframes(positions);
1793       selection.select(positions);
1794 
1795       if (!cellKeyframeData) {
1796         // Retrieve the keyframes bbox
1797         r1 = positions.rbegin()->first;
1798         c1 = c0;
1799 
1800         std::set<TKeyframeSelection::Position>::const_iterator it,
1801             end = positions.end();
1802         for (it = positions.begin(); it != end; ++it)
1803           c1 = std::max(c1, it->second);
1804       }
1805     }
1806     if (!initUndo) {
1807       initUndo = true;
1808       TUndoManager::manager()->beginBlock();
1809     }
1810     selection.pasteKeyframesWithShift(r0, r1, c0, c1);
1811   }
1812 
1813   if (const DrawingData *drawingData =
1814           dynamic_cast<const DrawingData *>(mimeData)) {
1815     if (isEmpty())  // Se la selezione delle celle e' vuota ritorno.
1816       return;
1817 
1818     std::set<TFrameId> frameIds;
1819     drawingData->getFrames(frameIds);
1820     TXshSimpleLevel *level = drawingData->getLevel();
1821     if (level && !frameIds.empty())
1822       pasteDrawingsInCellWithoutUndo(xsh, level, frameIds, r0, c0);
1823     if (!initUndo) {
1824       initUndo = true;
1825       TUndoManager::manager()->beginBlock();
1826     }
1827     TUndoManager::manager()->add(
1828         new PasteDrawingsInCellUndo(level, frameIds, r0, c0));
1829   }
1830   if (const StrokesData *strokesData =
1831           dynamic_cast<const StrokesData *>(mimeData)) {
1832     if (isEmpty())  // Se la selezione delle celle e' vuota ritorno.
1833       return;
1834 
1835     TImageP img = xsh->getCell(r0, c0).getImage(false);
1836     if (!img && r0 > 0) {
1837       TXshCell cell = xsh->getCell(r0 - 1, c0);
1838       TXshLevel *xl = cell.m_level.getPointer();
1839       if (xl && (xl->getType() != OVL_XSHLEVEL ||
1840                  xl->getPath().getFrame() != TFrameId::NO_FRAME))
1841         img = cell.getImage(false);
1842     }
1843     if (!initUndo) {
1844       initUndo = true;
1845       TUndoManager::manager()->beginBlock();
1846     }
1847     RasterImageData *rasterImageData = 0;
1848     if (TToonzImageP ti = img) {
1849       rasterImageData = strokesData->toToonzImageData(ti);
1850       pasteRasterImageInCell(r0, c0, rasterImageData);
1851     } else if (TRasterImageP ri = img) {
1852       double dpix, dpiy;
1853       ri->getDpi(dpix, dpiy);
1854       if (dpix == 0 || dpiy == 0) {
1855         TPointD dpi = xsh->getScene()->getCurrentCamera()->getDpi();
1856         dpix        = dpi.x;
1857         dpiy        = dpi.y;
1858         ri->setDpi(dpix, dpiy);
1859       }
1860       rasterImageData = strokesData->toFullColorImageData(ri);
1861       pasteRasterImageInCell(r0, c0, rasterImageData);
1862     } else
1863       pasteStrokesInCell(r0, c0, strokesData);
1864   }
1865   if (const RasterImageData *rasterImageData =
1866           dynamic_cast<const RasterImageData *>(mimeData)) {
1867     if (isEmpty())  // Se la selezione delle celle e' vuota ritorno.
1868       return;
1869 
1870     TImageP img = xsh->getCell(r0, c0).getImage(false);
1871     if (!img && r0 > 0) {
1872       TXshCell cell = xsh->getCell(r0 - 1, c0);
1873       TXshLevel *xl = cell.m_level.getPointer();
1874       if (xl && (xl->getType() != OVL_XSHLEVEL ||
1875                  xl->getPath().getFrame() != TFrameId::NO_FRAME))
1876         img = cell.getImage(false);
1877     }
1878     const FullColorImageData *fullColData =
1879         dynamic_cast<const FullColorImageData *>(rasterImageData);
1880     TToonzImageP ti(img);
1881     TVectorImageP vi(img);
1882     if (!initUndo) {
1883       initUndo = true;
1884       TUndoManager::manager()->beginBlock();
1885     }
1886     if (fullColData && (vi || ti)) {
1887       DVGui::error(QObject::tr(
1888           "The copied selection cannot be pasted in the current drawing."));
1889       return;
1890     }
1891     if (vi) {
1892       TXshSimpleLevel *sl = xsh->getCell(r0, c0).getSimpleLevel();
1893       if (!sl) sl = xsh->getCell(r0 - 1, c0).getSimpleLevel();
1894       assert(sl);
1895       StrokesData *strokesData = rasterImageData->toStrokesData(sl->getScene());
1896       pasteStrokesInCell(r0, c0, strokesData);
1897     } else
1898       pasteRasterImageInCell(r0, c0, rasterImageData);
1899   }
1900   if (!initUndo) {
1901     DVGui::error(QObject::tr(
1902         "It is not possible to paste data: there is nothing to paste."));
1903     return;
1904   }
1905   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1906   TUndoManager::manager()->endBlock();
1907 }
1908 
1909 //-----------------------------------------------------------------------------
1910 
pasteDuplicateCells()1911 void TCellSelection::pasteDuplicateCells() {
1912   int r0, c0, r1, c1;
1913   getSelectedCells(r0, c0, r1, c1);
1914 
1915   QClipboard *clipboard     = QApplication::clipboard();
1916   const QMimeData *mimeData = clipboard->mimeData();
1917 
1918   TXsheet *xsh         = TApp::instance()->getCurrentXsheet()->getXsheet();
1919   XsheetViewer *viewer = TApp::instance()->getCurrentXsheetViewer();
1920 
1921   bool initUndo = false;
1922   const TCellKeyframeData *cellKeyframeData =
1923       dynamic_cast<const TCellKeyframeData *>(mimeData);
1924 
1925   const TCellData *cellData = dynamic_cast<const TCellData *>(mimeData);
1926   if (!cellData && cellKeyframeData) cellData = cellKeyframeData->getCellData();
1927   if (!cellData) {
1928     DVGui::error(QObject::tr("There are no copied cells to duplicate."));
1929     return;
1930   }
1931   if (cellData) {
1932     // go through all the reasons this might fail
1933     if (isEmpty()) return;
1934 
1935     if (cellData->getCellCount() == 0) {
1936       DVGui::error(QObject::tr("No data to paste."));
1937       return;
1938     }
1939 
1940     // xsheet columns increase in number as they go to the right
1941     // timeline "columns" (visually rows) increase in number as they go up
1942     // To paste in the frames the user would expect, we need to adjust the
1943     // "column" placement if using the timeline.
1944 
1945     if (viewer && !viewer->orientation()->isVerticalTimeline()) {
1946       int cAdj = cellData->getColCount() - 1;
1947       c0 -= cAdj;
1948       c1 -= cAdj;
1949     }
1950 
1951     // make sure we aren't trying to paste anything in locked columns
1952     for (int i = 0; i < cellData->getColCount(); i++) {
1953       TXshColumn *column = xsh->getColumn(c0 + i);
1954       if (column && column->isLocked()) {
1955         DVGui::warning(QObject::tr("Cannot paste cells on locked layers."));
1956         return;
1957       }
1958     }
1959 
1960     // make sure the camera column isn't included in the selection
1961     if (c0 < 0) {
1962       DVGui::warning(QObject::tr("Can't place drawings on the camera column."));
1963       return;
1964     }
1965 
1966     int oldR0 = r0;
1967     int oldC0 = c0;
1968     int oldR1 = r1;
1969     int oldC1 = c1;
1970     if (!cellData->canChange(xsh, c0)) return;
1971 
1972     // find what cells we need
1973     int cellCount = cellData->getCellCount();
1974 
1975     // use a pair to ensure that only unique cells are duplicated
1976     std::set<TXshCell> cells;
1977     std::vector<TXshCell> cellsVector;
1978     for (int i = 0; i < cellCount; i++) {
1979       TXshCell cell = cellData->getCell(i);
1980       cells.insert(cell);
1981       cellsVector.push_back(cell);
1982     }
1983 
1984     // now put the set into a vector since it's easier to deal with
1985     std::vector<TXshCell> uniqueCells;
1986     std::set<TXshCell>::iterator it = cells.begin();
1987     while (it != cells.end()) {
1988       uniqueCells.push_back(*it);
1989       it++;
1990     }
1991 
1992     TXshSimpleLevel *sl;
1993     TXshLevelP level;
1994 
1995     // check that the selection only includes types that can be duplicated and
1996     // edited
1997     it = cells.begin();
1998     while (it != cells.end()) {
1999       if (it->isEmpty()) {
2000         it++;
2001         continue;
2002       }
2003       level = it->m_level;
2004       if (level) {
2005         int levelType = level->getType();
2006         if (levelType == ZERARYFX_XSHLEVEL || levelType == PLT_XSHLEVEL ||
2007             levelType == SND_XSHLEVEL || levelType == SND_TXT_XSHLEVEL ||
2008             levelType == MESH_XSHLEVEL) {
2009           DVGui::warning(
2010               QObject::tr("Cannot duplicate a drawing in the current column"));
2011           return;
2012         } else if (level->getSimpleLevel() &&
2013                    level->getSimpleLevel()->isReadOnly()) {
2014           DVGui::warning(
2015               QObject::tr("Cannot duplicate frames in read only levels"));
2016           return;
2017         } else if (level->getSimpleLevel() &&
2018                    it->getFrameId() == TFrameId::NO_FRAME) {
2019           DVGui::warning(QObject::tr(
2020               "Can only duplicate frames in image sequence levels."));
2021           return;
2022         }
2023       }
2024       it++;
2025     }
2026 
2027     // now we do the work of duplicating cells
2028 
2029     // start a undo block first
2030     initUndo = true;
2031     TUndoManager::manager()->beginBlock();
2032 
2033     // Turn on sync level strip changes with xsheet if it is off
2034     bool syncXsheetOn = true;
2035     if (!Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) {
2036       syncXsheetOn = false;
2037       Preferences::instance()->setValue(syncLevelRenumberWithXsheet, true);
2038     }
2039 
2040     // now actually duplicate
2041     std::vector<std::pair<TXshCell, TXshCell>> cellPairs;
2042 
2043     for (int i = 0; i < uniqueCells.size(); i++) {
2044       level = uniqueCells[i].m_level;
2045       if (level) {
2046         sl = uniqueCells[i].getSimpleLevel();
2047         if (sl) {
2048           std::set<TFrameId> frames;
2049           TFrameId fid = uniqueCells[i].getFrameId();
2050           frames.insert(fid);
2051           FilmstripCmd::duplicate(sl, frames, true);
2052 
2053           // see if we need to increment the frameId of
2054           // other cells in the same level
2055           std::set<int> cellsVectorAlreadyIncremented;
2056           std::set<int> cellsPairsAlreadyIncremented;
2057           for (int j = 0; j < uniqueCells.size(); j++) {
2058             if (j == i) continue;
2059             TXshSimpleLevel *tempSl;
2060             TXshLevelP tempLevel;
2061             tempLevel = uniqueCells[j].m_level;
2062             if (tempLevel) {
2063               tempSl = uniqueCells[j].getSimpleLevel();
2064               if (tempSl) {
2065                 if (tempSl == sl) {
2066                   TFrameId tempId    = uniqueCells[j].getFrameId();
2067                   TFrameId currentId = uniqueCells[i].getFrameId();
2068 
2069                   if (tempId.getNumber() > currentId.getNumber()) {
2070                     tempId = TFrameId(tempId.getNumber() + 1, 0);
2071 
2072                     // keep cellsVector in sync with uniqueCells
2073                     for (int k = 0; k < cellsVector.size(); k++) {
2074                       if (cellsVector[k] == uniqueCells[j] &&
2075                           cellsVectorAlreadyIncremented.find(k) ==
2076                               cellsVectorAlreadyIncremented.end()) {
2077                         cellsVector[k].m_frameId = tempId;
2078                         cellsVectorAlreadyIncremented.insert(k);
2079                       }
2080                     }
2081                     // and with cellPairs
2082                     for (int k = 0; k < cellPairs.size(); k++) {
2083                       if (cellPairs[k].first == uniqueCells[j] &&
2084                           cellsPairsAlreadyIncremented.find(k) ==
2085                               cellsPairsAlreadyIncremented.end()) {
2086                         cellPairs[k].first.m_frameId = tempId;
2087                         cellPairs[k].second.m_frameId =
2088                             cellPairs[k].second.m_frameId + 1;
2089                         cellsPairsAlreadyIncremented.insert(k);
2090                       }
2091                     }
2092                     uniqueCells[j].m_frameId = tempId;
2093                   }
2094                 }
2095               }
2096             }
2097           }
2098           TXshCell newCell;
2099           newCell.m_level   = sl;
2100           newCell.m_frameId = fid + 1;
2101           cellPairs.push_back(std::make_pair(uniqueCells[i], newCell));
2102         }
2103       }
2104     }
2105 
2106     // turn off sync with xsheet if it wasn't on originally
2107     if (syncXsheetOn == false) {
2108       Preferences::instance()->setValue(syncLevelRenumberWithXsheet, false);
2109     }
2110 
2111     // we should now have a vector with all original cells
2112     // and a pair vector with the original cell and its duplicate
2113     // let's make a new cellData populated with the new cells when needed.
2114     std::vector<TXshCell> newCells;
2115     for (int i = 0; i < cellsVector.size(); i++) {
2116       std::vector<std::pair<TXshCell, TXshCell>>::iterator pairIt =
2117           cellPairs.begin();
2118       bool found = false;
2119       while (pairIt != cellPairs.end()) {
2120         if (cellsVector[i] == pairIt->first) {
2121           found = true;
2122           newCells.push_back(pairIt->second);
2123           break;
2124         }
2125         pairIt++;
2126       }
2127       if (!found) {
2128         newCells.push_back(cellsVector[i]);
2129       }
2130     }
2131 
2132     TCellData *newCellData = new TCellData(cellData);
2133     newCellData->replaceCells(newCells);
2134 
2135     assert(newCellData->getCellCount() == cellData->getCellCount());
2136 
2137     bool isPaste = pasteCellsWithoutUndo(newCellData, r0, c0, r1, c1);
2138 
2139     // Se la selezione corrente e' TCellSelection selezione le celle copiate
2140     TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
2141         TApp::instance()->getCurrentSelection()->getSelection());
2142     if (cellSelection) cellSelection->selectCells(r0, c0, r1, c1);
2143 
2144     std::vector<bool> areColumnsEmpty;
2145     int c;
2146     for (c = c0; c <= c1; c++) {
2147       TXshColumn *column = xsh->getColumn(c);
2148       if (!column) {
2149         areColumnsEmpty.push_back(false);
2150         continue;
2151       }
2152       int newCr0, newCr1;
2153       column->getRange(newCr0, newCr1);
2154       areColumnsEmpty.push_back(!column || column->isEmpty() ||
2155                                 (newCr0 == r0 && newCr1 == r1));
2156     }
2157 
2158     if (!isPaste) {
2159       TUndoManager::manager()->endBlock();
2160       if (cellPairs.size() > 0) {
2161         TUndoManager::manager()->undo();
2162         TUndoManager::manager()->popUndo(1);
2163       }
2164       return;
2165     }
2166 
2167     // make sure that the pasting levels are registered in the scene cast
2168     // it may rename the level if there is another level with the same name
2169     std::set<TXshLevel *> pastedLevels;
2170     getLevelSetFromData(newCellData, pastedLevels);
2171     LevelCmd::addMissingLevelsToCast(pastedLevels);
2172 
2173     TUndoManager::manager()->add(new PasteCellsUndo(
2174         r0, c0, r1, c1, oldR0, oldC0, oldR1, oldC1, areColumnsEmpty));
2175     TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2176   }
2177 
2178   const TKeyframeData *keyframeData =
2179       dynamic_cast<const TKeyframeData *>(mimeData);
2180   if (!keyframeData && cellKeyframeData)
2181     keyframeData = cellKeyframeData->getKeyframeData();
2182   if (keyframeData) {
2183     if (keyframeData->m_keyData.empty()) {
2184       DVGui::error(QObject::tr(
2185           "It is not possible to paste data: there is nothing to paste."));
2186       return;
2187     }
2188     TKeyframeSelection selection;
2189     if (isEmpty() && TApp::instance()->getCurrentObject()->getObjectId() ==
2190                          TStageObjectId::CameraId(xsh->getCameraColumnIndex()))
2191     // Se la selezione e' vuota e l'objectId e' quello della camera sono nella
2192     // colonna di camera quindi devo selezionare la row corrente e -1.
2193     {
2194       int row = TApp::instance()->getCurrentFrame()->getFrame();
2195       selection.select(row, -1);
2196     } else {
2197       // Retrieves all keyframe positions from mime data and translates them by
2198       // (r0,c0)
2199       std::set<TKeyframeSelection::Position> positions;
2200       int newC0 = c0;
2201       if (viewer && !viewer->orientation()->isVerticalTimeline() && !cellData)
2202         newC0 = c0 - keyframeData->getColumnSpanCount() + 1;
2203       TKeyframeSelection::Position offset(keyframeData->getKeyframesOffset());
2204       positions.insert(TKeyframeSelection::Position(r0 + offset.first,
2205                                                     newC0 + offset.second));
2206       keyframeData->getKeyframes(positions);
2207       selection.select(positions);
2208 
2209       if (!cellKeyframeData) {
2210         // Retrieve the keyframes bbox
2211         r1 = positions.rbegin()->first;
2212         c1 = c0;
2213 
2214         std::set<TKeyframeSelection::Position>::const_iterator it,
2215             end = positions.end();
2216         for (it = positions.begin(); it != end; ++it)
2217           c1 = std::max(c1, it->second);
2218       }
2219     }
2220     if (!initUndo) {
2221       initUndo = true;
2222       TUndoManager::manager()->beginBlock();
2223     }
2224     selection.pasteKeyframesWithShift(r0, r1, c0, c1);
2225   }
2226 
2227   if (!initUndo) {
2228     DVGui::error(QObject::tr("There are no copied cells to duplicate."));
2229     return;
2230   }
2231   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
2232   TUndoManager::manager()->endBlock();
2233   clipboard->clear();
2234 }
2235 
2236 //-----------------------------------------------------------------------------
2237 
deleteCells()2238 void TCellSelection::deleteCells() {
2239   if (isEmpty()) return;
2240   int r0, c0, r1, c1;
2241   getSelectedCells(r0, c0, r1, c1);
2242   if (c0 < 0) c0 = 0;  // Ignore camera column
2243   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
2244   // if all the selected cells are already empty, then do nothing
2245   if (xsh->isRectEmpty(CellPosition(r0, c0), CellPosition(r1, c1))) return;
2246 
2247   std::set<int> removedColIds;
2248   // check if the operation may remove expression reference as column becomes
2249   // empty and deleted after the operation.
2250   if (!checkColumnRemoval(r0, c0, r1, c1, removedColIds)) return;
2251 
2252   TCellData *data = new TCellData();
2253   data->setCells(xsh, r0, c0, r1, c1);
2254 
2255   // clear empty column
2256   if (!removedColIds.empty()) {
2257     TUndoManager::manager()->beginBlock();
2258     // remove, then insert empty column
2259     for (auto colId : removedColIds) {
2260       ColumnCmd::deleteColumn(colId, true);
2261       ColumnCmd::insertEmptyColumn(colId);
2262     }
2263   }
2264 
2265   DeleteCellsUndo *undo =
2266       new DeleteCellsUndo(new TCellSelection(m_range), data);
2267 
2268   deleteCellsWithoutUndo(r0, c0, r1, c1);
2269 
2270   TUndoManager::manager()->add(undo);
2271 
2272   if (!removedColIds.empty()) {
2273     TUndoManager::manager()->endBlock();
2274   }
2275 
2276   // emit selectionChanged() signal so that the rename field will update
2277   // accordingly
2278   if (Preferences::instance()->isUseArrowKeyToShiftCellSelectionEnabled())
2279     TApp::instance()->getCurrentSelection()->notifySelectionChanged();
2280   else
2281     selectNone();
2282 
2283   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2284   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
2285 }
2286 
2287 //-----------------------------------------------------------------------------
2288 
cutCells()2289 void TCellSelection::cutCells() { cutCells(false); }
2290 
2291 //-----------------------------------------------------------------------------
2292 
cutCells(bool withoutCopy)2293 void TCellSelection::cutCells(bool withoutCopy) {
2294   if (isEmpty()) return;
2295 
2296   CutCellsUndo *undo = new CutCellsUndo(new TCellSelection(m_range));
2297 
2298   int r0, c0, r1, c1;
2299   getSelectedCells(r0, c0, r1, c1);
2300   if (c0 < 0) c0 = 0;  // Ignore camera column
2301 
2302   std::set<int> removedColIds;
2303   // check if the operation may remove expression reference as column becomes
2304   // empty and deleted after the operation.
2305   if (!checkColumnRemoval(r0, c0, r1, c1, removedColIds)) return;
2306 
2307   undo->setCurrentData(r0, c0, r1, c1);
2308   if (!withoutCopy) copyCellsWithoutUndo(r0, c0, r1, c1);
2309 
2310   // clear empty column
2311   if (!removedColIds.empty()) {
2312     TUndoManager::manager()->beginBlock();
2313     // remove, then insert empty column
2314     for (auto colId : removedColIds) {
2315       ColumnCmd::deleteColumn(colId, true);
2316       ColumnCmd::insertEmptyColumn(colId);
2317     }
2318   }
2319 
2320   cutCellsWithoutUndo(r0, c0, r1, c1);
2321 
2322   TUndoManager::manager()->add(undo);
2323 
2324   if (!removedColIds.empty()) {
2325     TUndoManager::manager()->endBlock();
2326   }
2327 
2328   // cutCellsWithoutUndo will clear the selection, so select cells again
2329   if (Preferences::instance()->isUseArrowKeyToShiftCellSelectionEnabled()) {
2330     selectCells(r0, c0, r1, c1);
2331     TApp::instance()->getCurrentSelection()->notifySelectionChanged();
2332   } else
2333     selectNone();
2334 
2335   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2336 }
2337 
2338 //-----------------------------------------------------------------------------
2339 
insertCells()2340 void TCellSelection::insertCells() {
2341   if (isEmpty()) return;
2342   InsertUndo *undo = new InsertUndo(getSelectedCells());
2343   undo->redo();
2344   TUndoManager::manager()->add(undo);
2345   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2346 }
2347 
2348 //-----------------------------------------------------------------------------
2349 
pasteKeyframesInto()2350 void TCellSelection::pasteKeyframesInto() {
2351   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
2352   const TKeyframeData *keyframeData = dynamic_cast<const TKeyframeData *>(
2353       QApplication::clipboard()->mimeData());
2354   if (keyframeData) {
2355     int r0, c0, r1, c1;
2356     getSelectedCells(r0, c0, r1, c1);
2357 
2358     TKeyframeSelection selection;
2359     if (isEmpty() && TApp::instance()->getCurrentObject()->getObjectId() ==
2360                          TStageObjectId::CameraId(xsh->getCameraColumnIndex()))
2361     // Se la selezione e' vuota e l'objectId e' quello della camera sono nella
2362     // colonna di camera quindi devo selezionare la row corrente e -1.
2363     {
2364       int row = TApp::instance()->getCurrentFrame()->getFrame();
2365       selection.select(row, -1);
2366     } else {
2367       // Retrieves all keyframe positions from mime data and translates them by
2368       // (r0,c0)
2369       XsheetViewer *viewer = TApp::instance()->getCurrentXsheetViewer();
2370       std::set<TKeyframeSelection::Position> positions;
2371       int newC0 = c0;
2372       if (viewer && !viewer->orientation()->isVerticalTimeline())
2373         newC0 = c0 - keyframeData->getColumnSpanCount() + 1;
2374       positions.insert(TKeyframeSelection::Position(r0, newC0));
2375       keyframeData->getKeyframes(positions);
2376       selection.select(positions);
2377     }
2378 
2379     selection.pasteKeyframes();
2380   }
2381 }
2382 
2383 //-----------------------------------------------------------------------------
2384 
createBlankDrawing(int row,int col,bool multiple)2385 void TCellSelection::createBlankDrawing(int row, int col, bool multiple) {
2386   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
2387 
2388   if (col < 0) {
2389     if (!multiple)
2390       DVGui::warning(
2391           QObject::tr("Unable to create a blank drawing on the camera column"));
2392     return;
2393   }
2394 
2395   TXshColumn *column = xsh->getColumn(col);
2396   if (column && column->isLocked()) {
2397     if (!multiple) DVGui::warning(QObject::tr("The current column is locked"));
2398     return;
2399   }
2400 
2401   TApp::instance()->getCurrentColumn()->setColumnIndex(col);
2402   TApp::instance()->getCurrentFrame()->setCurrentFrame(row + 1);
2403 
2404   TXshLevel *level = TApp::instance()->getCurrentLevel()->getLevel();
2405   if (!level && Preferences::instance()->isAutoCreateEnabled() &&
2406       Preferences::instance()->isAnimationSheetEnabled()) {
2407     int r0, r1;
2408     xsh->getCellRange(col, r0, r1);
2409     for (int r = std::min(r1, row); r > r0; r--) {
2410       TXshCell cell = xsh->getCell(r, col);
2411       if (cell.isEmpty()) continue;
2412       level = cell.m_level.getPointer();
2413       if (!level) continue;
2414       break;
2415     }
2416   }
2417   if (level) {
2418     int levelType = level->getType();
2419     if (levelType == ZERARYFX_XSHLEVEL || levelType == PLT_XSHLEVEL ||
2420         levelType == SND_XSHLEVEL || levelType == SND_TXT_XSHLEVEL ||
2421         levelType == MESH_XSHLEVEL) {
2422       if (!multiple)
2423         DVGui::warning(
2424             QObject::tr("Cannot create a blank drawing on the current column"));
2425       return;
2426     } else if (level->getSimpleLevel() &&
2427                level->getSimpleLevel()->isReadOnly()) {
2428       if (!multiple)
2429         DVGui::warning(QObject::tr("The current level is not editable"));
2430       return;
2431     }
2432   }
2433 
2434   ToolHandle *toolHandle = TApp::instance()->getCurrentTool();
2435 
2436   // If autocreate disabled, let's turn it on temporarily
2437   bool isAutoCreateEnabled = Preferences::instance()->isAutoCreateEnabled();
2438   if (!isAutoCreateEnabled)
2439     Preferences::instance()->setValue(EnableAutocreation, true, false);
2440   // Enable inserting in the hold cells temporarily too.
2441   bool isCreationInHoldCellsEnabled =
2442       Preferences::instance()->isCreationInHoldCellsEnabled();
2443   if (!isCreationInHoldCellsEnabled)
2444     Preferences::instance()->setValue(EnableCreationInHoldCells, true, false);
2445 
2446   TImage *img = toolHandle->getTool()->touchImage();
2447 
2448   TXshCell cell       = xsh->getCell(row, col);
2449   TXshSimpleLevel *sl = cell.getSimpleLevel();
2450 
2451   if (!img || !sl) {
2452     if (!isAutoCreateEnabled)
2453       Preferences::instance()->setValue(EnableAutocreation, false, false);
2454     if (!isCreationInHoldCellsEnabled)
2455       Preferences::instance()->setValue(EnableCreationInHoldCells, false,
2456                                         false);
2457     if (!multiple)
2458       DVGui::warning(QObject::tr(
2459           "Unable to create a blank drawing on the current column"));
2460     return;
2461   }
2462 
2463   if (!toolHandle->getTool()->m_isFrameCreated) {
2464     if (!isAutoCreateEnabled)
2465       Preferences::instance()->setValue(EnableAutocreation, false, false);
2466     if (!isCreationInHoldCellsEnabled)
2467       Preferences::instance()->setValue(EnableCreationInHoldCells, false,
2468                                         false);
2469     if (!multiple)
2470       DVGui::warning(QObject::tr(
2471           "Unable to replace the current drawing with a blank drawing"));
2472     return;
2473   }
2474 
2475   sl->setDirtyFlag(true);
2476 
2477   TPalette *palette = sl->getPalette();
2478   TFrameId frame    = cell.getFrameId();
2479 
2480   CreateBlankDrawingUndo *undo = new CreateBlankDrawingUndo(
2481       sl, frame, toolHandle->getTool()->m_isLevelCreated, palette);
2482   TUndoManager::manager()->add(undo);
2483 
2484   IconGenerator::instance()->invalidate(sl, frame);
2485 
2486   // Reset back to what these were
2487   if (!isAutoCreateEnabled)
2488     Preferences::instance()->setValue(EnableAutocreation, false, false);
2489   if (!isCreationInHoldCellsEnabled)
2490     Preferences::instance()->setValue(EnableCreationInHoldCells, false, false);
2491 }
2492 
2493 //-----------------------------------------------------------------------------
2494 
createBlankDrawings()2495 void TCellSelection::createBlankDrawings() {
2496   int col = TApp::instance()->getCurrentColumn()->getColumnIndex();
2497   int row = TApp::instance()->getCurrentFrame()->getFrameIndex();
2498 
2499   int r0, c0, r1, c1;
2500   getSelectedCells(r0, c0, r1, c1);
2501 
2502   bool multiple = (r1 - r0 > 1) || (c1 - c0 > 1);
2503 
2504   TUndoManager::manager()->beginBlock();
2505   for (int c = c0; c <= c1; c++) {
2506     for (int r = r0; r <= r1; r++) {
2507       createBlankDrawing(r, c, multiple);
2508     }
2509   }
2510   TUndoManager::manager()->endBlock();
2511 
2512   TApp::instance()->getCurrentColumn()->setColumnIndex(col);
2513   TApp::instance()->getCurrentFrame()->setCurrentFrame(row + 1);
2514 }
2515 
2516 //-----------------------------------------------------------------------------
2517 
duplicateFrame(int row,int col,bool multiple)2518 void TCellSelection::duplicateFrame(int row, int col, bool multiple) {
2519   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
2520 
2521   if (col < 0) {
2522     if (!multiple)
2523       DVGui::warning(QObject::tr(
2524           "There are no drawings in the camera column to duplicate"));
2525     return;
2526   }
2527 
2528   TXshColumn *column = xsh->getColumn(col);
2529   if (column && column->isLocked()) {
2530     if (!multiple) DVGui::warning(QObject::tr("The current column is locked"));
2531     return;
2532   }
2533 
2534   TApp::instance()->getCurrentColumn()->setColumnIndex(col);
2535   TApp::instance()->getCurrentFrame()->setCurrentFrame(row + 1);
2536 
2537   TXshLevel *level = TApp::instance()->getCurrentLevel()->getLevel();
2538   if (!level && Preferences::instance()->isAutoCreateEnabled() &&
2539       Preferences::instance()->isAnimationSheetEnabled()) {
2540     int r0, r1;
2541     xsh->getCellRange(col, r0, r1);
2542     for (int r = std::min(r1, row); r > r0; r--) {
2543       TXshCell cell = xsh->getCell(r, col);
2544       if (cell.isEmpty()) continue;
2545       level = cell.m_level.getPointer();
2546       if (!level) continue;
2547       break;
2548     }
2549   }
2550   if (level) {
2551     int levelType = level->getType();
2552     if (levelType == ZERARYFX_XSHLEVEL || levelType == PLT_XSHLEVEL ||
2553         levelType == SND_XSHLEVEL || levelType == SND_TXT_XSHLEVEL ||
2554         levelType == MESH_XSHLEVEL) {
2555       if (!multiple)
2556         DVGui::warning(
2557             QObject::tr("Cannot duplicate a drawing in the current column"));
2558       return;
2559     } else if (level->getSimpleLevel() &&
2560                level->getSimpleLevel()->isReadOnly()) {
2561       if (!multiple)
2562         DVGui::warning(QObject::tr("The current level is not editable"));
2563       return;
2564     }
2565   }
2566 
2567   TXshCell targetCell = xsh->getCell(row, col);
2568   TXshCell prevCell   = xsh->getCell(row - 1, col);
2569   ;
2570 
2571   // check if we use the current cell to duplicate or the previous cell
2572   if (!targetCell.isEmpty() && targetCell != prevCell) {
2573     // Current cell has a drawing to duplicate and it's not a hold shift focus
2574     // to next cell as if they selected that one to duplicate into
2575     prevCell = targetCell;
2576     TApp::instance()->getCurrentFrame()->setCurrentFrame(row + 2);
2577     row++;
2578   }
2579 
2580   if (prevCell.isEmpty() || !(prevCell.m_level->getSimpleLevel())) return;
2581 
2582   TXshSimpleLevel *sl = prevCell.getSimpleLevel();
2583   if (!sl || sl->isSubsequence() || sl->isReadOnly()) return;
2584 
2585   ToolHandle *toolHandle = TApp::instance()->getCurrentTool();
2586 
2587   // If autocreate disabled, let's turn it on temporarily
2588   bool isAutoCreateEnabled = Preferences::instance()->isAutoCreateEnabled();
2589   if (!isAutoCreateEnabled)
2590     Preferences::instance()->setValue(EnableAutocreation, true, false);
2591   // Enable inserting in the hold cells temporarily too.
2592   bool isCreationInHoldCellsEnabled =
2593       Preferences::instance()->isCreationInHoldCellsEnabled();
2594   if (!isCreationInHoldCellsEnabled)
2595     Preferences::instance()->setValue(EnableCreationInHoldCells, true, false);
2596 
2597   TImage *img = toolHandle->getTool()->touchImage();
2598   if (!img) {
2599     if (!isAutoCreateEnabled)
2600       Preferences::instance()->setValue(EnableAutocreation, false, false);
2601     if (!isCreationInHoldCellsEnabled)
2602       Preferences::instance()->setValue(EnableCreationInHoldCells, false,
2603                                         false);
2604     if (!multiple)
2605       DVGui::warning(
2606           QObject::tr("Unable to duplicate a drawing on the current column"));
2607     return;
2608   }
2609 
2610   bool frameCreated = toolHandle->getTool()->m_isFrameCreated;
2611   if (!frameCreated) {
2612     if (!isAutoCreateEnabled)
2613       Preferences::instance()->setValue(EnableAutocreation, false, false);
2614     if (!isCreationInHoldCellsEnabled)
2615       Preferences::instance()->setValue(EnableCreationInHoldCells, false,
2616                                         false);
2617     if (!multiple)
2618       DVGui::warning(
2619           QObject::tr("Unable to replace the current or next drawing with a "
2620                       "duplicate drawing"));
2621     return;
2622   }
2623 
2624   targetCell           = xsh->getCell(row, col);
2625   TPalette *palette    = sl->getPalette();
2626   TFrameId srcFrame    = prevCell.getFrameId();
2627   TFrameId targetFrame = targetCell.getFrameId();
2628   std::set<TFrameId> frames;
2629 
2630   FilmstripCmd::duplicateFrameWithoutUndo(sl, srcFrame, targetFrame);
2631 
2632   TApp::instance()->getCurrentLevel()->notifyLevelChange();
2633 
2634   DuplicateDrawingUndo *undo =
2635       new DuplicateDrawingUndo(sl, srcFrame, targetFrame);
2636   TUndoManager::manager()->add(undo);
2637 
2638   if (!isAutoCreateEnabled)
2639     Preferences::instance()->setValue(EnableAutocreation, false, false);
2640   if (!isCreationInHoldCellsEnabled)
2641     Preferences::instance()->setValue(EnableCreationInHoldCells, false, false);
2642 }
2643 
2644 //-----------------------------------------------------------------------------
2645 
duplicateFrames()2646 void TCellSelection::duplicateFrames() {
2647   int col = TApp::instance()->getCurrentColumn()->getColumnIndex();
2648   int row = TApp::instance()->getCurrentFrame()->getFrameIndex();
2649 
2650   int r0, c0, r1, c1;
2651   getSelectedCells(r0, c0, r1, c1);
2652 
2653   bool multiple = (r1 - r0 > 1) || (c1 - c0 > 1);
2654 
2655   TUndoManager::manager()->beginBlock();
2656   for (int c = c0; c <= c1; c++) {
2657     for (int r = r0; r <= r1; r++) {
2658       duplicateFrame(r, c, multiple);
2659     }
2660   }
2661   TUndoManager::manager()->endBlock();
2662 
2663   if (multiple) {
2664     TApp::instance()->getCurrentColumn()->setColumnIndex(col);
2665     TApp::instance()->getCurrentFrame()->setCurrentFrame(row + 1);
2666   }
2667 }
2668 
2669 //-----------------------------------------------------------------------------
2670 
openTimeStretchPopup()2671 void TCellSelection::openTimeStretchPopup() {
2672   if (!m_timeStretchPopup) m_timeStretchPopup = new TimeStretchPopup();
2673   m_timeStretchPopup->show();
2674   m_timeStretchPopup->raise();
2675   m_timeStretchPopup->activateWindow();
2676 }
2677 
2678 //-----------------------------------------------------------------------------
2679 
operator <(const TXshCell & a,const TXshCell & b)2680 static bool operator<(const TXshCell &a, const TXshCell &b) {
2681   if (a.getSimpleLevel() < b.getSimpleLevel())
2682     return true;
2683   else if (a.getSimpleLevel() > b.getSimpleLevel())
2684     return false;
2685   return a.m_frameId < b.m_frameId;
2686 }
2687 
2688 //-----------------------------------------------------------------------------
2689 
dRenumberCells(int col,int r0,int r1)2690 static void dRenumberCells(int col, int r0, int r1) {
2691   typedef std::vector<std::pair<TFrameId, TFrameId>> FramesMap;
2692   typedef std::map<TXshCell, TXshCell> CellsMap;
2693   typedef std::map<TXshSimpleLevel *, FramesMap> LevelsTable;
2694 
2695   LevelsTable levelsTable;
2696   CellsMap cellsMap;
2697 
2698   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
2699   for (int r = r0; r <= r1; ++r) {
2700     TXshCell cell = xsh->getCell(r, col);
2701     if (!cell.isEmpty() && cell.getSimpleLevel() &&
2702         (r <= 0 || xsh->getCell(r - 1, col) != cell)) {
2703       // In case the cell was already mapped, skip
2704       TXshCell &toCell = cellsMap[cell];
2705       if (!toCell.isEmpty()) continue;
2706 
2707       // Build cell mapping
2708       TXshSimpleLevel *sl = cell.getSimpleLevel();
2709 
2710       TFrameId oldFid = cell.getFrameId();
2711       TFrameId newFid = TFrameId(r + 1);
2712 
2713       toCell.m_level   = sl;
2714       toCell.m_frameId = newFid;
2715 
2716       // Build the level frames mapping
2717       if (sl->isFid(oldFid))
2718         levelsTable[sl].push_back(std::make_pair(oldFid, newFid));
2719     }
2720   }
2721 
2722   // Ensure renumber consistency in case some destination fid would overwrite
2723   // some unrenumbered fid in the level
2724   {
2725     CellsMap::iterator it, end = cellsMap.end();
2726     for (it = cellsMap.begin(); it != end; ++it) {
2727       if (cellsMap.find(it->second) == cellsMap.end() &&
2728           it->first.getSimpleLevel()->isFid(it->second.getFrameId())) {
2729         TFrameId &fid = it->second.m_frameId;
2730         fid           = TFrameId(fid.getNumber(),
2731                        fid.getLetter() ? fid.getLetter() + 1 : 'a');
2732       }
2733     }
2734   }
2735 
2736   // Renumber the level strip of each found level
2737   {
2738     LevelsTable::iterator it, end = levelsTable.end();
2739     for (it = levelsTable.begin(); it != end; ++it)
2740       FilmstripCmd::renumber(it->first, it->second, true);
2741   }
2742 }
2743 
2744 //-----------------------------------------------------------------------------
2745 
dRenumberCells()2746 void TCellSelection::dRenumberCells() {
2747   TUndoManager *undoManager = TUndoManager::manager();
2748 
2749   undoManager->beginBlock();
2750   undoManager->add(new RenumberUndo::UndoNotifier);
2751 
2752   int r0, c0, r1, c1;
2753   getSelectedCells(r0, c0, r1, c1);
2754   for (int c = c1; c >= c0; --c) ::dRenumberCells(c, r0, r1);
2755 
2756   undoManager->add(new RenumberUndo::RedoNotifier);
2757   undoManager->endBlock();
2758 
2759   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
2760   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2761 }
2762 
2763 //-----------------------------------------------------------------------------
2764 
2765 class PasteNewCellUndo final : public TUndo {
2766   TXshCell m_oldCell, m_newCell;
2767   TImageP m_img;
2768   int m_row, m_col;
2769   bool m_levelCreated;
2770 
2771 public:
PasteNewCellUndo(int row,int col,bool levelCreated)2772   PasteNewCellUndo(int row, int col, bool levelCreated)
2773       : m_row(row), m_col(col), m_levelCreated(levelCreated) {
2774     m_oldCell = getXsheet()->getCell(m_row, m_col);
2775   }
onAdd()2776   void onAdd() override {
2777     m_newCell   = getXsheet()->getCell(m_row, m_col);
2778     TImageP img = m_newCell.getImage(false);
2779     if (img) m_img = img->cloneImage();
2780   }
getXsheet() const2781   TXsheet *getXsheet() const {
2782     return TApp::instance()->getCurrentXsheet()->getXsheet();
2783   }
getLevelSet() const2784   TLevelSet *getLevelSet() const {
2785     return TApp::instance()->getCurrentScene()->getScene()->getLevelSet();
2786   }
2787 
undo() const2788   void undo() const override {
2789     getXsheet()->setCell(m_row, m_col, m_oldCell);
2790     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
2791     TXshSimpleLevel *sl = m_newCell.getSimpleLevel();
2792     if (sl) {
2793       sl->eraseFrame(m_newCell.getFrameId());
2794       if (m_levelCreated) {
2795         getLevelSet()->removeLevel(sl);
2796         TApp::instance()->getCurrentScene()->notifyCastChange();
2797       } else {
2798         TApp::instance()->getCurrentLevel()->notifyLevelChange();
2799       }
2800     }
2801   }
redo() const2802   void redo() const override {
2803     getXsheet()->setCell(m_row, m_col, m_newCell);
2804     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
2805     TXshSimpleLevel *sl = m_newCell.getSimpleLevel();
2806     if (sl) {
2807       TFrameId fid = m_newCell.getFrameId();
2808       sl->setFrame(fid, m_img);
2809       if (m_levelCreated) {
2810         getLevelSet()->insertLevel(sl);
2811         IconGenerator::instance()->invalidate(sl, fid);
2812         TApp::instance()->getCurrentScene()->notifyCastChange();
2813       } else {
2814         TApp::instance()->getCurrentLevel()->notifyLevelChange();
2815       }
2816     }
2817   }
getSize() const2818   int getSize() const override {
2819     return sizeof(*this) + sizeof(TXshLevel);  // impreciso.
2820   }
2821 };
2822 
2823 //-----------------------------------------------------------------------------
2824 
2825 // TODO: spostare queste funzioni in un posto piu' generale e riutilizzabile
2826 
getLevelType(const TImageP & img)2827 static int getLevelType(const TImageP &img) {
2828   if (img->getType() == TImage::RASTER)
2829     return OVL_XSHLEVEL;
2830   else if (img->getType() == TImage::VECTOR)
2831     return PLI_XSHLEVEL;
2832   else if (img->getType() == TImage::TOONZ_RASTER)
2833     return TZP_XSHLEVEL;
2834   else
2835     return NO_XSHLEVEL;
2836 }
2837 
2838 //-----------------------------------------------------------------------------
2839 /*
2840 void setNewDrawing(TXsheet *xsh, int row, int col, const TImageP &img)
2841 {
2842   // find a level type suitable for the image
2843 
2844   // search for the level
2845   TXshCell old = xsh->getCell(row,col);
2846   if(old.isEmpty())
2847   {
2848     for(int rr=row-1;rr>=0;rr--)
2849     {
2850       old = xsh->getCell(rr,col);
2851       if(!old.isEmpty())
2852         break;
2853     }
2854   }
2855   bool levelCreated = false;
2856   TXshSimpleLevel *sl;
2857   if(old.isEmpty())
2858   {
2859     // no level found. create it
2860     TXshLevel *xl = xsh->getScene()->createNewLevel(levelType);
2861     TApp::instance()->getCurrentScene()->notifyCastChange();
2862     levelCreated = true;
2863     sl = xl->getSimpleLevel();
2864   }
2865   else
2866   {
2867     // found a level. check its type
2868     sl = old.getSimpleLevel();
2869     if(sl->getType() != levelType)
2870       return;
2871   }
2872   // compute the frameid
2873   TFrameId fid(row+1);
2874   if(sl->isFid(fid))
2875   {
2876     fid = TFrameId(fid.getNumber(), 'a');
2877     while(fid.getLetter()<'z' && sl->isFid(fid))
2878       fid = TFrameId(fid.getNumber(), fid.getLetter()+1);
2879   }
2880   // add the new frame
2881   sl->setFrame(fid, img->cloneImage());
2882   TApp::instance()->getCurrentLevel()->notifyLevelChange();
2883   // undo
2884   TUndo *undo = new PasteNewCellUndo(row,col,levelCreated);
2885   // set xsheet cell
2886   xsh->setCell(row,col,TXshCell(sl,fid));
2887   TUndoManager::manager()->add(undo);
2888 }
2889 */
2890 
2891 //-----------------------------------------------------------------------------
2892 
createNewDrawing(TXsheet * xsh,int row,int col,int preferredLevelType)2893 static void createNewDrawing(TXsheet *xsh, int row, int col,
2894                              int preferredLevelType) {
2895   // search for the level
2896   TXshCell old = xsh->getCell(row, col);
2897   if (old.isEmpty()) {
2898     for (int rr = row - 1; rr >= 0; rr--) {
2899       old = xsh->getCell(rr, col);
2900       if (!old.isEmpty()) break;
2901     }
2902   }
2903   bool levelCreated = false;
2904   TXshSimpleLevel *sl;
2905   if (old.isEmpty()) {
2906     // no level found. create it
2907     TXshLevel *xl = xsh->getScene()->createNewLevel(preferredLevelType);
2908     sl            = xl->getSimpleLevel();
2909     TApp::instance()->getCurrentScene()->notifyCastChange();
2910     levelCreated = true;
2911   } else {
2912     sl = old.getSimpleLevel();
2913   }
2914   // compute the frameid
2915   TFrameId fid(row + 1);
2916   if (sl->isFid(fid)) {
2917     fid = TFrameId(fid.getNumber(), 'a');
2918     while (fid.getLetter() < 'z' && sl->isFid(fid))
2919       fid = TFrameId(fid.getNumber(), fid.getLetter() + 1);
2920   }
2921   // add the new frame
2922   sl->setFrame(fid, sl->createEmptyFrame());
2923   TApp::instance()->getCurrentLevel()->notifyLevelChange();
2924   // undo
2925   TUndo *undo = new PasteNewCellUndo(row, col, levelCreated);
2926   // set xsheet cell
2927   xsh->setCell(row, col, TXshCell(sl, fid));
2928   TUndoManager::manager()->add(undo);
2929 }
2930 
2931 //-----------------------------------------------------------------------------
2932 
2933 #define DYNAMIC_CAST(Type, Var, Data)                                          \
2934   const Type *Var = dynamic_cast<const Type *>(Data)
2935 
dPasteCells()2936 void TCellSelection::dPasteCells() {
2937   if (isEmpty()) return;
2938   int r0, c0, r1, c1;
2939   getSelectedCells(r0, c0, r1, c1);
2940   TUndoManager::manager()->beginBlock();
2941   TXsheet *xsh              = TApp::instance()->getCurrentXsheet()->getXsheet();
2942   QClipboard *clipboard     = QApplication::clipboard();
2943   const QMimeData *mimeData = clipboard->mimeData();
2944   if (DYNAMIC_CAST(TCellData, cellData, mimeData)) {
2945     if (!cellData->canChange(xsh, c0)) {
2946       TUndoManager::manager()->endBlock();
2947       return;
2948     }
2949     for (int c = 0; c < cellData->getColCount(); c++) {
2950       for (int r = 0; r < cellData->getRowCount(); r++) {
2951         TXshCell src = cellData->getCell(r, c);
2952         if (src.getSimpleLevel())
2953           createNewDrawing(xsh, r0 + r, c0 + c,
2954                            src.getSimpleLevel()->getType());
2955       }
2956     }
2957   } else if (DYNAMIC_CAST(DrawingData, drawingData, mimeData)) {
2958     TXshSimpleLevel *level = drawingData->getLevel();
2959     if (level) {
2960       std::set<TFrameId> frameIds;
2961       drawingData->getFrames(frameIds);
2962       for (int i = 0; i < frameIds.size(); ++i)
2963         createNewDrawing(xsh, r0 + i, c0, level->getType());
2964     }
2965   } else if (DYNAMIC_CAST(StrokesData, strokesData, mimeData)) {
2966     createNewDrawing(xsh, r0, c0, PLI_XSHLEVEL);
2967   } else if (DYNAMIC_CAST(ToonzImageData, toonzImageData, mimeData)) {
2968     createNewDrawing(xsh, r0, c0, TZP_XSHLEVEL);
2969   } else if (DYNAMIC_CAST(FullColorImageData, fullColorImageData, mimeData)) {
2970     createNewDrawing(xsh, r0, c0, OVL_XSHLEVEL);
2971   } else {
2972   }
2973   pasteCells();
2974   TUndoManager::manager()->endBlock();
2975 }
2976 
2977 //-----------------------------------------------------------------------------
2978 /*-- セルの上書きペースト --*/
overWritePasteCells()2979 void TCellSelection::overWritePasteCells() {
2980   int r0, c0, r1, c1;
2981   getSelectedCells(r0, c0, r1, c1);
2982 
2983   QClipboard *clipboard     = QApplication::clipboard();
2984   const QMimeData *mimeData = clipboard->mimeData();
2985 
2986   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
2987   if (const TCellData *cellData = dynamic_cast<const TCellData *>(mimeData)) {
2988     if (isEmpty())  // Se la selezione delle celle e' vuota ritorno.
2989       return;
2990 
2991     if (cellData->getCellCount() == 0) {
2992       DVGui::error(QObject::tr("No data to paste."));
2993       return;
2994     }
2995 
2996     XsheetViewer *viewer = TApp::instance()->getCurrentXsheetViewer();
2997     if (viewer && !viewer->orientation()->isVerticalTimeline()) {
2998       int cAdj = cellData->getColCount() - 1;
2999       c0 -= cAdj;
3000       c1 -= cAdj;
3001     }
3002 
3003     int oldR0 = r0;
3004     int oldC0 = c0;
3005     int oldR1 = r1;
3006     int oldC1 = c1;
3007 
3008     /*-- ペースト先の各カラムが、コピーした各セルと同種ならばOK --*/
3009     if (!cellData->canChange(xsh, c0)) return;
3010 
3011     // Check Circular References
3012     int i;
3013     for (i = 0; i < cellData->getCellCount(); i++) {
3014       if (!xsh->checkCircularReferences(cellData->getCell(i))) continue;
3015       DVGui::error(
3016           QObject::tr("It is not possible to paste the cells: there is a "
3017                       "circular reference."));
3018       return;
3019     }
3020 
3021     /*---- Undoのためにペースト前のCellDataを保存しておく --*/
3022     r1                    = r0 + cellData->getRowCount() - 1;
3023     c1                    = c0 + cellData->getColCount() - 1;
3024     TCellData *beforeData = new TCellData();
3025     beforeData->setCells(xsh, r0, c0, r1, c1);
3026 
3027     /*-- InsertをFalseにすることで、Ovewriteペーストになる
3028             r1,c1はペースト範囲にあわせリサイズされる
3029     --*/
3030     bool isPaste = pasteCellsWithoutUndo(r0, c0, r1, c1, false);
3031 
3032     if (!isPaste) {
3033       delete beforeData;
3034       return;
3035     }
3036 
3037     /*-- 各カラムについて、ペースト前にカラムが空だったならtrue --*/
3038     std::vector<bool> areColumnsEmpty;
3039     int c;
3040     /*-- ペースト後の各カラムについて --*/
3041     for (c = c0; c <= c1; c++) {
3042       TXshColumn *column = xsh->getColumn(c);
3043       if (!column) {
3044         areColumnsEmpty.push_back(false);
3045         continue;
3046       }
3047       int newCr0, newCr1;
3048       /*-- 新たなカラムに何かペーストされているかどうか --*/
3049       column->getRange(newCr0, newCr1);
3050       areColumnsEmpty.push_back(!column || column->isEmpty() ||
3051                                 (newCr0 == r0 && newCr1 == r1));
3052     }
3053     TUndoManager::manager()->beginBlock();
3054 
3055     // make sure that the pasting levels are registered in the scene cast
3056     // it may rename the level if there is another level with the same name
3057     std::set<TXshLevel *> pastedLevels;
3058     getLevelSetFromData(cellData, pastedLevels);
3059     LevelCmd::addMissingLevelsToCast(pastedLevels);
3060 
3061     /*-- r0,c0,r1,c1はペーストされた範囲 old付きはペースト前の選択範囲 --*/
3062     TUndoManager::manager()->add(
3063         new OverwritePasteCellsUndo(r0, c0, r1, c1, oldR0, oldC0, oldR1, oldC1,
3064                                     areColumnsEmpty, beforeData));
3065     TUndoManager::manager()->endBlock();
3066     TApp::instance()->getCurrentScene()->setDirtyFlag(true);
3067 
3068     delete beforeData;
3069   } else
3070     DVGui::error(QObject::tr("Cannot paste data \n Nothing to paste"));
3071 
3072   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
3073 }
3074 
3075 //-----------------------------------------------------------------------------
3076 // special paste command - overwrite paste cell numbers only
3077 
overwritePasteNumbers()3078 void TCellSelection::overwritePasteNumbers() {
3079   int r0, c0, r1, c1;
3080   getSelectedCells(r0, c0, r1, c1);
3081   if (c0 < 0) c0 = 0;  // Ignore camera column
3082 
3083   QClipboard *clipboard     = QApplication::clipboard();
3084   const QMimeData *mimeData = clipboard->mimeData();
3085 
3086   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
3087   if (const TCellData *cellData = dynamic_cast<const TCellData *>(mimeData)) {
3088     if (isEmpty()) return;
3089 
3090     if (cellData->getCellCount() == 0) {
3091       DVGui::error(QObject::tr("No data to paste."));
3092       return;
3093     }
3094 
3095     XsheetViewer *viewer = TApp::instance()->getCurrentXsheetViewer();
3096     if (viewer && !viewer->orientation()->isVerticalTimeline()) {
3097       int cAdj = cellData->getColCount() - 1;
3098       c0 -= cAdj;
3099       c1 -= cAdj;
3100     }
3101 
3102     int oldR0 = r0;
3103     int oldC0 = c0;
3104     int oldR1 = r1;
3105     int oldC1 = c1;
3106 
3107     // Check if the copied cells are the same type as the target columns
3108     if (!cellData->canChange(xsh, c0)) {
3109       DVGui::error(
3110           QObject::tr("It is not possible to paste the cells: "
3111                       "Some column is locked or column type is not match."));
3112       return;
3113     }
3114 
3115     // Check Circular References
3116     int i;
3117     for (i = 0; i < cellData->getCellCount(); i++) {
3118       if (!xsh->checkCircularReferences(cellData->getCell(i))) continue;
3119       DVGui::error(
3120           QObject::tr("It is not possible to paste the cells: there is a "
3121                       "circular reference."));
3122       return;
3123     }
3124 
3125     // store celldata for undo
3126     r1 = r0 + cellData->getRowCount() - 1;
3127     if (cellData->getColCount() != 1 || c0 == c1)
3128       c1 = c0 + cellData->getColCount() - 1;
3129     TCellData *beforeData = new TCellData();
3130     beforeData->setCells(xsh, r0, c0, r1, c1);
3131 
3132     bool isPaste = pasteNumbersWithoutUndo(r0, c0, r1, c1);
3133 
3134     if (!isPaste) {
3135       delete beforeData;
3136       return;
3137     }
3138 
3139     // r0,c0,r1,c1 : pasted region  oldR0,oldC0,oldR1,oldC1 : selected area
3140     // before pasting
3141     TUndoManager::manager()->add(new OverwritePasteNumbersUndo(
3142         r0, c0, r1, c1, oldR0, oldC0, oldR1, oldC1, beforeData));
3143     TApp::instance()->getCurrentScene()->setDirtyFlag(true);
3144 
3145     delete beforeData;
3146   } else
3147     DVGui::error(QObject::tr("Cannot paste data \n Nothing to paste"));
3148 
3149   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
3150 }
3151 
3152 //-----------------------------------------------------------------------------
3153 // called from RenameCellField::RenameCell
3154 
renameCells(TXshCell & cell)3155 void TCellSelection::renameCells(TXshCell &cell) {
3156   if (isEmpty()) return;
3157   int r0, c0, r1, c1;
3158   getSelectedCells(r0, c0, r1, c1);
3159   // register undo only if the cell is modified
3160   if (checkIfCellsHaveTheSameContent(r0, c0, r1, c1, cell)) return;
3161   TCellData *data = new TCellData();
3162   TXsheet *xsh    = TApp::instance()->getCurrentXsheet()->getXsheet();
3163   data->setCells(xsh, r0, c0, r1, c1);
3164   RenameCellsUndo *undo = new RenameCellsUndo(this, data, cell);
3165   undo->redo();
3166 
3167   TUndoManager::manager()->add(undo);
3168 }
3169 
3170 //-----------------------------------------------------------------------------
3171 // rename cells for each columns with correspondent item in the list
3172 
renameMultiCells(QList<TXshCell> & cells)3173 void TCellSelection::renameMultiCells(QList<TXshCell> &cells) {
3174   if (isEmpty()) return;
3175   int r0, c0, r1, c1;
3176   getSelectedCells(r0, c0, r1, c1);
3177   assert(cells.size() == c1 - c0 + 1);
3178   // register undo only if the cell is modified
3179   bool somethingChanged = false;
3180   for (int c = c0; c <= c1; c++) {
3181     somethingChanged =
3182         !checkIfCellsHaveTheSameContent(r0, c, r1, c, cells[c - c0]);
3183     if (somethingChanged) break;
3184   }
3185   if (!somethingChanged) return;
3186 
3187   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
3188   TUndoManager::manager()->beginBlock();
3189   for (int c = c0; c <= c1; c++) {
3190     TCellData *data = new TCellData();
3191     data->setCells(xsh, r0, c, r1, c);
3192     RenameCellsUndo *undo =
3193         new RenameCellsUndo(r0, c, r1, c, data, cells[c - c0]);
3194     undo->redo();
3195     TUndoManager::manager()->add(undo);
3196   }
3197   TUndoManager::manager()->endBlock();
3198 }
3199 
3200 //=============================================================================
3201 // CreateLevelUndo
3202 //-----------------------------------------------------------------------------
3203 
3204 class CreateLevelUndo final : public TUndo {
3205   int m_rowIndex;
3206   int m_columnIndex;
3207   int m_frameCount;
3208   int m_oldLevelCount;
3209   int m_step;
3210   TXshSimpleLevelP m_sl;
3211   bool m_areColumnsShifted;
3212   bool m_keepLevel;
3213 
3214 public:
CreateLevelUndo(int row,int column,int frameCount,int step,bool areColumnsShifted,bool keepLevel=false)3215   CreateLevelUndo(int row, int column, int frameCount, int step,
3216                   bool areColumnsShifted, bool keepLevel = false)
3217       : m_rowIndex(row)
3218       , m_columnIndex(column)
3219       , m_frameCount(frameCount)
3220       , m_step(step)
3221       , m_sl(0)
3222       , m_keepLevel(keepLevel)
3223       , m_areColumnsShifted(areColumnsShifted) {
3224     TApp *app         = TApp::instance();
3225     ToonzScene *scene = app->getCurrentScene()->getScene();
3226     m_oldLevelCount   = scene->getLevelSet()->getLevelCount();
3227   }
~CreateLevelUndo()3228   ~CreateLevelUndo() { m_sl = 0; }
3229 
onAdd(TXshSimpleLevelP sl)3230   void onAdd(TXshSimpleLevelP sl) { m_sl = sl; }
3231 
undo() const3232   void undo() const override {
3233     TApp *app         = TApp::instance();
3234     ToonzScene *scene = app->getCurrentScene()->getScene();
3235     TXsheet *xsh      = scene->getXsheet();
3236     if (m_areColumnsShifted)
3237       xsh->removeColumn(m_columnIndex);
3238     else if (m_frameCount > 0)
3239       xsh->removeCells(m_rowIndex, m_columnIndex, m_frameCount);
3240     if (!m_keepLevel) {
3241       TLevelSet *levelSet = scene->getLevelSet();
3242       if (levelSet) {
3243         int m = levelSet->getLevelCount();
3244         while (m > 0 && m > m_oldLevelCount) {
3245           --m;
3246           TXshLevel *level = levelSet->getLevel(m);
3247           if (level) levelSet->removeLevel(level);
3248         }
3249       }
3250     }
3251     app->getCurrentScene()->notifySceneChanged();
3252     app->getCurrentScene()->notifyCastChange();
3253     app->getCurrentXsheet()->notifyXsheetChanged();
3254   }
3255 
redo() const3256   void redo() const override {
3257     if (!m_sl.getPointer()) return;
3258     TApp *app         = TApp::instance();
3259     ToonzScene *scene = app->getCurrentScene()->getScene();
3260     scene->getLevelSet()->insertLevel(m_sl.getPointer());
3261     TXsheet *xsh = scene->getXsheet();
3262     if (m_areColumnsShifted) xsh->insertColumn(m_columnIndex);
3263     std::vector<TFrameId> fids;
3264     m_sl->getFids(fids);
3265     int i = m_rowIndex;
3266     int f = 0;
3267     while (i < m_frameCount + m_rowIndex) {
3268       TFrameId fid = (fids.size() != 0) ? fids[f] : i;
3269       TXshCell cell(m_sl.getPointer(), fid);
3270       f++;
3271       xsh->setCell(i, m_columnIndex, cell);
3272       int appo = i++;
3273       while (i < m_step + appo) xsh->setCell(i++, m_columnIndex, cell);
3274     }
3275     app->getCurrentScene()->notifySceneChanged();
3276     app->getCurrentScene()->notifyCastChange();
3277     app->getCurrentXsheet()->notifyXsheetChanged();
3278   }
3279 
getSize() const3280   int getSize() const override { return sizeof *this; }
getHistoryString()3281   QString getHistoryString() override {
3282     return QObject::tr("Create Level %1  at Column %2")
3283         .arg(QString::fromStdWString(m_sl->getName()))
3284         .arg(QString::number(m_columnIndex + 1));
3285   }
3286 };
3287 
3288 //-----------------------------------------------------------------------------
3289 // Convert selected vector cells to ToonzRaster
3290 
areOnlyVectorCellsSelected()3291 bool TCellSelection::areOnlyVectorCellsSelected() {
3292   // set up basics
3293   int r0, c0, r1, c1;
3294   getSelectedCells(r0, c0, r1, c1);
3295   int i;
3296   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
3297   TXsheet *xsh      = scene->getXsheet();
3298 
3299   TXshCell firstCell = xsh->getCell(r0, c0);
3300   if (firstCell.isEmpty()) {
3301     DVGui::error(QObject::tr("This command only works on vector cells."));
3302     return false;
3303   }
3304 
3305   TXshSimpleLevel *sourceSl = firstCell.getSimpleLevel();
3306   if (!sourceSl || sourceSl->getType() != PLI_XSHLEVEL) {
3307     DVGui::error(QObject::tr("This command only works on vector cells."));
3308     return false;
3309   }
3310 
3311   if (c0 != c1) {
3312     DVGui::error(
3313         QObject::tr("Please select only one column for this command."));
3314     return false;
3315   }
3316 
3317   for (i = r0; i <= r1; i++) {
3318     TXshCell testCell = xsh->getCell(i, c0);
3319     if (testCell.isEmpty() || testCell.getSimpleLevel() != sourceSl) {
3320       DVGui::error(
3321           QObject::tr("All selected cells must belong to the same level."));
3322       return false;
3323     }
3324   }
3325   return true;
3326 }
3327 
getNewToonzRasterLevel(TXshSimpleLevel * sourceSl)3328 TXshSimpleLevel *TCellSelection::getNewToonzRasterLevel(
3329     TXshSimpleLevel *sourceSl) {
3330   ToonzScene *scene       = TApp::instance()->getCurrentScene()->getScene();
3331   TFilePath sourcePath    = sourceSl->getPath();
3332   std::wstring sourceName = sourcePath.getWideName();
3333   TFilePath parentDir     = sourceSl->getPath().getParentDir();
3334   TFilePath fp            = scene->getDefaultLevelPath(TZP_XSHLEVEL, sourceName)
3335                      .withParentDir(parentDir);
3336   TFilePath actualFp = scene->decodeFilePath(fp);
3337 
3338   int i                = 1;
3339   std::wstring newName = sourceName;
3340   while (TSystem::doesExistFileOrLevel(actualFp)) {
3341     newName = sourceName + QString::number(i).toStdWString();
3342     fp      = scene->getDefaultLevelPath(TZP_XSHLEVEL, newName)
3343              .withParentDir(parentDir);
3344     actualFp = scene->decodeFilePath(fp);
3345     i++;
3346   }
3347   parentDir = scene->decodeFilePath(parentDir);
3348 
3349   TXshLevel *level =
3350       scene->createNewLevel(TZP_XSHLEVEL, newName, TDimension(), 0, fp);
3351   TXshSimpleLevel *sl = dynamic_cast<TXshSimpleLevel *>(level);
3352   return sl;
3353 }
3354 
3355 //=============================================================================
3356 // VectorToVectorUndo
3357 //-----------------------------------------------------------------------------
3358 
3359 class VectorToVectorUndo final : public TUndo {
3360   TXshSimpleLevelP m_sl;
3361   std::vector<TFrameId> m_frameIds;
3362   // std::vector<TFrameId> m_newFrameIds;
3363   std::vector<TImageP> m_oldImages;
3364   std::vector<TImageP> m_newImages;
3365   HookSet *m_oldLevelHooks;
3366 
3367 public:
VectorToVectorUndo(TXshSimpleLevelP sl,std::vector<TFrameId> frameIds,std::vector<TImageP> oldImages,std::vector<TImageP> newImages,HookSet * oldLevelHooks)3368   VectorToVectorUndo(TXshSimpleLevelP sl, std::vector<TFrameId> frameIds,
3369                      std::vector<TImageP> oldImages,
3370                      std::vector<TImageP> newImages, HookSet *oldLevelHooks)
3371       : m_sl(sl)
3372       , m_frameIds(frameIds)
3373       , m_oldImages(oldImages)
3374       , m_newImages(newImages)
3375       , m_oldLevelHooks(oldLevelHooks) {}
3376 
~VectorToVectorUndo()3377   ~VectorToVectorUndo() {}
3378 
undo() const3379   void undo() const override {
3380     for (int i = 0; i < m_frameIds.size(); i++) {
3381       m_sl->getSimpleLevel()->setFrame(m_frameIds[i], m_oldImages[i]);
3382     }
3383     TApp::instance()->getCurrentLevel()->notifyLevelChange();
3384 
3385     // update all icons
3386     std::vector<TFrameId> sl_fids;
3387     m_sl.getPointer()->getFids(sl_fids);
3388     invalidateIcons(m_sl.getPointer(), sl_fids);
3389 
3390     *m_sl->getHookSet() = *m_oldLevelHooks;
3391   }
3392 
redo() const3393   void redo() const override {
3394     for (int i = 0; i < m_frameIds.size(); i++) {
3395       m_sl->getSimpleLevel()->setFrame(m_frameIds[i], m_newImages[i]);
3396     }
3397     TApp::instance()->getCurrentLevel()->notifyLevelChange();
3398 
3399     // update all icons
3400     std::vector<TFrameId> sl_fids;
3401     m_sl.getPointer()->getFids(sl_fids);
3402     invalidateIcons(m_sl.getPointer(), m_frameIds);
3403 
3404     *m_sl->getHookSet() = *m_oldLevelHooks;
3405   }
3406 
getSize() const3407   int getSize() const override { return sizeof(*this); }
3408 
getHistoryString()3409   QString getHistoryString() override {
3410     QString str = QObject::tr("Simplify Vectors : Level %1")
3411                       .arg(QString::fromStdWString(m_sl->getName()));
3412     return str;
3413   }
getHistoryType()3414   int getHistoryType() override { return HistoryType::Xsheet; }
3415 };
3416 
3417 //-----------------------------------------------------------------------------
3418 // Convert selected vector cells to ToonzRaster
3419 
convertToToonzRaster()3420 void TCellSelection::convertToToonzRaster() {
3421   // set up basics
3422   int r0, c0, r1, c1;
3423   getSelectedCells(r0, c0, r1, c1);
3424 
3425   TApp *app = TApp::instance();
3426   int row   = app->getCurrentFrame()->getFrame();
3427   int col   = app->getCurrentColumn()->getColumnIndex();
3428   int i, j;
3429 
3430   ToonzScene *scene = app->getCurrentScene()->getScene();
3431   TXsheet *xsh      = scene->getXsheet();
3432 
3433   TXshCell firstCell        = xsh->getCell(r0, c0);
3434   TXshSimpleLevel *sourceSl = firstCell.getSimpleLevel();
3435 
3436   if (!areOnlyVectorCellsSelected()) return;
3437 
3438   // set up new level
3439   TXshSimpleLevel *sl = getNewToonzRasterLevel(sourceSl);
3440 
3441   // get camera settings
3442   TCamera *camera = app->getCurrentScene()->getScene()->getCurrentCamera();
3443   double dpi      = camera->getDpi().x;
3444   int xres        = camera->getRes().lx;
3445   int yres        = camera->getRes().ly;
3446 
3447   assert(sl);
3448 
3449   sl->getProperties()->setDpiPolicy(LevelProperties::DP_ImageDpi);
3450   sl->getProperties()->setDpi(dpi);
3451   sl->getProperties()->setImageDpi(TPointD(dpi, dpi));
3452   sl->getProperties()->setImageRes(TDimension(xres, yres));
3453 
3454   TFrameId frameId = firstCell.getFrameId();
3455   std::set<TFrameId> frameIdsSet;
3456   std::vector<TFrameId> frameIds;
3457   frameIds.push_back(frameId);
3458   for (i = r0 + 1; i <= r1; i++) {
3459     TXshCell newCell    = xsh->getCell(i, c0);
3460     TFrameId newFrameId = xsh->getCell(i, c0).getFrameId();
3461     if (newCell.getSimpleLevel() == sourceSl) {
3462       if (std::find(frameIds.begin(), frameIds.end(), newCell.getFrameId()) ==
3463           frameIds.end()) {
3464         frameIds.push_back(newFrameId);
3465       }
3466     }
3467   }
3468 
3469   int totalImages = frameIds.size();
3470   for (i = 0; i < totalImages; i++) {
3471     frameIdsSet.insert(frameIds[i]);
3472   }
3473 
3474   DrawingData *data = new DrawingData();
3475   data->setLevelFrames(sourceSl, frameIdsSet);
3476 
3477   // This is where the copying actually happens
3478   std::set<TFrameId> newFrameIds;
3479   for (i = 0; i < totalImages; i++) {
3480     TRasterCM32P raster(xres, yres);
3481     raster->fill(TPixelCM32());
3482     TToonzImageP firstImage(raster, TRect());
3483     firstImage->setDpi(dpi, dpi);
3484     TFrameId newFrameId = TFrameId(i + 1);
3485     sl->setFrame(newFrameId, firstImage);
3486     newFrameIds.insert(newFrameId);
3487     firstImage->setSavebox(TRect(0, 0, xres - 1, yres - 1));
3488   }
3489 
3490   bool keepOriginalPalette;
3491   bool success = data->getLevelFrames(
3492       sl, newFrameIds, DrawingData::OVER_SELECTION, true, keepOriginalPalette,
3493       true);  // setting is redo = true skips the
3494               // question about the palette
3495   std::vector<TFrameId> newFids;
3496   sl->getFids(newFids);
3497 
3498   // make a new column
3499   col += 1;
3500   TApp::instance()->getCurrentColumn()->setColumnIndex(col);
3501   xsh->insertColumn(col);
3502 
3503   CreateLevelUndo *undo = new CreateLevelUndo(row, col, totalImages, 1, true);
3504   TUndoManager::manager()->add(undo);
3505 
3506   // expose the new frames in the column
3507   for (i = 0; i < totalImages; i++) {
3508     for (int k = r0; k <= r1; k++) {
3509       TXshCell oldCell = xsh->getCell(k, c0);
3510       TXshCell newCell(sl, newFids[i]);
3511       if (oldCell.getFrameId() == frameIds[i]) {
3512         xsh->setCell(k, col, newCell);
3513       }
3514     }
3515   }
3516 
3517   invalidateIcons(sl, newFrameIds);
3518   sl->save(sl->getPath(), TFilePath(), true);
3519 
3520   DvDirModel::instance()->refreshFolder(sl->getPath().getParentDir());
3521 
3522   undo->onAdd(sl);
3523 
3524   app->getCurrentScene()->notifySceneChanged();
3525   app->getCurrentScene()->notifyCastChange();
3526   app->getCurrentXsheet()->notifyXsheetChanged();
3527 
3528   app->getCurrentTool()->onImageChanged(
3529       (TImage::Type)app->getCurrentImageType());
3530 }
3531 
3532 //-----------------------------------------------------------------------------
3533 // Convert selected vector cells to ToonzRaster and back to vecor
3534 
convertVectortoVector()3535 void TCellSelection::convertVectortoVector() {
3536   // set up basics
3537   int r0, c0, r1, c1;
3538   getSelectedCells(r0, c0, r1, c1);
3539 
3540   TApp *app = TApp::instance();
3541   int row   = app->getCurrentFrame()->getFrame();
3542   int col   = app->getCurrentColumn()->getColumnIndex();
3543   int i, j;
3544 
3545   ToonzScene *scene = app->getCurrentScene()->getScene();
3546   TXsheet *xsh      = scene->getXsheet();
3547 
3548   TXshCell firstCell        = xsh->getCell(r0, c0);
3549   TXshSimpleLevel *sourceSl = firstCell.getSimpleLevel();
3550   if (!areOnlyVectorCellsSelected()) return;
3551 
3552   // set up new level name
3553   TXshSimpleLevel *sl = getNewToonzRasterLevel(sourceSl);
3554   assert(sl);
3555 
3556   // get camera settings
3557   TCamera *camera = app->getCurrentScene()->getScene()->getCurrentCamera();
3558   double dpi      = camera->getDpi().x;
3559   int xres        = camera->getRes().lx;
3560   int yres        = camera->getRes().ly;
3561 
3562   if (Preferences::instance()->getUseHigherDpiOnVectorSimplify()) {
3563     dpi *= 2;
3564     xres *= 2;
3565     yres *= 2;
3566   }
3567 
3568   sl->getProperties()->setDpiPolicy(LevelProperties::DP_ImageDpi);
3569   sl->getProperties()->setDpi(dpi);
3570   sl->getProperties()->setImageDpi(TPointD(dpi, dpi));
3571   sl->getProperties()->setImageRes(TDimension(xres, yres));
3572 
3573   // Get the used FrameIds and images
3574   // The cloned images are used in the undo.
3575   TFrameId frameId = firstCell.getFrameId();
3576   std::set<TFrameId> frameIdsSet;
3577   std::vector<TFrameId> frameIds;
3578   std::vector<TImageP> oldImages;
3579   frameIds.push_back(frameId);
3580   oldImages.push_back(firstCell.getImage(false).getPointer()->cloneImage());
3581   for (i = r0 + 1; i <= r1; i++) {
3582     TXshCell newCell    = xsh->getCell(i, c0);
3583     TFrameId newFrameId = xsh->getCell(i, c0).getFrameId();
3584     if (newCell.getSimpleLevel() == sourceSl) {
3585       if (std::find(frameIds.begin(), frameIds.end(), newCell.getFrameId()) ==
3586           frameIds.end()) {
3587         frameIds.push_back(newFrameId);
3588         oldImages.push_back(newCell.getImage(false)->cloneImage());
3589       }
3590     }
3591   }
3592 
3593   int totalImages = frameIds.size();
3594 
3595   for (i = 0; i < totalImages; i++) {
3596     frameIdsSet.insert(frameIds[i]);
3597   }
3598 
3599   DrawingData *data = new DrawingData();
3600   data->setLevelFrames(sourceSl, frameIdsSet);
3601 
3602   // Make empty frames for the new data
3603   std::set<TFrameId> newFrameIds;
3604   for (i = 0; i < totalImages; i++) {
3605     TRasterCM32P raster(xres, yres);
3606     raster->fill(TPixelCM32());
3607     TToonzImageP firstImage(raster, TRect());
3608     firstImage->setDpi(dpi, dpi);
3609     TFrameId newFrameId = TFrameId(i + 1);
3610     sl->setFrame(newFrameId, firstImage);
3611     newFrameIds.insert(newFrameId);
3612     firstImage->setSavebox(TRect(0, 0, xres - 1, yres - 1));
3613   }
3614 
3615   // This is where the copying actually happens
3616   // copy old frames to Toonz Raster
3617   bool keepOriginalPalette;
3618   bool success = data->getLevelFrames(
3619       sl, newFrameIds, DrawingData::OVER_SELECTION, true, keepOriginalPalette,
3620       true);  // setting is redo = true skips the
3621               // question about the palette
3622               // get the new FrameIds
3623   std::vector<TFrameId> newFids;
3624   sl->getFids(newFids);
3625 
3626   // copy the Toonz Raster frames onto the old level
3627   data->clear();
3628   data->setLevelFrames(sl, newFrameIds);
3629   if (Preferences::instance()->getKeepFillOnVectorSimplify())
3630     data->setKeepVectorFills(true);
3631   success = data->getLevelFrames(
3632       sourceSl, frameIdsSet, DrawingData::OVER_SELECTION, true,
3633       keepOriginalPalette, true);  // setting is redo = true skips the
3634                                    // question about the palette
3635 
3636   // get clones of the new images for undo
3637   std::vector<TImageP> newImages;
3638   for (i = 0; i < totalImages; i++) {
3639     newImages.push_back(sourceSl->getFrame(frameIds[i], false)->cloneImage());
3640   }
3641 
3642   HookSet *oldLevelHooks = new HookSet();
3643   *oldLevelHooks         = *sourceSl->getHookSet();
3644 
3645   TUndoManager::manager()->add(new VectorToVectorUndo(
3646       sourceSl, frameIds, oldImages, newImages, oldLevelHooks));
3647 
3648   invalidateIcons(sourceSl, frameIdsSet);
3649 
3650   // remove the toonz raster level
3651   sl->clearFrames();
3652   app->getCurrentScene()->getScene()->getLevelSet()->removeLevel(sl, true);
3653 
3654   app->getCurrentScene()->notifySceneChanged();
3655   app->getCurrentScene()->notifyCastChange();
3656   app->getCurrentXsheet()->notifyXsheetChanged();
3657 
3658   app->getCurrentTool()->onImageChanged(
3659       (TImage::Type)app->getCurrentImageType());
3660 }
3661 
fillEmptyCell()3662 void TCellSelection::fillEmptyCell() {
3663   if (isEmpty()) return;
3664 
3665   // set up basics
3666   bool initUndo = false;
3667   TXsheet *xsh  = TApp::instance()->getCurrentXsheet()->getXsheet();
3668   int r, r0, c0, c, r1, c1;
3669 
3670   getSelectedCells(r0, c0, r1, c1);
3671 
3672   for (c = c0; c <= c1; c++) {
3673     TXshColumn *column = xsh->getColumn(c);
3674     if (!column || column->isEmpty() || column->isLocked() ||
3675         column->getSoundColumn())
3676       continue;
3677 
3678     for (r = r1; r >= r0; r--) {
3679       int fillCount = 0;
3680       TXshCell cell = xsh->getCell(r, c);
3681 
3682       if (cell.isEmpty()) fillCount++;
3683 
3684       int prevR = r - 1;
3685       while (prevR >= 0) {
3686         cell = xsh->getCell(prevR, c);
3687         if (!cell.isEmpty()) break;
3688 
3689         prevR--;
3690         fillCount++;
3691       }
3692 
3693       // We've gone past the beginning of timeline without finding a cell.
3694       // Do nothing, and end current column processing immediately.
3695       if (prevR < 0) break;
3696 
3697       // Adjacent cell is filled.  Move to next one.
3698       if (!fillCount) continue;
3699 
3700       // Ok, time fill
3701       int startR = prevR + 1;
3702       int endR   = startR + fillCount - 1;
3703 
3704       if (!initUndo) {
3705         initUndo = true;
3706         TUndoManager::manager()->beginBlock();
3707       }
3708 
3709       FillEmptyCellUndo *undo = new FillEmptyCellUndo(startR, endR, c, cell);
3710       TUndoManager::manager()->add(undo);
3711       undo->redo();
3712 
3713       // Let's skip cells we just filled
3714       r = prevR;
3715     }
3716   }
3717 
3718   if (initUndo) TUndoManager::manager()->endBlock();
3719 
3720   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
3721 }