Public Types | Public Member Functions

ProjectTreeModel Class Reference

Model used by ProjectTreeView which accesses tbl_trees_extended and transforms it into a tree. More...

#include <treemodel.h>

Inheritance diagram for ProjectTreeModel:
Inheritance graph
[legend]
Collaboration diagram for ProjectTreeModel:
Collaboration graph
[legend]

List of all members.

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
TreeItemtreeItemPointer (const QModelIndex &index) const
TreeItemtreeItemPointer (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)
QMimeDatamimeData (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

Detailed Description

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?

See also:
ProjectTreeModel::index(const TreePath &path)

Member Enumeration Documentation

Enumerator:
TreeItemPointer 
TreeId 
TreeItemPath 

                        {
        TreeItemPointer = Qt::UserRole + 1100,
        TreeId,
        TreeItemPath
    };


Constructor & Destructor Documentation

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;
}


Member Function Documentation

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

{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
        return rootItem->data(section);

    return QVariant();
}

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());
}


The documentation for this class was generated from the following files: