Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_categorized_list_model.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#ifndef __KIS_CATEGORIZED_LIST_MODEL_H
8#define __KIS_CATEGORIZED_LIST_MODEL_H
9
10#include <QAbstractListModel>
11#include <QSortFilterProxyModel>
13
14class KRITAUI_EXPORT __CategorizedListModelBase : public QAbstractListModel
15{
16 Q_OBJECT
17
18public:
20 IsHeaderRole = Qt::UserRole + 1,
21 ExpandCategoryRole = Qt::UserRole + 2,
22 SortRole = Qt::UserRole + 3,
23 isLockedRole = Qt::UserRole + 4,
24 isLockableRole = Qt::UserRole + 5,
25 isToggledRole = Qt::UserRole + 6
26 };
27
28public:
29 __CategorizedListModelBase(QObject *parent);
31
32private Q_SLOTS:
33
34 void slotRowChanged(int row) {
35 QModelIndex changedIndex(index(row));
36 Q_EMIT dataChanged(changedIndex, changedIndex);
37 }
38
39 void slotBeginInsertRow(int row) {
40 beginInsertRows(QModelIndex(), row, row);
41 }
42
44 endInsertRows();
45 }
46
47 void slotBeginRemoveRow(int row) {
48 beginRemoveRows(QModelIndex(), row, row);
49 }
50
52 endRemoveRows();
53 }
54};
55
56template<class TEntry, class TEntryToQStringConverter>
58{
59public:
60 typedef TEntry Entry_Type;
63
64public:
65 KisCategorizedListModel(QObject *parent = 0)
67 {
68 connect(&m_mapper, SIGNAL(rowChanged(int)), SLOT(slotRowChanged(int))); // helps with category expand menu
69 connect(&m_mapper, SIGNAL(beginInsertRow(int)), SLOT(slotBeginInsertRow(int)));
70 connect(&m_mapper, SIGNAL(endInsertRow()), SLOT(slotEndInsertRow()));
71 connect(&m_mapper, SIGNAL(beginRemoveRow(int)), SLOT(slotBeginRemoveRow(int)));
72 connect(&m_mapper, SIGNAL(endRemoveRow()), SLOT(slotEndRemoveRow()));
73
74
75
76 }
77
78 int rowCount(const QModelIndex& parent) const override {
79 Q_UNUSED(parent);
80 return m_mapper.rowCount();
81 }
82
83 QVariant data(const QModelIndex& idx, int role = Qt::DisplayRole) const override {
84 if (!idx.isValid()) return QVariant();
85
87 m_mapper.itemFromRow(idx.row());
88 Q_ASSERT(item);
89
90 switch (role) {
91 case IsHeaderRole:
92 return item->isCategory();
94 return item->isCategory() ? item->isExpanded() : item->parentCategory()->isExpanded();
95 case Qt::ToolTipRole:
96 case Qt::DisplayRole:
97 return item->name();
98 case Qt::CheckStateRole:
99 return item->isCheckable() ? item->isChecked() ? Qt::Checked : Qt::Unchecked : QVariant();
100 case SortRole:
101 return item->isCategory() ? item->name() : item->parentCategory()->name() + item->name();
102 case isLockedRole:
103 return item->isLocked();
104 case isLockableRole:
105 return item->isLockable();
106 case isToggledRole:
107 return item->isToggled();
108
109 }
110
111 return QVariant();
112 }
113
114 bool setData(const QModelIndex& idx, const QVariant& value, int role = Qt::EditRole) override {
115 if (!idx.isValid()) return false;
116
118 m_mapper.itemFromRow(idx.row());
119 Q_ASSERT(item);
120
121 switch (role) {
123 Q_ASSERT(item->isCategory());
124 item->setExpanded(value.toBool());
125 break;
126 case Qt::CheckStateRole:
127 Q_ASSERT(item->isCheckable());
128 item->setChecked(value.toInt() == Qt::Checked);
129 break;
130 }
131
132 // dataChanged() needs a QVector even though we are just passing one
133 QVector<int> roles;
134 roles.append(role);
135
136 Q_EMIT dataChanged(idx, idx, roles);
137 return true;
138 }
139
140 Qt::ItemFlags flags(const QModelIndex& idx) const override {
141 if (!idx.isValid()) return Qt::NoItemFlags;
142
144 m_mapper.itemFromRow(idx.row());
145 Q_ASSERT(item);
146
147 Qt::ItemFlags flags = Qt::NoItemFlags;
148
149 if (item->isEnabled()) {
150 flags |= Qt::ItemIsEnabled;
151 }
152
153 if (!item->isCategory()) {
154 flags |= Qt::ItemIsSelectable;
155
156 if (item->isCheckable()) {
157 flags |= Qt::ItemIsUserCheckable;
158 }
159 }
160
161 return flags;
162 }
163
164 QModelIndex indexOf(const TEntry& entry) const {
166 m_mapper.fetchOneEntry(entry);
167
168 return index(m_mapper.rowFromItem(item));
169 }
170
171 bool entryAt(TEntry& entry, QModelIndex index) const {
172 int row = index.row();
173 if (row < 0 || row >= m_mapper.rowCount()) return false;
174
177
178 if (!item->isCategory()) {
179 entry = *item->data();
180 return true;
181 }
182
183 return false;
184 }
185
189
191 return &m_mapper;
192 }
193
194private:
196};
197
198template<class TModel>
199class KRITAUI_EXPORT KisSortedCategorizedListModel : public QSortFilterProxyModel
200{
201 typedef typename TModel::Entry_Type Entry_Type;
202
203public:
204
206 : QSortFilterProxyModel(parent)
207 {
208 }
209
210 QModelIndex indexOf(const Entry_Type& entry) const {
217 Entry_Type e;
218
219 for (int i = 0; i < rowCount(); i++) {
220 QModelIndex index = this->index(i, 0);
221
222 if (entryAt(e, index) && e == entry) {
223 return index;
224 }
225 }
226
227 return QModelIndex();
228 }
229
230 bool entryAt(Entry_Type &entry, QModelIndex index) const {
231 QModelIndex srcIndex = mapToSource(index);
232 return m_model->entryAt(entry, srcIndex);
233 }
234
235protected:
236 void initializeModel(TModel *model) {
237 m_model = model;
238 setSourceModel(model);
239 setSortRole(TModel::SortRole);
240 }
241
242 bool lessThanPriority(const QModelIndex &left,
243 const QModelIndex &right,
244 const QString &priorityCategory) const {
245
246 QString leftKey = sourceModel()->data(left, sortRole()).toString();
247 QString rightKey = sourceModel()->data(right, sortRole()).toString();
248
249 bool leftIsSpecial = leftKey.startsWith(priorityCategory);
250 bool rightIsSpecial = rightKey.startsWith(priorityCategory);
251
252 return leftIsSpecial != rightIsSpecial ?
253 leftIsSpecial : leftKey < rightKey;
254 }
255
256private:
257 TModel *m_model {nullptr};
258};
259
260#endif /* __KIS_CATEGORIZED_LIST_MODEL_H */
float value(const T *src, size_t ch)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
int rowFromItem(DataItem *item) const
DataItem * fetchOneEntry(const TEntry &entry) const
DataItem * itemFromRow(int row) const
const SpecificCategoriesMapper * categoriesMapper() const
KisCategorizedListModel(QObject *parent=0)
QVariant data(const QModelIndex &idx, int role=Qt::DisplayRole) const override
bool entryAt(TEntry &entry, QModelIndex index) const
int rowCount(const QModelIndex &parent) const override
KisCategoriesMapper< TEntry, TEntryToQStringConverter > SpecificCategoriesMapper
Qt::ItemFlags flags(const QModelIndex &idx) const override
SpecificCategoriesMapper::DataItem DataItem
SpecificCategoriesMapper m_mapper
bool setData(const QModelIndex &idx, const QVariant &value, int role=Qt::EditRole) override
SpecificCategoriesMapper * categoriesMapper()
QModelIndex indexOf(const TEntry &entry) const
bool entryAt(Entry_Type &entry, QModelIndex index) const
QModelIndex indexOf(const Entry_Type &entry) const
bool lessThanPriority(const QModelIndex &left, const QModelIndex &right, const QString &priorityCategory) const