Commit bc9de7c4 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Make resizing mix work, fix crash on clip deletion

parent 3c0a8db4
......@@ -155,7 +155,7 @@ void ClipModel::deregisterClipToBin()
ClipModel::~ClipModel() = default;
bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool logUndo)
bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool logUndo, bool hasMix)
{
QWriteLocker locker(&m_lock);
// qDebug() << "RESIZE CLIP" << m_id << "target size=" << size << "right=" << right << "endless=" << m_endlessResize << "length" <<
......@@ -206,7 +206,7 @@ bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool l
if (right && ptr->getTrackById_const(m_currentTrackId)->isLastClip(getPosition())) {
trackDuration = ptr->getTrackById_const(m_currentTrackId)->trackDuration();
}
track_operation = ptr->getTrackById(m_currentTrackId)->requestClipResize_lambda(m_id, inPoint, outPoint, right);
track_operation = ptr->getTrackById(m_currentTrackId)->requestClipResize_lambda(m_id, inPoint, outPoint, right, hasMix);
} else {
qDebug() << "Error : Moving clip failed because parent timeline is not available anymore";
Q_ASSERT(false);
......@@ -224,7 +224,6 @@ bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool l
} else {
roles.push_back(TimelineModel::OutPointRole);
}
Fun operation = [this, inPoint, outPoint, roles, oldIn, oldOut, right, logUndo, track_operation]() {
if (track_operation()) {
setInOut(inPoint, outPoint);
......@@ -256,7 +255,7 @@ bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool l
return false;
};
if (operation()) {
Fun reverse = []() { return true; };
Fun reverse = [hasMix]() { return true; };
if (logUndo) {
// Now, we are in the state in which the timeline should be when we try to revert current action. So we can build the reverse action from here
if (m_currentTrackId != -1) {
......@@ -269,7 +268,7 @@ bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool l
ptr->getTrackById(m_currentTrackId)->m_effectStack->adjustStackLength(true, 0, trackDuration, 0, newDuration, 0, undo, redo, logUndo);
}
}
track_reverse = ptr->getTrackById(m_currentTrackId)->requestClipResize_lambda(m_id, old_in, old_out, right);
track_reverse = ptr->getTrackById(m_currentTrackId)->requestClipResize_lambda(m_id, old_in, old_out, right, hasMix);
}
}
reverse = [this, old_in, old_out, track_reverse, logUndo, oldIn, oldOut, right, roles]() {
......
......@@ -166,7 +166,7 @@ protected:
@param undo Lambda function containing the current undo stack. Will be updated with current operation
@param redo Lambda function containing the current redo queue. Will be updated with current operation
*/
bool requestResize(int size, bool right, Fun &undo, Fun &redo, bool logUndo = true) override;
bool requestResize(int size, bool right, Fun &undo, Fun &redo, bool logUndo = true, bool hasMix = false) override;
void setCurrentTrackId(int tid, bool finalMove = true) override;
void setPosition(int pos) override;
......
......@@ -77,8 +77,9 @@ int CompositionModel::construct(const std::weak_ptr<TimelineModel> &parent, cons
return id;
}
bool CompositionModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool logUndo)
bool CompositionModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool logUndo, bool hasMix)
{
Q_UNUSED(hasMix);
QWriteLocker locker(&m_lock);
if (size <= 0) {
return false;
......
......@@ -113,7 +113,7 @@ protected:
@param undo Lambda function containing the current undo stack. Will be updated with current operation
@param redo Lambda function containing the current redo queue. Will be updated with current operation
*/
bool requestResize(int size, bool right, Fun &undo, Fun &redo, bool logUndo = true) override;
bool requestResize(int size, bool right, Fun &undo, Fun &redo, bool logUndo = true, bool hasMix = false) override;
private:
int m_a_track;
......
......@@ -99,7 +99,7 @@ protected:
@param undo Lambda function containing the current undo stack. Will be updated with current operation
@param redo Lambda function containing the current redo queue. Will be updated with current operation
*/
virtual bool requestResize(int size, bool right, Fun &undo, Fun &redo, bool logUndo = true) = 0;
virtual bool requestResize(int size, bool right, Fun &undo, Fun &redo, bool logUndo = true, bool hasMix = false) = 0;
/* Updates the stored position of the item
This function is meant to be called by the trackmodel, not directly by the user.
......
......@@ -1631,14 +1631,23 @@ bool TimelineModel::requestClipInsertion(const QString &binClipId, int trackId,
return true;
}
bool TimelineModel::requestItemDeletion(int itemId, Fun &undo, Fun &redo)
bool TimelineModel::requestItemDeletion(int itemId, Fun &undo, Fun &redo, bool logUndo)
{
QWriteLocker locker(&m_lock);
if (m_groups->isInGroup(itemId)) {
return requestGroupDeletion(itemId, undo, redo);
}
if (isClip(itemId)) {
return requestClipDeletion(itemId, undo, redo);
int tid = m_allClips[itemId]->getCurrentTrackId();
bool syncMix = false;
if (tid > -1) {
syncMix = getTrackById_const(tid)->hasMix(itemId);
}
bool result = requestClipDeletion(itemId, undo, redo);
if (syncMix) {
getTrackById_const(tid)->syncronizeMixes(logUndo);
}
return result;
}
if (isComposition(itemId)) {
return requestCompositionDeletion(itemId, undo, redo);
......@@ -1664,7 +1673,7 @@ bool TimelineModel::requestItemDeletion(int itemId, bool logUndo)
}
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool res = requestItemDeletion(itemId, undo, redo);
bool res = requestItemDeletion(itemId, undo, redo, logUndo);
if (res && logUndo) {
PUSH_UNDO(undo, redo, actionLabel);
}
......@@ -2347,6 +2356,28 @@ void TimelineModel::processGroupResize(QVariantList startPos, QVariantList endPo
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool result = true;
QVector <int> mixTracks;
QList <int> ids = startData.keys();
for (auto &id : ids) {
if (isClip(id)) {
int tid = m_allClips[id]->getCurrentTrackId();
if (tid > -1 && getTrackById(tid)->hasMix(id)) {
if (!mixTracks.contains(tid)) {
mixTracks << tid;
}
}
}
}
Fun sync_mix = []() { return true; };
if (!mixTracks.isEmpty()) {
sync_mix = [this, mixTracks]() {
for (auto &tid : mixTracks) {
getTrackById_const(tid)->syncronizeMixes(true);
}
return true;
};
PUSH_LAMBDA(sync_mix, undo);
}
while (i.hasNext()) {
i.next();
QPair<int, int> startItemPos = i.value();
......@@ -2365,6 +2396,8 @@ void TimelineModel::processGroupResize(QVariantList startPos, QVariantList endPo
}
}
if (result) {
sync_mix();
PUSH_LAMBDA(sync_mix, redo);
PUSH_UNDO(undo, redo, i18n("Resize group"));
} else {
undo();
......@@ -2473,17 +2506,18 @@ int TimelineModel::requestItemResizeInfo(int itemId, int in, int out, int size,
if (snapDistance > 0 && getItemTrackId(itemId) != -1) {
Fun temp_undo = []() { return true; };
Fun temp_redo = []() { return true; };
int trackId = getItemTrackId(itemId);
if (right && size > out - in && isClip(itemId)) {
int playlist = m_allClips[itemId]->getSubPlaylistIndex();
int targetPos = in + size - 1;
int trackId = getItemTrackId(itemId);
if (!getTrackById_const(trackId)->isBlankAt(targetPos)) {
size = getTrackById_const(trackId)->getBlankEnd(out + 1) - in;
if (!getTrackById_const(trackId)->isBlankAt(targetPos, playlist)) {
size = getTrackById_const(trackId)->getBlankEnd(out + 1, playlist) - in;
}
} else if (!right && size > (out - in) && isClip(itemId)) {
int targetPos = out - size;
int trackId = getItemTrackId(itemId);
if (!getTrackById_const(trackId)->isBlankAt(targetPos)) {
size = out - getTrackById_const(trackId)->getBlankStart(in - 1);
int playlist = m_allClips[itemId]->getSubPlaylistIndex();
if (!getTrackById_const(trackId)->isBlankAt(targetPos, playlist)) {
size = out - getTrackById_const(trackId)->getBlankStart(in - 1, playlist);
}
}
int timelinePos = pCore->getTimelinePosition();
......@@ -2494,7 +2528,8 @@ int TimelineModel::requestItemResizeInfo(int itemId, int in, int out, int size,
// only test move if proposed_size is valid
bool success = false;
if (isClip(itemId)) {
success = m_allClips[itemId]->requestResize(proposed_size, right, temp_undo, temp_redo, false);
bool hasMix = getTrackById_const(trackId)->hasMix(itemId);
success = m_allClips[itemId]->requestResize(proposed_size, right, temp_undo, temp_redo, false, hasMix);
} else {
success = m_allCompositions[itemId]->requestResize(proposed_size, right, temp_undo, temp_redo, false);
}
......@@ -2559,8 +2594,26 @@ int TimelineModel::requestItemResize(int itemId, int size, bool right, bool logU
size = requestItemResizeInfo(itemId, in, out, size, right, snapDistance);
Fun undo = []() { return true; };
Fun redo = []() { return true; };
Fun sync_mix = []() { return true; };
std::unordered_set<int> all_items;
all_items.insert(itemId);
if (isClip(itemId) && logUndo) {
int tid = getItemTrackId(itemId);
if (right) {
if (getTrackById_const(tid)->hasEndMix(itemId)) {
sync_mix = [this, tid, logUndo]() {
getTrackById_const(tid)->syncronizeMixes(logUndo);
return true;
};
}
} else if (getTrackById_const(tid)->hasStartMix(itemId)) {
sync_mix = [this, tid, logUndo]() {
getTrackById_const(tid)->syncronizeMixes(logUndo);
return true;
};
}
}
PUSH_LAMBDA(sync_mix, undo);
if (!allowSingleResize && m_groups->isInGroup(itemId)) {
int groupId = m_groups->getRootId(itemId);
std::unordered_set<int> items = m_groups->getLeaves(groupId);
......@@ -2608,6 +2661,8 @@ int TimelineModel::requestItemResize(int itemId, int size, bool right, bool logU
}
if (result && logUndo) {
if (isClip(itemId)) {
sync_mix();
PUSH_LAMBDA(sync_mix, redo);
PUSH_UNDO(undo, redo, i18n("Resize clip"))
} else {
PUSH_UNDO(undo, redo, i18n("Resize composition"))
......@@ -2625,7 +2680,36 @@ bool TimelineModel::requestItemResize(int itemId, int size, bool right, bool log
Fun local_redo = []() { return true; };
bool result = false;
if (isClip(itemId)) {
result = m_allClips[itemId]->requestResize(size, right, local_undo, local_redo, logUndo);
bool hasMix = false;
if (!logUndo) {
int tid = m_allClips[itemId]->getCurrentTrackId();
if (tid > -1) {
if (right) {
if (getTrackById_const(tid)->hasEndMix(itemId)) {
std::pair<MixInfo, MixInfo> mixData = getTrackById_const(tid)->getMixInfo(itemId);
int mixDuration = mixData.second.firstClipInOut.second - (mixData.second.secondClipInOut.second - size);
m_allClips[mixData.second.secondClipId]->setMixDuration(mixDuration);
hasMix = true;
QModelIndex ix = makeClipIndexFromID(mixData.second.secondClipId);
emit dataChanged(ix, ix, {TimelineModel::MixRole});
}
} else if (getTrackById_const(tid)->hasStartMix(itemId)) {
std::pair<MixInfo, MixInfo> mixData = getTrackById_const(tid)->getMixInfo(itemId);
// We have a mix at clip start
int mixDuration = mixData.first.firstClipInOut.second - (mixData.first.secondClipInOut.second - size);
m_allClips[itemId]->setMixDuration(mixDuration);
hasMix = true;
QModelIndex ix = makeClipIndexFromID(itemId);
emit dataChanged(ix, ix, {TimelineModel::MixRole});
}
}
} else {
int tid = m_allClips[itemId]->getCurrentTrackId();
if (tid > -1 && getTrackById_const(tid)->hasMix(itemId)) {
hasMix = true;
}
}
result = m_allClips[itemId]->requestResize(size, right, local_undo, local_redo, logUndo, hasMix);
} else {
Q_ASSERT(isComposition(itemId));
result = m_allCompositions[itemId]->requestResize(size, right, local_undo, local_redo, logUndo);
......
......@@ -442,7 +442,7 @@ public:
@param logUndo if set to false, no undo object is stored */
Q_INVOKABLE bool requestItemDeletion(int itemId, bool logUndo = true);
/* Same function, but accumulates undo and redo*/
bool requestItemDeletion(int itemId, Fun &undo, Fun &redo);
bool requestItemDeletion(int itemId, Fun &undo, Fun &redo, bool logUndo = false);
/* @brief Move a group to a specific position
This action is undoable
......
......@@ -536,7 +536,7 @@ int TrackModel::getBlankSizeNearComposition(int compoId, bool after)
return length;
}
Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right, bool allowMix)
Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right, bool hasMix)
{
QWriteLocker locker(&m_lock);
int clip_position = m_allClips[clipId]->getPosition();
......@@ -618,8 +618,7 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
int blank = -1;
int other_blank_end = getBlankEnd(clip_position, (target_track + 1) % 2);
if (right) {
if (target_clip == m_playlists[target_track].count() - 1 && (allowMix || other_blank_end >= out)) {
// clip is last, it can always be extended
if (target_clip == m_playlists[target_track].count() - 1 && (hasMix || other_blank_end >= out)) { // clip is last, it can always be extended
return [this, target_clip, target_track, in, out, update_snaps, clipId]() {
if (isLocked()) return false;
// color, image and title clips can have unlimited resize
......@@ -654,7 +653,7 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
}
if (m_playlists[target_track].is_blank(blank)) {
int blank_length = m_playlists[target_track].clip_length(blank);
if (blank_length + delta >= 0 && (allowMix || other_blank_end >= out)) {
if (blank_length + delta >= 0 && (hasMix || other_blank_end >= out)) {
return [blank_length, blank, right, clipId, delta, update_snaps, this, in, out, target_clip, target_track]() {
if (isLocked()) return false;
int target_clip_mutable = target_clip;
......@@ -687,6 +686,8 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
m_playlists[target_track].unlock();
return err == 0;
};
} else {
qDebug()<<"==== NOPE ABORT RESIZE";
}
}
......@@ -1042,6 +1043,21 @@ int TrackModel::getBlankStart(int position)
return result;
}
int TrackModel::getBlankStart(int position, int track)
{
READ_LOCK();
int result = 0;
if (!m_playlists[track].is_blank_at(position)) {
return position;
}
int clip_index = m_playlists[track].get_clip_index_at(position);
int start = m_playlists[track].clip_start(clip_index);
if (start > result) {
result = start;
}
return result;
}
int TrackModel::getBlankEnd(int position, int track)
{
READ_LOCK();
......@@ -1456,7 +1472,6 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
}
return true;
};
// lock MLT playlist so that we don't end up with invalid frames in monitor
auto operation = requestClipDeletion_lambda(clipIds.second, updateView, finalMove, groupMove, finalMove);
bool res = operation();
......@@ -1616,6 +1631,7 @@ bool TrackModel::createMix(std::pair<int, int> clipIds, std::pair<int, int> mixD
if (auto ptr = m_parent.lock()) {
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipIds.second));
movedClip->setMixDuration(mixData.second);
qDebug()<<"==== CREATING MIX WITH DURATION:"<<mixData.second;
QModelIndex ix = ptr->makeClipIndexFromID(clipIds.second);
emit ptr->dataChanged(ix, ix, {TimelineModel::MixRole});
// Insert mix transition
......@@ -1676,7 +1692,6 @@ void TrackModel::syncronizeMixes(bool finalMove)
toDelete << secondClipId;
m_mixList.remove(firstClip);
} else {
qDebug()<<"=== ADJUSTING MIX FOR CID: "<<secondClipId<<" TO: "<<mixIn<<" - "<<mixOut;
transition.set_in_and_out(mixIn, mixOut);
}
if (auto ptr = m_parent.lock()) {
......@@ -1707,6 +1722,16 @@ bool TrackModel::hasMix(int cid) const
return false;
}
bool TrackModel::hasStartMix(int cid) const
{
return m_sameCompositions.count(cid) > 0;
}
bool TrackModel::hasEndMix(int cid) const
{
return m_mixList.contains(cid);
}
bool TrackModel::loadMix(Mlt::Transition &t)
{
//TODO: manage case where both mix clips would start at same position
......
......@@ -151,7 +151,7 @@ protected:
@param out is the new ending on the clip
@param right is true if we change the right side of the clip, false otherwise
*/
Fun requestClipResize_lambda(int clipId, int in, int out, bool right, bool allowMix = false);
Fun requestClipResize_lambda(int clipId, int in, int out, bool right, bool hasMix = false);
/* @brief Performs an insertion of the given clip.
Returns true if the operation succeeded, and otherwise, the track is not modified.
......@@ -207,6 +207,8 @@ protected:
int getBlankSizeNearClip(int clipId, bool after);
int getBlankSizeNearComposition(int compoId, bool after);
int getBlankStart(int position);
/* @brief Returns the start of the blank on a specific playlist */
int getBlankStart(int position, int track);
int getBlankSizeAtPos(int frame);
/* @brief Returns true if clip at position is the last on playlist
* @param position the position in playlist
......@@ -295,8 +297,12 @@ protected:
bool isAvailable(int position, int duration, int playlist);
/* @brief Returns the number of same track transitions (mix) in this track */
int mixCount() const;
/* @brief Returns true if the track has a same track transition for this clip (cid) */
/** @brief Returns true if the track has a same track transition for this clip (cid) */
bool hasMix(int cid) const;
/** @brief Returns true if this clip has a mix at start */
bool hasStartMix(int cid) const;
/** @brief Returns true if this clip has a mix at end */
bool hasEndMix(int cid) const;
public slots:
/*Delete the current track and all its associated clips */
......
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