diff --git a/toonz/sources/include/toonzqt/functionselection.h b/toonz/sources/include/toonzqt/functionselection.h index 711898f..cef835d 100644 --- a/toonz/sources/include/toonzqt/functionselection.h +++ b/toonz/sources/include/toonzqt/functionselection.h @@ -109,6 +109,28 @@ public: void doDelete(); void insertCells(); + // if inclusive == true, consider all segments overlapping the selection + void setStep(int, bool inclusive = true); + void setStep1() { setStep(1); } + void setStep2() { setStep(2); } + void setStep3() { setStep(3); } + void setStep4() { setStep(4); } + + // return step if all the selected segments has the same value. + // return -1 if the selection does not overlap any segments + // return 0 if the step value is uneven in the selection + // if inclusive == true, consider all segments overlapping the selection + int getCommonStep(bool inclusive = true); + + void setSegmentType(TDoubleKeyframe::Type type, bool inclusive = true); + + // return TDoubleKeyframe::Type value if the selected segments has the same + // interpolation type. return -1 if the selection does not overlap any + // segments return 0 (TDoubleKeyframe::None) if the interpolation is not + // identical in the selection if inclusive == true, consider all segments + // overlapping the selection + int getCommonSegmentType(bool inclusive = true); + signals: void selectionChanged(); }; diff --git a/toonz/sources/toonzqt/functionselection.cpp b/toonz/sources/toonzqt/functionselection.cpp index da5bf8e..b53c3a7 100644 --- a/toonz/sources/toonzqt/functionselection.cpp +++ b/toonz/sources/toonzqt/functionselection.cpp @@ -444,6 +444,11 @@ void FunctionSelection::enableCommands() { enableCommand(this, "MI_Cut", &FunctionSelection::doCut); enableCommand(this, "MI_Clear", &FunctionSelection::doDelete); enableCommand(this, "MI_Insert", &FunctionSelection::insertCells); + + enableCommand(this, "MI_ResetStep", &FunctionSelection::setStep1); + enableCommand(this, "MI_Step2", &FunctionSelection::setStep2); + enableCommand(this, "MI_Step3", &FunctionSelection::setStep3); + enableCommand(this, "MI_Step4", &FunctionSelection::setStep4); } void FunctionSelection::doCopy() { if (isEmpty()) return; @@ -516,12 +521,12 @@ void FunctionSelection::doCut() { KeyframesMoveUndo *moveUndo = new KeyframesMoveUndo(); for (int i = 0; i < m_selectedKeyframes.size(); i++) { - TDoubleParam *curve = m_selectedKeyframes[i].first; - QSet &kk = m_selectedKeyframes[i].second; - double delta = 0; + TDoubleParam *curve = m_selectedKeyframes[i].first; + QSet &kk = m_selectedKeyframes[i].second; + double delta = 0; if (cellsSelection) delta = -m_selectedCells.height(); - int n = curve ? curve->getKeyframeCount() : 0; - int j = 0; + int n = curve ? curve->getKeyframeCount() : 0; + int j = 0; for (int i = 0; i < n; i++) { if (kk.contains(i)) { if (i + 1 < n && kk.contains(i + 1) && !cellsSelection) @@ -585,6 +590,125 @@ void FunctionSelection::insertCells() { TUndoManager::manager()->add(undo); } +void FunctionSelection::setStep(int step, bool inclusive) { + if (isEmpty()) return; + TUndoManager::manager()->beginBlock(); + + int row = getSelectedCells().top(); + for (const auto &col : m_selectedKeyframes) { + TDoubleParam *curve = col.first; + // need to have at least one segment + if (!curve || curve->getKeyframeCount() <= 1) continue; + + // consider the keyframe just before the top row of the selected cells + if (inclusive) { + int topIndex = curve->getPrevKeyframe(row); + if (topIndex != -1 && topIndex != curve->getKeyframeCount() - 1 && + !col.second.contains(topIndex)) + KeyframeSetter(curve, topIndex).setStep(step); + } + + for (const int &kIndex : col.second) { + // ignore the last key + if (kIndex == curve->getKeyframeCount() - 1) continue; + KeyframeSetter(curve, kIndex).setStep(step); + } + } + + TUndoManager::manager()->endBlock(); +} + +int FunctionSelection::getCommonStep(bool inclusive) { + if (isEmpty()) return -1; + + int step = -1; + int row = getSelectedCells().top(); + for (const auto &col : m_selectedKeyframes) { + TDoubleParam *curve = col.first; + // need to have at least one segment + if (!curve || curve->getKeyframeCount() <= 1) continue; + + // consider the keyframe just before the top row of the selected cells + if (inclusive) { + int topIndex = curve->getPrevKeyframe(row); + if (topIndex != -1 && topIndex != curve->getKeyframeCount() - 1 && + !col.second.contains(topIndex)) + step = curve->getKeyframe(topIndex).m_step; + } + + for (const int &kIndex : col.second) { + // ignore the last key + if (kIndex == curve->getKeyframeCount() - 1) continue; + int tmpStep = curve->getKeyframe(kIndex).m_step; + if (step == -1) + step = tmpStep; + else if (step != tmpStep) + return 0; + } + } + return step; +} + +void FunctionSelection::setSegmentType(TDoubleKeyframe::Type type, + bool inclusive) { + if (isEmpty()) return; + TUndoManager::manager()->beginBlock(); + + int row = getSelectedCells().top(); + for (const auto &col : m_selectedKeyframes) { + TDoubleParam *curve = col.first; + // need to have at least one segment + if (!curve || curve->getKeyframeCount() <= 1) continue; + + // consider the keyframe just before the top row of the selected cells + if (inclusive) { + int topIndex = curve->getPrevKeyframe(row); + if (topIndex != -1 && topIndex != curve->getKeyframeCount() - 1 && + !col.second.contains(topIndex)) + KeyframeSetter(curve, topIndex).setType(type); + } + + for (const int &kIndex : col.second) { + // ignore the last key + if (kIndex == curve->getKeyframeCount() - 1) continue; + KeyframeSetter(curve, kIndex).setType(type); + } + } + + TUndoManager::manager()->endBlock(); +} + +int FunctionSelection::getCommonSegmentType(bool inclusive) { + if (isEmpty()) return -1; + + int type = -1; + int row = getSelectedCells().top(); + for (const auto &col : m_selectedKeyframes) { + TDoubleParam *curve = col.first; + // need to have at least one segment + if (!curve || curve->getKeyframeCount() <= 1) continue; + + // consider the keyframe just before the top row of the selected cells + if (inclusive) { + int topIndex = curve->getPrevKeyframe(row); + if (topIndex != -1 && topIndex != curve->getKeyframeCount() - 1 && + !col.second.contains(topIndex)) + type = (int)(curve->getKeyframe(topIndex).m_type); + } + + for (const int &kIndex : col.second) { + // ignore the last key + if (kIndex == curve->getKeyframeCount() - 1) continue; + int tmpType = (int)(curve->getKeyframe(kIndex).m_type); + if (type == -1) + type = tmpType; + else if (type != tmpType) + return 0; + } + } + return type; +} + //============================================================================= // // FunctionKeyframesData @@ -645,7 +769,7 @@ int FunctionKeyframesData::getRowCount() const { for (int c = 0; c < (int)m_keyframes.size(); c++) { const Keyframes &keyframes = m_keyframes[c]; if (!keyframes.empty()) { - int row = (int)(keyframes.rbegin()->m_frame); + int row = (int)(keyframes.rbegin()->m_frame); if (row + 1 > rowCount) rowCount = row + 1; } } diff --git a/toonz/sources/toonzqt/functionsheet.cpp b/toonz/sources/toonzqt/functionsheet.cpp index 4cdba4b..f505977 100644 --- a/toonz/sources/toonzqt/functionsheet.cpp +++ b/toonz/sources/toonzqt/functionsheet.cpp @@ -918,30 +918,17 @@ void FunctionSheetCellViewer::onMouseMovedInLineEdit(QMouseEvent *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); + + QStringList interpNames; + interpNames << tr("Constant Interpolation") << tr("Linear Interpolation") + << tr("Speed In / Speed Out Interpolation") + << tr("Ease In / Ease Out Interpolation") + << tr("Ease In / Ease Out (%) Interpolation") + << tr("Exponential Interpolation") + << tr("Expression Interpolation") << tr("File Interpolation") + << tr("Similar Shape Interpolation"); QAction activateCycleAction(tr("Activate Cycle"), 0); QAction deactivateCycleAction(tr("Deactivate Cycle"), 0); QAction showIbtwnAction(tr("Show Inbetween Values"), 0); @@ -967,6 +954,15 @@ void FunctionSheetCellViewer::openContextMenu(QMouseEvent *e) { } int kIndex = curve->getPrevKeyframe(row); + // if the FunctionSelection is not current or when clicking outside of the + // selection, then select the clicked cell. + FunctionSelection *selection = m_sheet->getSelection(); + if (!selection->getSelectedCells().contains(col, row)) { + selection->makeCurrent(); + selection->selectCells(QRect(col, row, 1, 1)); + } + CommandManager *cmdManager = CommandManager::instance(); + // build menu QMenu menu(0); @@ -981,37 +977,35 @@ void FunctionSheetCellViewer::openContextMenu(QMouseEvent *e) { if (!isKeyframe) // menu.addAction(&deleteKeyframeAction); else menu.addAction(&insertKeyframeAction); - if (!isEmpty && !isKeyframe && kIndex >= 0) { + // change interpolation commands + QList interpActions; + int interp = selection->getCommonSegmentType(); + if (interp != -1) { menu.addSeparator(); - QMenu *interpMenu = menu.addMenu(tr("Change Interpolation")); - TDoubleKeyframe kf = curve->getKeyframe(kIndex); - if (kf.m_type != TDoubleKeyframe::Linear) - interpMenu->addAction(&setLinearAction); - if (kf.m_type != TDoubleKeyframe::SpeedInOut) - interpMenu->addAction(&setSpeedInOutAction); - if (kf.m_type != TDoubleKeyframe::EaseInOut) - interpMenu->addAction(&setEaseInOutAction); - if (kf.m_type != TDoubleKeyframe::EaseInOutPercentage) - interpMenu->addAction(&setEaseInOut2Action); - if (kf.m_type != TDoubleKeyframe::Exponential) - interpMenu->addAction(&setExponentialAction); - if (kf.m_type != TDoubleKeyframe::Expression) - interpMenu->addAction(&setExpressionAction); - if (kf.m_type != TDoubleKeyframe::File) - interpMenu->addAction(&setFileAction); - if (kf.m_type != TDoubleKeyframe::Constant) - interpMenu->addAction(&setConstantAction); + QMenu *interpMenu = menu.addMenu(tr("Change Interpolation")); + for (int i = (int)TDoubleKeyframe::Constant; + i <= (int)TDoubleKeyframe::SimilarShape; i++) { + if (interp != i) { + QAction *interpAction = new QAction(interpNames[i - 1], 0); + interpAction->setData(i); + interpActions.append(interpAction); + interpMenu->addAction(interpAction); + } + } + } + // change step commands + int step = selection->getCommonStep(); + if (step != -1) { QMenu *stepMenu = menu.addMenu(tr("Change Step")); - if (kf.m_step != 1) stepMenu->addAction(&setStep1Action); - if (kf.m_step != 2) stepMenu->addAction(&setStep2Action); - if (kf.m_step != 3) stepMenu->addAction(&setStep3Action); - if (kf.m_step != 4) stepMenu->addAction(&setStep4Action); + if (step != 1) stepMenu->addAction(cmdManager->getAction("MI_ResetStep")); + if (step != 2) stepMenu->addAction(cmdManager->getAction("MI_Step2")); + if (step != 3) stepMenu->addAction(cmdManager->getAction("MI_Step3")); + if (step != 4) stepMenu->addAction(cmdManager->getAction("MI_Step4")); } menu.addSeparator(); - CommandManager *cmdManager = CommandManager::instance(); menu.addAction(cmdManager->getAction("MI_Cut")); menu.addAction(cmdManager->getAction("MI_Copy")); menu.addAction(cmdManager->getAction("MI_Paste")); @@ -1027,47 +1021,16 @@ void FunctionSheetCellViewer::openContextMenu(QMouseEvent *e) { menu.addAction(&showIbtwnAction); } - FunctionSelection *selection = m_sheet->getSelection(); - TSceneHandle *sceneHandle = m_sheet->getViewer()->getSceneHandle(); + TSceneHandle *sceneHandle = m_sheet->getViewer()->getSceneHandle(); // 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); - else if (action == &activateCycleAction) + } else if (interpActions.contains(action)) { + selection->setSegmentType((TDoubleKeyframe::Type)action->data().toInt()); + } else if (action == &activateCycleAction) KeyframeSetter::enableCycle(curve, true, sceneHandle); else if (action == &deactivateCycleAction) KeyframeSetter::enableCycle(curve, false, sceneHandle);