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