1 /****************************************************************************
2 **
3 ** Copyright (C) 2021 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "assetimportupdatetreemodel.h"
27 #include "assetimportupdatetreeitem.h"
28
29 #include <QSize>
30
31 namespace QmlDesigner {
32 namespace Internal {
33
AssetImportUpdateTreeModel(QObject * parent)34 AssetImportUpdateTreeModel::AssetImportUpdateTreeModel(QObject *parent)
35 : QAbstractItemModel(parent)
36 {
37 m_rootItem = new AssetImportUpdateTreeItem {{}};
38 }
39
~AssetImportUpdateTreeModel()40 AssetImportUpdateTreeModel::~AssetImportUpdateTreeModel()
41 {
42 delete m_rootItem;
43 }
44
flags(const QModelIndex & idx) const45 Qt::ItemFlags AssetImportUpdateTreeModel::flags(const QModelIndex &idx) const
46 {
47 Qt::ItemFlags flags = QAbstractItemModel::flags(idx);
48
49 if (idx.isValid())
50 flags |= Qt::ItemIsUserCheckable;
51
52 return flags;
53 }
54
index(int row,int column,const QModelIndex & parent) const55 QModelIndex AssetImportUpdateTreeModel::index(int row, int column,
56 const QModelIndex &parent) const
57 {
58 if (!hasIndex(row, column, parent))
59 return QModelIndex();
60
61 const AssetImportUpdateTreeItem *parentItem;
62
63 parentItem = parent.isValid() ? treeItemAtIndex(parent) : m_rootItem;
64
65 const AssetImportUpdateTreeItem *childItem = parentItem->childAt(row);
66 if (childItem)
67 return createIndex(row, column, const_cast<AssetImportUpdateTreeItem *>(childItem));
68 else
69 return QModelIndex();
70 }
71
index(AssetImportUpdateTreeItem * item) const72 QModelIndex AssetImportUpdateTreeModel::index(AssetImportUpdateTreeItem *item) const
73 {
74 return createIndex(item->rowOfItem(), 0, item);
75 }
76
data(const AssetImportUpdateTreeItem * row,int role) const77 QVariant AssetImportUpdateTreeModel::data(const AssetImportUpdateTreeItem *row, int role) const
78 {
79 if (role == Qt::DisplayRole)
80 return row->fileInfo().fileName();
81 if (role == Qt::CheckStateRole)
82 return row->checkState();
83 if (role == Qt::ToolTipRole)
84 return row->fileInfo().absoluteFilePath();
85 return {};
86 }
87
parent(const QModelIndex & idx) const88 QModelIndex AssetImportUpdateTreeModel::parent(const QModelIndex &idx) const
89 {
90 if (!idx.isValid())
91 return QModelIndex();
92
93 const AssetImportUpdateTreeItem *childItem = treeItemAtIndex(idx);
94 const AssetImportUpdateTreeItem *parentItem = childItem->parent();
95
96 if (parentItem == m_rootItem)
97 return QModelIndex();
98
99 return createIndex(parentItem->rowOfItem(), 0, const_cast<AssetImportUpdateTreeItem *>(parentItem));
100 }
101
rowCount(const QModelIndex & parent) const102 int AssetImportUpdateTreeModel::rowCount(const QModelIndex &parent) const
103 {
104 if (parent.column() > 0)
105 return 0;
106
107 return parent.isValid() ? treeItemAtIndex(parent)->childCount()
108 : m_rootItem->childCount();
109 }
110
columnCount(const QModelIndex &) const111 int AssetImportUpdateTreeModel::columnCount(const QModelIndex &) const
112 {
113 return 1;
114 }
115
treeItemAtIndex(const QModelIndex & idx)116 AssetImportUpdateTreeItem *AssetImportUpdateTreeModel::treeItemAtIndex(const QModelIndex &idx)
117 {
118 return static_cast<AssetImportUpdateTreeItem*>(idx.internalPointer());
119 }
120
data(const QModelIndex & idx,int role) const121 QVariant AssetImportUpdateTreeModel::data(const QModelIndex &idx, int role) const
122 {
123 if (!idx.isValid())
124 return {};
125
126 if (role == Qt::SizeHintRole)
127 return QSize(0, 20);
128
129 return data(treeItemAtIndex(idx), role);
130 }
131
setData(const QModelIndex & idx,const QVariant & value,int role)132 bool AssetImportUpdateTreeModel::setData(const QModelIndex &idx, const QVariant &value, int role)
133 {
134 if (role == Qt::CheckStateRole) {
135 auto checkState = static_cast<Qt::CheckState>(value.toInt());
136 return setCheckState(idx, checkState);
137 }
138 return QAbstractItemModel::setData(idx, value, role);
139 }
140
setCheckState(const QModelIndex & idx,Qt::CheckState checkState,bool firstCall)141 bool AssetImportUpdateTreeModel::setCheckState(const QModelIndex &idx, Qt::CheckState checkState,
142 bool firstCall)
143 {
144 AssetImportUpdateTreeItem *item = treeItemAtIndex(idx);
145 if (item->checkState() == checkState)
146 return false;
147 item->setCheckState(checkState);
148 if (firstCall) {
149 emit dataChanged(idx, idx);
150 // check parents
151 AssetImportUpdateTreeItem *parent = item->parent();
152 QModelIndex parentIdx = idx.parent();
153 while (parent) {
154 bool hasChecked = false;
155 bool hasUnchecked = false;
156 for (const auto child : parent->children()) {
157 if (child->checkState() == Qt::Checked) {
158 hasChecked = true;
159 } else if (child->checkState() == Qt::Unchecked) {
160 hasUnchecked = true;
161 } else if (child->checkState() == Qt::PartiallyChecked) {
162 hasChecked = true;
163 hasUnchecked = true;
164 }
165 }
166 if (hasChecked && hasUnchecked)
167 parent->setCheckState(Qt::PartiallyChecked);
168 else if (hasChecked)
169 parent->setCheckState(Qt::Checked);
170 else
171 parent->setCheckState(Qt::Unchecked);
172 emit dataChanged(parentIdx, parentIdx);
173 parent = parent->parent();
174 parentIdx = parentIdx.parent();
175 }
176 }
177 // check children
178 if (int children = item->childCount()) {
179 for (int i = 0; i < children; ++i)
180 setCheckState(index(i, 0, idx), checkState, false);
181 emit dataChanged(index(0, 0, idx), index(children - 1, 0, idx));
182 }
183 return true;
184 }
185
createItems(const QList<QFileInfo> & infos,const QSet<QString> & preselectedFiles)186 void AssetImportUpdateTreeModel::createItems(const QList<QFileInfo> &infos,
187 const QSet<QString> &preselectedFiles)
188 {
189 beginResetModel();
190 if (!infos.isEmpty()) {
191 QHash<QString, AssetImportUpdateTreeItem *> dirItems;
192 for (const auto &info : infos) {
193 auto parent = dirItems.value(info.absolutePath());
194 if (!parent)
195 parent = m_rootItem;
196 auto item = new AssetImportUpdateTreeItem(info, parent);
197 if (info.isDir()) {
198 dirItems.insert(info.absoluteFilePath(), item);
199 } else {
200 m_fileItems.append(item);
201 if (preselectedFiles.contains(info.absoluteFilePath()))
202 item->setCheckState(Qt::Checked);
203 }
204 }
205 // Remove dir items that have no children from the model
206 for (auto dirItem : qAsConst(dirItems)) {
207 if (dirItem->childCount() == 0)
208 delete dirItem;
209 }
210 std::function<Qt::CheckState (AssetImportUpdateTreeItem *)> updateDirCheckStatesRecursive;
211 updateDirCheckStatesRecursive = [&](AssetImportUpdateTreeItem *item) -> Qt::CheckState {
212 bool hasChecked = false;
213 bool hasUnchecked = false;
214 for (const auto child : item->children()) {
215 Qt::CheckState childState = child->childCount() > 0
216 ? updateDirCheckStatesRecursive(child)
217 : child->checkState();
218 if (childState == Qt::Checked) {
219 hasChecked = true;
220 } else if (childState == Qt::Unchecked) {
221 hasUnchecked = true;
222 } else {
223 hasChecked = true;
224 hasUnchecked = true;
225 break;
226 }
227 }
228 Qt::CheckState retval = Qt::Unchecked;
229 if (hasChecked && hasUnchecked)
230 retval = Qt::PartiallyChecked;
231 else if (hasChecked)
232 retval = Qt::Checked;
233 item->setCheckState(retval);
234 return retval;
235 };
236 m_rootItem->setCheckState(updateDirCheckStatesRecursive(m_rootItem));
237 }
238 endResetModel();
239 }
240
checkedFiles() const241 QStringList AssetImportUpdateTreeModel::checkedFiles() const
242 {
243 QStringList retList;
244
245 for (const auto item : qAsConst(m_fileItems)) {
246 if (item->checkState() == Qt::Checked)
247 retList.append(item->fileInfo().absoluteFilePath());
248 }
249
250 return retList;
251 }
252
clear()253 void AssetImportUpdateTreeModel::clear()
254 {
255 beginResetModel();
256 m_fileItems.clear();
257 m_rootItem->clear(); // Deletes all children
258 endResetModel();
259 }
260
261 } // namespace Internal
262 } // namespace QmlDesigner
263