Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Fix various timeline corruptions

parent 547d99d4
......@@ -42,7 +42,7 @@ ClipModel::ClipModel(std::shared_ptr<TimelineModel> parent, std::shared_ptr<Mlt:
, m_binClipId(binClipId)
{
m_producer->set("kdenlive:id", binClipId.toUtf8().constData());
m_producer->set("_kdenlive_cid", getId());
m_producer->set("_kdenlive_cid", id);
std::shared_ptr<ProjectClip> binClip = pCore->projectItemModel()->getClipByBinID(m_binClipId);
if (binClip) {
m_endlessResize = !binClip->hasLimitedDuration();
......@@ -54,8 +54,6 @@ ClipModel::ClipModel(std::shared_ptr<TimelineModel> parent, std::shared_ptr<Mlt:
int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QString &binClipId, int id, PlaylistState::ClipState state)
{
std::shared_ptr<ProjectClip> binClip = pCore->projectItemModel()->getClipByBinID(binClipId);
//std::shared_ptr<Mlt::Producer> originalProducer = binClip->originalProducer();
//std::shared_ptr<Mlt::Producer> cutProducer(originalProducer->cut());
std::shared_ptr<Mlt::Producer> cutProducer = binClip->timelineProducer(state);
return construct(parent, binClipId, cutProducer, id);
}
......@@ -257,7 +255,7 @@ bool ClipModel::isAudioOnly() const
return service.contains(QStringLiteral("avformat")) && (getIntProperty(QStringLiteral("video_index")) == -1);
}
void ClipModel::refreshProducerFromBin()
void ClipModel::refreshProducerFromBin(PlaylistState::ClipState state)
{
if (getProperty("mlt_service") == QLatin1String("timewarp")) {
// slowmotion producer, keep it
......@@ -276,8 +274,10 @@ void ClipModel::refreshProducerFromBin()
int in = getIn();
int out = getOut();
std::shared_ptr<ProjectClip> binClip = pCore->projectItemModel()->getClipByBinID(m_binClipId);
std::shared_ptr<Mlt::Producer> binProducer = binClip->timelineProducer(PlaylistState::Original, m_currentTrackId);
m_producer.reset(binProducer->cut(in, out));
std::shared_ptr<Mlt::Producer> binProducer = binClip->timelineProducer(state, m_currentTrackId);
m_producer = std::move(binProducer);
m_producer->set_in_and_out(in, out);
//m_producer.reset(binProducer->cut(in, out));
// replant effect stack in updated service
m_effectStack->resetService(m_producer);
m_producer->set("kdenlive:id", binClip->AbstractProjectItem::clipId().toUtf8().constData());
......@@ -392,7 +392,11 @@ bool ClipModel::setClipState(PlaylistState::ClipState state)
std::shared_ptr<Mlt::Producer> binProducer = binClip->timelineProducer(state, m_currentTrackId);
int in = getIn();
int out = getOut();
m_producer.reset(binProducer->cut(in, out));
m_producer = std::move(binProducer);
m_producer->set_in_and_out(in, out);
m_producer->set("kdenlive:id", m_binClipId.toUtf8().constData());
m_producer->set("_kdenlive_cid", m_id);
// replant effect stack in updated service
m_effectStack->resetService(m_producer);
return true;
......
......@@ -128,7 +128,7 @@ protected:
void setTimelineEffectsEnabled(bool enabled);
/* @brief This functions should be called when the producer of the binClip changes, to allow refresh */
void refreshProducerFromBin();
void refreshProducerFromBin(PlaylistState::ClipState state = PlaylistState::Original);
/* @brief This functions replaces the current producer with a slowmotion one */
void useTimewarpProducer(double speed, int extraSpace);
......
......@@ -323,13 +323,13 @@ bool TimelineFunctions::changeClipState(std::shared_ptr<TimelineItemModel> timel
int trackId = timeline->getClipTrackId(clipId);
bool res = timeline->m_allClips[clipId]->setClipState(status);
// in order to make the producer change effective, we need to unplant / replant the clip in int track
int start = timeline->getItemPosition(clipId);
int end = start + timeline->getItemPlaytime(clipId);
if (trackId != -1) {
timeline->getTrackById(trackId)->replugClip(clipId);
QModelIndex ix = timeline->makeClipIndexFromID(clipId);
timeline->dataChanged(ix, ix, {TimelineModel::StatusRole});
timeline->invalidateClip(clipId);
int start = timeline->getItemPosition(clipId);
int end = start + timeline->getItemPlaytime(clipId);
timeline->checkRefresh(start, end);
}
return res;
......@@ -337,18 +337,18 @@ bool TimelineFunctions::changeClipState(std::shared_ptr<TimelineItemModel> timel
undo = [timeline, clipId, oldState]() {
bool res = timeline->m_allClips[clipId]->setClipState(oldState);
// in order to make the producer change effective, we need to unplant / replant the clip in int track
int start = timeline->getItemPosition(clipId);
int end = start + timeline->getItemPlaytime(clipId);
int trackId = timeline->getClipTrackId(clipId);
std::function<bool(void)> local_undo = []() { return true; };
std::function<bool(void)> local_redo = []() { return true; };
if (trackId != -1) {
int start = timeline->getItemPosition(clipId);
int end = start + timeline->getItemPlaytime(clipId);
timeline->getTrackById(trackId)->replugClip(clipId);
QModelIndex ix = timeline->makeClipIndexFromID(clipId);
timeline->dataChanged(ix, ix, {TimelineModel::StatusRole});
timeline->invalidateClip(clipId);
timeline->checkRefresh(start, end);
}
QModelIndex ix = timeline->makeClipIndexFromID(clipId);
timeline->dataChanged(ix, ix, {TimelineModel::StatusRole});
timeline->invalidateClip(clipId);
timeline->checkRefresh(start, end);
return res;
};
bool result = redo();
......@@ -370,10 +370,9 @@ bool TimelineFunctions::requestSplitAudio(std::shared_ptr<TimelineItemModel> tim
int track = timeline->getClipTrackId(cid);
int newTrack = timeline->getNextTrackId(track);
int newId;
bool res = copyClip(timeline, cid, newId, PlaylistState::AudioOnly, undo, redo);
TimelineFunctions::changeClipState(timeline, clipId, PlaylistState::VideoOnly);
bool res = copyClip(timeline, cid, newId, PlaylistState::AudioOnly, undo, redo);
res = res && timeline->requestClipMove(newId, newTrack, position, true, false, undo, redo);
//TimelineFunctions::changeClipState(timeline, newId, PlaylistState::AudioOnly);
std::unordered_set<int> clips;
clips.insert(clipId);
clips.insert(newId);
......
......@@ -477,11 +477,11 @@ bool TimelineModel::requestClipCreation(const QString &binClipId, int &id, Playl
}
ClipModel::construct(shared_from_this(), bid, clipId, state);
auto clip = m_allClips[clipId];
Fun local_redo = [clip, this]() {
Fun local_redo = [clip, this,state]() {
// We capture a shared_ptr to the clip, which means that as long as this undo object lives, the clip object is not deleted. To insert it back it is
// sufficient to register it.
registerClip(clip);
clip->refreshProducerFromBin();
clip->refreshProducerFromBin(state);
return true;
};
......
......@@ -151,7 +151,7 @@ public:
/* @brief returns the number of tracks */
int getTracksCount() const;
/* @brief returns the track index (id) from its position */
int getTrackIndexFromPosition(int pos) const;
......
......@@ -161,6 +161,7 @@ Fun TrackModel::requestClipInsertion_lambda(int clipId, int position, bool updat
m_playlists[0].lock();
std::shared_ptr<ClipModel> clip = ptr->getClipPtr(clipId);
int index = m_playlists[0].insert_at(position, *clip, 1);
m_playlists[0].consolidate_blanks();
m_playlists[0].unlock();
return index != -1 && end_function();
}
......@@ -183,6 +184,7 @@ Fun TrackModel::requestClipInsertion_lambda(int clipId, int position, bool updat
m_playlists[0].lock();
std::shared_ptr<ClipModel> clip = ptr->getClipPtr(clipId);
int index = m_playlists[0].insert_at(position, *clip, 1);
m_playlists[0].consolidate_blanks();
m_playlists[0].unlock();
return index != -1 && end_function();
}
......@@ -211,7 +213,6 @@ bool TrackModel::requestClipInsertion(int clipId, int position, bool updateView,
void TrackModel::replugClip(int clipId)
{
int clip_position = m_allClips[clipId]->getPosition();
qDebug()<<"--------------replugging clp";
auto clip_loc = getClipIndexAt(clip_position);
int target_track = clip_loc.first;
int target_clip = clip_loc.second;
......@@ -224,6 +225,7 @@ void TrackModel::replugClip(int clipId)
std::shared_ptr<ClipModel> clip = ptr->getClipPtr(clipId);
int index = m_playlists[target_track].insert_at(clip_position, *clip, 1);
}
m_playlists[target_track].consolidate_blanks();
delete prod;
m_playlists[target_track].unlock();
}
......
......@@ -174,6 +174,18 @@ Column{
value: model.resource
when: loader.status == Loader.Ready && !loader.item.isComposition
}
Binding {
target: loader.item
property: "clipId"
value: model.item
when: loader.status == Loader.Ready
}
Binding {
target: loader.item
property: "binId"
value: model.binId
when: loader.status == Loader.Ready && !loader.item.isComposition
}
sourceComponent: {
if (model.isComposition) {
return compositionDelegate
......@@ -183,8 +195,8 @@ Column{
}
onLoaded: {
console.log('loaded clip: ', model.start)
item.clipId= model.item
item.binId= model.binId
//item.clipId= model.item
//item.binId= model.binId
item.isComposition= model.isComposition
if (!model.isComposition) {
item.audioLevels= model.audioLevels
......
......@@ -23,10 +23,6 @@ Rectangle {
font.family: "Arial"
}
function currentTrackId() {
return tracksRepeater.itemAt(root.currentTrack).trackId
}
function moveSelectedTrack(offset) {
var newTrack = root.currentTrack + offset
var max = tracksRepeater.count;
......@@ -37,6 +33,7 @@ Rectangle {
}
console.log('Setting curr tk: ', newTrack, 'MAX: ',max)
root.currentTrack = newTrack
timeline.activeTrack = tracksRepeater.itemAt(root.currentTrack).trackId
}
function zoomByWheel(wheel) {
......@@ -202,13 +199,16 @@ Rectangle {
keys: 'kdenlive/producerslist'
onEntered: {
if (clipBeingMovedId == -1) {
var track = Logic.getTrackIdFromPos(drag.y)
if (track >= 0) {
//var track = Logic.getTrackIdFromPos(drag.y)
var track = Logic.getTrackIndexFromPos(drag.y)
if (track >= 0 && track < tracksRepeater.count) {
var frame = Math.round((drag.x + scrollView.flickableItem.contentX) / timeline.scaleFactor)
droppedPosition = frame
root.currentTrack = track
timeline.activeTrack = tracksRepeater.itemAt(track).trackId
//drag.acceptProposedAction()
clipBeingDroppedData = drag.getDataAsString('kdenlive/producerslist')
clipBeingDroppedId = timeline.insertClip(track, frame, clipBeingDroppedData, false, true)
clipBeingDroppedId = timeline.insertClip(timeline.activeTrack, frame, clipBeingDroppedData, false, true)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
} else {
drag.accepted = false
......@@ -226,15 +226,19 @@ Rectangle {
}
onPositionChanged: {
if (clipBeingMovedId == -1) {
var track = Logic.getTrackIdFromPos(drag.y)
var frame = Math.round((drag.x + scrollView.flickableItem.contentX) / timeline.scaleFactor)
frame = controller.suggestSnapPoint(frame, root.snapping)
if (clipBeingDroppedId >= 0){
controller.requestClipMove(clipBeingDroppedId, track, frame, true, false, false)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
} else {
clipBeingDroppedId = timeline.insertClip(track, frame, drag.getDataAsString('kdenlive/producerslist'), false, true)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
var track = Logic.getTrackIndexFromPos(drag.y)
if (track >= 0 && track < tracksRepeater.count) {
root.currentTrack = track
timeline.activeTrack = tracksRepeater.itemAt(track).trackId
var frame = Math.round((drag.x + scrollView.flickableItem.contentX) / timeline.scaleFactor)
frame = controller.suggestSnapPoint(frame, root.snapping)
if (clipBeingDroppedId >= 0){
controller.requestClipMove(clipBeingDroppedId, timeline.activeTrack, frame, true, false, false)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
} else {
clipBeingDroppedId = timeline.insertClip(timeline.activeTrack, frame, drag.getDataAsString('kdenlive/producerslist'), false, true)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
}
}
}
}
......@@ -391,7 +395,7 @@ Rectangle {
width: headerWidth
height: model.trackHeight
selected: false
current: index === currentTrack
current: item === timeline.activeTrack
trackId: item
onIsLockedChanged: tracksRepeater.itemAt(index).isLocked = isLocked
onMyTrackHeightChanged: {
......@@ -406,6 +410,7 @@ Rectangle {
}
onClicked: {
currentTrack = index
timeline.activeTrack = tracksRepeater.itemAt(currentTrack).trackId
console.log('track name: ',index, ' = ', model.name)
//timeline.selectTrackHead(currentTrack)
}
......@@ -519,6 +524,7 @@ Rectangle {
menu.clickedX = mouse.x
menu.clickedY = mouse.y
currentTrack = Logic.getTrackIndexFromPos(mouse.y - ruler.height)
timeline.activeTrack = tracksRepeater.itemAt(currentTrack).trackId
menu.popup()
}
}
......@@ -719,7 +725,7 @@ Rectangle {
border.color: Qt.rgba(activePalette.windowText.r, activePalette.windowText.g, activePalette.windowText.b, 0.1)
//Layout.fillWidth: true
height: model.trackHeight
color: tracksRepeater.itemAt(index) ? ((index === currentTrack) ? Qt.tint(getTrackColor(tracksRepeater.itemAt(index).isAudio, false), selectedTrackColor) : getTrackColor(tracksRepeater.itemAt(index).isAudio, false)) : 'red'
color: tracksRepeater.itemAt(index) ? ((tracksRepeater.itemAt(index).trackId === timeline.activeTrack) ? Qt.tint(getTrackColor(tracksRepeater.itemAt(index).isAudio, false), selectedTrackColor) : getTrackColor(tracksRepeater.itemAt(index).isAudio, false)) : 'red'
}
}
}
......@@ -865,11 +871,12 @@ Rectangle {
isAudio: audio
isMute: mute
isHidden: hidden
isCurrentTrack: currentTrack === index
isCurrentTrack: item === timeline.activeTrack
trackId: item
selection: timeline.selection
onClipClicked: {
root.currentTrack = index
timeline.activeTrack = tracksRepeater.itemAt(index).trackId
if (shiftClick === 1) {
timeline.addSelection(clip.clipId)
} else {
......@@ -910,6 +917,7 @@ Rectangle {
var track = tracksRepeater.itemAt(activeTrack)
if (controller.requestClipMove(clip.clipId, track.trackId, frame, false, false, false)) {
currentTrack = activeTrack;
timeline.activeTrack = track.trackId
clip.reparent(track)
clip.trackIndex = track.DelegateModel.itemsIndex
clip.trackId = track.trackId
......@@ -931,14 +939,18 @@ Rectangle {
}
}
}
} else {
console.log('+ + + + +\nWARNING CLIP DRAGGED TO INVALID TRACK: ',activeTrack,'\n+ + + +');
}
}
onCompositionDraggedToTrack: {
var y = pos - ruler.height
currentTrack = Logic.getTrackIndexFromPos(y)
var tk = Logic.getTrackIndexFromPos(y)
var frame = Math.round(composition.x / timeScale)
if (currentTrack >= 0 && currentTrack < tracksRepeater.count) {
if (tk >= 0 && tk < tracksRepeater.count) {
currentTrack = tk
var track = tracksRepeater.itemAt(currentTrack)
timeline.activeTrack = track.trackId
if (controller.requestCompositionMove(composition.clipId, track.trackId, frame, false, false)) {
composition.reparent(track)
composition.trackIndex = track.DelegateModel.itemsIndex
......
......@@ -55,6 +55,7 @@ TimelineController::TimelineController(KActionCollection *actionCollection, QObj
, m_seekPosition(-1)
, m_audioTarget(-1)
, m_videoTarget(-1)
, m_activeTrack(0)
, m_scale(3.0)
, m_usePreview(false)
, m_timelinePreview(nullptr)
......@@ -273,9 +274,7 @@ int TimelineController::insertClip(int tid, int position, const QString &data_st
{
int id;
if (tid == -1) {
QVariant returnedValue;
QMetaObject::invokeMethod(m_root, "currentTrackId", Q_RETURN_ARG(QVariant, returnedValue));
tid = returnedValue.toInt();
tid = m_activeTrack;
}
if (position == -1) {
position = m_position;
......@@ -303,6 +302,7 @@ void TimelineController::deleteSelectedClips()
for (int cid : m_selection.selectedClips) {
m_model->requestItemDeletion(cid);
}
m_selection.selectedClips.clear();
}
void TimelineController::copyItem()
......@@ -330,9 +330,7 @@ bool TimelineController::pasteItem(int clipId, int tid, int position)
position = getMousePos();
}
if (tid == -1) {
QVariant returnedValue;
QMetaObject::invokeMethod(m_root, "currentTrackId", Q_RETURN_ARG(QVariant, returnedValue));
tid = returnedValue.toInt();
tid = m_activeTrack;
}
if (position == -1) {
position = m_position;
......@@ -558,6 +556,12 @@ void TimelineController::setVideoTarget(int track)
emit videoTargetChanged();
}
void TimelineController::setActiveTrack(int track)
{
m_activeTrack = track;
emit activeTrackChanged();
}
void TimelineController::setSeekPosition(int position)
{
m_seekPosition = position;
......@@ -628,9 +632,7 @@ void TimelineController::cutClipUnderCursor(int position, int track)
}
if (!foundClip) {
if (track == -1) {
QVariant returnedValue;
QMetaObject::invokeMethod(m_root, "currentTrackId", Q_RETURN_ARG(QVariant, returnedValue));
track = returnedValue.toInt();
track = m_activeTrack;
}
if (track >= 0) {
int cid = m_model->getClipByPosition(track, position);
......@@ -755,9 +757,7 @@ void TimelineController::adjustFade(int cid, const QString &effectId, int durati
} else {
m_model->adjustEffectLength(cid, effectId, duration);
QModelIndex ix = m_model->makeClipIndexFromID(cid);
QVector <int> roles;
roles << TimelineModel::FadeInRole;
m_model->dataChanged(ix, ix, roles);
m_model->dataChanged(ix, ix, {TimelineModel::FadeInRole});
}
}
......@@ -774,9 +774,7 @@ void TimelineController::setCompositionATrack(int cid, int aTrack)
field->unlock();
refreshItem(cid);
QModelIndex modelIndex = m_model->makeCompositionIndexFromID(cid);
QVector <int> roles;
roles << TimelineModel::ItemATrack;
m_model->dataChanged(modelIndex, modelIndex, roles);
m_model->dataChanged(modelIndex, modelIndex, {TimelineModel::ItemATrack});
}
const QString TimelineController::getClipBinId(int clipId) const
......@@ -1018,9 +1016,7 @@ void TimelineController::insertSpace(int trackId, int frame)
frame = m_position;
}
if (trackId == -1) {
QVariant returnedValue;
QMetaObject::invokeMethod(m_root, "currentTrackId", Q_RETURN_ARG(QVariant, returnedValue));
trackId = returnedValue.toInt();
trackId = m_activeTrack;
}
QPointer<SpacerDialog> d = new SpacerDialog(GenTime(65, pCore->getCurrentFps()), pCore->currentDoc()->timecode(), qApp->activeWindow());
if (d->exec() != QDialog::Accepted) {
......@@ -1044,9 +1040,7 @@ void TimelineController::removeSpace(int trackId, int frame, bool affectAllTrack
frame = m_position;
}
if (trackId == -1) {
QVariant returnedValue;
QMetaObject::invokeMethod(m_root, "currentTrackId", Q_RETURN_ARG(QVariant, returnedValue));
trackId = returnedValue.toInt();
trackId = m_activeTrack;
}
// find blank duration
int spaceDuration = m_model->getTrackById(trackId)->getBlankSizeAtPos(frame);
......@@ -1130,9 +1124,7 @@ void TimelineController::extractZone()
tracks << m_videoTarget;
}
if (tracks.isEmpty()) {
QVariant returnedValue;
QMetaObject::invokeMethod(m_root, "currentTrackId", Q_RETURN_ARG(QVariant, returnedValue));
tracks << returnedValue.toInt();
tracks << m_activeTrack;
}
TimelineFunctions::extractZone(m_model, tracks, m_zone, false);
}
......@@ -1156,9 +1148,7 @@ void TimelineController::liftZone()
tracks << m_videoTarget;
}
if (tracks.isEmpty()) {
QVariant returnedValue;
QMetaObject::invokeMethod(m_root, "currentTrackId", Q_RETURN_ARG(QVariant, returnedValue));
tracks << returnedValue.toInt();
tracks << m_activeTrack;
}
TimelineFunctions::extractZone(m_model, tracks, m_zone, true);
}
......@@ -1173,9 +1163,7 @@ bool TimelineController::insertZone(const QString &binId, QPoint zone, bool over
targetTrack = m_videoTarget;
}
if (targetTrack == -1) {
QVariant returnedValue;
QMetaObject::invokeMethod(m_root, "currentTrackId", Q_RETURN_ARG(QVariant, returnedValue));
targetTrack = returnedValue.toInt();
targetTrack = m_activeTrack;
}
return TimelineFunctions::insertZone(m_model, targetTrack, binId, m_position, zone, overwrite);
}
......
......@@ -57,6 +57,7 @@ class TimelineController : public QObject
Q_PROPERTY(QVariantList dirtyChunks READ dirtyChunks NOTIFY dirtyChunksChanged)
Q_PROPERTY(QVariantList renderedChunks READ renderedChunks NOTIFY renderedChunksChanged)
Q_PROPERTY(int workingPreview READ workingPreview NOTIFY workingPreviewChanged)
Q_PROPERTY(int activeTrack READ activeTrack WRITE setActiveTrack NOTIFY activeTrackChanged)
Q_PROPERTY(int audioTarget READ audioTarget WRITE setAudioTarget NOTIFY audioTargetChanged)
Q_PROPERTY(int videoTarget READ videoTarget WRITE setVideoTarget NOTIFY videoTargetChanged)
......@@ -90,6 +91,7 @@ public:
Q_INVOKABLE int seekPosition() const { return m_seekPosition; }
Q_INVOKABLE int audioTarget() const { return m_audioTarget; }
Q_INVOKABLE int videoTarget() const { return m_videoTarget; }
Q_INVOKABLE int activeTrack() const { return m_activeTrack; }
/* @brief Request a seek operation
@param position is the desired new timeline position
*/
......@@ -318,6 +320,7 @@ public slots:
Q_INVOKABLE void setSeekPosition(int position);
Q_INVOKABLE void setAudioTarget(int track);
Q_INVOKABLE void setVideoTarget(int track);
Q_INVOKABLE void setActiveTrack(int track);
void onSeeked(int position);
void addEffectToCurrentClip(const QStringList &effectData);
/** @brief Dis / enable timeline preview. */
......@@ -339,6 +342,7 @@ private:
int m_seekPosition;
int m_audioTarget;
int m_videoTarget;
int m_activeTrack;
QPoint m_zone;
double m_scale;
static int m_duration;
......@@ -362,6 +366,7 @@ signals:
void seekPositionChanged();
void audioTargetChanged();
void videoTargetChanged();
void activeTrackChanged();
void showThumbnailsChanged();
void showAudioThumbnailsChanged();
void showMarkersChanged();
......
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