1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2017 - 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 **************************************************************************/
19
20 #include "ItemModel.h"
21
22 #include <QtCore/QMimeData>
23
24 namespace Otter
25 {
26
Item(ItemModel::ItemType type)27 ItemModel::Item::Item(ItemModel::ItemType type) : QStandardItem()
28 {
29 setup(type);
30 }
31
Item(const QString & title,ItemModel::ItemType type)32 ItemModel::Item::Item(const QString &title, ItemModel::ItemType type) : QStandardItem(title)
33 {
34 setup(type);
35 }
36
Item(const QIcon & icon,const QString & title,ItemModel::ItemType type)37 ItemModel::Item::Item(const QIcon &icon, const QString &title, ItemModel::ItemType type) : QStandardItem(icon, title)
38 {
39 setup(type);
40 }
41
setup(ItemModel::ItemType type)42 void ItemModel::Item::setup(ItemModel::ItemType type)
43 {
44 setData(type, TypeRole);
45
46 switch (type)
47 {
48 case EntryType:
49 setFlags(flags() | Qt::ItemNeverHasChildren);
50
51 break;
52 case SeparatorType:
53 setData(QLatin1String("separator"), Qt::AccessibleDescriptionRole);
54 setFlags(Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
55
56 break;
57 default:
58 break;
59 }
60 }
61
isAncestorOf(QStandardItem * child) const62 bool ItemModel::Item::isAncestorOf(QStandardItem *child) const
63 {
64 if (child == nullptr || child == this)
65 {
66 return false;
67 }
68
69 QStandardItem *parent(child->parent());
70
71 while (parent)
72 {
73 if (parent == this)
74 {
75 return true;
76 }
77
78 parent = parent->parent();
79 }
80
81 return false;
82 }
83
ItemModel(QObject * parent)84 ItemModel::ItemModel(QObject *parent) : QStandardItemModel(parent),
85 m_isExclusive(false),
86 m_isIgnoringCheckStateReset(true)
87 {
88 }
89
setupItem(QStandardItem * item,ItemModel::ItemType type)90 void ItemModel::setupItem(QStandardItem *item, ItemModel::ItemType type)
91 {
92 item->setData(type, TypeRole);
93
94 if (type != FolderType)
95 {
96 item->setFlags((item->flags() & ~Qt::ItemIsDropEnabled) | Qt::ItemNeverHasChildren);
97 }
98 }
99
resetCheckState(const QModelIndex & parent)100 void ItemModel::resetCheckState(const QModelIndex &parent)
101 {
102 for (int i = 0; i < rowCount(parent); ++i)
103 {
104 const QModelIndex index(this->index(i, 0, parent));
105
106 if (index.data(Qt::CheckStateRole).toInt() != Qt::Unchecked)
107 {
108 setData(index, Qt::Unchecked, Qt::CheckStateRole);
109 }
110
111 if (static_cast<ItemType>(index.data(TypeRole).toInt()) == FolderType)
112 {
113 resetCheckState(index);
114 }
115 }
116 }
117
insertRow(QStandardItem * item,QStandardItem * parent,int row,ItemType type)118 void ItemModel::insertRow(QStandardItem *item, QStandardItem *parent, int row, ItemType type)
119 {
120 if (!item)
121 {
122 item = new QStandardItem();
123 }
124
125 if (!parent)
126 {
127 parent = invisibleRootItem();
128 }
129
130 setupItem(item, type);
131
132 if (row >= 0)
133 {
134 parent->insertRow(row, item);
135 }
136 else
137 {
138 parent->appendRow(item);
139 }
140 }
141
insertRow(const QList<QStandardItem * > & items,QStandardItem * parent,int row,ItemType type)142 void ItemModel::insertRow(const QList<QStandardItem*> &items, QStandardItem *parent, int row, ItemType type)
143 {
144 if (!parent)
145 {
146 parent = invisibleRootItem();
147 }
148
149 for (int i = 0; i < items.count(); ++i)
150 {
151 setupItem(items.at(i), type);
152 }
153
154 if (row >= 0)
155 {
156 parent->insertRow(row, items);
157 }
158 else
159 {
160 parent->appendRow(items);
161 }
162 }
163
setExclusive(bool isExclusive)164 void ItemModel::setExclusive(bool isExclusive)
165 {
166 m_isExclusive = isExclusive;
167 }
168
mimeData(const QModelIndexList & indexes) const169 QMimeData* ItemModel::mimeData(const QModelIndexList &indexes) const
170 {
171 QMimeData *mimeData(QStandardItemModel::mimeData(indexes));
172
173 if (indexes.count() == 1)
174 {
175 mimeData->setProperty("x-item-index", indexes.first());
176 }
177
178 return mimeData;
179 }
180
data(const QModelIndex & index,int role) const181 QVariant ItemModel::data(const QModelIndex &index, int role) const
182 {
183 if (role == Qt::AccessibleDescriptionRole && static_cast<ItemType>(QStandardItemModel::data(index, TypeRole).toInt()) == SeparatorType)
184 {
185 return QLatin1String("separator");
186 }
187
188 return QStandardItemModel::data(index, role);
189 }
190
getAllData(int role,int column,const QModelIndex & parent) const191 QVariantList ItemModel::getAllData(int role, int column, const QModelIndex &parent) const
192 {
193 QVariantList data;
194 const int rowAmount(rowCount(parent));
195 const bool useAllColumns(column < 0);
196
197 for (int i = 0; i < rowAmount; ++i)
198 {
199 const QModelIndex rowIndex(index(i, 0, parent));
200
201 if (useAllColumns)
202 {
203 const int columnAmount(columnCount(rowIndex));
204
205 for (int j = 0; j < columnAmount; ++j)
206 {
207 const QVariant value(index(i, j, parent).data(role));
208
209 if (!value.isNull())
210 {
211 data.append(value);
212 }
213 }
214 }
215 else
216 {
217 const QVariant value(index(i, column, parent).data(role));
218
219 if (!value.isNull())
220 {
221 data.append(value);
222 }
223 }
224
225 if (static_cast<ItemType>(rowIndex.data(TypeRole).toInt()) == FolderType)
226 {
227 data.append(getAllData(role, column, rowIndex));
228 }
229 }
230
231 return data;
232 }
233
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)234 bool ItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
235 {
236 Q_UNUSED(column)
237
238 if (static_cast<ItemType>(parent.data(TypeRole).toInt()) != FolderType)
239 {
240 return QStandardItemModel::dropMimeData(data, action, row, 0, parent);
241 }
242
243 QStandardItem *item(itemFromIndex(data->property("x-item-index").toModelIndex()));
244 QStandardItem *targetItem(itemFromIndex(parent.sibling(parent.row(), 0)));
245
246 if (!targetItem)
247 {
248 targetItem = invisibleRootItem();
249 }
250
251 if (!item || !targetItem)
252 {
253 return QStandardItemModel::dropMimeData(data, action, row, column, parent);
254 }
255
256 const QList<QStandardItem*> sourceItems(item->parent() ? item->parent()->takeRow(item->row()) : takeRow(item->row()));
257
258 if (sourceItems.isEmpty())
259 {
260 return false;
261 }
262
263 if (row < 0)
264 {
265 targetItem->appendRow(sourceItems);
266 }
267 else
268 {
269 if (item->parent() == targetItem && item->row() < row)
270 {
271 --row;
272 }
273
274 targetItem->insertRow(row, sourceItems);
275 }
276
277 return true;
278 }
279
isExclusive() const280 bool ItemModel::isExclusive() const
281 {
282 return m_isExclusive;
283 }
284
setData(const QModelIndex & index,const QVariant & value,int role)285 bool ItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
286 {
287 if (role == Qt::CheckStateRole && m_isExclusive)
288 {
289 if (m_isIgnoringCheckStateReset && !value.toBool())
290 {
291 return false;
292 }
293
294 if (value.toBool())
295 {
296 m_isIgnoringCheckStateReset = false;
297
298 resetCheckState(QModelIndex());
299
300 m_isIgnoringCheckStateReset = true;
301 }
302 }
303
304 return QStandardItemModel::setData(index, value, role);
305 }
306
307 }
308