1 #include <memory>
2
3 #include "cellselection.h"
4 #include "cellkeyframeselection.h"
5 #include "keyframeselection.h"
6 #include "keyframedata.h"
7
8 // Tnz6 includes
9 #include "tapp.h"
10 #include "duplicatepopup.h"
11 #include "overwritepopup.h"
12 #include "selectionutils.h"
13 #include "columnselection.h"
14 #include "reframepopup.h"
15
16 // TnzQt includes
17 #include "toonzqt/tselectionhandle.h"
18 #include "toonzqt/gutil.h"
19 #include "historytypes.h"
20
21 // TnzLib includes
22 #include "toonz/txshcell.h"
23 #include "toonz/txshsimplelevel.h"
24 #include "toonz/levelset.h"
25 #include "toonz/tstageobject.h"
26 #include "toonz/toonzscene.h"
27 #include "toonz/txsheethandle.h"
28 #include "toonz/tscenehandle.h"
29 #include "toonz/tobjecthandle.h"
30 #include "toonz/stageobjectutil.h"
31 #include "toonz/hook.h"
32 #include "toonz/levelproperties.h"
33 #include "toonz/childstack.h"
34 #include "toonz/tframehandle.h"
35 #include "toonz/tcolumnhandle.h"
36
37 // TnzCore includes
38 #include "tsystem.h"
39 #include "tundo.h"
40 #include "tmsgcore.h"
41 #include "trandom.h"
42 #include "tpalette.h"
43
44 // Qt includes
45 #include <QLabel>
46 #include <QPushButton>
47 #include <QMainWindow>
48
49 // tcg includes
50 #include "tcg/tcg_macros.h"
51
52 // STD includes
53 #include <ctime>
54
55 //*********************************************************************************
56 // Reverse Cells command
57 //*********************************************************************************
58
59 namespace {
60
61 class ReverseUndo final : public TUndo {
62 int m_r0, m_c0, m_r1, m_c1;
63
64 public:
ReverseUndo(int r0,int c0,int r1,int c1)65 ReverseUndo(int r0, int c0, int r1, int c1)
66 : m_r0(r0), m_c0(c0), m_r1(r1), m_c1(c1) {}
67
68 void redo() const override;
undo() const69 void undo() const override { redo(); } // Reverse is idempotent :)
70
getSize() const71 int getSize() const override { return sizeof(*this); }
72
getHistoryString()73 QString getHistoryString() override { return QObject::tr("Reverse"); }
getHistoryType()74 int getHistoryType() override { return HistoryType::Xsheet; }
75 };
76
77 //-----------------------------------------------------------------------------
78
redo() const79 void ReverseUndo::redo() const {
80 TCG_ASSERT(m_r1 >= m_r0 && m_c1 >= m_c0, return );
81
82 TApp::instance()->getCurrentXsheet()->getXsheet()->reverseCells(m_r0, m_c0,
83 m_r1, m_c1);
84
85 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
86 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
87 }
88
89 } // namespace
90
91 //=============================================================================
92
reverseCells()93 void TCellSelection::reverseCells() {
94 if (isEmpty() || areAllColSelectedLocked()) return;
95
96 TUndo *undo =
97 new ReverseUndo(m_range.m_r0, m_range.m_c0, m_range.m_r1, m_range.m_c1);
98 TUndoManager::manager()->add(undo);
99
100 undo->redo();
101 }
102
103 //*********************************************************************************
104 // Swing Cells command
105 //*********************************************************************************
106
107 namespace {
108
109 class SwingUndo final : public TUndo {
110 int m_r0, m_c0, m_r1, m_c1;
111
112 public:
SwingUndo(int r0,int c0,int r1,int c1)113 SwingUndo(int r0, int c0, int r1, int c1)
114 : m_r0(r0), m_c0(c0), m_r1(r1), m_c1(c1) {}
115
116 void redo() const override;
117 void undo() const override;
118
getSize() const119 int getSize() const override { return sizeof(*this); }
120
getHistoryString()121 QString getHistoryString() override { return QObject::tr("Swing"); }
getHistoryType()122 int getHistoryType() override { return HistoryType::Xsheet; }
123 };
124
125 //-----------------------------------------------------------------------------
126
redo() const127 void SwingUndo::redo() const {
128 TApp::instance()->getCurrentXsheet()->getXsheet()->swingCells(m_r0, m_c0,
129 m_r1, m_c1);
130
131 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
132 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
133 }
134
135 //-----------------------------------------------------------------------------
136
undo() const137 void SwingUndo::undo() const {
138 TCG_ASSERT(m_r1 >= m_r0 && m_c1 >= m_c0, return );
139
140 for (int c = m_c0; c <= m_c1; ++c)
141 TApp::instance()->getCurrentXsheet()->getXsheet()->removeCells(m_r1 + 1, c,
142 m_r1 - m_r0);
143
144 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
145 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
146 }
147
148 } // namespace
149
150 //=============================================================================
151
swingCells()152 void TCellSelection::swingCells() {
153 if (isEmpty() || areAllColSelectedLocked()) return;
154
155 TUndo *undo =
156 new SwingUndo(m_range.m_r0, m_range.m_c0, m_range.m_r1, m_range.m_c1);
157 TUndoManager::manager()->add(undo);
158
159 undo->redo();
160 }
161
162 //*********************************************************************************
163 // Increment Cells command
164 //*********************************************************************************
165
166 namespace {
167
168 class IncrementUndo final : public TUndo {
169 int m_r0, m_c0, m_r1, m_c1;
170 mutable std::vector<std::pair<TRect, TXshCell>> m_undoCells;
171
172 public:
173 mutable bool m_ok;
174
175 public:
IncrementUndo(int r0,int c0,int r1,int c1)176 IncrementUndo(int r0, int c0, int r1, int c1)
177 : m_r0(r0), m_c0(c0), m_r1(r1), m_c1(c1), m_ok(true) {}
178
179 void redo() const override;
180 void undo() const override;
181
getSize() const182 int getSize() const override { return sizeof(*this); }
183
getHistoryString()184 QString getHistoryString() override { return QObject::tr("Autoexpose"); }
getHistoryType()185 int getHistoryType() override { return HistoryType::Xsheet; }
186 };
187
188 //-----------------------------------------------------------------------------
189
redo() const190 void IncrementUndo::redo() const {
191 TCG_ASSERT(m_r1 >= m_r0 && m_c1 >= m_c0, return );
192
193 m_undoCells.clear();
194 m_ok = TApp::instance()->getCurrentXsheet()->getXsheet()->incrementCells(
195 m_r0, m_c0, m_r1, m_c1, m_undoCells);
196
197 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
198 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
199 }
200
201 //-----------------------------------------------------------------------------
202
undo() const203 void IncrementUndo::undo() const {
204 TCG_ASSERT(m_r1 >= m_r0 && m_c1 >= m_c0 && m_ok, return );
205
206 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
207
208 for (int i = m_undoCells.size() - 1; i >= 0; --i) {
209 const TRect &r = m_undoCells[i].first;
210 int size = r.x1 - r.x0 + 1;
211
212 if (m_undoCells[i].second.isEmpty())
213 xsh->removeCells(r.x0, r.y0, size);
214 else {
215 xsh->insertCells(r.x0, r.y0, size);
216 for (int j = 0; j < size; ++j)
217 xsh->setCell(r.x0 + j, r.y0, m_undoCells[i].second);
218 }
219 }
220
221 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
222 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
223 }
224
225 } // namespace
226
227 //=============================================================================
228
incrementCells()229 void TCellSelection::incrementCells() {
230 if (isEmpty() || areAllColSelectedLocked()) return;
231
232 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
233
234 std::unique_ptr<IncrementUndo> undo(new IncrementUndo(
235 m_range.m_r0, m_range.m_c0, m_range.m_r1, m_range.m_c1));
236
237 if (undo->redo(), !undo->m_ok) {
238 DVGui::error(
239 QObject::tr("Invalid selection: each selected column must contain one "
240 "single level with increasing frame numbering."));
241 return;
242 }
243
244 TUndoManager::manager()->add(undo.release());
245 }
246
247 //*********************************************************************************
248 // Random Cells command
249 //*********************************************************************************
250
251 namespace {
252
253 class RandomUndo final : public TUndo {
254 int m_r0, m_c0, m_r1, m_c1;
255
256 std::vector<int> m_shuffle; //!< Shuffled indices
257 std::vector<int> m_elffuhs; //!< Inverse shuffle indices
258
259 public:
260 RandomUndo(int r0, int c0, int r1, int c1);
261
262 void shuffleCells(int row, int col, const std::vector<int> &data) const;
263
264 void redo() const override;
265 void undo() const override;
266
getSize() const267 int getSize() const override {
268 return sizeof(*this) + 2 * sizeof(int) * m_shuffle.size();
269 }
270
getHistoryString()271 QString getHistoryString() override { return QObject::tr("Random"); }
getHistoryType()272 int getHistoryType() override { return HistoryType::Xsheet; }
273 };
274
275 //-----------------------------------------------------------------------------
276
RandomUndo(int r0,int c0,int r1,int c1)277 RandomUndo::RandomUndo(int r0, int c0, int r1, int c1)
278 : m_r0(r0), m_c0(c0), m_r1(r1), m_c1(c1) {
279 TCG_ASSERT(m_r1 >= m_r0 && m_c1 >= m_c0, return );
280
281 int r, rowCount = r1 - r0 + 1;
282 std::vector<std::pair<unsigned int, int>> rndTable(rowCount);
283
284 TRandom rnd(std::time(0)); // Standard seeding
285 for (r = 0; r < rowCount; ++r) rndTable[r] = std::make_pair(rnd.getUInt(), r);
286
287 std::sort(rndTable.begin(), rndTable.end()); // Random sort shuffle
288
289 m_shuffle.resize(rowCount);
290 m_elffuhs.resize(rowCount);
291 for (r = 0; r < rowCount; ++r) {
292 m_shuffle[r] = rndTable[r].second;
293 m_elffuhs[rndTable[r].second] = r;
294 }
295 }
296
297 //-----------------------------------------------------------------------------
298
shuffleCells(int row,int col,const std::vector<int> & data) const299 void RandomUndo::shuffleCells(int row, int col,
300 const std::vector<int> &data) const {
301 int rowCount = data.size();
302 assert(rowCount > 0);
303
304 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
305
306 std::vector<TXshCell> bCells(rowCount), aCells(rowCount);
307 xsh->getCells(row, col, rowCount, &bCells[0]);
308
309 for (int i = 0; i < rowCount; ++i) aCells[data[i]] = bCells[i];
310
311 xsh->setCells(row, col, rowCount, &aCells[0]);
312 }
313
314 //-----------------------------------------------------------------------------
315
undo() const316 void RandomUndo::undo() const {
317 for (int c = m_c0; c <= m_c1; ++c) shuffleCells(m_r0, c, m_elffuhs);
318
319 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
320 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
321 }
322
323 //-----------------------------------------------------------------------------
324
redo() const325 void RandomUndo::redo() const {
326 for (int c = m_c0; c <= m_c1; ++c) shuffleCells(m_r0, c, m_shuffle);
327
328 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
329 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
330 }
331
332 } // namespace
333
334 //=============================================================================
335
randomCells()336 void TCellSelection::randomCells() {
337 if (isEmpty() || areAllColSelectedLocked()) return;
338
339 TUndo *undo =
340 new RandomUndo(m_range.m_r0, m_range.m_c0, m_range.m_r1, m_range.m_c1);
341 TUndoManager::manager()->add(undo);
342
343 undo->redo();
344 }
345
346 //*********************************************************************************
347 // Step Cells command
348 //*********************************************************************************
349
350 namespace {
351
352 class StepUndo final : public TUndo {
353 int m_r0, m_c0, m_r1, m_c1;
354 int m_rowsCount, m_colsCount;
355
356 int m_step;
357 int m_newRows;
358
359 std::unique_ptr<TXshCell[]> m_cells;
360
361 public:
362 StepUndo(int r0, int c0, int r1, int c1, int step);
363
364 void redo() const override;
365 void undo() const override;
366
getSize() const367 int getSize() const override { return sizeof(*this); }
368
getHistoryString()369 QString getHistoryString() override {
370 return QObject::tr("Step %1").arg(QString::number(m_step));
371 }
getHistoryType()372 int getHistoryType() override { return HistoryType::Xsheet; }
373 };
374
375 //-----------------------------------------------------------------------------
376
StepUndo(int r0,int c0,int r1,int c1,int step)377 StepUndo::StepUndo(int r0, int c0, int r1, int c1, int step)
378 : m_r0(r0)
379 , m_c0(c0)
380 , m_r1(r1)
381 , m_c1(c1)
382 , m_rowsCount(r1 - r0 + 1)
383 , m_colsCount(c1 - c0 + 1)
384 , m_step(step)
385 , m_newRows(m_rowsCount * (step - 1))
386 , m_cells(new TXshCell[m_rowsCount * m_colsCount]) {
387 assert(m_rowsCount > 0 && m_colsCount > 0 && step > 0);
388 assert(m_cells);
389
390 int k = 0;
391 for (int r = r0; r <= r1; ++r)
392 for (int c = c0; c <= c1; ++c)
393 m_cells[k++] =
394 TApp::instance()->getCurrentXsheet()->getXsheet()->getCell(r, c);
395 }
396
397 //-----------------------------------------------------------------------------
398
redo() const399 void StepUndo::redo() const {
400 TCG_ASSERT(m_rowsCount > 0 && m_colsCount > 0, return );
401
402 TApp::instance()->getCurrentXsheet()->getXsheet()->stepCells(m_r0, m_c0, m_r1,
403 m_c1, m_step);
404
405 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
406 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
407 }
408
409 //-----------------------------------------------------------------------------
410
undo() const411 void StepUndo::undo() const {
412 TCG_ASSERT(m_rowsCount > 0 && m_colsCount > 0 && m_cells, return );
413
414 TApp *app = TApp::instance();
415 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
416
417 for (int c = m_c0; c <= m_c1; ++c) xsh->removeCells(m_r1 + 1, c, m_newRows);
418
419 int k = 0;
420 for (int r = m_r0; r <= m_r1; ++r)
421 for (int c = m_c0; c <= m_c1; ++c) {
422 if (m_cells[k].isEmpty())
423 xsh->clearCells(r, c);
424 else
425 xsh->setCell(r, c, m_cells[k]);
426 k++;
427 }
428 app->getCurrentXsheet()->notifyXsheetChanged();
429 app->getCurrentScene()->setDirtyFlag(true);
430 }
431
432 } // namespace
433
434 //=============================================================================
435
stepCells(int step)436 void TCellSelection::stepCells(int step) {
437 if (isEmpty() || areAllColSelectedLocked()) return;
438
439 TUndo *undo = new StepUndo(m_range.m_r0, m_range.m_c0, m_range.m_r1,
440 m_range.m_c1, step);
441 TUndoManager::manager()->add(undo);
442
443 undo->redo();
444 m_range.m_r1 += (step - 1) * m_range.getRowCount();
445 }
446
447 //*********************************************************************************
448 // Each Cells command
449 //*********************************************************************************
450
451 namespace {
452
453 class EachUndo final : public TUndo {
454 int m_r0, m_c0, m_r1, m_c1;
455 int m_rowsCount, m_colsCount;
456
457 int m_each;
458 int m_newRows;
459
460 std::unique_ptr<TXshCell[]> m_cells;
461
462 public:
463 EachUndo(int r0, int c0, int r1, int c1, int each);
464
465 void redo() const override;
466 void undo() const override;
467
getSize() const468 int getSize() const override { return sizeof(*this); }
469
getHistoryString()470 QString getHistoryString() override {
471 return QObject::tr("Each %1").arg(QString::number(m_each));
472 }
getHistoryType()473 int getHistoryType() override { return HistoryType::Xsheet; }
474 };
475
476 //-----------------------------------------------------------------------------
477
EachUndo(int r0,int c0,int r1,int c1,int each)478 EachUndo::EachUndo(int r0, int c0, int r1, int c1, int each)
479 : m_r0(r0)
480 , m_c0(c0)
481 , m_r1(r1)
482 , m_c1(c1)
483 , m_rowsCount(r1 - r0 + 1)
484 , m_colsCount(c1 - c0 + 1)
485 , m_each(each)
486 , m_newRows((m_rowsCount % each) ? m_rowsCount / each + 1
487 : m_rowsCount / each)
488 , m_cells(new TXshCell[m_rowsCount * m_colsCount]) {
489 assert(m_rowsCount > 0 && m_colsCount > 0 && each > 0);
490 assert(m_cells);
491
492 int k = 0;
493 for (int r = r0; r <= r1; ++r)
494 for (int c = c0; c <= c1; ++c)
495 m_cells[k++] =
496 TApp::instance()->getCurrentXsheet()->getXsheet()->getCell(r, c);
497 }
498
499 //-----------------------------------------------------------------------------
500
redo() const501 void EachUndo::redo() const {
502 TCG_ASSERT(m_rowsCount > 0 && m_colsCount > 0, return );
503
504 TApp::instance()->getCurrentXsheet()->getXsheet()->eachCells(m_r0, m_c0, m_r1,
505 m_c1, m_each);
506
507 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
508 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
509 }
510
511 //-----------------------------------------------------------------------------
512
undo() const513 void EachUndo::undo() const {
514 TCG_ASSERT(m_rowsCount > 0 && m_colsCount > 0 && m_cells, return );
515
516 TApp *app = TApp::instance();
517 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
518
519 for (int c = m_c0; c <= m_c1; ++c)
520 xsh->insertCells(m_r0 + m_newRows, c, m_rowsCount - m_newRows);
521
522 int k = 0;
523 for (int r = m_r0; r <= m_r1; ++r)
524 for (int c = m_c0; c <= m_c1; ++c) {
525 if (m_cells[k].isEmpty())
526 xsh->clearCells(r, c);
527 else
528 xsh->setCell(r, c, m_cells[k]);
529 k++;
530 }
531
532 app->getCurrentXsheet()->notifyXsheetChanged();
533 app->getCurrentScene()->setDirtyFlag(true);
534 }
535
536 } // namespace
537
538 //=============================================================================
539
eachCells(int each)540 void TCellSelection::eachCells(int each) {
541 if (isEmpty() || areAllColSelectedLocked()) return;
542
543 TUndo *undo = new EachUndo(m_range.m_r0, m_range.m_c0, m_range.m_r1,
544 m_range.m_c1, each);
545 TUndoManager::manager()->add(undo);
546
547 undo->redo();
548 m_range.m_r1 = m_range.m_r0 + (m_range.m_r1 - m_range.m_r0 + each) / each - 1;
549 }
550
551 //*********************************************************************************
552 // Reframe command : 強制的にNコマ打ちにする
553 //*********************************************************************************
554
555 namespace {
556
557 class ReframeUndo final : public TUndo {
558 int m_r0, m_r1;
559 int m_type;
560 int m_nr;
561 int m_withBlank;
562 std::unique_ptr<TXshCell[]> m_cells;
563
564 public:
565 std::vector<int> m_newRows;
566
567 std::vector<int> m_columnIndeces;
568
569 ReframeUndo(int r0, int r1, std::vector<int> columnIndeces, int type,
570 int withBlank = -1);
571 ~ReframeUndo();
572 void undo() const override;
573 void redo() const override;
574 void repeat() const;
575
getSize() const576 int getSize() const override { return sizeof(*this); }
577
getHistoryString()578 QString getHistoryString() override {
579 if (m_withBlank == -1)
580 return QObject::tr("Reframe to %1's").arg(QString::number(m_type));
581 else
582 return QObject::tr("Reframe to %1's with %2 blanks")
583 .arg(QString::number(m_type))
584 .arg(QString::number(m_withBlank));
585 }
getHistoryType()586 int getHistoryType() override { return HistoryType::Xsheet; }
587 };
588
589 //-----------------------------------------------------------------------------
590
ReframeUndo(int r0,int r1,std::vector<int> columnIndeces,int type,int withBlank)591 ReframeUndo::ReframeUndo(int r0, int r1, std::vector<int> columnIndeces,
592 int type, int withBlank)
593 : m_r0(r0)
594 , m_r1(r1)
595 , m_type(type)
596 , m_nr(0)
597 , m_columnIndeces(columnIndeces)
598 , m_withBlank(withBlank) {
599 m_nr = m_r1 - m_r0 + 1;
600 assert(m_nr > 0);
601 m_cells.reset(new TXshCell[m_nr * (int)m_columnIndeces.size()]);
602 assert(m_cells);
603 int k = 0;
604 for (int r = r0; r <= r1; r++)
605 for (int c = 0; c < (int)m_columnIndeces.size(); c++)
606 m_cells[k++] = TApp::instance()->getCurrentXsheet()->getXsheet()->getCell(
607 r, m_columnIndeces[c]);
608
609 m_newRows.clear();
610 }
611
612 //-----------------------------------------------------------------------------
613
~ReframeUndo()614 ReframeUndo::~ReframeUndo() {}
615
616 //-----------------------------------------------------------------------------
617
undo() const618 void ReframeUndo::undo() const {
619 TApp *app = TApp::instance();
620 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
621 int rowCount = m_r1 - m_r0;
622 if (rowCount < 0 || m_columnIndeces.size() < 1) return;
623
624 for (int c = 0; c < m_columnIndeces.size(); c++) {
625 /*-- コマンド後に縮んだカラムはその分引き伸ばす --*/
626 if (m_newRows[c] < m_nr)
627 xsh->insertCells(m_r0 + m_newRows[c], m_columnIndeces[c],
628 m_nr - m_newRows[c]);
629 /*-- コマンド後に延びたカラムはその分縮める --*/
630 else if (m_newRows[c] > m_nr)
631 xsh->removeCells(m_r1 + 1, m_columnIndeces[c], m_newRows[c] - m_nr);
632 }
633
634 if (m_cells) {
635 int k = 0;
636 for (int r = m_r0; r <= m_r1; r++)
637 for (int c = 0; c < m_columnIndeces.size(); c++) {
638 if (m_cells[k].isEmpty())
639 xsh->clearCells(r, m_columnIndeces[c]);
640 else
641 xsh->setCell(r, m_columnIndeces[c], m_cells[k]);
642 k++;
643 }
644 }
645 app->getCurrentXsheet()->notifyXsheetChanged();
646 }
647
648 //-----------------------------------------------------------------------------
649
redo() const650 void ReframeUndo::redo() const {
651 if (m_r1 - m_r0 < 0 || m_columnIndeces.size() < 1) return;
652
653 TApp *app = TApp::instance();
654
655 for (int c = 0; c < m_columnIndeces.size(); c++)
656 app->getCurrentXsheet()->getXsheet()->reframeCells(
657 m_r0, m_r1, m_columnIndeces[c], m_type, m_withBlank);
658
659 app->getCurrentXsheet()->notifyXsheetChanged();
660 }
661
662 //-----------------------------------------------------------------------------
663
repeat() const664 void ReframeUndo::repeat() const {}
665
666 } // namespace
667
668 //=============================================================================
669
reframeCells(int count)670 void TCellSelection::reframeCells(int count) {
671 if (isEmpty() || areAllColSelectedLocked()) return;
672
673 std::vector<int> colIndeces;
674 for (int c = m_range.m_c0; c <= m_range.m_c1; c++) colIndeces.push_back(c);
675
676 ReframeUndo *undo =
677 new ReframeUndo(m_range.m_r0, m_range.m_r1, colIndeces, count);
678
679 for (int c = m_range.m_c0; c <= m_range.m_c1; c++) {
680 int nrows = TApp::instance()->getCurrentXsheet()->getXsheet()->reframeCells(
681 m_range.m_r0, m_range.m_r1, c, count);
682 undo->m_newRows.push_back(nrows);
683 }
684
685 TUndoManager::manager()->add(undo);
686
687 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
688 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
689 }
690
reframeCells(int count)691 void TColumnSelection::reframeCells(int count) {
692 if (isEmpty()) return;
693
694 int rowCount =
695 TApp::instance()->getCurrentXsheet()->getXsheet()->getFrameCount();
696 std::vector<int> colIndeces;
697 std::set<int>::const_iterator it;
698 for (it = m_indices.begin(); it != m_indices.end(); it++)
699 colIndeces.push_back(*it);
700
701 ReframeUndo *undo = new ReframeUndo(0, rowCount - 1, colIndeces, count);
702
703 for (int c = 0; c < (int)colIndeces.size(); c++) {
704 int nrows = TApp::instance()->getCurrentXsheet()->getXsheet()->reframeCells(
705 0, rowCount - 1, colIndeces[c], count);
706 undo->m_newRows.push_back(nrows);
707 }
708
709 TUndoManager::manager()->add(undo);
710
711 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
712 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
713 }
714
715 //=============================================================================
716
reframeWithEmptyInbetweens()717 void TCellSelection::reframeWithEmptyInbetweens() {
718 if (isEmpty() || areAllColSelectedLocked()) return;
719
720 std::vector<int> colIndeces;
721 for (int c = m_range.m_c0; c <= m_range.m_c1; c++) colIndeces.push_back(c);
722
723 // destruction of m_reframePopup will be done along with the main window
724 if (!m_reframePopup) m_reframePopup = new ReframePopup();
725 int ret = m_reframePopup->exec();
726 if (ret == QDialog::Rejected) return;
727
728 int step, blank;
729 m_reframePopup->getValues(step, blank);
730
731 ReframeUndo *undo =
732 new ReframeUndo(m_range.m_r0, m_range.m_r1, colIndeces, step, blank);
733
734 int maximumRow = 0;
735 for (int c = m_range.m_c0; c <= m_range.m_c1; c++) {
736 int nrows = TApp::instance()->getCurrentXsheet()->getXsheet()->reframeCells(
737 m_range.m_r0, m_range.m_r1, c, step, blank);
738 undo->m_newRows.push_back(nrows);
739 if (maximumRow < nrows) maximumRow = nrows;
740 }
741
742 if (maximumRow == 0) {
743 delete undo;
744 return;
745 }
746
747 TUndoManager::manager()->add(undo);
748
749 // select reframed range
750 selectCells(m_range.m_r0, m_range.m_c0, m_range.m_r0 + maximumRow - 1,
751 m_range.m_c1);
752
753 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
754 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
755 }
756
reframeWithEmptyInbetweens()757 void TColumnSelection::reframeWithEmptyInbetweens() {
758 if (isEmpty()) return;
759
760 int rowCount =
761 TApp::instance()->getCurrentXsheet()->getXsheet()->getFrameCount();
762 std::vector<int> colIndeces;
763 std::set<int>::const_iterator it;
764 for (it = m_indices.begin(); it != m_indices.end(); it++)
765 colIndeces.push_back(*it);
766
767 if (!m_reframePopup) m_reframePopup = new ReframePopup();
768 int ret = m_reframePopup->exec();
769 if (ret == QDialog::Rejected) return;
770
771 int step, blank;
772 m_reframePopup->getValues(step, blank);
773
774 ReframeUndo *undo = new ReframeUndo(0, rowCount - 1, colIndeces, step, blank);
775
776 bool commandExecuted = false;
777 for (int c = 0; c < (int)colIndeces.size(); c++) {
778 int nrows = TApp::instance()->getCurrentXsheet()->getXsheet()->reframeCells(
779 0, rowCount - 1, colIndeces[c], step, blank);
780 undo->m_newRows.push_back(nrows);
781 if (nrows > 0) commandExecuted = true;
782 }
783
784 if (!commandExecuted) {
785 delete undo;
786 return;
787 }
788
789 TUndoManager::manager()->add(undo);
790
791 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
792 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
793 }
794
795 //*********************************************************************************
796 // Reset Step Cells command
797 //*********************************************************************************
798
799 namespace {
800
801 class ResetStepUndo final : public TUndo {
802 int m_r0, m_c0, m_r1, m_c1;
803 int m_rowsCount, m_colsCount;
804
805 std::unique_ptr<TXshCell[]> m_cells;
806 QMap<int, int> m_insertedCells; //!< Count of inserted cells, by column
807
808 public:
809 ResetStepUndo(int r0, int c0, int r1, int c1);
810
811 void redo() const override;
812 void undo() const override;
813
getSize() const814 int getSize() const override { return sizeof(*this); }
815 };
816
817 //-----------------------------------------------------------------------------
818
ResetStepUndo(int r0,int c0,int r1,int c1)819 ResetStepUndo::ResetStepUndo(int r0, int c0, int r1, int c1)
820 : m_r0(r0)
821 , m_c0(c0)
822 , m_r1(r1)
823 , m_c1(c1)
824 , m_rowsCount(m_r1 - m_r0 + 1)
825 , m_colsCount(m_c1 - m_c0 + 1)
826 , m_cells(new TXshCell[m_rowsCount * m_colsCount]) {
827 assert(m_rowsCount > 0 && m_colsCount > 0);
828 assert(m_cells);
829
830 TApp *app = TApp::instance();
831
832 int k = 0;
833 for (int c = c0; c <= c1; ++c) {
834 TXshCell prevCell;
835 m_insertedCells[c] = 0;
836
837 for (int r = r0; r <= r1; ++r) {
838 const TXshCell &cell =
839 app->getCurrentXsheet()->getXsheet()->getCell(r, c);
840 m_cells[k++] = cell;
841
842 if (prevCell != cell) {
843 prevCell = cell;
844 m_insertedCells[c]++;
845 }
846 }
847 }
848 }
849
850 //-----------------------------------------------------------------------------
851
redo() const852 void ResetStepUndo::redo() const {
853 TCG_ASSERT(m_rowsCount > 0 && m_colsCount > 0, return );
854
855 TApp::instance()->getCurrentXsheet()->getXsheet()->resetStepCells(m_r0, m_c0,
856 m_r1, m_c1);
857
858 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
859 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
860 }
861
862 //-----------------------------------------------------------------------------
863
undo() const864 void ResetStepUndo::undo() const {
865 TApp *app = TApp::instance();
866 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
867
868 int k = 0;
869 for (int c = m_c0; c <= m_c1; ++c) {
870 xsh->removeCells(m_r0, c, m_insertedCells[c]);
871
872 xsh->insertCells(m_r0, c, m_rowsCount);
873 for (int r = m_r0; r <= m_r1; ++r) xsh->setCell(r, c, m_cells[k++]);
874 }
875
876 app->getCurrentXsheet()->notifyXsheetChanged();
877 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
878 }
879
880 } // namespace
881
882 //=============================================================================
883
resetStepCells()884 void TCellSelection::resetStepCells() {
885 if (isEmpty() || areAllColSelectedLocked()) return;
886
887 TUndo *undo =
888 new ResetStepUndo(m_range.m_r0, m_range.m_c0, m_range.m_r1, m_range.m_c1);
889 TUndoManager::manager()->add(undo);
890
891 undo->redo();
892 }
893
894 //*********************************************************************************
895 // Increase Step Cells command
896 //*********************************************************************************
897
898 namespace {
899
900 class IncreaseStepUndo final : public TUndo {
901 int m_r0, m_c0, m_r1, m_c1;
902 int m_rowsCount, m_colsCount;
903
904 std::unique_ptr<TXshCell[]> m_cells;
905 QMap<int, int> m_insertedCells;
906
907 public:
908 mutable int m_newR1; //!< r1 updated by TXsheet::increaseStepCells()
909
910 public:
911 IncreaseStepUndo(int r0, int c0, int r1, int c1);
912
913 void redo() const override;
914 void undo() const override;
915
getSize() const916 int getSize() const override { return sizeof(*this); }
917 };
918
919 //-----------------------------------------------------------------------------
920
IncreaseStepUndo(int r0,int c0,int r1,int c1)921 IncreaseStepUndo::IncreaseStepUndo(int r0, int c0, int r1, int c1)
922 : m_r0(r0)
923 , m_c0(c0)
924 , m_r1(r1)
925 , m_c1(c1)
926 , m_rowsCount(m_r1 - m_r0 + 1)
927 , m_colsCount(m_c1 - m_c0 + 1)
928 , m_cells(new TXshCell[m_rowsCount * m_colsCount])
929 , m_newR1(m_r1) {
930 assert(m_cells);
931
932 int k = 0;
933 for (int c = c0; c <= c1; ++c) {
934 TXshCell prevCell;
935 m_insertedCells[c] = 0;
936
937 for (int r = r0; r <= r1; ++r) {
938 const TXshCell &cell =
939 TApp::instance()->getCurrentXsheet()->getXsheet()->getCell(r, c);
940 m_cells[k++] = cell;
941
942 if (prevCell != cell) {
943 prevCell = cell;
944 m_insertedCells[c]++;
945 }
946 }
947 }
948 }
949
950 //-----------------------------------------------------------------------------
951
redo() const952 void IncreaseStepUndo::redo() const {
953 m_newR1 = m_r1;
954 TApp::instance()->getCurrentXsheet()->getXsheet()->increaseStepCells(
955 m_r0, m_c0, m_newR1, m_c1);
956
957 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
958 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
959 }
960
961 //-----------------------------------------------------------------------------
962
undo() const963 void IncreaseStepUndo::undo() const {
964 TApp *app = TApp::instance();
965 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
966
967 int k = 0;
968 for (int c = m_c0; c <= m_c1; ++c) {
969 xsh->removeCells(m_r0, c, m_rowsCount + m_insertedCells[c]);
970
971 xsh->insertCells(m_r0, c, m_rowsCount);
972 for (int r = m_r0; r <= m_r1; ++r) xsh->setCell(r, c, m_cells[k++]);
973 }
974
975 app->getCurrentXsheet()->notifyXsheetChanged();
976 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
977 }
978
979 } // namespace
980
981 //=============================================================================
982
increaseStepCells()983 void TCellSelection::increaseStepCells() {
984 if (isEmpty()) {
985 int row = TTool::getApplication()->getCurrentFrame()->getFrame();
986 int col = TTool::getApplication()->getCurrentColumn()->getColumnIndex();
987 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
988 m_range.m_r0 = row;
989 m_range.m_r1 = row;
990 m_range.m_c0 = col;
991 m_range.m_c1 = col;
992 TXshCell cell;
993 cell = xsh->getCell(row, col);
994 if (cell.isEmpty()) return;
995 }
996 if (areAllColSelectedLocked()) return;
997
998 IncreaseStepUndo *undo = new IncreaseStepUndo(m_range.m_r0, m_range.m_c0,
999 m_range.m_r1, m_range.m_c1);
1000 TUndoManager::manager()->add(undo);
1001
1002 undo->redo();
1003
1004 if (undo->m_newR1 != m_range.m_r1) {
1005 m_range.m_r1 = undo->m_newR1;
1006 TApp::instance()->getCurrentSelection()->notifySelectionChanged();
1007 }
1008 }
1009
1010 //*********************************************************************************
1011 // Decrease Step Cells command
1012 //*********************************************************************************
1013
1014 namespace {
1015
1016 class DecreaseStepUndo final : public TUndo {
1017 int m_r0, m_c0, m_r1, m_c1;
1018 int m_rowsCount, m_colsCount;
1019
1020 std::unique_ptr<TXshCell[]> m_cells;
1021 QMap<int, int> m_removedCells;
1022
1023 public:
1024 mutable int m_newR1; //!< r1 updated by TXsheet::decreaseStepCells()
1025
1026 public:
1027 DecreaseStepUndo(int r0, int c0, int r1, int c1);
1028
1029 void redo() const override;
1030 void undo() const override;
1031
getSize() const1032 int getSize() const override { return sizeof(*this); }
1033 };
1034
1035 //-----------------------------------------------------------------------------
1036
DecreaseStepUndo(int r0,int c0,int r1,int c1)1037 DecreaseStepUndo::DecreaseStepUndo(int r0, int c0, int r1, int c1)
1038 : m_r0(r0)
1039 , m_c0(c0)
1040 , m_r1(r1)
1041 , m_c1(c1)
1042 , m_rowsCount(m_r1 - m_r0 + 1)
1043 , m_colsCount(m_c1 - m_c0 + 1)
1044 , m_cells(new TXshCell[m_rowsCount * m_colsCount])
1045 , m_newR1(m_r1) {
1046 assert(m_cells);
1047
1048 int k = 0;
1049 for (int c = c0; c <= c1; ++c) {
1050 TXshCell prevCell =
1051 TApp::instance()->getCurrentXsheet()->getXsheet()->getCell(r0, c);
1052 m_removedCells[c] = 0;
1053
1054 bool removed = false;
1055 m_cells[k++] = prevCell;
1056
1057 for (int r = r0 + 1; r <= r1; ++r) {
1058 const TXshCell &cell =
1059 TApp::instance()->getCurrentXsheet()->getXsheet()->getCell(r, c);
1060 m_cells[k++] = cell;
1061
1062 if (prevCell == cell) {
1063 if (!removed) {
1064 removed = true;
1065 m_removedCells[c]++;
1066 }
1067 } else {
1068 removed = false;
1069 prevCell = cell;
1070 }
1071 }
1072 }
1073 }
1074
1075 //-----------------------------------------------------------------------------
1076
redo() const1077 void DecreaseStepUndo::redo() const {
1078 m_newR1 = m_r1;
1079 TApp::instance()->getCurrentXsheet()->getXsheet()->decreaseStepCells(
1080 m_r0, m_c0, m_newR1, m_c1);
1081
1082 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1083 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1084 }
1085
1086 //-----------------------------------------------------------------------------
1087
undo() const1088 void DecreaseStepUndo::undo() const {
1089 TApp *app = TApp::instance();
1090 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1091
1092 int k = 0;
1093 for (int c = m_c0; c <= m_c1; ++c) {
1094 xsh->removeCells(m_r0, c, m_rowsCount - m_removedCells[c]);
1095
1096 xsh->insertCells(m_r0, c, m_rowsCount);
1097 for (int r = m_r0; r <= m_r1; ++r) xsh->setCell(r, c, m_cells[k++]);
1098 }
1099
1100 app->getCurrentXsheet()->notifyXsheetChanged();
1101 app->getCurrentScene()->setDirtyFlag(true);
1102 }
1103
1104 } // namespace
1105
1106 //=============================================================================
1107
decreaseStepCells()1108 void TCellSelection::decreaseStepCells() {
1109 if (isEmpty()) {
1110 int row = TTool::getApplication()->getCurrentFrame()->getFrame();
1111 int col = TTool::getApplication()->getCurrentColumn()->getColumnIndex();
1112 int r1 = row;
1113 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1114 TXshCell cell;
1115 TXshCell nextCell;
1116 bool sameCells = true;
1117 cell = xsh->getCell(row, col);
1118 if (cell.isEmpty()) return;
1119
1120 for (int i = 1; sameCells; i++) {
1121 nextCell = xsh->getCell(row + i, col);
1122 if (nextCell.m_frameId == cell.m_frameId &&
1123 nextCell.m_level == cell.m_level) {
1124 r1 = row + i;
1125 } else
1126 sameCells = false;
1127 }
1128 m_range.m_r0 = row;
1129 m_range.m_r1 = r1;
1130 m_range.m_c0 = col;
1131 m_range.m_c1 = col;
1132 TApp::instance()->getCurrentSelection()->notifySelectionChanged();
1133 }
1134 DecreaseStepUndo *undo = new DecreaseStepUndo(m_range.m_r0, m_range.m_c0,
1135 m_range.m_r1, m_range.m_c1);
1136 TUndoManager::manager()->add(undo);
1137
1138 undo->redo();
1139
1140 if (undo->m_newR1 != m_range.m_r1) {
1141 m_range.m_r1 = undo->m_newR1;
1142 TApp::instance()->getCurrentSelection()->notifySelectionChanged();
1143 }
1144 }
1145
1146 //*********************************************************************************
1147 // Rollup Cells command
1148 //*********************************************************************************
1149
1150 namespace {
1151
1152 class RollupUndo : public TUndo {
1153 int m_r0, m_c0, m_r1, m_c1;
1154
1155 public:
RollupUndo(int r0,int c0,int r1,int c1)1156 RollupUndo(int r0, int c0, int r1, int c1)
1157 : m_r0(r0), m_c0(c0), m_r1(r1), m_c1(c1) {}
1158
redo() const1159 void redo() const override {
1160 TApp *app = TApp::instance();
1161 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1162
1163 xsh->rollupCells(m_r0, m_c0, m_r1, m_c1);
1164
1165 app->getCurrentXsheet()->notifyXsheetChanged();
1166 app->getCurrentScene()->setDirtyFlag(true);
1167 }
1168
undo() const1169 void undo() const override {
1170 TApp *app = TApp::instance();
1171 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1172
1173 xsh->rolldownCells(m_r0, m_c0, m_r1, m_c1);
1174
1175 app->getCurrentXsheet()->notifyXsheetChanged();
1176 app->getCurrentScene()->setDirtyFlag(true);
1177 }
1178
getSize() const1179 int getSize() const override { return sizeof(*this); }
1180
getHistoryString()1181 QString getHistoryString() override { return QObject::tr("Roll Up"); }
getHistoryType()1182 int getHistoryType() override { return HistoryType::Xsheet; }
1183 };
1184
1185 } // namespace
1186
1187 //=============================================================================
1188
rollupCells()1189 void TCellSelection::rollupCells() {
1190 TUndo *undo =
1191 new RollupUndo(m_range.m_r0, m_range.m_c0, m_range.m_r1, m_range.m_c1);
1192 TUndoManager::manager()->add(undo);
1193
1194 undo->redo();
1195 }
1196
1197 //*********************************************************************************
1198 // Rolldown Cells command
1199 //*********************************************************************************
1200
1201 namespace {
1202
1203 class RolldownUndo final : public RollupUndo {
1204 public:
RolldownUndo(int r0,int c0,int r1,int c1)1205 RolldownUndo(int r0, int c0, int r1, int c1) : RollupUndo(r0, c0, r1, c1) {}
1206
redo() const1207 void redo() const override { RollupUndo::undo(); }
undo() const1208 void undo() const override { RollupUndo::redo(); }
1209
getHistoryString()1210 QString getHistoryString() override { return QObject::tr("Roll Down"); }
1211 };
1212
1213 } // namespace
1214
1215 //=============================================================================
1216
rolldownCells()1217 void TCellSelection::rolldownCells() {
1218 TUndo *undo =
1219 new RolldownUndo(m_range.m_r0, m_range.m_c0, m_range.m_r1, m_range.m_c1);
1220 TUndoManager::manager()->add(undo);
1221
1222 undo->redo();
1223 }
1224
1225 //*********************************************************************************
1226 // Set Keyframes command
1227 //*********************************************************************************
1228
setKeyframes()1229 void TCellSelection::setKeyframes() {
1230 if (isEmpty()) return;
1231
1232 // Preliminary data-fetching
1233 TApp *app = TApp::instance();
1234
1235 TXsheetHandle *xshHandle = app->getCurrentXsheet();
1236 TXsheet *xsh = xshHandle->getXsheet();
1237
1238 int row = m_range.m_r0, col = m_range.m_c0;
1239
1240 const TXshCell &cell = xsh->getCell(row, col);
1241 if (cell.getSoundLevel() || cell.getSoundTextLevel()) return;
1242
1243 const TStageObjectId &id =
1244 col >= 0 ? TStageObjectId::ColumnId(col)
1245 : TStageObjectId::CameraId(xsh->getCameraColumnIndex());
1246
1247 TStageObject *obj = xsh->getStageObject(id);
1248 if (!obj) return;
1249
1250 // Command body
1251 if (obj->isFullKeyframe(row)) {
1252 const TStageObject::Keyframe &key = obj->getKeyframe(row);
1253
1254 UndoRemoveKeyFrame *undo = new UndoRemoveKeyFrame(id, row, key, xshHandle);
1255 undo->setObjectHandle(app->getCurrentObject());
1256
1257 TUndoManager::manager()->add(undo);
1258 undo->redo();
1259 } else {
1260 UndoSetKeyFrame *undo = new UndoSetKeyFrame(id, row, xshHandle);
1261 undo->setObjectHandle(app->getCurrentObject());
1262
1263 TUndoManager::manager()->add(undo);
1264 undo->redo();
1265 }
1266
1267 TApp::instance()->getCurrentScene()->setDirtyFlag(
1268 true); // Should be moved inside the undos!
1269 }
1270
1271 //*********************************************************************************
1272 // Clone Level command
1273 //*********************************************************************************
1274
1275 namespace {
1276
1277 class CloneLevelUndo final : public TUndo {
1278 typedef std::map<TXshSimpleLevel *, TXshLevelP> InsertedLevelsMap;
1279 typedef std::set<int> InsertedColumnsSet;
1280
1281 struct ExistsFunc;
1282 class LevelNamePopup;
1283
1284 private:
1285 TCellSelection::Range m_range;
1286
1287 mutable InsertedLevelsMap m_insertedLevels;
1288 mutable InsertedColumnsSet m_insertedColumns;
1289 mutable bool m_clonedLevels;
1290
1291 public:
1292 mutable bool m_ok;
1293
1294 public:
CloneLevelUndo(const TCellSelection::Range & range)1295 CloneLevelUndo(const TCellSelection::Range &range)
1296 : m_range(range), m_clonedLevels(false), m_ok(false) {}
1297
1298 void redo() const override;
1299 void undo() const override;
1300
getSize() const1301 int getSize() const override {
1302 return sizeof *this +
1303 (sizeof(TXshLevelP) + sizeof(TXshSimpleLevel *)) *
1304 m_insertedLevels.size();
1305 }
1306
getHistoryString()1307 QString getHistoryString() override {
1308 if (m_insertedLevels.empty()) return QString();
1309 QString str;
1310 if (m_insertedLevels.size() == 1) {
1311 str = QObject::tr("Clone Level : %1 > %2")
1312 .arg(QString::fromStdWString(
1313 m_insertedLevels.begin()->first->getName()))
1314 .arg(QString::fromStdWString(
1315 m_insertedLevels.begin()->second->getName()));
1316 } else {
1317 str = QObject::tr("Clone Levels : ");
1318 std::map<TXshSimpleLevel *, TXshLevelP>::const_iterator it =
1319 m_insertedLevels.begin();
1320 for (; it != m_insertedLevels.end(); ++it) {
1321 str += QString("%1 > %2, ")
1322 .arg(QString::fromStdWString(it->first->getName()))
1323 .arg(QString::fromStdWString(it->second->getName()));
1324 }
1325 }
1326 return str;
1327 }
getHistoryType()1328 int getHistoryType() override { return HistoryType::Xsheet; }
1329
1330 private:
1331 TXshSimpleLevel *cloneLevel(const TXshSimpleLevel *srcSl,
1332 const TFilePath &dstPath,
1333 const std::set<TFrameId> &frames) const;
1334
1335 bool chooseLevelName(TFilePath &fp) const;
1336 bool chooseOverwrite(OverwriteDialog *dialog, TFilePath &dstPath,
1337 TXshSimpleLevel *&dstSl) const;
1338
1339 void cloneLevels() const;
1340 void insertLevels() const;
1341 void insertCells() const;
1342 };
1343
1344 //-----------------------------------------------------------------------------
1345
1346 struct CloneLevelUndo::ExistsFunc final : public OverwriteDialog::ExistsFunc {
1347 ToonzScene *m_scene;
1348
1349 public:
ExistsFunc__anonbf6bb6d40d11::CloneLevelUndo::ExistsFunc1350 ExistsFunc(ToonzScene *scene) : m_scene(scene) {}
1351
conflictString__anonbf6bb6d40d11::CloneLevelUndo::ExistsFunc1352 QString conflictString(const TFilePath &fp) const override {
1353 return OverwriteDialog::tr(
1354 "Level \"%1\" already exists.\n\nWhat do you want to do?")
1355 .arg(QString::fromStdWString(fp.withoutParentDir().getWideString()));
1356 }
1357
operator ()__anonbf6bb6d40d11::CloneLevelUndo::ExistsFunc1358 bool operator()(const TFilePath &fp) const override {
1359 return TSystem::doesExistFileOrLevel(fp) ||
1360 m_scene->getLevelSet()->getLevel(*m_scene, fp);
1361 }
1362 };
1363
1364 //-----------------------------------------------------------------------------
1365
1366 class CloneLevelUndo::LevelNamePopup final : public DVGui::Dialog {
1367 DVGui::LineEdit *m_name;
1368 QPushButton *m_ok, *m_cancel;
1369
1370 public:
LevelNamePopup(const std::wstring & defaultLevelName)1371 LevelNamePopup(const std::wstring &defaultLevelName)
1372 : DVGui::Dialog(TApp::instance()->getMainWindow(), true, true,
1373 "Clone Level") {
1374 setWindowTitle(
1375 QObject::tr("Clone Level", "CloneLevelUndo::LevelNamePopup"));
1376
1377 beginHLayout();
1378
1379 QLabel *label = new QLabel(
1380 QObject::tr("Level Name:", "CloneLevelUndo::LevelNamePopup"));
1381 addWidget(label);
1382
1383 m_name = new DVGui::LineEdit;
1384 addWidget(m_name);
1385
1386 m_name->setText(QString::fromStdWString(defaultLevelName));
1387
1388 endHLayout();
1389
1390 m_ok = new QPushButton(QObject::tr("Ok"));
1391 m_cancel = new QPushButton(QObject::tr("Cancel"));
1392 addButtonBarWidget(m_ok, m_cancel);
1393
1394 m_ok->setDefault(true);
1395
1396 connect(m_ok, SIGNAL(clicked()), this, SLOT(accept()));
1397 connect(m_cancel, SIGNAL(clicked()), this, SLOT(reject()));
1398 }
1399
getName() const1400 QString getName() const { return m_name->text(); }
1401 };
1402
1403 //-----------------------------------------------------------------------------
1404
cloneLevel(const TXshSimpleLevel * srcSl,const TFilePath & dstPath,const std::set<TFrameId> & frames) const1405 TXshSimpleLevel *CloneLevelUndo::cloneLevel(
1406 const TXshSimpleLevel *srcSl, const TFilePath &dstPath,
1407 const std::set<TFrameId> &frames) const {
1408 ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1409
1410 int levelType = srcSl->getType();
1411 assert(levelType > 0);
1412
1413 const std::wstring &dstName = dstPath.getWideName();
1414
1415 TXshSimpleLevel *dstSl =
1416 scene->createNewLevel(levelType, dstName)->getSimpleLevel();
1417
1418 assert(dstSl);
1419 dstSl->setPath(scene->codeFilePath(dstPath));
1420 dstSl->setName(dstName);
1421 dstSl->clonePropertiesFrom(srcSl);
1422 *dstSl->getHookSet() = *srcSl->getHookSet();
1423
1424 if (levelType == TZP_XSHLEVEL || levelType == PLI_XSHLEVEL) {
1425 TPalette *palette = srcSl->getPalette();
1426 assert(palette);
1427
1428 dstSl->setPalette(palette->clone());
1429 dstSl->getPalette()->setDirtyFlag(true);
1430 }
1431
1432 // The level clone shell was created. Now, clone the associated frames found
1433 // in the selection
1434 std::set<TFrameId>::const_iterator ft, fEnd(frames.end());
1435 for (ft = frames.begin(); ft != fEnd; ++ft) {
1436 const TFrameId &fid = *ft;
1437
1438 TImageP img = srcSl->getFullsampledFrame(*ft, ImageManager::dontPutInCache);
1439 if (!img) continue;
1440
1441 dstSl->setFrame(*ft, img->cloneImage());
1442 }
1443
1444 dstSl->setDirtyFlag(true);
1445
1446 return dstSl;
1447 }
1448
1449 //-----------------------------------------------------------------------------
1450
chooseLevelName(TFilePath & fp) const1451 bool CloneLevelUndo::chooseLevelName(TFilePath &fp) const {
1452 std::unique_ptr<LevelNamePopup> levelNamePopup(
1453 new LevelNamePopup(fp.getWideName()));
1454 if (levelNamePopup->exec() == QDialog::Accepted) {
1455 const QString &levelName = levelNamePopup->getName();
1456
1457 if (isValidFileName_message(levelName) &&
1458 !isReservedFileName_message(levelName)) {
1459 fp = fp.withName(levelName.toStdWString());
1460 return true;
1461 }
1462 }
1463
1464 return false;
1465 }
1466
1467 //-----------------------------------------------------------------------------
1468
chooseOverwrite(OverwriteDialog * dialog,TFilePath & dstPath,TXshSimpleLevel * & dstSl) const1469 bool CloneLevelUndo::chooseOverwrite(OverwriteDialog *dialog,
1470 TFilePath &dstPath,
1471 TXshSimpleLevel *&dstSl) const {
1472 ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1473 ExistsFunc exists(scene);
1474
1475 OverwriteDialog::Resolution acceptedRes = OverwriteDialog::ALL_RESOLUTIONS;
1476
1477 TXshLevel *xl = scene->getLevelSet()->getLevel(*scene, dstPath);
1478 if (xl)
1479 acceptedRes =
1480 OverwriteDialog::Resolution(acceptedRes & ~OverwriteDialog::OVERWRITE);
1481
1482 // Apply user's decision
1483 switch (dialog->execute(dstPath, exists, acceptedRes,
1484 OverwriteDialog::APPLY_TO_ALL_FLAG)) {
1485 default:
1486 return false;
1487
1488 case OverwriteDialog::KEEP_OLD:
1489 // Load the level at the preferred clone path
1490 if (!xl) xl = scene->loadLevel(dstPath); // Hard load - from disk
1491
1492 assert(xl);
1493 dstSl = xl->getSimpleLevel();
1494 break;
1495
1496 case OverwriteDialog::OVERWRITE:
1497 assert(!xl);
1498 break;
1499
1500 case OverwriteDialog::RENAME:
1501 break;
1502 }
1503
1504 return true;
1505 }
1506
1507 //-----------------------------------------------------------------------------
1508
cloneLevels() const1509 void CloneLevelUndo::cloneLevels() const {
1510 TApp *app = TApp::instance();
1511 ToonzScene *scene = app->getCurrentScene()->getScene();
1512 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1513
1514 // Retrieve the simple levels and associated frames in the specified range
1515 typedef std::set<TFrameId> FramesSet;
1516 typedef std::map<TXshSimpleLevel *, FramesSet> LevelsMap;
1517
1518 LevelsMap levels;
1519 getSelectedFrames(*xsh, m_range.m_r0, m_range.m_c0, m_range.m_r1,
1520 m_range.m_c1, levels);
1521
1522 if (!levels.empty()) {
1523 bool askCloneName = (levels.size() == 1);
1524
1525 // Now, try to clone every found level in the associated range
1526 std::unique_ptr<OverwriteDialog> dialog;
1527 ExistsFunc exists(scene);
1528
1529 LevelsMap::iterator lt, lEnd(levels.end());
1530 for (lt = levels.begin(); lt != lEnd; ++lt) {
1531 assert(lt->first && !lt->second.empty());
1532
1533 TXshSimpleLevel *srcSl = lt->first;
1534 if (srcSl->getPath().getType() == "psd" ||
1535 srcSl->getPath().getType() == "gif" ||
1536 srcSl->getPath().getType() == "mp4" ||
1537 srcSl->getPath().getType() == "webm")
1538 continue;
1539
1540 const TFilePath &srcPath = srcSl->getPath();
1541
1542 // Build the destination level data
1543 TXshSimpleLevel *dstSl = 0;
1544 TFilePath dstPath = scene->decodeFilePath(
1545 srcPath.withName(srcPath.getWideName() + L"_clone"));
1546
1547 // Ask user to suggest an appropriate level name
1548 if (askCloneName && !chooseLevelName(dstPath)) continue;
1549
1550 // Get a unique level path
1551 if (exists(dstPath)) {
1552 // Ask user for action
1553 if (!dialog.get()) dialog.reset(new OverwriteDialog);
1554
1555 if (!chooseOverwrite(dialog.get(), dstPath, dstSl)) continue;
1556 }
1557
1558 // If the destination level was not retained from existing data, it must
1559 // be created and cloned
1560 if (!dstSl) dstSl = cloneLevel(srcSl, dstPath, lt->second);
1561
1562 assert(dstSl);
1563 m_insertedLevels[srcSl] = dstSl;
1564 }
1565 }
1566 }
1567
1568 //-----------------------------------------------------------------------------
1569
insertLevels() const1570 void CloneLevelUndo::insertLevels() const {
1571 ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1572
1573 InsertedLevelsMap::iterator lt, lEnd = m_insertedLevels.end();
1574 for (lt = m_insertedLevels.begin(); lt != lEnd; ++lt)
1575 scene->getLevelSet()->insertLevel(lt->second.getPointer());
1576 }
1577
1578 //-----------------------------------------------------------------------------
1579
insertCells() const1580 void CloneLevelUndo::insertCells() const {
1581 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1582
1583 m_insertedColumns.clear();
1584
1585 // If necessary, insert blank columns AFTER the columns range.
1586 // Remember the indices of inserted columns, too.
1587 for (int c = 1; c <= m_range.getColCount(); ++c) {
1588 int colIndex = m_range.m_c1 + c;
1589 if (xsh->isColumnEmpty(
1590 colIndex)) // If there already is a hole, no need to insert -
1591 continue; // we'll just use it.
1592
1593 xsh->insertColumn(colIndex);
1594 m_insertedColumns.insert(colIndex);
1595 }
1596
1597 // Now, re-traverse the selected range, and add corresponding cells
1598 // in the destination range
1599 for (int c = m_range.m_c0; c <= m_range.m_c1; ++c) {
1600 for (int r = m_range.m_r0; r <= m_range.m_r1; ++r) {
1601 const TXshCell &srcCell = xsh->getCell(r, c);
1602 if (TXshSimpleLevel *srcSl = srcCell.getSimpleLevel()) {
1603 std::map<TXshSimpleLevel *, TXshLevelP>::iterator lt =
1604 m_insertedLevels.find(srcSl);
1605 if (lt != m_insertedLevels.end()) {
1606 TXshCell dstCell(lt->second, srcCell.getFrameId());
1607 xsh->setCell(r, c + m_range.getColCount(), dstCell);
1608 }
1609 }
1610 }
1611 }
1612 }
1613
1614 //-----------------------------------------------------------------------------
1615
redo() const1616 void CloneLevelUndo::redo() const {
1617 if (m_clonedLevels)
1618 insertLevels();
1619 else {
1620 m_clonedLevels = true;
1621 cloneLevels();
1622 }
1623
1624 if (m_insertedLevels.empty()) return;
1625
1626 // Command succeeded, let's deal with the xsheet
1627 m_ok = true;
1628 insertCells();
1629
1630 // Finally, emit notifications
1631 TApp *app = TApp::instance();
1632
1633 app->getCurrentXsheet()->notifyXsheetChanged();
1634 app->getCurrentScene()->setDirtyFlag(true);
1635 app->getCurrentScene()->notifyCastChange();
1636 }
1637
1638 //-----------------------------------------------------------------------------
1639
undo() const1640 void CloneLevelUndo::undo() const {
1641 assert(!m_insertedLevels.empty());
1642
1643 TApp *app = TApp::instance();
1644 ToonzScene *scene = app->getCurrentScene()->getScene();
1645
1646 TXsheet *xsh = scene->getXsheet();
1647 TXsheet *topXsh = scene->getChildStack()->getTopXsheet();
1648
1649 // Erase inserted columns from the xsheet
1650 for (int i = m_range.getColCount(); i > 0; --i) {
1651 int index = m_range.m_c1 + i;
1652 std::set<int>::const_iterator it = m_insertedColumns.find(index);
1653 xsh->removeColumn(index);
1654 if (it == m_insertedColumns.end()) xsh->insertColumn(index);
1655 }
1656
1657 // Attempt removal of inserted columns from the cast
1658 // NOTE: Cloned levels who were KEEP_OLD'd may have already been present in
1659 // the cast
1660
1661 std::map<TXshSimpleLevel *, TXshLevelP>::const_iterator lt,
1662 lEnd = m_insertedLevels.end();
1663 for (lt = m_insertedLevels.begin(); lt != lEnd; ++lt) {
1664 if (!topXsh->isLevelUsed(lt->second.getPointer()))
1665 scene->getLevelSet()->removeLevel(lt->second.getPointer());
1666 }
1667
1668 app->getCurrentXsheet()->notifyXsheetChanged();
1669 app->getCurrentScene()->setDirtyFlag(true);
1670 app->getCurrentScene()->notifyCastChange();
1671 }
1672
1673 } // namespace
1674
1675 //-----------------------------------------------------------------------------
1676
cloneLevel()1677 void TCellSelection::cloneLevel() {
1678 std::unique_ptr<CloneLevelUndo> undo(new CloneLevelUndo(m_range));
1679
1680 if (undo->redo(), undo->m_ok) TUndoManager::manager()->add(undo.release());
1681 }
1682
1683 //=============================================================================
1684
shiftKeyframes(int direction)1685 void TCellSelection::shiftKeyframes(int direction) {
1686 if (isEmpty() || areAllColSelectedLocked()) return;
1687
1688 int shift = m_range.getRowCount() * direction;
1689 if (!shift) return;
1690
1691 TXsheetHandle *xsheet = TApp::instance()->getCurrentXsheet();
1692 TXsheet *xsh = xsheet->getXsheet();
1693 TCellKeyframeSelection *cellKeyframeSelection = new TCellKeyframeSelection(
1694 new TCellSelection(), new TKeyframeSelection());
1695
1696 cellKeyframeSelection->setXsheetHandle(xsheet);
1697
1698 TUndoManager::manager()->beginBlock();
1699 for (int col = m_range.m_c0; col <= m_range.m_c1; col++) {
1700 TXshColumn *column = xsh->getColumn(col);
1701 if (!column || column->isLocked()) continue;
1702
1703 TStageObjectId colId =
1704 col < 0 ? TStageObjectId::ColumnId(xsh->getCameraColumnIndex())
1705 : TStageObjectId::ColumnId(col);
1706 TStageObject *colObj = xsh->getStageObject(colId);
1707 TStageObject::KeyframeMap keyframes;
1708 colObj->getKeyframes(keyframes);
1709 if (!keyframes.size()) continue;
1710 int row = m_range.m_r0;
1711 for (TStageObject::KeyframeMap::iterator it = keyframes.begin();
1712 it != keyframes.end(); it++) {
1713 if (it->first < m_range.m_r0) continue;
1714 row = it->first;
1715 cellKeyframeSelection->selectCellsKeyframes(row, col,
1716 xsh->getFrameCount(), col);
1717 cellKeyframeSelection->getKeyframeSelection()->shiftKeyframes(
1718 row, row + shift, col, col);
1719 break;
1720 }
1721 }
1722 TUndoManager::manager()->endBlock();
1723
1724 delete cellKeyframeSelection;
1725 }
1726