[multimedia/kdenlive/release/26.08] src/timeline2: Fix duplicate clip only duplicating one clip
Jean-Baptiste Mardelle <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 80cdc71f7ee8aaf4b64e6f3e2df19de73d16cb38 by Jean-Baptiste Mardelle.
Committed on 23/07/2026 at 07:36.
Pushed by mardelle into branch 'release/26.08'.
Fix duplicate clip only duplicating one clip
Related to !878
M +15 -6 src/timeline2/model/timelinefunctions.cpp
M +4 -3 src/timeline2/model/timelinefunctions.hpp
M +13 -14 src/timeline2/view/timelinecontroller.cpp
https://invent.kde.org/multimedia/kdenlive/-/commit/80cdc71f7ee8aaf4b64e6f3e2df19de73d16cb38
diff --git a/src/timeline2/model/timelinefunctions.cpp b/src/timeline2/model/timelinefunctions.cpp
index ad3177521d..1974f2ef23 100644
--- a/src/timeline2/model/timelinefunctions.cpp
+++ b/src/timeline2/model/timelinefunctions.cpp
@@ -1954,12 +1954,13 @@ QString TimelineFunctions::copyClips(const std::shared_ptr<TimelineItemModel> &t
return copiedItems.toString();
}
-bool TimelineFunctions::pasteClips(const std::shared_ptr<TimelineItemModel> &timeline, const QString &pasteString, int trackId, int position)
+bool TimelineFunctions::pasteClips(const std::shared_ptr<TimelineItemModel> &timeline, const QString &pasteString, int trackId, int position,
+ QString historyText, bool select)
{
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
- if (TimelineFunctions::pasteClips(timeline, pasteString, trackId, position, undo, redo)) {
- pCore->pushUndo(undo, redo, i18n("Paste clips"));
+ if (TimelineFunctions::pasteClips(timeline, pasteString, trackId, position, undo, redo, 0, -1, select)) {
+ pCore->pushUndo(undo, redo, historyText.isEmpty() ? i18n("Paste clips") : historyText);
return true;
}
return false;
@@ -2038,7 +2039,7 @@ bool TimelineFunctions::pasteClipsWithUndo(const std::shared_ptr<TimelineItemMod
}
bool TimelineFunctions::pasteClips(const std::shared_ptr<TimelineItemModel> &timeline, const QString &pasteString, int trackId, int position, Fun &undo,
- Fun &redo, int inPos, int duration)
+ Fun &redo, int inPos, int duration, bool select)
{
timeline->requestClearSelection();
if (!semaphore.tryAcquire(1)) {
@@ -2573,7 +2574,7 @@ bool TimelineFunctions::pasteClips(const std::shared_ptr<TimelineItemModel> &tim
if (!clipsImported) {
// Clips from same document, directly proceed to pasting
- bool result = TimelineFunctions::pasteTimelineClips(timeline, copiedItems, position, undo, redo, false, inPos, duration);
+ bool result = TimelineFunctions::pasteTimelineClips(timeline, copiedItems, position, undo, redo, false, inPos, duration, select);
if (result && updatedPosition > 0) {
pCore->seekMonitor(Kdenlive::ProjectMonitor, updatedPosition);
}
@@ -2592,7 +2593,7 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
}
bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemModel> &timeline, QDomDocument copiedItems, int position, Fun &timeline_undo,
- Fun &timeline_redo, bool pushToStack, int inPos, int duration)
+ Fun &timeline_redo, bool pushToStack, int inPos, int duration, bool select)
{
// Wait until all bin clips are inserted
QDomNodeList clips = copiedItems.documentElement().elementsByTagName(QStringLiteral("clip"));
@@ -2607,6 +2608,8 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
offset *= ratio;
}
+ std::unordered_set<int> pastedItems;
+
QDomElement documentMixes = copiedItems.createElement(QStringLiteral("mixes"));
for (int i = 0; i < clips.count(); i++) {
QDomElement prod = clips.at(i).toElement();
@@ -2710,6 +2713,7 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
if (targetPlaylist > 0) {
timeline->m_allClips[newId]->setSubPlaylistIndex(targetPlaylist, curTrackId);
}
+ pastedItems.insert(newId);
correspondingIds[targetId] = newId;
std::shared_ptr<EffectStackModel> destStack = timeline->getClipEffectStackModel(newId);
destStack->fromXml(prod.firstChildElement(QStringLiteral("effects")), timeline_undo, timeline_redo);
@@ -2831,6 +2835,7 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
if (compoDuration != compoDuration2) {
timeline->requestItemResize(newId, compoDuration2, true, true, timeline_undo, timeline_redo, false);
}
+ pastedItems.insert(newId);
res = res && timeline->requestCompositionMove(newId, curTrackId, aTrackPos, position + newPos, true, true, timeline_undo, timeline_redo);
}
}
@@ -2870,6 +2875,10 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
qDebug() << "after Selection " << timeline->m_currentSelection.size();
return true;
};
+ if (select) {
+ // Select all items if requested
+ timeline->requestSetSelection(pastedItems);
+ }
PUSH_FRONT_LAMBDA(unselect, timeline_undo);
PUSH_FRONT_LAMBDA(unselect, timeline_redo);
// UPDATE_UNDO_REDO_NOLOCK(timeline_redo, timeline_undo, undo, redo);
diff --git a/src/timeline2/model/timelinefunctions.hpp b/src/timeline2/model/timelinefunctions.hpp
index 34e588c954..1044072f65 100644
--- a/src/timeline2/model/timelinefunctions.hpp
+++ b/src/timeline2/model/timelinefunctions.hpp
@@ -54,15 +54,16 @@ struct TimelineFunctions
static QString copyClips(const std::shared_ptr<TimelineItemModel> &timeline, const std::unordered_set<int> &itemIds, int mainClip = -1);
/** @brief Paste the clips as described by the string. Returns true on success*/
- static bool pasteClips(const std::shared_ptr<TimelineItemModel> &timeline, const QString &pasteString, int trackId, int position);
+ static bool pasteClips(const std::shared_ptr<TimelineItemModel> &timeline, const QString &pasteString, int trackId, int position,
+ QString historyText = QString(), bool select = false);
static bool pasteClips(const std::shared_ptr<TimelineItemModel> &timeline, const QString &pasteString, int trackId, int position, Fun &undo, Fun &redo,
- int inPos = 0, int duration = -1);
+ int inPos = 0, int duration = -1, bool select = false);
static bool pasteClipsWithUndo(const std::shared_ptr<TimelineItemModel> &timeline, const QString &pasteString, int trackId, int position, Fun &undo,
Fun &redo);
static bool pasteTimelineClips(const std::shared_ptr<TimelineItemModel> &timeline, const QDomDocument &copiedItems, int position, int inPos = 0,
int duration = -1);
static bool pasteTimelineClips(const std::shared_ptr<TimelineItemModel> &timeline, QDomDocument copiedItems, int position, Fun &timeline_undo,
- Fun &timeline_redo, bool pushToStack, int inPos = 0, int duration = -1);
+ Fun &timeline_redo, bool pushToStack, int inPos = 0, int duration = -1, bool select = false);
/** @brief Request the addition of multiple clips to the timeline
* If the addition of any of the clips fails, the entire operation is undone.
diff --git a/src/timeline2/view/timelinecontroller.cpp b/src/timeline2/view/timelinecontroller.cpp
index 49e110df4a..ec70542bcd 100644
--- a/src/timeline2/view/timelinecontroller.cpp
+++ b/src/timeline2/view/timelinecontroller.cpp
@@ -923,25 +923,24 @@ void TimelineController::cutItem()
void TimelineController::duplicateClip()
{
- int clipId = getMainSelectedClip();
- if (clipId == -1) {
+ std::unordered_set<int> selectedIds = m_model->getCurrentSelection();
+ if (selectedIds.empty()) {
pCore->displayMessage(i18n("No clip selected"), ErrorMessage, 500);
return;
}
+ int clipId = getMainSelectedClip();
+ if (clipId == -1) {
+ clipId = *selectedIds.begin();
+ }
int trackId = m_model->getItemTrackId(clipId);
- int endPos = m_model->getItemPosition(clipId) + m_model->getItemPlaytime(clipId);
- Fun undo = []() { return true; };
- Fun redo = []() { return true; };
- int newId = -1;
- PlaylistState::ClipState state = m_model->m_allClips[clipId]->clipState();
- bool res = TimelineFunctions::cloneClip(m_model, clipId, newId, state, -1, undo, redo);
- if (res && newId != -1) {
- res = (m_model->requestClipMove(newId, trackId, endPos, true, true, true, true, undo, redo) == TimelineModel::MoveSuccess);
+ // Get paste position (last frame of the selection)
+ int lastFrame = 0;
+ for (auto &id : selectedIds) {
+ int last = m_model->getItemPosition(id) + m_model->getItemPlaytime(id);
+ lastFrame = qMax(last, lastFrame);
}
- if (res) {
- pCore->pushUndo(undo, redo, i18n("Duplicate clip"));
- } else {
- undo();
+ const QString copyString = TimelineFunctions::copyClips(m_model, selectedIds, clipId);
+ if (!TimelineFunctions::pasteClips(m_model, copyString, trackId, lastFrame, i18n("Duplicate clip"), true)) {
pCore->displayMessage(i18n("Could not duplicate clip"), ErrorMessage, 500);
}
}