Commit b1d63b64 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle

Merge branch 'master' of invent.kde.org:multimedia/kdenlive

parents 48de2321 64a60166
Pipeline #46993 passed with stage
in 10 minutes and 44 seconds
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<data name="effects" version="0">
<group list="chroma,frei0r.alpha0ps,frei0r.alphagrad,frei0r.alphaspot,frei0r.transparency,frei0r.mask0mate,rotoscoping,frei0r.keyspillm0pup,avfilter.despill,obscure,autotrack_rectangle,frei0r.bluescreen0r,lumakey,shape,spot_remover,frei0r.select0r,frei0r.spillsupress,frei0r.bgsubtract0r,opencv.tracker">
<group list="chroma,frei0r.alpha0ps,frei0r.alphagrad,frei0r.alphaspot,frei0r.transparency,frei0r.mask0mate,rotoscoping,frei0r.keyspillm0pup,avfilter.despill,obscure,autotrack_rectangle,frei0r.bluescreen0r,lumakey,shape,spot_remover,frei0r.select0r,frei0r.spillsupress,frei0r.bgsubtract0r,opencv.tracker,strobe">
<text>Alpha, Mask and Keying</text>
</group>
<group list="boxblur,frei0r.squareblur,avfilter.avgblur,avfilter.gblur,avfilter.smartblur,avfilter.boxblur,avfilter.unsharp,avfilter.sab">
......
......@@ -5,7 +5,7 @@
<comment xml:lang="ca">Document de projecte de vídeo del Kdenlive</comment>
<comment xml:lang="ca@valencia">Document de projecte de vídeo del Kdenlive</comment>
<comment xml:lang="en_GB">Kdenlive video project document</comment>
<comment xml:lang="es">Documento de proyecto de video de Kdenlive</comment>
<comment xml:lang="es">Proyecto de video de Kdenlive</comment>
<comment xml:lang="eu">Kdenlive-ko bideo-proiektuaren dokumentua</comment>
<comment xml:lang="fr">Document de projet vidéo pour Kdenlive</comment>
<comment xml:lang="it">Progetto video di Kdenlive</comment>
......@@ -47,6 +47,16 @@
</mime-type>
<mime-type type="application/kdenlivelayout">
<comment>Kdenlive application layout</comment>
<comment xml:lang="ca">Disposició de l'aplicació Kdenlive</comment>
<comment xml:lang="ca@valencia">Disposició de l'aplicació Kdenlive</comment>
<comment xml:lang="es">Organización de paneles de Kdenlive</comment>
<comment xml:lang="eu">Kdenlive aplikazioaren antolaera</comment>
<comment xml:lang="fr">Mise en page de l'application Kdenlive</comment>
<comment xml:lang="nl">Toepassingsindeling van Kdenlive</comment>
<comment xml:lang="pt">Disposição da aplicação Kdenlive</comment>
<comment xml:lang="ru">Макет приложения Kdenlive</comment>
<comment xml:lang="sv">Kdenlive programlayout</comment>
<comment xml:lang="uk">Компонування вікон програми Kdenlive</comment>
<sub-class-of type="text/plain"/>
<glob pattern="*.kdenlivelayout"/>
</mime-type>
......
......@@ -197,6 +197,32 @@ bool MarkerListModel::editMarker(GenTime oldPos, GenTime pos, QString comment, i
return res;
}
bool MarkerListModel::moveMarkers(QList<CommentedTime> markers, GenTime fromPos, GenTime toPos, Fun &undo, Fun &redo)
{
QWriteLocker locker(&m_lock);
if(markers.length() <= 0) {
return false;
}
bool res = false;
for (const auto &marker : markers) {
GenTime oldPos = marker.time();
QString oldComment = marker.comment();
int oldType = marker.markerType();
GenTime newPos = oldPos.operator+(toPos.operator-(fromPos));
res = removeMarker(oldPos, undo, redo);
if (res) {
res = addMarker(newPos, oldComment, oldType, undo, redo);
} else {
break;
}
}
return res;
}
Fun MarkerListModel::changeComment_lambda(GenTime pos, const QString &comment, int type)
{
QWriteLocker locker(&m_lock);
......@@ -357,6 +383,20 @@ QList<CommentedTime> MarkerListModel::getAllMarkers() const
return markers;
}
QList<CommentedTime> MarkerListModel::getMarkersInRange(int start, int end) const
{
READ_LOCK();
QList<CommentedTime> markers;
for (const auto &marker : m_markerList) {
int pos = marker.first.frames(pCore->getCurrentFps());
if(pos > start && (end == -1 || pos < end)) {
CommentedTime t(marker.first, marker.second.first, marker.second.second);
markers << t;
}
}
return markers;
}
std::vector<int> MarkerListModel::getSnapPoints() const
{
READ_LOCK();
......
......@@ -90,6 +90,16 @@ public:
*/
bool editMarker(GenTime oldPos, GenTime pos, QString comment = QString(), int type = -1);
/* @brief Moves all markers from on to another position
@param markers list of markers to move
@param fromPos
@param toPos
@param undo
@param redo
*/
bool moveMarkers(QList<CommentedTime> markers, GenTime fromPos, GenTime toPos, Fun &undo, Fun &redo);
/* @brief This describes the available markers type and their corresponding colors */
static std::array<QColor, 5> markerTypes;
......@@ -99,6 +109,12 @@ public:
/* @brief Returns all markers in model */
QList<CommentedTime> getAllMarkers() const;
/* @brief Returns all markers of model that are intersect with a given range.
* @param start is the position where start to search for markers
* @param end is the position after which markers will not be returned, set to -1 to get all markers after start
*/
QList<CommentedTime> getMarkersInRange(int start, int end) const;
/* @brief Returns all markers positions in model */
std::vector<int> getSnapPoints() const;
......
......@@ -457,28 +457,36 @@ void ClipCreationDialog::clipWidget(QDockWidget* m_DockClipWidget)
QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder"));
KFileWidget* fileWidget = new KFileWidget(QUrl::fromLocalFile(clipFolder), m_DockClipWidget);
fileWidget->setMode(KFile::Files | KFile::ExistingOnly | KFile::LocalOnly | KFile::Directory);
QString allExtensions = getExtensions().join(QLatin1Char(' '));
QString dialogFilter = allExtensions + QLatin1Char('|') + i18n("All Supported Files") + QStringLiteral("\n*|") + i18n("All Files");
QPushButton* importseq = new QPushButton(i18n("Import image sequence"));
// Make importseq checkable so that we can differentiate between a double click in filewidget and a click on the pushbutton
importseq->setCheckable(true);
fileWidget->setCustomWidget(importseq);
QObject::connect(fileWidget, &KFileWidget::accepted, fileWidget, [=] {
KFileItemList files = fileWidget->dirOperator()->selectedItems();
QList <QUrl> urls;
for (auto &f : files) {
urls << f.url();
// Required to only add file on double click and not on single click
fileWidget->setOperationMode(KFileWidget::Saving);
QObject::connect(fileWidget, &KFileWidget::accepted , [fileWidget, importseq]() {
if (importseq->isChecked()) {
// We are importing an image sequence, abort
return;
}
fileWidget->accept();
QList <QUrl> urls = fileWidget->selectedUrls();
pCore->bin()->droppedUrls(urls);
});
QObject::connect(importseq, &QPushButton::clicked, fileWidget, &KFileWidget::slotOk);
QObject::connect(importseq, &QPushButton::clicked, fileWidget, &KFileWidget::accepted);
QObject::connect(importseq, &QPushButton::clicked, fileWidget, &KFileWidget::accept);
fileWidget->setFilter(dialogFilter);
QObject::connect(importseq, &QPushButton::clicked, fileWidget, [=]{
QUrl url;
url = fileWidget->selectedUrl();
fileWidget->slotOk();
fileWidget->accepted();
fileWidget->accept();
QUrl url = fileWidget->selectedUrl();
QStringList patternlist;
QString pattern = SlideshowClip::selectedPath(url, false, QString(), &patternlist);
int count = patternlist.size();
QString fileName = url.fileName().section(QLatin1Char('.'), 0, -2);
importseq->setChecked(false);
if (count >= 1) {
while (fileName.size() > 0 && fileName.at(fileName.size() - 1).isDigit()) {
fileName.chop(1);
......
......@@ -3109,7 +3109,7 @@ void MainWindow::slotSetTool(ProjectTool tool)
QString message;
switch (tool) {
case SpacerTool:
message = i18n("Ctrl + click to use spacer on current track only");
message = i18n("Ctrl + click to use spacer on current track only, Shift + click to move guides too. You can combine both modifiers.");
break;
case RazorTool:
message = i18n("Click on a clip to cut it, Shift + move to preview cut frame");
......
......@@ -235,7 +235,11 @@ void MltConnection::refreshLumas()
} else {
format = f;
}
QStringList filesnames = dir.entryList(fileFilters, QDir::Files);
QStringList filesnames;
QDirIterator it(dir.absolutePath(), fileFilters, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()) {
filesnames.append(it.next());
}
if (MainWindow::m_lumaFiles.contains(format)) {
imagefiles = MainWindow::m_lumaFiles.value(format);
}
......
......@@ -256,56 +256,54 @@ bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool l
};
if (operation()) {
Fun reverse = []() { 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) {
if (auto ptr = m_parent.lock()) {
if (trackDuration > 0) {
// Operation changed parent track duration, update effect stack
int newDuration = ptr->getTrackById_const(m_currentTrackId)->trackDuration();
if (logUndo || trackDuration != newDuration) {
// A clip move changed the track duration, update track effects
ptr->getTrackById(m_currentTrackId)->m_effectStack->adjustStackLength(true, 0, trackDuration, 0, newDuration, 0, undo, redo, 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) {
if (auto ptr = m_parent.lock()) {
if (trackDuration > 0) {
// Operation changed parent track duration, update effect stack
int newDuration = ptr->getTrackById_const(m_currentTrackId)->trackDuration();
if (logUndo || trackDuration != newDuration) {
// A clip move changed the track duration, update track effects
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, hasMix);
}
}
reverse = [this, old_in, old_out, track_reverse, logUndo, oldIn, oldOut, right, roles]() {
if (track_reverse()) {
setInOut(old_in, old_out);
if (m_currentTrackId > -1) {
if (auto ptr = m_parent.lock()) {
QModelIndex ix = ptr->makeClipIndexFromID(m_id);
ptr->notifyChange(ix, ix, roles);
if (logUndo && !ptr->getTrackById_const(m_currentTrackId)->isAudioTrack()) {
if (right) {
int newOut = m_position + getOut() - getIn();
if (oldOut < newOut) {
emit ptr->invalidateZone(oldOut, newOut);
} else {
emit ptr->invalidateZone(newOut, oldOut);
}
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]() {
if (track_reverse()) {
setInOut(old_in, old_out);
if (m_currentTrackId > -1) {
if (auto ptr = m_parent.lock()) {
QModelIndex ix = ptr->makeClipIndexFromID(m_id);
ptr->notifyChange(ix, ix, roles);
if (logUndo && !ptr->getTrackById_const(m_currentTrackId)->isAudioTrack()) {
if (right) {
int newOut = m_position + getOut() - getIn();
if (oldOut < newOut) {
emit ptr->invalidateZone(oldOut, newOut);
} else {
if (oldIn < m_position) {
emit ptr->invalidateZone(oldIn, m_position);
} else {
emit ptr->invalidateZone(m_position, oldIn);
}
emit ptr->invalidateZone(newOut, oldOut);
}
} else {
if (oldIn < m_position) {
emit ptr->invalidateZone(oldIn, m_position);
} else {
emit ptr->invalidateZone(m_position, oldIn);
}
}
}
}
return true;
}
qDebug()<<"============\n+++++++++++++++++\nREVRSE TRACK OP FAILED\n\n++++++++++++++++";
return false;
};
qDebug() << "----------\n-----------\n// ADJUSTING EFFECT LENGTH, LOGUNDO " << logUndo << ", " << old_in << "/" << inPoint << ", "
<< m_producer->get_playtime();
return true;
}
qDebug()<<"============\n+++++++++++++++++\nREVRSE TRACK OP FAILED\n\n++++++++++++++++";
return false;
};
qDebug() << "----------\n-----------\n// ADJUSTING EFFECT LENGTH, LOGUNDO " << logUndo << ", " << old_in << "/" << inPoint << ", "
<< m_producer->get_playtime();
adjustEffectLength(right, old_in, inPoint, old_out - old_in, m_producer->get_playtime(), offset, reverse, operation, logUndo);
}
adjustEffectLength(right, old_in, inPoint, old_out - old_in, m_producer->get_playtime(), offset, reverse, operation, logUndo);
UPDATE_UNDO_REDO(operation, reverse, undo, redo);
return true;
}
......
......@@ -325,7 +325,7 @@ int TimelineFunctions::requestSpacerStartOperation(const std::shared_ptr<Timelin
return -1;
}
bool TimelineFunctions::requestSpacerEndOperation(const std::shared_ptr<TimelineItemModel> &timeline, int itemId, int startPosition, int endPosition, int affectedTrack)
bool TimelineFunctions::requestSpacerEndOperation(const std::shared_ptr<TimelineItemModel> &timeline, int itemId, int startPosition, int endPosition, int affectedTrack, Fun &undo, Fun &redo)
{
// Move group back to original position
int track = timeline->getItemTrackId(itemId);
......@@ -338,9 +338,6 @@ bool TimelineFunctions::requestSpacerEndOperation(const std::shared_ptr<Timeline
timeline->requestSubtitleMove(itemId, startPosition, false, false);
}
std::unordered_set<int> clips = timeline->getGroupElements(itemId);
// Start undoable command
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
int mainGroup = timeline->m_groups->getRootId(itemId);
bool final = false;
bool liftOk = true;
......@@ -1818,7 +1815,10 @@ bool TimelineFunctions::requestDeleteBlankAt(const std::shared_ptr<TimelineItemM
return false;
}
int start = timeline->getItemPosition(cid);
requestSpacerEndOperation(timeline, cid, start, start - spaceDuration, affectAllTracks ? -1 : trackId);
// Start undoable command
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
requestSpacerEndOperation(timeline, cid, start, start - spaceDuration, affectAllTracks ? -1 : trackId, undo, redo);
return true;
}
......
......@@ -86,7 +86,7 @@ struct TimelineFunctions
static bool requestDeleteBlankAt(const std::shared_ptr<TimelineItemModel> &timeline, int trackId, int position, bool affectAllTracks);
static int requestSpacerStartOperation(const std::shared_ptr<TimelineItemModel> &timeline, int trackId, int position);
static bool requestSpacerEndOperation(const std::shared_ptr<TimelineItemModel> &timeline, int itemId, int startPosition, int endPosition, int affectedTrack);
static bool requestSpacerEndOperation(const std::shared_ptr<TimelineItemModel> &timeline, int itemId, int startPosition, int endPosition, int affectedTrack, Fun &undo, Fun &redo);
static bool extractZone(const std::shared_ptr<TimelineItemModel> &timeline, QVector<int> tracks, QPoint zone, bool liftOnly);
static bool liftZone(const std::shared_ptr<TimelineItemModel> &timeline, int trackId, QPoint zone, Fun &undo, Fun &redo);
static bool removeSpace(const std::shared_ptr<TimelineItemModel> &timeline, QPoint zone, Fun &undo, Fun &redo, QVector<int> allowedTracks = QVector<int>(), bool useTargets = true);
......
......@@ -688,9 +688,10 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
if (old_trackId == trackId) {
// We are moving a clip on same track
if (finalMove && position >= mixData.first.firstClipInOut.second) {
position += m_allClips[clipId]->getMixDuration() - m_allClips[clipId]->getMixCutPosition();
removeMixWithUndo(clipId, local_undo, local_redo);
}
} else if (finalMove) {
} else {
// Clip moved to another track, delete mix
int subPlaylist = m_allClips[clipId]->getSubPlaylistIndex();
update_playlist = [this, clipId, old_trackId, trackId, finalMove]() {
......@@ -718,6 +719,7 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
if (finalMove && (position + clipDuration <= mixData.second.secondClipInOut.first)) {
// Moved outside mix zone
removeMixWithUndo(mixData.second.secondClipId, local_undo, local_redo);
}
} else {
// Clip moved to another track, delete mix
......@@ -738,11 +740,8 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
int mixEnd = m_allClips[mixData.first.firstClipId]->getPosition() + m_allClips[mixData.first.firstClipId]->getPlaytime();
if (mixEnd < position) {
// Mix will be deleted, recreate on undo
bool isAudio = getTrackById_const(old_trackId)->isAudioTrack();
update_playlist_undo = [this, mixData, old_trackId, isAudio]() {
bool result = getTrackById_const(old_trackId)->createMix(mixData.first, isAudio);
return result;
};
position += m_allClips[mixData.first.secondClipId]->getMixDuration() - m_allClips[mixData.first.secondClipId]->getMixCutPosition();
removeMixWithUndo(mixData.first.secondClipId, local_undo, local_redo);
}
}
}
......@@ -752,11 +751,7 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
int mixEnd = m_allClips[mixData.second.secondClipId]->getPosition();
if (mixEnd > position + m_allClips[clipId]->getPlaytime()) {
// Mix will be deleted, recreate on undo
bool isAudio = getTrackById_const(old_trackId)->isAudioTrack();
update_playlist_undo = [this, mixData, old_trackId, isAudio]() {
bool result = getTrackById_const(old_trackId)->createMix(mixData.second, isAudio);
return result;
};
removeMixWithUndo(mixData.second.secondClipId, local_undo, local_redo);
}
}
}
......@@ -785,6 +780,7 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
Q_ASSERT(undone);
return false;
}
qDebug()<<":::MOVED CLIP: "<<clipId<<" TO "<<position;
sync_mix();
update_model();
simple_move_mix();
......@@ -2364,7 +2360,7 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
Q_ASSERT(undone);
return false;
}
int newStart = getTrackById_const(current_track_id)->getBlankStart(current_in - 1);
int newStart = getTrackById_const(current_track_id)->getBlankStart(current_in - 1, subPlaylist);
delta_pos = qMax(delta_pos, newStart - current_in);
}
} else {
......@@ -2374,7 +2370,7 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
subPlaylist = m_allClips[item.first]->getSubPlaylistIndex();
}
if (!getTrackById_const(current_track_id)->isAvailable(moveStart, moveEnd - moveStart, subPlaylist)) {
int newStart = getTrackById_const(current_track_id)->getBlankEnd(current_in + playtime);
int newStart = getTrackById_const(current_track_id)->getBlankEnd(current_in + playtime, subPlaylist);
if (newStart == current_in + playtime) {
// No move possible, abort
bool undone = local_undo();
......@@ -2814,8 +2810,9 @@ int TimelineModel::requestItemResizeInfo(int itemId, int in, int out, int size,
//TODO: don't allow subtitle overlap?
success = true;
}
// undo temp move
temp_undo();
if (success) {
temp_undo(); // undo temp move
size = proposed_size;
}
}
......
......@@ -1548,10 +1548,10 @@ bool TrackModel::requestRemoveMix(std::pair<int, int> clipIds, Fun &undo, Fun &r
Fun local_undo = []() { return true; };
Fun local_redo = []() { return true; };
if (auto ptr = m_parent.lock()) {
// Resize main clip
result = ptr->getClipPtr(clipIds.second)->requestResize(endPos - secondInPos, false, local_undo, local_redo, true, true);
// Resize first part clip
result = result && ptr->getClipPtr(clipIds.first)->requestResize(secondInPos - firstInPos, true, local_undo, local_redo, true, true);
result = ptr->getClipPtr(clipIds.first)->requestResize(secondInPos - firstInPos, true, local_undo, local_redo, true, true);
// Resize main clip
result = result && ptr->getClipPtr(clipIds.second)->requestResize(endPos - secondInPos, false, local_undo, local_redo, true, true);
}
if (result) {
PUSH_LAMBDA(local_redo, redo);
......
......@@ -315,6 +315,7 @@ Rectangle {
property int spacerFrame: -1
property int finalSpacerFrame: -1
property int spacerClickFrame: -1
property bool spacerGuides: false
property real timeScale: timeline.scaleFactor
property int snapping: (timeline.snap && (timeline.scaleFactor < 2 * baseUnit)) ? Math.floor(baseUnit / (timeline.scaleFactor > 3 ? timeline.scaleFactor / 2 : timeline.scaleFactor)) : -1
property var timelineSelection: timeline.selection
......@@ -1018,13 +1019,19 @@ Rectangle {
spacerTrack = tracksRepeater.itemAt(Logic.getTrackIndexFromPos(y)).trackInternalId
}
}
if(mouse.modifiers & Qt.ShiftModifier) {
//spacer tool and shift modifier
spacerGuides = true;
}
spacerGroup = timeline.requestSpacerStartOperation(spacerTrack, frame)
if (spacerGroup > -1) {
if (spacerGroup > -1 || spacerGuides) {
drag.axis = Drag.XAxis
Drag.active = true
Drag.proposedAction = Qt.MoveAction
spacerClickFrame = frame
spacerFrame = controller.getItemPosition(spacerGroup)
spacerFrame = spacerGroup > -1 ? controller.getItemPosition(spacerGroup) : frame
finalSpacerFrame = spacerFrame
}
} else if (root.activeTool === 0 || mouse.y <= ruler.height) {
......@@ -1103,7 +1110,7 @@ Rectangle {
rubberSelect.height = newY - rubberSelect.originY
}
continuousScrolling(newX, newY)
} else if ((pressedButtons & Qt.LeftButton) && !shiftPress) {
} else if ((pressedButtons & Qt.LeftButton) && (!shiftPress || spacerGuides)) {
if (root.activeTool === 0 || mouse.y < ruler.height) {
proxy.position = Math.max(0, Math.min((scrollView.contentX + mouse.x) / timeline.scaleFactor, timeline.fullDuration - 1))
} else if (root.activeTool === 2 && spacerGroup > -1) {
......@@ -1112,7 +1119,10 @@ Rectangle {
var frame = Math.round((mouse.x + scrollView.contentX) / timeline.scaleFactor) + spacerFrame - spacerClickFrame
finalSpacerFrame = controller.suggestItemMove(spacerGroup, track, frame, root.consumerPosition, (mouse.modifiers & Qt.ShiftModifier) ? 0 : root.snapping)[0]
continuousScrolling(mouse.x + scrollView.contentX, mouse.y + scrollView.contentY)
} else if (spacerGuides) {
finalSpacerFrame = Math.round((mouse.x + scrollView.contentX) / timeline.scaleFactor) + spacerFrame - spacerClickFrame
}
scim = true
} else {
scim = false
......@@ -1152,7 +1162,7 @@ Rectangle {
timeline.selectItems(t, startFrame, endFrame, mouse.modifiers & Qt.ControlModifier, selectBottomCompositions, selectSubs);
}
rubberSelect.y = -1
} else if (shiftPress) {
} else if (shiftPress && !spacerGuides) {
if (root.activeTool == 1) {
// Shift click, process seek
proxy.position = Math.min((scrollView.contentX + mouse.x) / timeline.scaleFactor, timeline.fullDuration - 1)
......@@ -1169,13 +1179,21 @@ Rectangle {
}
return
}
if (spacerGroup > -1 && finalSpacerFrame > -1) {
var frame = controller.getItemPosition(spacerGroup)
timeline.requestSpacerEndOperation(spacerGroup, spacerFrame, finalSpacerFrame, spacerTrack);
timeline.requestSpacerEndOperation(spacerGroup, spacerFrame, finalSpacerFrame, spacerTrack, spacerGuides ? spacerClickFrame : -1);
} else if (spacerGuides) {
timeline.moveGuidesInRange(spacerClickFrame, -1, finalSpacerFrame - spacerFrame)
}
if (spacerGroup > -1 && finalSpacerFrame > -1 || spacerGuides) {
spacerClickFrame = -1
spacerFrame = -1
spacerGroup = -1
spacerGuides = false
}
scim = false
}
......@@ -1449,7 +1467,7 @@ Rectangle {
controller.requestCompositionMove(dragProxy.draggedItem, tId, dragFrame , true, true, true)
} else {
if (controller.normalEdit()) {
controller.requestClipMove(dragProxy.draggedItem, dragProxy.sourceTrack, dragProxy.sourceFrame, moveMirrorTracks, true, false, false)
controller.requestClipMove(dragProxy.draggedItem, dragProxy.sourceTrack, dragProxy.sourceFrame, moveMirrorTracks, false, false, false)
controller.requestClipMove(dragProxy.draggedItem, tId, dragFrame , moveMirrorTracks, true, true, true)
} else {
// Fake move, only process final move
......
......@@ -1073,6 +1073,33 @@ void TimelineController::moveGuide(int frame, int newFrame)
guideModel->editMarker(pos, newPos);
}
bool TimelineController::moveGuidesInRange(int start, int end, int offset)
{
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
bool final = false;
final = moveGuidesInRange(start, end, offset, undo, redo);
if (final) {
if (offset > 0) {
pCore->pushUndo(undo, redo, i18n("Insert space"));
} else {
pCore->pushUndo(undo, redo, i18n("Remove space"));
}
return true;
} else {
undo();
}
return false;
}
bool TimelineController::moveGuidesInRange(int start, int end, int offset, Fun &undo, Fun &redo)
{
GenTime fromPos(start, pCore->getCurrentFps());
GenTime toPos(start + offset, pCore->getCurrentFps());
QList<CommentedTime> guides = pCore->projectManager()->current()->getGuideModel()->getMarkersInRange(start, end);
return pCore->projectManager()->current()->getGuideModel()->moveMarkers(guides, fromPos, toPos, undo, redo);
}
void TimelineController::switchGuide(int frame, bool deleteOnly)
{
bool markerFound = false;
......@@ -1505,10 +1532,17 @@ int TimelineController::requestSpacerStartOperation(int trackId, int position)
return itemId;
}
bool TimelineController::requestSpacerEndOperation(int clipId, int startPosition, int endPosition, int affectedTrack)
bool TimelineController::requestSpacerEndOperation(int clipId, int startPosition, int endPosition, int affectedTrack, int guideStart)
{
QMutexLocker lk(&m_metaMutex);
bool result = TimelineFunctions::requestSpacerEndOperation(m_model, clipId, startPosition, endPosition, affectedTrack);
// Start undoable command
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
if(guideStart > -1) {
moveGuidesInRange(guideStart, -1, endPosition - startPosition, undo, redo);
}
bool result = TimelineFunctions::requestSpacerEndOperation(m_model, clipId, startPosition, endPosition, affectedTrack, undo, redo);
return result;
}
......
......@@ -299,6 +299,21 @@ public:
*/
Q_INVOKABLE void editGuide(int frame = -1);
Q_INVOKABLE void moveGuide(int frame, int newFrame);
/* @brief Move all guides in the given range
* @param start the start point of the range in frames
* @param end the end point of the range in frames