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