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

When deleting a category, allow reassigning its markers/guides to another...

When deleting a category, allow reassigning its markers/guides to another category instead of deleting them
parent c33efca7
Pipeline #261091 failed with stage
in 8 minutes and 51 seconds
......@@ -5483,21 +5483,16 @@ int Bin::getAllClipMarkers(int category) const
return markersCount;
}
void Bin::removeMarkerCategories(QList<int> toRemove)
void Bin::removeMarkerCategories(QList<int> toRemove, const QMap<int, int> remapCategories)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool found = false;
QList<std::shared_ptr<ProjectClip>> clipList = m_itemModel->getRootFolder()->childClips();
for (const std::shared_ptr<ProjectClip> &clip : qAsConst(clipList)) {
for (int i : toRemove) {
QList<CommentedTime> toDelete = clip->getMarkerModel()->getAllMarkers(i);
if (!found && toDelete.count() > 0) {
found = true;
}
for (CommentedTime c : toDelete) {
clip->getMarkerModel()->removeMarker(c.time(), undo, redo);
}
bool res = clip->removeMarkerCategories(toRemove, remapCategories, undo, redo);
if (!found && res) {
found = true;
}
}
if (found) {
......
......@@ -247,7 +247,7 @@ public:
int getAllClipMarkers(int category) const;
/** @brief Remove all clip markers using a category */
void removeMarkerCategories(QList<int> toRemove);
void removeMarkerCategories(QList<int> toRemove, const QMap<int, int> remapCategories);
/** @brief Returns a list of selected clip ids.
* @param allowSubClips: if true, will include subclip ids in the form: "master clip id/in/out"
......
......@@ -54,7 +54,7 @@ void MarkerListModel::setup()
connect(this, &MarkerListModel::dataChanged, this, &MarkerListModel::modelChanged);
}
void MarkerListModel::loadCategoriesWithUndo(const QStringList &categories, const QStringList &currentCategories)
void MarkerListModel::loadCategoriesWithUndo(const QStringList &categories, const QStringList &currentCategories, const QMap<int, int> remapCategories)
{
// Remove all markers of deleted category
Fun local_undo = []() { return true; };
......@@ -63,8 +63,15 @@ void MarkerListModel::loadCategoriesWithUndo(const QStringList &categories, cons
while (!deletedCategories.isEmpty()) {
int ix = deletedCategories.takeFirst();
QList<CommentedTime> toDelete = getAllMarkers(ix);
for (CommentedTime c : toDelete) {
removeMarker(c.time(), local_undo, local_redo);
if (remapCategories.contains(ix)) {
int newType = remapCategories.value(ix);
for (CommentedTime c : toDelete) {
addMarker(c.time(), c.comment(), newType, local_undo, local_redo);
}
} else {
for (CommentedTime c : toDelete) {
removeMarker(c.time(), local_undo, local_redo);
}
}
}
Fun undo = [this, currentCategories]() {
......
......@@ -31,6 +31,8 @@ class MarkerListModel : public QAbstractListModel
{
Q_OBJECT
friend class ClipController;
public:
/** @brief Construct a marker list bound to the bin clip with given id */
explicit MarkerListModel(QString clipId, std::weak_ptr<DocUndoStack> undo_stack, QObject *parent = nullptr);
......@@ -154,7 +156,7 @@ 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);
void loadCategoriesWithUndo(const QStringList &categories, const QStringList &currentCategories, const QMap<int, int> remapCategories = {});
QList<int> loadCategories(const QStringList &categories);
/** @brief Returns the marker categories in the form of a stringList for saving */
const QStringList categoriesToStringList() const;
......
......@@ -378,7 +378,7 @@ const QStringList KdenliveDoc::guidesCategories() const
return m_documentProperties.value(QStringLiteral("guidesCategories")).split(QLatin1Char('\n'));
}
void KdenliveDoc::updateGuideCategories(const QStringList &categories)
void KdenliveDoc::updateGuideCategories(const QStringList &categories, const QMap<int, int> remapCategories)
{
const QStringList currentCategories = m_documentProperties.value(QStringLiteral("guidesCategories")).split(QLatin1Char('\n'));
// Check if a guide category was removed
......@@ -395,9 +395,9 @@ void KdenliveDoc::updateGuideCategories(const QStringList &categories)
}
if (!currentIndexes.isEmpty()) {
// A marker category was removed, delete all Bin clip markers using it
pCore->bin()->removeMarkerCategories(currentIndexes);
pCore->bin()->removeMarkerCategories(currentIndexes, remapCategories);
}
m_guideModel->loadCategoriesWithUndo(categories, currentCategories);
m_guideModel->loadCategoriesWithUndo(categories, currentCategories, remapCategories);
}
void KdenliveDoc::saveGuideCategories()
......
......@@ -218,7 +218,7 @@ public:
/** @brief Returns the guides categories for the project in format {name:index:#color} */
const QStringList guidesCategories() const;
/** @brief Set the guides categories for the project in format {name:index:#color} */
void updateGuideCategories(const QStringList &categories);
void updateGuideCategories(const QStringList &categories, const QMap<int, int> remapCategories = {});
/** @brief Setup a filter to visible guides */
void setGuidesFilter(const QList<int> filter);
......
......@@ -2011,7 +2011,7 @@ void MainWindow::slotEditProjectSettings(int ix)
}
const QStringList guidesCat = w->guidesCategories();
if (guidesCat != project->guidesCategories()) {
project->updateGuideCategories(guidesCat);
project->updateGuideCategories(guidesCat, w->remapGuidesCategories());
}
if (KdenliveSettings::videothumbnails() != w->enableVideoThumbs()) {
slotSwitchVideoThumbs();
......
......@@ -1087,3 +1087,30 @@ std::shared_ptr<MarkerSortModel> ClipController::getFilteredMarkerModel() const
{
return m_markerFilterModel;
}
bool ClipController::removeMarkerCategories(QList<int> toRemove, const QMap<int, int> remapCategories, Fun &undo, Fun &redo)
{
bool found = false;
if (m_markerModel->rowCount() == 0) {
return false;
}
for (int i : toRemove) {
QList<CommentedTime> toDelete = m_markerModel->getAllMarkers(i);
if (!found && toDelete.count() > 0) {
found = true;
}
if (remapCategories.contains(i)) {
// Move markers to another category
int newType = remapCategories.value(i);
for (CommentedTime c : toDelete) {
m_markerModel->addMarker(c.time(), c.comment(), newType, undo, redo);
}
} else {
// Delete markers
for (CommentedTime c : toDelete) {
m_markerModel->removeMarker(c.time(), undo, redo);
}
}
}
return found;
}
......@@ -9,6 +9,7 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include "definitions.h"
#include "undohelper.hpp"
#include <QDateTime>
#include <QDir>
......@@ -211,6 +212,8 @@ public:
const QString getOriginalUrl();
/** @brief Returns true if we are using a proxy for this clip. */
bool hasProxy() const;
/** @brief Delete or re-assign all markers in a category. */
bool removeMarkerCategories(QList<int> toRemove, const QMap<int, int> remapCategories, Fun &undo, Fun &redo);
protected:
/** @brief Mutex to protect the producer properties on read/write */
......
......@@ -21,6 +21,7 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QDialog>
#include <QDialogButtonBox>
#include <QPainter>
#include <QRadioButton>
GuideCategories::GuideCategories(KdenliveDoc *doc, QWidget *parent)
: QWidget(parent)
......@@ -39,46 +40,46 @@ GuideCategories::GuideCategories(KdenliveDoc *doc, QWidget *parent)
// Edit an existing tag
QDialog d2(this);
d2.setWindowTitle(i18n("Edit Guide Category"));
QDialogButtonBox *buttonBox2 = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
QDialogButtonBox buttonBox2(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, &d2);
auto *l2 = new QVBoxLayout;
d2.setLayout(l2);
auto *l3 = new QHBoxLayout;
KColorCombo cb;
KColorCombo cb(&d2);
l3->addWidget(&cb);
KLineEdit le;
KLineEdit le(&d2);
le.setText(item->text());
QColor originalColor(item->data(Qt::UserRole).toString());
categoriesColor.removeAll(originalColor);
cb.setColor(originalColor);
l3->addWidget(&le);
l2->addLayout(l3);
KMessageWidget mw;
KMessageWidget mw(&d2);
mw.setText(i18n("This color is already used in another category"));
mw.setMessageType(KMessageWidget::Warning);
mw.setCloseButtonVisible(false);
mw.hide();
l2->addWidget(&mw);
l2->addWidget(buttonBox2);
d2.connect(buttonBox2, &QDialogButtonBox::rejected, &d2, &QDialog::reject);
d2.connect(buttonBox2, &QDialogButtonBox::accepted, &d2, &QDialog::accept);
connect(&le, &KLineEdit::textChanged, &d2, [buttonBox2, &le, &cb, &categoriesColor]() {
l2->addWidget(&buttonBox2);
d2.connect(&buttonBox2, &QDialogButtonBox::rejected, &d2, &QDialog::reject);
d2.connect(&buttonBox2, &QDialogButtonBox::accepted, &d2, &QDialog::accept);
connect(&le, &KLineEdit::textChanged, &d2, [&buttonBox2, &le, &cb, &categoriesColor]() {
if (le.text().isEmpty()) {
buttonBox2->button(QDialogButtonBox::Ok)->setEnabled(false);
buttonBox2.button(QDialogButtonBox::Ok)->setEnabled(false);
} else {
buttonBox2->button(QDialogButtonBox::Ok)->setEnabled(!categoriesColor.contains(cb.color()));
buttonBox2.button(QDialogButtonBox::Ok)->setEnabled(!categoriesColor.contains(cb.color()));
}
});
connect(&cb, &KColorCombo::activated, &d2, [buttonBox2, &categoriesColor, &mw, &le](const QColor &selectedColor) {
connect(&cb, &KColorCombo::activated, &d2, [&buttonBox2, &categoriesColor, &mw, &le](const QColor &selectedColor) {
if (categoriesColor.contains(selectedColor)) {
buttonBox2->button(QDialogButtonBox::Ok)->setEnabled(false);
buttonBox2.button(QDialogButtonBox::Ok)->setEnabled(false);
mw.animatedShow();
} else {
buttonBox2->button(QDialogButtonBox::Ok)->setEnabled(!le.text().isEmpty());
buttonBox2.button(QDialogButtonBox::Ok)->setEnabled(!le.text().isEmpty());
mw.animatedHide();
}
});
if (categoriesColor.contains(cb.color())) {
buttonBox2->button(QDialogButtonBox::Ok)->setEnabled(false);
buttonBox2.button(QDialogButtonBox::Ok)->setEnabled(false);
mw.animatedShow();
}
le.setFocus();
......@@ -143,7 +144,36 @@ GuideCategories::GuideCategories(KdenliveDoc *doc, QWidget *parent)
int count = item->data(Qt::UserRole + 2).toInt();
if (count > 0) {
// There are existing guides in this category, warn
if (KMessageBox::warningContinueCancel(this, i18n("This will delete the %1 guides using this category", count)) != KMessageBox::Continue) {
int category = item->data(Qt::UserRole + 1).toInt();
QDialog d(this);
d.setWindowTitle(i18n("Delete Guide Category"));
QDialogButtonBox buttonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, &d);
auto *l2 = new QVBoxLayout;
d.setLayout(l2);
auto *l3 = new QHBoxLayout;
QRadioButton cb(i18n("Delete the %1 markers using this category", count), &d);
QRadioButton cb2(i18n("Reassign the markers to :"), &d);
cb2.setChecked(true);
QComboBox combobox(&d);
for (int i = 0; i < guides_list->count(); i++) {
QListWidgetItem *cat = guides_list->item(i);
int ix = cat->data(Qt::UserRole + 1).toInt();
if (ix != category) {
combobox.addItem(cat->icon(), cat->text(), ix);
}
}
l3->addWidget(&cb2);
l3->addWidget(&combobox);
l2->addWidget(&cb);
l2->addLayout(l3);
l2->addWidget(&buttonBox);
d.connect(&buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
d.connect(&buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
if (d.exec() == QDialog::Accepted) {
if (cb2.isChecked()) {
m_remapCategories.insert(category, combobox.currentData().toInt());
}
} else {
return;
}
}
......@@ -179,3 +209,8 @@ const QStringList GuideCategories::updatedGuides() const
}
return categories;
}
const QMap<int, int> GuideCategories::remapedGuides() const
{
return m_remapCategories;
}
......@@ -20,6 +20,7 @@ public:
explicit GuideCategories(KdenliveDoc *doc, QWidget *parent = nullptr);
~GuideCategories() override;
const QStringList updatedGuides() const;
const QMap<int, int> remapedGuides() const;
protected:
public slots:
......@@ -29,6 +30,7 @@ private:
QIcon buildIcon(const QColor &col);
/** @brief The incremental index for newly created categories. */
int m_categoryIndex;
QMap<int, int> m_remapCategories;
signals:
};
......@@ -546,6 +546,11 @@ const QStringList ProjectSettings::guidesCategories() const
return m_guidesCategories->updatedGuides();
}
const QMap<int, int> ProjectSettings::remapGuidesCategories() const
{
return m_guidesCategories->remapedGuides();
}
QString ProjectSettings::selectedProfile() const
{
return m_pw->selectedProfile();
......
......@@ -27,6 +27,7 @@ public:
QString selectedProfile() const;
QPair<int, int> tracks() const;
const QStringList guidesCategories() const;
const QMap<int, int> remapGuidesCategories() const;
int audioChannels() const;
bool enableVideoThumbs() const;
bool enableAudioThumbs() const;
......
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