Skip to content

Commit

Permalink
Change border color for grouped clips
Browse files Browse the repository at this point in the history
Grouped clips have a quite boarder when they are selected.
  • Loading branch information
bmatherly committed Jan 15, 2024
1 parent 3c653c8 commit 121c01b
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 131 deletions.
155 changes: 51 additions & 104 deletions src/commands/timelinecommands.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023 Meltytech, LLC
* Copyright (c) 2013-2024 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -326,72 +326,44 @@ GroupCommand::GroupCommand(MultitrackModel &model, QUndoCommand *parent)
{
}

void GroupCommand::addToGroup(Mlt::Producer &clip)
void GroupCommand::addToGroup(int trackIndex, int clipIndex)
{
QUuid id = MLT.ensureHasUuid(clip);
m_uuids.insert(id);
m_clips.append(clip);
if (clip.property_exists(kShotcutGroupProperty)) {
m_prevGroups.insert(id, clip.get_int(kShotcutGroupProperty));
auto clipInfo = m_model.getClipInfo(trackIndex, clipIndex);
if (clipInfo && clipInfo->cut && !clipInfo->cut->is_blank()) {
ClipPosition position(trackIndex, clipIndex);
m_clips.append(position);
if (clipInfo->cut->property_exists(kShotcutGroupProperty)) {
m_prevGroups.insert(position, clipInfo->cut->get_int(kShotcutGroupProperty));
}
}
}

void GroupCommand::redo()
{
int groupNumber = getUniqueGroupNumber(m_model);
if (m_clips.size() > 0) {
// Use the original producer objects the first time.
setText(QObject::tr("Group %n clips", nullptr, m_clips.size()));
for (auto &clip : m_clips) {
clip.set(kShotcutGroupProperty, groupNumber);
}
m_clips.clear();
} else {
QSet<QUuid> tmpUuids = m_uuids;
for (int trackIndex = 0; trackIndex < m_model.trackList().size()
&& tmpUuids.size() > 0; trackIndex++) {
int i = m_model.trackList().at(trackIndex).mlt_index;
QScopedPointer<Mlt::Producer> track(m_model.tractor()->track(i));
if (track) {
Mlt::Playlist playlist(*track);
for (int clipIndex = 0; clipIndex < playlist.count() && tmpUuids.size() > 0; clipIndex++) {
QScopedPointer<Mlt::ClipInfo> info(playlist.clip_info(clipIndex));
if (info && info->cut) {
QUuid id = MLT.uuid(*info->cut);
if (!id.isNull() && tmpUuids.contains(id)) {
info->cut->set(kShotcutGroupProperty, groupNumber);
tmpUuids.remove(id);
}
}
}
}
setText(QObject::tr("Group %n clips", nullptr, m_clips.size()));
for (auto &clip : m_clips) {
auto clipInfo = m_model.getClipInfo(clip.trackIndex, clip.clipIndex);
if (clipInfo && clipInfo->cut) {
clipInfo->cut->set(kShotcutGroupProperty, groupNumber);
QModelIndex modelIndex = m_model.index(clip.clipIndex, 0, m_model.index(clip.trackIndex));
emit m_model.dataChanged(modelIndex, modelIndex, QVector<int>() << MultitrackModel::GroupRole);
}
}
}

void GroupCommand::undo()
{
QSet<QUuid> tmpUuids = m_uuids;
for (int trackIndex = 0; trackIndex < m_model.trackList().size()
&& tmpUuids.size() > 0; trackIndex++) {
int i = m_model.trackList().at(trackIndex).mlt_index;
QScopedPointer<Mlt::Producer> track(m_model.tractor()->track(i));
if (track) {
Mlt::Playlist playlist(*track);
for (int clipIndex = 0; clipIndex < playlist.count() && tmpUuids.size() > 0; clipIndex++) {
QScopedPointer<Mlt::ClipInfo> info(playlist.clip_info(clipIndex));
if (info && info->cut) {
QUuid id = MLT.uuid(*info->cut);
if (!id.isNull() && m_uuids.contains(id)) {
if (m_prevGroups.contains(id)) {
info->cut->set(kShotcutGroupProperty, m_prevGroups[id]);
} else {
info->cut->Mlt::Properties::clear(kShotcutGroupProperty);
}
tmpUuids.remove(id);
}
}
for (auto &clip : m_clips) {
auto clipInfo = m_model.getClipInfo(clip.trackIndex, clip.clipIndex);
if (clipInfo && clipInfo->cut) {
if (m_prevGroups.contains(clip)) {
clipInfo->cut->set(kShotcutGroupProperty, m_prevGroups[clip]);
} else {
clipInfo->cut->Mlt::Properties::clear(kShotcutGroupProperty);
}
QModelIndex modelIndex = m_model.index(clip.clipIndex, 0, m_model.index(clip.trackIndex));
emit m_model.dataChanged(modelIndex, modelIndex, QVector<int>() << MultitrackModel::GroupRole);
}
}
}
Expand All @@ -402,67 +374,38 @@ UngroupCommand::UngroupCommand(MultitrackModel &model, QUndoCommand *parent)
{
}

void UngroupCommand::removeFromGroup(Mlt::Producer &clip)
void UngroupCommand::removeFromGroup(int trackIndex, int clipIndex)
{
if (clip.property_exists(kShotcutGroupProperty)) {
QUuid id = MLT.ensureHasUuid(clip);
m_uuids.insert(id);
m_clips.append(clip);
m_prevGroups.insert(id, clip.get_int(kShotcutGroupProperty));
auto clipInfo = m_model.getClipInfo(trackIndex, clipIndex);
if (clipInfo && clipInfo->cut) {
ClipPosition position(trackIndex, clipIndex);
if (clipInfo->cut->property_exists(kShotcutGroupProperty)) {
m_prevGroups.insert(position, clipInfo->cut->get_int(kShotcutGroupProperty));
}
}
}

void UngroupCommand::redo()
{
if (m_clips.size() > 0) {
// Use the original producer objects the first time.
setText(QObject::tr("Ungroup %n clips", nullptr, m_clips.size()));
for (auto &clip : m_clips) {
clip.Mlt::Properties::clear(kShotcutGroupProperty);
}
m_clips.clear();
} else {
QSet<QUuid> tmpUuids = m_uuids;
for (int trackIndex = 0; trackIndex < m_model.trackList().size()
&& tmpUuids.size() > 0; trackIndex++) {
int i = m_model.trackList().at(trackIndex).mlt_index;
QScopedPointer<Mlt::Producer> track(m_model.tractor()->track(i));
if (track) {
Mlt::Playlist playlist(*track);
for (int clipIndex = 0; clipIndex < playlist.count() && tmpUuids.size() > 0; clipIndex++) {
QScopedPointer<Mlt::ClipInfo> info(playlist.clip_info(clipIndex));
if (info && info->cut) {
QUuid id = MLT.uuid(*info->cut);
if (!id.isNull() && m_uuids.contains(id)) {
info->cut->Mlt::Properties::clear(kShotcutGroupProperty);
tmpUuids.remove(id);
}
}
}
}
setText(QObject::tr("Ungroup %n clips", nullptr, m_prevGroups.size()));
for (auto &clip : m_prevGroups.keys()) {
auto clipInfo = m_model.getClipInfo(clip.trackIndex, clip.clipIndex);
if (clipInfo && clipInfo->cut) {
clipInfo->cut->Mlt::Properties::clear(kShotcutGroupProperty);
QModelIndex modelIndex = m_model.index(clip.clipIndex, 0, m_model.index(clip.trackIndex));
emit m_model.dataChanged(modelIndex, modelIndex, QVector<int>() << MultitrackModel::GroupRole);
}
}
}

void UngroupCommand::undo()
{
auto tmpPrevGroups = m_prevGroups;
for (int trackIndex = 0; trackIndex < m_model.trackList().size()
&& tmpPrevGroups.size() > 0; trackIndex++) {
int i = m_model.trackList().at(trackIndex).mlt_index;
QScopedPointer<Mlt::Producer> track(m_model.tractor()->track(i));
if (track) {
Mlt::Playlist playlist(*track);
for (int clipIndex = 0; clipIndex < playlist.count() && tmpPrevGroups.size() > 0; clipIndex++) {
QScopedPointer<Mlt::ClipInfo> info(playlist.clip_info(clipIndex));
if (info && info->cut) {
QUuid id = MLT.uuid(*info->cut);
if (!id.isNull() && m_prevGroups.contains(id)) {
info->cut->set(kShotcutGroupProperty, m_prevGroups[id]);
tmpPrevGroups.remove(id);
}
}
}
for (auto &clip : m_prevGroups.keys()) {
auto clipInfo = m_model.getClipInfo(clip.trackIndex, clip.clipIndex);
if (clipInfo && clipInfo->cut) {
clipInfo->cut->set(kShotcutGroupProperty, m_prevGroups[clip]);
QModelIndex modelIndex = m_model.index(clip.clipIndex, 0, m_model.index(clip.trackIndex));
emit m_model.dataChanged(modelIndex, modelIndex, QVector<int>() << MultitrackModel::GroupRole);
}
}
}
Expand Down Expand Up @@ -725,6 +668,8 @@ void MoveClipCommand::redo()
if (clipInfo && clipInfo->cut) {
clipInfo->cut->set(kShotcutGroupProperty, clip.get(kGroupProperty));
}
QModelIndex modelIndex = m_model.index(clipIndex, 0, m_model.index(toTrack));
emit m_model.dataChanged(modelIndex, modelIndex, QVector<int>() << MultitrackModel::GroupRole);
}
}
}
Expand Down Expand Up @@ -1863,7 +1808,8 @@ void DetachAudioCommand::redo()
}
m_undoHelper.recordAfterState();
QModelIndex modelIndex = m_model.makeIndex(m_trackIndex, m_clipIndex);
emit m_model.dataChanged(modelIndex, modelIndex, QVector<int>() << MultitrackModel::AudioIndexRole);
emit m_model.dataChanged(modelIndex, modelIndex,
QVector<int>() << MultitrackModel::AudioIndexRole << MultitrackModel::GroupRole);
}
}
}
Expand All @@ -1882,7 +1828,8 @@ void DetachAudioCommand::undo()
m_model.overwrite(m_trackIndex, originalClip, m_position, true);
QModelIndex modelIndex = m_model.makeIndex(m_trackIndex, m_clipIndex);
emit m_model.dataChanged(modelIndex, modelIndex,
QVector<int>() << MultitrackModel::AudioIndexRole << MultitrackModel::AudioLevelsRole);
QVector<int>() << MultitrackModel::AudioIndexRole << MultitrackModel::AudioLevelsRole <<
MultitrackModel::GroupRole);
}

ReplaceCommand::ReplaceCommand(MultitrackModel &model, int trackIndex, int clipIndex,
Expand Down
35 changes: 26 additions & 9 deletions src/commands/timelinecommands.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023 Meltytech, LLC
* Copyright (c) 2013-2024 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -46,6 +46,26 @@ enum {
UndoIdMoveClip
};

struct ClipPosition {
ClipPosition(int track, int clip)
{
trackIndex = track;
clipIndex = clip;
}

bool operator < (const ClipPosition &rhs) const
{
if (trackIndex == rhs.trackIndex) {
return clipIndex < rhs.clipIndex;
} else {
return trackIndex < rhs.trackIndex;
}
}

int trackIndex;
int clipIndex;
};

class AppendCommand : public QUndoCommand
{
public:
Expand Down Expand Up @@ -136,28 +156,25 @@ class GroupCommand : public QUndoCommand
{
public:
GroupCommand(MultitrackModel &model, QUndoCommand *parent = 0);
void addToGroup(Mlt::Producer &clip);
void addToGroup(int trackIndex, int clipIndex);
void redo();
void undo();
private:
MultitrackModel &m_model;
QList<Mlt::Producer> m_clips;
QSet<QUuid> m_uuids;
QMap<QUuid, int> m_prevGroups;
QList<ClipPosition> m_clips;
QMap<ClipPosition, int> m_prevGroups;
};

class UngroupCommand : public QUndoCommand
{
public:
UngroupCommand(MultitrackModel &model, QUndoCommand *parent = 0);
void removeFromGroup(Mlt::Producer &clip);
void removeFromGroup(int trackIndex, int clipIndex);
void redo();
void undo();
private:
MultitrackModel &m_model;
QList<Mlt::Producer> m_clips;
QSet<QUuid> m_uuids;
QMap<QUuid, int> m_prevGroups;
QMap<ClipPosition, int> m_prevGroups;
};

class NameTrackCommand : public QUndoCommand
Expand Down
14 changes: 4 additions & 10 deletions src/docks/timelinedock.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023 Meltytech, LLC
* Copyright (c) 2013-2024 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -1355,20 +1355,14 @@ void TimelineDock::setupActions()
// First clip is in a group. Need to ungroup
Timeline::UngroupCommand *ungroupCommand = new Timeline::UngroupCommand(m_model);
foreach (auto point, selectedClips) {
auto clipInfo = m_model.getClipInfo(point.y(), point.x());
if (!clipInfo->cut->is_blank()) {
ungroupCommand->removeFromGroup(*clipInfo->cut);
}
ungroupCommand->removeFromGroup(point.y(), point.x());
}
MAIN.undoStack()->push(ungroupCommand);
} else {
// First clip is not in a group - ungroup
// First clip is not in a group - group
Timeline::GroupCommand *groupCommand = new Timeline::GroupCommand(m_model);
foreach (auto point, selectedClips) {
auto clipInfo = m_model.getClipInfo(point.y(), point.x());
if (!clipInfo->cut->is_blank()) {
groupCommand->addToGroup(*clipInfo->cut);
}
groupCommand->addToGroup(point.y(), point.x());
}
MAIN.undoStack()->push(groupCommand);
}
Expand Down
8 changes: 7 additions & 1 deletion src/models/multitrackmodel.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023 Meltytech, LLC
* Copyright (c) 2013-2024 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -199,6 +199,11 @@ QVariant MultitrackModel::data(const QModelIndex &index, int role) const
return isFiltered(info->producer);
case AudioIndexRole:
return QString::fromLatin1(info->producer->get("audio_index"));
case GroupRole:
if (info->cut->property_exists(kShotcutGroupProperty))
return info->cut->get_int(kShotcutGroupProperty);
else
return -1;
default:
break;
}
Expand Down Expand Up @@ -325,6 +330,7 @@ QHash<int, QByteArray> MultitrackModel::roleNames() const
roles[IsTopAudioRole] = "isTopAudio";
roles[IsBottomAudioRole] = "isBottomAudio";
roles[AudioIndexRole] = "audioIndex";
roles[GroupRole] = "group";
return roles;
}

Expand Down
5 changes: 3 additions & 2 deletions src/models/multitrackmodel.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023 Meltytech, LLC
* Copyright (c) 2013-2024 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -77,7 +77,8 @@ class MultitrackModel : public QAbstractItemModel
IsBottomVideoRole,/// track only
IsTopAudioRole, /// track only
IsBottomAudioRole,/// track only
AudioIndexRole /// clip only
AudioIndexRole, /// clip only
GroupRole, /// clip only
};

explicit MultitrackModel(QObject *parent = 0);
Expand Down
5 changes: 3 additions & 2 deletions src/qml/views/timeline/Clip.qml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023 Meltytech, LLC
* Copyright (c) 2013-2024 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -42,6 +42,7 @@ Rectangle {
property string hash: ''
property double speed: 1
property string audioIndex: ''
property int group: -1
property bool isTrackMute: false
property bool elided: (width < 15) || (x + width < tracksFlickable.contentX) || (x > tracksFlickable.contentX + tracksFlickable.width) || (y + height < 0) || (y > tracksFlickable.contentY + tracksFlickable.contentHeight)

Expand Down Expand Up @@ -98,7 +99,7 @@ Rectangle {
return 'image://thumbnail/' + hash + '/' + mltService + '/' + clipResource + '#' + time;
}

border.color: (selected || Drag.active || trackIndex != originalTrackIndex) ? 'red' : 'black'
border.color: (selected || Drag.active || trackIndex != originalTrackIndex) ? group < 0 ? 'red' : 'white' : 'black'
border.width: isBlank && !selected ? 0 : 1
clip: true
Drag.active: mouseArea.drag.active
Expand Down
Loading

0 comments on commit 121c01b

Please sign in to comment.