Commit 9f129e22 authored by Nicolas Carion's avatar Nicolas Carion

better abstraction for locking mechanism

parent ef8942a6
Pipeline #3588 passed with stage
in 14 minutes and 3 seconds
......@@ -36,8 +36,8 @@
#include <QSet>
#include <mlt++/MltPlaylist.h>
#include <mlt++/MltProducer.h>
#include <mlt++/MltTransition.h>
#include <mlt++/MltProfile.h>
#include <mlt++/MltTransition.h>
static QStringList m_errorMessage;
......@@ -83,7 +83,7 @@ bool constructTimelineFromMelt(const std::shared_ptr<TimelineItemModel> &timelin
timeline->setTrackProperty(tid, QStringLiteral("kdenlive:thumbs_format"), track->get("kdenlive:thumbs_format"));
timeline->setTrackProperty(tid, QStringLiteral("kdenlive:audio_rec"), track->get("kdenlive:audio_rec"));
if (lockState > 0) {
timeline->setTrackProperty(tid, QStringLiteral("kdenlive:locked_track"), QString::number(lockState));
timeline->setTrackLockedState(tid, true);
}
break;
}
......@@ -104,7 +104,7 @@ bool constructTimelineFromMelt(const std::shared_ptr<TimelineItemModel> &timelin
timeline->setTrackProperty(tid, QStringLiteral("kdenlive:thumbs_format"), local_playlist.get("kdenlive:thumbs_format"));
timeline->setTrackProperty(tid, QStringLiteral("kdenlive:audio_rec"), track->get("kdenlive:audio_rec"));
if (lockState > 0) {
timeline->setTrackProperty(tid, QStringLiteral("kdenlive:locked_track"), QString::number(lockState));
timeline->setTrackLockedState(tid, true);
}
break;
}
......@@ -149,11 +149,11 @@ bool constructTimelineFromMelt(const std::shared_ptr<TimelineItemModel> &timelin
}
compositionOk = timeline->requestCompositionInsertion(id, timeline->getTrackIndexFromPosition(t->get_b_track() - 1), t->get_a_track(), t->get_in(),
t->get_length(), std::move(transProps), compoId, undo, redo);
t->get_length(), std::move(transProps), compoId, undo, redo);
if (!compositionOk) {
qDebug() << "ERROR : failed to insert composition in track " << t->get_b_track() << ", position" << t->get_in() << ", ID: " << id
<< ", MLT ID: " << t->get("id");
//timeline->requestItemDeletion(compoId, false);
// timeline->requestItemDeletion(compoId, false);
m_errorMessage << i18n("Invalid composition %1 found on track %2 at %3.", t->get("id"), t->get_b_track(), t->get_in());
continue;
}
......
......@@ -359,7 +359,7 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int role) const
case TrackTagRole:
return getTrackTagById(id);
case IsLockedRole:
return getTrackById_const(id)->getProperty("kdenlive:locked_track").toInt() == 1;
return getTrackById_const(id)->isLocked();
case HeightRole: {
int collapsed = getTrackById_const(id)->getProperty("kdenlive:collapsed").toInt();
if (collapsed > 0) {
......
......@@ -60,6 +60,7 @@ RTTR_REGISTRATION
{
using namespace rttr;
registration::class_<TimelineModel>("TimelineModel")
.method("setTrackLockedState", &TimelineModel::setTrackLockedState)(parameter_names("trackId", "lock"))
.method("requestClipMove", select_overload<bool(int, int, int, bool, bool, bool)>(&TimelineModel::requestClipMove))(
parameter_names("clipId", "trackId", "position", "updateView", "logUndo", "invalidateTimeline"))
.method("requestCompositionMove", select_overload<bool(int, int, int, bool, bool)>(&TimelineModel::requestCompositionMove))(
......@@ -87,7 +88,6 @@ RTTR_REGISTRATION
parameter_names("clipId", "trackId", "position", "updateView", "logUndo", "invalidateTimeline"))
.method("requestFakeGroupMove", select_overload<bool(int, int, int, int, bool, bool)>(&TimelineModel::requestFakeGroupMove))(
parameter_names("clipId", "groupId", "delta_track", "delta_pos", "updateView", "logUndo"))
// (parameter_names("clipId", "groupId", "delta_track", "delta_pos", "updateView" "logUndo"))
.method("suggestClipMove", &TimelineModel::suggestClipMove)(parameter_names("clipId", "trackId", "position", "cursorPosition", "snapDistance"))
.method("suggestCompositionMove",
&TimelineModel::suggestCompositionMove)(parameter_names("compoId", "trackId", "position", "cursorPosition", "snapDistance"))
......@@ -138,7 +138,7 @@ void TimelineModel::prepareClose()
m_closing = true;
auto it = m_allTracks.begin();
while (it != m_allTracks.end()) {
(*it)->setProperty(QStringLiteral("kdenlive:locked_track"), (char*) nullptr);
(*it)->unlock();
++it;
}
}
......@@ -1359,12 +1359,12 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
// Sort compositions. We need to delete in the move direction from top to bottom
std::vector<int> sorted_compositions(all_compositions.begin(), all_compositions.end());
std::sort(sorted_compositions.begin(), sorted_compositions.end(), [this, delta_track, delta_pos](int clipId1, int clipId2) {
int p1 = delta_track < 0 ? getTrackMltIndex(m_allCompositions[clipId1]->getCurrentTrackId())
: delta_track > 0 ? -getTrackMltIndex(m_allCompositions[clipId1]->getCurrentTrackId())
: m_allCompositions[clipId1]->getPosition();
int p2 = delta_track < 0 ? getTrackMltIndex(m_allCompositions[clipId2]->getCurrentTrackId())
: delta_track > 0 ? -getTrackMltIndex(m_allCompositions[clipId2]->getCurrentTrackId())
: m_allCompositions[clipId2]->getPosition();
int p1 = delta_track < 0
? getTrackMltIndex(m_allCompositions[clipId1]->getCurrentTrackId())
: delta_track > 0 ? -getTrackMltIndex(m_allCompositions[clipId1]->getCurrentTrackId()) : m_allCompositions[clipId1]->getPosition();
int p2 = delta_track < 0
? getTrackMltIndex(m_allCompositions[clipId2]->getCurrentTrackId())
: delta_track > 0 ? -getTrackMltIndex(m_allCompositions[clipId2]->getCurrentTrackId()) : m_allCompositions[clipId2]->getPosition();
return delta_track == 0 ? (delta_pos > 0 ? p2 <= p1 : p1 <= p2) : p1 <= p2;
});
sorted_clips.insert(sorted_clips.end(), sorted_compositions.begin(), sorted_compositions.end());
......@@ -1548,7 +1548,7 @@ const QVariantList TimelineModel::getGroupData(int itemId)
{
QWriteLocker locker(&m_lock);
if (!m_groups->isInGroup(itemId)) {
return {itemId,getItemPosition(itemId),getItemPlaytime(itemId)};
return {itemId, getItemPosition(itemId), getItemPlaytime(itemId)};
}
int groupId = m_groups->getRootId(itemId);
QVariantList result;
......@@ -1562,9 +1562,9 @@ const QVariantList TimelineModel::getGroupData(int itemId)
void TimelineModel::processGroupResize(QVariantList startPos, QVariantList endPos, bool right)
{
Q_ASSERT(startPos.size() == endPos.size());
QMap <int, QPair<int,int> >startData;
QMap <int, QPair<int,int> >endData;
while( !startPos.isEmpty()) {
QMap<int, QPair<int, int>> startData;
QMap<int, QPair<int, int>> endData;
while (!startPos.isEmpty()) {
int id = startPos.takeFirst().toInt();
int in = startPos.takeFirst().toInt();
int duration = startPos.takeFirst().toInt();
......@@ -1574,15 +1574,15 @@ void TimelineModel::processGroupResize(QVariantList startPos, QVariantList endPo
duration = endPos.takeFirst().toInt();
endData.insert(id, {in, duration});
}
QMapIterator<int, QPair<int,int> > i(startData);
QList <int> changedItems;
QMapIterator<int, QPair<int, int>> i(startData);
QList<int> changedItems;
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool result = true;
while (i.hasNext()) {
i.next();
QPair<int,int> startItemPos = i.value();
QPair<int,int> endItemPos = endData.value(i.key());
QPair<int, int> startItemPos = i.value();
QPair<int, int> endItemPos = endData.value(i.key());
if (startItemPos.first != endItemPos.first || startItemPos.second != endItemPos.second) {
// Revert individual items to original position
requestItemResize(i.key(), startItemPos.second, right, false, 0, true);
......@@ -1590,7 +1590,7 @@ void TimelineModel::processGroupResize(QVariantList startPos, QVariantList endPo
}
}
for (int id : changedItems) {
QPair<int,int> endItemPos = endData.value(id);
QPair<int, int> endItemPos = endData.value(id);
result = result & requestItemResize(id, endItemPos.second, right, true, undo, redo, false);
if (!result) {
break;
......@@ -1769,7 +1769,7 @@ int TimelineModel::requestClipsGroup(const std::unordered_set<int> &ids, Fun &un
requestClearSelection();
}
int clipsCount = 0;
QList <int> tracks;
QList<int> tracks;
for (int id : ids) {
if (isClip(id)) {
int trackId = getClipTrackId(id);
......@@ -3182,7 +3182,7 @@ bool TimelineModel::requestSetSelection(const std::unordered_set<int> &ids)
if (ids.size() == 2) {
// Check if we selected 2 clips from the same master
QList<int> pairIds;
for(auto &id : roots) {
for (auto &id : roots) {
if (isClip(id)) {
pairIds << id;
}
......@@ -3227,3 +3227,20 @@ bool TimelineModel::requestSetSelection(const std::unordered_set<int> &ids, Fun
}
return false;
}
void TimelineModel::setTrackLockedState(int trackId, bool lock)
{
TRACE(trackId, lock);
if (lock) {
getTrackById(trackId)->lock();
} else {
getTrackById(trackId)->unlock();
}
};
std::unordered_set<int> TimelineModel::getAllTracksIds() const
{
std::unordered_set<int> result;
std::transform(m_iteratorTable.begin(), m_iteratorTable.end(), std::inserter(result, result.begin()), [&](const auto &track) { return track.first; });
return result;
}
......@@ -172,6 +172,9 @@ public:
/* @brief returns the number of tracks */
int getTracksCount() const;
/* @brief returns the ids of all the tracks */
std::unordered_set<int> getAllTracksIds() const;
/* @brief returns the track index (id) from its position */
int getTrackIndexFromPosition(int pos) const;
......@@ -322,6 +325,13 @@ public:
int getMirrorVideoTrackId(int trackId) const;
int getMirrorTrackId(int trackId) const;
/* @brief Sets a track in a given lock state
Locked tracks can't receive any operations (resize, move, insertion, deletion...)
@param trackId is of the track to alter
@param lock if true, the track will be locked, otherwise unlocked.
*/
Q_INVOKABLE void setTrackLockedState(int trackId, bool lock);
/* @brief Move a clip to a specific position
This action is undoable
Returns true on success. If it fails, nothing is modified.
......@@ -446,7 +456,7 @@ public:
/* Same function, but accumulates undo and redo and doesn't deal with snapping*/
bool requestItemResize(int itemId, int size, bool right, bool logUndo, Fun &undo, Fun &redo, bool blockUndo = false);
/* Returns a list of {id, position duration} for all elements in the group*/
Q_INVOKABLE const QVariantList getGroupData(int itemId);
Q_INVOKABLE void processGroupResize(QVariantList startPos, QVariantList endPos, bool right);
......
......@@ -1198,7 +1198,7 @@ bool TrackModel::isTimelineActive() const
bool TrackModel::shouldReceiveTimelineOp() const
{
READ_LOCK();
return m_track->get_int("kdenlive:timeline_active") && !m_track->get_int("kdenlive:locked_track");
return isTimelineActive() && !isLocked();
}
bool TrackModel::isAudioTrack() const
......@@ -1233,3 +1233,20 @@ bool TrackModel::copyEffect(const std::shared_ptr<EffectStackModel> &stackModel,
QWriteLocker locker(&m_lock);
return m_effectStack->copyEffect(stackModel->getEffectStackRow(rowId), isAudioTrack() ? PlaylistState::AudioOnly : PlaylistState::VideoOnly);
}
void TrackModel::lock()
{
setProperty(QStringLiteral("kdenlive:locked_track"), QStringLiteral("1"));
if (auto ptr = m_parent.lock()) {
QModelIndex ix = ptr->makeTrackIndexFromID(m_id);
ptr->dataChanged(ix, ix, {TimelineModel::IsLockedRole});
}
}
void TrackModel::unlock()
{
setProperty(QStringLiteral("kdenlive:locked_track"), (char *)nullptr);
if (auto ptr = m_parent.lock()) {
QModelIndex ix = ptr->makeTrackIndexFromID(m_id);
ptr->dataChanged(ix, ix, {TimelineModel::IsLockedRole});
}
}
......@@ -82,6 +82,10 @@ public:
*/
operator Mlt::Producer &() { return *m_track.get(); }
/* @brief This will lock the track: it will no longer allow insertion/deletion/resize of items */
void lock();
void unlock();
/* @brief Returns true if track is in locked state
*/
bool isLocked() const;
......
......@@ -314,7 +314,7 @@ Rectangle {
implicitWidth: trackHeadRoot.iconSize
iconName: isLocked ? 'kdenlive-lock' : 'kdenlive-unlock'
iconSource: isLocked ? 'qrc:///pics/kdenlive-lock.svg' : 'qrc:///pics/kdenlive-unlock.svg'
onClicked: controller.setTrackProperty(trackId, "kdenlive:locked_track", isLocked ? '0' : '1')
onClicked: controller.setTrackLockedState(trackId, !isLocked)
tooltip: isLocked? i18n('Unlock track') : i18n('Lock track')
SequentialAnimation {
......
......@@ -608,7 +608,7 @@ void TimelineController::setInPoint()
{
if (dragOperationRunning()) {
// Don't allow timeline operation while drag in progress
qDebug()<<"Cannot operate while dragging";
qDebug() << "Cannot operate while dragging";
return;
}
......@@ -635,7 +635,7 @@ void TimelineController::setOutPoint()
{
if (dragOperationRunning()) {
// Don't allow timeline operation while drag in progress
qDebug()<<"Cannot operate while dragging";
qDebug() << "Cannot operate while dragging";
return;
}
int cursorPos = timelinePosition();
......@@ -1745,29 +1745,21 @@ void TimelineController::switchTrackLock(bool applyToAll)
if (!applyToAll) {
// apply to active track only
bool locked = m_model->getTrackById_const(m_activeTrack)->isLocked();
m_model->setTrackProperty(m_activeTrack, QStringLiteral("kdenlive:locked_track"), locked ? QStringLiteral("0") : QStringLiteral("1"));
m_model->setTrackLockedState(m_activeTrack, !locked);
} else {
// Invert track lock
// Get track states first
QMap<int, bool> trackLockState;
int unlockedTracksCount = 0;
int tracksCount = m_model->getTracksCount();
for (int track = tracksCount - 1; track >= 0; track--) {
int trackIx = m_model->getTrackIndexFromPosition(track);
bool isLocked = m_model->getTrackById_const(trackIx)->getProperty("kdenlive:locked_track").toInt() == 1;
if (!isLocked) {
unlockedTracksCount++;
const auto ids = m_model->getAllTracksIds();
// count the number of tracks to be locked
int toBeLockedCount =
std::accumulate(ids.begin(), ids.end(), 0, [this](int s, int id) { return s + (m_model->getTrackById_const(id)->isLocked() ? 0 : 1); });
bool leaveOneUnlocked = toBeLockedCount == m_model->getTracksCount();
for (const int id : ids) {
// leave active track unlocked
if (leaveOneUnlocked && id == m_activeTrack) {
continue;
}
trackLockState.insert(trackIx, isLocked);
}
if (unlockedTracksCount == tracksCount) {
// do not lock all tracks, leave active track unlocked
trackLockState.insert(m_activeTrack, true);
}
QMapIterator<int, bool> i(trackLockState);
while (i.hasNext()) {
i.next();
m_model->setTrackProperty(i.key(), QStringLiteral("kdenlive:locked_track"), i.value() ? QStringLiteral("0") : QStringLiteral("1"));
bool isLocked = m_model->getTrackById_const(id)->isLocked();
m_model->setTrackLockedState(id, !isLocked);
}
}
}
......@@ -2425,8 +2417,8 @@ void TimelineController::finishRecording(const QString &recordedFile)
res = m_model->requestClipInsertion(binId, m_recordTrack, m_recordStart.first, id, true, true, false);
}
};
QString binId = ClipCreator::createClipFromFile(recordedFile, pCore->projectItemModel()->getRootFolder()->clipId(), pCore->projectItemModel(), undo,
redo, callBack);
QString binId =
ClipCreator::createClipFromFile(recordedFile, pCore->projectItemModel()->getRootFolder()->clipId(), pCore->projectItemModel(), undo, redo, callBack);
if (binId != QStringLiteral("-1")) {
pCore->pushUndo(undo, redo, i18n("Record audio"));
}
......
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