diff options
Diffstat (limited to 'src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/KeyframeManager.cpp')
-rw-r--r-- | src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/KeyframeManager.cpp | 616 |
1 files changed, 616 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/KeyframeManager.cpp b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/KeyframeManager.cpp new file mode 100644 index 00000000..a4248fad --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/KeyframeManager.cpp @@ -0,0 +1,616 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "KeyframeManager.h" +#include "RowTree.h" +#include "RowTimeline.h" +#include "Keyframe.h" +#include "RowManager.h" +#include "TimelineGraphicsScene.h" +#include "StudioApp.h" +#include "Core.h" +#include "Doc.h" +#include "SlideSystem.h" +#include "StudioClipboard.h" +#include "CmdDataModelRemoveKeyframe.h" +#include "CmdDataModelInsertKeyframe.h" +#include "CmdDataModelChangeKeyframe.h" +#include "ClientDataModelBridge.h" +#include "Bindings/PasteKeyframesCommandHelper.h" +#include "Bindings/ITimelineItemBinding.h" +#include "StudioPreferences.h" +#include "Dialogs.h" +#include "TimeEnums.h" +#include "RowTimelinePropertyGraph.h" +#include "IDocumentEditor.h" + +using namespace qt3dsdm; + +KeyframeManager::KeyframeManager(TimelineGraphicsScene *scene) : m_scene(scene) +{ +} + +KeyframeManager::~KeyframeManager() +{ + delete m_pasteKeyframeCommandHelper; +} + +QList<Keyframe *> KeyframeManager::insertKeyframe(RowTimeline *row, long time, + bool selectInsertedKeyframes) +{ + QList<Keyframe *> addedKeyframes; + QList<RowTimeline *> propRows; + if (!row->rowTree()->isProperty()) { + const auto childRows = row->rowTree()->childRows(); + for (const auto r : childRows) { + if (r->isProperty()) + propRows.append(r->rowTimeline()); + } + } else { + propRows.append(row); + } + + if (!propRows.empty()) { + for (const auto &r : qAsConst(propRows)) { + Keyframe *keyframe = new Keyframe(time, r); + r->insertKeyframe(keyframe); + r->parentRow()->insertKeyframe(keyframe); + addedKeyframes.append(keyframe); + } + + if (selectInsertedKeyframes && !addedKeyframes.empty()) { + deselectAllKeyframes(); + selectKeyframes(addedKeyframes); + } + } + + return addedKeyframes; +} + +void KeyframeManager::selectKeyframe(Keyframe *keyframe) +{ + if (!m_selectedKeyframes.contains(keyframe)) { + m_selectedKeyframes.append(keyframe); + + if (!m_selectedKeyframesMasterRows.contains(keyframe->rowMaster)) + m_selectedKeyframesMasterRows.append(keyframe->rowMaster); + + keyframe->binding->SetSelected(true); + keyframe->rowMaster->putSelectedKeyframesOnTop(); + keyframe->rowMaster->updateKeyframes(); + } +} + +void KeyframeManager::selectConnectedKeyframes(Keyframe *keyframe) +{ + // Select all keyframes of same master row at same time + const auto keyframes = keyframe->rowMaster->keyframes(); + for (const auto k : keyframes) { + if (k->time == keyframe->time) + selectKeyframe(k); + } +} + +void KeyframeManager::selectKeyframes(const QList<Keyframe *> &keyframes) +{ + for (const auto keyframe : keyframes) { + if (!m_selectedKeyframes.contains(keyframe)) { + m_selectedKeyframes.append(keyframe); + + if (!m_selectedKeyframesMasterRows.contains(keyframe->rowMaster)) + m_selectedKeyframesMasterRows.append(keyframe->rowMaster); + } + } + + for (auto keyframe : qAsConst(m_selectedKeyframes)) + keyframe->binding->SetSelected(true); + + for (auto row : qAsConst(m_selectedKeyframesMasterRows)) { + row->putSelectedKeyframesOnTop(); + row->updateKeyframes(); + } +} + +QList<Keyframe *> KeyframeManager::selectedKeyframes() const +{ + return m_selectedKeyframes; +} + +void KeyframeManager::commitMoveSelectedKeyframes() +{ + Q3DStudio::ScopedDocumentEditor editor(*g_StudioApp.GetCore()->GetDoc(), + QObject::tr("Set Keyframe Time"), __FILE__, __LINE__); + for (Keyframe *keyframe : qAsConst(m_selectedKeyframes)) { + Qt3DSDMTimelineKeyframe::TKeyframeHandleList kfHandles; + keyframe->binding->GetKeyframeHandles(kfHandles); + + for (auto h : kfHandles) + editor->SetKeyframeTime(h, keyframe->time); + } +} + +void KeyframeManager::selectKeyframesInRect(const QRectF &rect) +{ + deselectAllKeyframes(); + + RowTree *row = m_scene->rowManager()->getRowAtPos(QPointF(0, rect.top())); + while (row && row->y() < rect.bottom()) { + if (!row->locked()) { + const auto keyframes = row->rowTimeline()->getKeyframesInRange(rect); + for (auto keyframe : keyframes) { + if (!m_selectedKeyframes.contains(keyframe)) { + m_selectedKeyframes.append(keyframe); + + if (!m_selectedKeyframesMasterRows.contains(keyframe->rowMaster)) + m_selectedKeyframesMasterRows.append(keyframe->rowMaster); + } + } + + if (row->propertyExpanded() + && row->propBinding()->animationType() == EAnimationTypeBezier) { + row->rowTimeline()->propertyGraph()->selectBezierKeyframesInRange(rect); + } + } + row = m_scene->rowManager()->getRowAtPos(QPointF(0, row->y() + row->size().height())); + } + + for (auto keyframe : qAsConst(m_selectedKeyframes)) + keyframe->binding->SetSelected(true); + + for (auto row : qAsConst(m_selectedKeyframesMasterRows)) { + row->putSelectedKeyframesOnTop(); + row->updateKeyframes(); + } +} + +void KeyframeManager::deselectKeyframe(Keyframe *keyframe) +{ + if (m_selectedKeyframes.contains(keyframe)) { + m_selectedKeyframes.removeAll(keyframe); + keyframe->rowMaster->updateKeyframes(); + m_selectedKeyframesMasterRows.removeAll(keyframe->rowMaster); + + keyframe->binding->SetSelected(false); + keyframe->rowMaster->putSelectedKeyframesOnTop(); + } +} + +void KeyframeManager::deselectConnectedKeyframes(Keyframe *keyframe) +{ + // Deselect all keyframes of same master row at same time + const auto keyframes = keyframe->rowMaster->keyframes(); + for (const auto k : keyframes) { + if (k->time == keyframe->time) + deselectKeyframe(k); + } +} + +void KeyframeManager::deselectAllKeyframes() +{ + for (auto keyframe : qAsConst(m_selectedKeyframes)) + keyframe->binding->SetSelected(false); + + for (auto row : qAsConst(m_selectedKeyframesMasterRows)) + row->updateKeyframes(); + + m_selectedKeyframes.clear(); + m_selectedKeyframesMasterRows.clear(); + + // deselect bezier keyframes + const auto expandedPropGraphs = m_scene->rowManager()->getExpandedPropertyGraphs(); + for (auto g : expandedPropGraphs) + g->deselectAllBezierKeyframes(); +} + +void KeyframeManager::deselectRowKeyframes(RowTree *row) +{ + const QList<Keyframe *> keyframes = row->rowTimeline()->keyframes(); + for (const auto keyframe : keyframes) { + if (row->isProperty()) + deselectKeyframe(keyframe); + else + deselectConnectedKeyframes(keyframe); + } +} + +bool KeyframeManager::deleteSelectedKeyframes() +{ + if (!m_selectedKeyframes.empty()) { + CDoc *theDoc = g_StudioApp.GetCore()->GetDoc(); + CCmdDataModelRemoveKeyframe *cmd = new CCmdDataModelRemoveKeyframe(theDoc); + for (auto keyframe : qAsConst(m_selectedKeyframes)) { + cmd->addKeyframeHandles(keyframe->binding); + + keyframe->rowMaster->removeKeyframe(keyframe); + keyframe->rowProperty->removeKeyframe(keyframe); + + delete keyframe; + } + + for (auto row : qAsConst(m_selectedKeyframesMasterRows)) + row->updateKeyframes(); + + m_selectedKeyframes.clear(); + m_selectedKeyframesMasterRows.clear(); + + g_StudioApp.GetCore()->ExecuteCommand(cmd); + return true; + } + + return false; +} + +// delete all keyframes on a row +void KeyframeManager::deleteKeyframes(RowTimeline *row, bool repaint) +{ + const auto keyframes = row->keyframes(); + for (auto keyframe : keyframes) { + keyframe->rowMaster->removeKeyframe(keyframe); + keyframe->rowProperty->removeKeyframe(keyframe); + + if (m_selectedKeyframes.contains(keyframe)) + m_selectedKeyframes.removeAll(keyframe); + + delete keyframe; + } + + if (m_selectedKeyframesMasterRows.contains(row)) + m_selectedKeyframesMasterRows.removeAll(row); + + if (repaint) + row->updateKeyframes(); +} + +void KeyframeManager::copySelectedKeyframes() +{ + if (!m_selectedKeyframes.empty() && m_selectedKeyframesMasterRows.count() == 1) { + // Keyframe copying doesn't use clipboard, so clear it so that next time we paste + // it will paste the keyframes rather than the last object we copied + CStudioClipboard::ClearClipboard(); + + if (m_pasteKeyframeCommandHelper) + m_pasteKeyframeCommandHelper->Clear(); // clear out previously copied data + else + m_pasteKeyframeCommandHelper = new CPasteKeyframeCommandHelper(); + + // calc min copied frames time + long minTime = LONG_MAX; + for (auto keyframe : qAsConst(m_selectedKeyframes)) { + if (keyframe->time < minTime) + minTime = keyframe->time; + } + + qt3dsdm::IAnimationCore *animationCore = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem() + ->GetAnimationCore(); + + for (auto keyframe : qAsConst(m_selectedKeyframes)) { + Qt3DSDMTimelineKeyframe *kf = keyframe->binding; + Qt3DSDMTimelineKeyframe::TKeyframeHandleList theKeyframeHandles; + kf->GetKeyframeHandles(theKeyframeHandles); + qt3dsdm::SGetOrSetKeyframeInfo info[4]; + size_t infoCount = 0; + if (!theKeyframeHandles.empty()) { + switch (theKeyframeHandles.size()) { + case 1: + info[0] = setKeyframeInfo(theKeyframeHandles[0], *animationCore); + infoCount = 1; + break; + case 3: + info[0] = setKeyframeInfo(theKeyframeHandles[0], *animationCore); + info[1] = setKeyframeInfo(theKeyframeHandles[1], *animationCore); + info[2] = setKeyframeInfo(theKeyframeHandles[2], *animationCore); + infoCount = 3; + break; + case 4: + info[0] = setKeyframeInfo(theKeyframeHandles[0], *animationCore); + info[1] = setKeyframeInfo(theKeyframeHandles[1], *animationCore); + info[2] = setKeyframeInfo(theKeyframeHandles[2], *animationCore); + info[3] = setKeyframeInfo(theKeyframeHandles[3], *animationCore); + infoCount = 4; + break; + default: + break; + } + + long dt = kf->GetTime() - minTime; + qt3dsdm::Qt3DSDMAnimationHandle animation + = animationCore->GetAnimationForKeyframe(theKeyframeHandles[0]); + m_pasteKeyframeCommandHelper->AddKeyframeData( + animationCore->GetAnimationInfo(animation).m_Property, dt, info, infoCount); + } + } + } +} + +qt3dsdm::SGetOrSetKeyframeInfo KeyframeManager::setKeyframeInfo( + qt3dsdm::Qt3DSDMKeyframeHandle keyframeHandle, qt3dsdm::IAnimationCore &animCore) +{ + qt3dsdm::TKeyframe keyframeData = animCore.GetKeyframeData(keyframeHandle); + + bool isDynamic = animCore.IsFirstKeyframe(keyframeHandle) && animCore.GetAnimationInfo( + animCore.GetAnimationForKeyframe(keyframeHandle)).m_DynamicFirstKeyframe; + + return qt3dsdm::SGetOrSetKeyframeInfo(keyframeData, isDynamic); +} + +void KeyframeManager::pasteKeyframes() +{ + CDoc *theDoc = g_StudioApp.GetCore()->GetDoc(); + if (m_pasteKeyframeCommandHelper && m_pasteKeyframeCommandHelper->HasCopiedKeyframes()) { + qt3dsdm::Qt3DSDMInstanceHandle theSelectedInstance = theDoc->GetSelectedInstance(); + if (theSelectedInstance.Valid()) { + CCmdDataModelInsertKeyframe *theInsertKeyframesCommand + = m_pasteKeyframeCommandHelper->GetCommand(theDoc, theDoc->GetCurrentViewTime(), + theSelectedInstance); + if (theInsertKeyframesCommand) + g_StudioApp.GetCore()->ExecuteCommand(theInsertKeyframesCommand); + } + } +} + +void KeyframeManager::moveSelectedKeyframes(long newTime) +{ + Keyframe *pressedKeyframe = m_scene->pressedKeyframe(); + + Q_ASSERT(pressedKeyframe); + + // make sure the min-time keyframe doesn't go below zero + long minTime = getMinSelectedKeyframesTime(); + if (pressedKeyframe->time - minTime > newTime) + newTime = pressedKeyframe->time - minTime; + + for (auto keyframe : qAsConst(m_selectedKeyframes)) { + if (keyframe != pressedKeyframe) + keyframe->time = newTime - (pressedKeyframe->time - keyframe->time); + } + pressedKeyframe->time = newTime; + + for (auto row : qAsConst(m_selectedKeyframesMasterRows)) + row->updateKeyframes(); +} + +long KeyframeManager::getMinSelectedKeyframesTime() const +{ + long minTime = LONG_MAX; + for (auto keyframe : qAsConst(m_selectedKeyframes)) { + if (keyframe->time < minTime) + minTime = keyframe->time; + } + + return minTime; +} + +// returns the distance between the pressed keyframe and the min-time keyframe in a multiselection +long KeyframeManager::getPressedKeyframeOffset() const +{ + if (m_scene->pressedKeyframe()) + return m_scene->pressedKeyframe()->time - getMinSelectedKeyframesTime(); + + return 0; +} + +// selected keyframes belong to only one master row +bool KeyframeManager::oneMasterRowSelected() const +{ + return m_selectedKeyframesMasterRows.count() == 1; +} + +bool KeyframeManager::hasSelectedKeyframes() const +{ + return !m_selectedKeyframes.empty(); +} + +bool KeyframeManager::hasCopiedKeyframes() const +{ + return m_pasteKeyframeCommandHelper && + m_pasteKeyframeCommandHelper->HasCopiedKeyframes(); +} + +bool KeyframeManager::hasDynamicKeyframes(RowTree *row) const +{ + const QList<Keyframe *> keyframes = row->rowTimeline()->keyframes(); + for (const auto keyframe : keyframes) { + if (keyframe->binding->IsDynamic()) + return true; + } + return false; +} + +// IKeyframesManager interface +void KeyframeManager::SetKeyframeTime(long inTime) +{ + g_StudioApp.GetDialogs()->asyncDisplayTimeEditDialog(inTime, g_StudioApp.GetCore()->GetDoc(), + ASSETKEYFRAME, this); +} + +void KeyframeManager::SetKeyframesDynamic(bool inDynamic) +{ + if (!hasSelectedKeyframes()) + return; + + CDoc *doc = g_StudioApp.GetCore()->GetDoc(); + IAnimationCore *animationCore = doc->GetStudioSystem()->GetAnimationCore(); + CCmdDataModelChangeDynamicKeyframe *cmd = nullptr; + + for (int i = 0; i < m_selectedKeyframes.size(); ++i) { + Qt3DSDMTimelineKeyframe *timelineKeyframe = m_selectedKeyframes[i]->binding; + Qt3DSDMTimelineKeyframe::TKeyframeHandleList keyframeHandles; + timelineKeyframe->GetKeyframeHandles(keyframeHandles); + + for (size_t keyIndex = 0; keyIndex < keyframeHandles.size(); ++keyIndex) { + qt3dsdm::Qt3DSDMAnimationHandle animation( + animationCore->GetAnimationForKeyframe(keyframeHandles.at(keyIndex))); + if (!cmd) + cmd = new CCmdDataModelChangeDynamicKeyframe(doc, animation, inDynamic); + else + cmd->AddHandle(animation); + } + } + + if (cmd) + g_StudioApp.GetCore()->ExecuteCommand(cmd); +} + +void KeyframeManager::CommitChangedKeyframes() +{ + m_scene->resetPressedKeyframe(); + commitMoveSelectedKeyframes(); +} + +void KeyframeManager::RollbackChangedKeyframes() +{ + m_scene->resetPressedKeyframe(); + + for (Keyframe *keyframe : qAsConst(m_selectedKeyframes)) + keyframe->time = keyframe->binding->GetTime(); + + for (auto row : qAsConst(m_selectedKeyframesMasterRows)) + row->updateKeyframes(); +} + +bool KeyframeManager::HasSelectedKeyframes() const +{ + return hasSelectedKeyframes(); +} + +bool KeyframeManager::HasDynamicKeyframes() const +{ + return false; // Mahmoud_TODO: implement +} + +bool KeyframeManager::CanPerformKeyframeCopy() const +{ + return !m_selectedKeyframes.empty() && m_selectedKeyframesMasterRows.count() == 1; +} + +bool KeyframeManager::canSetKeyframeInterpolation() const +{ + for (Keyframe *kf : qAsConst(m_selectedKeyframes)) { + auto animType = kf->rowProperty->rowTree()->propBinding()->animationType(); + if (animType == qt3dsdm::EAnimationTypeEaseInOut) + return true; + } + + return false; +} + +bool KeyframeManager::CanPerformKeyframePaste() const +{ + if (m_pasteKeyframeCommandHelper && m_pasteKeyframeCommandHelper->HasCopiedKeyframes()) { + qt3dsdm::Qt3DSDMInstanceHandle theSelectedInstance = + g_StudioApp.GetCore()->GetDoc()->GetSelectedInstance(); + if (theSelectedInstance.Valid()) + return true; + } + + return false; +} + +void KeyframeManager::CopyKeyframes() +{ + copySelectedKeyframes(); +} + +bool KeyframeManager::RemoveKeyframes(bool inPerformCopy) +{ + Q_UNUSED(inPerformCopy) + + return deleteSelectedKeyframes(); +} + +void KeyframeManager::PasteKeyframes() +{ + pasteKeyframes(); +} + +void KeyframeManager::SetKeyframeInterpolation() +{ + if (!hasSelectedKeyframes()) + return; + + CDoc *doc = g_StudioApp.GetCore()->GetDoc(); + IAnimationCore *animCore = doc->GetStudioSystem()->GetAnimationCore(); + + // find the first easing keyframe and use it to get the current easing values + auto firstEaseKeyframeIter = m_selectedKeyframes.begin(); + while ((*firstEaseKeyframeIter)->rowProperty->rowTree()->propBinding()->animationType() + != EAnimationTypeEaseInOut) { + firstEaseKeyframeIter++; + } + + Qt3DSDMTimelineKeyframe::TKeyframeHandleList keyframeHandles; + (*firstEaseKeyframeIter)->binding->GetKeyframeHandles(keyframeHandles); + + TKeyframe keyframeData = animCore->GetKeyframeData(keyframeHandles[0]); + + float easeIn = CStudioPreferences::isInterpolation() ? 100 : 0; + float easeOut = CStudioPreferences::isInterpolation() ? 100 : 0; + getEaseInOutValues(keyframeData, easeIn, easeOut); + + if (g_StudioApp.GetDialogs()->displayKeyframeInterpolation(easeIn, easeOut)) { + // Having "editor" variable here is important as its destructor creates proper transaction + Q3DStudio::ScopedDocumentEditor editor(*doc, QObject::tr("Set Keyframe Interpolation"), + __FILE__, __LINE__); + for (Keyframe *keyframe : qAsConst(m_selectedKeyframes)) { + Qt3DSDMTimelineKeyframe::TKeyframeHandleList keyframeHandles; + keyframe->binding->GetKeyframeHandles(keyframeHandles); + for (size_t i = 0; i < keyframeHandles.size(); ++i) { + TKeyframe keyframeData = animCore->GetKeyframeData(keyframeHandles[i]); + setEaseInOutValues(keyframeData, easeIn, easeOut); + animCore->SetKeyframeData(keyframeHandles[i], keyframeData); + } + } + } +} + +void KeyframeManager::DeselectAllKeyframes() +{ + deselectAllKeyframes(); +} + +void KeyframeManager::SetChangedKeyframes() +{ + CDoc *theDoc = g_StudioApp.GetCore()->GetDoc(); + qt3dsdm::Qt3DSDMInstanceHandle selectedInstance = theDoc->GetSelectedInstance(); + if (selectedInstance.Valid()) { + using namespace Q3DStudio; + Q3DStudio::ScopedDocumentEditor editor(*theDoc, QObject::tr("Set Changed Keyframes"), + __FILE__, __LINE__); + CStudioSystem *theStudioSystem = theDoc->GetStudioSystem(); + // Get all animated properties. + TPropertyHandleList properties; + theStudioSystem->GetPropertySystem()->GetAggregateInstanceProperties(selectedInstance, + properties); + for (size_t i = 0; i < properties.size(); ++i) { + if (theStudioSystem->GetAnimationSystem()->IsPropertyAnimated( + selectedInstance, properties[i])) { + editor->KeyframeProperty(selectedInstance, properties[i], true); + } + } + } +} |