File indexing completed on 2024-05-12 16:01:27

0001 /*
0002  *  SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef __KIS_CATEGORIES_MAPPER_H
0008 #define __KIS_CATEGORIES_MAPPER_H
0009 
0010 #include <QObject>
0011 #include <QScopedPointer>
0012 #include <kritaui_export.h>
0013 
0014 
0015 /**
0016  * Templated classes cannot inherit QObject, so the signal handling is
0017  * moved to a separate class
0018  */
0019 class KRITAUI_EXPORT __CategoriesSignalsBase : public QObject
0020 {
0021     Q_OBJECT
0022 
0023 Q_SIGNALS:
0024     void rowChanged(int row);
0025     void beginInsertRow(int row);
0026     void endInsertRow();
0027     void beginRemoveRow(int row);
0028     void endRemoveRow();
0029 };
0030 
0031 
0032 template<class TEntry, class TEntryToQStringConverter>
0033 class KisCategoriesMapper : public __CategoriesSignalsBase
0034 {
0035 public:
0036 
0037     class DataItem
0038     {
0039     public:
0040         DataItem(const QString &categoryName, KisCategoriesMapper *parent)
0041             : m_name(categoryName),
0042               m_category(0),
0043               m_expanded(false),
0044               m_enabled(true),
0045               m_checkable(false),
0046               m_checked(false),
0047               m_locked(false),
0048               m_lockable(false),
0049               m_toggled(false),
0050               m_parent(parent)
0051         {
0052         }
0053 
0054         DataItem(const TEntry &entry, DataItem *category, KisCategoriesMapper *parent)
0055             : m_data(new TEntry(entry)),
0056               m_category(category),
0057               m_expanded(false),
0058               m_enabled(true),
0059               m_checkable(false),
0060               m_checked(false),
0061               m_locked(false),
0062               m_lockable(false),
0063               m_toggled(false),
0064               m_parent(parent)
0065         {
0066             Q_ASSERT(category);
0067 
0068             TEntryToQStringConverter converter;
0069             m_name = converter(entry);
0070         }
0071 
0072         TEntry* data() const {
0073             return m_data.data();
0074         }
0075 
0076         QString name() const {
0077             return m_name;
0078         }
0079 
0080         bool isCategory() const {
0081             Q_ASSERT(static_cast<bool>(m_category) == static_cast<bool>(m_data));
0082             return !m_category;
0083         }
0084 
0085         DataItem* parentCategory() const {
0086             return m_category;
0087         }
0088 
0089         bool isExpanded() const {
0090             return m_expanded;
0091         }
0092 
0093         void setExpanded(bool value) {
0094             Q_ASSERT(isCategory());
0095             if (m_expanded == value) return;
0096 
0097             m_expanded = value;
0098             m_parent->notifyCategoryExpanded(this);
0099         }
0100 
0101         bool isEnabled() const {
0102             return m_enabled;
0103         }
0104 
0105         void setEnabled(bool value) {
0106             if (m_enabled == value) return;
0107 
0108             m_enabled = value;
0109             notifyItemChanged();
0110         }
0111 
0112         bool isCheckable() const {
0113             return m_checkable;
0114         }
0115 
0116         void setCheckable(bool value) {
0117             if (m_checkable == value) return;
0118 
0119             m_checkable = value;
0120             notifyItemChanged();
0121         }
0122 
0123         bool isChecked() const {
0124             return m_checked;
0125         }
0126 
0127         void setChecked(bool value) {
0128             if (m_checked == value) return;
0129 
0130             setToggled(value != m_checked);
0131             m_checked = value;
0132             notifyItemChanged();
0133         }
0134         bool isLocked() const {
0135             return m_locked;
0136         }
0137         void setLocked(bool value){
0138             m_locked = value;
0139         }
0140         bool isLockable() const {
0141             return m_lockable;
0142         }
0143         void setLockable(bool value){
0144             m_lockable = value;
0145         }
0146         bool isToggled() const {
0147             return m_toggled;
0148         }
0149         void setToggled(bool value){
0150             m_toggled = value;
0151         }
0152 
0153     private:
0154         void notifyItemChanged() {
0155             m_parent->notifyItemChanged(this);
0156         }
0157 
0158     private:
0159         QString m_name;
0160         QScopedPointer<TEntry> m_data;
0161         DataItem *m_category;
0162 
0163         bool m_expanded;
0164         bool m_enabled;
0165         bool m_checkable;
0166         bool m_checked;
0167         bool m_locked;
0168         bool m_lockable;
0169         bool m_toggled;
0170         KisCategoriesMapper *m_parent;
0171     };
0172 
0173 public:
0174     KisCategoriesMapper() {}
0175     ~KisCategoriesMapper() override {
0176         qDeleteAll(m_items);
0177     }
0178 
0179     DataItem* addCategory(const QString &category) {
0180         if (fetchCategory(category)) return 0;
0181         DataItem *item = new DataItem(category, this);
0182 
0183         emit beginInsertRow(m_items.size());
0184         m_items.append(item);
0185         emit endInsertRow();
0186         return item;
0187     }
0188 
0189     void removeCategory(const QString &category) {
0190         QMutableListIterator<DataItem*> it(m_items);
0191         DataItem *categoryItem = 0;
0192 
0193         int row = 0;
0194         while(it.hasNext()) {
0195             DataItem *item = it.next();
0196 
0197             if (!item->isCategory() &&
0198                 item->parentCategory()->name() == category) {
0199 
0200                 emit beginRemoveRow(row);
0201                 it.remove();
0202                 delete item;
0203                 emit endRemoveRow();
0204             } else {
0205                 if (item->isCategory() && item->name() == category) {
0206                     Q_ASSERT(!categoryItem);
0207                     categoryItem = item;
0208                 }
0209                 row++;
0210             }
0211         }
0212 
0213         if (categoryItem) {
0214             int row = m_items.indexOf(categoryItem);
0215             emit beginRemoveRow(row);
0216             delete m_items.takeAt(row);
0217             emit endRemoveRow();
0218         }
0219     }
0220 
0221     DataItem* addEntry(const QString &category, const TEntry &entry) {
0222         DataItem *categoryItem = fetchCategory(category);
0223         if (!categoryItem) {
0224             categoryItem = addCategory(category);
0225         }
0226         DataItem *item = new DataItem(entry, categoryItem, this);
0227 
0228         emit beginInsertRow(m_items.size());
0229         m_items.append(item);
0230         emit endInsertRow();
0231         return item;
0232     }
0233 
0234     void removeEntry(const QString &category, const TEntry &entry) {
0235         DataItem *item = fetchEntry(category, entry);
0236         if (!item) return;
0237 
0238         int row = m_items.indexOf(item);
0239         emit beginRemoveRow(row);
0240         delete m_items.takeAt(row);
0241         emit endRemoveRow();
0242     }
0243 
0244     DataItem* fetchCategory(const QString &category) const {
0245         Q_FOREACH (DataItem *item, m_items) {
0246             if (item->isCategory() && item->name() == category) return item;
0247         }
0248         return 0;
0249     }
0250 
0251     DataItem* fetchEntry(const QString &category, const TEntry &entry) const {
0252         Q_FOREACH (DataItem *item, m_items) {
0253             if (!item->isCategory() &&
0254                 *item->data() == entry &&
0255                 item->parentCategory()->name() == category) return item;
0256         }
0257         return 0;
0258     }
0259 
0260     DataItem* fetchOneEntry(const TEntry &entry) const {
0261         Q_FOREACH (DataItem *item, m_items) {
0262             if (!item->isCategory() &&
0263                 *item->data() == entry) return item;
0264         }
0265         return 0;
0266     }
0267 
0268     QVector<DataItem*> itemsForCategory(const QString &category) const {
0269         QVector<DataItem*> filteredItems;
0270 
0271         Q_FOREACH (DataItem *item, m_items) {
0272             if (!item->isCategory() &&
0273                 item->parentCategory()->name() == category) {
0274 
0275                 filteredItems.append(item);
0276             }
0277         }
0278 
0279         return filteredItems;
0280     }
0281 
0282     void expandAllCategories() {
0283         Q_FOREACH (DataItem *item, m_items) {
0284             if (item->isCategory()) {
0285                 item->setExpanded(true);
0286             }
0287         }
0288     }
0289 
0290     DataItem* itemFromRow(int row) const {
0291         return m_items[row];
0292     }
0293 
0294     int rowFromItem(DataItem *item) const {
0295         return m_items.indexOf(item);
0296     }
0297 
0298     int rowCount() const {
0299         return m_items.size();
0300     }
0301 
0302 private:
0303     void notifyItemChanged(DataItem *item) {
0304         emit rowChanged(m_items.indexOf(item));
0305     }
0306 
0307     void notifyCategoryExpanded(DataItem *categoryItem) {
0308         Q_ASSERT(categoryItem->isCategory());
0309         notifyItemChanged(categoryItem);
0310 
0311         Q_FOREACH (DataItem *item, m_items) {
0312             if (!item->isCategory() &&
0313                 item->parentCategory() == categoryItem) {
0314 
0315                 notifyItemChanged(item);
0316             }
0317         }
0318     }
0319 
0320 protected:
0321     QList<DataItem*>& testingGetItems() {
0322         return m_items;
0323     }
0324 
0325 private:
0326     QList<DataItem*> m_items;
0327 };
0328 
0329 #endif /* __KIS_CATEGORIES_MAPPER_H */