1 
2 
3 #include "toonzqt/functionsheet.h"
4 
5 // TnzQt includes
6 #include "toonzqt/gutil.h"
7 #include "toonzqt/functionviewer.h"
8 
9 // TnzLib includes
10 #include "toonz/tframehandle.h"
11 #include "toonz/doubleparamcmd.h"
12 #include "toonz/preferences.h"
13 #include "toonz/toonzfolders.h"
14 #include "toonz/tstageobject.h"
15 #include "toonz/txsheethandle.h"
16 #include "toonz/txsheet.h"
17 
18 // TnzBase includes
19 #include "tunit.h"
20 
21 #include "../toonz/menubarcommandids.h"
22 
23 // Qt includes
24 #include <QPainter>
25 #include <QGridLayout>
26 #include <QPaintEvent>
27 #include <QMenu>
28 #include <QApplication>  //for drag&drop
29 #include <QDrag>
30 
31 //********************************************************************************
32 //    Local namespace stuff
33 //********************************************************************************
34 
35 namespace {
36 
37 const int cColumnDragHandleWidth = 8;
38 
39 const int cGroupShortNameY =
40     0;  //!< Column header's y pos for channel groups' short name tabs
41 const int cGroupLongNameY = 27;  //!< Same for its long name tabs
42 const int cChannelNameY = 50;  //!< Column header's y pos of channel name tabs,
43                                //! up to the widget's height
44 const int cColHeadersEndY = 87;  //!< End of column headers y pos
45 
46 }  // namespace
47 
48 //********************************************************************************
49 //    Function Sheet Tools
50 //********************************************************************************
51 
52 /*--- NumericalColumnsのセグメントの左側のバーをクリックしたとき ---*/
53 class MoveChannelsDragTool final : public Spreadsheet::DragTool {
54   FunctionSheet *m_sheet;
55   std::vector<KeyframeSetter *> m_setters;
56   int m_oldRow;
57   QRect m_selectedCells;
58   int m_firstKeyframeRow;
59 
60 public:
MoveChannelsDragTool(FunctionSheet * sheet)61   MoveChannelsDragTool(FunctionSheet *sheet)
62       : m_sheet(sheet), m_firstKeyframeRow(-1) {}
63 
click(int row,int col,QMouseEvent * e)64   void click(int row, int col, QMouseEvent *e) override {
65     m_firstKeyframeRow                  = -1;
66     FunctionTreeModel::Channel *channel = m_sheet->getChannel(col);
67     if (!channel) return;
68     TDoubleParam *curve = channel->getParam();
69     int k0 = -1, k1 = -1;
70     if (curve->isKeyframe(row))
71       k0 = k1 = curve->getClosestKeyframe(row);
72     else {
73       k0 = curve->getPrevKeyframe(row);
74       k1 = curve->getNextKeyframe(row);
75     }
76     // return if clicking outside of the segments
77     if (k0 < 0 || k1 < 0) return;
78     int r0 = tround(curve->keyframeIndexToFrame(k0));
79     int r1 = tround(curve->keyframeIndexToFrame(k1));
80     if (m_sheet->isSelectedCell(row, col)) {
81       m_selectedCells = m_sheet->getSelectedCells();
82       m_selectedCells.setTop(r0);
83       m_selectedCells.setBottom(r1);
84     } else
85       m_selectedCells = QRect(col, r0, 1, r1 - r0 + 1);
86 
87     m_sheet->selectCells(m_selectedCells);
88 
89     /*---
90 シンプルに左のバーをクリックした場合はcolは1つだけ。
91 すでに複数Columnが選択されている上でその選択範囲内のセルの左バーをクリックした場合は
92 横(Column)幅は選択範囲に順ずるようになる。高さ(row)はクリックしたセグメントと同じになる。
93 ---*/
94     /*--- セグメントごとドラッグに備えてKeyFrameを格納する ---*/
95     for (int col = m_selectedCells.left(); col <= m_selectedCells.right();
96          ++col) {
97       TDoubleParam *curve = m_sheet->getCurve(col);
98       if (!curve) continue;
99       KeyframeSetter *setter = new KeyframeSetter(curve);
100       for (int k = 0; k < curve->getKeyframeCount(); k++) {
101         int row = (int)curve->keyframeIndexToFrame(k);
102         if (r0 <= row && row <= r1) {
103           if (m_firstKeyframeRow < 0 || row < m_firstKeyframeRow)
104             m_firstKeyframeRow = row;
105           setter->selectKeyframe(k);
106         }
107       }
108       m_setters.push_back(setter);
109     }
110     m_oldRow = row;
111   }
112 
drag(int row,int col,QMouseEvent * e)113   void drag(int row, int col, QMouseEvent *e) override {
114     int d    = row - m_oldRow;
115     m_oldRow = row;
116     if (d + m_firstKeyframeRow < 0) d = -m_firstKeyframeRow;
117     m_firstKeyframeRow += d;
118     for (int i = 0; i < (int)m_setters.size(); i++)
119       m_setters[i]->moveKeyframes(d, 0.0);
120     m_selectedCells.translate(0, d);
121     m_sheet->selectCells(m_selectedCells);
122   }
123 
release(int row,int col,QMouseEvent * e)124   void release(int row, int col, QMouseEvent *e) override {
125     for (int i = 0; i < (int)m_setters.size(); i++) delete m_setters[i];
126     m_setters.clear();
127   }
128 };
129 
130 //-----------------------------------------------------------------------------
131 /*--- NumericalColumnsのセル部分をクリックしたとき ---*/
132 class FunctionSheetSelectionTool final : public Spreadsheet::DragTool {
133   int m_firstRow, m_firstCol;
134   FunctionSheet *m_sheet;
135 
136 public:
FunctionSheetSelectionTool(FunctionSheet * sheet)137   FunctionSheetSelectionTool(FunctionSheet *sheet)
138       : m_sheet(sheet), m_firstRow(-1), m_firstCol(-1) {}
139 
click(int row,int col,QMouseEvent * e)140   void click(int row, int col, QMouseEvent *e) override {
141     if (0 != (e->modifiers() & Qt::ShiftModifier) &&
142         !m_sheet->getSelectedCells().isEmpty()) {
143       QRect selectedCells = m_sheet->getSelectedCells();
144       if (col < selectedCells.center().x()) {
145         m_firstCol = selectedCells.right();
146         selectedCells.setLeft(col);
147       } else {
148         m_firstCol = selectedCells.left();
149         selectedCells.setRight(col);
150       }
151       if (row < selectedCells.center().y()) {
152         m_firstRow = selectedCells.bottom();
153         selectedCells.setTop(row);
154       } else {
155         m_firstRow = selectedCells.top();
156         selectedCells.setBottom(row);
157       }
158       m_sheet->selectCells(selectedCells);
159     } else {
160       // regular click, no shift
161       m_firstCol = col;
162       m_firstRow = row;
163       QRect selectedCells(col, row, 1, 1);
164       m_sheet->selectCells(selectedCells);
165     }
166   }
167 
drag(int row,int col,QMouseEvent * e)168   void drag(int row, int col, QMouseEvent *e) override {
169     if (row < 0) row = 0;
170     if (col < 0) col = 0;
171     int r0 = std::min(row, m_firstRow);
172     int r1 = std::max(row, m_firstRow);
173     int c0 = std::min(col, m_firstCol);
174     int c1 = std::max(col, m_firstCol);
175     QRect selectedCells(c0, r0, c1 - c0 + 1, r1 - r0 + 1);
176     m_sheet->selectCells(selectedCells);
177   }
178 
release(int row,int col,QMouseEvent * e)179   void release(int row, int col, QMouseEvent *e) override {
180     if (row == m_firstRow && col == m_firstCol) {
181       if (Preferences::instance()->isMoveCurrentEnabled())
182         m_sheet->setCurrentFrame(row);
183       FunctionTreeModel::Channel *channel = m_sheet->getChannel(col);
184       if (channel) channel->setIsCurrent(true);
185     }
186   }
187 };
188 
189 //********************************************************************************
190 //    FunctionSheetRowViewer  implementation
191 //********************************************************************************
192 
FunctionSheetRowViewer(FunctionSheet * parent)193 FunctionSheetRowViewer::FunctionSheetRowViewer(FunctionSheet *parent)
194     : Spreadsheet::RowPanel(parent), m_sheet(parent) {
195   setMinimumSize(QSize(100, 100));
196   setMouseTracking(true);
197   setFocusPolicy(Qt::NoFocus);
198 }
199 
200 //-----------------------------------------------------------------------------
201 
paintEvent(QPaintEvent * e)202 void FunctionSheetRowViewer::paintEvent(QPaintEvent *e) {
203   // calls GenericPanel's event
204   Spreadsheet::RowPanel::paintEvent(e);
205 }
206 
207 //-----------------------------------------------------------------------------
208 
mousePressEvent(QMouseEvent * e)209 void FunctionSheetRowViewer::mousePressEvent(QMouseEvent *e) {
210   // calls GenericPanel's event
211   Spreadsheet::RowPanel::mousePressEvent(e);
212 }
213 
214 //-----------------------------------------------------------------------------
215 
mouseMoveEvent(QMouseEvent * e)216 void FunctionSheetRowViewer::mouseMoveEvent(QMouseEvent *e) {
217   // calls GenericPanel's event
218   Spreadsheet::RowPanel::mouseMoveEvent(e);
219 }
220 
221 //-----------------------------------------------------------------------------
222 
mouseReleaseEvent(QMouseEvent * e)223 void FunctionSheetRowViewer::mouseReleaseEvent(QMouseEvent *e) {
224   Spreadsheet::RowPanel::mouseReleaseEvent(e);
225 }
226 
227 //-----------------------------------------------------------------------------
228 
contextMenuEvent(QContextMenuEvent * event)229 void FunctionSheetRowViewer::contextMenuEvent(QContextMenuEvent *event) {
230   QMenu *menu                = new QMenu(this);
231   CommandManager *cmdManager = CommandManager::instance();
232   menu->addAction(cmdManager->getAction(MI_InsertSceneFrame));
233   menu->addAction(cmdManager->getAction(MI_RemoveSceneFrame));
234   menu->addAction(cmdManager->getAction(MI_InsertGlobalKeyframe));
235   menu->addAction(cmdManager->getAction(MI_RemoveGlobalKeyframe));
236   menu->exec(event->globalPos());
237 }
238 
239 //********************************************************************************
240 //    FunctionSheetColumnHeadViewer  implementation
241 //********************************************************************************
242 
FunctionSheetColumnHeadViewer(FunctionSheet * parent)243 FunctionSheetColumnHeadViewer::FunctionSheetColumnHeadViewer(
244     FunctionSheet *parent)
245     : Spreadsheet::ColumnPanel(parent), m_sheet(parent), m_draggingChannel(0) {
246   setMouseTracking(true);  // for updating the tooltip
247   setFocusPolicy(Qt::NoFocus);
248 }
249 
250 //-----------------------------------------------------------------------------
251 
paintEvent(QPaintEvent * e)252 void FunctionSheetColumnHeadViewer::paintEvent(QPaintEvent *e) {
253   QPainter painter(this);
254 
255   QRect toBeUpdated = e->rect();
256   painter.setClipRect(toBeUpdated);
257 
258   // visible columns range
259   CellRange visible = getViewer()->xyRectToRange(toBeUpdated);
260   int c0            = visible.from().layer();
261   int c1            = visible.to().layer();
262 
263   if (c0 > c1) return;
264 
265   int n = c1 - c0 + 1 + 2;
266 
267   FunctionTreeModel::ChannelGroup *currentGroup = 0;
268 
269   /*--- Display range + right and left 1 column range ChannelGroup. If there is
270    * none, put 0
271    * ---*/
272   std::vector<FunctionTreeModel::ChannelGroup *> channelGroups(n);
273   for (int i = 0; i < n; ++i) {
274     channelGroups[i] = 0;
275 
276     int c = c0 - 1 + i;
277     if (c < 0) continue;
278 
279     FunctionTreeModel::Channel *channel = m_sheet->getChannel(c);
280     if (!channel) continue;
281 
282     channelGroups[i] = channel->getChannelGroup();
283     if (!currentGroup && channel->isCurrent()) currentGroup = channelGroups[i];
284   }
285 
286   int y0 = 0;
287   int y1 = 17;  // needs work
288   int y2 = 34;
289   int y3 = 53;
290 
291   /*--- Fill header with background color ---*/
292   for (int c = c0; c <= c1; c++) {
293     FunctionTreeModel::Channel *channel = m_sheet->getChannel(c);
294     if (!channel) break;
295     int x0 = getViewer()->columnToX(c);
296     int x1 = getViewer()->columnToX(c + 1) - 1;
297     // Column Width
298     int width = x1 - x0 + 1;
299 
300     painter.fillRect(x0, y0, width, y3 - y0, getViewer()->getBGColor());
301   }
302 
303   for (int c = c0; c <= c1; ++c) {
304     FunctionTreeModel::Channel *channel = m_sheet->getChannel(c);
305     if (!channel) {
306       if (c != c0) {
307         // draw "the end" border
308         int x0 = getViewer()->columnToX(c);
309         painter.setPen(getViewer()->getColumnHeaderBorderColor());
310         painter.drawLine(x0, y0, x0, y3);
311       }
312       break;
313     }
314     int i = c - c0 + 1;
315     /*---- Channel Column of the current Column and the preceding and following
316      * Columns ---*/
317     FunctionTreeModel::ChannelGroup *prevGroup = channelGroups[c - c0],
318                                     *group     = channelGroups[c - c0 + 1],
319                                     *nextGroup = channelGroups[c - c0 + 2];
320 
321     /*---- If the group is different from the before and after, flags are set
322      * respectively ---*/
323     bool firstGroupColumn = prevGroup != group;
324     bool lastGroupColumn  = nextGroup != group;
325 
326     /*--- The left and right coordinates of the current column ---*/
327     int x0 = getViewer()->columnToX(c);
328     int x1 = getViewer()->columnToX(c + 1) - 1;
329     // Column width
330     int width = x1 - x0 + 1;
331 
332     QRect selectedRect = m_sheet->getSelectedCells();
333     bool isSelected =
334         (selectedRect.left() <= c && c <= selectedRect.right()) ? true : false;
335 
336     // paint with light color if selected
337     if (isSelected)
338       painter.fillRect(x0, y1, width, y3 - y1,
339                        getViewer()->getCurrentRowBgColor());
340 
341     // draw horizonntal lines
342     painter.setPen(QPen(getViewer()->getColumnHeaderBorderColor(), 3));
343     painter.drawLine(x0, y0, x1, y0);
344     painter.setPen(getViewer()->getColumnHeaderBorderColor());
345     painter.drawLine(x0, y1, x1, y1);
346 
347     // draw vertical bar
348     painter.fillRect(x0, y1, 6, y3 - y1,
349                      getViewer()->getColumnHeaderBorderColor());
350     if (firstGroupColumn)
351       painter.fillRect(x0, y0, 6, y1 - y0,
352                        getViewer()->getColumnHeaderBorderColor());
353 
354     // channel name
355     painter.setPen(getViewer()->getTextColor());
356     if (channel->isCurrent())
357       painter.setPen(m_sheet->getViewer()->getCurrentTextColor());
358 
359     QString text = channel->getShortName();
360     int d        = 8;
361     painter.drawText(x0 + d, y1, width - d, y3 - y1 + 1,
362                      Qt::TextWrapAnywhere | Qt::AlignLeft | Qt::AlignVCenter,
363                      text);
364 
365     // warning of losing expression reference
366     TXsheet *xsh = m_sheet->getViewer()->getXsheetHandle()->getXsheet();
367     if (xsh->isReferenceManagementIgnored(channel->getParam())) {
368       static QIcon paramIgnoredIcon(":Resources/paramignored_on.svg");
369       painter.drawPixmap(QPoint(x0 + 30, y1 + 20),
370                          paramIgnoredIcon.pixmap(21, 17));
371     }
372 
373     // group name
374     if (firstGroupColumn) {
375       int tmpwidth = (lastGroupColumn) ? width : width * 2;
376       painter.setPen(getViewer()->getTextColor());
377       if (group == currentGroup)
378         painter.setPen(m_sheet->getViewer()->getCurrentTextColor());
379       text = group->getShortName();
380       painter.drawText(x0 + d, y0, tmpwidth - d, y1 - y0 + 1,
381                        Qt::AlignLeft | Qt::AlignVCenter, text);
382     }
383   }
384 }
385 
386 //-----------------------------------------------------------------------------
387 /*! update tooltips
388  */
mouseMoveEvent(QMouseEvent * e)389 void FunctionSheetColumnHeadViewer::mouseMoveEvent(QMouseEvent *e) {
390   if ((e->buttons() & Qt::MidButton) && m_draggingChannel &&
391       (e->pos() - m_dragStartPosition).manhattanLength() >=
392           QApplication::startDragDistance()) {
393     QDrag *drag         = new QDrag(this);
394     QMimeData *mimeData = new QMimeData;
395     mimeData->setText(m_draggingChannel->getExprRefName());
396     drag->setMimeData(mimeData);
397     static const QPixmap cursorPixmap(":Resources/dragcursor_exp_text.png");
398     drag->setDragCursor(cursorPixmap, Qt::MoveAction);
399     Qt::DropAction dropAction = drag->exec();
400     return;
401   }
402 
403   // get the column under the cursor
404   int col = getViewer()->xyToPosition(e->pos()).layer();
405   FunctionTreeModel::Channel *channel = m_sheet->getChannel(col);
406   if (!channel) {
407     setToolTip(QString(""));
408   } else {
409     QString tooltip = channel->getExprRefName();
410     TXsheet *xsh    = m_sheet->getViewer()->getXsheetHandle()->getXsheet();
411     if (xsh->isReferenceManagementIgnored(channel->getParam()))
412       tooltip +=
413           "\n" + tr("Some key(s) in this parameter loses original reference in "
414                     "expression.\nManually changing any keyframe will clear "
415                     "the warning.");
416 
417     setToolTip(tooltip);
418   }
419 
420   // modify selected channel by left dragging
421   if (m_clickedColumn >= 0 && channel && e->buttons() & Qt::LeftButton) {
422     int fromC      = std::min(m_clickedColumn, col);
423     int toC        = std::max(m_clickedColumn, col);
424     int lastKeyPos = 0;
425     for (int c = fromC; c <= toC; c++) {
426       FunctionTreeModel::Channel *tmpChan = m_sheet->getChannel(c);
427       if (!tmpChan) continue;
428       std::set<double> frames;
429       tmpChan->getParam()->getKeyframes(frames);
430       if (!frames.empty())
431         lastKeyPos = std::max(lastKeyPos, (int)*frames.rbegin());
432     }
433     QRect rect(std::min(m_clickedColumn, col), 0,
434                std::abs(col - m_clickedColumn) + 1, lastKeyPos + 1);
435     getViewer()->selectCells(rect);
436   }
437 }
438 
439 //-----------------------------------------------------------------------------
440 
mousePressEvent(QMouseEvent * e)441 void FunctionSheetColumnHeadViewer::mousePressEvent(QMouseEvent *e) {
442   QPoint pos                          = e->pos();
443   int currentC                        = getViewer()->xyToPosition(pos).layer();
444   FunctionTreeModel::Channel *channel = m_sheet->getChannel(currentC);
445   if (!channel) {
446     m_clickedColumn = -1;
447     return;
448   }
449 
450   if (e->button() == Qt::MidButton) {
451     m_draggingChannel   = channel;
452     m_dragStartPosition = e->pos();
453     return;
454   } else
455     channel->setIsCurrent(true);
456   m_draggingChannel = 0;
457 
458   if (e->button() == Qt::LeftButton) {
459     int lastKeyPos = 0;
460     // if the current selection does not contain the first cell in m_firstColumn
461     // then we assume that the selection has been modified and treat shift+click
462     // as normal click.
463     if (getViewer()->getSelectedCells().contains(m_clickedColumn, 0) &&
464         (e->modifiers() & Qt::ShiftModifier)) {
465       int fromC = std::min(m_clickedColumn, currentC);
466       int toC   = std::max(m_clickedColumn, currentC);
467       for (int c = fromC; c <= toC; c++) {
468         FunctionTreeModel::Channel *tmpChan = m_sheet->getChannel(c);
469         if (!tmpChan) continue;
470         std::set<double> frames;
471         tmpChan->getParam()->getKeyframes(frames);
472         if (!frames.empty())
473           lastKeyPos = std::max(lastKeyPos, (int)*frames.rbegin());
474       }
475     } else {
476       // Open folder
477       FunctionTreeModel::ChannelGroup *channelGroup =
478           channel->getChannelGroup();
479       if (!channelGroup->isOpen())
480         channelGroup->getModel()->setExpandedItem(channelGroup->createIndex(),
481                                                   true);
482       // Select all segment
483       std::set<double> frames;
484       channel->getParam()->getKeyframes(frames);
485       if (!frames.empty()) lastKeyPos = (int)*frames.rbegin();
486       m_clickedColumn = currentC;
487     }
488     QRect rect(std::min(m_clickedColumn, currentC), 0,
489                std::abs(currentC - m_clickedColumn) + 1, lastKeyPos + 1);
490 
491     getViewer()->selectCells(rect);
492   }
493   // Switch selection before opening the context menu
494   // if the clicked column is out of the selection
495   else if (e->button() == Qt::RightButton) {
496     QRect selectedCell = getViewer()->getSelectedCells();
497     if (selectedCell.left() > currentC || selectedCell.right() < currentC) {
498       int lastKeyPos = 0;
499       std::set<double> frames;
500       channel->getParam()->getKeyframes(frames);
501       if (!frames.empty()) lastKeyPos = (int)*frames.rbegin();
502       getViewer()->selectCells(QRect(currentC, 0, 1, lastKeyPos + 1));
503     }
504   }
505 }
506 
507 //-----------------------------------------------------------------------------
508 
contextMenuEvent(QContextMenuEvent * ce)509 void FunctionSheetColumnHeadViewer::contextMenuEvent(QContextMenuEvent *ce) {
510   // First, select column under cursor
511   const QPoint &pos = ce->pos();
512   int cursorCol     = getViewer()->xyToPosition(pos).layer();
513 
514   if (cursorCol < 0 || cursorCol >= m_sheet->getChannelCount()) return;
515 
516   FunctionTreeModel::Channel *channel = m_sheet->getChannel(cursorCol);
517   if (!channel) return;
518 
519   // Ok, now let's summon a context menu with appropriate options
520   FunctionViewer *fv = m_sheet->getViewer();
521   if (!fv) {
522     assert(fv);
523     return;
524   }
525 
526   const QPoint &globalPos = mapToGlobal(pos);
527 
528   if (pos.y() >= cChannelNameY)
529     fv->openContextMenu(channel, globalPos);
530   else {
531     FunctionTreeModel::ChannelGroup *group = channel->getChannelGroup();
532 
533     // In this case, commands are different from the tree view. Rather than
534     // showing in the tree,
535     // channels get ACTIVATED
536     QMenu menu;
537 
538     QAction showAnimatedOnly(FunctionTreeView::tr("Show Animated Only"), 0);
539     QAction showAll(FunctionTreeView::tr("Show All"), 0);
540     QAction hideSelected(FunctionTreeView::tr("Hide Selected"), 0);
541     menu.addAction(&showAnimatedOnly);
542     menu.addAction(&showAll);
543     menu.addAction(&hideSelected);
544 
545     // execute menu
546     QAction *action = menu.exec(globalPos);
547 
548     // Process action
549     if (action == &showAll) {
550       int c, cCount = group->getChildCount();
551       for (c = 0; c != cCount; ++c) {
552         FunctionTreeModel::Channel *channel =
553             dynamic_cast<FunctionTreeModel::Channel *>(group->getChild(c));
554         if (channel && !channel->isHidden()) channel->setIsActive(true);
555       }
556     } else if (action == &showAnimatedOnly) {
557       int c, cCount = group->getChildCount();
558       for (c = 0; c != cCount; ++c) {
559         FunctionTreeModel::Channel *channel =
560             dynamic_cast<FunctionTreeModel::Channel *>(group->getChild(c));
561         if (channel && !channel->isHidden())
562           channel->setIsActive(channel->isAnimated());
563       }
564     } else if (action == &hideSelected) {
565       QRect selectedCells = getViewer()->getSelectedCells();
566       // hide the selected columns from the right to the left
567       for (int col = selectedCells.right(); col >= selectedCells.left();
568            col--) {
569         FunctionTreeModel::Channel *chan = m_sheet->getChannel(col);
570         if (chan) chan->setIsActive(false);
571       }
572       // clear cell selection
573       getViewer()->selectCells(QRect());
574     } else
575       return;
576 
577     fv->update();
578   }
579 }
580 
581 //********************************************************************************
582 //    FunctionSheetCellViewer  implementation
583 //********************************************************************************
584 
FunctionSheetCellViewer(FunctionSheet * parent)585 FunctionSheetCellViewer::FunctionSheetCellViewer(FunctionSheet *parent)
586     : Spreadsheet::CellPanel(parent)
587     , m_sheet(parent)
588     , m_editRow(0)
589     , m_editCol(0) {
590   m_lineEdit = new DVGui::LineEdit(this);
591   // lineEdit->setGeometry(10,10,100,30);
592   m_lineEdit->hide();
593   bool ret = connect(m_lineEdit, SIGNAL(editingFinished()), this,
594                      SLOT(onCellEditorEditingFinished()));
595   ret      = ret && connect(m_lineEdit, SIGNAL(mouseMoved(QMouseEvent *)), this,
596                        SLOT(onMouseMovedInLineEdit(QMouseEvent *)));
597   assert(ret);
598   setMouseTracking(true);
599 
600   setFocusProxy(m_lineEdit);
601 }
602 
603 //-----------------------------------------------------------------------------
604 /*! Called when the cell panel is left/right-clicked
605  */
createDragTool(QMouseEvent * e)606 Spreadsheet::DragTool *FunctionSheetCellViewer::createDragTool(QMouseEvent *e) {
607   CellPosition cellPosition = getViewer()->xyToPosition(e->pos());
608   int row                   = cellPosition.frame();
609   int col                   = cellPosition.layer();
610   bool isEmpty              = true;
611   TDoubleParam *curve       = m_sheet->getCurve(col);
612   if (curve) {
613     int kCount = curve->getKeyframeCount();
614     if (kCount > 0) {
615       int row0 = (int)curve->keyframeIndexToFrame(0);
616       int row1 = (int)curve->keyframeIndexToFrame(kCount - 1);
617       isEmpty  = row < row0 || row > row1;
618     }
619   }
620 
621   if (!isEmpty) {
622     int x = e->pos().x() - getViewer()->columnToX(col);
623     if (0 <= x && x < cColumnDragHandleWidth + 9)
624       return new MoveChannelsDragTool(m_sheet);
625   }
626   return new FunctionSheetSelectionTool(m_sheet);
627 
628   // return Spreadsheet::CellPanel::createDragTool(e);
629 }
630 
631 //-----------------------------------------------------------------------------
632 
drawCells(QPainter & painter,int r0,int c0,int r1,int c1)633 void FunctionSheetCellViewer::drawCells(QPainter &painter, int r0, int c0,
634                                         int r1, int c1) {
635   // key frames
636   QColor KeyFrameColor         = getViewer()->getKeyFrameColor();
637   QColor KeyFrameBorderColor   = getViewer()->getKeyFrameBorderColor();
638   QColor SelectedKeyFrameColor = getViewer()->getSelectedKeyFrameColor();
639   QColor IgnoredKeyFrameColor  = getViewer()->getIgnoredKeyFrameColor();
640   QColor SelectedIgnoredKeyFrameColor =
641       getViewer()->getSelectedIgnoredKeyFrameColor();
642   // inbetween
643   QColor InBetweenColor         = getViewer()->getInBetweenColor();
644   QColor InBetweenBorderColor   = getViewer()->getInBetweenBorderColor();
645   QColor SelectedInBetweenColor = getViewer()->getSelectedInBetweenColor();
646   QColor IgnoredInBetweenColor  = getViewer()->getIgnoredInBetweenColor();
647   QColor SelectedIgnoredInBetweenColor =
648       getViewer()->getSelectedIgnoredInBetweenColor();
649 
650   // empty cells
651   QColor SelectedEmptyColor = getViewer()->getSelectedEmptyColor();
652   // empty cells in scene frame range
653   QColor SelectedSceneRangeEmptyColor =
654       getViewer()->getSelectedSceneRangeEmptyColor();
655 
656   TXsheet *xsh = m_sheet->getViewer()->getXsheetHandle()->getXsheet();
657 
658   // top and bottom pos
659   int y0 = getViewer()->rowToY(r0);
660   int y1 = getViewer()->rowToY(r1 + 1) - 1;
661   for (int c = c0; c <= c1; c++) {
662     TDoubleParam *curve = m_sheet->getCurve(c);
663     /*--- もしカラムcにパラメータが無ければcurveには0が返る ---*/
664     if (!curve) continue;
665     // left and right pos
666     int x0 = getViewer()->columnToX(c);
667     int x1 = getViewer()->columnToX(c + 1) - 1;
668 
669     // find the curve keyframe range
670     int kr0 = 0, kr1 = -1;
671     int kCount = curve->getKeyframeCount();
672     if (kCount > 0) {
673       kr0 = curve->keyframeIndexToFrame(0);
674       kr1 = curve->keyframeIndexToFrame(kCount - 1);
675     }
676 
677     // get the unit
678     TMeasure *measure = curve->getMeasure();
679     const TUnit *unit = measure ? measure->getCurrentUnit() : 0;
680 
681     bool isStageObjectCycled = false;
682     TStageObject *obj        = m_sheet->getStageObject(c);
683     if (obj && obj->isCycleEnabled()) isStageObjectCycled = true;
684 
685     bool isParamCycled = curve->isCycleEnabled();
686     int rowCount       = getViewer()->getRowCount();
687 
688     bool isRefMngIgnored = xsh->isReferenceManagementIgnored(curve);
689 
690     // draw each cell
691     for (int row = r0; row <= r1; row++) {
692       int ya = m_sheet->rowToY(row);
693       int yb = m_sheet->rowToY(row + 1) - 1;
694 
695       bool isSelected = getViewer()->isSelectedCell(row, c);
696 
697       double value = (isStageObjectCycled)
698                          ? curve->getValue(obj->paramsTime((double)row))
699                          : curve->getValue(row);
700       if (unit) value = unit->convertTo(value);
701       enum { None, Key, Inbetween, CycleRange } drawValue = None;
702 
703       QRect cellRect(x0, ya, x1 - x0 + 1, yb - ya + 1);
704       QRect borderRect(x0, ya, 7, yb - ya + 1);
705       QColor cellColor, borderColor;
706 
707       /*--- キーフレーム間の範囲だけ色をつける ---*/
708       if (kr0 <= row && row <= kr1) {
709         if (curve->isKeyframe(row)) {
710           cellColor =
711               (isRefMngIgnored)
712                   ? ((isSelected) ? SelectedIgnoredKeyFrameColor
713                                   : IgnoredKeyFrameColor)
714                   : ((isSelected) ? SelectedKeyFrameColor : KeyFrameColor);
715           borderColor = KeyFrameBorderColor;
716         } else {
717           cellColor =
718               (isRefMngIgnored)
719                   ? ((isSelected) ? SelectedIgnoredInBetweenColor
720                                   : IgnoredInBetweenColor)
721                   : ((isSelected) ? SelectedInBetweenColor : InBetweenColor);
722           borderColor = InBetweenBorderColor;
723 
724           // when the inbetween values are hidden, change the cell colors to
725           // semi-transparent if the frame is in middle of the value step
726           if (!m_sheet->isIbtwnValueVisible()) {
727             TDoubleKeyframe kf =
728                 curve->getKeyframe(curve->getPrevKeyframe(row));
729             int step = kf.m_step;
730             if (step > 1 && (row - (int)std::floor(kf.m_frame)) % step != 0)
731               cellColor.setAlpha(128);
732           }
733         }
734         painter.setPen(Qt::NoPen);
735         painter.fillRect(cellRect, cellColor);
736         painter.fillRect(borderRect, borderColor);
737 
738         // display whether segment are Linked
739         if (curve->isKeyframe(row)) {
740           TDoubleKeyframe kf = curve->getKeyframeAt(row);
741           // if the segments are NOT linked, then cut off the side bar
742           if (!kf.m_linkedHandles) {
743             int rowCenterPos = (ya + yb) / 2;
744             QPoint points[4] = {
745                 QPoint(x0, rowCenterPos), QPoint(x0 + 7, rowCenterPos + 3),
746                 QPoint(x0 + 7, rowCenterPos - 3), QPoint(x0, rowCenterPos)};
747             QBrush oldBrush = painter.brush();
748             painter.setBrush(QBrush(cellColor));
749             painter.drawPolygon(points, 4);
750             painter.setBrush(oldBrush);
751           }
752         }
753 
754         drawValue = (curve->isKeyframe(row))
755                         ? Key
756                         : (m_sheet->isIbtwnValueVisible()) ? Inbetween : None;
757 
758       }
759       // empty cells
760       else {
761         // show values for cycled parameter.
762         // cycle option can be set in two ways; one is as TStageObject,
763         // the other is as TDoubleParam.
764         // - TStageObject cycle literally cycles values with no offset.
765         //   Applied to all transformation parameters of the cycled object.
766         // - TDoubleParam cycle includes value offset so that the curve
767         //   connects smoothly.
768         // - TStageObject cycle option has a priority to TDoubleParam one.
769         // (see TStageObject::paramsTime() in tstageobject.cpp)
770         if (kCount > 0 && row > kr1 && (isStageObjectCycled || isParamCycled) &&
771             (row < rowCount)) {
772           drawValue = CycleRange;
773         }
774         // empty and selected cell
775         if (isSelected) {
776           cellColor = (row >= rowCount) ? SelectedEmptyColor
777                                         : SelectedSceneRangeEmptyColor;
778           painter.setPen(Qt::NoPen);
779           painter.fillRect(cellRect, cellColor);
780         }
781       }
782 
783       if (drawValue != None) {
784         // draw cell value
785         if (drawValue == Key || drawValue == Inbetween)
786           painter.setPen(getViewer()->getTextColor());
787         else {
788           QColor semiTranspTextColor = getViewer()->getTextColor();
789           semiTranspTextColor.setAlpha(128);
790           painter.setPen(semiTranspTextColor);
791         }
792 
793         /*--- 整数から小数点以下3桁以内の場合はそれ以降の0000を描かない ---*/
794         QString text;
795 
796         double thousandValue = value * 1000.0;
797         if (areAlmostEqual(thousandValue, (double)tround(thousandValue),
798                            0.0001)) {
799           text = QString::number(value, 'f', 3);
800           while (text.endsWith("0")) {
801             text.chop(1);
802             if (text.endsWith(".")) {
803               text.chop(1);
804               break;
805             }
806           }
807         } else {
808           text = QString::number(value, 'f', 4);
809           text.truncate(5);
810           text.append("~");
811         }
812 
813         QString fontName = Preferences::instance()->getInterfaceFont();
814         if (fontName == "") {
815 #ifdef _WIN32
816           fontName = "Arial";
817 #else
818           fontName = "Helvetica";
819 #endif
820         }
821         static QFont font(fontName, -1);
822         font.setBold(drawValue == Key);
823         font.setPixelSize(12);
824         painter.setFont(font);
825         painter.drawText(cellRect.adjusted(10, 0, 0, 0),
826                          Qt::AlignVCenter | Qt::AlignLeft, text);
827       }
828     }
829 
830     if (kCount > 0 && (isStageObjectCycled || isParamCycled)) {
831       // draw the row zigzag
832       int ymax           = m_sheet->rowToY(r1 + 1);
833       int qx             = x0 + 4;
834       int qy             = m_sheet->rowToY(kr1 + 1);
835       int zig            = 2;
836       QColor zigzagColor = (isStageObjectCycled) ? getViewer()->getTextColor()
837                                                  : KeyFrameBorderColor;
838       painter.setPen(zigzagColor);
839       painter.drawLine(QPoint(qx, qy), QPoint(qx - zig, qy + zig));
840       qy += zig;
841       while (qy < ymax) {
842         painter.drawLine(QPoint(qx - zig, qy), QPoint(qx + zig, qy + 2 * zig));
843         painter.drawLine(QPoint(qx + zig, qy + 2 * zig),
844                          QPoint(qx - zig, qy + 4 * zig));
845         qy += 4 * zig;
846       }
847     }
848   }
849 }
850 
851 //-----------------------------------------------------------------------------
852 
mouseDoubleClickEvent(QMouseEvent * e)853 void FunctionSheetCellViewer::mouseDoubleClickEvent(QMouseEvent *e) {
854   CellPosition cellPosition = getViewer()->xyToPosition(e->pos());
855   int row                   = cellPosition.frame();
856   int col                   = cellPosition.layer();
857   int x0, y0, x1, y1;
858   x0 = getViewer()->columnToX(col);
859   x1 = getViewer()->columnToX(col + 1) - 1;
860   y0 = getViewer()->rowToY(row);
861   y1 = getViewer()->rowToY(row + 1) - 1;
862 
863   m_editRow = row;
864   m_editCol = col;
865 
866   TDoubleParam *curve = m_sheet->getCurve(col);
867   if (curve) {
868     double v          = curve->getValue(row);
869     TMeasure *measure = curve->getMeasure();
870     const TUnit *unit = measure ? measure->getCurrentUnit() : 0;
871     if (unit) v = unit->convertTo(v);
872     m_currentValue = v;
873     m_lineEdit->setText(QString::number(v, 'f', 4));
874     // in order to put the cursor to the left end
875     m_lineEdit->setSelection(m_lineEdit->text().length(),
876                              -m_lineEdit->text().length());
877   } else
878     m_lineEdit->setText("");
879 
880   QString fontName = Preferences::instance()->getInterfaceFont();
881   if (fontName == "") {
882 #ifdef _WIN32
883     fontName = "Arial";
884 #else
885     fontName = "Helvetica";
886 #endif
887   }
888   static QFont font(fontName, 9, QFont::Normal);
889   m_lineEdit->setFont(font);
890 
891   m_lineEdit->setGeometry(x0 - 2, y0 - 2, x1 - x0 + 1 + 4,
892                           y1 - y0 + 1 + 4);  // x0,y0,x1-x0+1,y0-y1+1);
893   m_lineEdit->show();
894   m_lineEdit->raise();
895   m_lineEdit->setFocus();
896 }
897 
898 //-----------------------------------------------------------------------------
899 
onCellEditorEditingFinished()900 void FunctionSheetCellViewer::onCellEditorEditingFinished() {
901   QString text = m_lineEdit->text();
902   if (!text.isEmpty() &&
903       (m_lineEdit->isReturnPressed() || m_lineEdit->getMouseDragEditing())) {
904     double value        = text.toDouble();
905     TDoubleParam *curve = m_sheet->getCurve(m_editCol);
906     if (curve) {
907       TMeasure *measure = curve->getMeasure();
908       const TUnit *unit = measure ? measure->getCurrentUnit() : 0;
909       if (unit) value = unit->convertFrom(value);
910       KeyframeSetter::setValue(curve, m_editRow, value);
911     }
912   }
913   m_lineEdit->hide();
914   m_lineEdit->clearFocus();
915   m_sheet->setFocus();
916   update();
917 }
918 
919 //-----------------------------------------------------------------------------
920 
mousePressEvent(QMouseEvent * e)921 void FunctionSheetCellViewer::mousePressEvent(QMouseEvent *e) {
922   // escape from the line edit by clicking outside
923   if (m_lineEdit->isVisible()) {
924     m_lineEdit->hide();
925     m_lineEdit->clearFocus();
926     m_sheet->setFocus();
927   }
928   if (e->button() == Qt::LeftButton && e->modifiers() == Qt::ControlModifier) {
929     mouseDoubleClickEvent(e);
930     if (m_lineEdit->text() != "") {
931       m_lineEdit->setMouseDragEditing(true);
932       m_mouseXPosition = e->x();
933     }
934   } else if (e->button() == Qt::LeftButton &&
935              e->modifiers() == Qt::AltModifier) {
936     CellPosition cellPosition = getViewer()->xyToPosition(e->pos());
937     int row                   = cellPosition.frame();
938     int col                   = cellPosition.layer();
939     TDoubleParam *curve       = m_sheet->getCurve(col);
940     if (curve) {
941       KeyframeSetter::removeKeyframeAt(curve, row);
942     }
943   } else if (e->button() == Qt::LeftButton || e->button() == Qt::MidButton)
944     Spreadsheet::CellPanel::mousePressEvent(e);
945   else if (e->button() == Qt::RightButton) {
946     update();
947     openContextMenu(e);
948   }
949 }
950 
951 //-----------------------------------------------------------------------------
952 
mouseReleaseEvent(QMouseEvent * e)953 void FunctionSheetCellViewer::mouseReleaseEvent(QMouseEvent *e) {
954   if (m_lineEdit->getMouseDragEditing()) {
955     std::string textValue = m_lineEdit->text().toStdString();
956     onCellEditorEditingFinished();
957     m_lineEdit->setMouseDragEditing(false);
958   } else
959     Spreadsheet::CellPanel::mouseReleaseEvent(e);
960   /*
961   CellPosition cellPosition = getViewer ()->xyToPosition (e->pos ());
962 int row = cellPosition.frame ();
963 int col = cellPosition.layer ();
964 FunctionSheet::DragTool *dragTool = m_sheet->getDragTool();
965 if(dragTool) dragTool->release(row,col);
966 m_sheet->setDragTool(0);
967 */
968 }
969 
970 //-----------------------------------------------------------------------------
971 
mouseMoveEvent(QMouseEvent * e)972 void FunctionSheetCellViewer::mouseMoveEvent(QMouseEvent *e) {
973   if (m_lineEdit->getMouseDragEditing()) {
974     double newValue = m_currentValue + ((e->x() - m_mouseXPosition) / 2);
975     m_lineEdit->setText(QString::number(newValue, 'f', 4));
976     m_updatedValue = newValue;
977   } else
978     Spreadsheet::CellPanel::mouseMoveEvent(e);
979 }
980 
981 //-----------------------------------------------------------------------------
982 
onMouseMovedInLineEdit(QMouseEvent * event)983 void FunctionSheetCellViewer::onMouseMovedInLineEdit(QMouseEvent *event) {
984   if (m_lineEdit->getMouseDragEditing()) mouseMoveEvent(event);
985 }
986 
987 //-----------------------------------------------------------------------------
988 
989 // TODO: refactor: cfr functionpanel.cpp
openContextMenu(QMouseEvent * e)990 void FunctionSheetCellViewer::openContextMenu(QMouseEvent *e) {
991   QAction deleteKeyframeAction(tr("Delete Key"), 0);
992   QAction insertKeyframeAction(tr("Set Key"), 0);
993 
994   QStringList interpNames;
995   interpNames << tr("Constant Interpolation") << tr("Linear Interpolation")
996               << tr("Speed In / Speed Out Interpolation")
997               << tr("Ease In / Ease Out Interpolation")
998               << tr("Ease In / Ease Out (%) Interpolation")
999               << tr("Exponential Interpolation")
1000               << tr("Expression Interpolation") << tr("File Interpolation")
1001               << tr("Similar Shape Interpolation");
1002   QAction activateCycleAction(tr("Activate Cycle"), 0);
1003   QAction deactivateCycleAction(tr("Deactivate Cycle"), 0);
1004   QAction showIbtwnAction(tr("Show Inbetween Values"), 0);
1005   QAction hideIbtwnAction(tr("Hide Inbetween Values"), 0);
1006 
1007   CellPosition cellPosition = getViewer()->xyToPosition(e->pos());
1008   int row                   = cellPosition.frame();
1009   int col                   = cellPosition.layer();
1010   TDoubleParam *curve       = m_sheet->getCurve(col);
1011   if (!curve) return;
1012 
1013   bool isEmpty    = true;
1014   bool isKeyframe = false;
1015 
1016   // find the curve keyframe range
1017   int kCount = curve->getKeyframeCount();
1018   if (kCount > 0) {
1019     if (curve->keyframeIndexToFrame(0) <= row &&
1020         row <= curve->keyframeIndexToFrame(kCount - 1)) {
1021       isEmpty    = false;
1022       isKeyframe = curve->isKeyframe(row);
1023     }
1024   }
1025   int kIndex = curve->getPrevKeyframe(row);
1026 
1027   // if the FunctionSelection is not current or when clicking outside of the
1028   // selection, then select the clicked cell.
1029   FunctionSelection *selection = m_sheet->getSelection();
1030   if (!selection->getSelectedCells().contains(col, row)) {
1031     selection->makeCurrent();
1032     selection->selectCells(QRect(col, row, 1, 1));
1033   }
1034   CommandManager *cmdManager = CommandManager::instance();
1035 
1036   // build menu
1037   QMenu menu(0);
1038 
1039   // on clicking after last keyframe
1040   if (kCount > 0 && isEmpty && kIndex == kCount - 1) {
1041     if (curve->isCycleEnabled())
1042       menu.addAction(&deactivateCycleAction);
1043     else
1044       menu.addAction(&activateCycleAction);
1045   }
1046 
1047   if (!isKeyframe)  // menu.addAction(&deleteKeyframeAction); else
1048     menu.addAction(&insertKeyframeAction);
1049 
1050   // change interpolation commands
1051   QList<QAction *> interpActions;
1052   int interp = selection->getCommonSegmentType();
1053   if (interp != -1) {
1054     menu.addSeparator();
1055     QMenu *interpMenu = menu.addMenu(tr("Change Interpolation"));
1056     for (int i = (int)TDoubleKeyframe::Constant;
1057          i <= (int)TDoubleKeyframe::SimilarShape; i++) {
1058       if (interp != i) {
1059         QAction *interpAction = new QAction(interpNames[i - 1], 0);
1060         interpAction->setData(i);
1061         interpActions.append(interpAction);
1062         interpMenu->addAction(interpAction);
1063       }
1064     }
1065   }
1066 
1067   // change step commands
1068   int step = selection->getCommonStep();
1069   if (step != -1) {
1070     QMenu *stepMenu = menu.addMenu(tr("Change Step"));
1071     if (step != 1) stepMenu->addAction(cmdManager->getAction("MI_ResetStep"));
1072     if (step != 2) stepMenu->addAction(cmdManager->getAction("MI_Step2"));
1073     if (step != 3) stepMenu->addAction(cmdManager->getAction("MI_Step3"));
1074     if (step != 4) stepMenu->addAction(cmdManager->getAction("MI_Step4"));
1075   }
1076 
1077   menu.addSeparator();
1078 
1079   menu.addAction(cmdManager->getAction("MI_Cut"));
1080   menu.addAction(cmdManager->getAction("MI_Copy"));
1081   menu.addAction(cmdManager->getAction("MI_Paste"));
1082   menu.addAction(cmdManager->getAction("MI_Clear"));
1083 
1084   menu.addAction(cmdManager->getAction("MI_Insert"));
1085 
1086   if (!isEmpty && kIndex >= 0) {
1087     menu.addSeparator();
1088     if (m_sheet->isIbtwnValueVisible())
1089       menu.addAction(&hideIbtwnAction);
1090     else
1091       menu.addAction(&showIbtwnAction);
1092   }
1093 
1094   TSceneHandle *sceneHandle = m_sheet->getViewer()->getSceneHandle();
1095   // execute menu
1096   QAction *action = menu.exec(e->globalPos());  // QCursor::pos());
1097   if (action == &deleteKeyframeAction) {
1098     KeyframeSetter::removeKeyframeAt(curve, row);
1099   } else if (action == &insertKeyframeAction) {
1100     KeyframeSetter(curve).createKeyframe(row);
1101   } else if (interpActions.contains(action)) {
1102     selection->setSegmentType((TDoubleKeyframe::Type)action->data().toInt());
1103   } else if (action == &activateCycleAction)
1104     KeyframeSetter::enableCycle(curve, true, sceneHandle);
1105   else if (action == &deactivateCycleAction)
1106     KeyframeSetter::enableCycle(curve, false, sceneHandle);
1107   else if (action == &hideIbtwnAction)
1108     m_sheet->setIbtwnValueVisible(false);
1109   else if (action == &showIbtwnAction)
1110     m_sheet->setIbtwnValueVisible(true);
1111 
1112   update();
1113 }
1114 
1115 //********************************************************************************
1116 //    FunctionSheetColumnToCurveMapper  implementation
1117 //********************************************************************************
1118 
1119 class FunctionSheetColumnToCurveMapper final : public ColumnToCurveMapper {
1120   FunctionSheet *m_sheet;
1121 
1122 public:
FunctionSheetColumnToCurveMapper(FunctionSheet * sheet)1123   FunctionSheetColumnToCurveMapper(FunctionSheet *sheet) : m_sheet(sheet) {}
getCurve(int columnIndex) const1124   TDoubleParam *getCurve(int columnIndex) const override {
1125     FunctionTreeModel::Channel *channel = m_sheet->getChannel(columnIndex);
1126     if (channel)
1127       return channel->getParam();
1128     else
1129       return 0;
1130   }
1131 };
1132 
1133 //********************************************************************************
1134 //    FunctionSheet  implementation
1135 //********************************************************************************
1136 
FunctionSheet(QWidget * parent,bool isFloating)1137 FunctionSheet::FunctionSheet(QWidget *parent, bool isFloating)
1138     : SpreadsheetViewer(parent)
1139     , m_selectedCells()
1140     , m_selection(0)
1141     , m_isFloating(isFloating) {
1142   setColumnsPanel(m_columnHeadViewer = new FunctionSheetColumnHeadViewer(this));
1143   setRowsPanel(m_rowViewer = new FunctionSheetRowViewer(this));
1144   setCellsPanel(m_cellViewer = new FunctionSheetCellViewer(this));
1145 
1146   setWindowFlag(Qt::Window);
1147   setColumnCount(20);
1148   setWindowTitle(tr("Function Editor"));
1149   setFocusPolicy(Qt::ClickFocus);
1150 
1151   if (m_isFloating) {
1152     // load the dialog size
1153     TFilePath fp(ToonzFolder::getMyModuleDir() + TFilePath("popups.ini"));
1154     QSettings settings(toQString(fp), QSettings::IniFormat);
1155 
1156     setGeometry(settings.value("FunctionSpreadsheet", QRect(500, 500, 400, 300))
1157                     .toRect());
1158   }
1159 }
1160 
1161 //-----------------------------------------------------------------------------
1162 
~FunctionSheet()1163 FunctionSheet::~FunctionSheet() {
1164   if (m_isFloating) {
1165     TFilePath fp(ToonzFolder::getMyModuleDir() + TFilePath("popups.ini"));
1166     QSettings settings(toQString(fp), QSettings::IniFormat);
1167 
1168     settings.setValue("FunctionSpreadsheet", geometry());
1169   }
1170 }
1171 
1172 //-----------------------------------------------------------------------------
1173 
anyWidgetHasFocus()1174 bool FunctionSheet::anyWidgetHasFocus() {
1175   return hasFocus() || m_rowViewer->hasFocus() ||
1176          m_columnHeadViewer->hasFocus() || m_cellViewer->hasFocus();
1177 }
1178 
1179 //-----------------------------------------------------------------------------
1180 
setSelection(FunctionSelection * selection)1181 void FunctionSheet::setSelection(FunctionSelection *selection) {
1182   m_selection = selection;
1183   m_selection->setColumnToCurveMapper(
1184       new FunctionSheetColumnToCurveMapper(this));
1185 }
1186 
1187 //-----------------------------------------------------------------------------
1188 
showEvent(QShowEvent * e)1189 void FunctionSheet::showEvent(QShowEvent *e) {
1190   m_frameScroller.registerFrameScroller();
1191   SpreadsheetViewer::showEvent(e);
1192 }
1193 
1194 //-----------------------------------------------------------------------------
1195 
hideEvent(QHideEvent * e)1196 void FunctionSheet::hideEvent(QHideEvent *e) {
1197   m_frameScroller.unregisterFrameScroller();
1198   SpreadsheetViewer::hideEvent(e);
1199 }
1200 
1201 //-----------------------------------------------------------------------------
1202 
onFrameSwitched()1203 void FunctionSheet::onFrameSwitched() {
1204   setCurrentRow(getCurrentFrame());
1205   m_rowViewer->update();
1206   m_cellViewer->update();
1207 }
1208 
1209 //-----------------------------------------------------------------------------
1210 
setCurrentFrame(int frame)1211 void FunctionSheet::setCurrentFrame(int frame) {
1212   if (getFrameHandle()) getFrameHandle()->setFrame(frame);
1213 }
1214 
1215 //-----------------------------------------------------------------------------
1216 
getCurrentFrame() const1217 int FunctionSheet::getCurrentFrame() const {
1218   return getFrameHandle() ? getFrameHandle()->getFrame() : 0;
1219 }
1220 
1221 //-----------------------------------------------------------------------------
1222 
getChannelCount()1223 int FunctionSheet::getChannelCount() {
1224   if (m_functionTreeModel == 0)
1225     return 0;
1226   else
1227     return m_functionTreeModel->getActiveChannelCount();
1228 }
1229 
1230 //-----------------------------------------------------------------------------
1231 
getChannel(int column)1232 FunctionTreeModel::Channel *FunctionSheet::getChannel(int column) {
1233   if (m_functionTreeModel == 0)
1234     return 0;
1235   else
1236     return m_functionTreeModel->getActiveChannel(column);
1237 }
1238 
1239 //-----------------------------------------------------------------------------
1240 
getCurve(int column)1241 TDoubleParam *FunctionSheet::getCurve(int column) {
1242   FunctionTreeModel::Channel *channel = getChannel(column);
1243   return channel ? channel->getParam() : 0;
1244 }
1245 
1246 //-----------------------------------------------------------------------------
1247 
setModel(FunctionTreeModel * model)1248 void FunctionSheet::setModel(FunctionTreeModel *model) {
1249   m_functionTreeModel = model;
1250 }
1251 
1252 //-----------------------------------------------------------------------------
1253 
setViewer(FunctionViewer * viewer)1254 void FunctionSheet::setViewer(FunctionViewer *viewer) {
1255   m_functionViewer = viewer;
1256 }
1257 
1258 //-----------------------------------------------------------------------------
1259 
getSelectedCells() const1260 QRect FunctionSheet::getSelectedCells() const {
1261   if (getSelection())
1262     return getSelection()->getSelectedCells();
1263   else
1264     return QRect();
1265 }
1266 
1267 //-----------------------------------------------------------------------------
1268 
selectCells(const QRect & selectedCells)1269 void FunctionSheet::selectCells(const QRect &selectedCells) {
1270   m_selectedCells = selectedCells;
1271   if (getSelection()) {
1272     QList<TDoubleParam *> curves;
1273     for (int c = selectedCells.left(); c <= selectedCells.right(); c++) {
1274       TDoubleParam *param = 0;
1275       if (c < getChannelCount()) param = getChannel(c)->getParam();
1276       curves.push_back(param);
1277     }
1278     getSelection()->selectCells(selectedCells, curves);
1279 
1280     if (selectedCells.width() == 1 && curves[0] &&
1281         !getChannel(selectedCells.x())->isCurrent())
1282       getChannel(selectedCells.x())->setIsCurrent(true);
1283   }
1284 
1285   updateAll();
1286 }
1287 
1288 //-----------------------------------------------------------------------------
1289 
updateAll()1290 void FunctionSheet::updateAll() {
1291   m_rowViewer->update();
1292   m_columnHeadViewer->update();
1293   m_cellViewer->update();
1294   setColumnCount(getChannelCount());
1295 }
1296 
1297 //-----------------------------------------------------------------------------
1298 /*! Display expression name of the current segment
1299  */
getSelectedParamName()1300 QString FunctionSheet::getSelectedParamName() {
1301   if (m_functionTreeModel->getCurrentChannel())
1302     return m_functionTreeModel->getCurrentChannel()->getExprRefName();
1303   else
1304     return QString();
1305 }
1306 
1307 //-----------------------------------------------------------------------------
1308 
getColumnIndexByCurve(TDoubleParam * param) const1309 int FunctionSheet::getColumnIndexByCurve(TDoubleParam *param) const {
1310   return m_functionTreeModel->getColumnIndexByCurve(param);
1311 }
1312 
1313 //-----------------------------------------------------------------------------
1314 /*! scroll column to show the current one
1315  */
onCurrentChannelChanged(FunctionTreeModel::Channel * channel)1316 void FunctionSheet::onCurrentChannelChanged(
1317     FunctionTreeModel::Channel *channel) {
1318   if (!channel) return;
1319   for (int c = 0; c < getChannelCount(); c++) {
1320     FunctionTreeModel::Channel *tmpChan = getChannel(c);
1321 
1322     if (tmpChan == channel) {
1323       ensureVisibleCol(c);
1324       return;
1325     }
1326   }
1327 }
1328 
1329 //-----------------------------------------------------------------------------
1330 /*! Obtains a pointer to the stage object containing the parameter of specified
1331  * column
1332  */
getStageObject(int column)1333 TStageObject *FunctionSheet::getStageObject(int column) {
1334   FunctionTreeModel::Channel *channel = getChannel(column);
1335   if (!channel) return nullptr;
1336 
1337   FunctionTreeModel::ChannelGroup *channelGroup = channel->getChannelGroup();
1338   if (!channelGroup) return nullptr;
1339 
1340   // returns nullptr if the channel is a fx parameter
1341   StageObjectChannelGroup *stageItem =
1342       dynamic_cast<StageObjectChannelGroup *>(channelGroup);
1343   if (!stageItem) return nullptr;
1344 
1345   return stageItem->getStageObject();
1346 }