File indexing completed on 2025-02-09 05:44:33
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 }