1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 **   * Redistributions of source code must retain the above copyright
25 **     notice, this list of conditions and the following disclaimer.
26 **   * Redistributions in binary form must reproduce the above copyright
27 **     notice, this list of conditions and the following disclaimer in
28 **     the documentation and/or other materials provided with the
29 **     distribution.
30 **   * Neither the name of The Qt Company Ltd nor the names of its
31 **     contributors may be used to endorse or promote products derived
32 **     from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50 
51 #include "treemodel.h"
52 #include "treeitem.h"
53 
54 #include <QtWidgets>
55 
56 //! [0]
TreeModel(const QStringList & headers,const QString & data,QObject * parent)57 TreeModel::TreeModel(const QStringList &headers, const QString &data, QObject *parent)
58     : QAbstractItemModel(parent)
59 {
60     QVector<QVariant> rootData;
61     for (const QString &header : headers)
62         rootData << header;
63 
64     rootItem = new TreeItem(rootData);
65     setupModelData(data.split('\n'), rootItem);
66 }
67 //! [0]
68 
69 //! [1]
~TreeModel()70 TreeModel::~TreeModel()
71 {
72     delete rootItem;
73 }
74 //! [1]
75 
76 //! [2]
columnCount(const QModelIndex & parent) const77 int TreeModel::columnCount(const QModelIndex &parent) const
78 {
79     Q_UNUSED(parent);
80     return rootItem->columnCount();
81 }
82 //! [2]
83 
data(const QModelIndex & index,int role) const84 QVariant TreeModel::data(const QModelIndex &index, int role) const
85 {
86     if (!index.isValid())
87         return QVariant();
88 
89     if (role != Qt::DisplayRole && role != Qt::EditRole)
90         return QVariant();
91 
92     TreeItem *item = getItem(index);
93 
94     return item->data(index.column());
95 }
96 
97 //! [3]
flags(const QModelIndex & index) const98 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
99 {
100     if (!index.isValid())
101         return Qt::NoItemFlags;
102 
103     return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
104 }
105 //! [3]
106 
107 //! [4]
getItem(const QModelIndex & index) const108 TreeItem *TreeModel::getItem(const QModelIndex &index) const
109 {
110     if (index.isValid()) {
111         TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
112         if (item)
113             return item;
114     }
115     return rootItem;
116 }
117 //! [4]
118 
headerData(int section,Qt::Orientation orientation,int role) const119 QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
120                                int role) const
121 {
122     if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
123         return rootItem->data(section);
124 
125     return QVariant();
126 }
127 
128 //! [5]
index(int row,int column,const QModelIndex & parent) const129 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
130 {
131     if (parent.isValid() && parent.column() != 0)
132         return QModelIndex();
133 //! [5]
134 
135 //! [6]
136     TreeItem *parentItem = getItem(parent);
137     if (!parentItem)
138         return QModelIndex();
139 
140     TreeItem *childItem = parentItem->child(row);
141     if (childItem)
142         return createIndex(row, column, childItem);
143     return QModelIndex();
144 }
145 //! [6]
146 
insertColumns(int position,int columns,const QModelIndex & parent)147 bool TreeModel::insertColumns(int position, int columns, const QModelIndex &parent)
148 {
149     beginInsertColumns(parent, position, position + columns - 1);
150     const bool success = rootItem->insertColumns(position, columns);
151     endInsertColumns();
152 
153     return success;
154 }
155 
insertRows(int position,int rows,const QModelIndex & parent)156 bool TreeModel::insertRows(int position, int rows, const QModelIndex &parent)
157 {
158     TreeItem *parentItem = getItem(parent);
159     if (!parentItem)
160         return false;
161 
162     beginInsertRows(parent, position, position + rows - 1);
163     const bool success = parentItem->insertChildren(position,
164                                                     rows,
165                                                     rootItem->columnCount());
166     endInsertRows();
167 
168     return success;
169 }
170 
171 //! [7]
parent(const QModelIndex & index) const172 QModelIndex TreeModel::parent(const QModelIndex &index) const
173 {
174     if (!index.isValid())
175         return QModelIndex();
176 
177     TreeItem *childItem = getItem(index);
178     TreeItem *parentItem = childItem ? childItem->parent() : nullptr;
179 
180     if (parentItem == rootItem || !parentItem)
181         return QModelIndex();
182 
183     return createIndex(parentItem->childNumber(), 0, parentItem);
184 }
185 //! [7]
186 
removeColumns(int position,int columns,const QModelIndex & parent)187 bool TreeModel::removeColumns(int position, int columns, const QModelIndex &parent)
188 {
189     beginRemoveColumns(parent, position, position + columns - 1);
190     const bool success = rootItem->removeColumns(position, columns);
191     endRemoveColumns();
192 
193     if (rootItem->columnCount() == 0)
194         removeRows(0, rowCount());
195 
196     return success;
197 }
198 
removeRows(int position,int rows,const QModelIndex & parent)199 bool TreeModel::removeRows(int position, int rows, const QModelIndex &parent)
200 {
201     TreeItem *parentItem = getItem(parent);
202     if (!parentItem)
203         return false;
204 
205     beginRemoveRows(parent, position, position + rows - 1);
206     const bool success = parentItem->removeChildren(position, rows);
207     endRemoveRows();
208 
209     return success;
210 }
211 
212 //! [8]
rowCount(const QModelIndex & parent) const213 int TreeModel::rowCount(const QModelIndex &parent) const
214 {
215     const TreeItem *parentItem = getItem(parent);
216 
217     return parentItem ? parentItem->childCount() : 0;
218 }
219 //! [8]
220 
setData(const QModelIndex & index,const QVariant & value,int role)221 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
222 {
223     if (role != Qt::EditRole)
224         return false;
225 
226     TreeItem *item = getItem(index);
227     bool result = item->setData(index.column(), value);
228 
229     if (result)
230         emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
231 
232     return result;
233 }
234 
setHeaderData(int section,Qt::Orientation orientation,const QVariant & value,int role)235 bool TreeModel::setHeaderData(int section, Qt::Orientation orientation,
236                               const QVariant &value, int role)
237 {
238     if (role != Qt::EditRole || orientation != Qt::Horizontal)
239         return false;
240 
241     const bool result = rootItem->setData(section, value);
242 
243     if (result)
244         emit headerDataChanged(orientation, section, section);
245 
246     return result;
247 }
248 
setupModelData(const QStringList & lines,TreeItem * parent)249 void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
250 {
251     QVector<TreeItem*> parents;
252     QVector<int> indentations;
253     parents << parent;
254     indentations << 0;
255 
256     int number = 0;
257 
258     while (number < lines.count()) {
259         int position = 0;
260         while (position < lines[number].length()) {
261             if (lines[number].at(position) != ' ')
262                 break;
263             ++position;
264         }
265 
266         const QString lineData = lines[number].mid(position).trimmed();
267 
268         if (!lineData.isEmpty()) {
269             // Read the column data from the rest of the line.
270             const QStringList columnStrings =
271                 lineData.split(QLatin1Char('\t'), Qt::SkipEmptyParts);
272             QVector<QVariant> columnData;
273             columnData.reserve(columnStrings.size());
274             for (const QString &columnString : columnStrings)
275                 columnData << columnString;
276 
277             if (position > indentations.last()) {
278                 // The last child of the current parent is now the new parent
279                 // unless the current parent has no children.
280 
281                 if (parents.last()->childCount() > 0) {
282                     parents << parents.last()->child(parents.last()->childCount()-1);
283                     indentations << position;
284                 }
285             } else {
286                 while (position < indentations.last() && parents.count() > 0) {
287                     parents.pop_back();
288                     indentations.pop_back();
289                 }
290             }
291 
292             // Append a new item to the current parent's list of children.
293             TreeItem *parent = parents.last();
294             parent->insertChildren(parent->childCount(), 1, rootItem->columnCount());
295             for (int column = 0; column < columnData.size(); ++column)
296                 parent->child(parent->childCount() - 1)->setData(column, columnData[column]);
297         }
298         ++number;
299     }
300 }
301