1
2
3 #include "xsheetdragtool.h"
4
5 // Tnz6 includes
6 #include "xsheetviewer.h"
7 #include "cellselection.h"
8 #include "columncommand.h"
9 #include "keyframeselection.h"
10 #include "cellkeyframeselection.h"
11 #include "tapp.h"
12 #include "xshcolumnviewer.h"
13 #include "columnselection.h"
14 #include "filmstripselection.h"
15 #include "castselection.h"
16 #include "iocommand.h"
17 #include "keyframemover.h"
18 #include "xshcellmover.h"
19
20 // TnzTools includes
21 #include "tools/cursors.h"
22 #include "tools/cursormanager.h"
23
24 // TnzQt includes
25 #include "toonzqt/tselectionhandle.h"
26 #include "historytypes.h"
27
28 // TnzLib includes
29 #include "toonz/tscenehandle.h"
30 #include "toonz/txsheethandle.h"
31 #include "toonz/txshlevelhandle.h"
32 #include "toonz/tcolumnhandle.h"
33 #include "toonz/tframehandle.h"
34 #include "toonz/tonionskinmaskhandle.h"
35 #include "toonz/tobjecthandle.h"
36 #include "toonz/toonzscene.h"
37 #include "toonz/txsheet.h"
38 #include "toonz/txshcolumn.h"
39 #include "toonz/txshlevelcolumn.h"
40 #include "toonz/tcolumnfxset.h"
41 #include "toonz/txshcell.h"
42 #include "toonz/fxdag.h"
43 #include "toonz/sceneproperties.h"
44 #include "toonz/tstageobjecttree.h"
45 #include "toonz/tstageobjectkeyframe.h"
46 #include "toonz/onionskinmask.h"
47 #include "toonz/txshsoundcolumn.h"
48 #include "toonz/txshsimplelevel.h"
49 #include "toonz/txshnoteset.h"
50 #include "toutputproperties.h"
51 #include "toonz/preferences.h"
52 #include "toonz/columnfan.h"
53
54 // TnzBase includes
55 #include "tfx.h"
56
57 // TnzCore includes
58 #include "tundo.h"
59
60 // Qt includes
61 #include <QPainter>
62 #include <QMouseEvent>
63 #include <QUrl>
64 #include <QDropEvent>
65
66 //=============================================================================
67 // XsheetGUI DragTool
68 //-----------------------------------------------------------------------------
69
DragTool(XsheetViewer * viewer)70 XsheetGUI::DragTool::DragTool(XsheetViewer *viewer) : m_viewer(viewer) {}
71
72 //-----------------------------------------------------------------------------
73
~DragTool()74 XsheetGUI::DragTool::~DragTool() {}
75
76 //-----------------------------------------------------------------------------
77
refreshCellsArea()78 void XsheetGUI::DragTool::refreshCellsArea() { getViewer()->updateCells(); }
79
80 //-----------------------------------------------------------------------------
81
refreshColumnsArea()82 void XsheetGUI::DragTool::refreshColumnsArea() { getViewer()->updateColumns(); }
83
84 //-----------------------------------------------------------------------------
85
refreshRowsArea()86 void XsheetGUI::DragTool::refreshRowsArea() { getViewer()->updateRows(); }
87
88 //-----------------------------------------------------------------------------
89
onClick(const QMouseEvent * event)90 void XsheetGUI::DragTool::onClick(const QMouseEvent *event) {
91 QPoint xy = event->pos();
92 CellPosition pos = getViewer()->xyToPosition(xy);
93 onClick(pos);
94 }
95
96 //-----------------------------------------------------------------------------
97
onDrag(const QMouseEvent * event)98 void XsheetGUI::DragTool::onDrag(const QMouseEvent *event) {
99 QPoint xy = event->pos();
100 CellPosition pos = getViewer()->xyToPosition(xy);
101 onDrag(pos);
102 }
103
104 //-----------------------------------------------------------------------------
105
onRelease(const QMouseEvent * event)106 void XsheetGUI::DragTool::onRelease(const QMouseEvent *event) {
107 QPoint xy = event->pos();
108 CellPosition pos = getViewer()->xyToPosition(xy);
109 onRelease(pos);
110 }
111
112 //=============================================================================
113 // XsheetSelection DragTool
114 //-----------------------------------------------------------------------------
115
116 class XsheetSelectionDragTool final : public XsheetGUI::DragTool {
117 int m_firstRow, m_firstCol;
118 Qt::KeyboardModifiers m_modifier;
119
120 public:
XsheetSelectionDragTool(XsheetViewer * viewer)121 XsheetSelectionDragTool(XsheetViewer *viewer)
122 : DragTool(viewer), m_firstRow(0), m_firstCol(0), m_modifier() {}
123 // activate when clicked the cell
onClick(const QMouseEvent * event)124 void onClick(const QMouseEvent *event) override {
125 m_modifier = event->modifiers();
126 CellPosition pos = getViewer()->xyToPosition(event->pos());
127 int row = pos.frame();
128 int col = pos.layer();
129 m_firstCol = col;
130 m_firstRow = row;
131 if (m_modifier & Qt::ShiftModifier) {
132 int r0, c0, r1, c1;
133 getViewer()->getCellSelection()->getSelectedCells(r0, c0, r1, c1);
134 if (r0 <= r1 && c0 <= c1) {
135 if (abs(row - r0) < abs(row - r1)) {
136 m_firstRow = r1;
137 r0 = row;
138 } else {
139 m_firstRow = r0;
140 r1 = row;
141 }
142 if (abs(col - c0) < abs(col - c1)) {
143 m_firstCol = c1;
144 c0 = col;
145 } else {
146 m_firstCol = c0;
147 c1 = col;
148 }
149 if (m_modifier & Qt::ControlModifier)
150 getViewer()->getCellKeyframeSelection()->selectCellsKeyframes(r0, c0,
151 r1, c1);
152 else
153 getViewer()->getCellSelection()->selectCells(r0, c0, r1, c1);
154 } else {
155 if (m_modifier & Qt::ControlModifier)
156 getViewer()->getCellKeyframeSelection()->selectCellsKeyframes(
157 row, col, row, col);
158 else
159 getViewer()->getCellSelection()->selectCells(row, col, row, col);
160 getViewer()->setCurrentColumn(col);
161 if (Preferences::instance()->isMoveCurrentEnabled())
162 getViewer()->setCurrentRow(row);
163 }
164 } else {
165 getViewer()->setCurrentColumn(col);
166 if (Preferences::instance()->isMoveCurrentEnabled())
167 getViewer()->setCurrentRow(row);
168 if (m_modifier & Qt::ControlModifier)
169 getViewer()->getCellKeyframeSelection()->selectCellKeyframe(row, col);
170 else
171 getViewer()->getCellSelection()->selectCell(row, col);
172 }
173 if (m_modifier & Qt::ControlModifier)
174 getViewer()->getCellKeyframeSelection()->makeCurrent();
175 else
176 getViewer()->getCellSelection()->makeCurrent();
177 refreshCellsArea();
178 refreshRowsArea();
179 }
onDrag(const CellPosition & pos)180 void onDrag(const CellPosition &pos) override {
181 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
182 int row = pos.frame(), col = pos.layer();
183 int firstCol =
184 Preferences::instance()->isXsheetCameraColumnVisible() ? -1 : 0;
185 if (col < firstCol || (!getViewer()->orientation()->isVerticalTimeline() &&
186 col >= xsh->getColumnCount()))
187 return;
188 if (row < 0) row = 0;
189 if (m_modifier & Qt::ControlModifier)
190 getViewer()->getCellKeyframeSelection()->selectCellsKeyframes(
191 m_firstRow, m_firstCol, row, col);
192 else
193 getViewer()->getCellSelection()->selectCells(m_firstRow, m_firstCol, row,
194 col);
195 refreshCellsArea();
196 refreshRowsArea();
197 }
onRelease(const QMouseEvent * event)198 void onRelease(const QMouseEvent *event) override {
199 TApp::instance()->getCurrentSelection()->notifySelectionChanged();
200 refreshRowsArea();
201 }
202 };
203
204 //-----------------------------------------------------------------------------
205
makeSelectionTool(XsheetViewer * viewer)206 XsheetGUI::DragTool *XsheetGUI::DragTool::makeSelectionTool(
207 XsheetViewer *viewer) {
208 return new XsheetSelectionDragTool(viewer);
209 }
210
211 //-----------------------------------------------------------------------------
212 namespace {
213
214 class UndoPlayRangeModifier final : public TUndo {
215 int m_oldR0, m_oldR1, m_newR0, m_newR1, m_newStep, m_oldStep;
216
217 public:
UndoPlayRangeModifier(int oldR0,int newR0,int oldR1,int newR1,int oldStep,int newStep,bool oldEnabled,bool newEnabled)218 UndoPlayRangeModifier(int oldR0, int newR0, int oldR1, int newR1, int oldStep,
219 int newStep, bool oldEnabled, bool newEnabled)
220 : m_oldR0(oldR0)
221 , m_newR0(newR0)
222 , m_oldR1(oldR1)
223 , m_newR1(newR1)
224 , m_oldStep(oldStep)
225 , m_newStep(newStep) {}
226
undo() const227 void undo() const override {
228 XsheetGUI::setPlayRange(m_oldR0, m_oldR1, m_oldStep, false);
229 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
230 }
231
redo() const232 void redo() const override {
233 XsheetGUI::setPlayRange(m_newR0, m_newR1, m_newStep, false);
234 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
235 }
236
getSize() const237 int getSize() const override { return sizeof(*this); }
238
getHistoryString()239 QString getHistoryString() override {
240 if (m_oldR0 < 0 || m_oldR1 < 0)
241 return QObject::tr("Modify Play Range : %1 - %2")
242 .arg(QString::number(m_newR0 + 1))
243 .arg(QString::number(m_newR1 + 1));
244
245 return QObject::tr("Modify Play Range : %1 - %2 > %3 - %4")
246 .arg(QString::number(m_oldR0 + 1))
247 .arg(QString::number(m_oldR1 + 1))
248 .arg(QString::number(m_newR0 + 1))
249 .arg(QString::number(m_newR1 + 1));
250 }
getHistoryType()251 int getHistoryType() override { return HistoryType::Xsheet; }
252 };
253
254 } // namespace
255
256 //-----------------------------------------------------------------------------
257
setPlayRange(int r0,int r1,int step,bool withUndo)258 void XsheetGUI::setPlayRange(int r0, int r1, int step, bool withUndo) {
259 if (r0 > r1) {
260 r0 = 0;
261 r1 = -1;
262 }
263 if (withUndo) {
264 int oldR0, oldR1, oldStep;
265 XsheetGUI::getPlayRange(oldR0, oldR1, oldStep);
266 TUndoManager::manager()->add(new UndoPlayRangeModifier(
267 oldR0, r0, oldR1, r1, oldStep, step, true, true));
268 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
269 }
270 ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
271 scene->getProperties()->getPreviewProperties()->setRange(r0, r1, step);
272 TApp::instance()->getCurrentScene()->notifySceneChanged();
273 }
274
275 //-----------------------------------------------------------------------------
276
getPlayRange(int & r0,int & r1,int & step)277 bool XsheetGUI::getPlayRange(int &r0, int &r1, int &step) {
278 ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
279 scene->getProperties()->getPreviewProperties()->getRange(r0, r1, step);
280 if (r0 > r1) {
281 r0 = -1;
282 r1 = -2;
283 return false;
284 } else
285 return true;
286 }
287
288 //-----------------------------------------------------------------------------
289
isPlayRangeEnabled()290 bool XsheetGUI::isPlayRangeEnabled() {
291 int playR0, playR1, step;
292 XsheetGUI::getPlayRange(playR0, playR1, step);
293 return playR0 <= playR1;
294 }
295
296 //=============================================================================
297 // LevelMover tool
298 //-----------------------------------------------------------------------------
299 namespace {
300
301 //=============================================================================
302 // CellMovementUndo
303 //-----------------------------------------------------------------------------
304
305 } // namespace
306
307 //-----------------------------------------------------------------------------
308
makeLevelMoverTool(XsheetViewer * viewer)309 XsheetGUI::DragTool *XsheetGUI::DragTool::makeLevelMoverTool(
310 XsheetViewer *viewer) {
311 return new LevelMoverTool(viewer);
312 }
313
314 //=============================================================================
315 // LevelExtender DragTool
316 //-----------------------------------------------------------------------------
317
318 namespace {
319 //-----------------------------------------------------------------------------
320
321 //=============================================================================
322 // LevelExtenderUndo
323 //-----------------------------------------------------------------------------
324
325 class LevelExtenderUndo final : public TUndo {
326 int m_colCount;
327 int m_rowCount;
328 int m_col, m_row, m_deltaRow;
329 std::vector<TXshCell> m_cells; // righe x colonne
330
331 bool m_insert;
332 bool m_invert; // upper-directional
333
334 public:
LevelExtenderUndo(bool insert=true,bool invert=false)335 LevelExtenderUndo(bool insert = true, bool invert = false)
336 : m_colCount(0)
337 , m_rowCount(0)
338 , m_col(0)
339 , m_row(0)
340 , m_deltaRow(0)
341 , m_insert(insert)
342 , m_invert(invert) {}
343
setCells(TXsheet * xsh,int row,int col,int rowCount,int colCount)344 void setCells(TXsheet *xsh, int row, int col, int rowCount, int colCount) {
345 assert(rowCount > 0 && colCount > 0);
346 m_row = row;
347 m_col = col;
348 m_rowCount = rowCount;
349 m_colCount = colCount;
350 m_cells.resize(rowCount * colCount, TXshCell());
351 int k = 0;
352 for (int r = row; r < row + rowCount; r++)
353 for (int c = col; c < col + colCount; c++)
354 m_cells[k++] = xsh->getCell(r, c);
355 }
356
setDeltaRow(int drow)357 void setDeltaRow(int drow) {
358 assert(drow != 0);
359 m_deltaRow = drow;
360 }
361
removeCells() const362 void removeCells() const {
363 assert(m_deltaRow != 0);
364 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
365 int count = abs(m_deltaRow);
366 int r = m_row + m_rowCount - count;
367 for (int c = m_col; c < m_col + m_colCount; c++) {
368 // Se e' una colonna sound l'extender non deve fare nulla.
369 TXshColumn *column = xsh->getColumn(c);
370 if (column && column->getSoundColumn()) continue;
371 xsh->removeCells(r, c, count);
372 }
373 }
374
insertCells() const375 void insertCells() const {
376 assert(m_deltaRow != 0);
377 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
378 int count = abs(m_deltaRow);
379 int r0 = m_row + m_rowCount - count;
380 int r1 = m_row + m_rowCount - 1;
381 for (int c = 0; c < m_colCount; c++) {
382 // Se e' una colonna sound l'extender non deve fare nulla.
383 TXshColumn *column = xsh->getColumn(c);
384 if (column && column->getSoundColumn()) continue;
385 int col = m_col + c;
386 xsh->insertCells(r0, col, count);
387 int r;
388 for (r = r0; r <= r1; r++) {
389 int k = (r - m_row) * m_colCount + c;
390 xsh->setCell(r, col, m_cells[k]);
391 }
392 }
393 }
394
395 // for upper-directional smart tab
396 // also used for non-insert(overwriting) extension
clearCells() const397 void clearCells() const {
398 assert(m_deltaRow != 0);
399 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
400 int count = abs(m_deltaRow);
401 for (int c = m_col; c < m_col + m_colCount; c++) {
402 TXshColumn *column = xsh->getColumn(c);
403 if (column && column->getSoundColumn()) continue;
404 if (m_invert)
405 xsh->clearCells(m_row, c, count);
406 else
407 xsh->clearCells(m_row + m_rowCount - count, c, count);
408 }
409 }
410
411 // for upper-directional smart tab
412 // also used for non-insert(overwriting) extension
setCells() const413 void setCells() const {
414 assert(m_deltaRow != 0);
415 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
416 int count = abs(m_deltaRow);
417
418 int r0, r1;
419 if (m_invert) {
420 r0 = m_row;
421 r1 = m_row + count - 1;
422 } else {
423 r0 = m_row + m_rowCount - count - 1;
424 r1 = m_row + m_rowCount - 1;
425 }
426 for (int c = 0; c < m_colCount; c++) {
427 TXshColumn *column = xsh->getColumn(c);
428 if (column && column->getSoundColumn()) continue;
429 int col = m_col + c;
430 for (int r = r0; r <= r1; r++) {
431 int k = (r - m_row) * m_colCount + c;
432 xsh->setCell(r, col, m_cells[k]);
433 }
434 }
435 }
436
undo() const437 void undo() const override {
438 // undo for shrinking operation -> revert cells
439 if (m_deltaRow < 0) (m_insert) ? insertCells() : setCells();
440 // undo for stretching operation -> remove cells
441 else if (m_deltaRow > 0)
442 (m_insert) ? removeCells() : clearCells();
443 else {
444 assert(0);
445 }
446 TSelection *selection =
447 TApp::instance()->getCurrentSelection()->getSelection();
448 if (selection) selection->selectNone();
449 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
450 }
451
redo() const452 void redo() const override {
453 // redo for shrinking operation -> remove cells
454 if (m_deltaRow < 0) (m_insert) ? removeCells() : clearCells();
455 // redo for stretching operation -> revert cells
456 else if (m_deltaRow > 0)
457 (m_insert) ? insertCells() : setCells();
458 else {
459 assert(0);
460 }
461 TSelection *selection =
462 TApp::instance()->getCurrentSelection()->getSelection();
463 if (selection) selection->selectNone();
464 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
465 }
466
getSize() const467 int getSize() const override {
468 return sizeof(*this) + sizeof(TXshCell) * m_cells.size();
469 }
470
getHistoryString()471 QString getHistoryString() override {
472 return QObject::tr("Use Level Extender");
473 }
getHistoryType()474 int getHistoryType() override { return HistoryType::Xsheet; }
475 };
476
477 //=============================================================================
478 // CellBuilder
479 //-----------------------------------------------------------------------------
480
481 class CellBuilder {
482 std::vector<TXshCell> m_sourceCells;
483 bool m_sequenceFound;
484 int m_base, m_inc, m_step, m_offset, m_count;
485 int m_firstRow;
486
487 // upper-directional smart tab
488 bool m_invert;
489
490 public:
491 // CellBuilder is constructed when the smart tab is clicked.
492 // row : top row in the selection, col : current column, rowCount :
493 // selected row count
CellBuilder(TXsheet * xsh,int row,int col,int rowCount,int invert=false)494 CellBuilder(TXsheet *xsh, int row, int col, int rowCount, int invert = false)
495 : m_firstRow(row)
496 , m_sequenceFound(false)
497 , m_base(0)
498 , m_inc(0)
499 , m_step(0)
500 , m_offset(0)
501 , m_count(0)
502 , m_sourceCells(rowCount, TXshCell())
503 , m_invert(invert) {
504 if (!m_invert)
505 xsh->getCells(row, col, rowCount, &m_sourceCells[0]);
506 else {
507 std::vector<TXshCell> cells(rowCount, TXshCell());
508 xsh->getCells(row, col, rowCount, &cells[0]);
509 for (int r = 0; r < rowCount; r++)
510 m_sourceCells[r] = cells[rowCount - 1 - r];
511 // the "first row" becomes the bottom-most row for the upper-directional
512 // smart tab
513 m_firstRow = row + rowCount - 1;
514 }
515 analyzeCells();
516 }
CellBuilder()517 CellBuilder()
518 : m_firstRow(0)
519 , m_sequenceFound(false)
520 , m_base(0)
521 , m_inc(0)
522 , m_step(0)
523 , m_offset(0)
524 , m_count(0)
525 , m_sourceCells() {}
526
computeFrame(int i) const527 TFrameId computeFrame(int i) const {
528 assert(i >= 0);
529 int k = i + m_offset;
530 // if there is a large cycle
531 if (m_count > 0) k = k % m_count;
532 k = m_base + (k / m_step) * m_inc;
533 if (m_inc < 0)
534 while (k < 1) k += m_base;
535 return TFrameId(k);
536 }
537
analyzeCells()538 void analyzeCells() {
539 // se e' vuoto non c'e' sequenza
540 if (m_sourceCells.empty()) return;
541 TXshCell cell = m_sourceCells[0];
542 if (!cell.m_level) return;
543
544 // controllo che tutte le celle appartengano allo stesso livello
545 // e che i frameId non abbiano suffissi (es. 0012b)
546 // there is no rule if there are different levels in the selection or
547 // letters in the frame numbers
548 int count = m_sourceCells.size();
549 int i;
550 for (i = 1; i < count; i++)
551 if (m_sourceCells[i].m_level != cell.m_level ||
552 m_sourceCells[i].m_frameId.getLetter() != 0)
553 return;
554
555 // check if all the selected cells have the same frame number
556 // 'm_base' e' il primo frame number
557 m_base = cell.m_frameId.getNumber();
558 // cerco il primo cambiamento di frame
559 for (i = 1; i < count; i++)
560 if (m_sourceCells[i].m_frameId != cell.m_frameId) break;
561
562 // there is no rule if all the selected cells are the same.
563 // se sono tutti uguali la sequenza e' banale (e la tratto come
564 // se non ci fosse)
565 if (i == count) return;
566
567 // check if there is a incremental rule.
568 // 'm_inc' e' l'incremento di frame number
569 m_inc = m_sourceCells[i].m_frameId.getNumber() - m_base;
570 assert(m_inc != 0);
571 // 'm_step' e' il numero di volte che viene ripetuto un frame
572 m_step = i;
573 // se 'm_offset'>0 vuol dire che m_step>1 e la selezione e' partita m_offset
574 // celle dopo un cambio di frame
575 m_offset = 0;
576 if (m_step > 1) {
577 //先頭と異なるセルを仮にループ終端とする
578 TFrameId fid(m_sourceCells[m_step].m_frameId);
579 //次にループ終端と異なるセルがくるまでセルを送る
580 for (i++; i < count && m_sourceCells[i].m_frameId == fid; i++) {
581 }
582 //次のループ終端候補が見つかった
583 int step = i - m_step;
584 //ループ距離が縮んでいたら規則なし
585 if (step < m_step) return;
586 m_offset = step - m_step;
587 m_step = step;
588 }
589
590 // la sequenza potrebbe essere ciclica. cerco di trovare
591 // l'ultimo elemento
592 for (; i < count; i++)
593 if (m_sourceCells[i].m_frameId != computeFrame(i)) break;
594 // check if there are the double loops
595 if (i < count) {
596 TFrameId first(m_base);
597 if (m_sourceCells[i].m_frameId != first) return;
598 if (m_step > 1 && ((i + m_offset) % m_step) != 0) return;
599 m_count = i + m_offset;
600
601 // controllo che le celle restanti verifichino la sequenza
602 for (; i < count; i++)
603 if (m_sourceCells[i].m_frameId != computeFrame(i)) return;
604 }
605
606 // eureka!
607 m_sequenceFound = true;
608 }
609
generate(int row) const610 TXshCell generate(int row) const {
611 int i = row - m_firstRow;
612
613 if (m_invert) i = -i;
614
615 if (i < 0 || m_sourceCells.empty())
616 return TXshCell();
617 else if (i < (int)m_sourceCells.size())
618 return m_sourceCells[i];
619 else if (m_sequenceFound) {
620 TXshCell cell;
621 cell.m_level = m_sourceCells[0].m_level;
622 cell.m_frameId = computeFrame(i);
623 return cell;
624 } else {
625 i = i % m_sourceCells.size();
626 return m_sourceCells[i];
627 }
628 }
629 };
630
631 //-----------------------------------------------------------------------------
632 } // namespace
633 //-----------------------------------------------------------------------------
634
635 //=============================================================================
636 // LevelExtenderTool
637 //-----------------------------------------------------------------------------
638
639 class LevelExtenderTool final : public XsheetGUI::DragTool {
640 int m_colCount;
641 int m_rowCount;
642 int m_c0, m_r0, m_r1;
643 std::vector<CellBuilder> m_columns;
644 LevelExtenderUndo *m_undo;
645
646 bool m_invert; // upper directional smart tab
647 bool m_insert;
648
649 public:
LevelExtenderTool(XsheetViewer * viewer,bool insert=true,bool invert=false)650 LevelExtenderTool(XsheetViewer *viewer, bool insert = true,
651 bool invert = false)
652 : XsheetGUI::DragTool(viewer)
653 , m_colCount(0)
654 , m_undo(0)
655 , m_insert(insert)
656 , m_invert(invert) {}
657
658 // called when the smart tab is clicked
onClick(const CellPosition & pos)659 void onClick(const CellPosition &pos) override {
660 int row = pos.frame(), col = pos.layer();
661 int r0, c0, r1, c1;
662 getViewer()->getCellSelection()->getSelectedCells(r0, c0, r1, c1);
663 if (m_invert)
664 assert(r0 == row + 1);
665 else
666 assert(r1 == row - 1);
667 m_c0 = c0;
668 m_r0 = r0;
669 m_r1 = r1;
670 m_colCount = c1 - c0 + 1;
671 m_rowCount = r1 - r0 + 1;
672 if (m_colCount <= 0 || m_rowCount <= 0) return;
673
674 // if m_insert is false but there are no empty rows under the tab,
675 // then switch m_insert to true so that the operation works anyway
676 if (!m_insert && !m_invert) {
677 TXsheet *xsh = getViewer()->getXsheet();
678 for (int c = c0; c <= c1; c++) {
679 TXshColumn *column = xsh->getColumn(c);
680 if (!column || column->getSoundColumn()) continue;
681 if (!column->isCellEmpty(r1 + 1)) {
682 m_insert = true; // switch the behavior
683 break;
684 }
685 }
686 }
687
688 m_columns.reserve(m_colCount);
689 TXsheet *xsh = getViewer()->getXsheet();
690 for (int c = c0; c <= c1; c++)
691 m_columns.push_back(CellBuilder(xsh, r0, c, m_rowCount, m_invert));
692 m_undo = new LevelExtenderUndo(m_insert, m_invert);
693 m_undo->setCells(xsh, r0, c0, m_rowCount, m_colCount);
694 }
695
onDrag(const CellPosition & pos)696 void onDrag(const CellPosition &pos) override {
697 int row = pos.frame(), col = pos.layer();
698 if (!m_invert)
699 onCellChange(row, col);
700 else
701 onCellChangeInvert(row, col);
702 refreshCellsArea();
703 }
704
onCellChange(int row,int col)705 void onCellChange(int row, int col) {
706 if (m_colCount <= 0 || m_rowCount <= 0) return;
707 if (row <= m_r0) row = m_r0 + 1;
708 int r1 = row - 1; // r1 e' la riga inferiore della nuova selezione
709 if (r1 < m_r0) r1 = m_r0;
710 int dr = r1 - m_r1;
711 if (dr == 0) return;
712 TXsheet *xsh = getViewer()->getXsheet();
713 // shrink
714 if (dr < 0) {
715 for (int c = 0; c < m_colCount; c++) {
716 // Se e' una colonna sound l'extender non deve fare nulla.
717 TXshColumn *column = xsh->getColumn(m_c0 + c);
718 if (column && column->getSoundColumn()) continue;
719 if (m_insert)
720 xsh->removeCells(row, m_c0 + c, -dr);
721 else {
722 for (int r = row; r <= m_r1; r++)
723 xsh->setCell(r, m_c0 + c, TXshCell());
724 }
725 }
726 }
727 // extend
728 else {
729 // check how many vacant rows
730 if (!m_insert) {
731 int tmp_dr;
732 bool found = false;
733 for (tmp_dr = 1; tmp_dr <= dr; tmp_dr++) {
734 for (int c = 0; c < m_colCount; c++) {
735 TXshColumn *column = xsh->getColumn(m_c0 + c);
736 if (!column || column->getSoundColumn()) continue;
737 if (!column->isCellEmpty(m_r1 + tmp_dr)) {
738 found = true;
739 break;
740 }
741 }
742 if (found) break;
743 }
744 if (tmp_dr == 1) return;
745 dr = tmp_dr - 1;
746 r1 = m_r1 + dr;
747 }
748
749 for (int c = 0; c < m_colCount; c++) {
750 // Se e' una colonna sound l'extender non deve fare nulla.
751 TXshColumn *column = xsh->getColumn(m_c0 + c);
752 if (column && column->getSoundColumn()) continue;
753 if (m_insert) xsh->insertCells(m_r1 + 1, m_c0 + c, dr);
754 for (int r = m_r1 + 1; r <= r1; r++)
755 xsh->setCell(r, m_c0 + c, m_columns[c].generate(r));
756 }
757 }
758 m_r1 = r1;
759 getViewer()->getCellSelection()->selectCells(m_r0, m_c0, m_r1,
760 m_c0 + m_colCount - 1);
761 }
762
763 // for upper-directinoal smart tab
onCellChangeInvert(int row,int col)764 void onCellChangeInvert(int row, int col) {
765 if (m_colCount <= 0 || m_rowCount <= 0) return;
766 if (row >= m_r1) row = m_r1 - 1;
767 int r0 = row + 1;
768 if (r0 > m_r1) r0 = m_r1;
769
770 if (r0 < 0) r0 = 0;
771
772 TXsheet *xsh = getViewer()->getXsheet();
773 // check how many vacant rows
774 int emptyRow;
775 for (emptyRow = m_r0 - 1; emptyRow >= 0; emptyRow--) {
776 bool found = false;
777 for (int c = 0; c < m_colCount; c++) {
778 TXshColumn *column = xsh->getColumn(m_c0 + c);
779 if (!column || column->getSoundColumn()) continue;
780 if (!column->isCellEmpty(emptyRow)) {
781 emptyRow += 1;
782 found = true;
783 break;
784 }
785 }
786 if (found) break;
787 }
788
789 // upper-directional tab can extend cells only in the mpty area
790 if (r0 < emptyRow) r0 = emptyRow;
791
792 int dr = r0 - m_r0;
793 if (dr == 0) return;
794
795 // shrink
796 if (dr > 0) {
797 // clear cells
798 for (int c = 0; c < m_colCount; c++) {
799 TXshColumn *column = xsh->getColumn(m_c0 + c);
800 if (!column || column->getSoundColumn()) continue;
801 xsh->clearCells(m_r0, m_c0 + c, dr);
802 }
803 }
804 // extend
805 else {
806 for (int c = 0; c < m_colCount; c++) {
807 TXshColumn *column = xsh->getColumn(m_c0 + c);
808 if (!column || column->getSoundColumn()) continue;
809 for (int r = r0; r <= m_r0 - 1; r++) {
810 xsh->setCell(r, m_c0 + c, m_columns[c].generate(r));
811 }
812 }
813 }
814 m_r0 = r0;
815 getViewer()->getCellSelection()->selectCells(m_r0, m_c0, m_r1,
816 m_c0 + m_colCount - 1);
817 }
818
onRelease(const CellPosition & pos)819 void onRelease(const CellPosition &pos) override {
820 int row = pos.frame(), col = pos.layer();
821 int delta = m_r1 - (m_r0 + m_rowCount - 1);
822 if (delta == 0)
823 delete m_undo;
824 else {
825 if (delta > 0)
826 m_undo->setCells(getViewer()->getXsheet(), m_r0, m_c0, m_r1 - m_r0 + 1,
827 m_colCount);
828 m_undo->setDeltaRow(delta);
829 TUndoManager::manager()->add(m_undo);
830 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
831 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
832 }
833 m_undo = 0;
834 }
835 };
836
837 //-----------------------------------------------------------------------------
838
makeLevelExtenderTool(XsheetViewer * viewer,bool insert,bool invert)839 XsheetGUI::DragTool *XsheetGUI::DragTool::makeLevelExtenderTool(
840 XsheetViewer *viewer, bool insert, bool invert) {
841 return new LevelExtenderTool(viewer, insert, invert);
842 }
843
844 //=============================================================================
845 // LevelExtender DragTool
846 //-----------------------------------------------------------------------------
847
848 namespace {
849 //-----------------------------------------------------------------------------
850
851 //=============================================================================
852 // SoundLevelModifierUndo
853 //-----------------------------------------------------------------------------
854
855 class SoundLevelModifierUndo final : public TUndo {
856 int m_col;
857 TXshSoundColumnP m_newSoundColumn;
858 TXshSoundColumnP m_oldSoundColumn;
859
860 TXsheetHandle *m_xshHandle;
861
862 public:
SoundLevelModifierUndo(int col,TXshSoundColumn * oldSoundColumn)863 SoundLevelModifierUndo(int col, TXshSoundColumn *oldSoundColumn)
864 : m_col(col), m_newSoundColumn(), m_oldSoundColumn(oldSoundColumn) {
865 m_xshHandle = TApp::instance()->getCurrentXsheet();
866 }
867
setNewColumn(TXshSoundColumn * newSoundcolumn)868 void setNewColumn(TXshSoundColumn *newSoundcolumn) {
869 m_newSoundColumn = dynamic_cast<TXshSoundColumn *>(newSoundcolumn->clone());
870 }
871
getColumn() const872 TXshSoundColumn *getColumn() const {
873 TXsheet *xsh = m_xshHandle->getXsheet();
874 TXshColumn *column = xsh->getColumn(m_col);
875 assert(column);
876 // La colonna sound deve esserci, metto un controllo di sicurezza.
877 TXshSoundColumn *soundColumn = column->getSoundColumn();
878 assert(soundColumn);
879 return soundColumn;
880 }
881
undo() const882 void undo() const override {
883 TXshSoundColumn *soundColumn = getColumn();
884 if (!soundColumn) return;
885 soundColumn->assignLevels(m_oldSoundColumn.getPointer());
886 m_xshHandle->notifyXsheetChanged();
887 }
888
redo() const889 void redo() const override {
890 TXshSoundColumn *soundColumn = getColumn();
891 if (!soundColumn) return;
892 soundColumn->assignLevels(m_newSoundColumn.getPointer());
893 m_xshHandle->notifyXsheetChanged();
894 }
895
getSize() const896 int getSize() const override { return sizeof(*this); }
897
getHistoryString()898 QString getHistoryString() override {
899 return QObject::tr("Modify Sound Level");
900 }
901
getHistoryType()902 int getHistoryType() override { return HistoryType::Xsheet; }
903 };
904
905 } // namespace
906
907 //=============================================================================
908 // SoundLevelModifierTool
909 //-----------------------------------------------------------------------------
910
911 class SoundLevelModifierTool final : public XsheetGUI::DragTool {
912 int m_col;
913 int m_firstRow;
914 TXshSoundColumnP m_oldColumn;
915 SoundLevelModifierUndo *m_undo;
916
917 enum ModifierType {
918 UNKNOWN_TYPE = 0,
919 START_TYPE = 1,
920 END_TYPE = 2,
921 } m_modifierType;
922
923 public:
SoundLevelModifierTool(XsheetViewer * viewer)924 SoundLevelModifierTool(XsheetViewer *viewer)
925 : XsheetGUI::DragTool(viewer)
926 , m_col(-1)
927 , m_firstRow(-1)
928 , m_oldColumn()
929 , m_modifierType(UNKNOWN_TYPE)
930 , m_undo(0) {}
931
getType(int r0,int r1,int delta)932 ModifierType getType(int r0, int r1, int delta) {
933 if (m_firstRow == r0 && m_firstRow == r1) {
934 if (delta == 0)
935 return UNKNOWN_TYPE;
936 else if (delta < 0)
937 return START_TYPE;
938 else
939 return END_TYPE;
940 }
941 if (m_firstRow == r0)
942 return START_TYPE;
943 else if (m_firstRow == r1)
944 return END_TYPE;
945 return UNKNOWN_TYPE;
946 }
947
~SoundLevelModifierTool()948 ~SoundLevelModifierTool() { delete m_undo; }
949
getColumn() const950 TXshSoundColumn *getColumn() const {
951 TXshColumn *column = getViewer()->getXsheet()->getColumn(m_col);
952 assert(column);
953 // La colonna sound deve esserci, metto un controllo di sicurezza.
954 TXshSoundColumn *soundColumn = column->getSoundColumn();
955 assert(soundColumn);
956 return soundColumn;
957 }
958
onClick(const CellPosition & pos)959 void onClick(const CellPosition &pos) override {
960 m_firstRow = pos.frame();
961 m_col = pos.layer();
962 TXshSoundColumn *soundColumn = getColumn();
963 if (!soundColumn) return;
964 m_oldColumn = dynamic_cast<TXshSoundColumn *>(soundColumn->clone());
965 m_undo = new SoundLevelModifierUndo(m_col, m_oldColumn.getPointer());
966 getViewer()->update();
967 }
968
onDrag(const CellPosition & pos)969 void onDrag(const CellPosition &pos) override {
970 onChange(pos.frame());
971 refreshCellsArea();
972 }
973
onChange(int row)974 void onChange(int row) {
975 TXshSoundColumn *soundColumn = getColumn();
976 if (!soundColumn) return;
977
978 int delta = row - m_firstRow;
979 if (delta == 0) {
980 soundColumn->assignLevels(m_oldColumn.getPointer());
981 return;
982 }
983 int r0, r1;
984 m_oldColumn->getLevelRange(m_firstRow, r0, r1);
985 ModifierType type = getType(r0, r1, delta);
986 if (m_modifierType == UNKNOWN_TYPE && type != UNKNOWN_TYPE)
987 m_modifierType = type;
988 else if (m_modifierType != type || type == UNKNOWN_TYPE)
989 return;
990
991 soundColumn->assignLevels(m_oldColumn.getPointer());
992 soundColumn->modifyCellRange(m_firstRow, delta,
993 m_modifierType == START_TYPE);
994
995 TApp::instance()->getCurrentXsheet()->notifyXsheetSoundChanged();
996 }
997
onRelease(const CellPosition & pos)998 void onRelease(const CellPosition &pos) override {
999 int row = pos.frame();
1000 if (row - m_firstRow == 0) {
1001 m_undo = 0;
1002 return;
1003 }
1004
1005 TXshColumn *column = getViewer()->getXsheet()->getColumn(m_col);
1006 TXshSoundColumn *soundColumn = getColumn();
1007 if (!soundColumn) return;
1008
1009 soundColumn->updateCells(row, 1);
1010
1011 m_undo->setNewColumn(soundColumn);
1012 TUndoManager::manager()->add(m_undo);
1013 m_undo = 0;
1014 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1015 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1016 }
1017 };
1018
1019 //-----------------------------------------------------------------------------
1020
makeSoundLevelModifierTool(XsheetViewer * viewer)1021 XsheetGUI::DragTool *XsheetGUI::DragTool::makeSoundLevelModifierTool(
1022 XsheetViewer *viewer) {
1023 return new SoundLevelModifierTool(viewer);
1024 }
1025
1026 //=============================================================================
1027 // KeyFrame Mover DragTool
1028 //-----------------------------------------------------------------------------
1029
makeKeyframeMoverTool(XsheetViewer * viewer)1030 XsheetGUI::DragTool *XsheetGUI::DragTool::makeKeyframeMoverTool(
1031 XsheetViewer *viewer) {
1032 return new KeyframeMoverTool(viewer);
1033 }
1034
1035 //=============================================================================
1036 // Cell KeyFrame Mover DragTool
1037 //-----------------------------------------------------------------------------
1038
1039 class CellKeyframeMoverTool final : public LevelMoverTool {
1040 KeyframeMoverTool *m_keyframeMoverTool;
1041
1042 protected:
canMove(const TPoint & pos)1043 bool canMove(const TPoint &pos) override {
1044 if (!m_keyframeMoverTool->canMove(pos)) return false;
1045 return LevelMoverTool::canMove(pos);
1046 }
1047
1048 public:
CellKeyframeMoverTool(XsheetViewer * viewer)1049 CellKeyframeMoverTool(XsheetViewer *viewer) : LevelMoverTool(viewer) {
1050 m_keyframeMoverTool = new KeyframeMoverTool(viewer, true);
1051 }
1052
onClick(const QMouseEvent * e)1053 void onClick(const QMouseEvent *e) override {
1054 LevelMoverTool::onClick(e);
1055 m_keyframeMoverTool->onClick(e);
1056 }
1057
onDrag(const QMouseEvent * e)1058 void onDrag(const QMouseEvent *e) override {
1059 LevelMoverTool::onDrag(e);
1060 if (m_validPos) m_keyframeMoverTool->onDrag(e);
1061 }
onRelease(const CellPosition & pos)1062 void onRelease(const CellPosition &pos) override {
1063 int row = pos.frame(), col = pos.layer();
1064 TUndoManager::manager()->beginBlock();
1065 LevelMoverTool::onRelease(pos);
1066 m_keyframeMoverTool->onRelease(pos);
1067 TUndoManager::manager()->endBlock();
1068 }
1069
drawCellsArea(QPainter & p)1070 void drawCellsArea(QPainter &p) override {
1071 LevelMoverTool::drawCellsArea(p);
1072 m_keyframeMoverTool->drawCellsArea(p);
1073 }
1074 };
1075
1076 //=============================================================================
1077
makeCellKeyframeMoverTool(XsheetViewer * viewer)1078 XsheetGUI::DragTool *XsheetGUI::DragTool::makeCellKeyframeMoverTool(
1079 XsheetViewer *viewer) {
1080 return new CellKeyframeMoverTool(viewer);
1081 }
1082
1083 //=============================================================================
1084 // KeyFrame Handle Mover DragTool
1085 //-----------------------------------------------------------------------------
1086
1087 namespace {
1088 //-----------------------------------------------------------------------------
1089
1090 //=============================================================================
1091 // KeyFrameHandleUndo
1092 //-----------------------------------------------------------------------------
1093
1094 class KeyFrameHandleUndo final : public TUndo {
1095 TStageObjectId m_objId;
1096 int m_row;
1097 TStageObject::Keyframe m_oldKeyframe, m_newKeyframe;
1098
1099 public:
KeyFrameHandleUndo(TStageObjectId id,int row)1100 KeyFrameHandleUndo(TStageObjectId id, int row) : m_objId(id), m_row(row) {
1101 TStageObject *pegbar =
1102 TApp::instance()->getCurrentXsheet()->getXsheet()->getStageObject(
1103 m_objId);
1104 assert(pegbar);
1105 m_oldKeyframe = pegbar->getKeyframe(m_row);
1106 }
1107
onAdd()1108 void onAdd() override {
1109 TStageObject *pegbar =
1110 TApp::instance()->getCurrentXsheet()->getXsheet()->getStageObject(
1111 m_objId);
1112 assert(pegbar);
1113 m_newKeyframe = pegbar->getKeyframe(m_row);
1114 }
1115
setKeyframe(const TStageObject::Keyframe & k) const1116 void setKeyframe(const TStageObject::Keyframe &k) const {
1117 TStageObject *pegbar =
1118 TApp::instance()->getCurrentXsheet()->getXsheet()->getStageObject(
1119 m_objId);
1120 assert(pegbar);
1121 pegbar->setKeyframeWithoutUndo(m_row, k);
1122 TApp::instance()->getCurrentObject()->notifyObjectIdChanged(false);
1123 }
1124
undo() const1125 void undo() const override { setKeyframe(m_oldKeyframe); }
redo() const1126 void redo() const override { setKeyframe(m_newKeyframe); }
getSize() const1127 int getSize() const override { return sizeof *this; }
1128
getHistoryString()1129 QString getHistoryString() override {
1130 return QObject::tr("Move keyframe handle : %1 Handle of the keyframe %2")
1131 .arg(QString::fromStdString(m_objId.toString()))
1132 .arg(QString::number(m_row + 1));
1133 }
1134
getHistoryType()1135 int getHistoryType() override { return HistoryType::Xsheet; }
1136 };
1137
1138 //=============================================================================
1139 // KeyFrameHandleMoverTool
1140 //-----------------------------------------------------------------------------
1141
1142 class KeyFrameHandleMoverTool final : public XsheetGUI::DragTool {
1143 public:
1144 enum HandleType { EaseOut = 0, EaseIn = 1 };
1145
1146 private:
1147 TStageObject *m_stageObject;
1148 TStageObject::Keyframe m_k;
1149 int m_startRow; // E' la riga corrispondente alla key della handle corrente!
1150 HandleType m_handleType;
1151 KeyFrameHandleUndo *m_undo;
1152 int m_r0, m_r1; // m_r0 e' la riga in cui si clicca, m_r1 e' la riga che
1153 // varia nel drag
1154 bool m_enable;
1155
1156 public:
KeyFrameHandleMoverTool(XsheetViewer * viewer,HandleType handleType,int keyRow)1157 KeyFrameHandleMoverTool(XsheetViewer *viewer, HandleType handleType,
1158 int keyRow)
1159 : XsheetGUI::DragTool(viewer)
1160 , m_stageObject(0)
1161 , m_handleType(handleType)
1162 , m_startRow(keyRow)
1163 , m_r0(0)
1164 , m_r1(0)
1165 , m_enable(true) {}
1166
onClick(const CellPosition & pos)1167 void onClick(const CellPosition &pos) override {
1168 int row = pos.frame(), col = pos.layer();
1169 m_r0 = m_r1 = row;
1170 TXsheet *xsh = getViewer()->getXsheet();
1171 TStageObjectId cameraId =
1172 TStageObjectId::CameraId(xsh->getCameraColumnIndex());
1173
1174 TStageObjectId objId = col >= 0 ? TStageObjectId::ColumnId(col) : cameraId;
1175 if (xsh->getColumn(col) && xsh->getColumn(col)->isLocked()) {
1176 m_enable = false;
1177 return;
1178 }
1179 m_stageObject = xsh->getStageObject(objId);
1180 assert(m_stageObject);
1181 m_k = m_stageObject->getKeyframe(m_startRow);
1182 m_undo = new KeyFrameHandleUndo(objId, m_startRow);
1183 }
1184
onDrag(const CellPosition & pos)1185 void onDrag(const CellPosition &pos) override {
1186 int row = pos.frame(), col = pos.layer();
1187 if (!m_enable) return;
1188 m_r1 = row;
1189 onCellChange(row, col);
1190 TApp::instance()->getCurrentObject()->notifyObjectIdChanged(true);
1191 refreshCellsArea();
1192 }
1193
onCellChange(int row,int col)1194 void onCellChange(int row, int col) {
1195 int dr = row - m_startRow;
1196 if (m_handleType == EaseOut) {
1197 if (dr <= 0) dr = 0;
1198 if (m_k.m_easeOut == dr) return;
1199 m_k.m_easeOut = dr;
1200 } else {
1201 if (dr >= 0) dr = 0;
1202 if (m_k.m_easeIn == dr) return;
1203 m_k.m_easeIn = -dr;
1204 }
1205 m_stageObject->setKeyframeWithoutUndo(m_startRow, m_k);
1206 }
1207
onRelease(const CellPosition & pos)1208 void onRelease(const CellPosition &pos) override {
1209 int row = pos.frame(), col = pos.layer();
1210 if (!m_enable) return;
1211 if (m_r0 == m_r1)
1212 delete m_undo;
1213 else {
1214 TUndoManager::manager()->add(m_undo);
1215 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1216 }
1217 }
1218 };
1219
1220 //-----------------------------------------------------------------------------
1221 } // namespace
1222 //-----------------------------------------------------------------------------
1223
makeKeyFrameHandleMoverTool(XsheetViewer * viewer,bool isEaseOut,int keyRow)1224 XsheetGUI::DragTool *XsheetGUI::DragTool::makeKeyFrameHandleMoverTool(
1225 XsheetViewer *viewer, bool isEaseOut, int keyRow) {
1226 return new KeyFrameHandleMoverTool(viewer,
1227 isEaseOut
1228 ? KeyFrameHandleMoverTool::EaseOut
1229 : KeyFrameHandleMoverTool::EaseIn,
1230 keyRow);
1231 }
1232
1233 //=============================================================================
1234 // OnionSkinMaskModifier tool
1235 //-----------------------------------------------------------------------------
1236
1237 namespace {
1238
1239 //=============================================================================
1240 // OnionSkinMaskModifierTool
1241 //-----------------------------------------------------------------------------
1242
1243 class NoteMoveTool final : public XsheetGUI::DragTool {
1244 TPointD m_startPos, m_delta;
1245 int m_startRow, m_startCol;
1246
1247 public:
NoteMoveTool(XsheetViewer * viewer)1248 NoteMoveTool(XsheetViewer *viewer) : DragTool(viewer) {}
1249
onClick(const QMouseEvent * e)1250 void onClick(const QMouseEvent *e) override {
1251 TXshNoteSet *notes = getViewer()->getXsheet()->getNotes();
1252 int currentIndex = getViewer()->getCurrentNoteIndex();
1253 m_startPos = notes->getNotePos(currentIndex);
1254 m_startRow = notes->getNoteRow(currentIndex);
1255 m_startCol = notes->getNoteCol(currentIndex);
1256 QPoint p = e->pos();
1257 TPointD mousePos(p.x(), p.y());
1258 QPoint xy = getViewer()->positionToXY(CellPosition(m_startRow, m_startCol));
1259 TPointD cellTopLeft(xy.x(), xy.y());
1260 m_delta = mousePos - (cellTopLeft + m_startPos);
1261 }
1262
onChange(TPointD pos)1263 void onChange(TPointD pos) {
1264 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1265 pos = pos - m_delta;
1266 CellPosition cellPosition = getViewer()->xyToPosition(pos);
1267 int row = cellPosition.frame();
1268 int col = cellPosition.layer();
1269
1270 if (row < 0) row = 0;
1271 if (col < 0 || (!getViewer()->orientation()->isVerticalTimeline() &&
1272 (col == 0 || col >= xsh->getColumnCount())))
1273 return;
1274
1275 QPoint xy = getViewer()->positionToXY(CellPosition(row, col));
1276 TPointD newPos = pos - TPointD(xy.x(), xy.y());
1277
1278 if (newPos.x < 0) newPos.x = 0;
1279 if (newPos.y < 0) newPos.y = 0;
1280
1281 TXshNoteSet *notes = getViewer()->getXsheet()->getNotes();
1282 int currentIndex = getViewer()->getCurrentNoteIndex();
1283 notes->setNoteRow(currentIndex, row);
1284 notes->setNoteCol(currentIndex, col);
1285 notes->setNotePos(currentIndex, newPos);
1286 }
1287
onDrag(const QMouseEvent * event)1288 void onDrag(const QMouseEvent *event) override {
1289 QPoint p = event->pos();
1290 onChange(TPointD(p.x(), p.y()));
1291 refreshCellsArea();
1292 }
1293
onRelease(const QMouseEvent * event)1294 void onRelease(const QMouseEvent *event) override {
1295 QPoint p = event->pos();
1296 onChange(TPointD(p.x(), p.y()));
1297
1298 TXshNoteSet *notes = getViewer()->getXsheet()->getNotes();
1299 int currentIndex = getViewer()->getCurrentNoteIndex();
1300 TPointD pos = notes->getNotePos(currentIndex);
1301 int row = notes->getNoteRow(currentIndex);
1302 int col = notes->getNoteCol(currentIndex);
1303 if (m_startPos == pos && m_startRow == row && m_startCol == col) return;
1304
1305 refreshCellsArea();
1306 }
1307 };
1308
1309 //-----------------------------------------------------------------------------
1310 } // namespace
1311 //-----------------------------------------------------------------------------
1312
makeNoteMoveTool(XsheetViewer * viewer)1313 XsheetGUI::DragTool *XsheetGUI::DragTool::makeNoteMoveTool(
1314 XsheetViewer *viewer) {
1315 return new NoteMoveTool(viewer);
1316 }
1317
1318 //=============================================================================
1319 // OnionSkinMaskModifier tool
1320 //-----------------------------------------------------------------------------
1321
1322 namespace {
1323
1324 //=============================================================================
1325 // OnionSkinMaskModifierTool
1326 //-----------------------------------------------------------------------------
1327
1328 class OnionSkinMaskModifierTool final : public XsheetGUI::DragTool {
1329 OnionSkinMaskModifier m_modifier;
1330 bool m_isFos;
1331
1332 public:
OnionSkinMaskModifierTool(XsheetViewer * viewer,bool isFos)1333 OnionSkinMaskModifierTool(XsheetViewer *viewer, bool isFos)
1334 : DragTool(viewer)
1335 , m_modifier(TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask(),
1336 TApp::instance()->getCurrentFrame()->getFrame())
1337 , m_isFos(isFos) {}
1338
onClick(const CellPosition & pos)1339 void onClick(const CellPosition &pos) override {
1340 int row = pos.frame();
1341 OnionSkinMask mask = m_modifier.getMask();
1342 TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(mask);
1343 m_modifier.click(row, m_isFos);
1344 TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
1345 }
1346
onDrag(const CellPosition & pos)1347 void onDrag(const CellPosition &pos) override {
1348 int row = pos.frame();
1349 if (row < 0) row = 0;
1350 onRowChange(row);
1351 TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
1352 }
1353
onRowChange(int row)1354 void onRowChange(int row) {
1355 m_modifier.drag(row);
1356 OnionSkinMask mask = m_modifier.getMask();
1357 TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(mask);
1358 }
1359
onRelease(const CellPosition & pos)1360 void onRelease(const CellPosition &pos) override {
1361 int row = pos.frame();
1362 m_modifier.release(row);
1363 OnionSkinMask mask = m_modifier.getMask();
1364 TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(mask);
1365 TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
1366 }
1367 };
1368
1369 //-----------------------------------------------------------------------------
1370 } // namespace
1371 //-----------------------------------------------------------------------------
1372
makeKeyOnionSkinMaskModifierTool(XsheetViewer * viewer,bool isFos)1373 XsheetGUI::DragTool *XsheetGUI::DragTool::makeKeyOnionSkinMaskModifierTool(
1374 XsheetViewer *viewer, bool isFos) {
1375 return new OnionSkinMaskModifierTool(viewer, isFos);
1376 }
1377
1378 //=============================================================================
1379 // CurrentFrameModifier tool
1380 //-----------------------------------------------------------------------------
1381
1382 namespace {
1383
1384 //=============================================================================
1385 // CurrentFrameModifier
1386 //-----------------------------------------------------------------------------
1387
1388 class CurrentFrameModifier final : public XsheetGUI::DragTool {
1389 public:
CurrentFrameModifier(XsheetViewer * viewer)1390 CurrentFrameModifier(XsheetViewer *viewer) : DragTool(viewer) {}
1391
onClick(const CellPosition & pos)1392 void onClick(const CellPosition &pos) override {
1393 int row = pos.frame();
1394 TApp::instance()->getCurrentFrame()->setFrame(row);
1395 refreshRowsArea();
1396 }
1397
onDrag(const CellPosition & pos)1398 void onDrag(const CellPosition &pos) override {
1399 int row = pos.frame();
1400 if (row < 0) row = 0;
1401 int lastRow = TApp::instance()->getCurrentFrame()->getFrameIndex();
1402 if (lastRow == row) return;
1403 onRowChange(row);
1404 refreshRowsArea();
1405 }
1406
onRowChange(int row)1407 void onRowChange(int row) {
1408 TApp *app = TApp::instance();
1409 app->getCurrentFrame()->setFrame(row);
1410 int columnIndex = app->getCurrentColumn()->getColumnIndex();
1411 app->getCurrentFrame()->scrubXsheet(row, row, getViewer()->getXsheet());
1412 }
1413
onRelease(const CellPosition & pos)1414 void onRelease(const CellPosition &pos) override {
1415 getViewer()->getXsheet()->stopScrub();
1416 }
1417 };
1418
1419 //-----------------------------------------------------------------------------
1420 } // namespace
1421 //-----------------------------------------------------------------------------
1422
makeCurrentFrameModifierTool(XsheetViewer * viewer)1423 XsheetGUI::DragTool *XsheetGUI::DragTool::makeCurrentFrameModifierTool(
1424 XsheetViewer *viewer) {
1425 return new CurrentFrameModifier(viewer);
1426 }
1427
1428 //=============================================================================
1429 // PlayRangeModifier tool
1430 //-----------------------------------------------------------------------------
1431
1432 namespace {
1433
1434 //=============================================================================
1435 // PlayRangeModifier
1436 //-----------------------------------------------------------------------------
1437
1438 class PlayRangeModifier final : public XsheetGUI::DragTool {
1439 bool m_movingFirst;
1440 int m_oldR0, m_oldR1, m_oldStep;
1441
1442 public:
PlayRangeModifier(XsheetViewer * viewer)1443 PlayRangeModifier(XsheetViewer *viewer)
1444 : DragTool(viewer), m_movingFirst(false) {}
1445
onClick(const CellPosition & pos)1446 void onClick(const CellPosition &pos) override {
1447 int row = pos.frame();
1448 XsheetGUI::getPlayRange(m_oldR0, m_oldR1, m_oldStep);
1449 assert(m_oldR0 == row || m_oldR1 == row);
1450 m_movingFirst = m_oldR0 == row;
1451 refreshRowsArea();
1452 }
1453
onDrag(const CellPosition & pos)1454 void onDrag(const CellPosition &pos) override {
1455 int row = pos.frame();
1456 if (row < 0) row = 0;
1457 onRowChange(row);
1458 refreshRowsArea();
1459 }
1460
onRowChange(int row)1461 void onRowChange(int row) {
1462 if (row < 0) return;
1463 int r0, r1, step;
1464 XsheetGUI::getPlayRange(r0, r1, step);
1465 if (m_movingFirst) {
1466 if (row <= r1)
1467 r0 = row;
1468 else if (row > r1) {
1469 r0 = (r1 > 0) ? r1 : 0;
1470 r1 = row;
1471 m_movingFirst = false;
1472 }
1473 } else {
1474 if (row >= r0)
1475 r1 = row;
1476 else if (row < r0) {
1477 r1 = r0;
1478 r0 = row;
1479 m_movingFirst = true;
1480 }
1481 }
1482 XsheetGUI::setPlayRange(r0, r1, step, false);
1483 }
1484
onRelease(const CellPosition & pos)1485 void onRelease(const CellPosition &pos) override {
1486 int row = pos.frame();
1487 int newR0, newR1, newStep;
1488 XsheetGUI::getPlayRange(newR0, newR1, newStep);
1489 if (m_oldR0 != newR0 || m_oldR1 != newR1) {
1490 TUndoManager::manager()->add(new UndoPlayRangeModifier(
1491 m_oldR0, newR0, m_oldR1, newR1, m_oldStep, newStep, true, true));
1492 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1493 }
1494 refreshRowsArea();
1495 }
1496 };
1497
1498 //-----------------------------------------------------------------------------
1499 } // namespace
1500 //-----------------------------------------------------------------------------
1501
makePlayRangeModifierTool(XsheetViewer * viewer)1502 XsheetGUI::DragTool *XsheetGUI::DragTool::makePlayRangeModifierTool(
1503 XsheetViewer *viewer) {
1504 return new PlayRangeModifier(viewer);
1505 }
1506
1507 //=============================================================================
1508 // ColumnSelectionTool
1509 //-----------------------------------------------------------------------------
1510
1511 namespace {
1512
1513 class ColumnSelectionTool final : public XsheetGUI::DragTool {
1514 int m_firstColumn;
1515 bool m_enabled;
1516
1517 public:
ColumnSelectionTool(XsheetViewer * viewer)1518 ColumnSelectionTool(XsheetViewer *viewer)
1519 : DragTool(viewer), m_firstColumn(-1), m_enabled(false) {}
1520
onClick(const QMouseEvent * event)1521 void onClick(const QMouseEvent *event) override {
1522 TColumnSelection *selection = getViewer()->getColumnSelection();
1523 CellPosition cellPosition = getViewer()->xyToPosition(event->pos());
1524 int col = cellPosition.layer();
1525 m_firstColumn = col;
1526 bool isSelected = selection->isColumnSelected(col);
1527 if (event->modifiers() & Qt::ControlModifier) {
1528 selection->selectColumn(col, !isSelected);
1529 } else if (event->modifiers() & Qt::ShiftModifier) {
1530 // m_enabled = true;
1531 if (isSelected) return;
1532 int ia = col, ib = col;
1533 int columnCount = getViewer()->getXsheet()->getColumnCount();
1534 while (ia > 0 && !selection->isColumnSelected(ia - 1)) --ia;
1535 if (ia == 0) ia = col;
1536 while (ib < columnCount - 1 && !selection->isColumnSelected(ib + 1)) ++ib;
1537 if (ib == columnCount - 1) ib = col;
1538 int i;
1539 for (i = ia; i <= ib; i++) selection->selectColumn(i, true);
1540 } else {
1541 m_enabled = true;
1542 selection->selectNone();
1543 selection->selectColumn(col, true);
1544 }
1545 selection->makeCurrent();
1546 getViewer()->update();
1547 }
1548
onDrag(const CellPosition & pos)1549 void onDrag(const CellPosition &pos) override {
1550 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1551 int col = pos.layer();
1552 if (!m_enabled) return;
1553 int firstCol =
1554 Preferences::instance()->isXsheetCameraColumnVisible() ? -1 : 0;
1555 if (col < firstCol || (!getViewer()->orientation()->isVerticalTimeline() &&
1556 col >= xsh->getColumnCount()))
1557 return;
1558 TColumnSelection *selection = getViewer()->getColumnSelection();
1559 selection->selectNone();
1560 int i, ia = m_firstColumn, ib = col;
1561 if (ia > ib) std::swap(ia, ib);
1562 for (i = ia; i <= ib; i++) selection->selectColumn(i, true);
1563 getViewer()->update();
1564 refreshCellsArea();
1565 return;
1566 }
onRelease(const CellPosition & pos)1567 void onRelease(const CellPosition &pos) override {
1568 TSelectionHandle::getCurrent()->notifySelectionChanged();
1569 }
1570 };
1571
1572 //-----------------------------------------------------------------------------
1573 } // namespace
1574 //-----------------------------------------------------------------------------
1575
makeColumnSelectionTool(XsheetViewer * viewer)1576 XsheetGUI::DragTool *XsheetGUI::DragTool::makeColumnSelectionTool(
1577 XsheetViewer *viewer) {
1578 return new ColumnSelectionTool(viewer);
1579 }
1580
1581 //=============================================================================
1582 // Column Movement
1583 //-----------------------------------------------------------------------------
1584
moveColumns(const std::set<int> & indices,int delta)1585 static void moveColumns(const std::set<int> &indices, int delta) {
1586 if (indices.empty()) return;
1587 if (delta < 0 && *indices.begin() + delta < 0) delta = -*indices.begin();
1588 if (delta == 0) return;
1589
1590 TApp *app = TApp::instance();
1591 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1592 std::vector<int> ii;
1593 if (delta > 0)
1594 ii.assign(indices.rbegin(), indices.rend());
1595 else
1596 ii.assign(indices.begin(), indices.end());
1597 int i, m = ii.size();
1598 for (i = 0; i < m; i++) {
1599 int a = ii[i];
1600 int b = a + delta;
1601 xsh->moveColumn(a, b);
1602 }
1603 int col = app->getCurrentColumn()->getColumnIndex();
1604 if (indices.count(col) > 0)
1605 app->getCurrentColumn()->setColumnIndex(col + delta);
1606 }
1607
1608 //-----------------------------------------------------------------------------
1609
1610 class ColumnMoveUndo final : public TUndo {
1611 std::set<int> m_indices;
1612 int m_delta;
1613
1614 public:
1615 // nota: indices sono gli indici DOPO aver fatto il movimento
ColumnMoveUndo(const std::set<int> & indices,int delta)1616 ColumnMoveUndo(const std::set<int> &indices, int delta)
1617 : m_indices(indices), m_delta(delta) {
1618 assert(delta != 0);
1619 assert(!indices.empty());
1620 assert(*indices.begin() >= 0);
1621 assert(delta < 0 || *indices.begin() - delta >= 0);
1622 }
undo() const1623 void undo() const override {
1624 moveColumns(m_indices, -m_delta);
1625 TSelection *selection =
1626 TApp::instance()->getCurrentSelection()->getSelection();
1627 if (selection) selection->selectNone();
1628 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1629 }
redo() const1630 void redo() const override {
1631 std::set<int> ii;
1632 for (std::set<int>::const_iterator it = m_indices.begin();
1633 it != m_indices.end(); ++it)
1634 ii.insert(*it - m_delta);
1635 moveColumns(ii, m_delta);
1636 TSelection *selection =
1637 TApp::instance()->getCurrentSelection()->getSelection();
1638 if (selection) selection->selectNone();
1639 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1640 }
getSize() const1641 int getSize() const override {
1642 return sizeof(*this) + m_indices.size() * sizeof(int);
1643 }
1644
getHistoryString()1645 QString getHistoryString() override { return QObject::tr("Move Columns"); }
getHistoryType()1646 int getHistoryType() override { return HistoryType::Xsheet; }
1647 };
1648
1649 //-----------------------------------------------------------------------------
1650
1651 class ColumnMoveDragTool final : public XsheetGUI::DragTool {
1652 int m_offset, m_firstCol, m_lastCol, m_origOffset;
1653
1654 public:
ColumnMoveDragTool(XsheetViewer * viewer)1655 ColumnMoveDragTool(XsheetViewer *viewer)
1656 : XsheetGUI::DragTool(viewer)
1657 , m_firstCol(-1)
1658 , m_lastCol(-1)
1659 , m_offset(0)
1660 , m_origOffset(0) {}
1661
onClick(const CellPosition & pos)1662 void onClick(const CellPosition &pos) override {
1663 int col = pos.layer();
1664 TColumnSelection *selection = getViewer()->getColumnSelection();
1665 if (!selection->isColumnSelected(col)) {
1666 selection->selectNone();
1667 selection->selectColumn(col);
1668 selection->makeCurrent();
1669 }
1670 std::set<int> indices = selection->getIndices();
1671 if (indices.empty()) return;
1672 m_firstCol = m_lastCol = *indices.begin();
1673 assert(m_firstCol >= 0);
1674 m_origOffset = m_offset = m_firstCol - col;
1675 assert(m_lastCol == *indices.begin());
1676 getViewer()->update();
1677
1678 if (!getViewer()->orientation()->isVerticalTimeline())
1679 TUndoManager::manager()->beginBlock();
1680 }
onDrag(const CellPosition & pos)1681 void onDrag(const CellPosition &pos) override {
1682 int col = pos.layer();
1683 TColumnSelection *selection = getViewer()->getColumnSelection();
1684 TApp *app = TApp::instance();
1685 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1686
1687 std::set<int> indices = selection->getIndices();
1688 indices.erase(-1); // Ignore camera column
1689 if (indices.empty()) return;
1690
1691 assert(m_lastCol == *indices.begin());
1692
1693 int currEnd = xsh->getColumnCount() - 1;
1694 int origCol = col;
1695 if (col < 0)
1696 col = 0;
1697 else if (!getViewer()->orientation()->isVerticalTimeline() && col > currEnd)
1698 col = currEnd;
1699 int dCol = col - (m_lastCol - m_offset);
1700
1701 // ignore if the cursor moves in the drag-starting column
1702 if (dCol == 0) return;
1703
1704 if (dCol < 0 &&
1705 !xsh->getColumnFan(getViewer()->orientation())->isActive(col)) {
1706 while (
1707 col != 0 &&
1708 !xsh->getColumnFan(getViewer()->orientation())->isActive(col - 1)) {
1709 col--;
1710 dCol--;
1711 }
1712 }
1713
1714 int newBegin = *indices.begin() + dCol;
1715 int newEnd = *indices.rbegin() + dCol;
1716
1717 if (newBegin < 0)
1718 dCol -= newBegin;
1719 else if (!getViewer()->orientation()->isVerticalTimeline() &&
1720 newEnd > currEnd)
1721 dCol -= (newEnd - currEnd);
1722
1723 // ignore if the dragged columns comes up against the end of column stack
1724 if (dCol == 0) return;
1725
1726 m_lastCol += dCol;
1727
1728 assert(*indices.begin() + dCol >= 0);
1729
1730 moveColumns(indices, dCol);
1731
1732 selection->selectNone();
1733 for (std::set<int>::iterator it = indices.begin(); it != indices.end();
1734 ++it)
1735 selection->selectColumn(*it + dCol, true);
1736 }
onRelease(const CellPosition & pos)1737 void onRelease(const CellPosition &pos) override {
1738 int delta = m_lastCol - m_firstCol;
1739 if (delta != 0) {
1740 TColumnSelection *selection = getViewer()->getColumnSelection();
1741 std::set<int> indices = selection->getIndices();
1742 if (!indices.empty()) {
1743 TUndoManager::manager()->add(new ColumnMoveUndo(indices, delta));
1744 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1745 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1746 }
1747 }
1748
1749 if (!getViewer()->orientation()->isVerticalTimeline())
1750 TUndoManager::manager()->endBlock();
1751 }
1752 };
1753
makeColumnMoveTool(XsheetViewer * viewer)1754 XsheetGUI::DragTool *XsheetGUI::DragTool::makeColumnMoveTool(
1755 XsheetViewer *viewer) {
1756 return new ColumnMoveDragTool(viewer);
1757 }
1758
1759 //=============================================================================
1760 // Parent Change
1761 //-----------------------------------------------------------------------------
1762
1763 class ChangePegbarParentUndo final : public TUndo {
1764 TStageObjectId m_oldParentId;
1765 TStageObjectId m_newParentId;
1766 TStageObjectId m_child;
1767
1768 public:
ChangePegbarParentUndo(const TStageObjectId & child,const TStageObjectId & oldParentId,const TStageObjectId & newParentId)1769 ChangePegbarParentUndo(const TStageObjectId &child,
1770 const TStageObjectId &oldParentId,
1771 const TStageObjectId &newParentId)
1772 : m_oldParentId(oldParentId)
1773 , m_newParentId(newParentId)
1774 , m_child(child) {}
1775
undo() const1776 void undo() const override {
1777 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1778 xsh->setStageObjectParent(m_child, m_oldParentId);
1779 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1780 }
1781
redo() const1782 void redo() const override {
1783 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1784 xsh->setStageObjectParent(m_child, m_newParentId);
1785 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1786 }
1787
getSize() const1788 int getSize() const override { return sizeof(*this); }
1789
getHistoryString()1790 QString getHistoryString() override { return QObject::tr("Change Pegbar"); }
getHistoryType()1791 int getHistoryType() override { return HistoryType::Xsheet; }
1792 };
1793
1794 //-----------------------------------------------------------------------------
1795
1796 class ChangePegbarParentDragTool final : public XsheetGUI::DragTool {
1797 int m_firstCol, m_lastCol;
1798
1799 public:
ChangePegbarParentDragTool(XsheetViewer * viewer)1800 ChangePegbarParentDragTool(XsheetViewer *viewer)
1801 : XsheetGUI::DragTool(viewer), m_firstCol(-1), m_lastCol(-1) {}
1802
onClick(const CellPosition & pos)1803 void onClick(const CellPosition &pos) override {
1804 m_firstCol = m_lastCol = pos.layer();
1805 }
onDrag(const CellPosition & pos)1806 void onDrag(const CellPosition &pos) override { m_lastCol = pos.layer(); }
onRelease(const CellPosition & pos)1807 void onRelease(const CellPosition &pos) override {
1808 // TUndoManager::manager()->add(new ColumnMoveUndo(indices, delta));
1809 TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1810 TStageObjectId columnId(getViewer()->getObjectId(m_firstCol));
1811 if (m_firstCol == -1)
1812 columnId = TStageObjectId::CameraId(
1813 getViewer()->getXsheet()->getCameraColumnIndex());
1814 TStageObjectId parentId(getViewer()->getObjectId(m_lastCol));
1815 if (m_lastCol == -1)
1816 parentId = TStageObjectId::CameraId(
1817 getViewer()->getXsheet()->getCameraColumnIndex());
1818 if (getViewer()->getXsheet()->getColumn(m_lastCol) &&
1819 getViewer()->getXsheet()->getColumn(m_lastCol)->getSoundColumn())
1820 return;
1821
1822 if (m_firstCol == m_lastCol && m_firstCol != -1) {
1823 // vuol dire che la colonna torna al suo padre di default
1824 // la prima colonna e' attaccata alla pegbar 0 le altre alla pegbar 1.
1825 // brutto che questa cosa sia qui. Bisognerebbe spostarlo altrove (in
1826 // tnzlib)
1827 parentId = TStageObjectId::TableId;
1828 }
1829 if (m_firstCol == -1 && m_lastCol <= -1) {
1830 parentId = TStageObjectId::NoneId;
1831 }
1832
1833 if (parentId == xsh->getStageObjectParent(columnId)) return;
1834 TUndoManager::manager()->add(new ChangePegbarParentUndo(
1835 columnId, xsh->getStageObjectParent(columnId), parentId));
1836 TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1837
1838 xsh->setStageObjectParent(columnId, parentId);
1839 getViewer()->update();
1840 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1841 }
1842 };
1843
1844 //-----------------------------------------------------------------------------
1845
makeColumnLinkTool(XsheetViewer * viewer)1846 XsheetGUI::DragTool *XsheetGUI::DragTool::makeColumnLinkTool(
1847 XsheetViewer *viewer) {
1848 return new ChangePegbarParentDragTool(viewer);
1849 }
1850
1851 //=============================================================================
1852 // Volume adjust
1853 //-----------------------------------------------------------------------------
1854
1855 namespace {
1856
1857 class VolumeDragTool final : public XsheetGUI::DragTool {
1858 int m_index;
1859 bool m_enabled;
1860
1861 public:
VolumeDragTool(XsheetViewer * viewer)1862 VolumeDragTool(XsheetViewer *viewer)
1863 : DragTool(viewer)
1864 , m_index(TApp::instance()->getCurrentColumn()->getColumnIndex())
1865 , m_enabled(false) {
1866 TXshColumn *column = viewer->getXsheet()->getColumn(m_index);
1867 m_enabled = column != 0 && !column->isLocked();
1868 TXshSoundColumn *soundColumn = column->getSoundColumn();
1869 assert(soundColumn);
1870 if (soundColumn->isPlaying()) {
1871 viewer->update();
1872 // soundColumn->stop();
1873 }
1874 }
1875
onClick(const QMouseEvent *)1876 void onClick(const QMouseEvent *) override {}
1877
onDrag(const QMouseEvent * event)1878 void onDrag(const QMouseEvent *event) override {
1879 if (!m_enabled) return;
1880
1881 const Orientation *o = getViewer()->orientation();
1882 QRect track = o->rect(PredefinedRect::VOLUME_TRACK);
1883 NumberRange range = o->frameSide(track);
1884 int frameAxis = o->frameAxis(event->pos());
1885 if (o->isVerticalTimeline() &&
1886 !o->flag(PredefinedFlag::VOLUME_AREA_VERTICAL)) {
1887 range = o->layerSide(track);
1888 frameAxis =
1889 o->layerAxis(event->pos()) - getViewer()->columnToLayerAxis(m_index);
1890 }
1891
1892 double v = range.ratio(frameAxis);
1893 if (o->flag(PredefinedFlag::VOLUME_AREA_VERTICAL)) v = 1 - v;
1894
1895 TXsheet *xsh = getViewer()->getXsheet();
1896 TXshColumn *column = xsh->getColumn(m_index);
1897 if (!column) return;
1898 TXshSoundColumn *soundColumn = column->getSoundColumn();
1899 if (!soundColumn) return;
1900 soundColumn->setVolume(v);
1901
1902 getViewer()->update();
1903 }
1904
onRelease(const QMouseEvent *)1905 void onRelease(const QMouseEvent *) override {
1906 TApp::instance()->getCurrentXsheet()->notifyXsheetSoundChanged();
1907 }
1908 };
1909
1910 //-----------------------------------------------------------------------------
1911 } // namespace
1912 //-----------------------------------------------------------------------------
1913
makeVolumeDragTool(XsheetViewer * viewer)1914 XsheetGUI::DragTool *XsheetGUI::DragTool::makeVolumeDragTool(
1915 XsheetViewer *viewer) {
1916 return new VolumeDragTool(viewer);
1917 }
1918
1919 //=============================================================================
1920 // SoundScrub tool
1921 //-----------------------------------------------------------------------------
1922
1923 namespace {
1924
1925 class SoundScrubTool final : public XsheetGUI::DragTool {
1926 TXshSoundColumn *m_soundColumn;
1927 int m_startRow;
1928 std::pair<int, int> m_playRange;
1929 int m_row, m_oldRow;
1930 int m_timerId;
1931
1932 public:
SoundScrubTool(XsheetViewer * viewer,TXshSoundColumn * sc)1933 SoundScrubTool(XsheetViewer *viewer, TXshSoundColumn *sc)
1934 : DragTool(viewer)
1935 , m_soundColumn(sc)
1936 , m_startRow(-1)
1937 , m_playRange(0, -1)
1938 , m_row(0)
1939 , m_oldRow(0)
1940 , m_timerId(0) {}
1941
onClick(const CellPosition & pos)1942 void onClick(const CellPosition &pos) override {
1943 int row = pos.frame(), col = pos.layer();
1944 TColumnSelection *selection = getViewer()->getColumnSelection();
1945 selection->selectNone();
1946 m_startRow = row;
1947 getViewer()->setScrubHighlight(row, m_startRow, col);
1948 getViewer()->updateCells();
1949 }
1950
onDrag(const CellPosition & pos)1951 void onDrag(const CellPosition &pos) override {
1952 int row = pos.frame(), col = pos.layer();
1953 onCellChange(row, col);
1954 }
1955
onCellChange(int row,int col)1956 void onCellChange(int row, int col) {
1957 assert(m_startRow >= 0);
1958 getViewer()->setScrubHighlight(row, m_startRow, col);
1959 getViewer()->updateCells();
1960 }
1961
onRelease(const CellPosition & pos)1962 void onRelease(const CellPosition &pos) override {
1963 int r0 = std::min(pos.frame(), m_startRow);
1964 int r1 = std::max(pos.frame(), m_startRow);
1965 assert(m_soundColumn);
1966 TApp *app = TApp::instance();
1967 ToonzScene *scene = app->getCurrentScene()->getScene();
1968 double fps = scene->getProperties()->getOutputProperties()->getFrameRate();
1969 app->getCurrentFrame()->scrubColumn(r0, r1, m_soundColumn, fps);
1970 }
1971 };
1972
1973 //-----------------------------------------------------------------------------
1974 } // namespace
1975 //-----------------------------------------------------------------------------
1976
makeSoundScrubTool(XsheetViewer * viewer,TXshSoundColumn * sc)1977 XsheetGUI::DragTool *XsheetGUI::DragTool::makeSoundScrubTool(
1978 XsheetViewer *viewer, TXshSoundColumn *sc) {
1979 return new SoundScrubTool(viewer, sc);
1980 }
1981
1982 //=============================================================================
1983 // DataDragTool
1984 //-----------------------------------------------------------------------------
1985
1986 namespace {
1987
1988 //=============================================================================
1989 // DragAndDropData
1990 //-----------------------------------------------------------------------------
1991
1992 class DragAndDropData {
1993 public:
1994 std::vector<std::pair<TXshSimpleLevelP, std::vector<TFrameId>>> m_levels;
1995 std::vector<TFilePath> m_paths;
1996
DragAndDropData()1997 DragAndDropData() {}
1998
addSimpleLevel(std::pair<TXshSimpleLevelP,std::vector<TFrameId>> level)1999 void addSimpleLevel(
2000 std::pair<TXshSimpleLevelP, std::vector<TFrameId>> level) {
2001 m_levels.push_back(level);
2002 }
setSimpleLevels(std::vector<std::pair<TXshSimpleLevelP,std::vector<TFrameId>>> levels)2003 void setSimpleLevels(
2004 std::vector<std::pair<TXshSimpleLevelP, std::vector<TFrameId>>> levels) {
2005 m_levels = levels;
2006 }
2007
getLevelFrameRect(bool isVertical)2008 TRect getLevelFrameRect(bool isVertical) {
2009 if (!m_paths.empty()) return TRect();
2010 int maxRow = 0;
2011 int columnCount = m_levels.size();
2012 int i;
2013 for (i = 0; i < columnCount; i++) {
2014 std::vector<TFrameId> fids = m_levels[i].second;
2015 int size = fids.size();
2016 if (maxRow < size) maxRow = size;
2017 }
2018 if (!isVertical) return TRect(0, 0, maxRow - 1, columnCount - 1);
2019
2020 return TRect(0, 0, columnCount - 1, maxRow - 1);
2021 }
2022
addPath(TFilePath path)2023 void addPath(TFilePath path) { m_paths.push_back(path); }
setLevelPath(std::vector<TFilePath> paths)2024 void setLevelPath(std::vector<TFilePath> paths) { m_paths = paths; }
2025 };
2026
2027 //=============================================================================
2028 // DataDragTool
2029 //-----------------------------------------------------------------------------
2030
2031 enum CellMovementType { NO_MOVEMENT, INSERT_CELLS, OVERWRITE_CELLS };
2032
2033 class DataDragTool final : public XsheetGUI::DragTool {
2034 DragAndDropData *m_data;
2035 bool m_valid;
2036 TPoint m_curPos; // screen xy of drag begin
2037 CellMovementType m_type;
2038
2039 protected:
canChange(int row,int col)2040 bool canChange(int row, int col) {
2041 int c = col;
2042 int r = row;
2043 TXsheet *xsh = getViewer()->getXsheet();
2044 TRect rect = m_data->getLevelFrameRect(
2045 getViewer()->orientation()->isVerticalTimeline());
2046 for (c = col; c < rect.getLx() + col; c++) {
2047 for (r = row; r < rect.getLy() + row; r++)
2048 if (!xsh->getCell(r, c).isEmpty()) return false;
2049 }
2050 return true;
2051 }
2052
2053 public:
DataDragTool(XsheetViewer * viewer)2054 DataDragTool(XsheetViewer *viewer)
2055 : DragTool(viewer)
2056 , m_data(new DragAndDropData())
2057 , m_valid(false)
2058 , m_type(NO_MOVEMENT) {}
2059
onClick(const QDropEvent * e)2060 void onClick(const QDropEvent *e) override {
2061 if (e->mimeData()->hasUrls()) {
2062 QList<QUrl> urls = e->mimeData()->urls();
2063 int i;
2064 for (i = 0; i < urls.size(); i++)
2065 m_data->addPath(TFilePath(urls[i].toLocalFile().toStdWString()));
2066 } else if (e->mimeData()->hasFormat(CastItems::getMimeFormat())) {
2067 const CastItems *cast = dynamic_cast<const CastItems *>(e->mimeData());
2068 int i;
2069 for (i = 0; i < cast->getItemCount(); i++) {
2070 CastItem *item = cast->getItem(i);
2071 TXshSimpleLevel *sl = item->getSimpleLevel();
2072 if (!sl) continue;
2073 std::vector<TFrameId> fids;
2074 sl->getFids(fids);
2075 m_data->addSimpleLevel(std::make_pair(sl, fids));
2076 }
2077 } else if (e->mimeData()->hasFormat("application/vnd.toonz.drawings")) {
2078 TFilmstripSelection *s =
2079 dynamic_cast<TFilmstripSelection *>(TSelection::getCurrent());
2080 if (!s) return;
2081 TXshSimpleLevel *sl =
2082 TApp::instance()->getCurrentLevel()->getSimpleLevel();
2083 if (!sl) return;
2084 std::vector<TFrameId> fids;
2085 std::set<TFrameId> fidsSet = s->getSelectedFids();
2086 for (auto const &fid : fidsSet) {
2087 fids.push_back(fid);
2088 }
2089 m_data->addSimpleLevel(std::make_pair(sl, fids));
2090 }
2091 refreshCellsArea();
2092 }
onDrag(const QDropEvent * e)2093 void onDrag(const QDropEvent *e) override {
2094 TPoint pos(e->pos().x(), e->pos().y());
2095 CellPosition cellPosition = getViewer()->xyToPosition(e->pos());
2096 int row = cellPosition.frame();
2097 int col = cellPosition.layer();
2098
2099 m_valid = true;
2100 if (e->keyboardModifiers() & Qt::ShiftModifier)
2101 m_type = INSERT_CELLS;
2102 else if (e->keyboardModifiers() & Qt::AltModifier)
2103 m_type = OVERWRITE_CELLS;
2104 else
2105 m_valid = canChange(row, col);
2106 m_curPos = pos;
2107 refreshCellsArea();
2108 }
onRelease(const QDropEvent * e)2109 void onRelease(const QDropEvent *e) override {
2110 TPoint pos(e->pos().x(), e->pos().y());
2111 CellPosition cellPosition = getViewer()->xyToPosition(e->pos());
2112 int row = cellPosition.frame();
2113 int col = cellPosition.layer();
2114 if (m_type != NO_MOVEMENT && !m_valid) return;
2115
2116 bool insert = m_type == INSERT_CELLS;
2117 bool overWrite = m_type == OVERWRITE_CELLS;
2118
2119 if (!m_data->m_paths
2120 .empty()) // caso in cui ho i path e deve caricare i livelli
2121 {
2122 IoCmd::LoadResourceArguments args;
2123
2124 args.row0 = row;
2125 args.col0 = col;
2126
2127 args.resourceDatas.assign(m_data->m_paths.begin(), m_data->m_paths.end());
2128
2129 IoCmd::loadResources(args);
2130 } else if (!m_data->m_levels.empty()) {
2131 int i;
2132 for (i = 0; i < (int)m_data->m_levels.size(); i++) {
2133 TXshSimpleLevel *sl = m_data->m_levels[i].first.getPointer();
2134 std::vector<TFrameId> fids = m_data->m_levels[i].second;
2135 if (!sl || fids.empty()) continue;
2136 IoCmd::exposeLevel(sl, row, col, fids, insert, overWrite);
2137 }
2138 }
2139 refreshCellsArea();
2140 }
drawCellsArea(QPainter & p)2141 void drawCellsArea(QPainter &p) override {
2142 const Orientation *o = getViewer()->orientation();
2143 CellPosition beginDragPosition = getViewer()->xyToPosition(m_curPos);
2144 TPoint pos(beginDragPosition.layer(),
2145 beginDragPosition.frame()); // row and cell coordinates
2146 bool isVertical = getViewer()->orientation()->isVerticalTimeline();
2147 if (!isVertical) {
2148 pos.x = beginDragPosition.frame();
2149 pos.y = beginDragPosition.layer();
2150 }
2151 TRect rect =
2152 m_data->getLevelFrameRect(isVertical); // row and cell coordinates
2153 if (rect.isEmpty()) return;
2154 rect += pos;
2155 if (rect.x1 < 0 || rect.y1 < 0) return;
2156 if (rect.x0 < 0) rect.x0 = 0;
2157 if (rect.y0 < 0) rect.y0 = 0;
2158 QRect screenCell;
2159 if (o->isVerticalTimeline())
2160 screenCell = getViewer()->rangeToXYRect(
2161 CellRange(CellPosition(rect.y0, rect.x0),
2162 CellPosition(rect.y1 + 1, rect.x1 + 1)));
2163 else {
2164 int newY0 = std::max(rect.y0, rect.y1);
2165 int newY1 = std::min(rect.y0, rect.y1);
2166 screenCell = getViewer()->rangeToXYRect(CellRange(
2167 CellPosition(rect.x0, newY0), CellPosition(rect.x1 + 1, newY1 - 1)));
2168 }
2169 p.setPen(m_valid ? QColor(190, 220, 255) : QColor(255, 0, 0));
2170 int i;
2171 for (i = 0; i < 3; i++) // thick border within cell
2172 p.drawRect(QRect(screenCell.topLeft() + QPoint(i, i),
2173 screenCell.size() - QSize(2 * i, 2 * i)));
2174 }
2175 };
2176
2177 //-----------------------------------------------------------------------------
2178 } // namespace
2179 //-----------------------------------------------------------------------------
2180
makeDragAndDropDataTool(XsheetViewer * viewer)2181 XsheetGUI::DragTool *XsheetGUI::DragTool::makeDragAndDropDataTool(
2182 XsheetViewer *viewer) {
2183 return new DataDragTool(viewer);
2184 }
2185