1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the examples of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
14 ** * Redistributions of source code must retain the above copyright
15 ** notice, this list of conditions and the following disclaimer.
16 ** * Redistributions in binary form must reproduce the above copyright
17 ** notice, this list of conditions and the following disclaimer in
18 ** the documentation and/or other materials provided with the
20 ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21 ** of its contributors may be used to endorse or promote products derived
22 ** from this software without specific prior written permission.
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
39 ****************************************************************************/
41 #include <QtCore/QUrl>
42 #include <QtCore/QVariant>
43 #include <QtXmlPatterns/QXmlNamePool>
47 The model has two types of nodes: elements & attributes.
54 In QXmlNodeModelIndex we store two values. QXmlNodeIndex::data()
55 is treated as a signed int, and it is an index into m_fileInfos
56 unless it is -1, in which case it has no meaning and the value
57 of QXmlNodeModelIndex::additionalData() is a Type name instead.
61 The constructor passes \a pool to the base class, then loads an
62 internal vector with an instance of QXmlName for each of the
63 strings "file", "directory", "fileName", "filePath", "size",
64 "mimeType", and "suffix".
67 FileTree::FileTree(const QXmlNamePool& pool)
68 : QSimpleXmlNodeModel(pool),
69 m_filterAllowAll(QDir::AllEntries |
71 QDir::NoDotAndDotDot |
73 m_sortFlags(QDir::Name)
75 QXmlNamePool np = namePool();
77 m_names[File] = QXmlName(np, QLatin1String("file"));
78 m_names[Directory] = QXmlName(np, QLatin1String("directory"));
79 m_names[AttributeFileName] = QXmlName(np, QLatin1String("fileName"));
80 m_names[AttributeFilePath] = QXmlName(np, QLatin1String("filePath"));
81 m_names[AttributeSize] = QXmlName(np, QLatin1String("size"));
82 m_names[AttributeMIMEType] = QXmlName(np, QLatin1String("mimeType"));
83 m_names[AttributeSuffix] = QXmlName(np, QLatin1String("suffix"));
88 Returns the QXmlNodeModelIndex for the model node representing
89 the directory \a dirName.
91 It calls QDir::cleanPath(), because an instance of QFileInfo
92 constructed for a path ending in '/' will return the empty string in
93 fileName(), instead of the directory name.
95 QXmlNodeModelIndex FileTree::nodeFor(const QString& dirName) const
97 QFileInfo dirInfo(QDir::cleanPath(dirName));
98 Q_ASSERT(dirInfo.exists());
99 return toNodeIndex(dirInfo);
103 Since the value will always be in m_fileInfos, it is safe for
104 us to return a const reference to it.
108 FileTree::toFileInfo(const QXmlNodeModelIndex &nodeIndex) const
110 return m_fileInfos.at(nodeIndex.data());
115 Returns the model node index for the node specified by the
116 QFileInfo and node Type.
120 FileTree::toNodeIndex(const QFileInfo &fileInfo, Type attributeName) const
122 const int indexOf = m_fileInfos.indexOf(fileInfo);
125 m_fileInfos.append(fileInfo);
126 return createIndex(m_fileInfos.count()-1, attributeName);
129 return createIndex(indexOf, attributeName);
134 Returns the model node index for the node specified by the
135 QFileInfo, which must be a Type::File or Type::Directory.
138 QXmlNodeModelIndex FileTree::toNodeIndex(const QFileInfo &fileInfo) const
140 return toNodeIndex(fileInfo, fileInfo.isDir() ? Directory : File);
145 This private helper function is only called by nextFromSimpleAxis().
146 It is called whenever nextFromSimpleAxis() is called with an axis
147 parameter of either \c{PreviousSibling} or \c{NextSibling}.
150 QXmlNodeModelIndex FileTree::nextSibling(const QXmlNodeModelIndex &nodeIndex,
151 const QFileInfo &fileInfo,
154 Q_ASSERT(offset == -1 || offset == 1);
156 // Get the context node's parent.
157 const QXmlNodeModelIndex parent(nextFromSimpleAxis(Parent, nodeIndex));
160 return QXmlNodeModelIndex();
162 // Get the parent's child list.
163 const QFileInfo parentFI(toFileInfo(parent));
164 Q_ASSERT(Type(parent.additionalData()) == Directory);
165 const QFileInfoList siblings(QDir(parentFI.absoluteFilePath()).entryInfoList(QStringList(),
168 Q_ASSERT_X(!siblings.isEmpty(), Q_FUNC_INFO, "Can't happen! We started at a child.");
170 // Find the index of the child where we started.
171 const int indexOfMe = siblings.indexOf(fileInfo);
174 const int siblingIndex = indexOfMe + offset;
175 if (siblingIndex < 0 || siblingIndex > siblings.count() - 1)
176 return QXmlNodeModelIndex();
178 return toNodeIndex(siblings.at(siblingIndex));
183 This function is called by the Qt XML Patterns query engine when it
184 wants to move to the next node in the model. It moves along an \a
185 axis, \e from the node specified by \a nodeIndex.
187 This function is usually the one that requires the most design and
188 implementation work, because the implementation depends on the
189 perhaps unique structure of your non-XML data.
191 There are \l {QAbstractXmlNodeModel::SimpleAxis} {four values} for
192 \a axis that the implementation must handle, but there are really
193 only two axes, i.e., vertical and horizontal. Two of the four values
194 specify direction on the vertical axis (\c{Parent} and
195 \c{FirstChild}), and the other two values specify direction on the
196 horizontal axis (\c{PreviousSibling} and \c{NextSibling}).
198 The typical implementation will be a \c switch statement with
199 a case for each of the four \a axis values.
203 FileTree::nextFromSimpleAxis(SimpleAxis axis, const QXmlNodeModelIndex &nodeIndex) const
205 const QFileInfo fi(toFileInfo(nodeIndex));
206 const Type type = Type(nodeIndex.additionalData());
208 if (type != File && type != Directory) {
209 Q_ASSERT_X(axis == Parent, Q_FUNC_INFO, "An attribute only has a parent!");
210 return toNodeIndex(fi, Directory);
215 return toNodeIndex(QFileInfo(fi.path()), Directory);
219 if (type == File) // A file has no children.
220 return QXmlNodeModelIndex();
222 Q_ASSERT(type == Directory);
223 Q_ASSERT_X(fi.isDir(), Q_FUNC_INFO, "It isn't really a directory!");
224 const QDir dir(fi.absoluteFilePath());
225 Q_ASSERT(dir.exists());
227 const QFileInfoList children(dir.entryInfoList(QStringList(),
230 if (children.isEmpty())
231 return QXmlNodeModelIndex();
232 const QFileInfo firstChild(children.first());
233 return toNodeIndex(firstChild);
237 case PreviousSibling:
238 return nextSibling(nodeIndex, fi, -1);
241 return nextSibling(nodeIndex, fi, 1);
244 Q_ASSERT_X(false, Q_FUNC_INFO, "Don't ever get here!");
245 return QXmlNodeModelIndex();
250 No matter what part of the file system we model (the whole file
251 tree or a subtree), \a node will always have \c{file:///} as
254 QUrl FileTree::documentUri(const QXmlNodeModelIndex &node) const
257 return QUrl("file:///");
261 This function returns QXmlNodeModelIndex::Element if \a node
262 is a directory or a file, and QXmlNodeModelIndex::Attribute
265 QXmlNodeModelIndex::NodeKind
266 FileTree::kind(const QXmlNodeModelIndex &node) const
268 switch (Type(node.additionalData())) {
271 return QXmlNodeModelIndex::Element;
273 return QXmlNodeModelIndex::Attribute;
278 No order is defined for this example, so we always return
279 QXmlNodeModelIndex::Precedes, just to keep everyone happy.
281 QXmlNodeModelIndex::DocumentOrder
282 FileTree::compareOrder(const QXmlNodeModelIndex&,
283 const QXmlNodeModelIndex&) const
285 return QXmlNodeModelIndex::Precedes;
289 Returns the name of \a node. The caller guarantees that \a node is
290 not null and that it is contained in this node model.
293 QXmlName FileTree::name(const QXmlNodeModelIndex &node) const
295 return m_names.at(node.additionalData());
300 Always returns the QXmlNodeModelIndex for the root of the
301 file system, i.e. "/".
303 QXmlNodeModelIndex FileTree::root(const QXmlNodeModelIndex &node) const
306 return toNodeIndex(QFileInfo(QLatin1String("/")));
310 Returns the typed value for \a node, which must be either an
311 attribute or an element. The QVariant returned represents the atomic
312 value of an attribute or the atomic value contained in an element.
314 If the QVariant is returned as a default constructed variant,
315 it means that \a node has no typed value.
317 QVariant FileTree::typedValue(const QXmlNodeModelIndex &node) const
319 const QFileInfo &fi = toFileInfo(node);
321 switch (Type(node.additionalData())) {
323 // deliberate fall through.
326 case AttributeFileName:
327 return fi.fileName();
328 case AttributeFilePath:
329 return fi.filePath();
332 case AttributeMIMEType:
334 /* We don't have any MIME detection code currently, so return
335 * the most generic one. */
336 return QLatin1String("application/octet-stream");
338 case AttributeSuffix:
342 Q_ASSERT_X(false, Q_FUNC_INFO, "This line should never be reached.");
347 Returns the attributes of \a element. The caller guarantees
348 that \a element is an element in this node model.
350 QVector<QXmlNodeModelIndex>
351 FileTree::attributes(const QXmlNodeModelIndex &element) const
353 QVector<QXmlNodeModelIndex> result;
355 /* Both elements has this attribute. */
356 const QFileInfo &forElement = toFileInfo(element);
357 result.append(toNodeIndex(forElement, AttributeFilePath));
358 result.append(toNodeIndex(forElement, AttributeFileName));
360 if (Type(element.additionalData() == File)) {
361 result.append(toNodeIndex(forElement, AttributeSize));
362 result.append(toNodeIndex(forElement, AttributeSuffix));
363 //result.append(toNodeIndex(forElement, AttributeMIMEType));
366 Q_ASSERT(element.additionalData() == Directory);