Update timeline headers to have icons on first line

Introduce split video, fix issues with audio split
parent fe0ed6e0
......@@ -447,7 +447,7 @@ bool TimelineFunctions::requestSplitAudio(std::shared_ptr<TimelineItemModel> tim
// Now clear selection so we don't mess with groups
pCore->clearSelection();
for (int cid : clips) {
if (!timeline->getClipPtr(cid)->audioEnabled() || timeline->getClipPtr(cid)->clipState() == PlaylistState::AudioOnly) {
if (!timeline->getClipPtr(cid)->canBeAudio() || timeline->getClipPtr(cid)->clipState() == PlaylistState::AudioOnly) {
// clip without audio or audio only, skip
continue;
}
......@@ -489,6 +489,57 @@ bool TimelineFunctions::requestSplitAudio(std::shared_ptr<TimelineItemModel> tim
return done;
}
bool TimelineFunctions::requestSplitVideo(std::shared_ptr<TimelineItemModel> timeline, int clipId, int videoTarget)
{
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
const std::unordered_set<int> clips = timeline->getGroupElements(clipId);
bool done = false;
// Now clear selection so we don't mess with groups
pCore->clearSelection();
for (int cid : clips) {
if (!timeline->getClipPtr(cid)->canBeVideo() || timeline->getClipPtr(cid)->clipState() == PlaylistState::VideoOnly) {
// clip without audio or audio only, skip
continue;
}
int position = timeline->getClipPosition(cid);
int track = timeline->getClipTrackId(cid);
QList<int> possibleTracks = QList<int>() << videoTarget;
if (possibleTracks.isEmpty()) {
// No available audio track for splitting, abort
undo();
pCore->displayMessage(i18n("No available video track for split operation"), ErrorMessage);
return false;
}
int newId;
bool res = copyClip(timeline, cid, newId, PlaylistState::VideoOnly, undo, redo);
if (!res) {
bool undone = undo();
Q_ASSERT(undone);
pCore->displayMessage(i18n("Video split failed"), ErrorMessage);
return false;
}
bool success = false;
while (!success && !possibleTracks.isEmpty()) {
int newTrack = possibleTracks.takeFirst();
success = timeline->requestClipMove(newId, newTrack, position, true, false, undo, redo);
}
TimelineFunctions::changeClipState(timeline, cid, PlaylistState::AudioOnly, undo, redo);
success = success && timeline->m_groups->createGroupAtSameLevel(cid, std::unordered_set<int>{newId}, GroupType::AVSplit, undo, redo);
if (!success) {
bool undone = undo();
Q_ASSERT(undone);
pCore->displayMessage(i18n("Video split failed"), ErrorMessage);
return false;
}
done = true;
}
if (done) {
pCore->pushUndo(undo, redo, i18n("Split Video"));
}
return done;
}
void TimelineFunctions::setCompositionATrack(std::shared_ptr<TimelineItemModel> timeline, int cid, int aTrack)
{
std::function<bool(void)> undo = []() { return true; };
......
......@@ -87,6 +87,7 @@ struct TimelineFunctions
static bool changeClipState(std::shared_ptr<TimelineItemModel> timeline, int clipId, PlaylistState::ClipState status, Fun &undo, Fun &redo);
static bool requestSplitAudio(std::shared_ptr<TimelineItemModel> timeline, int clipId, int audioTarget);
static bool requestSplitVideo(std::shared_ptr<TimelineItemModel> timeline, int clipId, int videoTarget);
static void setCompositionATrack(std::shared_ptr<TimelineItemModel> timeline, int cid, int aTrack);
};
......
......@@ -199,6 +199,8 @@ QHash<int, QByteArray> TimelineItemModel::roleNames() const
roles[ItemIdRole] = "item";
roles[ItemATrack] = "a_track";
roles[HasAudio] = "hasAudio";
roles[CanBeAudioRole] = "canBeAudio";
roles[CanBeVideoRole] = "canBeVideo";
roles[ReloadThumbRole] = "reloadThumb";
return roles;
}
......@@ -259,6 +261,10 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int role) const
return clip->audioEnabled();
case IsAudioRole:
return clip->isAudioOnly();
case CanBeAudioRole:
return clip->canBeAudio();
case CanBeVideoRole:
return clip->canBeVideo();
case MarkersRole: {
return QVariant::fromValue<MarkerListModel *>(clip->getMarkerModel().get());
}
......
......@@ -119,6 +119,8 @@ public:
FramerateRole, /// clip only
GroupedRole, /// clip only
HasAudio, /// clip only
CanBeAudioRole, /// clip only
CanBeVideoRole, /// clip only
IsMuteRole, /// track only
IsHiddenRole, /// track only
IsAudioRole,
......
......@@ -61,6 +61,8 @@ Rectangle {
property int draggedX: x
property bool selected: false
property bool hasAudio
property bool canBeAudio
property bool canBeVideo
property string hash: 'ccc' //TODO
property double speed: 1.0
property color borderColor: 'black'
......@@ -280,7 +282,7 @@ Rectangle {
}
}
onPositionChanged: {
if (pressed) {
if (pressed && mouse.buttons === Qt.LeftButton) {
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
......@@ -327,6 +329,8 @@ Rectangle {
clipMenu.clipStatus = clipRoot.clipStatus
clipMenu.grouped = clipRoot.grouped
clipMenu.trackId = clipRoot.trackId
clipMenu.canBeAudio = clipRoot.canBeAudio
clipMenu.canBeVideo = clipRoot.canBeVideo
clipMenu.popup()
}
}
......
......@@ -8,6 +8,8 @@ OLD.Menu {
property int clipStatus
property int trackId
property bool grouped
property bool canBeAudio
property bool canBeVideo
function show() {
//mergeItem.visible = timeline.mergeClipWithNext(trackIndex, index, true)
menu.popup()
......@@ -51,7 +53,12 @@ OLD.Menu {
OLD.MenuItem {
text: i18n('Split Audio')
onTriggered: timeline.splitAudio(clipId)
visible: clipStatus == ClipState.VideoOnly
visible: canBeAudio && clipStatus == ClipState.VideoOnly
}
OLD.MenuItem {
text: i18n('Split Video')
onTriggered: timeline.splitVideo(clipId)
visible: canBeVideo && clipStatus == ClipState.AudioOnly
}
OLD.MenuItem {
text: i18n('Remove')
......
......@@ -211,6 +211,8 @@ Column{
item.isAudio= model.audio
item.markers= model.markers
item.hasAudio = model.hasAudio
item.canBeAudio = model.canBeAudio
item.canBeVideo = model.canBeVideo
//item.binId= model.binId
} else {
console.log('loaded composition: ', model.start, ', ID: ', model.item, ', index: ', trackRoot.DelegateModel.itemsIndex)
......
......@@ -125,7 +125,7 @@ Rectangle {
onClicked: {
trackHeadRoot.myTrackHeight = trackHeadRoot.collapsed ? Math.max(collapsedHeight * 1.5, controller.getTrackProperty(trackId, "kdenlive:trackheight")) : collapsedHeight
}
tooltip: buttonBar.visible? i18n('Minimize') : i18n('Expand')
tooltip: trackLabel.visible? i18n('Minimize') : i18n('Expand')
}
Item {
width: nameEdit.height
......@@ -203,6 +203,66 @@ Rectangle {
]
}
}
Rectangle {
// Spacer
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
}
ToolButton {
id: muteButton
implicitHeight: trackHeadRoot.iconSize
implicitWidth: trackHeadRoot.iconSize
visible: isAudio
iconName: isMute ? 'kdenlive-hide-audio' : 'kdenlive-show-audio'
iconSource: isMute ? 'qrc:///pics/kdenlive-hide-audio.svgz' : 'qrc:///pics/kdenlive-show-audio.svgz'
onClicked: controller.setTrackProperty(trackId, "hide", isMute ? isHidden ? '1' : '0' : isHidden ? '3' : '2')
tooltip: isMute? i18n('Unmute') : i18n('Mute')
}
ToolButton {
id: hideButton
implicitHeight: trackHeadRoot.iconSize
implicitWidth: trackHeadRoot.iconSize
visible: !isAudio
iconName: isHidden ? 'kdenlive-hide-video' : 'kdenlive-show-video'
iconSource: isHidden? 'qrc:///pics/kdenlive-hide-video.svgz' : 'qrc:///pics/kdenlive-show-video.svgz'
onClicked: {
controller.setTrackProperty(trackId, "hide", isHidden ? isMute ? '2' : '0' : isMute ? '3' : '1')
timeline.requestRefresh()
}
tooltip: isHidden? i18n('Show') : i18n('Hide')
}
ToolButton {
id: lockButton
implicitHeight: trackHeadRoot.iconSize
implicitWidth: trackHeadRoot.iconSize
iconName: isLocked ? 'kdenlive-lock' : 'kdenlive-unlock'
iconSource: isLocked ? 'qrc:///pics/kdenlive-lock.svg' : 'qrc:///pics/kdenlive-unlock.svg'
onClicked: controller.setTrackProperty(trackId, "kdenlive:locked_track", isLocked ? '0' : '1')
tooltip: isLocked? i18n('Unlock track') : i18n('Lock track')
SequentialAnimation {
id: flashLock
loops: 1
ScaleAnimator {
target: lockButton
from: 1
to: 2
duration: 500
}
ScaleAnimator {
target: lockButton
from: 2
to: 1
duration: 500
}
}
}
Layout.rightMargin: 4
}
RowLayout {
Rectangle {
id: trackLabel
color: 'transparent'
......@@ -210,6 +270,7 @@ Rectangle {
radius: 2
border.color: trackNameMouseArea.containsMouse ? activePalette.highlight : 'transparent'
height: nameEdit.height
visible: (trackHeadRoot.height >= trackLabel.height + muteButton.height + resizer.height)
MouseArea {
id: trackNameMouseArea
anchors.fill: parent
......@@ -271,69 +332,6 @@ Rectangle {
}
}
}
RowLayout {
spacing: root.baseUnit / 2
id: buttonBar
visible: (trackHeadRoot.height >= trackLabel.height + muteButton.height + resizer.height)
Layout.rightMargin: 4
Rectangle {
// Spacer
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
}
ToolButton {
id: muteButton
implicitHeight: trackHeadRoot.iconSize
implicitWidth: trackHeadRoot.iconSize
visible: isAudio
iconName: isMute ? 'kdenlive-hide-audio' : 'kdenlive-show-audio'
iconSource: isMute ? 'qrc:///pics/kdenlive-hide-audio.svgz' : 'qrc:///pics/kdenlive-show-audio.svgz'
onClicked: controller.setTrackProperty(trackId, "hide", isMute ? isHidden ? '1' : '0' : isHidden ? '3' : '2')
tooltip: isMute? i18n('Unmute') : i18n('Mute')
}
ToolButton {
id: hideButton
implicitHeight: trackHeadRoot.iconSize
implicitWidth: trackHeadRoot.iconSize
visible: !isAudio
iconName: isHidden ? 'kdenlive-hide-video' : 'kdenlive-show-video'
iconSource: isHidden? 'qrc:///pics/kdenlive-hide-video.svgz' : 'qrc:///pics/kdenlive-show-video.svgz'
onClicked: {
controller.setTrackProperty(trackId, "hide", isHidden ? isMute ? '2' : '0' : isMute ? '3' : '1')
timeline.requestRefresh()
}
tooltip: isHidden? i18n('Show') : i18n('Hide')
}
ToolButton {
id: lockButton
implicitHeight: trackHeadRoot.iconSize
implicitWidth: trackHeadRoot.iconSize
iconName: isLocked ? 'kdenlive-lock' : 'kdenlive-unlock'
iconSource: isLocked ? 'qrc:///pics/kdenlive-lock.svg' : 'qrc:///pics/kdenlive-unlock.svg'
onClicked: controller.setTrackProperty(trackId, "kdenlive:locked_track", isLocked ? '0' : '1')
tooltip: isLocked? i18n('Unlock track') : i18n('Lock track')
SequentialAnimation {
id: flashLock
loops: 1
ScaleAnimator {
target: lockButton
from: 1
to: 2
duration: 500
}
ScaleAnimator {
target: lockButton
from: 2
to: 1
duration: 500
}
}
}
}
Rectangle {
// Spacer
color: "transparent"
......
......@@ -1472,6 +1472,11 @@ void TimelineController::splitAudio(int clipId)
TimelineFunctions::requestSplitAudio(m_model, clipId, audioTarget());
}
void TimelineController::splitVideo(int clipId)
{
TimelineFunctions::requestSplitVideo(m_model, clipId, videoTarget());
}
void TimelineController::switchTrackLock(bool applyToAll)
{
if (!applyToAll) {
......
......@@ -261,6 +261,7 @@ public:
Q_INVOKABLE void extract(int clipId);
Q_INVOKABLE void splitAudio(int clipId);
Q_INVOKABLE void splitVideo(int clipId);
/* @brief Seeks to selected clip start / end
*/
......
Markdown is supported
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