Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Kdenlive
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
259
Issues
259
List
Boards
Labels
Service Desk
Milestones
Merge Requests
14
Merge Requests
14
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Multimedia
Kdenlive
Commits
fc5f133a
Commit
fc5f133a
authored
Sep 11, 2020
by
Jean-Baptiste Mardelle
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Moving clip with mixes should now work correctly
parent
ee8fb1c2
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
279 additions
and
135 deletions
+279
-135
src/timeline2/model/timelinemodel.cpp
src/timeline2/model/timelinemodel.cpp
+123
-78
src/timeline2/model/timelinemodel.hpp
src/timeline2/model/timelinemodel.hpp
+1
-1
src/timeline2/model/trackmodel.cpp
src/timeline2/model/trackmodel.cpp
+101
-34
src/timeline2/model/trackmodel.hpp
src/timeline2/model/trackmodel.hpp
+7
-6
src/timeline2/view/qml/Clip.qml
src/timeline2/view/qml/Clip.qml
+7
-1
src/timeline2/view/qml/Track.qml
src/timeline2/view/qml/Track.qml
+1
-1
tests/mixtest.cpp
tests/mixtest.cpp
+39
-14
No files found.
src/timeline2/model/timelinemodel.cpp
View file @
fc5f133a
...
...
@@ -568,7 +568,7 @@ bool TimelineModel::requestFakeClipMove(int clipId, int trackId, int position, b
return
false
;
}
bool
TimelineModel
::
requestClipMove
(
int
clipId
,
int
trackId
,
int
position
,
bool
moveMirrorTracks
,
bool
updateView
,
bool
invalidateTimeline
,
bool
finalMove
,
Fun
&
undo
,
Fun
&
redo
,
bool
groupMove
)
bool
TimelineModel
::
requestClipMove
(
int
clipId
,
int
trackId
,
int
position
,
bool
moveMirrorTracks
,
bool
updateView
,
bool
invalidateTimeline
,
bool
finalMove
,
Fun
&
undo
,
Fun
&
redo
,
bool
groupMove
,
QMap
<
int
,
int
>
moving_clips
)
{
Q_UNUSED
(
moveMirrorTracks
)
// qDebug() << "// FINAL MOVE: " << invalidateTimeline << ", UPDATE VIEW: " << updateView<<", FINAL: "<<finalMove;
...
...
@@ -609,91 +609,111 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
return
true
;
};
}
Fun
move_mix
=
[]()
{
return
true
;
};
Fun
restore_mix
=
[]()
{
return
true
;
};
Fun
update_mix
=
[]()
{
return
true
;
};
Fun
move_mix_end
=
[]()
{
return
true
;
};
Fun
restore_mix_end
=
[]()
{
return
true
;
};
Fun
update_mix_end
=
[]()
{
return
true
;
};
if
(
isTrack
(
old_trackId
)
&&
getTrackById_const
(
old_trackId
)
->
hasMix
(
clipId
))
{
Fun
sync_mix
=
[]()
{
return
true
;
};
Fun
update_playlist
=
[]()
{
return
true
;
};
Fun
update_playlist_undo
=
[]()
{
return
true
;
};
Fun
simple_move_mix
=
[]()
{
return
true
;
};
Fun
simple_restore_mix
=
[]()
{
return
true
;
};
int
previous_track
=
moving_clips
.
value
(
clipId
,
-
1
);
if
(
old_trackId
==
-
1
&&
isTrack
(
previous_track
)
&&
getTrackById_const
(
previous_track
)
->
hasMix
(
clipId
))
{
// Clip is moved to another track
std
::
pair
<
MixInfo
,
MixInfo
>
mixData
=
getTrackById_const
(
previous_track
)
->
getMixInfo
(
clipId
);
bool
mixGroupMove
=
false
;
if
(
m_groups
->
isInGroup
(
clipId
))
{
int
parentGroup
=
m_groups
->
getRootId
(
clipId
);
if
(
parentGroup
>
-
1
)
{
std
::
unordered_set
<
int
>
sub
=
m_groups
->
getLeaves
(
parentGroup
);
if
(
sub
.
count
(
mixData
.
first
.
firstClipId
)
>
0
&&
sub
.
count
(
mixData
.
first
.
secondClipId
)
>
0
)
{
mixGroupMove
=
true
;
}
}
}
if
(
mixGroupMove
)
{
// We are moving a group on another track, delete and re-add
bool
isAudio
=
getTrackById_const
(
previous_track
)
->
isAudioTrack
();
simple_move_mix
=
[
this
,
previous_track
,
trackId
,
clipId
,
finalMove
,
mixData
,
isAudio
]()
{
getTrackById_const
(
previous_track
)
->
syncronizeMixes
(
finalMove
);
bool
result
=
getTrackById_const
(
trackId
)
->
createMix
(
mixData
.
first
,
isAudio
);
return
result
;
};
simple_restore_mix
=
[
this
,
previous_track
,
trackId
,
finalMove
,
mixData
,
isAudio
]()
{
bool
result
=
true
;
qDebug
()
<<
"===================
\n\n
TRACKMOVE UNDO DELETE MIX!!!
\n\n
======================="
;
if
(
finalMove
)
{
result
=
getTrackById_const
(
previous_track
)
->
createMix
(
mixData
.
first
,
isAudio
);
getTrackById_const
(
trackId
)
->
syncronizeMixes
(
finalMove
);
//getTrackById_const(previous_track)->syncronizeMixes(finalMove);
}
return
result
;
};
}
}
else
if
(
!
groupMove
&&
isTrack
(
old_trackId
)
&&
getTrackById_const
(
old_trackId
)
->
hasMix
(
clipId
))
{
// Clip has a mix
std
::
pair
<
MixInfo
,
MixInfo
>
mixData
=
getTrackById_const
(
old_trackId
)
->
getMixInfo
(
clipId
);
if
(
mixData
.
first
.
firstClipId
>
-
1
)
{
// We have a mix at clip start
update_mix
=
[
this
,
mixData
]()
{
QModelIndex
ix
=
makeClipIndexFromID
(
mixData
.
first
.
secondClipId
);
emit
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
StartRole
,
TimelineModel
::
MixRole
});
bool
mixGroupMove
=
false
;
if
(
m_groups
->
isInGroup
(
clipId
))
{
int
parentGroup
=
m_groups
->
getRootId
(
clipId
);
if
(
parentGroup
>
-
1
)
{
std
::
unordered_set
<
int
>
sub
=
m_groups
->
getLeaves
(
parentGroup
);
if
(
sub
.
count
(
mixData
.
first
.
firstClipId
)
>
0
&&
sub
.
count
(
mixData
.
first
.
secondClipId
)
>
0
)
{
mixGroupMove
=
true
;
}
}
}
sync_mix
=
[
this
,
old_trackId
,
finalMove
]()
{
getTrackById_const
(
old_trackId
)
->
syncronizeMixes
(
finalMove
);
return
true
;
};
if
(
old_trackId
!=
trackId
||
position
>=
mixData
.
first
.
firstClipInOut
.
second
)
{
// Clip moved to another track, or outside of mix duration, delete mix
int
subPlaylist
=
m_allClips
[
clipId
]
->
getSubPlaylistIndex
();
move_mix
=
[
this
,
old_trackId
,
clipId
,
finalMove
,
subPlaylist
]()
{
bool
result
=
getTrackById_const
(
old_trackId
)
->
deleteMix
(
clipId
,
finalMove
);
if
(
finalMove
)
{
//TODO: check if there is another mix on this clip
if
(
old_trackId
==
trackId
)
{
// We are moving a group on same track
if
(
finalMove
&&
position
>=
mixData
.
first
.
firstClipInOut
.
second
)
{
int
subPlaylist
=
m_allClips
[
clipId
]
->
getSubPlaylistIndex
();
update_playlist
=
[
this
,
clipId
,
subPlaylist
]()
{
m_allClips
[
clipId
]
->
setSubPlaylistIndex
(
subPlaylist
==
0
?
1
:
0
);
}
return
result
;
};
restore_mix
=
[
this
,
old_trackId
,
mixData
,
finalMove
,
subPlaylist
]()
{
if
(
finalMove
)
{
m_allClips
[
mixData
.
first
.
secondClipId
]
->
setSubPlaylistIndex
(
subPlaylist
);
}
bool
result
=
getTrackById_const
(
old_trackId
)
->
createMix
({
mixData
.
first
.
firstClipId
,
mixData
.
first
.
secondClipId
},
{
mixData
.
first
.
secondClipInOut
.
first
,
mixData
.
first
.
firstClipInOut
.
second
-
mixData
.
first
.
secondClipInOut
.
first
});
return
result
;
};
}
else
if
(
old_trackId
==
trackId
)
{
// Clip moved on same track, resize mix
move_mix
=
[
this
,
old_trackId
,
clipId
,
position
]()
{
return
getTrackById_const
(
old_trackId
)
->
resizeMixStart
(
clipId
,
position
);
};
restore_mix
=
[
this
,
old_trackId
,
clipId
,
mixData
]()
{
// Resize mix to oringinal start
return
getTrackById_const
(
old_trackId
)
->
resizeMixStart
(
clipId
,
mixData
.
first
.
secondClipInOut
.
first
);
};
return
true
;
};
bool
isAudio
=
getTrackById_const
(
old_trackId
)
->
isAudioTrack
();
update_playlist_undo
=
[
this
,
clipId
,
subPlaylist
,
old_trackId
,
mixData
,
isAudio
]()
{
m_allClips
[
clipId
]
->
setSubPlaylistIndex
(
subPlaylist
);
bool
result
=
getTrackById_const
(
old_trackId
)
->
createMix
(
mixData
.
first
,
isAudio
);
return
result
;
};
}
}
}
if
(
mixData
.
second
.
firstClipId
>
-
1
)
{
// We have a mix at clip end
update_mix_end
=
[
this
,
mixData
]()
{
QModelIndex
ix
=
makeClipIndexFromID
(
mixData
.
second
.
secondClipId
);
emit
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
StartRole
,
TimelineModel
::
MixRole
}
);
int
clipDuration
=
mixData
.
second
.
firstClipInOut
.
second
-
mixData
.
second
.
firstClipInOut
.
first
;
sync_mix
=
[
this
,
old_trackId
,
finalMove
]()
{
getTrackById_const
(
old_trackId
)
->
syncronizeMixes
(
finalMove
);
return
true
;
};
int
clipDuration
=
mixData
.
second
.
firstClipInOut
.
second
-
mixData
.
second
.
firstClipInOut
.
first
;
if
(
old_trackId
!=
trackId
||
(
position
+
clipDuration
<=
mixData
.
second
.
secondClipInOut
.
first
))
{
// Clip moved to another track, or outside of mix duration, delete mix
int
subPlaylist
=
m_allClips
[
mixData
.
second
.
secondClipId
]
->
getSubPlaylistIndex
();
move_mix_end
=
[
this
,
old_trackId
,
mixData
,
finalMove
,
subPlaylist
]()
{
bool
result
=
getTrackById_const
(
old_trackId
)
->
deleteMix
(
mixData
.
second
.
secondClipId
,
finalMove
);
if
(
finalMove
)
{
//TODO: check if there is another mix on this clip
if
(
old_trackId
==
trackId
)
{
if
(
finalMove
&&
(
position
+
clipDuration
<=
mixData
.
second
.
secondClipInOut
.
first
))
{
int
subPlaylist
=
m_allClips
[
mixData
.
second
.
secondClipId
]
->
getSubPlaylistIndex
();
update_playlist
=
[
this
,
mixData
,
subPlaylist
,
trackId
]()
{
m_allClips
[
mixData
.
second
.
secondClipId
]
->
setSubPlaylistIndex
(
subPlaylist
==
0
?
1
:
0
);
}
return
result
;
};
restore_mix_end
=
[
this
,
old_trackId
,
mixData
,
finalMove
,
subPlaylist
]()
{
if
(
finalMove
)
{
bool
result
=
getTrackById_const
(
trackId
)
->
switchPlaylist
(
mixData
.
second
.
secondClipId
,
mixData
.
second
.
secondClipInOut
.
first
,
subPlaylist
==
0
?
1
:
0
);
return
result
;
};
bool
isAudio
=
getTrackById_const
(
trackId
)
->
isAudioTrack
();
update_playlist_undo
=
[
this
,
mixData
,
subPlaylist
,
trackId
,
isAudio
](
)
{
m_allClips
[
mixData
.
second
.
secondClipId
]
->
setSubPlaylistIndex
(
subPlaylist
);
}
bool
result
=
getTrackById_const
(
old_trackId
)
->
createMix
({
mixData
.
second
.
firstClipId
,
mixData
.
second
.
secondClipId
},
{
mixData
.
second
.
secondClipInOut
.
first
,
mixData
.
second
.
firstClipInOut
.
second
-
mixData
.
second
.
secondClipInOut
.
first
});
return
result
;
};
}
else
if
(
old_trackId
==
trackId
)
{
// Clip moved on same track, resize mix
move_mix_end
=
[
this
,
old_trackId
,
mixData
,
position
,
clipDuration
]()
{
return
getTrackById_const
(
old_trackId
)
->
resizeMixEnd
(
mixData
.
second
.
secondClipId
,
position
+
clipDuration
);
};
restore_mix_end
=
[
this
,
old_trackId
,
mixData
]()
{
// Resize mix to oringinal start
return
getTrackById_const
(
old_trackId
)
->
resizeMixEnd
(
mixData
.
second
.
secondClipId
,
mixData
.
second
.
firstClipInOut
.
second
);
};
bool
result
=
getTrackById_const
(
trackId
)
->
switchPlaylist
(
mixData
.
second
.
secondClipId
,
mixData
.
second
.
secondClipInOut
.
first
,
subPlaylist
);
result
=
result
&&
getTrackById_const
(
trackId
)
->
createMix
(
mixData
.
second
,
isAudio
);
return
result
;
};
}
}
}
}
move_mix_end
();
PUSH_LAMBDA
(
update_mix
,
local_undo
);
PUSH_LAMBDA
(
update_mix_end
,
local_undo
);
PUSH_LAMBDA
(
simple_restore_mix
,
local_undo
);
if
(
finalMove
)
{
PUSH_LAMBDA
(
sync_mix
,
local_undo
);
}
if
(
old_trackId
!=
-
1
)
{
if
(
notifyViewOnly
)
{
PUSH_LAMBDA
(
update_model
,
local_undo
);
...
...
@@ -705,9 +725,8 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
return
false
;
}
}
move_mix
();
UPDATE_UNDO_REDO
(
move_mix
,
restore_mix
,
local_undo
,
local_redo
);
UPDATE_UNDO_REDO
(
move_mix_end
,
restore_mix_end
,
local_undo
,
local_redo
);
update_playlist
();
UPDATE_UNDO_REDO
(
update_playlist
,
update_playlist_undo
,
local_undo
,
local_redo
);
ok
=
getTrackById
(
trackId
)
->
requestClipInsertion
(
clipId
,
position
,
updateView
,
finalMove
,
local_undo
,
local_redo
,
groupMove
);
if
(
!
ok
)
{
qDebug
()
<<
"-------------
\n\n
INSERTION FAILED, REVERTING
\n\n
-------------------"
;
...
...
@@ -715,12 +734,15 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
Q_ASSERT
(
undone
);
return
false
;
}
sync_mix
();
update_model
();
update_mix
();
simple_move_mix
();
PUSH_LAMBDA
(
simple_move_mix
,
local_redo
);
if
(
finalMove
)
{
PUSH_LAMBDA
(
sync_mix
,
local_redo
);
}
if
(
notifyViewOnly
)
{
PUSH_LAMBDA
(
update_model
,
local_redo
);
PUSH_LAMBDA
(
update_mix
,
local_redo
);
PUSH_LAMBDA
(
update_mix_end
,
local_redo
);
}
UPDATE_UNDO_REDO
(
local_redo
,
local_undo
,
undo
,
redo
);
return
true
;
...
...
@@ -1852,9 +1874,11 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
Fun
local_undo
=
[]()
{
return
true
;
};
Fun
local_redo
=
[]()
{
return
true
;
};
std
::
vector
<
std
::
pair
<
int
,
int
>
>
sorted_clips
;
std
::
vector
<
int
>
sorted_clips_ids
;
std
::
vector
<
std
::
pair
<
int
,
std
::
pair
<
int
,
int
>
>
>
sorted_compositions
;
int
lowerTrack
=
-
1
;
int
upperTrack
=
-
1
;
QVector
<
int
>
tracksWithMix
;
// Separate clips from compositions to sort and check source tracks
for
(
int
affectedItemId
:
all_items
)
{
...
...
@@ -1870,6 +1894,12 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
}
if
(
isClip
(
affectedItemId
))
{
sorted_clips
.
push_back
({
affectedItemId
,
m_allClips
[
affectedItemId
]
->
getPosition
()});
sorted_clips_ids
.
push_back
(
affectedItemId
);
int
current_track_id
=
getClipTrackId
(
affectedItemId
);
if
(
!
tracksWithMix
.
contains
(
current_track_id
)
&&
getTrackById_const
(
current_track_id
)
->
hasMix
(
affectedItemId
))
{
// There is a mix, prepare for update
tracksWithMix
<<
current_track_id
;
}
}
else
{
sorted_compositions
.
push_back
({
affectedItemId
,
{
m_allCompositions
[
affectedItemId
]
->
getPosition
(),
getTrackMltIndex
(
m_allCompositions
[
affectedItemId
]
->
getCurrentTrackId
())}});
}
...
...
@@ -1900,7 +1930,6 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
};
// Check if there is a track move
bool
updatePositionOnly
=
false
;
// Second step, reinsert clips at correct positions
int
audio_delta
,
video_delta
;
audio_delta
=
video_delta
=
delta_track
;
...
...
@@ -1955,7 +1984,6 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
if
(
delta_track
==
0
&&
updateView
)
{
updateView
=
false
;
allowViewRefresh
=
false
;
updatePositionOnly
=
true
;
update_model
=
[
sorted_clips
,
sorted_compositions
,
finalMove
,
this
]()
{
QModelIndex
modelIndex
;
QVector
<
int
>
roles
{
StartRole
};
...
...
@@ -1975,6 +2003,7 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
}
std
::
unordered_map
<
int
,
int
>
old_track_ids
,
old_position
,
old_forced_track
;
QMap
<
int
,
int
>
oldTrackIds
;
// First, remove clips
if
(
delta_track
!=
0
)
{
// We delete our clips only if changing track
...
...
@@ -1983,6 +2012,8 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
old_track_ids
[
item
.
first
]
=
old_trackId
;
if
(
old_trackId
!=
-
1
)
{
bool
updateThisView
=
allowViewRefresh
;
// Keep track of old track for mixes
oldTrackIds
.
insert
(
item
.
first
,
old_trackId
);
ok
=
ok
&&
getTrackById
(
old_trackId
)
->
requestClipDeletion
(
item
.
first
,
updateThisView
,
finalMove
,
local_undo
,
local_redo
,
true
,
false
);
old_position
[
item
.
first
]
=
item
.
second
;
if
(
!
ok
)
{
...
...
@@ -2007,6 +2038,13 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
audio_delta
=
-
delta_track
;
}
}
Fun
sync_mix
=
[
this
,
tracksWithMix
,
finalMove
]()
{
for
(
int
tid
:
tracksWithMix
)
{
getTrackById_const
(
tid
)
->
syncronizeMixes
(
finalMove
);
}
return
true
;
};
// We need to insert depending on the move direction to avoid confusing the view
// std::reverse(std::begin(sorted_clips), std::end(sorted_clips));
bool
updateThisView
=
allowViewRefresh
;
...
...
@@ -2054,6 +2092,7 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
}
}
}
PUSH_LAMBDA
(
sync_mix
,
local_undo
);
for
(
const
std
::
pair
<
int
,
int
>
&
item
:
sorted_clips
)
{
int
current_track_id
=
getClipTrackId
(
item
.
first
);
if
(
!
allowedTracks
.
isEmpty
()
&&
!
allowedTracks
.
contains
(
current_track_id
))
{
...
...
@@ -2067,6 +2106,9 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
break
;
}
}
sync_mix
();
PUSH_LAMBDA
(
sync_mix
,
local_redo
);
if
(
ok
)
{
for
(
const
std
::
pair
<
int
,
std
::
pair
<
int
,
int
>>
&
item
:
sorted_compositions
)
{
int
current_track_id
=
getItemTrackId
(
item
.
first
);
...
...
@@ -2088,6 +2130,7 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
}
}
else
{
// Track changed
PUSH_LAMBDA
(
sync_mix
,
local_undo
);
for
(
const
std
::
pair
<
int
,
int
>
&
item
:
sorted_clips
)
{
int
current_track_id
=
old_track_ids
[
item
.
first
];
int
current_track_position
=
getTrackPosition
(
current_track_id
);
...
...
@@ -2101,7 +2144,7 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
std
::
advance
(
it
,
target_track_position
);
int
target_track
=
(
*
it
)
->
getId
();
int
target_position
=
old_position
[
item
.
first
]
+
delta_pos
;
ok
=
ok
&&
requestClipMove
(
item
.
first
,
target_track
,
target_position
,
moveMirrorTracks
,
updateThisView
,
finalMove
,
finalMove
,
local_undo
,
local_redo
,
true
);
ok
=
ok
&&
requestClipMove
(
item
.
first
,
target_track
,
target_position
,
moveMirrorTracks
,
updateThisView
,
finalMove
,
finalMove
,
local_undo
,
local_redo
,
true
,
oldTrackIds
);
}
else
{
ok
=
false
;
}
...
...
@@ -2111,6 +2154,8 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
return
false
;
}
}
sync_mix
();
PUSH_LAMBDA
(
sync_mix
,
local_redo
);
for
(
const
std
::
pair
<
int
,
std
::
pair
<
int
,
int
>
>
&
item
:
sorted_compositions
)
{
int
current_track_id
=
old_track_ids
[
item
.
first
];
int
current_track_position
=
getTrackPosition
(
current_track_id
);
...
...
src/timeline2/model/timelinemodel.hpp
View file @
fc5f133a
...
...
@@ -368,7 +368,7 @@ public:
/* Same function, but accumulates undo and redo, and doesn't check
for group*/
bool
requestClipMove
(
int
clipId
,
int
trackId
,
int
position
,
bool
moveMirrorTracks
,
bool
updateView
,
bool
invalidateTimeline
,
bool
finalMove
,
Fun
&
undo
,
Fun
&
redo
,
bool
groupMove
=
false
);
bool
requestClipMove
(
int
clipId
,
int
trackId
,
int
position
,
bool
moveMirrorTracks
,
bool
updateView
,
bool
invalidateTimeline
,
bool
finalMove
,
Fun
&
undo
,
Fun
&
redo
,
bool
groupMove
=
false
,
QMap
<
int
,
int
>
moving_clips
=
QMap
<
int
,
int
>
()
);
bool
requestCompositionMove
(
int
transid
,
int
trackId
,
int
compositionTrack
,
int
position
,
bool
updateView
,
bool
finalMove
,
Fun
&
undo
,
Fun
&
redo
);
/* When timeline edit mode is insert or overwrite, we fake the move (as it will overlap existing clips, and only process the real move on drop */
...
...
src/timeline2/model/trackmodel.cpp
View file @
fc5f133a
...
...
@@ -128,6 +128,20 @@ int TrackModel::getClipsCount()
return
count
;
}
bool
TrackModel
::
switchPlaylist
(
int
clipId
,
int
position
,
int
playlist
)
{
QWriteLocker
locker
(
&
m_lock
);
int
source_playlist
=
playlist
==
1
?
0
:
1
;
int
target_clip
=
m_playlists
[
source_playlist
].
get_clip_index_at
(
position
);
std
::
unique_ptr
<
Mlt
::
Producer
>
prod
(
m_playlists
[
source_playlist
].
replace_with_blank
(
target_clip
));
if
(
auto
ptr
=
m_parent
.
lock
())
{
std
::
shared_ptr
<
ClipModel
>
clip
=
ptr
->
getClipPtr
(
clipId
);
int
index
=
m_playlists
[
playlist
].
insert_at
(
position
,
*
clip
,
1
);
return
index
!=
-
1
;
}
return
false
;
}
Fun
TrackModel
::
requestClipInsertion_lambda
(
int
clipId
,
int
position
,
bool
updateView
,
bool
finalMove
,
bool
groupMove
)
{
QWriteLocker
locker
(
&
m_lock
);
...
...
@@ -1538,14 +1552,16 @@ std::pair<MixInfo, MixInfo> TrackModel::getMixInfo(int clipId) const
return
result
;
}
bool
TrackModel
::
deleteMix
(
int
clipId
,
bool
final
)
bool
TrackModel
::
deleteMix
(
int
clipId
,
bool
final
,
bool
notify
)
{
Q_ASSERT
(
m_sameCompositions
.
count
(
clipId
)
>
0
);
if
(
auto
ptr
=
m_parent
.
lock
())
{
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
clipId
));
movedClip
->
setMixDuration
(
final
?
0
:
1
);
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
clipId
);
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
StartRole
,
TimelineModel
::
MixRole
});
if
(
notify
)
{
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
clipId
));
movedClip
->
setMixDuration
(
final
?
0
:
1
);
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
clipId
);
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
StartRole
,
TimelineModel
::
MixRole
});
}
if
(
final
)
{
Mlt
::
Transition
&
transition
=
*
m_sameCompositions
[
clipId
].
get
();
QScopedPointer
<
Mlt
::
Field
>
field
(
m_track
->
field
());
...
...
@@ -1563,6 +1579,35 @@ bool TrackModel::deleteMix(int clipId, bool final)
return
false
;
}
bool
TrackModel
::
createMix
(
MixInfo
info
,
bool
isAudio
)
{
if
(
m_sameCompositions
.
count
(
info
.
secondClipId
)
>
0
)
{
return
false
;
}
if
(
auto
ptr
=
m_parent
.
lock
())
{
// Insert mix transition
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
info
.
secondClipId
));
int
in
=
movedClip
->
getPosition
();
//int out = in + info.firstClipInOut.second - info.secondClipInOut.first;
int
out
=
in
+
movedClip
->
getMixDuration
();
movedClip
->
setMixDuration
(
out
-
in
);
if
(
isAudio
)
{
std
::
shared_ptr
<
Mlt
::
Transition
>
t
(
new
Mlt
::
Transition
(
*
ptr
->
getProfile
(),
"mix"
));
t
->
set_in_and_out
(
in
,
out
);
m_track
->
plant_transition
(
*
t
.
get
(),
0
,
1
);
m_sameCompositions
[
info
.
secondClipId
]
=
t
;
}
else
{
std
::
shared_ptr
<
Mlt
::
Transition
>
t
(
new
Mlt
::
Transition
(
*
ptr
->
getProfile
(),
"luma"
));
t
->
set_in_and_out
(
in
,
out
);
m_track
->
plant_transition
(
*
t
.
get
(),
0
,
1
);
m_sameCompositions
[
info
.
secondClipId
]
=
t
;
}
m_mixList
.
insert
(
info
.
firstClipId
,
info
.
secondClipId
);
return
true
;
}
return
false
;
}
bool
TrackModel
::
createMix
(
std
::
pair
<
int
,
int
>
clipIds
,
std
::
pair
<
int
,
int
>
mixData
)
{
if
(
m_sameCompositions
.
count
(
clipIds
.
second
)
>
0
)
{
...
...
@@ -1571,6 +1616,8 @@ bool TrackModel::createMix(std::pair<int, int> clipIds, std::pair<int, int> mixD
if
(
auto
ptr
=
m_parent
.
lock
())
{
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
clipIds
.
second
));
movedClip
->
setMixDuration
(
mixData
.
second
);
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
clipIds
.
second
);
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
MixRole
});
// Insert mix transition
if
(
isAudioTrack
())
{
std
::
shared_ptr
<
Mlt
::
Transition
>
t
(
new
Mlt
::
Transition
(
*
ptr
->
getProfile
(),
"mix"
));
...
...
@@ -1589,38 +1636,58 @@ bool TrackModel::createMix(std::pair<int, int> clipIds, std::pair<int, int> mixD
return
false
;
}
bool
TrackModel
::
resizeMixStart
(
int
clipId
,
int
position
)
void
TrackModel
::
syncronizeMixes
(
bool
finalMove
)
{
Q_ASSERT
(
m_sameCompositions
.
count
(
clipId
)
>
0
);
if
(
auto
ptr
=
m_parent
.
lock
())
{
Mlt
::
Transition
&
transition
=
*
m_sameCompositions
[
clipId
].
get
();
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
clipId
));
int
in
=
position
;
int
out
=
transition
.
get_out
();
transition
.
set_in_and_out
(
in
,
out
);
int
updatedDuration
=
out
-
in
;
movedClip
->
setMixDuration
(
qMax
(
1
,
updatedDuration
));
return
true
;
QList
<
int
>
toDelete
;
for
(
const
auto
&
n
:
m_sameCompositions
)
{
std
::
cout
<<
"Key:["
<<
n
.
first
<<
"] Value:["
<<
n
.
second
<<
"]
\n
"
;
int
secondClipId
=
n
.
first
;
int
firstClip
=
m_mixList
.
key
(
secondClipId
,
-
1
);
Q_ASSERT
(
firstClip
>
-
1
);
if
(
m_allClips
.
find
(
firstClip
)
==
m_allClips
.
end
()
||
m_allClips
.
find
(
secondClipId
)
==
m_allClips
.
end
())
{
// One of the clip was removed, delete the mix
qDebug
()
<<
"=== CLIPS: "
<<
firstClip
<<
" / "
<<
secondClipId
<<
" ARE MISSING!!!!"
;
Mlt
::
Transition
&
transition
=
*
m_sameCompositions
[
secondClipId
].
get
();
QScopedPointer
<
Mlt
::
Field
>
field
(
m_track
->
field
());
field
->
lock
();
field
->
disconnect_service
(
transition
);
field
->
unlock
();
toDelete
<<
secondClipId
;
m_mixList
.
remove
(
firstClip
);
continue
;
}
// Asjust mix in/out
int
mixIn
=
m_allClips
[
secondClipId
]
->
getPosition
();
int
mixOut
=
m_allClips
[
firstClip
]
->
getPosition
()
+
m_allClips
[
firstClip
]
->
getPlaytime
();
if
(
mixOut
<=
mixIn
)
{
if
(
finalMove
)
{
// Delete mix
mixOut
=
mixIn
;
}
else
{
mixOut
=
mixIn
+
1
;
}
}
Mlt
::
Transition
&
transition
=
*
m_sameCompositions
[
secondClipId
].
get
();
if
(
mixIn
==
mixOut
)
{
QScopedPointer
<
Mlt
::
Field
>
field
(
m_track
->
field
());
field
->
lock
();
field
->
disconnect_service
(
transition
);
field
->
unlock
();
toDelete
<<
secondClipId
;
m_mixList
.
remove
(
firstClip
);
}
else
{
qDebug
()
<<
"=== ADJUSTING MIX FOR CID: "
<<
secondClipId
<<
" TO: "
<<
mixIn
<<
" - "
<<
mixOut
;
transition
.
set_in_and_out
(
mixIn
,
mixOut
);
}
if
(
auto
ptr
=
m_parent
.
lock
())
{
ptr
->
getClipPtr
(
secondClipId
)
->
setMixDuration
(
mixOut
-
mixIn
);
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
secondClipId
);
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
MixRole
});
}
}
return
false
;
}
bool
TrackModel
::
resizeMixEnd
(
int
clipId
,
int
position
)
{
Q_ASSERT
(
m_sameCompositions
.
count
(
clipId
)
>
0
);
if
(
auto
ptr
=
m_parent
.
lock
())
{
Mlt
::
Transition
&
transition
=
*
m_sameCompositions
[
clipId
].
get
();
std
::
shared_ptr
<
ClipModel
>
refClip
(
ptr
->
getClipPtr
(
clipId
));
int
in
=
transition
.
get_in
();
int
out
=
position
;
transition
.
set_in_and_out
(
in
,
out
);
int
updatedDuration
=
out
-
in
;
refClip
->
setMixDuration
(
qMax
(
1
,
updatedDuration
));
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
clipId
);
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
MixRole
});
return
true
;
for
(
int
i
:
toDelete
)
{
m_sameCompositions
.
erase
(
i
);
}
return
false
;
}
int
TrackModel
::
mixCount
()
const
...
...
src/timeline2/model/trackmodel.hpp
View file @
fc5f133a
...
...
@@ -124,13 +124,14 @@ public:
/** @brief Get clip ids and in/out position for mixes in this clip */
std
::
pair
<
MixInfo
,
MixInfo
>
getMixInfo
(
int
cid
)
const
;
/** @brief Delete a mix composition */
bool
deleteMix
(
int
clipId
,
bool
final
);
/** @brief Create a mix composition */
bool
deleteMix
(
int
clipId
,
bool
final
,
bool
notify
=
true
);
/** @brief Create a mix composition
using clip ids
*/
bool
createMix
(
std
::
pair
<
int
,
int
>
clipIds
,
std
::
pair
<
int
,
int
>
mixData
);
/** @brief Resize a mix composition start pos */
bool
resizeMixStart
(
int
clipId
,
int
offset
);
/** @brief Resize a mix composition end pos */
bool
resizeMixEnd
(
int
clipId
,
int
position
);
/** @brief Create a mix composition using mix info */
bool
createMix
(
MixInfo
info
,
bool
isAudio
);
void
syncronizeMixes
(
bool
finalMove
);
/** @brief Switch a clip from one playlist to the other */
bool
switchPlaylist
(
int
clipId
,
int
position
,
int
playlist
);
protected:
/* @brief This will lock the track: it will no longer allow insertion/deletion/resize of items
...
...
src/timeline2/view/qml/Clip.qml
View file @
fc5f133a
...
...
@@ -338,7 +338,13 @@ Rectangle {
anchors.bottom
:
parent
.
bottom
width
:
clipRoot
.
mixDuration
*
timeScale
color
:
'
red
'
opacity
:
0.5
//opacity: 0.5
Text
{
text
:
clipRoot
.
mixDuration
anchors
{
bottom
:
parent
.
bottom
}
}
}
Repeater
{
...
...
src/timeline2/view/qml/Track.qml
View file @
fc5f133a
...
...
@@ -47,7 +47,7 @@ Item{
id
:
trackModel
delegate
:
Item
{
property
var
itemModel
:
model
z
:
model
.
clipType
==
ProducerType
.
Composition
?
5
:
0
z
:
model
.
clipType
==
ProducerType
.
Composition
?
5
:
model
.
mixDuration
>
0
?
2
:
0
Loader
{
id
:
loader
Binding
{
...
...
tests/mixtest.cpp
View file @
fc5f133a
...
...
@@ -123,19 +123,6 @@ TEST_CASE("Simple Mix", "[SameTrackMix]")
state0
();
}
SECTION
(
"Create and delete mix on AV clips"
)
{
state0
();
REQUIRE
(
timeline
->
mixClip
(
cid2
));
state1
();
undoStack
->
undo
();
state0
();
undoStack
->
redo
();
state1
();
undoStack
->
undo
();
state0
();
}
SECTION
(
"Create mix on color clips and move main (right side) clip"
)
{
state0
();
...
...
@@ -163,7 +150,7 @@ TEST_CASE("Simple Mix", "[SameTrackMix]")
state0
();
}
SECTION
(
"Create mix on color clip and left side clip"
)
SECTION
(
"Create mix on color clip and
move
left side clip"
)
{
state0
();
REQUIRE
(
timeline
->
mixClip
(
cid4
));
...
...
@@ -188,6 +175,44 @@ TEST_CASE("Simple Mix", "[SameTrackMix]")
state0
();
}
SECTION
(
"Create mix on color clips and group move"
)
{
state0
();
REQUIRE
(
timeline
->
mixClip
(
cid4
));
state2
();
// Move clip inside mix zone, should resize the mix