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 */