Commit c74e7908 authored by Gustavo Pichorim Boiko's avatar Gustavo Pichorim Boiko Committed by Gustavo Pichorim Boiko

Summary: Make it possible to drag multiple clips to the timeline

Test Plan:
- Add clips to the project bin
- Select two or more clips
- Drag them to the timeline
- Check that the "Insert Clips" entry got added to the undo history
- Undo the change
- Redo the change

Reviewers: alcinos, mardelle

Reviewed By: alcinos, mardelle

Differential Revision: https://phabricator.kde.org/D8928
parent 93616d2e
......@@ -60,6 +60,30 @@ bool TimelineFunctions::copyClip(std::shared_ptr<TimelineItemModel> timeline, in
return res;
}
bool TimelineFunctions::requestMultipleClipsInsertion(std::shared_ptr<TimelineItemModel> timeline, const QStringList &binIds, int trackId, int position, QList<int> &clipIds, bool logUndo, bool refreshView)
{
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
for (const QString &binId : binIds) {
int clipId;
if (timeline->requestClipInsertion(binId, trackId, position, clipId, logUndo, refreshView, undo, redo)) {
clipIds.append(clipId);
position += timeline->getItemPlaytime(clipId);
} else {
undo();
clipIds.clear();
return false;
}
}
if (logUndo) {
pCore->pushUndo(undo, redo, i18n("Insert Clips"));
}
return true;
}
bool TimelineFunctions::processClipCut(std::shared_ptr<TimelineItemModel> timeline, int clipId, int position, int &newId, Fun &undo, Fun &redo)
{
int trackId = timeline->getClipTrackId(clipId);
......
......@@ -52,6 +52,16 @@ struct TimelineFunctions
/* @brief Makes a perfect copy of a given clip, but do not insert it */
static bool copyClip(std::shared_ptr<TimelineItemModel> timeline, int clipId, int &newId, PlaylistState::ClipState state, Fun &undo, Fun &redo);
/* @brief Request the addition of multiple clips to the timeline
* If the addition of any of the clips fails, the entire operation is undone.
* @returns true on success, false otherwise.
* @param binIds the list of bin ids to be inserted
* @param trackId the track where the insertion should happen
* @param position the position at which the clips should be inserted
* @param clipIds a return parameter with the ids assigned to the clips if success, empty otherwise
*/
static bool requestMultipleClipsInsertion(std::shared_ptr<TimelineItemModel> timeline, const QStringList &binIds, int trackId, int position, QList<int> &clipIds, bool logUndo, bool refreshView);
static int requestSpacerStartOperation(std::shared_ptr<TimelineItemModel> timeline, int trackId, int position);
static bool requestSpacerEndOperation(std::shared_ptr<TimelineItemModel> timeline, int clipId, int startPosition, int endPosition);
static bool extractZone(std::shared_ptr<TimelineItemModel> timeline, QVector<int> tracks, QPoint zone, bool liftOnly);
......
......@@ -665,6 +665,7 @@ bool TimelineModel::requestItemDeletion(int clipId, Fun &undo, Fun &redo)
return requestGroupDeletion(clipId, undo, redo);
}
return requestClipDeletion(clipId, undo, redo);
}
bool TimelineModel::requestItemDeletion(int itemId, bool logUndo)
......
......@@ -299,11 +299,14 @@ public:
Q_INVOKABLE int suggestCompositionMove(int compoId, int trackId, int position, int snapDistance = -1);
/* @brief Request clip insertion at given position. This action is undoable
Returns true on success. If it fails, nothing is modified. @param
binClipId id of the clip in the bin @param track Id of the track where to
insert @param Requested position @param ID return parameter of the id of
the inserted clip @param logUndo if set to false, no undo object is
stored */
Returns true on success. If it fails, nothing is modified.
@param binClipId id of the clip in the bin
@param track Id of the track where to insert
@param position Requested position
@param ID return parameter of the id of the inserted clip
@param logUndo if set to false, no undo object is stored
@param refreshView whether the view should be refreshed
*/
bool requestClipInsertion(const QString &binClipId, int trackId, int position, int &id, bool logUndo = true, bool refreshView = false);
/* Same function, but accumulates undo and redo*/
bool requestClipInsertion(const QString &binClipId, int trackId, int position, int &id, bool logUndo, bool refreshView, Fun &undo, Fun &redo);
......
......@@ -101,6 +101,13 @@ Rectangle {
return col
}
function clearDropData() {
clipBeingDroppedId = -1
droppedPosition = -1
droppedTrack = -1
scrollTimer.running = false
}
property int headerWidth: timeline.headerWidth()
property int activeTool: 0
property int projectMargin: 200
......@@ -190,10 +197,7 @@ Rectangle {
if (clipBeingDroppedId != -1) {
controller.requestItemDeletion(clipBeingDroppedId, false)
}
clipBeingDroppedId = -1
droppedPosition = -1
droppedTrack = -1
scrollTimer.running = false
clearDropData()
}
onDropped: {
if (clipBeingDroppedId != -1) {
......@@ -203,13 +207,32 @@ Rectangle {
controller.requestItemDeletion(clipBeingDroppedId, false)
timeline.insertComposition(track, frame, clipBeingDroppedData, true)
}
clipBeingDroppedId = -1
droppedPosition = -1
droppedTrack = -1
scrollTimer.running = false
clearDropData()
}
}
DropArea { //Drop area for bin/clips
/** @brief local helper function to handle the insertion of multiple dragged items */
function insertAndMaybeGroup(track, frame, droppedData) {
var binIds = droppedData.split(";")
if (binIds.length == 0) {
return -1
}
var id = -1
if (binIds.length == 1) {
id = timeline.insertClip(timeline.activeTrack, frame, clipBeingDroppedData, false, true)
} else {
var ids = timeline.insertClips(timeline.activeTrack, frame, binIds, false, true)
// if the clip insertion succeeded, request the clips to be grouped
if (ids.length > 0) {
timeline.groupClips(ids)
id = ids[0]
}
}
return id
}
width: root.width - headerWidth
height: root.height - ruler.height
y: ruler.height
......@@ -226,7 +249,7 @@ Rectangle {
//drag.acceptProposedAction()
clipBeingDroppedData = drag.getDataAsString('kdenlive/producerslist')
console.log('dropped data: ', clipBeingDroppedData)
clipBeingDroppedId = timeline.insertClip(timeline.activeTrack, frame, clipBeingDroppedData, false, true)
clipBeingDroppedId = insertAndMaybeGroup(timeline.activeTrack, frame, clipBeingDroppedData)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
} else {
drag.accepted = false
......@@ -237,10 +260,7 @@ Rectangle {
if (clipBeingDroppedId != -1) {
controller.requestItemDeletion(clipBeingDroppedId, false)
}
clipBeingDroppedId = -1
droppedPosition = -1
droppedTrack = -1
scrollTimer.running = false
clearDropData()
}
onPositionChanged: {
if (clipBeingMovedId == -1) {
......@@ -253,7 +273,7 @@ Rectangle {
controller.requestClipMove(clipBeingDroppedId, timeline.activeTrack, frame, true, false, false)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
} else {
clipBeingDroppedId = timeline.insertClip(timeline.activeTrack, frame, drag.getDataAsString('kdenlive/producerslist'), false, true)
clipBeingDroppedId = insertAndMaybeGroup(timeline.activeTrack, frame, drag.getDataAsString('kdenlive/producerslist'), false, true)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
}
}
......@@ -263,14 +283,20 @@ Rectangle {
if (clipBeingDroppedId != -1) {
var frame = controller.getClipPosition(clipBeingDroppedId)
var track = controller.getClipTrackId(clipBeingDroppedId)
// we simulate insertion at the final position so that stored undo has correct value
/* We simulate insertion at the final position so that stored undo has correct value
* NOTE: even if dropping multiple clips, requesting the deletion of the first one is
* enough as internally it will request the group deletion
*/
controller.requestItemDeletion(clipBeingDroppedId, false)
timeline.insertClip(track, frame, clipBeingDroppedData, true, true)
var binIds = clipBeingDroppedData.split(";")
if (binIds.length == 1) {
timeline.insertClip(track, frame, clipBeingDroppedData, true, true)
} else {
timeline.insertClips(track, frame, binIds, true, true)
}
}
clipBeingDroppedId = -1
droppedPosition = -1
droppedTrack = -1
scrollTimer.running = false
clearDropData()
}
}
OLD.Menu {
......
......@@ -377,6 +377,20 @@ int TimelineController::insertClip(int tid, int position, const QString &data_st
return id;
}
QList<int> TimelineController::insertClips(int tid, int position, const QStringList &binIds, bool logUndo, bool refreshView)
{
QList<int> clipIds;
if (tid == -1) {
tid = m_activeTrack;
}
if (position == -1) {
position = timelinePosition();
}
TimelineFunctions::requestMultipleClipsInsertion(m_model, binIds, tid, position, clipIds, logUndo, refreshView);
// we don't need to check the return value of the above function, in case of failure it will return an empty list of ids.
return clipIds;
}
int TimelineController::insertComposition(int tid, int position, const QString &transitionId, bool logUndo)
{
int id;
......@@ -1500,3 +1514,14 @@ void TimelineController::resetTrackHeight()
QModelIndex modelEnd = m_model->makeTrackIndexFromID(m_model->getTrackIndexFromPosition(tracksCount - 1));
m_model->dataChanged(modelStart, modelEnd, {TimelineModel::HeightRole});
}
int TimelineController::groupClips(const QList<int> &clipIds)
{
std::unordered_set<int> theSet(clipIds.begin(), clipIds.end());
return m_model->requestClipsGroup(theSet, false, GroupType::Selection);
}
bool TimelineController::ungroupClips(int clipId)
{
return m_model->requestClipUngroup(clipId);
}
......@@ -118,6 +118,24 @@ public:
@return the id of the inserted clip
*/
Q_INVOKABLE int insertClip(int tid, int position, const QString &xml, bool logUndo, bool refreshView);
/* @brief Request inserting multiple clips into the timeline (dragged from bin or monitor)
* @param tid is the destination track
* @param position is the timeline position
* @param binIds the IDs of the bins being dropped
* @param logUndo if set to false, no undo object is stored
* @return the ids of the inserted clips
*/
Q_INVOKABLE QList<int> insertClips(int tid, int position, const QStringList &binIds, bool logUndo, bool refreshView);
/* @brief Request the grouping of the given clips
* @param clipIds the ids to be grouped
* @return the group id or -1 in case of faiure
*/
Q_INVOKABLE int groupClips(const QList<int> &clipIds);
/* @brief Request the ungrouping of clips
* @param clipId the id of a clip belonging to the group
* @return true in case of success, false otherwise
*/
Q_INVOKABLE bool ungroupClips(int clipId);
Q_INVOKABLE void copyItem();
Q_INVOKABLE bool pasteItem(int clipId = -1, int tid = -1, int position = -1);
/* @brief Request inserting a new composition in timeline (dragged from compositions list)
......
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