Model used by ProjectTreeView which accesses tbl_trees_extended and transforms it into a tree. More...
#include <treemodel.h>
Public Types | |
enum | TreeModelRoles { TreeItemPointer = Qt::UserRole + 1100, TreeId, TreeItemPath } |
Public Member Functions | |
ProjectTreeModel (QObject *parent=0) | |
~ProjectTreeModel () | |
QVariant | data (const QModelIndex &index, int role) const |
QVariant | headerData (int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const |
TreeItem * | treeItemPointer (const QModelIndex &index) const |
TreeItem * | treeItemPointer (const int tree_id) const |
QModelIndex | index (int row, int column, const QModelIndex &parent=QModelIndex()) const |
QModelIndex | index (const TreePath &path) |
QModelIndex | parent (const QModelIndex &index) const |
int | rowCount (const QModelIndex &parent=QModelIndex()) const |
int | columnCount (const QModelIndex &parent=QModelIndex()) const |
Qt::ItemFlags | flags (const QModelIndex &index) const |
bool | setData (const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) |
QMimeData * | mimeData (const QModelIndexList &indexes) const |
QStringList | mimeTypes () const |
Qt::DropActions | supportedDropActions () const |
bool | insertRows (int position, int rows, const QModelIndex &parent=QModelIndex()) |
bool | removeRows (int position, int rows, const QModelIndex &parent=QModelIndex()) |
void | reLoad () |
virtual QModelIndex | match (const QModelIndex &start, const TreePath &path) const |
Model used by ProjectTreeView which accesses tbl_trees_extended and transforms it into a tree.
This class is heavily inspired by the Editable Tree Model example (and other similar) in the QT documentation. But it is a messē, (better: a working messē). It should be completely rewritten, which should also take lots of time. The benefit of rewriting should be that it would be decently editable. Currently, the code for editing is very cumbersome. An other benefit would be a better code structure.
Problem is the TreeItem itself, which poses a unnecessary (?) complication to the model and model indexes. If each TreeItem could be incorporated into the model index, then I think it would be much simpler. Also, some decent mapping between a hierarchical structure and a two dimensional should be used. It could also be looked into whether QPersistentModelIndex could be used as index?
{ TreeItemPointer = Qt::UserRole + 1100, TreeId, TreeItemPath };
ProjectTreeModel::ProjectTreeModel | ( | QObject * | parent = 0 |
) |
: QAbstractItemModel(parent) { QList<QVariant> rootData; rootData << "Project tree" << "Scope" << "Description"; rootItem = new TreeItem(rootData, QString("root"), 0); setupModelData(rootItem); // Required by QSqlHelper because it can not inherit QObject and connect in it's constructor. QObject::connect(Database::instance(), SIGNAL(tableChanged(QString)), this, SLOT(onTableChange(QString)), Qt::QueuedConnection); setDependency("tbl_trees"); setDependency("tbl_setup"); setDependency("tbl_projects"); setDependency("tbl_locations"); setDependency("tbl_installations"); setDependency("tbl_sensor_categories"); setDependency("tbl_sensors"); setDependency("tbl_sensor_channels"); setDependency("tbl_sensor_channel_data"); }
ProjectTreeModel::~ProjectTreeModel | ( | ) |
{
delete rootItem;
}
int ProjectTreeModel::columnCount | ( | const QModelIndex & | parent = QModelIndex() |
) | const |
Reimplemented in AdjustedTreeModel.
{ if (parent.isValid()) return static_cast<TreeItem*>(parent.internalPointer())->columnCount(); else return rootItem->columnCount(); }
QVariant ProjectTreeModel::data | ( | const QModelIndex & | index, | |
int | role | |||
) | const |
Referenced by flags(), AdjustedTreeModel::flags(), and match().
{ if (!index.isValid()) return QVariant(); if (role == ProjectTreeModel::TreeItemPointer) return QVariant::fromValue(static_cast<TreeItem*>(index.internalPointer())); if (role == ProjectTreeModel::TreeId){ Q_ASSERT(static_cast<TreeItem*>(index.internalPointer())); return QVariant::fromValue(static_cast<TreeItem*>(index.internalPointer())->data(TreeItem::TreeItemId)); } if (role == ProjectTreeModel::TreeItemPath){ Q_ASSERT(static_cast<TreeItem*>(index.internalPointer())); return QVariant::fromValue(static_cast<TreeItem*>(index.internalPointer())->data(TreeItem::TreeItemPath)); } if (role == Qt::ToolTipRole && index.column() == 0){ Q_ASSERT(static_cast<TreeItem*>(index.internalPointer())); TreeItem *item = static_cast<TreeItem*>(index.internalPointer()); QString columnName = item->data(TreeItem::TreeItemColumnName).toString(); TablePrefs prefs = Tables::instance()->value(columnName); // Setup and execute query QString constraint(columnName); if (columnName == "sens_chan_nr") //if it's sens_chan_nr, then we also need sens_id as a constraint to get the description. constraint.prepend(item->parent()->data(TreeItem::TreeItemColumnName).toString().append(" = ").append(item->parent()->data(TreeItem::TreeItemColumnId).toString()).append(" AND ")); constraint.append(" = ").append(item->data(TreeItem::TreeItemColumnId).toString()); QSqlQuery q = Database::execStatic("SELECT " + prefs.descFieldName() + " FROM " + prefs.tableName() + " WHERE " + constraint); // Fetch results Q_ASSERT(q.size() == 1); q.next(); QString toolTip = q.value(0).toString(); if (toolTip.isEmpty()) return "No description..."; else return toolTip; } if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); TreeItem *item = static_cast<TreeItem*>(index.internalPointer()); Q_ASSERT(item); return item->data(index.column()); }
Qt::ItemFlags ProjectTreeModel::flags | ( | const QModelIndex & | index | ) | const |
Reimplemented in AdjustedTreeModel.
{ Qt::ItemFlags flag = Qt::ItemIsEnabled; if (index.column() == 0) flag = flag | Qt::ItemIsSelectable; if (index.isValid()) flag = flag | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; QString treeItemColumnName = index.data(ProjectTreeModel::TreeItemPointer).value<TreeItem*>()->data(TreeItem::TreeItemColumnName).toString(); if ((index.column() == 0 || index.column() == 2) && treeItemColumnName == QString("sens_chan_nr")) flag = flag | Qt::ItemIsEditable; if (index.column() == 2 && treeItemColumnName == QString("sens_id")) flag = flag | Qt::ItemIsEditable; return flag; }
QVariant ProjectTreeModel::headerData | ( | int | section, | |
Qt::Orientation | orientation, | |||
int | role = Qt::DisplayRole | |||
) | const |
QModelIndex ProjectTreeModel::index | ( | int | row, | |
int | column, | |||
const QModelIndex & | parent = QModelIndex() | |||
) | const |
Referenced by match(), and mimeData().
{ TreeItem *parentItem; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast<TreeItem*>(parent.internalPointer()); TreeItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); }
QModelIndex ProjectTreeModel::index | ( | const TreePath & | path | ) |
Might be implemented in the future. Problem is that akthough the TreeItem can seek on TreePath, the found item doesn't know its corresponding QModelIndex... This is one argument more for the fact that TreeItem is obsolete and it's functionality should somehow be implemented in the model with QModelIndex-es
{ Q_ASSERT_X(false, "TreeModel", "Function not implemented yet!"); return QModelIndex(); }
bool ProjectTreeModel::insertRows | ( | int | position, | |
int | rows, | |||
const QModelIndex & | parent = QModelIndex() | |||
) |
{ TreeItem *parentItem; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast<TreeItem*>(parent.internalPointer()); if (position < 0 || position > parentItem->childCount()) return false; QList<QVariant> blankList; for (int column = 0; column < columnCount(); ++column) blankList << QVariant(""); beginInsertRows(parent, position, position + rows - 1); for (int row = 0; row < rows; ++row) { TreeItem *newItem = new TreeItem(blankList, QString("Dummy"), 1000000, parentItem); //DUMMY ID: CHANGE!! if (!parentItem->insertChild(position, newItem)) break; } endInsertRows(); return true; }
QModelIndex ProjectTreeModel::match | ( | const QModelIndex & | start, | |
const TreePath & | path | |||
) | const [virtual] |
We cannot use the original match() function here as we can not compare the custom types in QVariant: by default only the memory addresses are compared, and there doesn't seem any other way to adjust this. Alos, the function is inefficient compared to this one.
{ QModelIndex p = parent(start); int from = start.row(); int to = rowCount(p); for (int r = from; r < to; ++r) { QModelIndex idx = index(r, 0, p); if (!idx.isValid()) continue; TreePath currentPath = data(idx, ProjectTreeModel::TreeItemPath).value<TreePath>(); if (path == currentPath) return idx; if (hasChildren(idx)) { //search the hierarchy if (path[currentPath.size() - 1] == currentPath.last()) //optimalization to search only in child where it will be likely found return match(index(0, 0, idx), path); } } return QModelIndex(); }
QMimeData * ProjectTreeModel::mimeData | ( | const QModelIndexList & | indexes | ) | const |
{ QMimeData *mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); //QObject pointer foreach (QModelIndex index, indexes) { if (index.isValid() && index.column() == 0) { stream << reinterpret_cast<qint64>(index.internalPointer()); } } mimeData->setData("appobj/treeitem.pointer", encodedData); return mimeData; }
QStringList ProjectTreeModel::mimeTypes | ( | ) | const |
{ return QStringList() << "appobj/treeitem.pointer"; }
QModelIndex ProjectTreeModel::parent | ( | const QModelIndex & | index | ) | const |
Referenced by match().
{ if (!index.isValid()) return QModelIndex(); TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer()); TreeItem *parentItem = childItem->parent(); if (parentItem == rootItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); }
void ProjectTreeModel::reLoad | ( | ) |
{ beginResetModel(); delete rootItem; //best heel de root-creatie in een aparte functie steken QList<QVariant> rootData; rootData << "Project tree" << "Scope" << "Description"; rootItem = new TreeItem(rootData, QString("root"), 0); setupModelData(rootItem); endResetModel(); }
bool ProjectTreeModel::removeRows | ( | int | position, | |
int | rows, | |||
const QModelIndex & | parent = QModelIndex() | |||
) |
{ TreeItem *parentItem; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast<TreeItem*>(parent.internalPointer()); Q_ASSERT(parentItem); Q_ASSERT(parentItem != rootItem); //betekent dat project verwijderd moet worden. Q_ASSERT(parentItem->data(TreeItem::TreeItemColumnName).toString() != "sens_id"); //betekent dat sens_chan_nr verwijderd wil worden. if (position < 0 || position > parentItem->childCount()) return false; for (int row = 0; row < rows; ++row) { // This should be made obsolete as all deletions should go through rewriting rules in vew_trees_ext if (Database::execStatic(parentItem->child(position)->getQueryConstraints().prepend("DELETE FROM tbl_setup WHERE ")).lastError().isValid()) { return false; } else { Q_ASSERT(parentItem->child(position)); beginRemoveRows(parent, position, position); if (!parentItem->removeChild(position)) { endRemoveRows(); break; } endRemoveRows(); } } return true; }
int ProjectTreeModel::rowCount | ( | const QModelIndex & | parent = QModelIndex() |
) | const |
{ TreeItem *parentItem; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast<TreeItem*>(parent.internalPointer()); return parentItem->childCount(); }
bool ProjectTreeModel::setData | ( | const QModelIndex & | index, | |
const QVariant & | value, | |||
int | role = Qt::EditRole | |||
) |
{ if (!index.isValid() || role != Qt::EditRole) //carefull here: the index may refer to some other "model" return false; if (value == index.data(Qt::DisplayRole)) return true; TreeItem *item = static_cast<TreeItem*>(index.internalPointer()); if (item->data(TreeItem::TreeItemColumnName) == QString("sens_chan_nr")){ if (index.column() == 0) { bool ok; QString query("UPDATE tbl_trees SET tree_name = '%1' WHERE tree_id = %2;"); QString tree_name = value.toString(); QString tree_id = QString::number(item->data(TreeItem::TreeItemId).toInt(&ok)); Q_ASSERT(ok); Q_ASSERT(tree_id.toInt() != 0); if (tree_name.isEmpty()) { Database::execStatic(QString("UPDATE tbl_trees SET tree_name = NULL WHERE tree_id = %1").arg(tree_id)); } else { Database::execStatic(query.arg(tree_name, tree_id)); } Database::notifyTableChange("tbl_trees"); } else if (index.column() == 2) { bool ok; QString query("UPDATE tbl_trees SET tree_desc = $text$%1$text$ WHERE tree_id = %2"); QString tree_id = QString::number(item->data(TreeItem::TreeItemId).toInt(&ok)); Q_ASSERT(ok); Database::execStatic(query.arg(value.toString(), tree_id)); Database::notifyTableChange("tbl_trees"); } } else if (index.column() == 2 && item->data(TreeItem::TreeItemColumnName) == QString("sens_id")) { bool ok; QString query("UPDATE tbl_setup SET setu_desc = $text$%1$text$ WHERE setu_id = %2"); QString setu_id = QString::number(item->data(TreeItem::TreeItemSetupId).toInt(&ok)); qDebug() << query.arg(value.toString(), setu_id) << item->data(TreeItem::TreeItemSetupId); Q_ASSERT(ok); Database::execStatic(query.arg(value.toString(), setu_id)); Database::notifyTableChange("tbl_setup"); } else return false; emit dataChanged(index, index); return true; }
Qt::DropActions ProjectTreeModel::supportedDropActions | ( | ) | const |
{
return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
}
TreeItem * ProjectTreeModel::treeItemPointer | ( | const int | tree_id | ) | const |
{ return rootItem->find(tree_id); }
TreeItem * ProjectTreeModel::treeItemPointer | ( | const QModelIndex & | index | ) | const |
Referenced by TagContainer::setColumnTagValue(), and TreeViewMenu::TreeViewMenu().
{ return static_cast<TreeItem*>(index.internalPointer()); }