CMakeLists.txt0000644000175000017500000000072314373551130013236 0ustar wookeywookeycmake_minimum_required(VERSION 2.8.12) project(spatialite LANGUAGES C) find_package(Qt5 COMPONENTS Core REQUIRED) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) file(GLOB CPPSources *.cpp) file(GLOB HSources *.h) add_library(qt-qml-models STATIC ${CPPSources} ${HSources}) target_include_directories(qt-qml-models PUBLIC ./) target_link_libraries(qt-qml-models PRIVATE Qt5::Core) LICENSE.md0000664000175000017500000000134613415523320012102 0ustar wookeywookeyHow can I use this ? Well, license here is pretty "super-open", not even copy-left or copy-right, basically its close to the WTFPL, just use it as you want, as it's the most practical to you : * if you want to use it as GIT submodule and compile it in your app, do it ; * if you prefer separate project as a shared library, do it ; * if you need to modify the project to be able to integrate in you app (opensource or not), do it ; * if you want to share you work on the library, thanks a lot, but if you don't, no problem ; * if you think about some weird case I didn't talk about, well, do whatever you want, I don't need to know about it ; * if you like it so much you want to spread the word, thank you, you're welcome. Enjoy !QQmlGadgetListModel.cpp0000664000175000017500000001547413415523320015014 0ustar wookeywookey #include "qqmlobjectlistmodel.h" /*! \class QQmlGadgetListModel \ingroup QT_QML_MODELS \brief Provides a generic way to generate a list model from Q_GADGET derived class, suitable for QML QQmlGadgetListModel is a convenience subclass \c QAbstractListModel that makes use of C++ templates and Qt Meta Object to extract properties from a \c Q_GADGET derived class and create according roles inside the model. This is a far better way than to expose directly a \c QList inside a \c QVariant. And this is far simpler than doing all Qt model stuff manually : no subclassing or reimplementing need. The class was designed so that most of the added API is really common with \c QList one. \b Note : Simply needs that the class used for items inherits \c Q_GADGET and has Qt Meta Properties. \sa QQmlVariantListModel */ /*! \fn static QQmlGadgetListModel * QQmlGadgetListModel::create (Q_GADGET * parent = Q_NULLPTR) \details A factory to create a new model from a class that will be used as item type. \tparam ItemType The class to use as item \param parent The owner object for the model \return The newly created and configurerd model This is a template method, meant to be used like this : \code QQmlGadgetListModel * myModel = QQmlGadgetListModel::create(this); \endcode No other customization in needed after that. */ /*! \fn ItemType * QQmlGadgetListModel::getAs () const \details A template method to retreive a given item as a precise \c T* Qt object pointer. \tparam ItemType The class to use as return pointer type \param idx The position of the item in the model \return The typed pointer to the object, or \c Q_NULLPTR if the type doesn't match \sa get(int) const, getByUid(QString) const */ /*! \fn QList QQmlGadgetListModel::listAs () const \details A template method to retreive all the items as \c QList typed Qt object pointer list. \tparam ItemType The class as object type to use in the returned pointer list \return A strongly typed \c QList of items Qt object pointers \sa list() const */ /*! \details Returns the data in a specific index for a given role. Reimplemented for QAbstractItemModel. \param index The item index (row, column and parent) \param role The role for property \return The data in the role \b Note : the \c 0 role is a pointer to item object itself. */ /*! \details Returns the roles available in the model. Reimplemented for QAbstractItemModel. \return The hash table of role to name matching \b Note : an additional \c 'qtObject' role is added for convenience. */ /*! \details Modifies the data in a specific index for a given role. Reimplemented for QAbstractItemModel. \param index The item index (row, column and parent) \param value The data to write \param role The role for property \return Weither the modification was done */ /*! \details Returns the role associated to the given property name. \param name The property name inside the item class \return The matching role, \c -1 if not found */ /*! \details Counts the items in the model. \return The count of items in the model */ /*! \details Counts the items in the model. \return The count of items in the model */ /*! \details Tests the content of the model. \return Whether the model contains no item */ /*! \details Tests the presence of a given item in the model. \param item The pointer to the item \return Whether the item was found */ /*! \details Finds the position of given item in the model. \param item The pointer to the item \return The row index of the item, \c -1 if not found */ /*! \details Delete all the items in the model. \b Note : The items objects will be removed from the model but they will be destructed only if they have no parent (because the model took the ownership). */ /*! \details Adds the given item at the end of the model. \param item The pointer to the item \sa prepend(Q_GADGET*), insert(int,Q_GADGET*) */ /*! \details Adds the given item at the beginning of the model. \param item The pointer to the item \sa append(Q_GADGET*), insert(int,Q_GADGET*) */ /*! \details Adds the given item at a certain position in the model. \param idx The position where the item must be added \param item The pointer to the item \sa append(Q_GADGET*), prepend(Q_GADGET*) */ /*! \details Adds the given list of items at the end of the model. \param itemList The list of items \sa prepend(Q_GADGETList), insert(int, Q_GADGETList) */ /*! \details Adds the given list of items at the beginning of the model. \param itemList The list of items \sa append(Q_GADGETList), insert(int, Q_GADGETList) */ /*! \details Adds the given list of items at a certain position in the model. \param idx The position where the items must be added \param itemList The list of items \sa append(Q_GADGETList), prepend(Q_GADGETList) */ /*! \details Moves an item from the model to another position. \param idx The current position of the item \param pos The position where it willl be after the move */ /*! \details Remove an item from the model. \param item The pointer to the item object */ /*! \details Remove an item from the model. \param idx The position of the item in the model */ /*! \details Retreives a model item as standard Qt object pointer. \param idx The position of the item in the model \return A pointer to the \c Q_GADGET \sa getAs(int) const, getByUid(QString) const */ /*! \details Retreives the first item of the model as standard Qt object pointer. \return A pointer to the \c Q_GADGET \sa last() */ /*! \details Retreives the last item of the model as standard Qt object pointer. \return A pointer to the \c Q_GADGET \sa first() */ /*! \details Retreives all the items of the model as a standard Qt object pointer list. \return A \c Q_GADGETList containing all the pointers \sa listAs() const */ /*! \details Retreives a model item as standard Qt object pointer using its indexed property. Works only if setRoleNameForUid() was used correctly at start. \param uid The identifier value that points to the item in the index \return A pointer to the \c Q_GADGET \sa getAs(int) const, get(int) const */ /*! \details Sets which property of the items will be used as an index key. This can be used or not, but if not, getByUid() won't work. Ideally, the property used for UID should not change after items are added to the model, because it could have some side-effects. \param name The name of the property / role that is used as the index key */ QQmlGadgetListModel.h0000644000175000017500000003442014373551130014453 0ustar wookeywookey#ifndef QQMLGADGETLISTMODEL_H #define QQMLGADGETLISTMODEL_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "QQmlQListVariantConverter.h" // custom foreach for QList, which uses no copy and check pointer non-null /* #define FOREACH_PTR_IN_QLIST(_type_, _var_, _list_) \ for (typename QList<_type_ *>::const_iterator it = _list_.begin (); it != _list_.end (); it++) \ for (_type_ * _var_ = (_type_ *) (* it); _var_ != Q_NULLPTR; _var_ = Q_NULLPTR) */ class QQmlGadgetListModelBase : public QAbstractListModel { // abstract Qt base class Q_OBJECT Q_PROPERTY (int count READ count NOTIFY countChanged) public: explicit QQmlGadgetListModelBase (QObject * parent = Q_NULLPTR) : QAbstractListModel (parent) { } virtual bool setData(const QModelIndex& index, const QVariant& value, int role) { return QAbstractListModel::setData(index, value, role); } virtual QVariant data(const QModelIndex& index, int role) const = 0; public slots: // virtual methods API for QML virtual int size (void) const = 0; virtual int count (void) const = 0; virtual bool isEmpty (void) const = 0; virtual bool contains (QVariant item) const = 0; virtual int indexOf (QVariant item) const = 0; virtual int roleForName (const QByteArray & name) const = 0; virtual void clear (void) = 0; virtual void append (QVariant item) = 0; virtual void prepend (QVariant item) = 0; virtual void insert (int idx, QVariant item) = 0; virtual void move (int idx, int pos) = 0; virtual void remove (QVariant item) = 0; virtual void remove (int idx) = 0; virtual void replace(int idx, QVariant item) = 0; virtual QVariant get (int idx) const = 0; virtual QVariant get (const QString & uid) const = 0; virtual QVariant getFirst (void) const = 0; virtual QVariant getLast (void) const = 0; virtual QVariantList toVarArray (void) const = 0; virtual bool setData(int idx, const QVariant& value, const QByteArray& roleName) = 0; virtual QVariant data(int idx, const QByteArray& roleName) const = 0; //protected slots: // internal callback // virtual void onItemPropertyChanged (void) = 0; signals: // notifier void countChanged (void); }; template class QQmlGadgetListModel : public QQmlGadgetListModelBase { public: explicit QQmlGadgetListModel (QObject * parent = Q_NULLPTR, const QByteArray & displayRole = QByteArray (), const QByteArray & uidRole = QByteArray ()) : QQmlGadgetListModelBase (parent) , m_count (0) , m_uidRoleName (uidRole) , m_dispRoleName (displayRole) , m_metaObj (ItemType::staticMetaObject) { static QSet roleNamesBlacklist; if (roleNamesBlacklist.isEmpty ()) { roleNamesBlacklist << QByteArrayLiteral ("id") << QByteArrayLiteral ("index") << QByteArrayLiteral ("class") << QByteArrayLiteral ("model") << QByteArrayLiteral ("modelData"); } static const char * HANDLER = "onItemPropertyChanged()"; m_handler = metaObject ()->method (metaObject ()->indexOfMethod (HANDLER)); if (!displayRole.isEmpty ()) { m_roles.insert (Qt::DisplayRole, QByteArrayLiteral ("display")); } m_roles.insert (baseRole (), QByteArrayLiteral ("qtObject")); const int len = m_metaObj.propertyCount (); for (int propertyIdx = 0, role = (baseRole () +1); propertyIdx < len; propertyIdx++, role++) { QMetaProperty metaProp = m_metaObj.property (propertyIdx); const QByteArray propName = QByteArray (metaProp.name ()); if (!roleNamesBlacklist.contains (propName)) { m_roles.insert (role, propName); if (metaProp.hasNotifySignal ()) { m_signalIdxToRole.insert (metaProp.notifySignalIndex (), role); } } else { static const QByteArray CLASS_NAME = (QByteArrayLiteral ("QQmlGadgetListModel<") % m_metaObj.className () % '>'); qWarning () << "Can't have" << propName << "as a role name in" << qPrintable (CLASS_NAME); } } } bool setData (const QModelIndex & index, const QVariant & value, int role) { bool ret = false; int row = index.row(); if(row >= 0 && row < m_items.size()) { ItemType& item = m_items[row]; const QByteArray rolename = (role != Qt::DisplayRole ? m_roles.value (role, emptyBA ()) : m_dispRoleName); if (role != baseRole () && !rolename.isEmpty ()) { int propertyIndex = m_metaObj.indexOfProperty(rolename); ret = m_metaObj.property(propertyIndex).writeOnGadget(&item, value); if (ret) { QVector roles; roles.resize(1); roles.first() = role; emit dataChanged(index, index, roles); } } } return ret; } QVariant data (const QModelIndex & index, int role) const { QVariant ret; int row = index.row(); if(row >= 0 && row < m_items.size()) { const ItemType& item = at (row); const QByteArray rolename = (role != Qt::DisplayRole ? m_roles.value (role, emptyBA ()) : m_dispRoleName); if (!rolename.isEmpty ()) { int propertyIndex = m_metaObj.indexOfProperty(rolename); ret.setValue (role != baseRole () ? m_metaObj.property(propertyIndex).readOnGadget(&item) : QVariant::fromValue (item)); } } return ret; } QHash roleNames (void) const { return m_roles; } typedef typename QList::iterator iterator; iterator begin (void) { return m_items.begin (); } iterator end (void) { return m_items.end (); } typedef typename QList::const_iterator const_iterator; const_iterator constBegin (void) const { return m_items.constBegin (); } const_iterator constEnd (void) const { return m_items.constEnd (); } public: // C++ API ItemType at (int idx) const { ItemType ret; if (idx >= 0 && idx < m_items.size ()) { ret = m_items.value (idx); } return ret; } ItemType getByUid (const QString & uid) const { ItemType ret; if(!m_indexByUid.contains(uid)) { return *(m_indexByUid.value (uid, Q_NULLPTR)); } return ret; } Q_INVOKABLE int roleForName (const QByteArray & name) const { return m_roles.key (name, -1); } int count (void) const { return m_count; } int size (void) const { return m_count; } bool isEmpty (void) const { return m_items.isEmpty (); } bool contains (ItemType item) const { return m_items.contains (item); } int indexOf (ItemType item) const { return m_items.indexOf (item); } void clear (void) { if (!m_items.isEmpty ()) { beginRemoveRows (noParent (), 0, m_items.count () -1); for(int i = 0; i < m_items.size(); i++) { dereferenceItem(&m_items[i]); } m_items.clear (); endRemoveRows (); updateCounter (); } } void append (ItemType item) { const int pos = m_items.count (); beginInsertRows (noParent (), pos, pos); m_items.append (item); referenceItem (&m_items.last()); endInsertRows (); updateCounter (); } void prepend (ItemType item) { beginInsertRows (noParent (), 0, 0); m_items.prepend (item); referenceItem (&m_items.first()); endInsertRows (); updateCounter (); } void insert (int idx, ItemType item) { beginInsertRows (noParent (), idx, idx); m_items.insert (idx, item); referenceItem (&m_items[idx]); endInsertRows (); updateCounter (); } void append (const QList & itemList) { insert(m_items.count(), itemList); } void prepend (const QList & itemList) { insert(0, itemList); } void insert (int idx, const QList & itemList) { if (!itemList.isEmpty ()) { beginInsertRows (noParent (), idx, idx + itemList.count () -1); m_items.reserve (m_items.count () + itemList.count ()); for(int i = 0; i < itemList.count(); i++) { m_items.insert(idx + i, itemList.at(i)); referenceItem(&m_items[i]); } endInsertRows (); updateCounter (); } } void move (int idx, int pos) { if (idx != pos) { const int lowest = qMin (idx, pos); const int highest = qMax (idx, pos); beginMoveRows (noParent (), highest, highest, noParent (), lowest); m_items.move (highest, lowest); endMoveRows (); } } void remove (ItemType item) { const int idx = m_items.indexOf (item); remove (idx); } void remove (int idx) { if (idx >= 0 && idx < m_items.size ()) { beginRemoveRows (noParent (), idx, idx); ItemType& item = m_items[idx]; //.takeAt (idx); dereferenceItem (&item); m_items.takeAt(idx); endRemoveRows (); updateCounter (); } } void replace (int idx, ItemType item) { if(idx >= 0 && idx < m_items.size()) { if(item != m_items.at(idx)) { m_items.replace(idx, item); QModelIndex modelIndex = index(idx); dataChanged(modelIndex, modelIndex, roleNames().keys().toVector()); } } } ItemType first (void) const { return m_items.first (); } ItemType last (void) const { return m_items.last (); } const QList & toList (void) const { return m_items; } public: // QML slots implementation void append (QVariant item) { append (item.value()); } void prepend (QVariant item) { prepend (item.value()); } void insert (int idx, QVariant item) { insert (idx, item.value()); } void remove (QVariant item) { remove (item.value()); } void replace (int idx, QVariant item) { replace (idx, item.value()); } bool contains (QVariant item) const { return contains (item.value()); } int indexOf (QVariant item) const { return indexOf (item.value()); } int indexOf (const QString & uid) const { return indexOf (get (uid)); } QVariant get (int idx) const { return QVariant::fromValue(at(idx)); } QVariant get (const QString & uid) const { return QVariant::fromValue(getByUid(uid)); } QVariant getFirst (void) const { return QVariant::fromValue(first()); } QVariant getLast (void) const { return QVariant::fromValue(last()); } QVariantList toVarArray (void) const { return qListToVariant (m_items); } bool setData(int idx, const QVariant& value, const QByteArray& roleName) { return setData(index(idx), value, roleForName(roleName)); } QVariant data(int idx, const QByteArray& rolename) const { return data(index(idx), roleForName(rolename)); } protected: // internal stuff static const QString & emptyStr (void) { static const QString ret = QStringLiteral (""); return ret; } static const QByteArray & emptyBA (void) { static const QByteArray ret = QByteArrayLiteral (""); return ret; } static const QModelIndex & noParent (void) { static const QModelIndex ret = QModelIndex (); return ret; } static const int & baseRole (void) { static const int ret = Qt::UserRole; return ret; } int rowCount (const QModelIndex & parent = QModelIndex ()) const { Q_UNUSED (parent); return m_items.count (); } void referenceItem (ItemType * item) { if (item != Q_NULLPTR) { if (!m_uidRoleName.isEmpty ()) { const QString key = m_indexByUid.key (item, emptyStr ()); if (!key.isEmpty ()) { m_indexByUid.remove (key); } int propertyIndex = m_metaObj.indexOfProperty(m_uidRoleName); const QString value = m_metaObj.property(propertyIndex).readOnGadget(item).toString (); if (!value.isEmpty ()) { m_indexByUid.insert (value, item); } } } } void dereferenceItem (ItemType* item) { if (item != Q_NULLPTR) { if (!m_uidRoleName.isEmpty ()) { const QString key = m_indexByUid.key (item, emptyStr ()); if (!key.isEmpty ()) { m_indexByUid.remove (key); } } } } inline void updateCounter (void) { if (m_count != m_items.count ()) { m_count = m_items.count (); emit countChanged (); } } private: // data members int m_count; QByteArray m_uidRoleName; QByteArray m_dispRoleName; QMetaObject m_metaObj; QMetaMethod m_handler; QHash m_roles; QHash m_signalIdxToRole; QList m_items; QHash m_indexByUid; }; //#define QML_OBJMODEL_PROPERTY(type, name) \ // protected: Q_PROPERTY (QQmlGadgetListModelBase * name READ get_##name CONSTANT) \ // private: QQmlGadgetListModel * m_##name; \ // public: QQmlGadgetListModel * get_##name (void) const { return m_##name; } \ // private: #endif // QQMLOBJECTLISTMODEL_H QQmlObjectListModel.cpp0000664000175000017500000001544513415523320015025 0ustar wookeywookey #include "QQmlObjectListModel.h" /*! \class QQmlObjectListModel \ingroup QT_QML_MODELS \brief Provides a generic way to generate a list model from QObject derived class, suitable for QML QQmlObjectListModel is a convenience subclass \c QAbstractListModel that makes use of C++ templates and Qt Meta Object to extract properties from a \c QObject derived class and create according roles inside the model. This is a far better way than to expose directly a \c QList inside a \c QVariant. And this is far simpler than doing all Qt model stuff manually : no subclassing or reimplementing need. The class was designed so that most of the added API is really common with \c QList one. \b Note : Simply needs that the class used for items inherits \c QObject and has Qt Meta Properties. \sa QQmlVariantListModel */ /*! \fn static QQmlObjectListModel * QQmlObjectListModel::create (QObject * parent = Q_NULLPTR) \details A factory to create a new model from a class that will be used as item type. \tparam ItemType The class to use as item \param parent The owner object for the model \return The newly created and configurerd model This is a template method, meant to be used like this : \code QQmlObjectListModel * myModel = QQmlObjectListModel::create(this); \endcode No other customization in needed after that. */ /*! \fn ItemType * QQmlObjectListModel::getAs () const \details A template method to retreive a given item as a precise \c T* Qt object pointer. \tparam ItemType The class to use as return pointer type \param idx The position of the item in the model \return The typed pointer to the object, or \c Q_NULLPTR if the type doesn't match \sa get(int) const, getByUid(QString) const */ /*! \fn QList QQmlObjectListModel::listAs () const \details A template method to retreive all the items as \c QList typed Qt object pointer list. \tparam ItemType The class as object type to use in the returned pointer list \return A strongly typed \c QList of items Qt object pointers \sa list() const */ /*! \details Returns the data in a specific index for a given role. Reimplemented for QAbstractItemModel. \param index The item index (row, column and parent) \param role The role for property \return The data in the role \b Note : the \c 0 role is a pointer to item object itself. */ /*! \details Returns the roles available in the model. Reimplemented for QAbstractItemModel. \return The hash table of role to name matching \b Note : an additional \c 'qtObject' role is added for convenience. */ /*! \details Modifies the data in a specific index for a given role. Reimplemented for QAbstractItemModel. \param index The item index (row, column and parent) \param value The data to write \param role The role for property \return Weither the modification was done */ /*! \details Returns the role associated to the given property name. \param name The property name inside the item class \return The matching role, \c -1 if not found */ /*! \details Counts the items in the model. \return The count of items in the model */ /*! \details Counts the items in the model. \return The count of items in the model */ /*! \details Tests the content of the model. \return Whether the model contains no item */ /*! \details Tests the presence of a given item in the model. \param item The pointer to the item \return Whether the item was found */ /*! \details Finds the position of given item in the model. \param item The pointer to the item \return The row index of the item, \c -1 if not found */ /*! \details Delete all the items in the model. \b Note : The items objects will be removed from the model but they will be destructed only if they have no parent (because the model took the ownership). */ /*! \details Adds the given item at the end of the model. \param item The pointer to the item \sa prepend(QObject*), insert(int,QObject*) */ /*! \details Adds the given item at the beginning of the model. \param item The pointer to the item \sa append(QObject*), insert(int,QObject*) */ /*! \details Adds the given item at a certain position in the model. \param idx The position where the item must be added \param item The pointer to the item \sa append(QObject*), prepend(QObject*) */ /*! \details Adds the given list of items at the end of the model. \param itemList The list of items \sa prepend(QObjectList), insert(int, QObjectList) */ /*! \details Adds the given list of items at the beginning of the model. \param itemList The list of items \sa append(QObjectList), insert(int, QObjectList) */ /*! \details Adds the given list of items at a certain position in the model. \param idx The position where the items must be added \param itemList The list of items \sa append(QObjectList), prepend(QObjectList) */ /*! \details Moves an item from the model to another position. \param idx The current position of the item \param pos The position where it willl be after the move */ /*! \details Remove an item from the model. \param item The pointer to the item object */ /*! \details Remove an item from the model. \param idx The position of the item in the model */ /*! \details Retreives a model item as standard Qt object pointer. \param idx The position of the item in the model \return A pointer to the \c QObject \sa getAs(int) const, getByUid(QString) const */ /*! \details Retreives the first item of the model as standard Qt object pointer. \return A pointer to the \c QObject \sa last() */ /*! \details Retreives the last item of the model as standard Qt object pointer. \return A pointer to the \c QObject \sa first() */ /*! \details Retreives all the items of the model as a standard Qt object pointer list. \return A \c QObjectList containing all the pointers \sa listAs() const */ /*! \details Retreives a model item as standard Qt object pointer using its indexed property. Works only if setRoleNameForUid() was used correctly at start. \param uid The identifier value that points to the item in the index \return A pointer to the \c QObject \sa getAs(int) const, get(int) const */ /*! \details Sets which property of the items will be used as an index key. This can be used or not, but if not, getByUid() won't work. Ideally, the property used for UID should not change after items are added to the model, because it could have some side-effects. \param name The name of the property / role that is used as the index key */ QQmlObjectListModel.h0000644000175000017500000003675514373551130014503 0ustar wookeywookey#ifndef QQMLOBJECTLISTMODEL_H #define QQMLOBJECTLISTMODEL_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "QQmlQListVariantConverter.h" // custom foreach for QList, which uses no copy and check pointer non-null #define FOREACH_PTR_IN_QLIST(_type_, _var_, _list_) \ for (typename QList<_type_ *>::const_iterator it = _list_.begin (); it != _list_.end (); it++) \ for (_type_ * _var_ = static_cast<_type_ *> (* it); _var_ != Q_NULLPTR; _var_ = Q_NULLPTR) class QQmlObjectListModelBase : public QAbstractListModel { // abstract Qt base class Q_OBJECT Q_PROPERTY (int count READ count NOTIFY countChanged) public: explicit QQmlObjectListModelBase (QObject * parent = Q_NULLPTR) : QAbstractListModel (parent) { } public slots: // virtual methods API for QML virtual int size (void) const = 0; virtual int count (void) const = 0; virtual bool isEmpty (void) const = 0; virtual bool contains (QObject * item) const = 0; virtual int indexOf (QObject * item) const = 0; virtual int roleForName (const QByteArray & name) const = 0; virtual void clear (void) = 0; virtual void append (QObject * item) = 0; virtual void prepend (QObject * item) = 0; virtual void insert (int idx, QObject * item) = 0; virtual void move (int idx, int pos) = 0; virtual void remove (QObject * item) = 0; virtual void remove (int idx) = 0; virtual QObject * get (int idx) const = 0; virtual QObject * get (const QString & uid) const = 0; virtual QObject * getFirst (void) const = 0; virtual QObject * getLast (void) const = 0; virtual QVariantList toVarArray (void) const = 0; protected slots: // internal callback virtual void onItemPropertyChanged (void) = 0; signals: // notifier void countChanged (void); }; template class QQmlObjectListModel : public QQmlObjectListModelBase { public: explicit QQmlObjectListModel (QObject * parent = Q_NULLPTR, const QByteArray & displayRole = QByteArray (), const QByteArray & uidRole = QByteArray ()) : QQmlObjectListModelBase (parent) , m_count (0) , m_uidRoleName (uidRole) , m_dispRoleName (displayRole) , m_metaObj (ItemType::staticMetaObject) { static QSet roleNamesBlacklist; if (roleNamesBlacklist.isEmpty ()) { roleNamesBlacklist << QByteArrayLiteral ("id") << QByteArrayLiteral ("index") << QByteArrayLiteral ("class") << QByteArrayLiteral ("model") << QByteArrayLiteral ("modelData"); } static const char * HANDLER = "onItemPropertyChanged()"; m_handler = metaObject ()->method (metaObject ()->indexOfMethod (HANDLER)); if (!displayRole.isEmpty ()) { m_roles.insert (Qt::DisplayRole, QByteArrayLiteral ("display")); } m_roles.insert (baseRole (), QByteArrayLiteral ("qtObject")); const int len = m_metaObj.propertyCount (); for (int propertyIdx = 0, role = (baseRole () +1); propertyIdx < len; propertyIdx++, role++) { QMetaProperty metaProp = m_metaObj.property (propertyIdx); const QByteArray propName = QByteArray (metaProp.name ()); if (!roleNamesBlacklist.contains (propName)) { m_roles.insert (role, propName); if (metaProp.hasNotifySignal ()) { m_signalIdxToRole.insert (metaProp.notifySignalIndex (), role); } } else { static const QByteArray CLASS_NAME = (QByteArrayLiteral ("QQmlObjectListModel<") % m_metaObj.className () % '>'); qWarning () << "Can't have" << propName << "as a role name in" << qPrintable (CLASS_NAME); } } } bool setData (const QModelIndex & index, const QVariant & value, int role) { bool ret = false; ItemType * item = at (index.row ()); const QByteArray rolename = (role != Qt::DisplayRole ? m_roles.value (role, emptyBA ()) : m_dispRoleName); if (item != Q_NULLPTR && role != baseRole () && !rolename.isEmpty ()) { ret = item->setProperty (rolename, value); } return ret; } QVariant data (const QModelIndex & index, int role) const { QVariant ret; ItemType * item = at (index.row ()); const QByteArray rolename = (role != Qt::DisplayRole ? m_roles.value (role, emptyBA ()) : m_dispRoleName); if (item != Q_NULLPTR && !rolename.isEmpty ()) { ret.setValue (role != baseRole () ? item->property (rolename) : QVariant::fromValue (static_cast (item))); } return ret; } QHash roleNames (void) const { return m_roles; } typedef typename QList::const_iterator const_iterator; const_iterator begin (void) const { return m_items.begin (); } const_iterator end (void) const { return m_items.end (); } const_iterator constBegin (void) const { return m_items.constBegin (); } const_iterator constEnd (void) const { return m_items.constEnd (); } public: // C++ API ItemType * at (int idx) const { ItemType * ret = Q_NULLPTR; if (idx >= 0 && idx < m_items.size ()) { ret = m_items.value (idx); } return ret; } ItemType * getByUid (const QString & uid) const { return m_indexByUid.value (uid, Q_NULLPTR); } int roleForName (const QByteArray & name) const { return m_roles.key (name, -1); } int count (void) const { return m_count; } int size (void) const { return m_count; } bool isEmpty (void) const { return m_items.isEmpty (); } bool contains (ItemType * item) const { return m_items.contains (item); } int indexOf (ItemType * item) const { return m_items.indexOf (item); } void clear (void) { if (!m_items.isEmpty ()) { beginRemoveRows (noParent (), 0, m_items.count () -1); FOREACH_PTR_IN_QLIST (ItemType, item, m_items) { dereferenceItem (item); } m_items.clear (); endRemoveRows (); updateCounter (); } } void append (ItemType * item) { if (item != Q_NULLPTR) { const int pos = m_items.count (); beginInsertRows (noParent (), pos, pos); m_items.append (item); referenceItem (item); endInsertRows (); updateCounter (); } } void prepend (ItemType * item) { if (item != Q_NULLPTR) { beginInsertRows (noParent (), 0, 0); m_items.prepend (item); referenceItem (item); endInsertRows (); updateCounter (); } } void insert (int idx, ItemType * item) { if (item != Q_NULLPTR) { beginInsertRows (noParent (), idx, idx); m_items.insert (idx, item); referenceItem (item); endInsertRows (); updateCounter (); } } void append (const QList & itemList) { if (!itemList.isEmpty ()) { const int pos = m_items.count (); beginInsertRows (noParent (), pos, pos + itemList.count () -1); m_items.reserve (m_items.count () + itemList.count ()); m_items.append (itemList); FOREACH_PTR_IN_QLIST (ItemType, item, itemList) { referenceItem (item); } endInsertRows (); updateCounter (); } } void prepend (const QList & itemList) { if (!itemList.isEmpty ()) { beginInsertRows (noParent (), 0, itemList.count () -1); m_items.reserve (m_items.count () + itemList.count ()); int offset = 0; FOREACH_PTR_IN_QLIST (ItemType, item, itemList) { m_items.insert (offset, item); referenceItem (item); offset++; } endInsertRows (); updateCounter (); } } void insert (int idx, const QList & itemList) { if (!itemList.isEmpty ()) { beginInsertRows (noParent (), idx, idx + itemList.count () -1); m_items.reserve (m_items.count () + itemList.count ()); int offset = 0; FOREACH_PTR_IN_QLIST (ItemType, item, itemList) { m_items.insert (idx + offset, item); referenceItem (item); offset++; } endInsertRows (); updateCounter (); } } void move (int idx, int pos) { if (idx != pos) { const int lowest = qMin (idx, pos); const int highest = qMax (idx, pos); beginMoveRows (noParent (), highest, highest, noParent (), lowest); m_items.move (highest, lowest); endMoveRows (); } } void remove (ItemType * item) { if (item != Q_NULLPTR) { const int idx = m_items.indexOf (item); remove (idx); } } void remove (int idx) { if (idx >= 0 && idx < m_items.size ()) { beginRemoveRows (noParent (), idx, idx); ItemType * item = m_items.takeAt (idx); dereferenceItem (item); endRemoveRows (); updateCounter (); } } ItemType * first (void) const { return m_items.first (); } ItemType * last (void) const { return m_items.last (); } const QList & toList (void) const { return m_items; } public: // QML slots implementation void append (QObject * item) { append (qobject_cast (item)); } void prepend (QObject * item) { prepend (qobject_cast (item)); } void insert (int idx, QObject * item) { insert (idx, qobject_cast (item)); } void remove (QObject * item) { remove (qobject_cast (item)); } bool contains (QObject * item) const { return contains (qobject_cast (item)); } int indexOf (QObject * item) const { return indexOf (qobject_cast (item)); } int indexOf (const QString & uid) const { return indexOf (get (uid)); } QObject * get (int idx) const { return static_cast (at (idx)); } QObject * get (const QString & uid) const { return static_cast (getByUid (uid)); } QObject * getFirst (void) const { return static_cast (first ()); } QObject * getLast (void) const { return static_cast (last ()); } QVariantList toVarArray (void) const { return qListToVariant (m_items); } protected: // internal stuff static const QString & emptyStr (void) { static const QString ret = QStringLiteral (""); return ret; } static const QByteArray & emptyBA (void) { static const QByteArray ret = QByteArrayLiteral (""); return ret; } static const QModelIndex & noParent (void) { static const QModelIndex ret = QModelIndex (); return ret; } static const int & baseRole (void) { static const int ret = Qt::UserRole; return ret; } int rowCount (const QModelIndex & parent = QModelIndex ()) const { Q_UNUSED (parent); return m_items.count (); } void referenceItem (ItemType * item) { if (item != Q_NULLPTR) { if (item->parent () == Q_NULLPTR) { item->setParent (this); } const QList signalsIdxList = m_signalIdxToRole.keys (); for (QList::const_iterator it = signalsIdxList.constBegin (); it != signalsIdxList.constEnd (); it++) { const int signalIdx = static_cast (* it); QMetaMethod notifier = item->metaObject ()->method (signalIdx); connect (item, notifier, this, m_handler, Qt::UniqueConnection); } if (!m_uidRoleName.isEmpty ()) { const QString key = m_indexByUid.key (item, emptyStr ()); if (!key.isEmpty ()) { m_indexByUid.remove (key); } const QString value = item->property (m_uidRoleName).toString (); if (!value.isEmpty ()) { m_indexByUid.insert (value, item); } } } } void dereferenceItem (ItemType * item) { if (item != Q_NULLPTR) { disconnect (this, Q_NULLPTR, item, Q_NULLPTR); disconnect (item, Q_NULLPTR, this, Q_NULLPTR); if (!m_uidRoleName.isEmpty ()) { const QString key = m_indexByUid.key (item, emptyStr ()); if (!key.isEmpty ()) { m_indexByUid.remove (key); } } if (item->parent () == this) { // FIXME : maybe that's not the best way to test ownership ? item->deleteLater (); } } } void onItemPropertyChanged (void) { ItemType * item = qobject_cast (sender ()); const int row = m_items.indexOf (item); const int sig = senderSignalIndex (); const int role = m_signalIdxToRole.value (sig, -1); if (row >= 0 && role >= 0) { QModelIndex index = QAbstractListModel::index (row, 0, noParent ()); QVector rolesList; rolesList.append (role); if (m_roles.value (role) == m_dispRoleName) { rolesList.append (Qt::DisplayRole); } emit dataChanged (index, index, rolesList); } if (!m_uidRoleName.isEmpty ()) { const QByteArray roleName = m_roles.value (role, emptyBA ()); if (!roleName.isEmpty () && roleName == m_uidRoleName) { const QString key = m_indexByUid.key (item, emptyStr ()); if (!key.isEmpty ()) { m_indexByUid.remove (key); } const QString value = item->property (m_uidRoleName).toString (); if (!value.isEmpty ()) { m_indexByUid.insert (value, item); } } } } inline void updateCounter (void) { if (m_count != m_items.count ()) { m_count = m_items.count (); emit countChanged (); } } private: // data members int m_count; QByteArray m_uidRoleName; QByteArray m_dispRoleName; QMetaObject m_metaObj; QMetaMethod m_handler; QHash m_roles; QHash m_signalIdxToRole; QList m_items; QHash m_indexByUid; }; #define QML_OBJMODEL_PROPERTY(type, name) \ protected: Q_PROPERTY (QQmlObjectListModelBase * name READ get_##name CONSTANT) \ private: QQmlObjectListModel * m_##name; \ public: QQmlObjectListModel * get_##name (void) const { return m_##name; } \ private: #endif // QQMLOBJECTLISTMODEL_H QQmlQListVariantConverter.h0000644000175000017500000000153114373551130015711 0ustar wookeywookey#ifndef QQMLQLISTVARIANTCONVERTER_H #define QQMLQLISTVARIANTCONVERTER_H #include #include #include template QList qListFromVariant (const QVariantList & list) { QList ret; ret.reserve (list.size ()); for (QVariantList::const_iterator it = list.constBegin (); it != list.constEnd (); it++) { const QVariant & var = (QVariant) (* it); ret.append (var.value ()); } return ret; } template QVariantList qListToVariant (const QList & list) { QVariantList ret; ret.reserve (list.size ()); for (typename QList::const_iterator it = list.constBegin (); it != list.constEnd (); it++) { const T & val = static_cast(* it); ret.append (QVariant::fromValue (val)); } return ret; } #endif // QQMLQLISTVARIANTCONVERTER_H QQmlVariantListModel.cpp0000664000175000017500000002065513415523320015222 0ustar wookeywookey #include "QQmlVariantListModel.h" #define NO_PARENT QModelIndex () #define BASE_ROLE Qt::UserRole #define EMPTY_STR QStringLiteral ("") #define EMPTY_BA QByteArrayLiteral ("") /*! \class QQmlVariantListModel \ingroup QT_QML_MODELS \brief Provides a generic way to generate a list model from QVariant, suitable for QML QQmlVariantListModel is a convenience subclass \c QAbstractListModel that makes use of the versatile nature of QVariant to allow creating a list model from every type : \li Booleans \li Numbers \li Strings \li Lists \li Maps \li Object pointers \li etc... This is a far better way than to expose directly a \c QList<____> inside a \c QVariant. And this is far simpler than doing all Qt model stuff manually : no subclassing or reimplementing need. The class was designed so that most of the added API is really common with \c QList one. \b Note : Simply needs that the type items inherits is handled by Qt MetaType system and \c QVariant. \sa QQmlObjectListModel */ /*! \details Constructs a new model that will hold QVariant as items. \param parent The parent object for the model memory management */ QQmlVariantListModel::QQmlVariantListModel (QObject * parent) : QAbstractListModel (parent) , m_count(0) , m_items() , m_roles() { m_roles.insert (BASE_ROLE, QByteArrayLiteral ("qtVariant")); } /*! \internal */ QQmlVariantListModel::~QQmlVariantListModel (void) { clear (); } /*! \internal */ int QQmlVariantListModel::rowCount (const QModelIndex & parent) const { Q_UNUSED (parent); return m_items.count (); } /*! \details Returns the data in a specific index for a given role. Reimplemented for QAbstractItemModel. \param index The item index (row, column and parent) \param role The role \return The data in the role \b Note : the \c 0 role contains the QVariant itself. */ QVariant QQmlVariantListModel::data (const QModelIndex & index, int role) const { QVariant ret; int idx = index.row (); if (idx >= 0 && idx < count () && role == BASE_ROLE) { ret = m_items.value (idx); } return ret; } /*! \details Returns the roles available in the model. Reimplemented for QAbstractItemModel. \return The hash table of role to name matching \b Note : the only role is \c 'qtVariant'. */ QHash QQmlVariantListModel::roleNames () const { return m_roles; } /*! \details Modifies the data in a specific index for a given role. Reimplemented for QAbstractItemModel. \param index The item index (row, column and parent) \param value The data to write \param role The role \return Weither the modification was done \b Note : as the only role is \c 0 ('qtVariant'), it replaces the QVariant value */ bool QQmlVariantListModel::setData (const QModelIndex & index, const QVariant & value, int role) { bool ret = false; int idx = index.row (); if (idx >= 0 && idx < count () && role == BASE_ROLE) { m_items.replace (idx, value); QModelIndex item = QAbstractListModel::index (idx, 0, NO_PARENT); emit dataChanged (item, item, QVector (1, role)); ret = true; } return ret; } /*! \details Counts the items in the model. \return The count of items in the model */ int QQmlVariantListModel::count () const { return m_items.size (); } /*! \details Tests the content of the model. \return Whether the model contains no item */ bool QQmlVariantListModel::isEmpty () const { return m_items.isEmpty (); } /*! \details Delete all the items in the model. */ void QQmlVariantListModel::clear () { if (!m_items.isEmpty ()) { beginRemoveRows (NO_PARENT, 0, count () -1); m_items.clear (); endRemoveRows (); updateCounter (); } } /*! \details Adds the given item at the end of the model. \param item The variant value \sa prepend(QVariant), insert(int,QVariant) */ void QQmlVariantListModel::append (const QVariant & item) { int pos = m_items.count (); beginInsertRows (NO_PARENT, pos, pos); m_items.append (item); endInsertRows (); updateCounter (); } /*! \details Adds the given item at the beginning of the model. \param item The variant value \sa append(QVariant), insert(int,QVariant) */ void QQmlVariantListModel::prepend (const QVariant & item) { beginInsertRows (NO_PARENT, 0, 0); m_items.prepend (item); endInsertRows (); updateCounter (); } /*! \details Adds the given item at a certain position in the model. \param idx The position where the item must be added \param item The variant value \sa append(QVariant), prepend(QVariant) */ void QQmlVariantListModel::insert (int idx, const QVariant & item) { beginInsertRows (NO_PARENT, idx, idx); m_items.insert (idx, item); endInsertRows (); updateCounter (); } /*! \details Replace the variant at a certain position in the model with another value. \param pos The position where the item must be replaced \param item The variant value \b Note : this is the regular way in C++ to modify the variant value. */ void QQmlVariantListModel::replace (int pos, const QVariant & item) { if (pos >= 0 && pos < count ()) { m_items.replace (pos, item); QModelIndex index = QAbstractListModel::index (pos, 0, NO_PARENT); emit dataChanged (index, index, QVector (1, BASE_ROLE)); } } /*! \details Adds the given list of items at the end of the model. \param itemList The list of items \sa prepend(QVariantList), insert(int, QVariantList) */ void QQmlVariantListModel::appendList (const QVariantList & itemList) { if (!itemList.isEmpty ()) { int pos = m_items.count (); beginInsertRows (NO_PARENT, pos, pos + itemList.count () -1); m_items.append (itemList); endInsertRows (); updateCounter (); } } /*! \details Adds the given list of items at the beginning of the model. \param itemList The list of items \sa append(QVariantList), insert(int, QVariantList) */ void QQmlVariantListModel::prependList (const QVariantList & itemList) { if (!itemList.isEmpty ()) { beginInsertRows (NO_PARENT, 0, itemList.count () -1); int offset = 0; foreach (QVariant item, itemList) { m_items.insert (offset, item); } endInsertRows (); updateCounter (); } } /*! \details Adds the given list of items at a certain position in the model. \param idx The position where the items must be added \param itemList The list of items \sa append(QVariantList), prepend(QVariantList) */ void QQmlVariantListModel::insertList (int idx, const QVariantList & itemList) { if (!itemList.isEmpty ()) { beginInsertRows (NO_PARENT, idx, idx + itemList.count () -1); int offset = 0; foreach (QVariant item, itemList) { m_items.insert (idx + offset, item); offset++; } endInsertRows (); updateCounter (); } } /*! \details Moves an item from the model to another position. \param idx The current position of the item \param pos The position where it willl be after the move */ void QQmlVariantListModel::move (int idx, int pos) { beginMoveRows (NO_PARENT, idx, idx, NO_PARENT, pos); m_items.move (idx, pos); endMoveRows (); } /*! \details Remove an item from the model. \param idx The position of the item in the model */ void QQmlVariantListModel::remove (int idx) { if (idx >= 0 && idx < m_items.size ()) { beginRemoveRows (NO_PARENT, idx, idx); m_items.removeAt (idx); endRemoveRows (); updateCounter (); } } /*! \details Retreives a model item as a standard Qt variant object. \param idx The position of the item in the model \return A variant containing the item */ QVariant QQmlVariantListModel::get (int idx) const { QVariant ret; if (idx >= 0 && idx < m_items.size ()) { ret = m_items.value (idx); } return ret; } /*! \details Retreives all the items of the model as a standard Qt variant list. \return A \c QVariantList containing all the variants */ QVariantList QQmlVariantListModel::list () const { return m_items; } /*! \internal */ void QQmlVariantListModel::updateCounter () { if (m_count != m_items.count ()) { m_count = m_items.count (); emit countChanged (m_count); } } QQmlVariantListModel.h0000664000175000017500000000302513415523320014657 0ustar wookeywookey#ifndef QQMLVARIANTLISTMODEL_H #define QQMLVARIANTLISTMODEL_H #include #include #include #include class QQmlVariantListModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY (int count READ count NOTIFY countChanged) public: explicit QQmlVariantListModel (QObject * parent = Q_NULLPTR); ~QQmlVariantListModel (void); public: // QAbstractItemModel interface reimplemented int rowCount (const QModelIndex & parent = QModelIndex ()) const; bool setData (const QModelIndex & index, const QVariant & value, int role); QVariant data (const QModelIndex & index, int role) const; QHash roleNames (void) const; public slots: // public API void clear (void); int count (void) const; bool isEmpty (void) const; void append (const QVariant & item); void prepend (const QVariant & item); void insert (int idx, const QVariant & item); void appendList (const QVariantList & itemList); void prependList (const QVariantList & itemList); void replace (int pos, const QVariant & item); void insertList (int idx, const QVariantList & itemList); void move (int idx, int pos); void remove (int idx); QVariant get (int idx) const; QVariantList list (void) const; signals: // notifiers void countChanged (int count); protected: void updateCounter (void); private: int m_count; QVariantList m_items; QHash m_roles; }; #endif // QQMLVARIANTLISTMODEL_H QtQmlModels.pri0000644000175000017500000000044414373551130013414 0ustar wookeywookey # Qt QML Models QT += core qml INCLUDEPATH += $$PWD HEADERS += \ $$PWD/QQmlObjectListModel.h \ $$PWD/QQmlGadgetListModel.h \ $$PWD/QQmlVariantListModel.h SOURCES += \ $$PWD/QQmlObjectListModel.cpp \ $$PWD/QQmlGadgetListModel.cpp \ $$PWD/QQmlVariantListModel.cpp QtQmlModels.pro0000664000175000017500000000013113415523320013411 0ustar wookeywookey TARGET = QtQmlModels TEMPLATE = lib CONFIG += static include ($$PWD/QtQmlModels.pri) QtQmlModels.qbs0000644000175000017500000000240214373551130013403 0ustar wookeywookeyimport qbs; Project { name: "Qt QML Models"; Product { name: "libqtqmltricks-qtqmlmodels"; type: "staticlibrary"; targetName: "QtQmlModels"; Export { cpp.includePaths: "."; Depends { name: "cpp"; } Depends { name: "Qt"; submodules: ["core", "qml"]; } } Depends { name: "cpp"; } Depends { name: "Qt"; submodules: ["core", "qml"]; } Group { name: "C++ template sources"; fileTags: ["txt"] files: [ "QQmlObjectListModel.cpp", "QQmlGadgetListModel.cpp" ] } Group { name: "C++ headers"; files: [ "QQmlObjectListModel.h", "QQmlVariantListModel.h", "QQmlGadgetListModel.h", "QtQmlTricksPlugin_SmartDataModels.h", ] } Group { name: "C++ sources" files: [ "QQmlVariantListModel.cpp", ] } Group { qbs.install: (product.type === "dynamiclibrary"); fileTagsFilter: product.type; } } } QtQmlTricksPlugin_SmartDataModels.h0000664000175000017500000000130413415523320017342 0ustar wookeywookey#ifndef QTQMLTRICKSPLUGIN_SMARTDATAMODELS_H #define QTQMLTRICKSPLUGIN_SMARTDATAMODELS_H #include #include #include "QQmlObjectListModel.h" #include "QQmlVariantListModel.h" static void registerQtQmlTricksSmartDataModel (QQmlEngine * engine) { Q_UNUSED (engine) const char * uri = "QtQmlTricks.SmartDataModels"; // @uri QtQmlTricks.SmartDataModels const int maj = 2; const int min = 0; const char * msg = "!!!"; qmlRegisterUncreatableType (uri, maj, min, "ObjectListModel", msg); qmlRegisterUncreatableType (uri, maj, min, "VariantListModel", msg); } #endif // QTQMLTRICKSPLUGIN_SMARTDATAMODELS_H README.md0000664000175000017500000000140413415523320011750 0ustar wookeywookeyQt QML Models ============= Additional data models aimed to bring more power to QML applications by using useful C++ models in back-end. * `QQmlObjectListModel` : a much nicer way to expose C++ list to QML than the quick & dirty `QList` property . Supports all the strong model features of `QAbstractListModel` while showing the simple and well know API of QList. * `QQmlVariantListModel` : a dead-simple way to create a dynamic C++ list of any type and expose it to QML, way better than using a `QVariantList` property. > NOTE : If you want to donate, use this link : [![Flattr this](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=thebootroo&url=http://gitlab.unique-conception.org/qt-qml-tricks/qt-qml-models)