Commit 63a11481 authored by Eric Jiang's avatar Eric Jiang Committed by Jean-Baptiste Mardelle
Browse files

Fix ordering of new inserted tracks

Previously, when adding multiple AV tracks, the new tracks were always
added at the same index. Because audio tracks are added before the
insertion index, later video tracks would be inserted too early in the
list. This patch increments the insertion index for each new added
track.

This bug was reported as #1233
parent 93118c5d
Pipeline #174900 passed with stage
in 6 minutes and 53 seconds
......@@ -2726,7 +2726,7 @@ void MainWindow::slotNormalizeAudioChannel()
void MainWindow::slotInsertTrack()
{
pCore->monitorManager()->activateMonitor(Kdenlive::ProjectMonitor);
getCurrentTimeline()->controller()->addTrack(-1);
getCurrentTimeline()->controller()->beginAddTrack(-1);
}
void MainWindow::slotDeleteTrack()
......
......@@ -98,6 +98,48 @@ QModelIndex TimelineItemModel::index(int row, int column, const QModelIndex &par
return index(clipIndex, 0, index(trackIndex));
}*/
bool TimelineItemModel::addTracksAtPosition(int position, int tracksCount, QString &trackName, bool addAudioTrack, bool addAVTrack, bool addRecTrack)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool result = true;
int insertionIndex = position;
for (int ix = 0; ix < tracksCount; ++ix) {
int newTid;
result = requestTrackInsertion(insertionIndex, newTid, trackName, addAudioTrack, undo, redo);
// bump up insertion index so that the next new track goes after this one
insertionIndex++;
if (result) {
if (addAVTrack) {
int newTid2;
int mirrorPos = 0;
int mirrorId = getMirrorAudioTrackId(newTid);
if (mirrorId > -1) {
mirrorPos = getTrackMltIndex(mirrorId);
}
result = requestTrackInsertion(mirrorPos, newTid2, trackName, true, undo, redo);
// because we also added an audio track, we need to put the next
// new track's index is 1 further
insertionIndex++;
}
if (addRecTrack) {
setTrackProperty(newTid, "kdenlive:audio_rec", QStringLiteral("1"));
}
} else {
break;
}
}
if (result) {
pCore->pushUndo(undo, redo, addAVTrack || tracksCount > 1 ? i18nc("@action", "Insert Tracks") : i18nc("@action", "Insert Track"));
return true;
} else {
undo();
return false;
}
}
QModelIndex TimelineItemModel::makeClipIndexFromID(int clipId) const
{
Q_ASSERT(m_allClips.count(clipId) > 0);
......
......@@ -54,6 +54,11 @@ public:
QHash<int, QByteArray> roleNames() const override;
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
// QModelIndex makeIndex(int trackIndex, int clipIndex) const;
/** @brief Add multiple tracks at a specific position (used by the Add Track
* dialog). Returns true if successful, false otherwise. */
bool addTracksAtPosition(int position, int tracksCount, QString &trackName, bool addAudioTrack, bool addAVTrack, bool addRecTrack);
/** @brief Creates an index based on the ID of the clip*/
QModelIndex makeClipIndexFromID(int clipId) const override;
/** @brief Creates an index based on the ID of the compoition*/
......
......@@ -688,44 +688,19 @@ bool TimelineController::showWaveforms() const
return KdenliveSettings::audiothumbnails();
}
void TimelineController::addTrack(int tid)
void TimelineController::beginAddTrack(int tid)
{
if (tid == -1) {
tid = m_activeTrack;
}
QPointer<TrackDialog> d = new TrackDialog(m_model, tid, qApp->activeWindow());
if (d->exec() == QDialog::Accepted) {
bool audioRecTrack = d->addRecTrack();
bool addAVTrack = d->addAVTrack();
int tracksCount = d->tracksCount();
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool result = true;
for (int ix = 0; ix < tracksCount; ++ix) {
int newTid;
result = m_model->requestTrackInsertion(d->selectedTrackPosition(), newTid, d->trackName(), d->addAudioTrack(), undo, redo);
if (result) {
if (addAVTrack) {
int newTid2;
int mirrorPos = 0;
int mirrorId = m_model->getMirrorAudioTrackId(newTid);
if (mirrorId > -1) {
mirrorPos = m_model->getTrackMltIndex(mirrorId);
}
result = m_model->requestTrackInsertion(mirrorPos, newTid2, d->trackName(), true, undo, redo);
}
if (audioRecTrack) {
m_model->setTrackProperty(newTid, "kdenlive:audio_rec", QStringLiteral("1"));
}
} else {
break;
}
}
if (result) {
pCore->pushUndo(undo, redo, addAVTrack || tracksCount > 1 ? i18nc("@action", "Insert Tracks") : i18nc("@action", "Insert Track"));
} else {
auto trackName = d->trackName();
bool result = m_model->addTracksAtPosition(d->selectedTrackPosition(),
d->tracksCount(), trackName,
d->addAudioTrack(), d->addAVTrack(), d->addRecTrack());
if (!result) {
pCore->displayMessage(i18n("Could not insert track"), ErrorMessage, 500);
undo();
}
}
}
......
......@@ -272,9 +272,9 @@ public:
/** @brief Do we want to display audio thumbnails
*/
Q_INVOKABLE bool showWaveforms() const;
/** @brief Insert a timeline track
/** @brief Invoke the GUI to add new timeline tracks
*/
Q_INVOKABLE void addTrack(int tid);
Q_INVOKABLE void beginAddTrack(int tid);
/** @brief Remove multiple(or single) timeline tracks
*/
Q_INVOKABLE void deleteMultipleTracks(int tid);
......
......@@ -115,6 +115,85 @@ TEST_CASE("Basic creation/deletion of a track", "[TrackModel]")
pCore->m_projectManager = nullptr;
}
TEST_CASE("Adding multiple A/V tracks", "[TrackModel]")
{
auto binModel = pCore->projectItemModel();
std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
std::shared_ptr<MarkerListModel> guideModel = std::make_shared<MarkerListModel>(undoStack);
Mock<ProjectManager> pmMock;
When(Method(pmMock, undoStack)).AlwaysReturn(undoStack);
When(Method(pmMock, cacheDir)).AlwaysReturn(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
ProjectManager &mocked = pmMock.get();
pCore->m_projectManager = &mocked;
TimelineItemModel tim(&profile_model, undoStack);
Mock<TimelineItemModel> timMock(tim);
auto timeline = std::shared_ptr<TimelineItemModel>(&timMock.get(), [](...) {});
TimelineItemModel::finishConstruct(timeline, guideModel);
SECTION("Check AV track ordering")
{
// start state:
// * V2 (position 3)
// * V1
// * A1
// * A2 (position 0)
int a1, a2, v1, v2;
REQUIRE(timeline->requestTrackInsertion(0, a2, QString(), true));
REQUIRE(timeline->requestTrackInsertion(1, a1, QString(), true));
REQUIRE(timeline->requestTrackInsertion(2, v1, QString(), false));
REQUIRE(timeline->requestTrackInsertion(3, v2, QString(), false));
// when we add 3 AV tracks above V1, we should have:
// * V5 (position 9)
// * V4
// * V3
// * V2
// * V1
// * A1
// * A2
// * A3
// * A4
// * A5 (position 0)
QString trackName("New track");
REQUIRE(timeline->addTracksAtPosition(3, 3, trackName, false, true, false));
// but if the new tracks keep getting added at the same position 3, then we'll get
// * V2
// * V3
// * V1
// * V4
// * A4
// * V5
// * A5
// * A1
// * A3
// * A2
// (numbering doesn't look like this in the GUI)
REQUIRE(timeline->getTracksCount() == 10);
// first 5 tracks should be audio, and last 5 tracks should be video
auto it = timeline->m_allTracks.cbegin();
int position = 0;
while (it != timeline->m_allTracks.cend()) {
if (position < 5) {
CHECK((*it)->isAudioTrack());
} else {
CHECK(!(*it)->isAudioTrack());
}
it++;
position++;
}
// V1 track should be at index 5 (i.e. we shouldn't have inserted any video
// tracks before it)
REQUIRE(timeline->getTrackIndexFromPosition(5) == v1);
}
binModel->clean();
pCore->m_projectManager = nullptr;
}
TEST_CASE("Basic creation/deletion of a clip", "[ClipModel]")
{
......@@ -2065,8 +2144,7 @@ TEST_CASE("Operations under locked tracks", "[Locked]")
REQUIRE(timeline->requestItemResize(compo, 17, true) == 17);
check(17);
}
binModel->clean();
pCore->m_projectManager = nullptr;
}
Supports Markdown
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