diff --git a/src/timeline2/model/clipmodel.cpp b/src/timeline2/model/clipmodel.cpp --- a/src/timeline2/model/clipmodel.cpp +++ b/src/timeline2/model/clipmodel.cpp @@ -181,15 +181,20 @@ }; if (operation()) { // we send a list of roles to be updated - QVector roles{TimelineModel::DurationRole}; - if (right) { - roles.push_back(TimelineModel::StartRole); - roles.push_back(TimelineModel::InPointRole); - } else { - roles.push_back(TimelineModel::OutPointRole); - } - list.emplace_back(std::make_shared(getId(), m_parent, roles)); // 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) { + QVector roles{TimelineModel::DurationRole}; + if (!right) { + roles.push_back(TimelineModel::StartRole); + roles.push_back(TimelineModel::InPointRole); + } else { + roles.push_back(TimelineModel::OutPointRole); + } + list.emplace_back(std::make_shared(getId(), m_parent, roles)); + if (auto ptr = m_parent.lock()) { + track_reverse = ptr->getTrackById(m_currentTrackId)->requestClipResize_lambda(m_id, old_in, old_out, right); + } + } Fun reverse = [this, old_in, old_out, track_reverse]() { if (track_reverse()) { m_producer->set_in_and_out(old_in, old_out); diff --git a/src/timeline2/model/compositionmodel.cpp b/src/timeline2/model/compositionmodel.cpp --- a/src/timeline2/model/compositionmodel.cpp +++ b/src/timeline2/model/compositionmodel.cpp @@ -109,7 +109,7 @@ if (operation()) { // we send a list of roles to be updated QVector roles{TimelineModel::DurationRole}; - if (right) { + if (!right) { roles.push_back(TimelineModel::StartRole); roles.push_back(TimelineModel::InPointRole); } else { diff --git a/src/timeline2/model/modelupdater.cpp b/src/timeline2/model/modelupdater.cpp --- a/src/timeline2/model/modelupdater.cpp +++ b/src/timeline2/model/modelupdater.cpp @@ -24,7 +24,6 @@ #include "trackmodel.hpp" #include -namespace { // This function assumes all the updates in the list correspond to the same item // It will merge move update together. We also merge insert + move in an insert to the right position std::vector> mergeMoves(const std::vector> &list) @@ -117,6 +116,7 @@ bool isClip = true; std::weak_ptr sourceTimeline; + qDebug()<<"******** MERGIN UPDATES FOR ITEM : "<getItemId() == itemId); Q_ASSERT(!update->isMove()); @@ -126,8 +126,10 @@ sourcePos = del->getPos(); sourceTimeline = del->getTimeline(); isClip = del->isClip(); + qDebug()<<"******** FOUND DELETE: "<isInsert()) { + auto tl = sourceTimeline.lock(); auto insert = std::static_pointer_cast(update); result.emplace_back( new MoveUpdate(itemId, sourceTimeline, sourceTrackId, sourcePos, insert->getTimeline(), insert->getTrackId(), insert->getPos())); @@ -141,6 +143,7 @@ result.push_back(update); } } + qDebug()<<"******** MERGIN DONE FOR ITEM : "<> result; int itemId = list[0]->getItemId(); + qDebug() << "merging changes for item: "< roles; bool seenMove = false; std::weak_ptr timeline; @@ -193,6 +202,8 @@ auto insert = std::static_pointer_cast(update); timeline = insert->getTimeline(); seenMove = true; + qDebug() << "merging INSERT change FOR: "<isMove()) { Q_ASSERT(!seenMove); @@ -233,10 +244,11 @@ for (const auto &u : list) { updatesByItem[u->getItemId()].push_back(u); + qDebug()<<"***FOUND UPDATE FOR ITEM: "<getItemId(); } // Then, we simplify updates element by element - + qDebug()<<"+ + + + + ++ "; std::vector> res; for (const auto &u : updatesByItem) { auto curated = mergeChanges(mergeMoves(mergeDeleteInsert(u.second))); @@ -309,6 +321,19 @@ }; return [ list, getCurrentRow = std::move(getCurrentRow), getTentativeRow = std::move(getTentativeRow) ]() { + QMap trackRowOffset; + for (const auto &u : list) { + if (u->isInsert()) { + auto ins = std::static_pointer_cast(u); + int trackId = ins->getTrackId(); + if (trackRowOffset.contains(trackId)) { + int offset = trackRowOffset.value(trackId); + trackRowOffset[trackId] = offset + 1; + } else { + trackRowOffset[trackId] = 0; + } + } + } for (const auto &u : list) { if (u->isDelete()) { auto del = std::static_pointer_cast(u); @@ -325,7 +350,20 @@ if (auto timeline = ins->getTimeline().lock()) { int trackId = ins->getTrackId(), itemId = ins->getItemId(); int row = getTentativeRow(timeline, trackId, itemId, ins->isClip()); - timeline->_beginInsertRows(timeline->makeTrackIndexFromID(trackId), row, row); + //HACK: dirty workaround for the getTentativeRow that does not work in case of multiple insert in one track + int offset = trackRowOffset.value(trackId); + /*if (trackRowOffset.contains(trackId)) { + int offset = trackRowOffset.value(trackId); + row += offset; + trackRowOffset[trackId] = offset + 1; + } else { + trackRowOffset[trackId] = 1; + }*/ + if (offset >= 0) { + qDebug()<<"===========\n==========\n===========\nINSERTING ROWS: "<_beginInsertRows(timeline->makeTrackIndexFromID(trackId), row, row + offset); + trackRowOffset[trackId] = -1; + } } else { qDebug() << "ERROR: impossible to lock timeline"; Q_ASSERT(false); @@ -334,7 +372,8 @@ auto move = std::static_pointer_cast(u); if (auto sTimeline = move->getSourceTimeline().lock()) { if (auto tTimeline = move->getTargetTimeline().lock()) { - int sTrackId = move->getSourceTrackId(), itemId = move->getItemId(); + int sTrackId = move->getSourceTrackId(); + int itemId = move->getItemId(); int tTrackId = move->getTargetTrackId(); int sRow = getCurrentRow(sTimeline, sTrackId, itemId); int tRow = getTentativeRow(tTimeline, tTrackId, itemId, sTimeline->isClip(itemId)); @@ -369,8 +408,10 @@ Fun ModelUpdater::postApply_lambda(const std::vector> &list) { return [list]() { + bool insertSent = false; for (const auto &u : list) { if (u->isDelete()) { + qDebug()<<"+ + + + ++ PREPARING AFTER DELETE UPDATE"; auto del = std::static_pointer_cast(u); if (auto timeline = del->getTimeline().lock()) { timeline->_endRemoveRows(); @@ -379,14 +420,21 @@ Q_ASSERT(false); } } else if (u->isInsert()) { + if (insertSent) { + continue; + } + qDebug()<<"+ + + + ++ PREPARING AFTER INSERT UPDATE"; auto ins = std::static_pointer_cast(u); if (auto timeline = ins->getTimeline().lock()) { + qDebug()<<"*** DONE INSERT NEW ROW: "<getItemId(); timeline->_endInsertRows(); + insertSent = true; } else { qDebug() << "ERROR: impossible to lock timeline"; Q_ASSERT(false); } } else if (u->isMove()) { + qDebug()<<"+ + + + ++ PREPARING AFTER MOVE UPDATE"; auto move = std::static_pointer_cast(u); if (auto sTimeline = move->getSourceTimeline().lock()) { if (auto tTimeline = move->getTargetTimeline().lock()) { @@ -405,6 +453,7 @@ Q_ASSERT(false); } } else if (u->isChange()) { + qDebug()<<"+ + + + ++ PREPARING AFTER CHANGE UPDATE"; auto change = std::static_pointer_cast(u); if (auto timeline = change->getTimeline().lock()) { int itemId = change->getItemId(); @@ -415,6 +464,7 @@ Q_ASSERT(timeline->isComposition(itemId)); idx = timeline->makeCompositionIndexFromID(itemId); } + qDebug()<<"// MOVE UPDATE INDEX: "<notifyChange(idx, idx, change->getRoles()); } } else { @@ -434,7 +484,6 @@ } auto updates = simplify(list); auto rev_updates = reverse(updates); - // firstly, we need to undo the action bool undone = undo(); Q_ASSERT(undone); @@ -588,7 +637,7 @@ std::shared_ptr MoveUpdate::reverse() { - return std::make_shared(m_itemId, m_targetTimeline, m_targetTrackId, m_targetPos, m_targetTimeline, m_targetTrackId, m_targetPos); + return std::make_shared(m_itemId, m_targetTimeline, m_targetTrackId, m_targetPos, m_sourceTimeline, m_sourceTrackId, m_sourcePos); } void MoveUpdate::print() const @@ -633,6 +682,9 @@ case TimelineModel::NameRole: qDebug() << "NameRole"; break; + case TimelineModel::TrackIdRole: + qDebug() << "TrackIdRole"; + break; case TimelineModel::ResourceRole: qDebug() << "ResourceRole"; break; diff --git a/src/timeline2/model/timelinefunctions.cpp b/src/timeline2/model/timelinefunctions.cpp --- a/src/timeline2/model/timelinefunctions.cpp +++ b/src/timeline2/model/timelinefunctions.cpp @@ -68,6 +68,7 @@ std::function redo = []() { return true; }; Updates list; + qDebug()<<"* * *MULTIPLE CLIP INSERTION START"; for (const QString &binId : binIds) { int clipId; if (timeline->requestClipInsertion(binId, trackId, position, clipId, logUndo, refreshView, true, undo, redo, list)) { @@ -79,8 +80,10 @@ return false; } } + qDebug()<<"* * *MULTIPLE CLIP INSERTION DONE"; ModelUpdater::applyUpdates(undo, redo, list); + qDebug()<<"* * *MULTIPLE CLIP INSERTION DONE UPDATING"; if (logUndo) { pCore->pushUndo(undo, redo, i18n("Insert Clips")); } diff --git a/src/timeline2/model/timelineitemmodel.hpp b/src/timeline2/model/timelineitemmodel.hpp --- a/src/timeline2/model/timelineitemmodel.hpp +++ b/src/timeline2/model/timelineitemmodel.hpp @@ -73,11 +73,11 @@ QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override; // QModelIndex makeIndex(int trackIndex, int clipIndex) const; /* @brief Creates an index based on the ID of the clip*/ - QModelIndex makeClipIndexFromID(int clipId) const override; + const QPersistentModelIndex makeClipIndexFromID(int clipId) const override; /* @brief Creates an index based on the ID of the compoition*/ - QModelIndex makeCompositionIndexFromID(int compoId) const override; + const QPersistentModelIndex makeCompositionIndexFromID(int compoId) const override; /* @brief Creates an index based on the ID of the track*/ - QModelIndex makeTrackIndexFromID(int trackId) const override; + const QPersistentModelIndex makeTrackIndexFromID(int trackId) const override; QModelIndex parent(const QModelIndex &index) const override; Q_INVOKABLE void setTrackProperty(int tid, const QString &name, const QString &value); /* @brief Enabled/disabled a track's effect stack */ diff --git a/src/timeline2/model/timelineitemmodel.cpp b/src/timeline2/model/timelineitemmodel.cpp --- a/src/timeline2/model/timelineitemmodel.cpp +++ b/src/timeline2/model/timelineitemmodel.cpp @@ -70,6 +70,7 @@ if (parent.isValid()) { auto trackId = int(parent.internalId()); Q_ASSERT(isTrack(trackId)); + qDebug()<<" * ** CHECKING ITEM FOR ROW: "<getClipByRow(row); if (clipId != -1) { result = createIndex(row, 0, quintptr(clipId)); @@ -93,26 +94,27 @@ return index(clipIndex, 0, index(trackIndex)); }*/ -QModelIndex TimelineItemModel::makeClipIndexFromID(int clipId) const +const QPersistentModelIndex TimelineItemModel::makeClipIndexFromID(int clipId) const { Q_ASSERT(m_allClips.count(clipId) > 0); int trackId = m_allClips.at(clipId)->getCurrentTrackId(); if (trackId == -1) { // Clip is not inserted in a track - return QModelIndex(); + qDebug()<<"// MAKING IX FROM NOT INSERTED CLIP!!!"; + return QPersistentModelIndex(); } int row = getTrackById_const(trackId)->getRowfromClip(clipId); return index(row, 0, makeTrackIndexFromID(trackId)); } -QModelIndex TimelineItemModel::makeCompositionIndexFromID(int compoId) const +const QPersistentModelIndex TimelineItemModel::makeCompositionIndexFromID(int compoId) const { Q_ASSERT(m_allCompositions.count(compoId) > 0); int trackId = m_allCompositions.at(compoId)->getCurrentTrackId(); return index(getTrackById_const(trackId)->getRowfromComposition(compoId), 0, makeTrackIndexFromID(trackId)); } -QModelIndex TimelineItemModel::makeTrackIndexFromID(int trackId) const +const QPersistentModelIndex TimelineItemModel::makeTrackIndexFromID(int trackId) const { // we retrieve iterator Q_ASSERT(m_iteratorTable.count(trackId) > 0); @@ -124,11 +126,11 @@ QModelIndex TimelineItemModel::parent(const QModelIndex &index) const { READ_LOCK(); - // qDebug() << "TimelineItemModel::parent"<< index; if (index == QModelIndex()) { return index; } const int id = static_cast(index.internalId()); + //qDebug() << "TimelineItemModel::parent from: "< roles; roles[NameRole] = "name"; roles[ResourceRole] = "resource"; + roles[ItemIdRole] = "itemId"; roles[ServiceRole] = "mlt_service"; roles[BinIdRole] = "binId"; + roles[TrackIdRole] = "trackId"; roles[IsBlankRole] = "blank"; roles[StartRole] = "start"; roles[DurationRole] = "duration"; @@ -195,7 +201,6 @@ roles[SpeedRole] = "speed"; roles[HeightRole] = "trackHeight"; roles[TrackTagRole] = "trackTag"; - roles[ItemIdRole] = "item"; roles[ItemATrack] = "a_track"; roles[HasAudio] = "hasAudio"; roles[CanBeAudioRole] = "canBeAudio"; @@ -223,7 +228,7 @@ if (isTrack(id)) { return getTrackSortValue(id, KdenliveSettings::audiotracksbelow()); } - return id; + return QVariant(); } if (isClip(id)) { // qDebug() << "REQUESTING DATA "<binId(); + case TrackIdRole: + return clip->getCurrentTrackId(); case ServiceRole: return clip->getProperty("mlt_service"); break; @@ -360,6 +367,8 @@ return false; case StartRole: return compo->getPosition(); + case TrackIdRole: + return compo->getCurrentTrackId(); case DurationRole: return compo->getPlaytime(); case GroupedRole: @@ -536,7 +545,10 @@ void TimelineItemModel::_beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationChild) { - beginMoveRows(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild); + qDebug()<<"===========\nMOVING ROWS: "< &roles) = 0; virtual void notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, int role) = 0; - virtual QModelIndex makeClipIndexFromID(int) const = 0; - virtual QModelIndex makeCompositionIndexFromID(int) const = 0; - virtual QModelIndex makeTrackIndexFromID(int) const = 0; + virtual const QPersistentModelIndex makeClipIndexFromID(int) const = 0; + virtual const QPersistentModelIndex makeCompositionIndexFromID(int) const = 0; + virtual const QPersistentModelIndex makeTrackIndexFromID(int) const = 0; virtual void _resetView() = 0; }; #endif diff --git a/src/timeline2/model/timelinemodel.cpp b/src/timeline2/model/timelinemodel.cpp --- a/src/timeline2/model/timelinemodel.cpp +++ b/src/timeline2/model/timelinemodel.cpp @@ -166,6 +166,19 @@ return getClipTrackId(itemId); } +void TimelineModel::setItemTrackId(int itemId, int trackId) +{ + READ_LOCK(); + Q_ASSERT(isClip(itemId) || isComposition(itemId)); + if (isComposition(itemId)) { + m_allCompositions[itemId]->setCurrentTrackId(trackId); + } else { + m_allClips[itemId]->setCurrentTrackId(trackId); + m_allClips[itemId]->setGrab(true); + } + qDebug()<<"// SETTING CLIP "< 0); if (m_allClips[clipId]->getPosition() == position && getClipTrackId(clipId) == trackId) { + qDebug()<<"// SAME FRAME MOVE, ABORT"; return true; } if (m_groups->isInGroup(clipId)) { @@ -418,8 +434,10 @@ std::function redo = []() { return true; }; Updates list; bool res = requestClipMove(clipId, trackId, position, updateView, invalidateTimeline, undo, redo, list); + qDebug()<<"// CLIP MOVED: "<suggestClipMove(" << clipId << "," << trackId << " ," << position << "); " << std::endl; @@ -470,7 +488,8 @@ Q_ASSERT(isClip(clipId)); Q_ASSERT(isTrack(trackId)); int currentPos = getClipPosition(clipId); - if (currentPos == position) { + int sourceTrackId = getClipTrackId(clipId); + if (currentPos == position && sourceTrackId == trackId) { return position; } bool after = position > currentPos; @@ -498,18 +517,24 @@ } } // we check if move is possible - bool possible; - if (allowViewUpdate) { - possible = requestClipMove(clipId, trackId, position, false, false, false); + bool possible = requestClipMove(clipId, trackId, position, true, false, false); + if (!possible) { + qDebug() << "CANNOT MOVE CLIP : "<isInGroup(clipId)) { // Easy + //int currentTrackId = getClipTrackId(clipId); + // Try same track move + trackId = sourceTrackId; + possible = requestClipMove(clipId, trackId, position, true, false, false); + if (!possible) { + qDebug() << "CANNOT MOVE CLIP : "<getBlankSizeNearClip(clipId, after); qDebug() << "Found blank" << blank_length; if (blank_length < INT_MAX) { @@ -519,13 +544,9 @@ position = currentPos - blank_length; } } else { - return false; - } - if (allowViewUpdate) { - possible = requestClipMove(clipId, trackId, position, false, false, false); - } else { - possible = requestClipMoveAttempt(clipId, trackId, position); + return currentPos; } + possible = requestClipMove(clipId, trackId, position, true, false, false); return possible ? position : currentPos; } // find best pos for groups @@ -577,17 +598,21 @@ } } } + /* + * This returns erratic results, moving clips to 0. if (blank_length != 0) { int updatedPos = currentPos + (after ? blank_length : -blank_length); + qDebug()<<"===== CURRENTPOS: "<getForcedTrack(), position, false, undo, redo, list); + bool possible = requestCompositionMove(compoId, trackId, position, true, false); qDebug() << "Original move success" << possible; if (possible) { - bool undone = undo(); - Q_ASSERT(undone); return position; } - bool after = position > currentPos; + /*bool after = position > currentPos; int blank_length = getTrackById(trackId)->getBlankSizeNearComposition(compoId, after); qDebug() << "Found blank" << blank_length; if (blank_length < INT_MAX) { @@ -651,8 +671,8 @@ return currentPos + blank_length; } return currentPos - blank_length; - } - return position; + }*/ + return currentPos; } bool TimelineModel::requestClipCreation(const QString &binClipId, int &id, PlaylistState::ClipState state, Fun &undo, Fun &redo, Updates &list) @@ -1841,9 +1861,11 @@ return getCompositionPlaytime(itemId); } -int TimelineModel::getTrackCompositionsCount(int compoId) const +int TimelineModel::getTrackCompositionsCount(int trackId) const { - return getTrackById_const(compoId)->getCompositionsCount(); + Q_ASSERT(isTrack(trackId)); + qDebug()<<"*** COMPOSITIONS ON TRACK: "<getCompositionsCount(); + return getTrackById_const(trackId)->getCompositionsCount(); } bool TimelineModel::requestCompositionMove(int compoId, int trackId, int position, bool updateView, bool logUndo) @@ -1873,6 +1895,7 @@ int min = getCompositionPosition(compoId); int max = min + getCompositionPlaytime(compoId); int tk = getCompositionTrackId(compoId); + qDebug()<<" * * * *REQUESTING COMPO MOVE TO TK: "<getForcedTrack(), position, updateView, undo, redo, list); if (tk > -1) { min = qMin(min, getCompositionPosition(compoId)); diff --git a/src/timeline2/model/trackmodel.cpp b/src/timeline2/model/trackmodel.cpp --- a/src/timeline2/model/trackmodel.cpp +++ b/src/timeline2/model/trackmodel.cpp @@ -337,6 +337,7 @@ auto operation = requestClipDeletion_lambda(clipId, updateView, finalMove); if (operation()) { if (updateView) { + //qDebug()<<"xxxxxxxxxxxxxxxxx\npreparing delete ix: "<(clipId, m_parent, getId(), old_position, true)); } auto reverse = requestClipInsertion_lambda(clipId, old_position, updateView, finalMove); @@ -381,7 +382,9 @@ other_index = m_playlists[other_track].get_clip_index_at(last_pos); index--; } - if (index < 0) return 0; + if (index < 0) { + return 0; + } int length = INT_MAX; if (index < m_playlists[track].count()) { if (!m_playlists[track].is_blank(index)) { @@ -441,6 +444,7 @@ auto update_snaps = [clipId, old_in, old_out, checkRefresh, this](int new_in, int new_out) { if (auto ptr = m_parent.lock()) { + qDebug()<<"* * *MOVING SNAP POINTS: "<m_snaps->removePoint(old_in); ptr->m_snaps->removePoint(old_out); ptr->m_snaps->addPoint(new_in); @@ -974,7 +978,7 @@ if (row < (int)m_allClips.size()) { return -1; } - // Q_ASSERT(row <= (int)m_allClips.size() + m_allCompositions.size()); + Q_ASSERT(row < (int)m_allClips.size() + m_allCompositions.size()); auto it = m_allCompositions.cbegin(); std::advance(it, row - (int)m_allClips.size()); return (*it).first; diff --git a/src/timeline2/view/qml/Clip.qml b/src/timeline2/view/qml/Clip.qml --- a/src/timeline2/view/qml/Clip.qml +++ b/src/timeline2/view/qml/Clip.qml @@ -51,11 +51,11 @@ property int fadeIn: 0 property int fadeOut: 0 property int binId: 0 - property var parentTrack: trackRoot + property var parentTrack property int trackIndex //Index in track repeater - property int trackId: -42 //Id in the model + property int trackId //Id in the model property int clipId //Id of the clip in the model - property int originalTrackId: trackId + property int originalTrackId: -1 property int originalX: x property int originalDuration: clipDuration property int lastValidDuration: clipDuration @@ -70,11 +70,6 @@ property bool forceReloadThumb: false width : clipDuration * timeScale; - signal clicked(var clip, int shiftClick) - signal moved(var clip) - signal dragged(var clip, var mouse) - signal dropped(var clip) - signal draggedToTrack(var clip, int pos, int xpos) signal trimmingIn(var clip, real newDuration, var mouse, bool shiftTrim) signal trimmedIn(var clip, bool shiftTrim) signal trimmingOut(var clip, real newDuration, var mouse, bool shiftTrim) @@ -95,6 +90,10 @@ } } + onClipIdChanged: { + console.log('()()(()()()()((\nWARNING CLIP ID CHANGED TO: ', clipRoot.clipId,'\n())()()()') + } + onInPointChanged: { if (parentTrack.isAudio) { thumbsLoader.item.reload() @@ -108,7 +107,7 @@ } ToolTip { - visible: mouseArea.containsMouse && !mouseArea.pressed + visible: mouseArea.containsMouse && !dragProxyArea.pressed font.pixelSize: root.baseUnit delay: 1000 timeout: 5000 @@ -137,6 +136,11 @@ x = modelStart * timeScale; } + onTrackIdChanged: { + clipRoot.parentTrack = Logic.getTrackById(trackId) + clipRoot.y = clipRoot.originalTrackId == -1 || trackId == originalTrackId ? 0 : parentTrack.y - Logic.getTrackById(clipRoot.originalTrackId).y; + } + onForceReloadThumbChanged: { // TODO: find a way to force reload of clip thumbs thumbsLoader.item.reload() @@ -146,7 +150,7 @@ x = modelStart * timeScale; width = clipDuration * timeScale; labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : 0 - if (parentTrack.isAudio) { + if (parentTrack && parentTrack.isAudio) { thumbsLoader.item.reload(); } } @@ -177,7 +181,7 @@ return isAudio? '#445f5a' : '#416e8c' } - function reparent(track) { + /*function reparent(track) { console.log('TrackId: ',trackId) parent = track height = track.height @@ -185,7 +189,7 @@ trackId = parentTrack.trackId console.log('Reparenting clip to Track: ', trackId) //generateWaveform() - } + }*/ property bool variableThumbs: (isAudio || clipType == ProducerType.Color || mltService === '') property bool isImage: clipType == ProducerType.Image @@ -218,7 +222,7 @@ } onAudioLevelsChanged: { - if (parentTrack.isAudio) { + if (parentTrack && parentTrack.isAudio) { thumbsLoader.item.reload() } } @@ -226,27 +230,12 @@ id: mouseArea visible: root.activeTool === 0 anchors.fill: clipRoot - acceptedButtons: Qt.LeftButton | Qt.RightButton - drag.target: parent - drag.axis: Drag.XAxis - property int startX - drag.smoothed: false + acceptedButtons: Qt.RightButton //Qt.LeftButton | hoverEnabled: true - cursorShape: containsMouse ? pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor : tracksArea.cursorShape + cursorShape: dragProxyArea.drag.active ? Qt.ClosedHandCursor : Qt.OpenHandCursor onPressed: { root.stopScrolling = true - originalX = clipRoot.x - originalTrackId = clipRoot.trackId - startX = clipRoot.x - root.stopScrolling = true - clipRoot.forceActiveFocus(); - if (!clipRoot.selected) { - clipRoot.clicked(clipRoot, mouse.modifiers == Qt.ShiftModifier) - } - if (mouse.button == Qt.LeftButton) { - drag.target = clipRoot - focus = true - } else if (mouse.button == Qt.RightButton) { + if (mouse.button == Qt.RightButton) { drag.target = undefined clipMenu.item.clipId = clipRoot.clipId clipMenu.item.clipStatus = clipRoot.clipStatus @@ -271,34 +260,15 @@ controller.requestClipMove(clipRoot.clipId, controller.getPreviousTrackId(clipRoot.trackId), clipRoot.modelStart, true, true, true); } onPositionChanged: { - if (pressed && mouse.buttons === Qt.LeftButton && drag.target != undefined) { - var trackIndex = Logic.getTrackIndexFromId(clipRoot.trackId) - if ((mouse.y < 0 && trackIndex > 0) || (mouse.y > height && trackIndex < tracksRepeater.count - 1)) { - var mapped = parentTrack.mapFromItem(clipRoot, mouse.x, mouse.y).x - clipRoot.draggedToTrack(clipRoot, mapToItem(null, 0, mouse.y).y, mapped) - } else { - clipRoot.dragged(clipRoot, mouse) - } - } + var mapped = parentTrack.mapFromItem(clipRoot, mouse.x, mouse.y).x + root.mousePosChanged(Math.round(mapped / timeline.scaleFactor)) } - onReleased: { - root.stopScrolling = false - if (mouse.button == Qt.LeftButton && drag.target != undefined) { - var delta = clipRoot.x - startX - drag.target = undefined - cursorShape = Qt.OpenHandCursor - if (trackId !== originalTrackId) { - var track = Logic.getTrackById(trackId) - parent.moved(clipRoot) - reparent(track) - originalX = clipRoot.x - clipRoot.y = 0 - originalTrackId = trackId - } else if (delta != 0) { - parent.dropped(clipRoot) - originalX = clipRoot.x - } - } + onEntered: { + var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height) + initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false) + } + onExited: { + endDrag() } onDoubleClicked: { drag.target = undefined @@ -429,7 +399,7 @@ KeyframeView { id: effectRow - visible: clipRoot.showKeyframes && keyframeModel + visible: clipRoot.showKeyframes && clipRoot.keyframeModel selected: clipRoot.selected inPoint: clipRoot.inPoint outPoint: clipRoot.outPoint diff --git a/src/timeline2/view/qml/Composition.qml b/src/timeline2/view/qml/Composition.qml --- a/src/timeline2/view/qml/Composition.qml +++ b/src/timeline2/view/qml/Composition.qml @@ -87,6 +87,10 @@ mouseArea.focus = true } } + onTrackIdChanged: { + compositionRoot.parentTrack = Logic.getTrackById(trackId) + compositionRoot.y = compositionRoot.originalTrackId == -1 || trackId == originalTrackId ? 0 : parentTrack.y - Logic.getTrackById(compositionRoot.originalTrackId).y; + } onClipDurationChanged: { width = clipDuration * timeScale; @@ -99,13 +103,13 @@ onScrollXChanged: { labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : 0 } - function reparent(track) { + /*function reparent(track) { parent = track isAudio = track.isAudio parentTrack = track displayHeight = track.height / 2 compositionRoot.trackId = parentTrack.trackId - } + }*/ SystemPalette { id: activePalette } Rectangle { @@ -155,8 +159,8 @@ outPoint: compositionRoot.clipDuration } } - Drag.active: mouseArea.drag.active - Drag.proposedAction: Qt.MoveAction + /*Drag.active: mouseArea.drag.active + Drag.proposedAction: Qt.MoveAction*/ states: [ State { @@ -181,13 +185,10 @@ MouseArea { id: mouseArea anchors.fill: parent - acceptedButtons: Qt.LeftButton - drag.target: compositionRoot - drag.axis: Drag.XAxis - drag.smoothed: false - property int startX + acceptedButtons: Qt.RightButton + hoverEnabled: true - onPressed: { + /*onPressed: { root.stopScrolling = true originalX = compositionRoot.x originalTrackId = compositionRoot.trackId @@ -197,7 +198,7 @@ if (!compositionRoot.selected) { compositionRoot.clicked(compositionRoot, mouse.modifiers === Qt.ShiftModifier) } - } + }*/ Keys.onShortcutOverride: event.accepted = compositionRoot.isGrabbed && (event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Up || event.key === Qt.Key_Down) Keys.onLeftPressed: { controller.requestCompositionMove(compositionRoot.clipId, compositionRoot.originalTrackId, compositionRoot.modelStart - 1, true, true) @@ -211,24 +212,28 @@ Keys.onDownPressed: { controller.requestCompositionMove(compositionRoot.clipId, controller.getPreviousTrackId(compositionRoot.originalTrackId), compositionRoot.modelStart, true, true) } - onPositionChanged: { - if (mouse.y < -height || (mouse.y > height && parentTrack.rootIndex.row < tracksRepeater.count - 1)) { - var mapped = parentTrack.mapFromItem(compositionRoot, mouse.x, mouse.y).x - compositionRoot.draggedToTrack(compositionRoot, mapToItem(null, 0, mouse.y).y, mapped) - } else { - compositionRoot.dragged(compositionRoot, mouse) + cursorShape: (trimInMouseArea.drag.active || trimOutMouseArea.drag.active)? Qt.SizeHorCursor : + dragProxyArea.drag.active ? Qt.ClosedHandCursor : Qt.OpenHandCursor + + onPressed: { + root.stopScrolling = true + compositionRoot.forceActiveFocus(); + /*if (!compositionRoot.selected) { + compositionRoot.clicked(compositionRoot, false) + }*/ + if (mouse.button == Qt.RightButton) { + compositionMenu.item.clipId = compositionRoot.clipId + compositionMenu.item.grouped = compositionRoot.grouped + compositionMenu.item.trackId = compositionRoot.trackId + compositionMenu.item.popup() + } } + onEntered: { + var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height) + initDrag(compositionRoot, itemPos, compositionRoot.clipId, compositionRoot.modelStart, compositionRoot.trackId, true) } - onReleased: { - root.stopScrolling = false - var delta = compositionRoot.x - startX - if (Math.abs(delta) >= 1.0 || trackId !== originalTrackId) { - compositionRoot.moved(compositionRoot) - originalX = compositionRoot.x - originalTrackId = trackId - } else if (Math.abs(delta) >= 1.0) { - compositionRoot.dropped(compositionRoot) - } + onExited: { + endDrag() } onDoubleClicked: { drag.target = undefined @@ -246,26 +251,6 @@ } } onWheel: zoomByWheel(wheel) - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - cursorShape: (trimInMouseArea.drag.active || trimOutMouseArea.drag.active)? Qt.SizeHorCursor : - drag.active? Qt.ClosedHandCursor : Qt.OpenHandCursor - onPressed: { - root.stopScrolling = true - compositionRoot.forceActiveFocus(); - if (!compositionRoot.selected) { - compositionRoot.clicked(compositionRoot, false) - } - if (mouse.button == Qt.RightButton) { - compositionMenu.item.clipId = compositionRoot.clipId - compositionMenu.item.grouped = compositionRoot.grouped - compositionMenu.item.trackId = compositionRoot.trackId - compositionMenu.item.popup() - } - } - } } Rectangle { @@ -279,7 +264,7 @@ Drag.active: trimInMouseArea.drag.active Drag.proposedAction: Qt.MoveAction enabled: !compositionRoot.grouped - visible: root.activeTool === 0 && !mouseArea.drag.active + visible: root.activeTool === 0 && !dragProxyArea.drag.active MouseArea { id: trimInMouseArea @@ -326,7 +311,7 @@ Drag.active: trimOutMouseArea.drag.active Drag.proposedAction: Qt.MoveAction enabled: !compositionRoot.grouped - visible: root.activeTool === 0 && !mouseArea.drag.active + visible: root.activeTool === 0 && !dragProxyArea.drag.active MouseArea { id: trimOutMouseArea diff --git a/src/timeline2/view/qml/Track.qml b/src/timeline2/view/qml/Track.qml --- a/src/timeline2/view/qml/Track.qml +++ b/src/timeline2/view/qml/Track.qml @@ -21,8 +21,8 @@ Column{ id: trackRoot - property alias model: trackModel.model - property alias rootIndex: trackModel.rootIndex + property alias trackModel: trackModel.model + property alias trackRootIndex: trackModel.rootIndex property bool isAudio property bool isMute property bool isHidden @@ -35,12 +35,6 @@ SystemPalette { id: activePalette } - signal clipClicked(var clip, var track, int shiftClick) - signal clipDragged(var clip, int x, int y) - signal clipDropped(var clip) - signal compositionDropped(var clip) - signal clipDraggedToTrack(var clip, int pos, int xpos) - signal compositionDraggedToTrack(var composition, int pos, int xpos) /*function redrawWaveforms() { for (var i = 0; i < repeater.count; i++) @@ -51,6 +45,10 @@ return repeater.itemAt(index) } + onTrackRootIndexChanged: { + console.log('====== * TRACK Index CHANGED: ', trackRoot.trackId, ' NEW IX: ',trackRoot.trackRootIndex,'\n__________') + } + width: clipRow.width DelegateModel { @@ -66,6 +64,12 @@ value: trackRoot.timeScale when: loader.status == Loader.Ready } + Binding { + target: loader.item + property: "trackId" + value: model.trackId + when: loader.status == Loader.Ready + } Binding { target: loader.item property: "selected" @@ -212,9 +216,10 @@ } } onLoaded: { - item.clipId= model.item + item.clipId= model.itemId + item.parentTrack = trackRoot if (loader.item.isComposition === false) { - console.log('loaded clip: ', model.start, ', ID: ', model.item, ', index: ', trackRoot.DelegateModel.itemsIndex,', TYPE:', model.clipType) + console.log('loaded clip: ', model.start, ', ID: ', model.itemId, ', index: ', trackRoot.DelegateModel.itemsIndex,', TYPE:', model.clipType) item.isAudio= model.audio item.markers= model.markers item.hasAudio = model.hasAudio @@ -223,10 +228,10 @@ item.clipType = model.clipType //item.binId= model.binId } else { - console.log('loaded composition: ', model.start, ', ID: ', model.item, ', index: ', trackRoot.DelegateModel.itemsIndex) + console.log('loaded composition: ', model.start, ', ID: ', model.itemId, ', index: ', trackRoot.DelegateModel.itemsIndex) //item.aTrack = model.a_track } - item.trackId= trackRoot.trackId + //item.trackId= trackRoot.trackId //item.selected= trackRoot.selection.indexOf(item.clipId) !== -1 //console.log(width, height); } @@ -244,55 +249,6 @@ id: clipDelegate Clip { height: trackRoot.height - - onClicked: { - console.log("Clip clicked",clip.clipId) - trackRoot.clipClicked(clip, clip.parentTrack, shiftClick); - clip.draggedX = clip.x - } - onMoved: { //called when the movement is finished - var toTrack = clip.trackId - var cIndex = clip.clipId - var frame = clip.currentFrame - var origFrame = clip.modelStart - if (frame > -1) { - controller.requestClipMove(cIndex, clip.originalTrackId, origFrame, false, false, false) - controller.requestClipMove(cIndex, toTrack, frame, true, true, true) - } - clip.currentFrame = -1 - } - onDropped: { //called when the movement is finished - var toTrack = clip.trackId - var cIndex = clip.clipId - var frame = clip.currentFrame - var origFrame = clip.modelStart - if (frame != origFrame && frame > -1) { - controller.requestClipMove(cIndex, toTrack, origFrame, false, false, false) - controller.requestClipMove(cIndex, toTrack, frame, true, true, true) - } - clip.currentFrame = -1 - } - onDragged: { //called when the move is in process - var toTrack = clip.trackId - var cIndex = clip.clipId - clip.x = Math.max(0, clip.x) - if (Math.round(clip.currentFrame * timeScale) == clip.x) { - // No move to perform - } else { - var frame = Math.round(clip.x / timeScale) - frame = controller.suggestClipMove(cIndex, toTrack, frame, root.snapping); - if (clip.currentFrame == frame) { - // Abort move - clip.x = frame * timeScale - } else { - clip.x = frame * timeScale - clip.currentFrame = frame - var mapped = trackRoot.mapFromItem(clip, mouse.x, mouse.y) - trackRoot.clipDragged(clip, mapped.x, mapped.y) - clip.draggedX = clip.x - } - } - } onTrimmingIn: { var new_duration = controller.requestItemResize(clip.clipId, newDuration, false, false, root.snapping, shiftTrim) if (new_duration > 0) { @@ -313,6 +269,7 @@ controller.requestItemResize(clip.clipId, clip.lastValidDuration, false, true, root.snapping, shiftTrim) } onTrimmingOut: { + console.log(' + + +RQST RESIZE: ', newDuration, ' ON CLP: ', clip.clipId) var new_duration = controller.requestItemResize(clip.clipId, newDuration, true, false, root.snapping, shiftTrim) if (new_duration > 0) { clip.lastValidDuration = new_duration @@ -330,13 +287,6 @@ controller.requestItemResize(clip.clipId, clip.originalDuration, true, false, root.snapping, shiftTrim) controller.requestItemResize(clip.clipId, clip.lastValidDuration, true, true, root.snapping, shiftTrim) } - - Component.onCompleted: { - moved.connect(trackRoot.clipDropped) - dropped.connect(trackRoot.clipDropped) - draggedToTrack.connect(trackRoot.clipDraggedToTrack) - //console.log('BUILDING CLIP item ', model.clipId, 'name', model.name, ' service: ',mltService) - } } } Component { @@ -345,42 +295,6 @@ displayHeight: trackRoot.height / 2 opacity: 0.8 selected: root.timelineSelection.indexOf(clipId) !== -1 - - onClicked: { - console.log("Composition clicked",clip.clipId) - trackRoot.clipClicked(clip, trackRoot, shiftClick); - clip.draggedX = clip.x - } - onMoved: { //called when the movement is finished - console.log("Composition released",clip.clipId) - var toTrack = clip.trackId - var cIndex = clip.clipId - var frame = Math.round(clip.x / timeScale) - var origFrame = Math.round(clip.originalX / timeScale) - - console.log("Asking move ",toTrack, cIndex, frame) - controller.requestCompositionMove(cIndex, clip.originalTrackId, origFrame, false, false) - var val = controller.requestCompositionMove(cIndex, toTrack, frame, true, true) - console.log("RESULT", val) - } - onDragged: { //called when the move is in process - var toTrack = clip.trackId - var cIndex = clip.clipId - clip.x = Math.max(0, clip.x) - var frame = Math.round(clip.x / timeScale) - - frame = controller.suggestCompositionMove(cIndex, toTrack, frame, root.snapping); - - if (!controller.requestCompositionMove(cIndex, toTrack, frame, false, false)) { - // Abort move - clip.x = clip.draggedX - } else { - clip.x = frame * timeScale - var mapped = trackRoot.mapFromItem(clip, mouse.x, mouse.y) - trackRoot.clipDragged(clip, mapped.x, mapped.y) - clip.draggedX = clip.x - } - } onTrimmingIn: { if (controller.requestItemResize(clip.clipId, newDuration, false, false, root.snapping)) { clip.lastValidDuration = newDuration @@ -416,13 +330,6 @@ controller.requestItemResize(clip.clipId, clip.originalDuration, true, false, root.snapping) controller.requestItemResize(clip.clipId, clip.lastValidDuration, true, true, root.snapping) } - - Component.onCompleted: { - moved.connect(trackRoot.compositionDropped) - dropped.connect(trackRoot.compositionDropped) - draggedToTrack.connect(trackRoot.compositionDraggedToTrack) - // console.log('Showing item ', model.item, 'name', model.name, ' service: ',mltService) - } } } } diff --git a/src/timeline2/view/qml/timeline.qml b/src/timeline2/view/qml/timeline.qml --- a/src/timeline2/view/qml/timeline.qml +++ b/src/timeline2/view/qml/timeline.qml @@ -124,6 +124,25 @@ scrollTimer.running = false } + function initDrag(itemObject, itemCoord, itemId, itemPos, itemTrack, isComposition) { + dragProxy.x = itemCoord.x + dragProxy.y = itemCoord.y + dragProxy.width = itemCoord.width + dragProxy.height = itemCoord.height + dragProxy.masterObject = itemObject + dragProxy.draggedItem = itemId + dragProxy.sourceTrack = itemTrack + dragProxy.sourceFrame = itemPos + dragProxy.isComposition = isComposition + } + function endDrag() { + dragProxy.draggedItem = -1 + dragProxy.x = 0 + dragProxy.y = 0 + dragProxy.width = 0 + dragProxy.height = 0 + } + property int headerWidth: timeline.headerWidth() property int activeTool: 0 property real baseUnit: fontMetrics.font.pointSize @@ -507,8 +526,8 @@ width: headerWidth height: model.trackHeight selected: false - current: item === timeline.activeTrack - trackId: item + current: itemId === timeline.activeTrack + trackId: itemId onIsLockedChanged: tracksRepeater.itemAt(index).isLocked = isLocked collapsed: height <= collapsedHeight onMyTrackHeightChanged: { @@ -622,6 +641,10 @@ rubberSelect.width = 0 rubberSelect.height = 0 } else if (mouse.button & Qt.LeftButton) { + if (dragProxy.draggedItem > -1) { + mouse.accepted = false + return + } if (root.activeTool === 2 && mouse.y > ruler.height) { // spacer tool var y = mouse.y - ruler.height @@ -663,6 +686,10 @@ scim = false } onPositionChanged: { + if (dragProxy.draggedItem > -1) { + mouse.accepted = false + return + } root.mousePosChanged(Math.round((mouse.x + scrollView.flickableItem.contentX) / timeline.scaleFactor)) ruler.showZoneLabels = mouse.y < ruler.height if ((mouse.modifiers & Qt.ShiftModifier) && mouse.buttons === Qt.LeftButton && !rubberSelect.visible) { @@ -694,8 +721,7 @@ // Move group var track = controller.getClipTrackId(spacerGroup) var frame = Math.round((mouse.x + scrollView.flickableItem.contentX) / timeline.scaleFactor) + spacerFrame - spacerClickFrame - frame = controller.suggestClipMove(spacerGroup, track, frame, root.snapping, false); - controller.requestClipMove(spacerGroup, track, frame, true, false, false) + frame = controller.suggestClipMove(spacerGroup, track, frame, root.snapping); continuousScrolling(mouse.x + scrollView.flickableItem.contentX) } scim = true @@ -748,7 +774,6 @@ timeline.seekPosition = Math.min(timeline.position + 10, timeline.fullDuration - 1) } } - Column { Flickable { // Non-slider scroll area for the Ruler. @@ -795,11 +820,123 @@ height: Math.max(trackHeaders.height, scrollView.height - scrollView.__horizontalScrollBar.height) color: activePalette.window id: tracksContainerArea + Rectangle { + // Drag proxy, responsible for clip / composition move + id: dragProxy + x: 0 + y: 0 + width: 0 + height: 0 + property int draggedItem: -1 + property int sourceTrack + property int sourceFrame + property bool isComposition + property var masterObject + color: 'green' + opacity: 0.8 + MouseArea { + id: dragProxyArea + anchors.fill: parent + drag.target: parent + drag.axis: Drag.XAxis + drag.smoothed: false + property int dragFrame + property bool shiftClick: false + cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor + onPressed: { + dragFrame = -1 + timeline.activeTrack = dragProxy.sourceTrack + if (mouse.modifiers & Qt.ShiftModifier) { + if (timeline.selection.indexOf(dragProxy.draggedItem) == -1) { + timeline.addSelection(dragProxy.draggedItem) + } else { + timeline.removeSelection(dragProxy.draggedItem) + endDrag() + shiftClick = true + return + } + shiftClick = true + } else { + if (timeline.selection.indexOf(dragProxy.draggedItem) == -1) { + timeline.selection = [ dragProxy.draggedItem ] + } + shiftClick = false + } + timeline.showAsset(dragProxy.draggedItem) + root.stopScrolling = true + clipBeingMovedId = dragProxy.draggedItem + if (dragProxy.draggedItem > -1) { + var tk = controller.getItemTrackId(dragProxy.draggedItem) + var x = controller.getItemPosition(dragProxy.draggedItem) + var posx = Math.round((parent.x)/ root.timeScale) + if (tk != Logic.getTrackIdFromPos(parent.y) || x != posx) { + console.log('INCORRECT DRAG, ABORTING\n!!!!!!!!!!') + dragProxy.draggedItem = -1 + mouse.accepted = false + } else { + focus = true; + dragProxy.masterObject.originalX = dragProxy.masterObject.x + dragProxy.masterObject.originalTrackId = dragProxy.masterObject.trackId + dragProxy.masterObject.forceActiveFocus(); + if (!dragProxy.masterObject.selected) { + dragProxy.masterObject.clicked(dragProxy.masterObject, mouse.modifiers == Qt.ShiftModifier) + } + } + } else { + mouse.accepted = false + parent.x = 0 + parent.y = 0 + parent.width = 0 + parent.height = 0 + } + } + onPositionChanged: { + if (!shiftClick && dragProxy.draggedItem > -1 && mouse.buttons === Qt.LeftButton) { + continuousScrolling(mouse.x + parent.x) + var mapped = tracksContainerArea.mapFromItem(dragProxy, mouse.x, mouse.y).x + root.mousePosChanged(Math.round(mapped / timeline.scaleFactor)) + var posx = Math.round((parent.x)/ root.timeScale) + var posy = Math.min(Math.max(0, mouse.y + parent.y), tracksContainerArea.height) + var tId = Logic.getTrackIdFromPos(posy) + timeline.activeTrack = tId + console.log('+ + RQST ITEM MOVE: ', posx, ' TK: ', tId) + if (dragProxy.isComposition) { + dragFrame = controller.suggestCompositionMove(dragProxy.draggedItem, tId, posx, root.snapping) + } else { + dragFrame = controller.suggestClipMove(dragProxy.draggedItem, tId, posx, root.snapping) + } + var delta = dragFrame - dragProxy.sourceFrame + if (delta != 0) { + var s = timeline.timecode(Math.abs(delta)) + // remove leading zeroes + if (s.substring(0, 3) === '00:') + s = s.substring(3) + s = ((delta < 0)? '-' : (delta > 0)? '+' : '') + s + bubbleHelp.show(parent.x, ruler.height, s) + } else bubbleHelp.hide() + } + } + onReleased: { + clipBeingMovedId = -1 + if (!shiftClick && dragProxy.draggedItem > -1 && dragFrame > -1) { + var tId = controller.getItemTrackId(dragProxy.draggedItem) + if (dragProxy.isComposition) { + controller.requestCompositionMove(dragProxy.draggedItem, dragProxy.sourceTrack, dragProxy.sourceFrame, false, false, false) + controller.requestCompositionMove(dragProxy.draggedItem, tId, dragFrame , true, true, true) + } else { + controller.requestClipMove(dragProxy.draggedItem, dragProxy.sourceTrack, dragProxy.sourceFrame, false, false, false) + controller.requestClipMove(dragProxy.draggedItem, tId, dragFrame , true, true, true) + } + bubbleHelp.hide() + } + } + } + } MouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton onWheel: zoomByWheel(wheel) - cursorShape: tracksArea.cursorShape + cursorShape: dragProxyArea.drag.active ? Qt.ClosedHandCursor : tracksArea.cursorShape } Column { // These make the striped background for the tracks. @@ -931,105 +1068,15 @@ id: trackDelegateModel model: multitrack delegate: Track { - model: multitrack - rootIndex: trackDelegateModel.modelIndex(index) + trackModel: multitrack + trackRootIndex: trackDelegateModel.modelIndex(index) height: trackHeight timeScale: timeline.scaleFactor width: tracksContainerArea.width isAudio: audio trackThumbsFormat: thumbsFormat - isCurrentTrack: item === timeline.activeTrack - trackId: item - onClipClicked: { - timeline.activeTrack = track.trackId - if (shiftClick === 1) { - timeline.addSelection(clip.clipId) - } else { - timeline.selection = [ clip.clipId ] - } - timeline.showAsset(clip.clipId) - } - onClipDragged: { - continuousScrolling(x) - // Show distance moved as time in a "bubble" help. - var delta = Math.round((clip.x / timeline.scaleFactor) - Math.round(clip.originalX) / timeline.scaleFactor) - if (delta != 0) { - var s = timeline.timecode(Math.abs(delta)) - // remove leading zeroes - if (s.substring(0, 3) === '00:') - s = s.substring(3) - s = ((delta < 0)? '-' : (delta > 0)? '+' : '') + s - bubbleHelp.show(x, mapToItem(null, x, clip.y).y, s) - clipBeingMovedId = clip.clipId - } else bubbleHelp.hide() - } - onClipDropped: { - console.log(" + + + ++ + DROPPED + + + + + + +"); - scrollTimer.running = false - bubbleHelp.hide() - clipBeingMovedId = -1 - } - onCompositionDropped: { - console.log(" + + + ++ + COMPOSITION DROPPED + + + + + + +"); - scrollTimer.running = false - bubbleHelp.hide() - clipBeingMovedId = -1 - } - onClipDraggedToTrack: { - var y = Math.max(0, pos - ruler.height) - var activeTrack = Logic.getTrackIndexFromPos(y) - var frame = Math.max(0, Math.round(clip.x / timeScale)) - if (activeTrack >= 0 && activeTrack < tracksRepeater.count) { - var track = tracksRepeater.itemAt(activeTrack) - //console.log('Dragging clip ',clip.clipId,' to track: ', activeTrack, ' - ', y) - if (controller.requestClipMove(clip.clipId, track.trackId, frame, false, false, false)) { - // Query the model to make sure on which track the clip is - timeline.activeTrack = controller.getClipTrackId(clip.clipId) - track = Logic.getTrackById(timeline.activeTrack) - clip.height = track.height - clip.y = track.y - Logic.getTrackById(clip.originalTrackId).y - clip.trackId = track.trackId - clip.currentFrame = frame - } else { - if (track.trackId != clip.trackId) { - // check if we can move on existing track - if (!controller.requestClipMove(clip.clipId, clip.trackId, frame, false, false, false)) { - // Abort move - clip.x = clip.currentFrame * timeScale - } else { - clip.x = frame * timeScale - clip.currentFrame = frame - var delta = Math.round((clip.x - clip.originalX) / timeline.scaleFactor) - var s = timeline.timecode(Math.abs(delta)) - // remove leading zeroes - if (s.substring(0, 3) === '00:') - s = s.substring(3) - s = ((delta < 0)? '-' : (delta > 0)? '+' : '') + s - bubbleHelp.show(xpos, mapToItem(null, x, clip.y).y, s) - } - } else { - // Abort move - clip.x = clip.draggedX - } - } - } else { - console.log('+ + + + +\nWARNING CLIP DRAGGED TO INVALID TRACK: ',activeTrack,'\n+ + + +'); - } - } - onCompositionDraggedToTrack: { - var y = pos - ruler.height - var tk = Logic.getTrackIndexFromPos(y) - var frame = Math.round(composition.x / timeScale) - if (tk >= 0 && tk < tracksRepeater.count) { - var track = tracksRepeater.itemAt(tk) - timeline.activeTrack = track.trackId - if (controller.requestCompositionMove(composition.clipId, track.trackId, frame, false, false)) { - composition.reparent(track) - composition.trackIndex = track.DelegateModel.itemsIndex - composition.trackId = track.trackId - } - } - } + isCurrentTrack: itemId === timeline.activeTrack + trackId: itemId Rectangle { anchors.right: parent.right anchors.left: parent.left @@ -1124,9 +1171,6 @@ target: timeline onPositionChanged: if (!stopScrolling) Logic.scrollIfNeeded() onFrameFormatChanged: ruler.adjustFormat() - /*onDragging: Logic.dragging(pos, duration) - onDropped: Logic.dropped() - onDropAccepted: Logic.acceptDrop(xml)*/ onSelectionChanged: { //cornerstone.selected = timeline.isMultitrackSelected() var selectedTrack = timeline.selectedTrack() diff --git a/src/timeline2/view/timelinecontroller.h b/src/timeline2/view/timelinecontroller.h --- a/src/timeline2/view/timelinecontroller.h +++ b/src/timeline2/view/timelinecontroller.h @@ -75,6 +75,7 @@ Q_INVOKABLE int selectedTrack() const { return m_selection.selectedTrack; } /** @brief Add a clip id to current selection */ + Q_INVOKABLE void removeSelection(int newSelection); Q_INVOKABLE void addSelection(int newSelection); /** @brief Edit an item's in/out points with a dialog */ diff --git a/src/timeline2/view/timelinecontroller.cpp b/src/timeline2/view/timelinecontroller.cpp --- a/src/timeline2/view/timelinecontroller.cpp +++ b/src/timeline2/view/timelinecontroller.cpp @@ -108,12 +108,33 @@ return m_model->tractor(); } +void TimelineController::removeSelection(int newSelection) +{ + if (!m_selection.selectedItems.contains(newSelection)) { + return; + } + m_selection.selectedItems.removeAll(newSelection); + std::unordered_set ids; + ids.insert(m_selection.selectedItems.cbegin(), m_selection.selectedItems.cend()); + m_model->m_temporarySelectionGroup = m_model->requestClipsGroup(ids, true, GroupType::Selection); + + std::unordered_set newIds; + if (m_model->m_temporarySelectionGroup >= 0) { + // new items were selected, inform model to prepare for group drag + newIds = m_model->getGroupElements(m_selection.selectedItems.constFirst()); + } + emit selectionChanged(); + if (!m_selection.selectedItems.isEmpty()) + emitSelectedFromSelection(); + else + emit selected(nullptr); +} + void TimelineController::addSelection(int newSelection) { if (m_selection.selectedItems.contains(newSelection)) { return; } - std::unordered_set previousSelection = getCurrentSelectionIds(); m_selection.selectedItems << newSelection; std::unordered_set ids; ids.insert(m_selection.selectedItems.cbegin(), m_selection.selectedItems.cend()); diff --git a/src/timeline2/view/timelinewidget.h b/src/timeline2/view/timelinewidget.h --- a/src/timeline2/view/timelinewidget.h +++ b/src/timeline2/view/timelinewidget.h @@ -69,6 +69,7 @@ std::unique_ptr m_transitionProxyModel; std::shared_ptr m_effectsModel; std::unique_ptr m_effectsProxyModel; + std::unique_ptr m_proxyFilter; /* @brief Returns an alphabetically sorted list of favorite effects or transitions */ const QStringList sortedItems(const QStringList &items, bool isTransition); diff --git a/src/timeline2/view/timelinewidget.cpp b/src/timeline2/view/timelinewidget.cpp --- a/src/timeline2/view/timelinewidget.cpp +++ b/src/timeline2/view/timelinewidget.cpp @@ -117,12 +117,15 @@ void TimelineWidget::setModel(std::shared_ptr model) { m_thumbnailer->resetProject(); - auto sortModel = new QSortFilterProxyModel(this); - sortModel->setSourceModel(model.get()); - sortModel->setSortRole(TimelineItemModel::SortRole); - sortModel->sort(0, Qt::DescendingOrder); + // Disable sort filter that causes index corruption on begin/endmoverows + /*m_proxyFilter.reset(new QSortFilterProxyModel(this)); + m_proxyFilter->setSourceModel(model.get()); + m_proxyFilter->setSortRole(TimelineItemModel::SortRole); + m_proxyFilter->setFilterRole(TimelineItemModel::SortRole); + m_proxyFilter->sort(0, Qt::DescendingOrder); + m_proxyFilter->setDynamicSortFilter(false);*/ m_proxy->setModel(model); - rootContext()->setContextProperty("multitrack", sortModel); + rootContext()->setContextProperty("multitrack", model.get()); //m_proxyFilter.get()); rootContext()->setContextProperty("controller", model.get()); rootContext()->setContextProperty("timeline", m_proxy); rootContext()->setContextProperty("transitionModel", sortedItems(KdenliveSettings::favorite_transitions(), true)); //m_transitionProxyModel.get());