You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
368 lines
20 KiB
368 lines
20 KiB
/****************************************************************************
|
|
**
|
|
** https://www.qxorm.com/
|
|
** Copyright (C) 2013 Lionel Marty (contact@qxorm.com)
|
|
**
|
|
** This file is part of the QxOrm library
|
|
**
|
|
** This software is provided 'as-is', without any express or implied
|
|
** warranty. In no event will the authors be held liable for any
|
|
** damages arising from the use of this software
|
|
**
|
|
** Commercial Usage
|
|
** Licensees holding valid commercial QxOrm 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 Lionel Marty
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3.0 as published by the Free Software
|
|
** Foundation and appearing in the file 'license.gpl3.txt' included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
** met : http://www.gnu.org/copyleft/gpl.html
|
|
**
|
|
** If you are unsure which license is appropriate for your use, or
|
|
** if you have questions regarding the use of this file, please contact :
|
|
** contact@qxorm.com
|
|
**
|
|
****************************************************************************/
|
|
|
|
#ifndef _IX_MODEL_H_
|
|
#define _IX_MODEL_H_
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma once
|
|
#endif
|
|
|
|
/*!
|
|
* \file IxModel.h
|
|
* \author Lionel Marty
|
|
* \ingroup QxModelView
|
|
* \brief Interface to manage Qt model/view architecture with classes registered into QxOrm context (Qt widgets and/or QML views)
|
|
*/
|
|
|
|
#ifdef _QX_NO_PRECOMPILED_HEADER
|
|
#ifndef Q_MOC_RUN
|
|
#include <QxPrecompiled.h> // Need to include precompiled header for the generated moc file
|
|
#endif // Q_MOC_RUN
|
|
#endif // _QX_NO_PRECOMPILED_HEADER
|
|
|
|
#include <QtCore/qabstractitemmodel.h>
|
|
|
|
#ifndef Q_MOC_RUN
|
|
#include <QxRegister/IxClass.h>
|
|
#include <QxDataMember/IxDataMemberX.h>
|
|
#include <QxCollection/QxCollection.h>
|
|
#include <QxDao/IxSqlRelation.h>
|
|
#include <QxDao/QxSqlQuery.h>
|
|
#include <QxDao/QxDao.h>
|
|
#include <QxValidator/QxInvalidValueX.h>
|
|
#include <QxValidator/QxValidatorFct.h>
|
|
#endif // Q_MOC_RUN
|
|
|
|
namespace qx {
|
|
namespace model_view {
|
|
namespace detail {
|
|
|
|
template <class T, class M> struct QxNestedModel;
|
|
template <class T, class M> struct QxNestedModel_Generic;
|
|
template <class T, class M> struct QxNestedModel_Container;
|
|
|
|
} // namespace detail
|
|
} // namespace model_view
|
|
} // namespace qx
|
|
|
|
namespace qx {
|
|
|
|
/*!
|
|
* \ingroup QxModelView
|
|
* \brief qx::IxModel : interface to manage Qt model/view architecture with classes registered into QxOrm context (Qt widgets and/or QML views)
|
|
*
|
|
* <b>QxModelView</b> module provides an easy way to work with Qt model/view engine with all classes registered into QxOrm context :
|
|
* - Qt widgets : QTableView or QListView for example to display/modify a database table content ;
|
|
* - QML : each property defined in QxOrm context is exposed to QML engine : QxModelView module makes easier integration between QML and databases.
|
|
*
|
|
* qx::IxModel interface provides a generic way for all models linked to persistents classes registered into QxOrm context. All methods of this class prefixed by <i>qx</i> call functions from qx::dao namespace and then communicate with database.
|
|
*
|
|
* The <i>qxBlogModelView</i> sample project in <i>./test/</i> directory of QxOrm package shows how to create quickly a model and associate it to the Qt model/view engine (first with a Qt widget, then with a QML view).
|
|
*
|
|
* 1- Here is an example to display/modify data from 'author' table (go to qxBlog tutorial for 'author' class definition) in a QTableView :
|
|
* \code
|
|
// Create a model and fetch all data from database
|
|
qx::IxModel * pModel = new qx::QxModel<author>();
|
|
pModel->qxFetchAll();
|
|
|
|
// Associate the model to a QTableView and display it
|
|
QTableView tableView;
|
|
tableView.setModel(pModel);
|
|
tableView.show();
|
|
* \endcode
|
|
*
|
|
* 2- Here is another example in QML (with Qt5, QxModelView module works fine with Qt4 too) :
|
|
* \code
|
|
// Create a model and fetch all data from database
|
|
qx::IxModel * pModel = new qx::QxModel<author>();
|
|
pModel->qxFetchAll();
|
|
|
|
// Associate the model to a QML view and display it
|
|
QQuickView qmlView;
|
|
qmlView.rootContext()->setContextProperty("myModel", pModel);
|
|
qmlView.setSource(QUrl("qrc:/documents/main.qml"));
|
|
qmlView.show();
|
|
* \endcode
|
|
*
|
|
* And here is the 'main.qml' file content :
|
|
* \code
|
|
import QtQuick 2.1
|
|
import QtQuick.Controls 1.0
|
|
|
|
Item {
|
|
width: 400
|
|
height: 300
|
|
Row {
|
|
height: 20
|
|
spacing: 20
|
|
Button {
|
|
text: "Clear"
|
|
onClicked: myModel.clear()
|
|
}
|
|
Button {
|
|
text: "Fetch All"
|
|
onClicked: myModel.qxFetchAll_()
|
|
}
|
|
Button {
|
|
text: "Save"
|
|
onClicked: myModel.qxSave_()
|
|
}
|
|
}
|
|
ListView {
|
|
y: 30
|
|
height: 270
|
|
model: myModel
|
|
delegate: Row {
|
|
height: 20
|
|
spacing: 10
|
|
Text { text: "id: " + author_id }
|
|
TextField {
|
|
text: name
|
|
onTextChanged: name = text
|
|
}
|
|
}
|
|
}
|
|
}
|
|
* \endcode
|
|
*
|
|
* As you can see in the 'main.qml' file, 'author_id' and 'name' properties of 'author' model ('myModel' variable) can be automatically read and write (because they are registered into QxOrm context).
|
|
* Moreover, qx::IxModel interface provides a list of methods for QML side (Q_INVOKABLE) to communicate with database : for example, the 'Save' button will save the model in database without having to write a C++ function.
|
|
*
|
|
* <b>Note :</b> a <b>QxEntityEditor</b> plugin generates automatically the code to manage models with relationships. Then it is possible to work with nested C++ models.
|
|
*/
|
|
class QX_DLL_EXPORT IxModel : public QAbstractItemModel
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
template <typename U, typename V> friend struct qx::model_view::detail::QxNestedModel;
|
|
template <typename U, typename V> friend struct qx::model_view::detail::QxNestedModel_Generic;
|
|
template <typename U, typename V> friend struct qx::model_view::detail::QxNestedModel_Container;
|
|
|
|
public:
|
|
|
|
enum e_auto_update_database { e_no_auto_update, e_auto_update_on_field_change };
|
|
|
|
typedef QHash<QString, IxModel *> type_relation_by_name;
|
|
typedef QList<type_relation_by_name> type_lst_relation_by_name;
|
|
typedef QHash<IxModel *, QPair<int, QString> > type_child_to_its_relation;
|
|
|
|
protected:
|
|
|
|
IxClass * m_pClass; //!< Class introspection registered into QxOrm context associated to the model
|
|
IxClass * m_pModelClass; //!< If model itself is registered into QxOrm context, then you can use this property to work with introspection engine
|
|
IxDataMemberX * m_pDataMemberX; //!< List of properties defined into QxOrm context
|
|
IxDataMember * m_pDataMemberId; //!< Primary key (property id) defined into QxOrm context
|
|
IxCollection * m_pCollection; //!< Interface to store a list of items
|
|
QHash<int, QByteArray> m_lstRoleNames; //!< List of model's role names to expose data to QML
|
|
QList<IxDataMember *> m_lstDataMember; //!< List of data member exposed by the model
|
|
QHash<QString, int> m_lstDataMemberByKey; //!< List of data member key to get column index in model
|
|
QHash<QString, QVariant> m_lstHeadersData; //!< List of headers data by role and data member key
|
|
QStringList m_lstColumns; //!< List of columns exposed by the model (if empty, all columns)
|
|
QSqlDatabase m_database; //!< Database connexion to execute SQL queries (if empty, default database connexion)
|
|
QSqlError m_lastError; //!< Last SQL error
|
|
IxModel * m_pParent; //!< Parent model, NULL if current model is the root model
|
|
type_lst_relation_by_name m_lstChild; //!< List of child model : QxEntityEditor uses this property to manage relationships and create complex data structure
|
|
type_child_to_its_relation m_hChild; //!< Reverse link to m_lstChild, used in setData() to save relations
|
|
e_auto_update_database m_eAutoUpdateDatabase; //!< Auto-update database on field change (detected by the setData() method)
|
|
IxDataMember * m_pDataMemberRelationToParent; //!< The data member holding relationship to its parent model (if one exists), used only by nested models
|
|
long m_lManualInsertIndex; //!< Index to insert manually items to the collection
|
|
QHash<QString, QVariant> m_hCustomProperties; //!< Use this generic hash-table to define extra-properties in your custom classes which inherit from qx::IxModel interface (instead of creating new properties) ==> this will ensure that sizeof(qx::IxModel) == sizeof(YourCustomClass), this is important with nested models feature
|
|
|
|
public:
|
|
|
|
IxModel(QObject * parent = 0);
|
|
virtual ~IxModel();
|
|
|
|
IxClass * getClass() const;
|
|
IxClass * getModelClass() const;
|
|
IxCollection * getCollection() const;
|
|
QSqlDatabase getDatabase() const;
|
|
QSqlError getLastError() const;
|
|
Q_INVOKABLE QString getLastErrorAsString() const;
|
|
Q_INVOKABLE QStringList getListOfColumns() const;
|
|
QHash<QString, QString> getListOfHeaders() const; //!< Obsolete : use headerData() instead
|
|
IxDataMember * getDataMember(int column) const;
|
|
Q_INVOKABLE QString getDataMemberKey(int column) const;
|
|
Q_INVOKABLE int getRowCount() const;
|
|
Q_INVOKABLE QVariant getModelValue(int row, const QString & column) const;
|
|
Q_INVOKABLE int getColumnIndex(const QString & sColumnName) const;
|
|
Q_INVOKABLE int getAutoUpdateDatabase_() const;
|
|
e_auto_update_database getAutoUpdateDatabase() const;
|
|
Q_INVOKABLE virtual bool getShowEmptyLine() const = 0; //!< Can be useful when a model is displayed in a table (QTableView for example) to add automatically an empty row at the end of the table to insert quickly new items (for example, same style like QxEntityEditor list of properties/relationships)
|
|
Q_INVOKABLE QVariant getCustomProperty(const QString & key) const;
|
|
Q_INVOKABLE QObject * getParentModel() const; //!< Can be used to figure out whether this model has a parent model (used only by nested models)
|
|
Q_INVOKABLE void dumpModel(bool bJsonFormat = true) const;
|
|
Q_INVOKABLE QObject * cloneModel();
|
|
|
|
void setDatabase(const QSqlDatabase & db);
|
|
Q_INVOKABLE void setListOfColumns(const QStringList & lst);
|
|
void setListOfHeaders(const QHash<QString, QString> & lst); //!< Obsolete : use setHeaderData() instead
|
|
Q_INVOKABLE bool setModelValue(int row, const QString & column, const QVariant & value);
|
|
void setParentModel(IxModel * pParent);
|
|
Q_INVOKABLE void setAutoUpdateDatabase_(int i);
|
|
void setAutoUpdateDatabase(e_auto_update_database e);
|
|
Q_INVOKABLE virtual void setShowEmptyLine(bool b) = 0;
|
|
Q_INVOKABLE void setCustomProperty(const QString & key, const QVariant & val);
|
|
|
|
Q_INVOKABLE QString toJson(int row = -1) const; //!< On QML side, use JSON.parse() to create a javascript object after calling this qx::IxModel::toJson() method
|
|
Q_INVOKABLE bool fromJson(const QString & json, int row = -1); //!< On QML side, use JSON.stringify() on a javascript object before calling this qx::IxModel::fromJson() method
|
|
|
|
Q_INVOKABLE QVariant getRelationshipValues(int row, const QString & relation, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString()); //!< Depending on relationship type (1-1, 1-n, n-1, n-n) : can return a QVariantMap type or a QVariantList type
|
|
Q_INVOKABLE bool setRelationshipValues(int row, const QString & relation, const QVariant & values); //!< Depending on relationship type (1-1, 1-n, n-1, n-n) : values parameter can be a QVariantMap type or a QVariantList type
|
|
|
|
virtual long qxCount(const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxCount(long & lCount, const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxFetchById(const QVariant & id, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxFetchAll(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery & query, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxFetchRow(int row, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxInsert(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL, bool bUseExecBatch = false) = 0;
|
|
virtual QSqlError qxInsertRow(int row, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxUpdate(const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL, bool bUseExecBatch = false) = 0;
|
|
virtual QSqlError qxUpdateRow(int row, const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxSave(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxSaveRow(int row, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxSaveRowData(int row, const QStringList & column = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxDeleteById(const QVariant & id, QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxDeleteAll(QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxDeleteRow(int row, QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxDestroyById(const QVariant & id, QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxDestroyAll(QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxDestroyRow(int row, QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual QSqlError qxExecuteQuery(qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual qx_bool qxExist(const QVariant & id, QSqlDatabase * pDatabase = NULL) = 0;
|
|
virtual qx::QxInvalidValueX qxValidate(const QStringList & groups = QStringList()) = 0;
|
|
virtual qx::QxInvalidValueX qxValidateRow(int row, const QStringList & groups = QStringList()) = 0;
|
|
|
|
Q_INVOKABLE int qxCount_(const QString & sQuery);
|
|
Q_INVOKABLE bool qxFetchById_(const QVariant & id, const QStringList & relation = QStringList());
|
|
Q_INVOKABLE bool qxFetchAll_(const QStringList & relation = QStringList());
|
|
Q_INVOKABLE bool qxFetchByQuery_(const QString & sQuery, const QStringList & relation = QStringList());
|
|
Q_INVOKABLE bool qxFetchRow_(int row, const QStringList & relation = QStringList());
|
|
Q_INVOKABLE bool qxInsert_(const QStringList & relation = QStringList(), bool bUseExecBatch = false);
|
|
Q_INVOKABLE bool qxInsertRow_(int row, const QStringList & relation = QStringList());
|
|
Q_INVOKABLE bool qxUpdate_(const QString & sQuery, const QStringList & relation = QStringList(), bool bUseExecBatch = false);
|
|
Q_INVOKABLE bool qxUpdateRow_(int row, const QString & sQuery, const QStringList & relation = QStringList());
|
|
Q_INVOKABLE bool qxSave_(const QStringList & relation = QStringList());
|
|
Q_INVOKABLE bool qxSaveRow_(int row, const QStringList & relation = QStringList());
|
|
Q_INVOKABLE bool qxDeleteById_(const QVariant & id);
|
|
Q_INVOKABLE bool qxDeleteAll_();
|
|
Q_INVOKABLE bool qxDeleteByQuery_(const QString & sQuery);
|
|
Q_INVOKABLE bool qxDeleteRow_(int row);
|
|
Q_INVOKABLE bool qxDestroyById_(const QVariant & id);
|
|
Q_INVOKABLE bool qxDestroyAll_();
|
|
Q_INVOKABLE bool qxDestroyByQuery_(const QString & sQuery);
|
|
Q_INVOKABLE bool qxDestroyRow_(int row);
|
|
Q_INVOKABLE bool qxExecuteQuery_(const QString & sQuery);
|
|
Q_INVOKABLE bool qxExist_(const QVariant & id);
|
|
Q_INVOKABLE QString qxValidate_(const QStringList & groups = QStringList());
|
|
Q_INVOKABLE QString qxValidateRow_(int row, const QStringList & groups = QStringList());
|
|
|
|
QSqlError saveChildRelations(IxModel * pChild);
|
|
QVariant getIdFromChild(IxModel * pChild) const; //!< Used to save foreign key in a nested model
|
|
QPair<int, QString> getChildPosition(IxModel * pChild) const;
|
|
|
|
protected:
|
|
|
|
void raiseEvent_headerDataChanged(Qt::Orientation orientation, int first, int last);
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
|
void raiseEvent_dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> & roles = QVector<int>());
|
|
void raiseEvent_layoutAboutToBeChanged(const QList<QPersistentModelIndex> & parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
|
|
void raiseEvent_layoutChanged(const QList<QPersistentModelIndex> & parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
|
|
#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
|
void raiseEvent_dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight);
|
|
void raiseEvent_layoutAboutToBeChanged();
|
|
void raiseEvent_layoutChanged();
|
|
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
|
|
|
public:
|
|
|
|
Q_INVOKABLE void clear(bool bUpdateColumns = false);
|
|
|
|
virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
|
|
virtual bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
|
|
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
|
|
virtual int columnCount(const QModelIndex & parent = QModelIndex()) const;
|
|
virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
|
|
virtual QModelIndex parent(const QModelIndex & index) const;
|
|
virtual bool hasChildren(const QModelIndex & parent = QModelIndex()) const;
|
|
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
|
virtual Qt::ItemFlags flags(const QModelIndex & index) const;
|
|
virtual Qt::DropActions supportedDropActions() const;
|
|
virtual bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex());
|
|
virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant & value, int role = Qt::EditRole);
|
|
bool setHeaderData(const QString & sColumnName, const QVariant & value, int role = Qt::EditRole);
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
|
virtual QHash<int, QByteArray> roleNames() const;
|
|
virtual Qt::DropActions supportedDragActions() const;
|
|
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
|
|
|
protected:
|
|
|
|
virtual QObject * cloneModelImpl() = 0;
|
|
virtual void dumpModelImpl(bool bJsonFormat) const = 0;
|
|
virtual void * getRowItemAsVoidPtr(int row) const = 0;
|
|
virtual bool isDirtyRow(int row) const = 0;
|
|
virtual void insertDirtyRowToModel() = 0;
|
|
virtual void updateShowEmptyLine() = 0;
|
|
virtual void syncNestedModel(int row, const QStringList & relation);
|
|
virtual void syncAllNestedModel(const QStringList & relation);
|
|
void syncNestedModelRecursive(IxModel * pNestedModel, const QStringList & relation);
|
|
|
|
void generateRoleNames();
|
|
QSqlDatabase * database(QSqlDatabase * other);
|
|
IxModel * getChild(long row, const QString & relation);
|
|
void insertChild(long row, const QString & relation, IxModel * pChild);
|
|
void removeListOfChild(long row);
|
|
bool removeRowsGeneric(int row, int count);
|
|
bool removeRowsAutoUpdateOnFieldChange(int row, int count);
|
|
|
|
#ifndef _QX_NO_JSON
|
|
|
|
virtual QString toJson_Helper(int row) const = 0;
|
|
virtual bool fromJson_Helper(const QString & json, int row) = 0;
|
|
|
|
virtual QVariant getRelationshipValues_Helper(int row, const QString & relation, bool bLoadFromDatabase, const QString & sAppendRelations) = 0;
|
|
virtual bool setRelationshipValues_Helper(int row, const QString & relation, const QVariant & values) = 0;
|
|
|
|
#endif // _QX_NO_JSON
|
|
|
|
};
|
|
|
|
} // namespace qx
|
|
|
|
#endif // _IX_MODEL_H_
|