#include "toonzqt/functionsheet.h" // TnzQt includes #include "toonzqt/gutil.h" #include "toonzqt/functionviewer.h" // TnzLib includes #include "toonz/tframehandle.h" #include "toonz/doubleparamcmd.h" #include "toonz/preferences.h" // TnzBase includes #include "tunit.h" #include "../toonz/menubarcommandids.h" // Qt includes #include #include #include #include #include //for drag&drop #include //******************************************************************************** // Local namespace stuff //******************************************************************************** namespace { const int cColumnDragHandleWidth = 8; const int cGroupShortNameY = 0; //!< Column header's y pos for channel groups' short name tabs const int cGroupLongNameY = 27; //!< Same for its long name tabs const int cChannelNameY = 50; //!< Column header's y pos of channel name tabs, //! up to the widget's height const int cColHeadersEndY = 87; //!< End of column headers y pos } // namespace //******************************************************************************** // Function Sheet Tools //******************************************************************************** /*--- NumericalColumnsのセグメントの左側のバーをクリックしたとき ---*/ class MoveChannelsDragTool final : public Spreadsheet::DragTool { FunctionSheet *m_sheet; std::vector m_setters; int m_oldRow; QRect m_selectedCells; int m_firstKeyframeRow; public: MoveChannelsDragTool(FunctionSheet *sheet) : m_sheet(sheet), m_firstKeyframeRow(-1) {} void click(int row, int col, QMouseEvent *e) override { m_firstKeyframeRow = -1; FunctionTreeModel::Channel *channel = m_sheet->getChannel(col); if (!channel) return; TDoubleParam *curve = channel->getParam(); int k0 = -1, k1 = -1; if (curve->isKeyframe(row)) k0 = k1 = curve->getClosestKeyframe(row); else { k0 = curve->getPrevKeyframe(row); k1 = curve->getNextKeyframe(row); } // return if clicking outside of the segments if (k0 < 0 || k1 < 0) return; int r0 = tround(curve->keyframeIndexToFrame(k0)); int r1 = tround(curve->keyframeIndexToFrame(k1)); if (m_sheet->isSelectedCell(row, col)) { m_selectedCells = m_sheet->getSelectedCells(); m_selectedCells.setTop(r0); m_selectedCells.setBottom(r1); } else m_selectedCells = QRect(col, r0, 1, r1 - r0 + 1); m_sheet->selectCells(m_selectedCells); /*--- シンプルに左のバーをクリックした場合はcolは1つだけ。 すでに複数Columnが選択されている上でその選択範囲内のセルの左バーをクリックした場合は 横(Column)幅は選択範囲に順ずるようになる。高さ(row)はクリックしたセグメントと同じになる。 ---*/ /*--- セグメントごとドラッグに備えてKeyFrameを格納する ---*/ for (int col = m_selectedCells.left(); col <= m_selectedCells.right(); ++col) { TDoubleParam *curve = m_sheet->getCurve(col); if (!curve) continue; KeyframeSetter *setter = new KeyframeSetter(curve); for (int k = 0; k < curve->getKeyframeCount(); k++) { int row = (int)curve->keyframeIndexToFrame(k); if (r0 <= row && row <= r1) { if (m_firstKeyframeRow < 0 || row < m_firstKeyframeRow) m_firstKeyframeRow = row; setter->selectKeyframe(k); } } m_setters.push_back(setter); } m_oldRow = row; } void drag(int row, int col, QMouseEvent *e) override { int d = row - m_oldRow; m_oldRow = row; if (d + m_firstKeyframeRow < 0) d = -m_firstKeyframeRow; m_firstKeyframeRow += d; for (int i = 0; i < (int)m_setters.size(); i++) m_setters[i]->moveKeyframes(d, 0.0); m_selectedCells.translate(0, d); m_sheet->selectCells(m_selectedCells); } void release(int row, int col, QMouseEvent *e) override { for (int i = 0; i < (int)m_setters.size(); i++) delete m_setters[i]; m_setters.clear(); } }; //----------------------------------------------------------------------------- /*--- NumericalColumnsのセル部分をクリックしたとき ---*/ class FunctionSheetSelectionTool final : public Spreadsheet::DragTool { int m_firstRow, m_firstCol; FunctionSheet *m_sheet; public: FunctionSheetSelectionTool(FunctionSheet *sheet) : m_sheet(sheet), m_firstRow(-1), m_firstCol(-1) {} void click(int row, int col, QMouseEvent *e) override { if (0 != (e->modifiers() & Qt::ShiftModifier) && !m_sheet->getSelectedCells().isEmpty()) { QRect selectedCells = m_sheet->getSelectedCells(); if (col < selectedCells.center().x()) { m_firstCol = selectedCells.right(); selectedCells.setLeft(col); } else { m_firstCol = selectedCells.left(); selectedCells.setRight(col); } if (row < selectedCells.center().y()) { m_firstRow = selectedCells.bottom(); selectedCells.setTop(row); } else { m_firstRow = selectedCells.top(); selectedCells.setBottom(row); } m_sheet->selectCells(selectedCells); } else { // regular click, no shift m_firstCol = col; m_firstRow = row; QRect selectedCells(col, row, 1, 1); m_sheet->selectCells(selectedCells); } } void drag(int row, int col, QMouseEvent *e) override { if (row < 0) row = 0; if (col < 0) col = 0; int r0 = qMin(row, m_firstRow); int r1 = qMax(row, m_firstRow); int c0 = qMin(col, m_firstCol); int c1 = qMax(col, m_firstCol); QRect selectedCells(c0, r0, c1 - c0 + 1, r1 - r0 + 1); m_sheet->selectCells(selectedCells); } void release(int row, int col, QMouseEvent *e) override { if (row == m_firstRow && col == m_firstCol) { if (Preferences::instance()->isMoveCurrentEnabled()) m_sheet->setCurrentFrame(row); FunctionTreeModel::Channel *channel = m_sheet->getChannel(col); if (channel) channel->setIsCurrent(true); } } }; //******************************************************************************** // FunctionSheetRowViewer implementation //******************************************************************************** FunctionSheetRowViewer::FunctionSheetRowViewer(FunctionSheet *parent) : Spreadsheet::RowPanel(parent), m_sheet(parent) { setMinimumSize(QSize(100, 100)); setMouseTracking(true); setFocusPolicy(Qt::NoFocus); } //----------------------------------------------------------------------------- void FunctionSheetRowViewer::paintEvent(QPaintEvent *e) { // calls GenericPanel's event Spreadsheet::RowPanel::paintEvent(e); } //----------------------------------------------------------------------------- void FunctionSheetRowViewer::mousePressEvent(QMouseEvent *e) { // calls GenericPanel's event Spreadsheet::RowPanel::mousePressEvent(e); } //----------------------------------------------------------------------------- void FunctionSheetRowViewer::mouseMoveEvent(QMouseEvent *e) { // calls GenericPanel's event Spreadsheet::RowPanel::mouseMoveEvent(e); } //----------------------------------------------------------------------------- void FunctionSheetRowViewer::mouseReleaseEvent(QMouseEvent *e) { Spreadsheet::RowPanel::mouseReleaseEvent(e); } //----------------------------------------------------------------------------- void FunctionSheetRowViewer::contextMenuEvent(QContextMenuEvent *event) { QMenu *menu = new QMenu(this); CommandManager *cmdManager = CommandManager::instance(); menu->addAction(cmdManager->getAction(MI_InsertSceneFrame)); menu->addAction(cmdManager->getAction(MI_RemoveSceneFrame)); menu->addAction(cmdManager->getAction(MI_InsertGlobalKeyframe)); menu->addAction(cmdManager->getAction(MI_RemoveGlobalKeyframe)); menu->exec(event->globalPos()); } //******************************************************************************** // FunctionSheetColumnHeadViewer implementation //******************************************************************************** FunctionSheetColumnHeadViewer::FunctionSheetColumnHeadViewer( FunctionSheet *parent) : Spreadsheet::ColumnPanel(parent), m_sheet(parent), m_draggingChannel(0) { setMouseTracking(true); // for updating the tooltip setFocusPolicy(Qt::NoFocus); } //----------------------------------------------------------------------------- void FunctionSheetColumnHeadViewer::paintEvent(QPaintEvent *e) { QPainter painter(this); QRect toBeUpdated = e->rect(); painter.setClipRect(toBeUpdated); // visible columns range CellRange visible = getViewer()->xyRectToRange(toBeUpdated); int c0 = visible.from().layer(); int c1 = visible.to().layer(); if (c0 > c1) return; int n = c1 - c0 + 1 + 2; FunctionTreeModel::ChannelGroup *currentGroup = 0; /*--- Display range + right and left 1 column range ChannelGroup. If there is * none, put 0 * ---*/ std::vector channelGroups(n); for (int i = 0; i < n; ++i) { channelGroups[i] = 0; int c = c0 - 1 + i; if (c < 0) continue; FunctionTreeModel::Channel *channel = m_sheet->getChannel(c); if (!channel) continue; channelGroups[i] = channel->getChannelGroup(); if (!currentGroup && channel->isCurrent()) currentGroup = channelGroups[i]; } int y0 = 0; int y1 = 17; // needs work int y2 = 34; int y3 = 53; /*--- Fill header with background color ---*/ for (int c = c0; c <= c1; c++) { FunctionTreeModel::Channel *channel = m_sheet->getChannel(c); if (!channel) continue; int x0 = getViewer()->columnToX(c); int x1 = getViewer()->columnToX(c + 1) - 1; // Column Width int width = x1 - x0 + 1; painter.fillRect(x0, y0, width, y3 - y0, getViewer()->getBGColor()); } for (int c = c0; c <= c1; ++c) { FunctionTreeModel::Channel *channel = m_sheet->getChannel(c); if (!channel) continue; int i = c - c0 + 1; /*---- Channel Column of the current Column and the preceding and following * Columns ---*/ FunctionTreeModel::ChannelGroup *prevGroup = channelGroups[c - c0], *group = channelGroups[c - c0 + 1], *nextGroup = channelGroups[c - c0 + 2]; /*---- If the group is different from the before and after, flags are set * respectively ---*/ bool firstGroupColumn = prevGroup != group; bool lastGroupColumn = nextGroup != group; /*--- The left and right coordinates of the current column ---*/ int x0 = getViewer()->columnToX(c); int x1 = getViewer()->columnToX(c + 1) - 1; // Column width int width = x1 - x0 + 1; QRect selectedRect = m_sheet->getSelectedCells(); bool isSelected = (selectedRect.left() <= c && c <= selectedRect.right()) ? true : false; // paint with light color if selected if (isSelected) painter.fillRect(x0, y1, width, y3 - y1, getViewer()->getCurrentRowBgColor()); // draw horizonntal lines painter.setPen(QPen(getViewer()->getColumnHeaderBorderColor(), 3)); painter.drawLine(x0, y0, x1, y0); painter.setPen(getViewer()->getColumnHeaderBorderColor()); painter.drawLine(x0, y1, x1, y1); // draw vertical bar painter.fillRect(x0, y1, 6, y3 - y1, getViewer()->getColumnHeaderBorderColor()); if (firstGroupColumn) painter.fillRect(x0, y0, 6, y1 - y0, getViewer()->getColumnHeaderBorderColor()); // channel name painter.setPen(getViewer()->getTextColor()); if (channel->isCurrent()) painter.setPen(getViewer()->getSelectedColumnTextColor()); QString text = channel->getShortName(); int d = 8; painter.drawText(x0 + d, y1, width - d, y3 - y1 + 1, Qt::TextWrapAnywhere | Qt::AlignLeft | Qt::AlignVCenter, text); // group name if (firstGroupColumn) { int tmpwidth = (lastGroupColumn) ? width : width * 2; painter.setPen(getViewer()->getTextColor()); if (group == currentGroup) painter.setPen(getViewer()->getSelectedColumnTextColor()); text = group->getShortName(); painter.drawText(x0 + d, y0, tmpwidth - d, y1 - y0 + 1, Qt::AlignLeft | Qt::AlignVCenter, text); } } } //----------------------------------------------------------------------------- /*! update tooltips */ void FunctionSheetColumnHeadViewer::mouseMoveEvent(QMouseEvent *e) { if ((e->buttons() & Qt::MidButton) && m_draggingChannel && (e->pos() - m_dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; mimeData->setText(m_draggingChannel->getExprRefName()); drag->setMimeData(mimeData); static const QPixmap cursorPixmap(":Resources/dragcursor_exp_text.png"); drag->setDragCursor(cursorPixmap, Qt::MoveAction); Qt::DropAction dropAction = drag->exec(); return; } // get the column under the cursor int col = getViewer()->xyToPosition(e->pos()).layer(); FunctionTreeModel::Channel *channel = m_sheet->getChannel(col); if (!channel) { setToolTip(QString("")); } else setToolTip(channel->getExprRefName()); } //----------------------------------------------------------------------------- void FunctionSheetColumnHeadViewer::mousePressEvent(QMouseEvent *e) { QPoint pos = e->pos(); int currentC = getViewer()->xyToPosition(pos).layer(); FunctionTreeModel::Channel *channel; for (int c = 0; c <= m_sheet->getChannelCount(); c++) { channel = m_sheet->getChannel(c); if (!channel || c != currentC) continue; break; } if (channel && e->button() == Qt::MidButton) { m_draggingChannel = channel; m_dragStartPosition = e->pos(); return; } else if (channel) channel->setIsCurrent(true); m_draggingChannel = 0; if (!channel) return; // Open folder FunctionTreeModel::ChannelGroup *channelGroup = channel->getChannelGroup(); if (!channelGroup->isOpen()) channelGroup->getModel()->setExpandedItem(channelGroup->createIndex(), true); // Select all segment std::set frames; channel->getParam()->getKeyframes(frames); QRect rect(0, 0, 0, 0); if (!frames.empty()) rect = QRect(currentC, 0, 1, (*frames.rbegin()) + 1); getViewer()->selectCells(rect); } //----------------------------------------------------------------------------- void FunctionSheetColumnHeadViewer::contextMenuEvent(QContextMenuEvent *ce) { // First, select column under cursor const QPoint &pos = ce->pos(); int cursorCol = getViewer()->xyToPosition(pos).layer(); if (cursorCol < 0 || cursorCol >= m_sheet->getChannelCount()) return; FunctionTreeModel::Channel *channel = m_sheet->getChannel(cursorCol); if (!channel) return; // Ok, now let's summon a context menu with appropriate options FunctionViewer *fv = m_sheet->getViewer(); if (!fv) { assert(fv); return; } const QPoint &globalPos = mapToGlobal(pos); if (pos.y() >= cChannelNameY) fv->openContextMenu(channel, globalPos); else { FunctionTreeModel::ChannelGroup *group = channel->getChannelGroup(); // In this case, commands are different from the tree view. Rather than // showing in the tree, // channels get ACTIVATED QMenu menu; QAction showAnimatedOnly(FunctionTreeView::tr("Show Animated Only"), 0); QAction showAll(FunctionTreeView::tr("Show All"), 0); menu.addAction(&showAnimatedOnly); menu.addAction(&showAll); // execute menu QAction *action = menu.exec(globalPos); if (action != &showAll && action != &showAnimatedOnly) return; // Process action if (action == &showAll) { int c, cCount = group->getChildCount(); for (c = 0; c != cCount; ++c) { FunctionTreeModel::Channel *channel = dynamic_cast(group->getChild(c)); if (channel && !channel->isHidden()) channel->setIsActive(true); } } else if (action == &showAnimatedOnly) { int c, cCount = group->getChildCount(); for (c = 0; c != cCount; ++c) { FunctionTreeModel::Channel *channel = dynamic_cast(group->getChild(c)); if (channel && !channel->isHidden()) channel->setIsActive(channel->isAnimated()); } } fv->update(); } } //******************************************************************************** // FunctionSheetCellViewer implementation //******************************************************************************** FunctionSheetCellViewer::FunctionSheetCellViewer(FunctionSheet *parent) : Spreadsheet::CellPanel(parent) , m_sheet(parent) , m_editRow(0) , m_editCol(0) { m_lineEdit = new DVGui::LineEdit(this); // lineEdit->setGeometry(10,10,100,30); m_lineEdit->hide(); bool ret = connect(m_lineEdit, SIGNAL(editingFinished()), this, SLOT(onCellEditorEditingFinished())); ret = ret && connect(m_lineEdit, SIGNAL(mouseMoved(QMouseEvent *)), this, SLOT(onMouseMovedInLineEdit(QMouseEvent *))); assert(ret); setMouseTracking(true); setFocusProxy(m_lineEdit); } //----------------------------------------------------------------------------- /*! Called when the cell panel is left/right-clicked */ Spreadsheet::DragTool *FunctionSheetCellViewer::createDragTool(QMouseEvent *e) { CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); int row = cellPosition.frame(); int col = cellPosition.layer(); bool isEmpty = true; TDoubleParam *curve = m_sheet->getCurve(col); if (curve) { int kCount = curve->getKeyframeCount(); if (kCount > 0) { int row0 = (int)curve->keyframeIndexToFrame(0); int row1 = (int)curve->keyframeIndexToFrame(kCount - 1); isEmpty = row < row0 || row > row1; } } if (!isEmpty) { int x = e->pos().x() - getViewer()->columnToX(col); if (0 <= x && x < cColumnDragHandleWidth + 9) return new MoveChannelsDragTool(m_sheet); } return new FunctionSheetSelectionTool(m_sheet); // return Spreadsheet::CellPanel::createDragTool(e); } //----------------------------------------------------------------------------- void FunctionSheetCellViewer::drawCells(QPainter &painter, int r0, int c0, int r1, int c1) { // key frames QColor KeyFrameColor = getViewer()->getKeyFrameColor(); QColor KeyFrameBorderColor = getViewer()->getKeyFrameBorderColor(); QColor SelectedKeyFrameColor = getViewer()->getSelectedKeyFrameColor(); // inbetween QColor InBetweenColor = getViewer()->getInBetweenColor(); QColor InBetweenBorderColor = getViewer()->getInBetweenBorderColor(); QColor SelectedInBetweenColor = getViewer()->getSelectedInBetweenColor(); // empty cells QColor SelectedEmptyColor = getViewer()->getSelectedEmptyColor(); // empty cells in scene frame range QColor SelectedSceneRangeEmptyColor = getViewer()->getSelectedSceneRangeEmptyColor(); // top and bottom pos int y0 = getViewer()->rowToY(r0); int y1 = getViewer()->rowToY(r1 + 1) - 1; for (int c = c0; c <= c1; c++) { TDoubleParam *curve = m_sheet->getCurve(c); /*--- もしカラムcにパラメータが無ければcurveには0が返る ---*/ if (!curve) continue; // left and right pos int x0 = getViewer()->columnToX(c); int x1 = getViewer()->columnToX(c + 1) - 1; // find the curve keyframe range int kr0 = 0, kr1 = -1; int kCount = curve->getKeyframeCount(); if (kCount > 0) { kr0 = curve->keyframeIndexToFrame(0); kr1 = curve->keyframeIndexToFrame(kCount - 1); } // get the unit TMeasure *measure = curve->getMeasure(); const TUnit *unit = measure ? measure->getCurrentUnit() : 0; // draw each cell for (int row = r0; row <= r1; row++) { int ya = m_sheet->rowToY(row); int yb = m_sheet->rowToY(row + 1) - 1; bool isSelected = getViewer()->isSelectedCell(row, c); double value = curve->getValue(row); if (unit) value = unit->convertTo(value); QRect cellRect(x0, ya, x1 - x0 + 1, yb - ya + 1); QRect borderRect(x0, ya, 7, yb - ya + 1); QColor cellColor, borderColor; /*--- キーフレーム間の範囲だけ色をつける ---*/ if (kr0 <= row && row <= kr1) { if (curve->isKeyframe(row)) { cellColor = (isSelected) ? SelectedKeyFrameColor : KeyFrameColor; borderColor = KeyFrameBorderColor; } else { cellColor = (isSelected) ? SelectedInBetweenColor : InBetweenColor; borderColor = InBetweenBorderColor; } painter.setPen(Qt::NoPen); painter.fillRect(cellRect, cellColor); painter.fillRect(borderRect, borderColor); // display whether segment are Linked if (curve->isKeyframe(row)) { TDoubleKeyframe kf = curve->getKeyframeAt(row); // if the segments are NOT linked, then cut off the side bar if (!kf.m_linkedHandles) { int rowCenterPos = (ya + yb) / 2; QPoint points[4] = { QPoint(x0, rowCenterPos), QPoint(x0 + 7, rowCenterPos + 3), QPoint(x0 + 7, rowCenterPos - 3), QPoint(x0, rowCenterPos)}; QBrush oldBrush = painter.brush(); painter.setBrush(QBrush(cellColor)); painter.drawPolygon(points, 4); painter.setBrush(oldBrush); } } // draw cell value painter.setPen(getViewer()->getTextColor()); /*--- 整数から小数点以下3桁以内の場合はそれ以降の0000を描かない ---*/ QString text; double thousandValue = value * 1000.0; if (areAlmostEqual(thousandValue, (double)tround(thousandValue), 0.0001)) { text = QString::number(value, 'f', 3); while (text.endsWith("0")) { text.chop(1); if (text.endsWith(".")) { text.chop(1); break; } } } else { text = QString::number(value, 'f', 4); text.truncate(5); text.append("~"); } QString fontName = Preferences::instance()->getInterfaceFont(); if (fontName == "") { #ifdef _WIN32 fontName = "Arial"; #else fontName = "Helvetica"; #endif } static QFont font(fontName, -1, QFont::Bold); font.setPixelSize(12); painter.setFont(font); painter.drawText(cellRect.adjusted(10, 0, 0, 0), Qt::AlignVCenter | Qt::AlignLeft, text); } // empty and selected cell else if (isSelected) { int rowCount = getViewer()->getRowCount(); cellColor = (row >= rowCount) ? SelectedEmptyColor : SelectedSceneRangeEmptyColor; painter.setPen(Qt::NoPen); painter.fillRect(cellRect, cellColor); } } } } //----------------------------------------------------------------------------- void FunctionSheetCellViewer::mouseDoubleClickEvent(QMouseEvent *e) { CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); int row = cellPosition.frame(); int col = cellPosition.layer(); int x0, y0, x1, y1; x0 = getViewer()->columnToX(col); x1 = getViewer()->columnToX(col + 1) - 1; y0 = getViewer()->rowToY(row); y1 = getViewer()->rowToY(row + 1) - 1; m_editRow = row; m_editCol = col; TDoubleParam *curve = m_sheet->getCurve(col); if (curve) { double v = curve->getValue(row); TMeasure *measure = curve->getMeasure(); const TUnit *unit = measure ? measure->getCurrentUnit() : 0; if (unit) v = unit->convertTo(v); m_currentValue = v; m_lineEdit->setText(QString::number(v, 'f', 4)); // in order to put the cursor to the left end m_lineEdit->setSelection(m_lineEdit->text().length(), -m_lineEdit->text().length()); } else m_lineEdit->setText(""); QString fontName = Preferences::instance()->getInterfaceFont(); if (fontName == "") { #ifdef _WIN32 fontName = "Arial"; #else fontName = "Helvetica"; #endif } static QFont font(fontName, 9, QFont::Normal); m_lineEdit->setFont(font); m_lineEdit->setGeometry(x0 - 2, y0 - 2, x1 - x0 + 1 + 4, y1 - y0 + 1 + 4); // x0,y0,x1-x0+1,y0-y1+1); m_lineEdit->show(); m_lineEdit->raise(); m_lineEdit->setFocus(); } //----------------------------------------------------------------------------- void FunctionSheetCellViewer::onCellEditorEditingFinished() { QString text = m_lineEdit->text(); if (!text.isEmpty() && (m_lineEdit->isReturnPressed() || m_lineEdit->getMouseDragEditing())) { double value = text.toDouble(); TDoubleParam *curve = m_sheet->getCurve(m_editCol); if (curve) { TMeasure *measure = curve->getMeasure(); const TUnit *unit = measure ? measure->getCurrentUnit() : 0; if (unit) value = unit->convertFrom(value); KeyframeSetter::setValue(curve, m_editRow, value); } } m_lineEdit->hide(); m_lineEdit->clearFocus(); m_sheet->setFocus(); update(); } //----------------------------------------------------------------------------- void FunctionSheetCellViewer::mousePressEvent(QMouseEvent *e) { // escape from the line edit by clicking outside if (m_lineEdit->isVisible()) { m_lineEdit->hide(); m_lineEdit->clearFocus(); m_sheet->setFocus(); } if (e->button() == Qt::LeftButton && e->modifiers() == Qt::ControlModifier) { mouseDoubleClickEvent(e); if (m_lineEdit->text() != "") { m_lineEdit->setMouseDragEditing(true); m_mouseXPosition = e->x(); } } else if (e->button() == Qt::LeftButton && e->modifiers() == Qt::AltModifier) { CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); int row = cellPosition.frame(); int col = cellPosition.layer(); TDoubleParam *curve = m_sheet->getCurve(col); if (curve) { KeyframeSetter::removeKeyframeAt(curve, row); } } else if (e->button() == Qt::LeftButton || e->button() == Qt::MidButton) Spreadsheet::CellPanel::mousePressEvent(e); else if (e->button() == Qt::RightButton) { update(); openContextMenu(e); } } //----------------------------------------------------------------------------- void FunctionSheetCellViewer::mouseReleaseEvent(QMouseEvent *e) { if (m_lineEdit->getMouseDragEditing()) { std::string textValue = m_lineEdit->text().toStdString(); onCellEditorEditingFinished(); m_lineEdit->setMouseDragEditing(false); } else Spreadsheet::CellPanel::mouseReleaseEvent(e); /* CellPosition cellPosition = getViewer ()->xyToPosition (e->pos ()); int row = cellPosition.frame (); int col = cellPosition.layer (); FunctionSheet::DragTool *dragTool = m_sheet->getDragTool(); if(dragTool) dragTool->release(row,col); m_sheet->setDragTool(0); */ } //----------------------------------------------------------------------------- void FunctionSheetCellViewer::mouseMoveEvent(QMouseEvent *e) { if (m_lineEdit->getMouseDragEditing()) { double newValue = m_currentValue + ((e->x() - m_mouseXPosition) / 2); m_lineEdit->setText(QString::number(newValue, 'f', 4)); m_updatedValue = newValue; } else Spreadsheet::CellPanel::mouseMoveEvent(e); } //----------------------------------------------------------------------------- void FunctionSheetCellViewer::onMouseMovedInLineEdit(QMouseEvent *event) { if (m_lineEdit->getMouseDragEditing()) mouseMoveEvent(event); } //----------------------------------------------------------------------------- // TODO: refactor: cfr functionpanel.cpp void FunctionSheetCellViewer::openContextMenu(QMouseEvent *e) { struct locals { static void sheet__setSegmentType(FunctionSelection *selection, TDoubleParam *curve, int segmentIndex, TDoubleKeyframe::Type type) { selection->selectSegment(curve, segmentIndex); KeyframeSetter setter(curve, segmentIndex); setter.setType(type); } }; // locals QAction deleteKeyframeAction(tr("Delete Key"), 0); QAction insertKeyframeAction(tr("Set Key"), 0); QAction setLinearAction(tr("Linear Interpolation"), 0); QAction setSpeedInOutAction(tr("Speed In / Speed Out Interpolation"), 0); QAction setEaseInOutAction(tr("Ease In / Ease Out Interpolation"), 0); QAction setEaseInOut2Action(tr("Ease In / Ease Out (%) Interpolation"), 0); QAction setExponentialAction(tr("Exponential Interpolation"), 0); QAction setExpressionAction(tr("Expression Interpolation"), 0); QAction setFileAction(tr("File Interpolation"), 0); QAction setConstantAction(tr("Constant Interpolation"), 0); QAction setStep1Action(tr("Step 1"), 0); QAction setStep2Action(tr("Step 2"), 0); QAction setStep3Action(tr("Step 3"), 0); QAction setStep4Action(tr("Step 4"), 0); CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); int row = cellPosition.frame(); int col = cellPosition.layer(); TDoubleParam *curve = m_sheet->getCurve(col); if (!curve) return; bool isEmpty = true; bool isKeyframe = false; // find the curve keyframe range int kCount = curve->getKeyframeCount(); if (kCount > 0) { if (curve->keyframeIndexToFrame(0) <= row && row <= curve->keyframeIndexToFrame(kCount - 1)) { isEmpty = false; isKeyframe = curve->isKeyframe(row); } } int kIndex = curve->getPrevKeyframe(row); // build menu QMenu menu(0); if (!isKeyframe) // menu.addAction(&deleteKeyframeAction); else menu.addAction(&insertKeyframeAction); if (!isEmpty && !isKeyframe && kIndex >= 0) { menu.addSeparator(); TDoubleKeyframe kf = curve->getKeyframe(kIndex); if (kf.m_type != TDoubleKeyframe::Linear) menu.addAction(&setLinearAction); if (kf.m_type != TDoubleKeyframe::SpeedInOut) menu.addAction(&setSpeedInOutAction); if (kf.m_type != TDoubleKeyframe::EaseInOut) menu.addAction(&setEaseInOutAction); if (kf.m_type != TDoubleKeyframe::EaseInOutPercentage) menu.addAction(&setEaseInOut2Action); if (kf.m_type != TDoubleKeyframe::Exponential) menu.addAction(&setExponentialAction); if (kf.m_type != TDoubleKeyframe::Expression) menu.addAction(&setExpressionAction); if (kf.m_type != TDoubleKeyframe::File) menu.addAction(&setFileAction); if (kf.m_type != TDoubleKeyframe::Constant) menu.addAction(&setConstantAction); menu.addSeparator(); if (kf.m_step != 1) menu.addAction(&setStep1Action); if (kf.m_step != 2) menu.addAction(&setStep2Action); if (kf.m_step != 3) menu.addAction(&setStep3Action); if (kf.m_step != 4) menu.addAction(&setStep4Action); } menu.addSeparator(); CommandManager *cmdManager = CommandManager::instance(); menu.addAction(cmdManager->getAction("MI_Cut")); menu.addAction(cmdManager->getAction("MI_Copy")); menu.addAction(cmdManager->getAction("MI_Paste")); menu.addAction(cmdManager->getAction("MI_Clear")); menu.addAction(cmdManager->getAction("MI_Insert")); FunctionSelection *selection = m_sheet->getSelection(); // execute menu QAction *action = menu.exec(e->globalPos()); // QCursor::pos()); if (action == &deleteKeyframeAction) { KeyframeSetter::removeKeyframeAt(curve, row); } else if (action == &insertKeyframeAction) { KeyframeSetter(curve).createKeyframe(row); } else if (action == &setLinearAction) locals::sheet__setSegmentType(selection, curve, kIndex, TDoubleKeyframe::Linear); else if (action == &setSpeedInOutAction) locals::sheet__setSegmentType(selection, curve, kIndex, TDoubleKeyframe::SpeedInOut); else if (action == &setEaseInOutAction) locals::sheet__setSegmentType(selection, curve, kIndex, TDoubleKeyframe::EaseInOut); else if (action == &setEaseInOut2Action) locals::sheet__setSegmentType(selection, curve, kIndex, TDoubleKeyframe::EaseInOutPercentage); else if (action == &setExponentialAction) locals::sheet__setSegmentType(selection, curve, kIndex, TDoubleKeyframe::Exponential); else if (action == &setExpressionAction) locals::sheet__setSegmentType(selection, curve, kIndex, TDoubleKeyframe::Expression); else if (action == &setFileAction) locals::sheet__setSegmentType(selection, curve, kIndex, TDoubleKeyframe::File); else if (action == &setConstantAction) locals::sheet__setSegmentType(selection, curve, kIndex, TDoubleKeyframe::Constant); else if (action == &setStep1Action) KeyframeSetter(curve, kIndex).setStep(1); else if (action == &setStep2Action) KeyframeSetter(curve, kIndex).setStep(2); else if (action == &setStep3Action) KeyframeSetter(curve, kIndex).setStep(3); else if (action == &setStep4Action) KeyframeSetter(curve, kIndex).setStep(4); update(); } //******************************************************************************** // FunctionSheetColumnToCurveMapper implementation //******************************************************************************** class FunctionSheetColumnToCurveMapper final : public ColumnToCurveMapper { FunctionSheet *m_sheet; public: FunctionSheetColumnToCurveMapper(FunctionSheet *sheet) : m_sheet(sheet) {} TDoubleParam *getCurve(int columnIndex) const override { FunctionTreeModel::Channel *channel = m_sheet->getChannel(columnIndex); if (channel) return channel->getParam(); else return 0; } }; //******************************************************************************** // FunctionSheet implementation //******************************************************************************** FunctionSheet::FunctionSheet(QWidget *parent) : SpreadsheetViewer(parent), m_selectedCells(), m_selection(0) { setColumnsPanel(m_columnHeadViewer = new FunctionSheetColumnHeadViewer(this)); setRowsPanel(m_rowViewer = new FunctionSheetRowViewer(this)); setCellsPanel(m_cellViewer = new FunctionSheetCellViewer(this)); setColumnCount(20); } //----------------------------------------------------------------------------- FunctionSheet::~FunctionSheet() {} //----------------------------------------------------------------------------- bool FunctionSheet::anyWidgetHasFocus() { return hasFocus() || m_rowViewer->hasFocus() || m_columnHeadViewer->hasFocus() || m_cellViewer->hasFocus(); } //----------------------------------------------------------------------------- void FunctionSheet::setSelection(FunctionSelection *selection) { m_selection = selection; m_selection->setColumnToCurveMapper( new FunctionSheetColumnToCurveMapper(this)); } //----------------------------------------------------------------------------- void FunctionSheet::showEvent(QShowEvent *e) { m_frameScroller.registerFrameScroller(); SpreadsheetViewer::showEvent(e); } //----------------------------------------------------------------------------- void FunctionSheet::hideEvent(QHideEvent *e) { m_frameScroller.unregisterFrameScroller(); SpreadsheetViewer::hideEvent(e); } //----------------------------------------------------------------------------- void FunctionSheet::onFrameSwitched() { setCurrentRow(getCurrentFrame()); m_rowViewer->update(); m_cellViewer->update(); } //----------------------------------------------------------------------------- void FunctionSheet::setCurrentFrame(int frame) { if (getFrameHandle()) getFrameHandle()->setFrame(frame); } //----------------------------------------------------------------------------- int FunctionSheet::getCurrentFrame() const { return getFrameHandle() ? getFrameHandle()->getFrame() : 0; } //----------------------------------------------------------------------------- int FunctionSheet::getChannelCount() { if (m_functionTreeModel == 0) return 0; else return m_functionTreeModel->getActiveChannelCount(); } //----------------------------------------------------------------------------- FunctionTreeModel::Channel *FunctionSheet::getChannel(int column) { if (m_functionTreeModel == 0) return 0; else return m_functionTreeModel->getActiveChannel(column); } //----------------------------------------------------------------------------- TDoubleParam *FunctionSheet::getCurve(int column) { FunctionTreeModel::Channel *channel = getChannel(column); return channel ? channel->getParam() : 0; } //----------------------------------------------------------------------------- void FunctionSheet::setModel(FunctionTreeModel *model) { m_functionTreeModel = model; } //----------------------------------------------------------------------------- void FunctionSheet::setViewer(FunctionViewer *viewer) { m_functionViewer = viewer; } //----------------------------------------------------------------------------- QRect FunctionSheet::getSelectedCells() const { if (getSelection()) return getSelection()->getSelectedCells(); else return QRect(); } //----------------------------------------------------------------------------- void FunctionSheet::selectCells(const QRect &selectedCells) { m_selectedCells = selectedCells; if (getSelection()) { QList curves; for (int c = selectedCells.left(); c <= selectedCells.right(); c++) { TDoubleParam *param = 0; if (c < getChannelCount()) param = getChannel(c)->getParam(); curves.push_back(param); } getSelection()->selectCells(selectedCells, curves); if (selectedCells.width() == 1 && curves[0] && !getChannel(selectedCells.x())->isCurrent()) getChannel(selectedCells.x())->setIsCurrent(true); } updateAll(); } //----------------------------------------------------------------------------- void FunctionSheet::updateAll() { m_rowViewer->update(); m_columnHeadViewer->update(); m_cellViewer->update(); setColumnCount(getChannelCount()); } //----------------------------------------------------------------------------- /*! Display expression name of the current segment */ QString FunctionSheet::getSelectedParamName() { if (m_functionTreeModel->getCurrentChannel()) return m_functionTreeModel->getCurrentChannel()->getExprRefName(); else return QString(); } //----------------------------------------------------------------------------- int FunctionSheet::getColumnIndexByCurve(TDoubleParam *param) const { return m_functionTreeModel->getColumnIndexByCurve(param); } //----------------------------------------------------------------------------- /*! scroll column to show the current one */ void FunctionSheet::onCurrentChannelChanged( FunctionTreeModel::Channel *channel) { if (!channel) return; for (int c = 0; c < getChannelCount(); c++) { FunctionTreeModel::Channel *tmpChan = getChannel(c); if (tmpChan == channel) { ensureVisibleCol(c); return; } } }