Commit 32c7da90 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle

Merge branch 'markers_snap' into 'Applications/19.04'

Make timeline snap to clip markers

See merge request kde/kdenlive!30
parents 162ebeaa 1005eb66
......@@ -243,7 +243,7 @@ QHash<int, QByteArray> MarkerListModel::roleNames() const
void MarkerListModel::addSnapPoint(GenTime pos)
{
QWriteLocker locker(&m_lock);
std::vector<std::weak_ptr<SnapModel>> validSnapModels;
std::vector<std::weak_ptr<SnapInterface>> validSnapModels;
for (const auto &snapModel : m_registeredSnaps) {
if (auto ptr = snapModel.lock()) {
validSnapModels.push_back(snapModel);
......@@ -257,7 +257,7 @@ void MarkerListModel::addSnapPoint(GenTime pos)
void MarkerListModel::removeSnapPoint(GenTime pos)
{
QWriteLocker locker(&m_lock);
std::vector<std::weak_ptr<SnapModel>> validSnapModels;
std::vector<std::weak_ptr<SnapInterface>> validSnapModels;
for (const auto &snapModel : m_registeredSnaps) {
if (auto ptr = snapModel.lock()) {
validSnapModels.push_back(snapModel);
......@@ -326,13 +326,23 @@ QList<CommentedTime> MarkerListModel::getAllMarkers() const
return markers;
}
std::vector<size_t> MarkerListModel::getSnapPoints() const
{
READ_LOCK();
std::vector<size_t> markers;
for (const auto &marker : m_markerList) {
markers.push_back(marker.first.frames(pCore->getCurrentFps()));
}
return markers;
}
bool MarkerListModel::hasMarker(int frame) const
{
READ_LOCK();
return m_markerList.count(GenTime(frame, pCore->getCurrentFps())) > 0;
}
void MarkerListModel::registerSnapModel(const std::weak_ptr<SnapModel> &snapModel)
void MarkerListModel::registerSnapModel(const std::weak_ptr<SnapInterface> &snapModel)
{
READ_LOCK();
// make sure ptr is valid
......@@ -342,8 +352,8 @@ void MarkerListModel::registerSnapModel(const std::weak_ptr<SnapModel> &snapMode
// we now add the already existing markers to the snap
for (const auto &marker : m_markerList) {
GenTime pos = marker.first;
ptr->addPoint(pos.frames(pCore->getCurrentFps()));
qDebug()<<" *- *-* REGISTEING MARKER: "<<marker.first.frames(pCore->getCurrentFps());
ptr->addPoint(marker.first.frames(pCore->getCurrentFps()));
}
} else {
qDebug() << "Error: added snapmodel is null";
......
......@@ -35,7 +35,7 @@
class ClipController;
class DocUndoStack;
class SnapModel;
class SnapInterface;
/* @brief This class is the model for a list of markers.
A marker is defined by a time, a type (the color used to represent it) and a comment string.
......@@ -97,6 +97,9 @@ public:
/* @brief Returns all markers in model */
QList<CommentedTime> getAllMarkers() const;
/* @brief Returns all markers positions in model */
std::vector<size_t> getSnapPoints() const;
/* @brief Returns true if a marker exists at given pos
Notice that add/remove queries are done in real time (gentime), but this request is made in frame
*/
......@@ -108,7 +111,7 @@ public:
The snap logic for clips is managed from the Timeline
Note that no deregistration is necessary, the weak_ptr will be discarded as soon as it becomes invalid.
*/
void registerSnapModel(const std::weak_ptr<SnapModel> &snapModel);
void registerSnapModel(const std::weak_ptr<SnapInterface> &snapModel);
/* @brief Exports the model to json using format above */
QString toJson() const;
......@@ -172,7 +175,7 @@ private:
mutable QReadWriteLock m_lock; // This is a lock that ensures safety in case of concurrent access
std::map<GenTime, std::pair<QString, int>> m_markerList;
std::vector<std::weak_ptr<SnapModel>> m_registeredSnaps;
std::vector<std::weak_ptr<SnapInterface>> m_registeredSnaps;
signals:
void modelChanged();
......
......@@ -5,6 +5,7 @@ set(kdenlive_SRCS
timeline2/model/compositionmodel.cpp
timeline2/model/groupsmodel.cpp
timeline2/model/snapmodel.cpp
timeline2/model/clipsnapmodel.cpp
timeline2/model/timelinefunctions.cpp
timeline2/model/timelineitemmodel.cpp
timeline2/model/timelinemodel.cpp
......
......@@ -21,6 +21,7 @@
#include "clipmodel.hpp"
#include "bin/projectclip.h"
#include "bin/projectitemmodel.h"
#include "clipsnapmodel.hpp"
#include "core.h"
#include "effects/effectstack/model/effectstackmodel.hpp"
#include "logger.hpp"
......@@ -37,6 +38,7 @@ ClipModel::ClipModel(const std::shared_ptr<TimelineModel> &parent, std::shared_p
: MoveableItem<Mlt::Producer>(parent, id)
, m_producer(std::move(prod))
, m_effectStack(EffectStackModel::construct(m_producer, {ObjectType::TimelineClip, m_id}, parent->m_undoStack))
, m_clipMarkerModel(new ClipSnapModel())
, m_binClipId(binClipId)
, forceThumbReload(false)
, m_currentState(state)
......@@ -81,6 +83,7 @@ int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QSt
TRACE_CONSTR(clip.get(), parent, binClipId, id, state, speed);
clip->setClipState_lambda(state)();
parent->registerClip(clip);
clip->m_clipMarkerModel->setReferenceModel(binClip->getMarkerModel());
return id;
}
......@@ -110,6 +113,7 @@ int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QSt
clip->setClipState_lambda(state)();
clip->m_effectStack->importEffects(producer, state, result.second);
parent->registerClip(clip);
clip->m_clipMarkerModel->setReferenceModel(binClip->getMarkerModel());
return id;
}
......@@ -184,7 +188,7 @@ bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool l
}
Fun operation = [this, inPoint, outPoint, track_operation]() {
if (track_operation()) {
m_producer->set_in_and_out(inPoint, outPoint);
setInOut(inPoint, outPoint);
return true;
}
return false;
......@@ -208,7 +212,7 @@ bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool l
}
Fun reverse = [this, old_in, old_out, track_reverse]() {
if (track_reverse()) {
m_producer->set_in_and_out(old_in, old_out);
setInOut(old_in, old_out);
return true;
}
return false;
......@@ -510,12 +514,36 @@ void ClipModel::setShowKeyframes(bool show)
service()->set("kdenlive:hide_keyframes", (int)!show);
}
void ClipModel::setPosition(int pos)
{
MoveableItem::setPosition(pos);
m_clipMarkerModel->updateSnapModelPos(pos);
}
void ClipModel::setInOut(int in, int out)
{
MoveableItem::setInOut(in, out);
m_clipMarkerModel->updateSnapModelInOut(std::pair<int, int>(in, out));
}
void ClipModel::setCurrentTrackId(int tid, bool finalMove)
{
if (tid == m_currentTrackId) {
return;
}
bool registerSnap = m_currentTrackId == -1 && tid > -1;
if (m_currentTrackId > -1 && tid == -1) {
// Removing clip
m_clipMarkerModel->deregisterSnapModel();
}
MoveableItem::setCurrentTrackId(tid, finalMove);
if (registerSnap) {
if (auto ptr = m_parent.lock()) {
m_clipMarkerModel->registerSnapModel(ptr->m_snaps, getPosition(), getIn(), getOut());
}
}
if (finalMove && tid != -1) {
refreshProducerFromBin(m_currentState);
}
......@@ -692,7 +720,9 @@ int ClipModel::getSubPlaylistIndex() const
{
return m_subPlaylistIndex;
}
void ClipModel::setSubPlaylistIndex(int index)
{
m_subPlaylistIndex = index;
}
......@@ -35,6 +35,7 @@ class MarkerListModel;
class TimelineModel;
class TrackModel;
class KeyframeModel;
class ClipSnapModel;
/* @brief This class represents a Clip object, as viewed by the backend.
In general, the Gui associated with it will send modification queries (such as resize or move), and this class authorize them or not depending on the
......@@ -162,6 +163,8 @@ protected:
bool requestResize(int size, bool right, Fun &undo, Fun &redo, bool logUndo = true) override;
void setCurrentTrackId(int tid, bool finalMove = true) override;
void setPosition(int pos) override;
void setInOut(int in, int out) override;
/* @brief This function change the global (timeline-wise) enabled state of the effects
*/
......@@ -200,6 +203,7 @@ protected:
std::shared_ptr<Mlt::Producer> getProducer();
std::shared_ptr<EffectStackModel> m_effectStack;
std::shared_ptr<ClipSnapModel> m_clipMarkerModel;
QString m_binClipId; // This is the Id of the bin clip this clip corresponds to.
......
/***************************************************************************
* Copyright (C) 2019 by Jean-Baptiste Mardelle *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "bin/model/markerlistmodel.hpp"
#include "clipsnapmodel.hpp"
#include <QDebug>
#include <climits>
#include <cstdlib>
#include <memory>
ClipSnapModel::ClipSnapModel() = default;
void ClipSnapModel::addPoint(int position)
{
m_snapPoints.insert(position);
if (position <= m_inPoint || position >= m_outPoint) {
return;
}
if (auto ptr = m_registeredSnap.lock()) {
ptr->addPoint(m_position + position - m_inPoint);
}
}
void ClipSnapModel::removePoint(int position)
{
m_snapPoints.erase(position);
if (position <= m_inPoint || position >= m_outPoint) {
return;
}
if (auto ptr = m_registeredSnap.lock()) {
ptr->removePoint(m_position + position - m_inPoint);
}
}
void ClipSnapModel::updateSnapModelPos(int newPos)
{
if (newPos == m_position) {
return;
}
removeAllSnaps();
m_position = newPos;
addAllSnaps();
}
void ClipSnapModel::updateSnapModelInOut(std::pair<int, int> newInOut)
{
removeAllSnaps();
m_inPoint = newInOut.first;
m_outPoint = newInOut.second;
addAllSnaps();
}
void ClipSnapModel::addAllSnaps()
{
if (auto ptr = m_registeredSnap.lock()) {
for (const auto &snap : m_snapPoints) {
if (snap >= m_inPoint && snap < m_outPoint) {
ptr->addPoint(m_position + snap - m_inPoint);
}
}
}
}
void ClipSnapModel::removeAllSnaps()
{
if (auto ptr = m_registeredSnap.lock()) {
for (const auto &snap : m_snapPoints) {
if (snap >= m_inPoint && snap < m_outPoint) {
ptr->removePoint(m_position + snap - m_inPoint);
}
}
}
}
void ClipSnapModel::registerSnapModel(const std::weak_ptr<SnapModel> &snapModel, int position, int in, int out)
{
// make sure ptr is valid
m_inPoint = in;
m_outPoint = out;
m_position = qMax(0, position);
m_registeredSnap = snapModel;
addAllSnaps();
}
void ClipSnapModel::deregisterSnapModel()
{
// make sure ptr is valid
removeAllSnaps();
m_registeredSnap.reset();
}
void ClipSnapModel::setReferenceModel(const std::weak_ptr<MarkerListModel> &markerModel)
{
m_parentModel = markerModel;
if (auto ptr = m_parentModel.lock()) {
ptr->registerSnapModel(std::static_pointer_cast<SnapInterface>(shared_from_this()));
}
}
/***************************************************************************
* Copyright (C) 2019 by Jean-Baptiste Mardelle *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifndef CLIPSNAPMODEL_H
#define CLIPSNAPMODEL_H
#include "snapmodel.hpp"
#include <map>
#include <memory>
#include <unordered_set>
class MarkerListModel;
/** @brief This class represents the snap points of a clip of the timeline.
Basically, one can add or remove snap points
*
*/
class ClipSnapModel : public virtual SnapInterface, public std::enable_shared_from_this<SnapInterface>
{
public:
ClipSnapModel();
/* @brief Adds a snappoint at given position */
void addPoint(int position) override;
/* @brief Removes a snappoint from given position */
void removePoint(int position) override;
void registerSnapModel(const std::weak_ptr<SnapModel> &snapModel, int position, int in, int out);
void deregisterSnapModel();
void setReferenceModel(const std::weak_ptr<MarkerListModel> &markerModel);
void updateSnapModelPos(int newPos);
void updateSnapModelInOut(std::pair<int, int> newInOut);
private:
std::weak_ptr<SnapModel> m_registeredSnap;
std::weak_ptr<MarkerListModel> m_parentModel;
std::unordered_set<int> m_snapPoints;
int m_inPoint;
int m_outPoint;
int m_position;
void addAllSnaps();
void removeAllSnaps();
};
#endif
......@@ -99,7 +99,7 @@ protected:
This function is meant to be called by the trackmodel, not directly by the user.
If you wish to actually move the item, use the requestMove slot.
*/
void setPosition(int position);
virtual void setPosition(int position);
/* Updates the stored track id of the item
This function is meant to be called by the timeline, not directly by the user.
If you wish to actually change the track the item, use the slot in the timeline
......
......@@ -23,6 +23,10 @@
#include <climits>
#include <cstdlib>
SnapInterface::SnapInterface() = default;
SnapInterface::~SnapInterface() = default;
SnapModel::SnapModel() = default;
void SnapModel::addPoint(int position)
......
......@@ -25,21 +25,38 @@
#include <map>
#include <vector>
/** @brief This is a base class for snap models (timeline, clips)
Implements only basic functions like add or remove snap points
*/
class SnapInterface
{
public:
SnapInterface();
virtual ~SnapInterface();
/* @brief Adds a snappoint at given position */
virtual void addPoint(int position) = 0;
/* @brief Removes a snappoint from given position */
virtual void removePoint(int position) = 0;
};
/** @brief This class represents the snap points of the timeline.
Basically, one can add or remove snap points, and query the closest snap point to a given location
*
*/
class SnapModel
class SnapModel : public virtual SnapInterface
{
public:
SnapModel();
/* @brief Adds a snappoint at given position */
void addPoint(int position);
void addPoint(int position) override;
/* @brief Removes a snappoint from given position */
void removePoint(int position);
void removePoint(int position) override;
/* @brief Retrieves closest point. Returns -1 if there is no snappoint available */
int getClosestPoint(int position);
......
......@@ -31,6 +31,7 @@
#include "kdenlivesettings.h"
#include "macros.hpp"
#include "trackmodel.hpp"
#include "snapmodel.hpp"
#include "transitions/transitionsrepository.hpp"
#include <QDebug>
#include <QFileInfo>
......@@ -62,7 +63,7 @@ void TimelineItemModel::finishConstruct(const std::shared_ptr<TimelineItemModel>
{
ptr->weak_this_ = ptr;
ptr->m_groups = std::make_unique<GroupsModel>(ptr);
guideModel->registerSnapModel(ptr->m_snaps);
guideModel->registerSnapModel(std::static_pointer_cast<SnapInterface>(ptr->m_snaps));
}
std::shared_ptr<TimelineItemModel> TimelineItemModel::construct(Mlt::Profile *profile, std::shared_ptr<MarkerListModel> guideModel,
......
......@@ -477,7 +477,7 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
checkRefresh = true;
}
auto update_snaps = [old_in, old_out, checkRefresh, this](int new_in, int new_out) {
auto update_snaps = [old_in, old_out, clipId, checkRefresh, this](int new_in, int new_out) {
if (auto ptr = m_parent.lock()) {
ptr->m_snaps->removePoint(old_in);
ptr->m_snaps->removePoint(old_out);
......
......@@ -614,6 +614,10 @@ void TimelineController::setOutPoint()
void TimelineController::editMarker(int cid, int position)
{
Q_ASSERT(m_model->isClip(cid));
if (position < m_model->getClipPosition(cid) || position > (m_model->getClipPosition(cid) + m_model->getClipPlaytime(cid))) {
pCore->displayMessage(i18n("Cannot find clip to edit marker"), InformationMessage, 500);
return;
}
std::shared_ptr<ProjectClip> clip = pCore->bin()->getBinClip(getClipBinId(cid));
GenTime pos(position - m_model->getClipPosition(cid) + m_model->getClipIn(cid), pCore->getCurrentFps());
clip->getMarkerModel()->editMarkerGui(pos, qApp->activeWindow(), false, clip.get());
......@@ -622,6 +626,10 @@ void TimelineController::editMarker(int cid, int position)
void TimelineController::addMarker(int cid, int position)
{
Q_ASSERT(m_model->isClip(cid));
if (position < m_model->getClipPosition(cid) || position > (m_model->getClipPosition(cid) + m_model->getClipPlaytime(cid))) {
pCore->displayMessage(i18n("Cannot find clip to add marker"), InformationMessage, 500);
return;
}
std::shared_ptr<ProjectClip> clip = pCore->bin()->getBinClip(getClipBinId(cid));
GenTime pos(position - m_model->getClipPosition(cid) + m_model->getClipIn(cid), pCore->getCurrentFps());
clip->getMarkerModel()->editMarkerGui(pos, qApp->activeWindow(), true, clip.get());
......@@ -630,6 +638,10 @@ void TimelineController::addMarker(int cid, int position)
void TimelineController::addQuickMarker(int cid, int position)
{
Q_ASSERT(m_model->isClip(cid));
if (position < m_model->getClipPosition(cid) || position > (m_model->getClipPosition(cid) + m_model->getClipPlaytime(cid))) {
pCore->displayMessage(i18n("Cannot find clip to add marker"), InformationMessage, 500);
return;
}
std::shared_ptr<ProjectClip> clip = pCore->bin()->getBinClip(getClipBinId(cid));
GenTime pos(position - m_model->getClipPosition(cid) + m_model->getClipIn(cid), pCore->getCurrentFps());
CommentedTime marker(pos, pCore->currentDoc()->timecode().getDisplayTimecode(pos, false), KdenliveSettings::default_marker_type());
......@@ -639,6 +651,10 @@ void TimelineController::addQuickMarker(int cid, int position)
void TimelineController::deleteMarker(int cid, int position)
{
Q_ASSERT(m_model->isClip(cid));
if (position < m_model->getClipPosition(cid) || position > (m_model->getClipPosition(cid) + m_model->getClipPlaytime(cid))) {
pCore->displayMessage(i18n("Cannot find clip to remove marker"), InformationMessage, 500);
return;
}
std::shared_ptr<ProjectClip> clip = pCore->bin()->getBinClip(getClipBinId(cid));
GenTime pos(position - m_model->getClipPosition(cid) + m_model->getClipIn(cid), pCore->getCurrentFps());
clip->getMarkerModel()->removeMarker(pos);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment