| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the documentation of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:FDL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Free Documentation License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Free |
| ** Documentation License version 1.3 as published by the Free Software |
| ** Foundation and appearing in the file included in the packaging of |
| ** this file. Please review the following information to ensure |
| ** the GNU Free Documentation License version 1.3 requirements |
| ** will be met: https://www.gnu.org/licenses/fdl-1.3.html. |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| /*! |
| \example itemviews/simpledommodel |
| \title Simple DOM Model Example |
| \ingroup examples-itemviews |
| \brief The Simple DOM Model example shows how an existing class can be adapted for use with |
| the model/view framework. |
| |
| \image simpledommodel-example.png |
| |
| Qt provides two complementary sets of classes for reading XML files: The classes based |
| around QXmlReader provide a SAX-style API for incremental reading of large files, and |
| the classes based around QDomDocument enable developers to access the contents of XML |
| files using a Document Object Model (DOM) API. |
| |
| In this example, we create a model that uses the DOM API to expose the structure and |
| contents of XML documents to views via the standard QAbstractModel interface. |
| |
| \section1 Design and Concepts |
| |
| Reading an XML document with Qt's DOM classes is a straightforward process. Typically, |
| the contents of a file are supplied to QDomDocument, and nodes are accessed using the |
| functions provided by QDomNode and its subclasses. |
| |
| \omit |
| For example, the following code |
| snippet reads the contents of a file into a QDomDocument object and traverses the |
| document, reading all the plain text that can be found: |
| |
| \snippet doc/src/snippets/code/doc_src_examples_simpledommodel.cpp 0 |
| |
| In principle, the functions provided by QDomNode can be used to navigate from any |
| given starting point in a document to the piece of data requested by another component. |
| Since QDomDocument maintains information about the structure of a document, we can |
| use this to implement the required virtual functions in a QAbstractItemModel subclass. |
| \endomit |
| |
| The aim is to use the structure provided by QDomDocument by wrapping QDomNode objects |
| in item objects similar to the \c TreeItem objects used in the |
| \l{Simple Tree Model Example}{Simple Tree Model} example. |
| |
| \section1 DomModel Class Definition |
| |
| Let us begin by examining the \c DomModel class: |
| |
| \snippet itemviews/simpledommodel/dommodel.h 0 |
| |
| The class definition contains all the basic functions that are needed for a |
| read-only model. Only the constructor and \c document() function are specific to |
| this model. The private \c domDocument variable is used to hold the document |
| that is exposed by the model; the \c rootItem variable contains a pointer to |
| the root item in the model. |
| |
| \section1 DomItem Class Definition |
| |
| The \c DomItem class is used to hold information about a specific QDomNode in |
| the document: |
| |
| \snippet itemviews/simpledommodel/domitem.h 0 |
| |
| Each \c DomItem provides a wrapper for a QDomNode obtained from the underlying |
| document which contains a reference to the node, it's location in the parent node's |
| list of child nodes, and a pointer to a parent wrapper item. |
| |
| The \c parent(), \c child(), and \c row() functions are convenience functions for |
| the \c DomModel to use that provide basic information about the item to be discovered |
| quickly. The node() function provides access to the underlying QDomNode object. |
| |
| As well as the information supplied in the constructor, the class maintains a cache |
| of information about any child items. This is used to provide a collection of |
| persistent item objects that the model can identify consistently and improve the |
| performance of the model when accessing child items. |
| |
| \section1 DomItem Class Implementation |
| |
| Since the \c DomItem class is only a thin wrapper around QDomNode objects, with a |
| few additional features to help improve performance and memory usage, we can provide |
| a brief outline of the class before discussing the model itself. |
| |
| The constructor simply records details of the QDomNode that needs to be wrapped: |
| |
| \snippet itemviews/simpledommodel/domitem.cpp 0 |
| \snippet itemviews/simpledommodel/domitem.cpp 1 |
| |
| As a result, functions to provide the parent wrapper, the row number occupied by |
| the item in its parent's list of children, and the underlying QDomNode for each item |
| are straightforward to write: |
| |
| \snippet itemviews/simpledommodel/domitem.cpp 4 |
| \codeline |
| \snippet itemviews/simpledommodel/domitem.cpp 6 |
| \codeline |
| \snippet itemviews/simpledommodel/domitem.cpp 3 |
| |
| It is necessary to maintain a collection of items which can be consistently identified |
| by the model. For that reason, we maintain a hash of child wrapper items that, to |
| minimize memory usage, is initially empty. The model uses the item's \c child() |
| function to help create model indexes, and this constructs wrappers for the children |
| of the item's QDomNode, relating the row number of each child to the newly-constructed |
| wrapper: |
| |
| \snippet itemviews/simpledommodel/domitem.cpp 5 |
| |
| If a QDomNode was previously wrapped, the cached wrapper is returned; otherwise, a |
| new wrapper is constructed and stored for valid children, and zero is returned for |
| invalid ones. |
| |
| The class's destructor deletes all the child items of the wrapper: |
| |
| \snippet itemviews/simpledommodel/domitem.cpp 2 |
| |
| These, in turn, will delete their children and free any QDomNode objects in use. |
| |
| \section1 DomModel Class Implementation |
| |
| The structure provided by the \c DomItem class makes the implementation of \c DomModel |
| similar to the \c TreeModel shown in the |
| \l{Simple Tree Model Example}{Simple Tree Model} example. |
| |
| The constructor accepts an existing document and a parent object for the model: |
| |
| \snippet itemviews/simpledommodel/dommodel.cpp 0 |
| |
| A shallow copy of the document is stored for future reference, and a root item is |
| created to provide a wrapper around the document. We assign the root item a row |
| number of zero only to be consistent since the root item will have no siblings. |
| |
| Since the model only contains information about the root item, the destructor only |
| needs to delete this one item: |
| |
| \snippet itemviews/simpledommodel/dommodel.cpp 1 |
| |
| All of the child items in the tree will be deleted by the \c DomItem destructor as |
| their parent items are deleted. |
| |
| \section2 Basic Properties of The Model |
| |
| Some aspects of the model do not depend on the structure of the underlying document, |
| and these are simple to implement. |
| |
| The number of columns exposed by the model is returned by the \c columnCount() |
| function: |
| |
| \snippet itemviews/simpledommodel/dommodel.cpp 2 |
| |
| This value is fixed, and does not depend on the location or type of the underlying |
| node in the document. We will use these three columns to display different kinds of |
| data from the underlying document. |
| |
| Since we only implement a read-only model, the \c flags() function is straightforward |
| to write: |
| |
| \snippet itemviews/simpledommodel/dommodel.cpp 5 |
| |
| Since the model is intended for use in a tree view, the \c headerData() function only |
| provides a horizontal header: |
| |
| \snippet itemviews/simpledommodel/dommodel.cpp 6 |
| |
| The model presents the names of nodes in the first column, element attributes in the |
| second, and any node values in the third. |
| |
| \section2 Navigating The Document |
| |
| The index() function creates a model index for the item with the given row, column, |
| and parent in the model: |
| |
| \snippet itemviews/simpledommodel/dommodel.cpp 7 |
| |
| The function first has to relate the parent index to an item that contains a node |
| from the underlying document. If the parent index is invalid, it refers to the root |
| node in the document, so we retrieve the root item that wraps it; otherwise, we |
| obtain a pointer to the relevant item using the QModelIndex::internalPointer() |
| function. We are able to extract a pointer in this way because any valid model index |
| will have been created by this function, and we store pointers to item objects in |
| any new indexes that we create with QAbstractItemModel::createIndex(): |
| |
| \snippet itemviews/simpledommodel/dommodel.cpp 8 |
| |
| A child item for the given row is provided by the parent item's \c child() function. |
| If a suitable child item was found then we call |
| \l{QAbstractItemModel::createIndex()}{createIndex()} to produce a model index for the |
| requested row and column, passing a pointer to the child item for it to store |
| internally. If no suitable child item is found, an invalid model index is returned. |
| |
| Note that the items themselves maintain ownership of their child items. This means |
| that the model does not need to keep track of the child items that have been created, |
| and can let the items themselves tidy up when they are deleted. |
| |
| The number of rows beneath a given item in the model is returned by the \c rowCount() |
| function, and is the number of child nodes contained by the node that corresponds to |
| the specified model index: |
| |
| \snippet itemviews/simpledommodel/dommodel.cpp 10 |
| |
| To obtain the relevant node in the underlying document, we access the item via the |
| internal pointer stored in the model index. If an invalid index is supplied, the |
| root item is used instead. We use the item's \c node() function to access the node |
| itself, and simply count the number of child nodes it contains. |
| |
| Since the model is used to represent a hierarchical data structure, it needs to |
| provide an implementation for the \c parent() function. This returns a model index |
| that corresponds to the parent of a child model index supplied as its argument: |
| |
| \snippet itemviews/simpledommodel/dommodel.cpp 9 |
| |
| For valid indexes other than the index corresponding to the root item, we obtain |
| a pointer to the relevant item using the method described in the \c index() function, |
| and use the item's \c parent() function to obtain a pointer to the parent item. |
| |
| If no valid parent item exists, or if the parent item is the root item, we can simply |
| follow convention and return an invalid model index. For all other parent items, we |
| create a model index containing the appropriate row and column numbers, and a pointer |
| to the parent item we just obtained. |
| |
| Data is provided by the \c data() function. For simplicity, we only provide data for |
| the \l{Qt::DisplayRole}{display role}, returning an invalid variant for all other |
| requests: |
| |
| \snippet itemviews/simpledommodel/dommodel.cpp 3 |
| |
| As before, we obtain an item pointer for the index supplied, and use it to obtain |
| the underlying document node. Depending on the column specified, the data we return |
| is obtained in different ways: |
| |
| \snippet itemviews/simpledommodel/dommodel.cpp 4 |
| |
| For the first column, we return the node's name. For the second column, we read any |
| attributes that the node may have, and return a string that contains a space-separated |
| list of attribute-value assignments. For the third column, we return any value that |
| the node may have; this allows the contents of text nodes to be displayed in a view. |
| |
| If data from any other column is requested, an invalid variant is returned. |
| |
| \section1 Implementation Notes |
| |
| Ideally, we would rely on the structure provided by QDomDocument to help us write |
| the \l{QAbstractItemModel::parent()}{parent()} and |
| \l{QAbstractItemModel::index()}{index()} functions that are required when subclassing |
| QAbstractItemModel. However, since Qt's DOM classes use their own system for |
| dynamically allocating memory for DOM nodes, we cannot guarantee that the QDomNode |
| objects returned for a given piece of information will be the same for subsequent |
| accesses to the document. |
| |
| We use item wrappers for each QDomNode to provide consistent pointers that the model |
| can use to navigate the document structure. |
| \omit |
| Since these items contain value references to the QDomNode objects themselves, this |
| has the side effect that the DOM nodes themselves can be used to reliably navigate |
| the document [not sure about this - QDom* may return different QDomNode objects for |
| the same piece of information]. However, this advantage is redundant since we need to |
| use wrapper items to obtain it. [Possible use of QDomNode cache in the model itself.] |
| \endomit |
| */ |