Commit fb8a1c11 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Guide categories: use struct for better readability, move catagory deletion in...

Guide categories: use struct for better readability, move catagory deletion in markerlistmodel, add test
parent ab75eda2
Pipeline #258292 failed with stage
in 21 minutes and 38 seconds
......@@ -54,6 +54,32 @@ void MarkerListModel::setup()
connect(this, &MarkerListModel::dataChanged, this, &MarkerListModel::modelChanged);
}
void MarkerListModel::loadCategoriesWithUndo(const QStringList &categories, const QStringList &currentCategories)
{
// Remove all markers of deleted category
Fun local_undo = []() { return true; };
Fun local_redo = []() { return true; };
QList<int> deletedCategories = loadCategories(categories);
while (!deletedCategories.isEmpty()) {
int ix = deletedCategories.takeFirst();
QList<CommentedTime> toDelete = getAllMarkers(ix);
for (CommentedTime c : toDelete) {
removeMarker(c.time(), local_undo, local_redo);
}
}
Fun undo = [this, currentCategories]() {
loadCategories(currentCategories);
return true;
};
Fun redo = [this, categories]() {
loadCategories(categories);
return true;
};
PUSH_FRONT_LAMBDA(local_redo, redo);
PUSH_LAMBDA(local_undo, undo);
pCore->pushUndo(undo, redo, i18n("Update guides categories"));
}
QList<int> MarkerListModel::loadCategories(const QStringList &categories)
{
QList<int> previousCategories = pCore->markerTypes.keys();
......@@ -74,6 +100,17 @@ QList<int> MarkerListModel::loadCategories(const QStringList &categories)
return previousCategories;
}
const QStringList MarkerListModel::categoriesToStringList() const
{
QStringList categories;
QMapIterator<int, Core::MarkerCategory> i(pCore->markerTypes);
while (i.hasNext()) {
i.next();
categories << QString("%1:%2:%3").arg(i.value().displayName, QString::number(i.key()), i.value().color.name());
}
return categories;
}
int MarkerListModel::markerIdAtFrame(int pos) const
{
if (m_markerPositions.contains(pos)) {
......@@ -463,7 +500,7 @@ QVariant MarkerListModel::data(const QModelIndex &index, int role) const
return it->second.time().frames(pCore->getCurrentFps());
case ColorRole:
case Qt::DecorationRole:
return pCore->markerTypes.value(it->second.markerType()).first;
return pCore->markerTypes.value(it->second.markerType()).color;
case TypeRole:
return it->second.markerType();
case IdRole:
......@@ -590,31 +627,6 @@ void MarkerListModel::registerSnapModel(const std::weak_ptr<SnapInterface> &snap
}
}
void MarkerListModel::unregisterSnapModel(const std::weak_ptr<SnapInterface> &snapModel)
{
READ_LOCK();
// make sure ptr is valid
if (auto ptr = snapModel.lock()) {
// we now remove the already existing markers to the snap
QMap<int, int>::const_iterator i = m_markerPositions.constBegin();
while (i != m_markerPositions.constEnd()) {
ptr->removePoint(i.key());
++i;
}
auto copySnaps = m_registeredSnaps;
m_registeredSnaps.clear();
for (auto &c : copySnaps) {
if (c.lock() != ptr) {
m_registeredSnaps.push_back(c);
}
}
} else {
qDebug() << "Error: added snapmodel is null";
Q_ASSERT(false);
}
}
bool MarkerListModel::importFromJson(const QString &data, bool ignoreConflicts, bool pushUndo)
{
Fun undo = []() { return true; };
......
......@@ -122,7 +122,6 @@ public:
Note that no deregistration is necessary, the weak_ptr will be discarded as soon as it becomes invalid.
*/
void registerSnapModel(const std::weak_ptr<SnapInterface> &snapModel);
void unregisterSnapModel(const std::weak_ptr<SnapInterface> &snapModel);
/** @brief Exports the model to json using format above */
QString toJson() const;
......@@ -147,7 +146,10 @@ public:
/** @brief Load the marker categories from a stringList
* @return the list of deleted categories ids (if any)
*/
void loadCategoriesWithUndo(const QStringList &categories, const QStringList &currentCategories);
QList<int> loadCategories(const QStringList &categories);
/** @brief Returns the marker categories in the form of a stringList for saving */
const QStringList categoriesToStringList() const;
// Mandatory overloads
QVariant data(const QModelIndex &index, int role) const override;
......
/*
SPDX-FileCopyrightText: 2014 Jean-Baptiste Mardelle <jb@kdenlive.org>
SPDX-FileCopyrightText: 2022 Jean-Baptiste Mardelle <jb@kdenlive.org>
This file is part of Kdenlive. See www.kdenlive.org.
SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
......
/*
SPDX-FileCopyrightText: 2014 Jean-Baptiste Mardelle <jb@kdenlive.org>
SPDX-FileCopyrightText: 2022 Jean-Baptiste Mardelle <jb@kdenlive.org>
This file is part of Kdenlive. See www.kdenlive.org.
SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
......
......@@ -292,7 +292,12 @@ public:
/** @brief Start / stop audio capture */
void switchCapture();
/** @brief A list of markers type categories {marker type, {color, category name}} */
QMap<int, std::pair<QColor, QString>> markerTypes;
struct MarkerCategory
{
QColor color;
QString displayName;
};
QMap<int, MarkerCategory> markerTypes;
private:
explicit Core(const QString &packageType);
......
/*
SPDX-FileCopyrightText: 2008 Jean-Baptiste Mardelle <jb@kdenlive.org>
SPDX-FileCopyrightText: 2022 Jean-Baptiste Mardelle <jb@kdenlive.org>
SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
......
......@@ -72,6 +72,8 @@ KdenliveDoc::KdenliveDoc(QString projectFolder, QUndoGroup *undoGroup, const QSt
, m_guideModel(new MarkerListModel(m_commandStack, this))
{
connect(m_guideModel.get(), &MarkerListModel::modelChanged, this, &KdenliveDoc::guidesChanged);
connect(m_guideModel.get(), &MarkerListModel::categoriesChanged, this, &KdenliveDoc::saveGuideCategories);
if (parent) {
connect(this, &KdenliveDoc::updateCompositionMode, parent, &MainWindow::slotUpdateCompositeAction);
}
......@@ -131,6 +133,7 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, QDomDocument& newDom, QString projectF
, m_guideModel(new MarkerListModel(m_commandStack, this))
{
connect(m_guideModel.get(), &MarkerListModel::modelChanged, this, &KdenliveDoc::guidesChanged);
connect(m_guideModel.get(), &MarkerListModel::categoriesChanged, this, &KdenliveDoc::saveGuideCategories);
if (parent) {
connect(this, &KdenliveDoc::updateCompositionMode, parent, &MainWindow::slotUpdateCompositeAction);
}
......@@ -373,31 +376,14 @@ const QStringList KdenliveDoc::guidesCategories() const
void KdenliveDoc::updateGuideCategories(const QStringList &categories)
{
Fun local_undo = []() { return true; };
Fun local_redo = []() { return true; };
const QStringList currentCategories = m_documentProperties.value(QStringLiteral("guidesCategories")).split(QLatin1Char('\n'));
QList<int> deletedCategories = m_guideModel->loadCategories(categories);
// Remove all markers of deleted category
while (!deletedCategories.isEmpty()) {
int ix = deletedCategories.takeFirst();
QList<CommentedTime> toDelete = m_guideModel->getAllMarkers(ix);
for (CommentedTime c : toDelete) {
m_guideModel->removeMarker(c.time(), local_undo, local_redo);
}
}
Fun undo = [this, currentCategories]() {
m_guideModel->loadCategories(currentCategories);
m_documentProperties[QStringLiteral("guidesCategories")] = currentCategories.join(QLatin1Char('\n'));
return true;
};
Fun redo = [this, categories]() {
m_guideModel->loadCategories(categories);
m_documentProperties[QStringLiteral("guidesCategories")] = categories.join(QLatin1Char('\n'));
return true;
};
PUSH_FRONT_LAMBDA(local_redo, redo);
PUSH_LAMBDA(local_undo, undo);
pCore->pushUndo(undo, redo, i18n("Update guides categories"));
m_guideModel->loadCategoriesWithUndo(categories, currentCategories);
}
void KdenliveDoc::saveGuideCategories()
{
const QStringList categories = m_guideModel->categoriesToStringList();
m_documentProperties[QStringLiteral("guidesCategories")] = categories.join(QLatin1Char('\n'));
}
int KdenliveDoc::updateClipsCount()
......
......@@ -331,6 +331,8 @@ private slots:
void guidesChanged();
/** @brief Display error message on failed move. */
void slotMoveFinished(KJob *job);
/** @brief Save the project guide categories in the document properties. */
void saveGuideCategories();
signals:
void resetProjectList();
......
......@@ -1337,7 +1337,7 @@ void Monitor::checkOverlay(int pos)
if (mid > -1) {
CommentedTime marker = model->markerById(mid);
overlayText = marker.comment();
color = pCore->markerTypes.value(marker.markerType()).first;
color = pCore->markerTypes.value(marker.markerType()).color;
}
}
m_glMonitor->getControllerProxy()->setMarker(overlayText, color);
......
......@@ -90,8 +90,9 @@ GuideCategories::GuideCategories(KdenliveDoc *doc, QWidget *parent)
item->setIcon(icon);
item->setText(le.text());
item->setData(Qt::UserRole, cb.color());
return true;
}
return true;
return false;
};
QStringList guidesCategories = doc ? doc->guidesCategories() : KdenliveSettings::guidesCategories();
QList<int> existingCategories;
......@@ -128,7 +129,9 @@ GuideCategories::GuideCategories(KdenliveDoc *doc, QWidget *parent)
item->setData(Qt::UserRole + 1, m_categoryIndex++);
guides_list->addItem(item);
guides_list->setCurrentItem(item);
editItem();
if (!editItem()) {
delete item;
}
});
connect(guide_delete, &QPushButton::clicked, this, [=]() {
auto *item = guides_list->currentItem();
......
......@@ -158,12 +158,12 @@ void GuidesList::rebuildCategories()
catGroup = new QButtonGroup(this);
catGroup->setExclusive(false);
QPixmap pixmap(32, 32);
QMapIterator<int, std::pair<QColor, QString>> i(pCore->markerTypes);
QMapIterator<int, Core::MarkerCategory> i(pCore->markerTypes);
while (i.hasNext()) {
i.next();
pixmap.fill(i.value().first);
pixmap.fill(i.value().color);
QIcon colorIcon(pixmap);
QCheckBox *cb = new QCheckBox(i.value().second, this);
QCheckBox *cb = new QCheckBox(i.value().displayName, this);
cb->setProperty("index", i.key());
cb->setIcon(colorIcon);
catGroup->addButton(cb);
......
......@@ -23,15 +23,15 @@ void MarkerCategoryChooser::refresh()
clear();
// Set up guide categories
QPixmap pixmap(32, 32);
QMapIterator<int, std::pair<QColor, QString>> i(pCore->markerTypes);
QMapIterator<int, Core::MarkerCategory> i(pCore->markerTypes);
while (i.hasNext()) {
i.next();
if (m_onlyUsed && m_markerListModel && m_markerListModel->getAllMarkers(i.key()).isEmpty()) {
continue;
}
pixmap.fill(i.value().first);
pixmap.fill(i.value().color);
QIcon colorIcon(pixmap);
addItem(colorIcon, i.value().second, i.key());
addItem(colorIcon, i.value().displayName, i.key());
}
if (count() == 0) {
setEnabled(false);
......
......@@ -34,7 +34,7 @@ int main(int argc, char *argv[])
EffectsRepository::get()->reloadCustom(QFileInfo("../data/effects/audiobalance.xml").absoluteFilePath());
// init markers
for (int i = 0; i < 5; i++) {
pCore->markerTypes.insert(i, {Qt::red, QString("cat %1").arg(i)});
pCore->markerTypes.insert(i, {QColor(30 * i, 30 * i, 30 * i), QString("cat %1").arg(i)});
}
int result = Catch::Session().run(argc, argv);
......
......@@ -37,7 +37,7 @@ void checkMarkerList(const std::shared_ptr<MarkerListModel> &model, const std::v
REQUIRE(qAbs(std::get<0>(m).seconds() - model->data(model->index(i), MarkerListModel::PosRole).toDouble()) < 0.9 / fps);
REQUIRE(std::get<1>(m) == model->data(model->index(i), MarkerListModel::CommentRole).toString());
REQUIRE(std::get<2>(m) == model->data(model->index(i), MarkerListModel::TypeRole).toInt());
REQUIRE(pCore->markerTypes.value(std::get<2>(m)).first == model->data(model->index(i), MarkerListModel::ColorRole).value<QColor>());
REQUIRE(pCore->markerTypes.value(std::get<2>(m)).color == model->data(model->index(i), MarkerListModel::ColorRole).value<QColor>());
// check for marker existence
int frame = std::get<0>(m).frames(fps);
......@@ -79,6 +79,7 @@ TEST_CASE("Marker model", "[MarkerListModel]")
Mock<ProjectManager> pmMock;
When(Method(pmMock, getGuideModel)).AlwaysReturn(model);
When(Method(pmMock, undoStack)).AlwaysReturn(undoStack);
When(Method(pmMock, cacheDir)).AlwaysReturn(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
ProjectManager &mocked = pmMock.get();
......@@ -169,6 +170,38 @@ TEST_CASE("Marker model", "[MarkerListModel]")
checkStates(undoStack, model, {{}, state1, state2, state3, state4, state5, state6, state7, state8, state9, {}}, snaps);
}
SECTION("Test Categories")
{
std::vector<Marker> list;
checkMarkerList(model, list, snaps);
// add markers
list.emplace_back(GenTime(1.3), QLatin1String("test marker"), 3);
REQUIRE(model->addMarker(GenTime(1.3), QLatin1String("test marker"), 3));
REQUIRE(model->addMarker(GenTime(0.3), QLatin1String("test marker2"), 0));
QStringList categories = model->categoriesToStringList();
// We instanciated 5 marker categories in TestMain
Q_ASSERT(categories.count() == 5);
QStringList newCategories = categories;
newCategories.removeFirst();
REQUIRE(model->rowCount() == int(snaps->_snaps().size()));
REQUIRE(model->rowCount() == 2);
model->loadCategoriesWithUndo(newCategories, categories);
REQUIRE(model->rowCount() == int(snaps->_snaps().size()));
REQUIRE(model->rowCount() == 1);
// Reset to default categories
undoStack->undo();
REQUIRE(model->rowCount() == int(snaps->_snaps().size()));
REQUIRE(model->rowCount() == 2);
REQUIRE(model->removeAllMarkers());
checkMarkerList(model, {}, snaps);
}
SECTION("Json identity test")
{
std::vector<Marker> list;
......
Supports Markdown
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