File indexing completed on 2024-03-03 05:04:09

0001 /*
0002     SPDX-FileCopyrightText: 2022 Jean-Baptiste Mardelle <jb@kdenlive.org>
0003 
0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "guidecategories.h"
0008 #include "bin/bin.h"
0009 #include "bin/model/markerlistmodel.hpp"
0010 #include "core.h"
0011 #include "kdenlive_debug.h"
0012 #include "kdenlivesettings.h"
0013 
0014 #include <KColorCombo>
0015 #include <KIconEffect>
0016 #include <KLineEdit>
0017 #include <KLocalizedString>
0018 #include <KMessageBox>
0019 #include <KStandardAction>
0020 
0021 #include <QDialog>
0022 #include <QDialogButtonBox>
0023 #include <QPainter>
0024 #include <QRadioButton>
0025 
0026 GuideCategories::GuideCategories(KdenliveDoc *doc, QWidget *parent)
0027     : QWidget(parent)
0028 {
0029     setupUi(this);
0030     Fun editItem = [this]() {
0031         QListWidgetItem *item = guides_list->currentItem();
0032         if (!item) {
0033             return false;
0034         }
0035         QList<QColor> categoriesColor;
0036         for (int i = 0; i < guides_list->count(); i++) {
0037             QColor color(guides_list->item(i)->data(Qt::UserRole).toString());
0038             categoriesColor << color;
0039         }
0040         // Edit an existing tag
0041         QDialog d2(this);
0042         d2.setWindowTitle(i18n("Edit Guide Category"));
0043         QDialogButtonBox buttonBox2(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, &d2);
0044         auto *l2 = new QVBoxLayout;
0045         d2.setLayout(l2);
0046         auto *l3 = new QHBoxLayout;
0047         KColorCombo cb(&d2);
0048         l3->addWidget(&cb);
0049         KLineEdit le(&d2);
0050         le.setText(item->text());
0051         QColor originalColor(item->data(Qt::UserRole).toString());
0052         categoriesColor.removeAll(originalColor);
0053         cb.setColor(originalColor);
0054         l3->addWidget(&le);
0055         l2->addLayout(l3);
0056         KMessageWidget mw(&d2);
0057         mw.setText(i18n("This color is already used in another category"));
0058         mw.setMessageType(KMessageWidget::Warning);
0059         mw.setCloseButtonVisible(false);
0060         mw.hide();
0061         l2->addWidget(&mw);
0062         l2->addWidget(&buttonBox2);
0063         d2.connect(&buttonBox2, &QDialogButtonBox::rejected, &d2, &QDialog::reject);
0064         d2.connect(&buttonBox2, &QDialogButtonBox::accepted, &d2, &QDialog::accept);
0065         connect(&le, &KLineEdit::textChanged, &d2, [&buttonBox2, &le, &cb, &categoriesColor]() {
0066             if (le.text().isEmpty()) {
0067                 buttonBox2.button(QDialogButtonBox::Ok)->setEnabled(false);
0068             } else {
0069                 buttonBox2.button(QDialogButtonBox::Ok)->setEnabled(!categoriesColor.contains(cb.color()));
0070             }
0071         });
0072         connect(&cb, &KColorCombo::activated, &d2, [&buttonBox2, &categoriesColor, &mw, &le](const QColor &selectedColor) {
0073             if (categoriesColor.contains(selectedColor)) {
0074                 buttonBox2.button(QDialogButtonBox::Ok)->setEnabled(false);
0075                 mw.animatedShow();
0076             } else {
0077                 buttonBox2.button(QDialogButtonBox::Ok)->setEnabled(!le.text().isEmpty());
0078                 mw.animatedHide();
0079             }
0080         });
0081         if (categoriesColor.contains(cb.color())) {
0082             buttonBox2.button(QDialogButtonBox::Ok)->setEnabled(false);
0083             mw.animatedShow();
0084         }
0085         le.setFocus();
0086         le.selectAll();
0087         if (d2.exec() == QDialog::Accepted) {
0088             QImage img(guides_list->iconSize(), QImage::Format_ARGB32_Premultiplied);
0089             img.fill(Qt::transparent);
0090             QIcon icon = buildIcon(cb.color());
0091             item->setIcon(icon);
0092             item->setText(le.text());
0093             item->setData(Qt::UserRole, cb.color());
0094             return true;
0095         }
0096         return false;
0097     };
0098     QStringList guidesCategories = doc ? doc->guidesCategories() : KdenliveSettings::guidesCategories();
0099     QList<int> existingCategories;
0100     std::shared_ptr<MarkerListModel> markerModel = doc ? doc->getGuideModel(doc->activeUuid) : nullptr;
0101     for (auto &g : guidesCategories) {
0102         if (g.count(QLatin1Char(':')) < 2) {
0103             // Invalid guide data found
0104             qDebug() << "Invalid guide data found: " << g;
0105             continue;
0106         }
0107         const QColor color(g.section(QLatin1Char(':'), -1));
0108         const QString name = g.section(QLatin1Char(':'), 0, -3);
0109         int ix = g.section(QLatin1Char(':'), -2, -2).toInt();
0110         existingCategories << ix;
0111         QIcon ic = buildIcon(color);
0112         auto *item = new QListWidgetItem(ic, name);
0113         item->setData(Qt::UserRole, color);
0114         item->setData(Qt::UserRole + 1, ix);
0115         // Check usage
0116         if (doc) {
0117             int count = markerModel->getAllMarkers(ix).count();
0118             count += pCore->bin()->getAllClipMarkers(ix);
0119             item->setData(Qt::UserRole + 2, count);
0120         }
0121         guides_list->addItem(item);
0122     }
0123     std::sort(existingCategories.begin(), existingCategories.end());
0124     m_categoryIndex = existingCategories.last() + 1;
0125     QAction *a = KStandardAction::renameFile(this, editItem, this);
0126     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0127     ;
0128     guides_list->addAction(a);
0129     connect(guides_list, &QListWidget::itemDoubleClicked, this, [=]() { editItem(); });
0130     connect(guide_edit, &QPushButton::clicked, this, [=]() { editItem(); });
0131     connect(guide_add, &QPushButton::clicked, this, [=]() {
0132         QIcon ic = buildIcon(Qt::white);
0133         auto *item = new QListWidgetItem(ic, i18n("Category %1", guides_list->count() + 1));
0134         item->setData(Qt::UserRole + 1, m_categoryIndex++);
0135         guides_list->addItem(item);
0136         guides_list->setCurrentItem(item);
0137         if (!editItem()) {
0138             delete item;
0139         }
0140         guide_delete->setEnabled(guides_list->count() > 1);
0141     });
0142     connect(guide_delete, &QPushButton::clicked, this, [=]() {
0143         auto *item = guides_list->currentItem();
0144         if (!item || guides_list->count() == 1) {
0145             return;
0146         }
0147         int count = item->data(Qt::UserRole + 2).toInt();
0148         if (count > 0) {
0149             // There are existing guides in this category, warn
0150             int category = item->data(Qt::UserRole + 1).toInt();
0151             QDialog d(this);
0152             d.setWindowTitle(i18n("Delete Guide Category"));
0153             QDialogButtonBox buttonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, &d);
0154             auto *l2 = new QVBoxLayout;
0155             d.setLayout(l2);
0156             auto *l3 = new QHBoxLayout;
0157             QRadioButton cb(i18n("Delete the %1 markers using this category", count), &d);
0158             QRadioButton cb2(i18n("Reassign the markers to :"), &d);
0159             cb2.setChecked(true);
0160             QComboBox combobox(&d);
0161             for (int i = 0; i < guides_list->count(); i++) {
0162                 QListWidgetItem *cat = guides_list->item(i);
0163                 int ix = cat->data(Qt::UserRole + 1).toInt();
0164                 if (ix != category) {
0165                     combobox.addItem(cat->icon(), cat->text(), ix);
0166                 }
0167             }
0168             l3->addWidget(&cb2);
0169             l3->addWidget(&combobox);
0170             l2->addWidget(&cb);
0171             l2->addLayout(l3);
0172             l2->addWidget(&buttonBox);
0173             d.connect(&buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
0174             d.connect(&buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
0175             if (d.exec() == QDialog::Accepted) {
0176                 if (cb2.isChecked()) {
0177                     m_remapCategories.insert(category, combobox.currentData().toInt());
0178                 }
0179             } else {
0180                 return;
0181             }
0182         }
0183         delete item;
0184         guide_delete->setEnabled(guides_list->count() > 1);
0185     });
0186 }
0187 
0188 GuideCategories::~GuideCategories() = default;
0189 
0190 QIcon GuideCategories::buildIcon(const QColor &col)
0191 {
0192     int size = style()->pixelMetric(QStyle::PM_SmallIconSize);
0193     QImage img(size, size, QImage::Format_ARGB32_Premultiplied);
0194     img.fill(Qt::transparent);
0195     QIcon icon = QIcon::fromTheme(QStringLiteral("tag"));
0196     QPainter p(&img);
0197     icon.paint(&p, 0, 0, img.width(), img.height());
0198     p.end();
0199     KIconEffect::toMonochrome(img, col, col, 1);
0200     return QIcon(QPixmap::fromImage(img));
0201 }
0202 
0203 const QStringList GuideCategories::updatedGuides() const
0204 {
0205     QStringList categories;
0206     for (int i = 0; i < guides_list->count(); i++) {
0207         auto item = guides_list->item(i);
0208         QString color = item->data(Qt::UserRole).toString();
0209         QString name = item->text();
0210         int ix = item->data(Qt::UserRole + 1).toInt();
0211         categories << QString("%1:%2:%3").arg(name, QString::number(ix), color);
0212     }
0213     return categories;
0214 }
0215 
0216 const QMap<int, int> GuideCategories::remapedGuides() const
0217 {
0218     return m_remapCategories;
0219 }