Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/commands/timelinecommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,31 @@ void NameTrackCommand::undo()
m_model.setTrackName(m_trackIndex, m_oldName);
}

MergeCommand::MergeCommand(MultitrackModel& model, int trackIndex, int clipIndex, QUndoCommand * parent)
: QUndoCommand(parent)
, m_model(model)
, m_trackIndex(trackIndex)
, m_clipIndex(clipIndex)
, m_undoHelper(m_model)
{
setText(QObject::tr("Merge adjacent clips"));
}

void MergeCommand::redo()
{
LOG_DEBUG() << "trackIndex" << m_trackIndex << "clipindex" << m_clipIndex;
m_undoHelper.recordBeforeState();
m_model.mergeClipWithNext(m_trackIndex, m_clipIndex, false);
m_undoHelper.recordAfterState();
}

void MergeCommand::undo()
{
LOG_DEBUG() << "trackIndex" << m_trackIndex << "clipindex" << m_clipIndex;
m_undoHelper.undoChanges();
}


MuteTrackCommand::MuteTrackCommand(MultitrackModel &model, int trackIndex, QUndoCommand *parent)
: QUndoCommand(parent)
, m_model(model)
Expand Down
13 changes: 13 additions & 0 deletions src/commands/timelinecommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,19 @@ class NameTrackCommand : public QUndoCommand
QString m_oldName;
};

class MergeCommand : public QUndoCommand
{
public:
MergeCommand(MultitrackModel& model, int trackIndex, int clipIndex, QUndoCommand * parent = 0);
void redo();
void undo();
private:
MultitrackModel& m_model;
int m_trackIndex;
int m_clipIndex;
UndoHelper m_undoHelper;
};

class MuteTrackCommand : public QUndoCommand
{
public:
Expand Down
9 changes: 9 additions & 0 deletions src/docks/timelinedock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,15 @@ void TimelineDock::removeTrack()
new Timeline::RemoveTrackCommand(m_model, currentTrack()));
}

bool TimelineDock::mergeClipWithNext(int trackIndex, int clipIndex, bool dryrun)
{
if (dryrun)
return m_model.mergeClipWithNext(trackIndex, clipIndex, true);

MAIN.undoStack()->push(
new Timeline::MergeCommand(m_model, trackIndex, clipIndex));
}

void TimelineDock::onProducerChanged(Mlt::Producer* after)
{
int trackIndex = currentTrack();
Expand Down
1 change: 1 addition & 0 deletions src/docks/timelinedock.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public slots:
void onSeeked(int position);
void append(int trackIndex);
void remove(int trackIndex, int clipIndex);
bool mergeClipWithNext(int trackIndex, int clipIndex, bool dryrun);
void lift(int trackIndex, int clipIndex);
void removeSelection(bool withCopy = false);
void liftSelection();
Expand Down
37 changes: 37 additions & 0 deletions src/models/multitrackmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2648,6 +2648,43 @@ void MultitrackModel::insertOrAdjustBlankAt(QList<int> tracks, int position, int
}
}

bool MultitrackModel::mergeClipWithNext(int trackIndex, int clipIndex, bool dryrun)
{
int i = m_trackList.at(trackIndex).mlt_index;
QScopedPointer<Mlt::Producer> track(m_tractor->track(i));
if (!track)
return false;
Mlt::Playlist playlist(*track);
if (clipIndex >= playlist.count() + 1)
return false;
Mlt::ClipInfo clip1;
Mlt::ClipInfo clip2;
playlist.clip_info(clipIndex, &clip1);
playlist.clip_info(clipIndex + 1, &clip2);

if (QString::fromUtf8(clip1.resource) != QString::fromUtf8(clip2.resource))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to use qstrcmp() when comparing C strings.

return false;

if (clip1.frame_out + 1 != clip2.frame_in)
return false;

if (dryrun)
return true;

playlist.resize_clip(clipIndex, clip1.frame_in, clip1.frame_out + clip2.frame_count);
QModelIndex modelIndex = createIndex(clipIndex, trackIndex);
QVector<int> roles;
roles << DurationRole;
roles << InPointRole;
emit dataChanged(modelIndex, modelIndex, roles);

beginRemoveRows(index(trackIndex), clipIndex + 1, clipIndex + 1);
playlist.remove(clipIndex + 1);
endRemoveRows();
emit modified();
return true;
}

void MultitrackModel::load()
{
if (m_tractor) {
Expand Down
1 change: 1 addition & 0 deletions src/models/multitrackmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class MultitrackModel : public QAbstractItemModel
bool isTransition(Mlt::Playlist& playlist, int clipIndex) const;
void insertTrack(int trackIndex, TrackType type = VideoTrackType);
void insertOrAdjustBlankAt(QList<int> tracks, int position, int length);
bool mergeClipWithNext(int trackIndex, int clipIndex, bool dryrun);

signals:
void created();
Expand Down
13 changes: 11 additions & 2 deletions src/qml/timeline/Clip.qml
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ Rectangle {
anchors.fill: parent
enabled: isBlank
acceptedButtons: Qt.RightButton
onClicked: menu.popup()
onClicked: menu.show()
}

MouseArea {
Expand Down Expand Up @@ -296,7 +296,7 @@ Rectangle {
(fadeInMouseArea.drag.active || fadeOutMouseArea.drag.active)? Qt.PointingHandCursor :
drag.active? Qt.ClosedHandCursor :
isBlank? Qt.ArrowCursor : Qt.OpenHandCursor
onClicked: menu.popup()
onClicked: menu.show()
}
}

Expand Down Expand Up @@ -565,6 +565,10 @@ Rectangle {
}
Menu {
id: menu
function show() {
mergeItem.visible = timeline.mergeClipWithNext(trackIndex, index, true)
popup()
}
MenuItem {
visible: !isBlank && !isTransition
text: qsTr('Cut')
Expand Down Expand Up @@ -602,6 +606,11 @@ Rectangle {
text: qsTr('Split At Playhead (S)')
onTriggered: timeline.splitClip(trackIndex, index)
}
MenuItem {
id: mergeItem
text: qsTr('Merge with next clip')
onTriggered: timeline.mergeClipWithNext(trackIndex, index, false)
}
MenuItem {
visible: !isBlank && !isTransition
text: qsTr('Rebuild Audio Waveform')
Expand Down