Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Multimedia
Kdenlive
Commits
5c0f642a
Commit
5c0f642a
authored
Sep 23, 2020
by
Jean-Baptiste Mardelle
Browse files
Easy selection and deletion of clip mix
parent
7f3f3365
Changes
12
Hide whitespace changes
Inline
Side-by-side
src/timeline2/model/clipmodel.cpp
View file @
5c0f642a
...
...
@@ -47,6 +47,7 @@ ClipModel::ClipModel(const std::shared_ptr<TimelineModel> &parent, std::shared_p
,
m_positionOffset
(
0
)
,
m_subPlaylistIndex
(
0
)
,
m_mixDuration
(
0
)
,
m_mixCutPos
(
0
)
{
m_producer
->
set
(
"kdenlive:id"
,
binClipId
.
toUtf8
().
constData
());
m_producer
->
set
(
"_kdenlive_cid"
,
m_id
);
...
...
@@ -651,6 +652,18 @@ void ClipModel::setPosition(int pos)
m_clipMarkerModel
->
updateSnapModelPos
(
pos
);
}
void
ClipModel
::
setMixDuration
(
int
mix
,
int
cutOffset
)
{
if
(
mix
==
0
)
{
// Deleting a mix
m_mixCutPos
=
0
;
}
else
{
// Creating a new mix
m_mixCutPos
=
cutOffset
;
}
m_mixDuration
=
mix
;
}
void
ClipModel
::
setMixDuration
(
int
mix
)
{
m_mixDuration
=
mix
;
...
...
@@ -661,6 +674,11 @@ int ClipModel::getMixDuration() const
return
m_mixDuration
;
}
int
ClipModel
::
getMixCutPosition
()
const
{
return
m_mixCutPos
;
}
void
ClipModel
::
setInOut
(
int
in
,
int
out
)
{
MoveableItem
::
setInOut
(
in
,
out
);
...
...
src/timeline2/model/clipmodel.hpp
View file @
5c0f642a
...
...
@@ -101,8 +101,10 @@ public:
void
setFakeTrackId
(
int
fid
);
int
getFakePosition
()
const
;
void
setFakePosition
(
int
fid
);
void
setMixDuration
(
int
mix
,
int
offset
);
void
setMixDuration
(
int
mix
);
int
getMixDuration
()
const
;
int
getMixCutPosition
()
const
;
void
setGrab
(
bool
grab
)
override
;
void
setSelected
(
bool
sel
)
override
;
...
...
@@ -253,6 +255,8 @@ protected:
// Duration of a same track mix.
int
m_mixDuration
;
// Position of the original cut, relative to mix right side
int
m_mixCutPos
;
};
#endif
src/timeline2/model/timelineitemmodel.cpp
View file @
5c0f642a
...
...
@@ -198,6 +198,7 @@ QHash<int, QByteArray> TimelineItemModel::roleNames() const
roles
[
FakePositionRole
]
=
"fakePosition"
;
roles
[
StartRole
]
=
"start"
;
roles
[
MixRole
]
=
"mixDuration"
;
roles
[
MixCutRole
]
=
"mixCut"
;
roles
[
DurationRole
]
=
"duration"
;
roles
[
MaxDurationRole
]
=
"maxDuration"
;
roles
[
MarkersRole
]
=
"markers"
;
...
...
@@ -336,6 +337,8 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int role) const
return
clip
->
fadeOut
();
case
MixRole
:
return
clip
->
getMixDuration
();
case
MixCutRole
:
return
clip
->
getMixCutPosition
();
case
ReloadThumbRole
:
return
clip
->
forceThumbReload
;
case
PositionOffsetRole
:
...
...
src/timeline2/model/timelinemodel.cpp
View file @
5c0f642a
...
...
@@ -2642,6 +2642,20 @@ int TimelineModel::requestItemSpeedChange(int itemId, int size, bool right, int
return
proposed_size
>
0
?
proposed_size
:
size
;
}
bool
TimelineModel
::
removeMix
(
int
cid
)
{
Fun
undo
=
[]()
{
return
true
;
};
Fun
redo
=
[]()
{
return
true
;
};
int
tid
=
getItemTrackId
(
cid
);
MixInfo
mixData
=
getTrackById_const
(
tid
)
->
getMixInfo
(
cid
).
first
;
bool
res
=
getTrackById
(
tid
)
->
requestRemoveMix
({
mixData
.
firstClipId
,
mixData
.
secondClipId
},
undo
,
redo
);
if
(
res
)
{
PUSH_UNDO
(
undo
,
redo
,
i18n
(
"Remove mix"
));
}
else
{
pCore
->
displayMessage
(
i18n
(
"Removing mix failed"
),
InformationMessage
,
500
);
}
return
res
;
}
int
TimelineModel
::
requestItemResize
(
int
itemId
,
int
size
,
bool
right
,
bool
logUndo
,
int
snapDistance
,
bool
allowSingleResize
)
{
...
...
@@ -2660,6 +2674,7 @@ int TimelineModel::requestItemResize(int itemId, int size, bool right, bool logU
Fun
undo
=
[]()
{
return
true
;
};
Fun
redo
=
[]()
{
return
true
;
};
Fun
sync_mix
=
[]()
{
return
true
;
};
Fun
adjust_mix
=
[]()
{
return
true
;
};
Fun
sync_end_mix
=
[]()
{
return
true
;
};
Fun
sync_end_mix_undo
=
[]()
{
return
true
;
};
PUSH_LAMBDA
(
sync_mix
,
undo
);
...
...
@@ -2673,24 +2688,32 @@ int TimelineModel::requestItemResize(int itemId, int size, bool right, bool logU
if
(
getTrackById_const
(
tid
)
->
hasEndMix
(
itemId
))
{
tracksWithMixes
<<
tid
;
std
::
pair
<
MixInfo
,
MixInfo
>
mixData
=
getTrackById_const
(
tid
)
->
getMixInfo
(
itemId
);
if
(
in
+
size
<=
mixData
.
second
.
secondClipInOut
.
first
)
{
if
(
in
+
size
<=
mixData
.
second
.
secondClipInOut
.
first
+
m_allClips
[
mixData
.
second
.
secondClipId
]
->
getMixDuration
()
-
m_allClips
[
mixData
.
second
.
secondClipId
]
->
getMixCutPosition
()
)
{
// Clip resized outside of mix zone, mix will be deleted
bool
switchPlaylist
=
getTrackById_const
(
tid
)
->
hasEndMix
(
mixData
.
second
.
secondClipId
)
==
false
&&
m_allClips
[
mixData
.
second
.
secondClipId
]
->
getSubPlaylistIndex
()
==
1
;
Fun
sync_mix_undo
=
[
this
,
tid
,
mixData
]()
{
getTrackById_const
(
tid
)
->
createMix
(
mixData
.
second
,
getTrackById_const
(
tid
)
->
isAudioTrack
());
getTrackById_const
(
tid
)
->
syncronizeMixes
(
true
);
bool
res
=
removeMix
(
mixData
.
second
.
secondClipId
);
if
(
res
)
{
return
m_allClips
[
itemId
]
->
getPlaytime
();
}
return
-
1
;
}
else
{
// Mix was resized, update cut position
int
currentMixDuration
=
m_allClips
[
mixData
.
second
.
secondClipId
]
->
getMixDuration
();
int
currentMixCut
=
m_allClips
[
mixData
.
second
.
secondClipId
]
->
getMixCutPosition
();
adjust_mix
=
[
this
,
tid
,
mixData
,
currentMixCut
,
itemId
]()
{
MixInfo
secondMixData
=
getTrackById_const
(
tid
)
->
getMixInfo
(
itemId
).
second
;
int
offset
=
mixData
.
second
.
firstClipInOut
.
second
-
secondMixData
.
firstClipInOut
.
second
;
m_allClips
[
secondMixData
.
secondClipId
]
->
setMixDuration
(
secondMixData
.
firstClipInOut
.
second
-
secondMixData
.
secondClipInOut
.
first
,
currentMixCut
-
offset
);
QModelIndex
ix
=
makeClipIndexFromID
(
secondMixData
.
secondClipId
);
emit
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
MixRole
,
TimelineModel
::
MixCutRole
});
return
true
;
};
// Move second clip to first playlist again
if
(
switchPlaylist
)
{
sync_end_mix
=
[
this
,
tid
,
mixData
]()
{
return
getTrackById_const
(
tid
)
->
switchPlaylist
(
mixData
.
second
.
secondClipId
,
mixData
.
second
.
secondClipInOut
.
first
,
1
,
0
);
};
sync_end_mix_undo
=
[
this
,
tid
,
mixData
]()
{
return
getTrackById_const
(
tid
)
->
switchPlaylist
(
mixData
.
second
.
secondClipId
,
m_allClips
[
mixData
.
second
.
secondClipId
]
->
getPosition
(),
0
,
1
);
};
}
PUSH_LAMBDA
(
sync_mix_undo
,
undo
);
Fun
adjust_mix_undo
=
[
this
,
mixData
,
currentMixCut
,
currentMixDuration
]()
{
m_allClips
[
mixData
.
second
.
secondClipId
]
->
setMixDuration
(
currentMixDuration
,
currentMixCut
);
QModelIndex
ix
=
makeClipIndexFromID
(
mixData
.
second
.
secondClipId
);
emit
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
MixRole
,
TimelineModel
::
MixCutRole
});
return
true
;
};
PUSH_LAMBDA
(
adjust_mix_undo
,
undo
);
}
}
}
else
if
(
getTrackById_const
(
tid
)
->
hasStartMix
(
itemId
))
{
...
...
@@ -2839,7 +2862,9 @@ int TimelineModel::requestItemResize(int itemId, int size, bool right, bool logU
if
(
isClip
(
itemId
))
{
sync_end_mix
();
sync_mix
();
adjust_mix
();
PUSH_LAMBDA
(
sync_end_mix
,
redo
);
PUSH_LAMBDA
(
adjust_mix
,
redo
);
PUSH_LAMBDA
(
sync_mix
,
redo
);
PUSH_LAMBDA
(
undo
,
sync_end_mix_undo
);
PUSH_UNDO
(
sync_end_mix_undo
,
redo
,
i18n
(
"Resize clip"
))
...
...
@@ -2867,19 +2892,21 @@ bool TimelineModel::requestItemResize(int itemId, int size, bool right, bool log
if
(
getTrackById_const
(
tid
)
->
hasEndMix
(
itemId
))
{
hasMix
=
true
;
/*std::pair<MixInfo, MixInfo> mixData = getTrackById_const(tid)->getMixInfo(itemId);
int mixDuration = mixData.second.firstClipInOut.second - (mixData.second.secondClipInOut.second - size);
m_allClips[mixData.second.secondClipId]->setMixDuration(mixDuration);
int mixDuration = mixData.second.firstClipInOut.first + size - mixData.second.secondClipInOut.second;
qDebug() << "- - - -\nGOT RIGHT NEW MIX DURATION: "<<mixDuration<<"\nCURRENT CUT POS: "<<m_allClips[mixData.second.secondClipId]->getMixCutPosition()<<", CURRENT MIX DURATION: "<<m_allClips[mixData.second.secondClipId]->getMixDuration();
int mixCut = m_allClips[mixData.second.secondClipId]->getMixCutPosition() + m_allClips[mixData.second.secondClipId]->getMixDuration() - mixDuration;
m_allClips[mixData.second.secondClipId]->setMixDuration(mixDuration, mixCut);
QModelIndex ix = makeClipIndexFromID(mixData.second.secondClipId);
emit dataChanged(ix, ix, {TimelineModel::MixRole});*/
emit dataChanged(ix, ix, {TimelineModel::MixRole
,TimelineModel::MixCutRole
});*/
}
}
else
if
(
getTrackById_const
(
tid
)
->
hasStartMix
(
itemId
))
{
hasMix
=
true
;
std
::
pair
<
MixInfo
,
MixInfo
>
mixData
=
getTrackById_const
(
tid
)
->
getMixInfo
(
itemId
);
// We have a mix at clip start
int
mixDuration
=
mixData
.
first
.
firstClipInOut
.
second
-
(
mixData
.
first
.
secondClipInOut
.
second
-
size
);
m_allClips
[
itemId
]
->
setMixDuration
(
qMax
(
1
,
mixDuration
));
m_allClips
[
itemId
]
->
setMixDuration
(
qMax
(
1
,
mixDuration
)
,
m_allClips
[
itemId
]
->
getMixCutPosition
()
);
QModelIndex
ix
=
makeClipIndexFromID
(
itemId
);
emit
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
MixRole
});
emit
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
MixRole
,
TimelineModel
::
MixCutRole
});
}
}
}
else
{
...
...
@@ -4498,6 +4525,10 @@ bool TimelineModel::requestClearSelection(bool onDeletion)
{
QWriteLocker
locker
(
&
m_lock
);
TRACE
();
if
(
m_selectedMix
>
-
1
)
{
m_selectedMix
=
-
1
;
emit
selectedMixChanged
();
}
if
(
m_currentSelection
==
-
1
)
{
TRACE_RES
(
true
);
return
true
;
...
...
@@ -4536,6 +4567,14 @@ bool TimelineModel::requestClearSelection(bool onDeletion)
TRACE_RES
(
true
);
return
true
;
}
void
TimelineModel
::
requestMixSelection
(
int
cid
)
{
requestClearSelection
();
m_selectedMix
=
cid
;
emit
selectedMixChanged
();
}
void
TimelineModel
::
requestClearSelection
(
bool
onDeletion
,
Fun
&
undo
,
Fun
&
redo
)
{
Fun
operation
=
[
this
,
onDeletion
]()
{
...
...
src/timeline2/model/timelinemodel.hpp
View file @
5c0f642a
...
...
@@ -117,7 +117,8 @@ public:
IsProxyRole
,
/// clip only
ServiceRole
,
/// clip only
StartRole
,
/// clip only
MixRole
,
/// clip only
MixRole
,
/// clip only, the duration of the mix
MixCutRole
,
/// The original cut position for the mix
BinIdRole
,
/// clip only
TrackIdRole
,
FakeTrackIdRole
,
...
...
@@ -420,6 +421,7 @@ public:
/** @brief Plant a same track composition in track tid
*/
void
plantMix
(
int
tid
,
Mlt
::
Transition
&
t
);
bool
removeMix
(
int
cid
);
protected:
/* @brief Creates a new clip instance without inserting it.
...
...
@@ -671,6 +673,11 @@ public:
Q_INVOKABLE
bool
requestClearSelection
(
bool
onDeletion
=
false
);
// same function with undo/redo accumulation
void
requestClearSelection
(
bool
onDeletion
,
Fun
&
undo
,
Fun
&
redo
);
/** @brief Select a given mix in timeline
@param cid clip id
*/
Q_INVOKABLE
void
requestMixSelection
(
int
cid
);
/** @brief Add the given item to the selection
If @param clear is true, the selection is first cleared
...
...
@@ -791,6 +798,8 @@ signals:
/* @brief Signal sent whenever the selection changes */
void
selectionChanged
();
/* @brief Signal sent whenever the selected mix changes */
void
selectedMixChanged
();
/* @brief Signal when a track is deleted so we make sure we don't store its id */
void
checkTrackDeletion
(
int
tid
);
/* @brief Emitted when a clip is deleted to check if it was not used in timeline qml */
...
...
@@ -834,6 +843,7 @@ protected:
// item, or, finally, the id of a group which is not of type selection. The last case happens when the selection exactly matches an existing group
// (in that case we cannot further group it because the selection would have only one child, which is prohibited by design)
int
m_currentSelection
=
-
1
;
int
m_selectedMix
=
-
1
;
// The index of the temporary overlay track in tractor, or -1 if not connected
int
m_overlayTrackCount
;
...
...
src/timeline2/model/trackmodel.cpp
View file @
5c0f642a
...
...
@@ -628,7 +628,8 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
int
blank
=
-
1
;
int
other_blank_end
=
getBlankEnd
(
clip_position
,
(
target_track
+
1
)
%
2
);
if
(
right
)
{
if
(
target_clip
==
m_playlists
[
target_track
].
count
()
-
1
&&
(
hasMix
||
other_blank_end
>=
out
))
{
// clip is last, it can always be extended
if
(
target_clip
==
m_playlists
[
target_track
].
count
()
-
1
&&
(
hasMix
||
other_blank_end
>=
out
))
{
// clip is last, it can always be extended
return
[
this
,
target_clip
,
target_track
,
in
,
out
,
update_snaps
,
clipId
]()
{
if
(
isLocked
())
return
false
;
// color, image and title clips can have unlimited resize
...
...
@@ -652,7 +653,6 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
return
err
==
0
;
};
}
blank
=
target_clip
+
1
;
}
else
{
if
(
target_clip
==
0
)
{
...
...
@@ -697,7 +697,6 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
return
err
==
0
;
};
}
else
{
qDebug
()
<<
"==== NOPE ABORT RESIZE"
;
}
}
return
[]()
{
return
false
;
};
...
...
@@ -1431,6 +1430,107 @@ bool TrackModel::isAvailable(int position, int duration, int playlist)
return
m_playlists
[
playlist
].
is_blank
(
start_clip
);
}
bool
TrackModel
::
requestRemoveMix
(
std
::
pair
<
int
,
int
>
clipIds
,
Fun
&
undo
,
Fun
&
redo
)
{
int
mixDuration
;
int
mixCutPos
;
int
endPos
;
int
firstInPos
;
int
secondInPos
;
int
mixPosition
;
int
src_track
=
0
;
bool
clipHasEndMix
=
false
;
if
(
auto
ptr
=
m_parent
.
lock
())
{
// The clip that will be moved to playlist 1
std
::
shared_ptr
<
ClipModel
>
secondClip
(
ptr
->
getClipPtr
(
clipIds
.
second
));
mixDuration
=
secondClip
->
getMixDuration
();
mixCutPos
=
secondClip
->
getMixCutPosition
();
mixPosition
=
secondClip
->
getPosition
();
secondInPos
=
mixPosition
+
mixDuration
-
mixCutPos
;
firstInPos
=
ptr
->
getClipPtr
(
clipIds
.
first
)
->
getPosition
();
endPos
=
mixPosition
+
secondClip
->
getPlaytime
();
clipHasEndMix
=
hasEndMix
(
clipIds
.
second
);
src_track
=
secondClip
->
getSubPlaylistIndex
();
}
else
{
return
false
;
}
bool
result
=
false
;
Fun
local_undo
=
[]()
{
return
true
;
};
Fun
local_redo
=
[]()
{
return
true
;
};
if
(
auto
ptr
=
m_parent
.
lock
())
{
// Resize main clip
result
=
ptr
->
getClipPtr
(
clipIds
.
second
)
->
requestResize
(
endPos
-
secondInPos
,
false
,
local_undo
,
local_redo
,
true
,
clipHasEndMix
);
// Resize first part clip
result
=
result
&&
ptr
->
getClipPtr
(
clipIds
.
first
)
->
requestResize
(
secondInPos
-
firstInPos
,
true
,
local_undo
,
local_redo
,
true
,
true
);
}
if
(
result
)
{
PUSH_LAMBDA
(
local_redo
,
redo
);
Fun
replay
=
[
this
,
clipIds
,
secondInPos
,
clipHasEndMix
,
src_track
]()
{
if
(
src_track
==
1
&&
!
clipHasEndMix
)
{
// Revert clip to playlist 0 since it has no mix
switchPlaylist
(
clipIds
.
second
,
secondInPos
,
1
,
0
);
}
// Delete transition
Mlt
::
Transition
&
transition
=
*
m_sameCompositions
[
clipIds
.
second
].
get
();
QScopedPointer
<
Mlt
::
Field
>
field
(
m_track
->
field
());
field
->
lock
();
field
->
disconnect_service
(
transition
);
field
->
unlock
();
m_sameCompositions
.
erase
(
clipIds
.
second
);
m_mixList
.
remove
(
clipIds
.
first
);
if
(
auto
ptr
=
m_parent
.
lock
())
{
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
clipIds
.
second
));
movedClip
->
setMixDuration
(
0
);
/*QModelIndex ix = ptr->makeClipIndexFromID(clipIds.first);
emit ptr->dataChanged(ix, ix, {TimelineModel::DurationRole});*/
QModelIndex
ix2
=
ptr
->
makeClipIndexFromID
(
clipIds
.
second
);
emit
ptr
->
dataChanged
(
ix2
,
ix2
,
{
TimelineModel
::
MixRole
,
TimelineModel
::
MixCutRole
});
}
return
true
;
};
replay
();
Fun
reverse
=
[
this
,
clipIds
,
mixDuration
,
mixPosition
,
mixCutPos
,
secondInPos
,
clipHasEndMix
,
src_track
]()
{
// First restore correct playlist
if
(
src_track
==
1
&&
!
clipHasEndMix
)
{
// Revert clip to playlist 0 since it has no mix
switchPlaylist
(
clipIds
.
second
,
secondInPos
,
0
,
1
);
}
// Build mix
if
(
auto
ptr
=
m_parent
.
lock
())
{
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
clipIds
.
second
));
movedClip
->
setMixDuration
(
mixDuration
,
mixCutPos
);
// Insert mix transition
if
(
isAudioTrack
())
{
std
::
shared_ptr
<
Mlt
::
Transition
>
t
(
new
Mlt
::
Transition
(
*
ptr
->
getProfile
(),
"mix"
));
t
->
set_in_and_out
(
mixPosition
,
mixPosition
+
mixDuration
);
if
(
src_track
==
0
)
{
t
->
set
(
"reverse"
,
1
);
}
m_track
->
plant_transition
(
*
t
.
get
(),
0
,
1
);
m_sameCompositions
[
clipIds
.
second
]
=
t
;
}
else
{
std
::
shared_ptr
<
Mlt
::
Transition
>
t
(
new
Mlt
::
Transition
(
*
ptr
->
getProfile
(),
"luma"
));
t
->
set_in_and_out
(
mixPosition
,
mixPosition
+
mixDuration
);
if
(
src_track
==
0
)
{
t
->
set
(
"reverse"
,
1
);
}
m_track
->
plant_transition
(
*
t
.
get
(),
0
,
1
);
m_sameCompositions
[
clipIds
.
second
]
=
t
;
}
m_mixList
.
insert
(
clipIds
.
first
,
clipIds
.
second
);
QModelIndex
ix2
=
ptr
->
makeClipIndexFromID
(
clipIds
.
second
);
emit
ptr
->
dataChanged
(
ix2
,
ix2
,
{
TimelineModel
::
MixRole
,
TimelineModel
::
MixCutRole
});
}
return
true
;
};
PUSH_LAMBDA
(
replay
,
redo
);
PUSH_LAMBDA
(
reverse
,
undo
);
PUSH_LAMBDA
(
local_undo
,
undo
);
return
true
;
}
return
false
;
}
bool
TrackModel
::
requestClipMix
(
std
::
pair
<
int
,
int
>
clipIds
,
int
mixDuration
,
bool
updateView
,
bool
finalMove
,
Fun
&
undo
,
Fun
&
redo
,
bool
groupMove
)
{
QWriteLocker
locker
(
&
m_lock
);
...
...
@@ -1442,6 +1542,7 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
int
firstClipDuration
;
int
source_track
;
int
mixPosition
;
int
secondClipCut
=
0
;
int
dest_track
=
1
;
bool
remixPlaylists
=
false
;
bool
clipHasEndMix
=
false
;
...
...
@@ -1473,6 +1574,7 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
}
}
mixDuration
=
qMin
(
mixDuration
,
maxPos
-
mixPosition
);
secondClipCut
=
maxPos
-
secondClipPos
;
}
else
{
// Error, timeline unavailable
return
false
;
...
...
@@ -1498,7 +1600,6 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
}
rearrange_playlists
=
[
this
,
rearrangedPlaylists
]()
{
bool
result
=
true
;
// First, remove all clips on playlist 0
QMapIterator
<
int
,
int
>
i
(
rearrangedPlaylists
);
while
(
i
.
hasNext
())
{
...
...
@@ -1548,7 +1649,6 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
}
else
return
false
;
};
rearrange_playlists_undo
=
[
this
,
rearrangedPlaylists
]()
{
bool
result
=
true
;
// First, remove all clips on playlist 1
QMapIterator
<
int
,
int
>
i
(
rearrangedPlaylists
);
while
(
i
.
hasNext
())
{
...
...
@@ -1599,10 +1699,10 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
};
}
// Create mix compositing
Fun
build_mix
=
[
clipIds
,
mixPosition
,
mixDuration
,
dest_track
,
this
]()
{
Fun
build_mix
=
[
clipIds
,
mixPosition
,
mixDuration
,
dest_track
,
secondClipCut
,
this
]()
{
if
(
auto
ptr
=
m_parent
.
lock
())
{
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
clipIds
.
second
));
movedClip
->
setMixDuration
(
mixDuration
);
movedClip
->
setMixDuration
(
mixDuration
,
secondClipCut
);
// Insert mix transition
if
(
isAudioTrack
())
{
std
::
shared_ptr
<
Mlt
::
Transition
>
t
(
new
Mlt
::
Transition
(
*
ptr
->
getProfile
(),
"mix"
));
...
...
@@ -1632,7 +1732,7 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
clipIds
.
second
));
movedClip
->
setMixDuration
(
0
);
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
clipIds
.
second
);
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
StartRole
,
TimelineModel
::
MixRole
});
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
StartRole
,
TimelineModel
::
MixRole
,
TimelineModel
::
MixCutRole
});
QScopedPointer
<
Mlt
::
Field
>
field
(
m_track
->
field
());
field
->
lock
();
field
->
disconnect_service
(
transition
);
...
...
@@ -1661,7 +1761,7 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
result
=
ptr
->
getClipPtr
(
clipIds
.
second
)
->
requestResize
(
secondClipPos
+
secondClipDuration
-
mixPosition
,
false
,
local_undo
,
local_redo
,
clipHasEndMix
);
result
=
result
&&
ptr
->
getClipPtr
(
clipIds
.
first
)
->
requestResize
(
mixPosition
+
mixDuration
-
firstClipPos
,
true
,
local_undo
,
local_redo
,
false
);
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
clipIds
.
second
);
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
StartRole
,
TimelineModel
::
MixRole
});
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
StartRole
,
TimelineModel
::
MixRole
,
TimelineModel
::
MixCutRole
});
}
}
return
result
;
...
...
@@ -1747,7 +1847,7 @@ bool TrackModel::deleteMix(int clipId, bool final, bool 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
});
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
StartRole
,
TimelineModel
::
MixRole
,
TimelineModel
::
MixCutRole
});
}
if
(
final
)
{
Mlt
::
Transition
&
transition
=
*
m_sameCompositions
[
clipId
].
get
();
...
...
@@ -1811,7 +1911,7 @@ bool TrackModel::createMix(std::pair<int, int> clipIds, std::pair<int, int> mixD
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
});
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
MixRole
,
TimelineModel
::
MixCutRole
});
bool
reverse
=
movedClip
->
getSubPlaylistIndex
()
==
0
;
// Insert mix transition
if
(
isAudioTrack
())
{
...
...
@@ -1882,7 +1982,7 @@ void TrackModel::syncronizeMixes(bool finalMove)
if
(
auto
ptr
=
m_parent
.
lock
())
{
ptr
->
getClipPtr
(
secondClipId
)
->
setMixDuration
(
mixOut
-
mixIn
);
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
secondClipId
);
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
MixRole
});
emit
ptr
->
dataChanged
(
ix
,
ix
,
{
TimelineModel
::
MixRole
,
TimelineModel
::
MixCutRole
});
}
}
for
(
int
i
:
toDelete
)
{
...
...
src/timeline2/model/trackmodel.hpp
View file @
5c0f642a
...
...
@@ -119,6 +119,8 @@ public:
// TODO make protected
QVariant
getProperty
(
const
QString
&
name
)
const
;
void
setProperty
(
const
QString
&
name
,
const
QString
&
value
);
/** @brief Remove a composition between 2 same track clips */
bool
requestRemoveMix
(
std
::
pair
<
int
,
int
>
clipIds
,
Fun
&
undo
,
Fun
&
redo
);
/** @brief Create a composition between 2 same track clips */
bool
requestClipMix
(
std
::
pair
<
int
,
int
>
clipIds
,
int
mixDuration
,
bool
updateView
,
bool
finalMove
,
Fun
&
undo
,
Fun
&
redo
,
bool
groupMove
);
/** @brief Get clip ids and in/out position for mixes in this clip */
...
...
src/timeline2/view/qml/Clip.qml
View file @
5c0f642a
...
...
@@ -34,6 +34,7 @@ Rectangle {
property
bool
isProxy
:
false
property
int
modelStart
property
int
mixDuration
:
0
property
int
mixCut
:
0
property
real
scrollX
:
0
property
int
inPoint
:
0
property
int
outPoint
:
0
...
...
@@ -353,7 +354,29 @@ Rectangle {
anchors.bottom
:
parent
.
bottom
text
:
clipRoot
.
mixDuration
}
opacity
:
0.7
opacity
:
mixArea
.
containsMouse
||
root
.
selectedMix
==
clipRoot
.
clipId
?
1
:
0.5
border.color
:
root
.
selectedMix
==
clipRoot
.
clipId
?
root
.
selectionColor
:
"
transparent
"
border.width
:
2
MouseArea
{
// Mix click mouse area
id
:
mixArea
anchors.fill
:
parent
hoverEnabled
:
true
cursorShape
:
Qt
.
PointingHandCursor
onPressed
:
{
console
.
log
(
'
MIX CLICKED:
'
,
clipRoot
.
clipId
)
controller
.
requestMixSelection
(
clipRoot
.
clipId
);
}
}
Rectangle
{
id
:
mixCutPos
anchors.right
:
parent
.
right
anchors.rightMargin
:
clipRoot
.
mixCut
*
clipRoot
.
timeScale
anchors.top
:
parent
.
top
anchors.bottom
:
parent
.
bottom
width
:
2
color
:
"
#000
"
}
MouseArea
{
// Left resize handle
id
:
trimInMixArea
...
...
@@ -376,6 +399,7 @@ Rectangle {
mixOut
.
opacity
=
1
anchors
.
left
=
undefined
parent
.
anchors
.
right
=
undefined
mixCutPos
.
anchors
.
right
=
undefined
}
onReleased
:
{
controller
.
resizeStartMix
(
clipRoot
.
clipId
,
Math
.
round
(
Math
.
max
(
0
,
x
)
/
clipRoot
.
timeScale
))
...
...
@@ -386,6 +410,7 @@ Rectangle {
anchors
.
left
=
parent
.
left
parent
.
anchors
.
right
=
mixContainer
.
right
mixOut
.
opacity
=
0.5
mixCutPos
.
anchors
.
right
=
mixCutPos
.
parent
.
right
}
onPositionChanged
:
{
if
(
mouse
.
buttons
===
Qt
.
LeftButton
)
{
...
...
@@ -1053,7 +1078,7 @@ Rectangle {
hoverEnabled
:
true
cursorShape
:
Qt
.
PointingHandCursor
drag.target
:
fadeInMouseArea
drag.minimumX
:
-
Math
.
ceil
(
width
/
2
)
drag.minimumX
:
-
2
*
root
.
baseUnit
drag.maximumX
:
container
.
width
-
width
/
2
drag.axis
:
Drag
.
XAxis
drag.smoothed
:
false
...
...
@@ -1074,13 +1099,22 @@ Rectangle {
onReleased
:
{
root
.
autoScrolling
=
timeline
.
autoScroll
fadeInTriangle
.
opacity
=
0.4
timeline
.
adjustFade
(
clipRoot
.
clipId
,
'
fadein
'
,
clipRoot
.
fadeIn
,
startFadeIn
)
if
(
startFadeIn
==
0
&&
x
<
0
)
{
timeline
.
mixClip
(
clipRoot
.
clipId
)
}
else
{
timeline
.
adjustFade
(
clipRoot
.
clipId
,
'
fadein
'
,
clipRoot
.
fadeIn
,
startFadeIn
)
}
bubbleHelp
.
hide
()
anchors
.
left
=
container
.
left
}
onPositionChanged
:
{
if
(
mouse
.
buttons
===
Qt
.
LeftButton
)
{
var
delta
=
Math
.
round
((
x
+
width
/
2
)
/
timeScale
)
if
(
delta
<
root
.
baseUnit
)
{
fadeInControl
.
radius
=
0
}
else
{
fadeInControl
.
radius
=
fadeInControl
.
width
/
2
}
var
duration
=
Math
.
max
(
0
,
delta
)
duration
=
Math
.
min
(
duration
,
clipRoot
.
clipDuration
-
1
)
if
(
duration
!=
clipRoot
.
fadeIn
)
{
...
...
src/timeline2/view/qml/Track.qml
View file @
5c0f642a
...
...
@@ -47,6 +47,7 @@ Item{
id
:
trackModel
delegate
:
Item
{
property
var
itemModel
:
model
property
bool
clipItem
:
isClip
(
model
.
clipType
)
z
:
model
.
clipType
==
ProducerType
.
Composition
?
5
:
model
.
mixDuration
>
0
?
model
.
start
/
25
:
0
Loader
{
id
:
loader
...
...
@@ -60,19 +61,25 @@ Item{
target
:
loader
.
item
property
:
"
fakeTid
"
value
:
model
.
fakeTrackId
when
:
loader
.
status
==
Loader
.
Ready
&&
loader
.
item
&&
isClip
(
model
.
clipType
)