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 }