X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fclient%2Ftreemodel.cpp;h=48394c5ee29c1e231f219304ba3be3b654e5198c;hp=fda1df4de1ca8f924f116559c966291a789ca197;hb=e561e02a8d2f1f009559d17c7b1c66cb6f4e2a5a;hpb=36b2b9680ff608f41c681b8c23865abb06a2464c diff --git a/src/client/treemodel.cpp b/src/client/treemodel.cpp index fda1df4d..48394c5e 100644 --- a/src/client/treemodel.cpp +++ b/src/client/treemodel.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-07 by the Quassel IRC Team * + * Copyright (C) 2005-08 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -19,111 +19,267 @@ ***************************************************************************/ #include "treemodel.h" +#include "global.h" + +#include +#include /***************************************** - * Buffer Items stored in the Tree Model + * Abstract Items of a TreeModel *****************************************/ -TreeItem::TreeItem(const QList &data, TreeItem *parent) +AbstractTreeItem::AbstractTreeItem(AbstractTreeItem *parent) : QObject(parent), - itemData(data), - _parentItem(parent), - _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled) + _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled), + _treeItemFlags(0) { } -TreeItem::TreeItem(TreeItem *parent) - : QObject(parent), - itemData(QList()), - _parentItem(parent), - _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled) -{ +bool AbstractTreeItem::newChild(AbstractTreeItem *item) { + int newRow = childCount(); + emit beginAppendChilds(newRow, newRow); + _childItems.append(item); + emit endAppendChilds(); + return true; } -TreeItem::~TreeItem() { - qDeleteAll(_childItems); -} +bool AbstractTreeItem::newChilds(const QList &items) { + if(items.isEmpty()) + return false; + + int nextRow = childCount(); + int lastRow = nextRow + items.count() - 1; -uint TreeItem::id() const { - return (uint)this; + emit beginAppendChilds(nextRow, lastRow); + _childItems << items; + emit endAppendChilds(); + + return true; } -void TreeItem::appendChild(TreeItem *item) { - _childItems.append(item); - _childHash[item->id()] = item; - connect(item, SIGNAL(destroyed()), - this, SLOT(childDestroyed())); +bool AbstractTreeItem::removeChild(int row) { + if(row < 0 || childCount() <= row) + return false; + + child(row)->removeAllChilds(); + emit beginRemoveChilds(row, row); + AbstractTreeItem *treeitem = _childItems.takeAt(row); + delete treeitem; + emit endRemoveChilds(); + + checkForDeletion(); + + return true; } -void TreeItem::removeChild(int row) { - if(row >= _childItems.size()) +void AbstractTreeItem::removeAllChilds() { + const int numChilds = childCount(); + + if(numChilds == 0) return; - TreeItem *treeitem = _childItems.value(row); - _childItems.removeAt(row); - _childHash.remove(_childHash.key(treeitem)); + + AbstractTreeItem *child; + + QList::iterator childIter; + + childIter = _childItems.begin(); + while(childIter != _childItems.end()) { + child = *childIter; + child->setTreeItemFlags(0); // disable self deletion, as this would only fuck up consitency and the child gets deleted anyways + child->removeAllChilds(); + childIter++; + } + + emit beginRemoveChilds(0, numChilds - 1); + childIter = _childItems.begin(); + while(childIter != _childItems.end()) { + child = *childIter; + childIter = _childItems.erase(childIter); + delete child; + } + emit endRemoveChilds(); + + checkForDeletion(); } -TreeItem *TreeItem::child(int row) const { - if(row < _childItems.size()) - return _childItems.value(row); - else - return 0; +bool AbstractTreeItem::reParent(AbstractTreeItem *newParent) { + // currently we support only re parenting if the child that's about to be + // adopted does not have any children itself. + if(childCount() != 0) { + qDebug() << "AbstractTreeItem::reParent(): cannot reparent" << this << "with children."; + return false; + } + + int oldRow = row(); + if(oldRow == -1) + return false; + + emit parent()->beginRemoveChilds(oldRow, oldRow); + parent()->_childItems.removeAt(oldRow); + emit parent()->endRemoveChilds(); + + parent()->checkForDeletion(); + + setParent(newParent); + + bool success = newParent->newChild(this); + if(!success) + qWarning() << "AbstractTreeItem::reParent(): failed to attach to new parent after removing from old parent! this:" << this << "new parent:" << newParent; + + return success; } -TreeItem *TreeItem::childById(const uint &id) const { - if(_childHash.contains(id)) - return _childHash.value(id); +AbstractTreeItem *AbstractTreeItem::child(int row) const { + if(childCount() <= row) + return 0; else + return _childItems[row]; +} + +int AbstractTreeItem::childCount(int column) const { + if(column > 0) return 0; + else + return _childItems.count(); } -int TreeItem::childCount() const { - return _childItems.count(); +int AbstractTreeItem::row() const { + if(!parent()) { + qWarning() << "AbstractTreeItem::row():" << this << "has no parent AbstractTreeItem as it's parent! parent is" << QObject::parent(); + return -1; + } + + int row_ = parent()->_childItems.indexOf(const_cast(this)); + if(row_ == -1) + qWarning() << "AbstractTreeItem::row():" << this << "is not in the child list of" << QObject::parent(); + return row_; } -int TreeItem::row() const { - if(_parentItem) - return _parentItem->_childItems.indexOf(const_cast(this)); - else - return 0; +void AbstractTreeItem::dumpChildList() { + qDebug() << "==== Childlist for Item:" << this << "===="; + if(childCount() > 0) { + AbstractTreeItem *child; + QList::const_iterator childIter = _childItems.constBegin(); + while(childIter != _childItems.constEnd()) { + child = *childIter; + qDebug() << "Row:" << child->row() << child << child->data(0, Qt::DisplayRole); + childIter++; + } + } + qDebug() << "==== End Of Childlist ===="; } -TreeItem *TreeItem::parent() { - return _parentItem; +/***************************************** + * SimpleTreeItem + *****************************************/ +SimpleTreeItem::SimpleTreeItem(const QList &data, AbstractTreeItem *parent) + : AbstractTreeItem(parent), + _itemData(data) +{ } -int TreeItem::columnCount() const { - return itemData.count(); +SimpleTreeItem::~SimpleTreeItem() { } -QVariant TreeItem::data(int column, int role) const { - if(role == Qt::DisplayRole && column < itemData.count()) - return itemData[column]; - else +QVariant SimpleTreeItem::data(int column, int role) const { + if(column >= columnCount() || role != Qt::DisplayRole) return QVariant(); + else + return _itemData[column]; +} + +bool SimpleTreeItem::setData(int column, const QVariant &value, int role) { + if(column > columnCount() || role != Qt::DisplayRole) + return false; + + if(column == columnCount()) + _itemData.append(value); + else + _itemData[column] = value; + + emit dataChanged(column); + return true; +} + +int SimpleTreeItem::columnCount() const { + return _itemData.count(); } -Qt::ItemFlags TreeItem::flags() const { - return _flags; +/***************************************** + * PropertyMapItem + *****************************************/ +PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent) + : AbstractTreeItem(parent), + _propertyOrder(propertyOrder) +{ } -void TreeItem::setFlags(Qt::ItemFlags flags) { - _flags = flags; +PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent) + : AbstractTreeItem(parent), + _propertyOrder(QStringList()) +{ } -void TreeItem::childDestroyed() { - TreeItem *item = static_cast(sender()); - removeChild(item->row()); + +PropertyMapItem::~PropertyMapItem() { } +QVariant PropertyMapItem::data(int column, int role) const { + if(column >= columnCount()) + return QVariant(); + + switch(role) { + case Qt::ToolTipRole: + return toolTip(column); + case Qt::DisplayRole: + case TreeModel::SortRole: // fallthrough, since SortRole should default to DisplayRole + return property(_propertyOrder[column].toAscii()); + default: + return QVariant(); + } + +} + +bool PropertyMapItem::setData(int column, const QVariant &value, int role) { + if(column >= columnCount() || role != Qt::DisplayRole) + return false; + + emit dataChanged(column); + return setProperty(_propertyOrder[column].toAscii(), value); +} + +int PropertyMapItem::columnCount() const { + return _propertyOrder.count(); +} + +void PropertyMapItem::appendProperty(const QString &property) { + _propertyOrder << property; +} + /***************************************** * TreeModel *****************************************/ TreeModel::TreeModel(const QList &data, QObject *parent) - : QAbstractItemModel(parent) + : QAbstractItemModel(parent), + _childStatus(QModelIndex(), 0, 0, 0), + _aboutToRemoveOrInsert(false) { - rootItem = new TreeItem(data, 0); + rootItem = new SimpleTreeItem(data, 0); + connectItem(rootItem); + + if(Global::parser.isSet("debugmodel")) { + connect(this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(debug_rowsAboutToBeInserted(const QModelIndex &, int, int))); + connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(debug_rowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(debug_rowsInserted(const QModelIndex &, int, int))); + connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(debug_rowsRemoved(const QModelIndex &, int, int))); + connect(this, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(debug_dataChanged(const QModelIndex &, const QModelIndex &))); + } } TreeModel::~TreeModel() { @@ -134,42 +290,43 @@ QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) con if(!hasIndex(row, column, parent)) return QModelIndex(); - TreeItem *parentItem; + AbstractTreeItem *parentItem; if(!parent.isValid()) parentItem = rootItem; else - parentItem = static_cast(parent.internalPointer()); + parentItem = static_cast(parent.internalPointer()); - TreeItem *childItem = parentItem->child(row); + AbstractTreeItem *childItem = parentItem->child(row); + if(childItem) return createIndex(row, column, childItem); else return QModelIndex(); } -QModelIndex TreeModel::indexById(uint id, const QModelIndex &parent) const { - TreeItem *parentItem; - - if(!parent.isValid()) - parentItem = rootItem; - else - parentItem = static_cast(parent.internalPointer()); +QModelIndex TreeModel::indexByItem(AbstractTreeItem *item) const { + if(item == 0) { + qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer"; + return QModelIndex(); + } - TreeItem *childItem = parentItem->childById(id); - if(childItem) - return createIndex(childItem->row(), 0, childItem); - else + if(item == rootItem) return QModelIndex(); + else + return createIndex(item->row(), 0, item); } QModelIndex TreeModel::parent(const QModelIndex &index) const { - if(!index.isValid()) + if(!index.isValid()) { + qWarning() << "TreeModel::parent(): has been asked for the rootItems Parent!"; return QModelIndex(); + } - TreeItem *childItem = static_cast(index.internalPointer()); - TreeItem *parentItem = static_cast(childItem->parent()); - + AbstractTreeItem *childItem = static_cast(index.internalPointer()); + AbstractTreeItem *parentItem = childItem->parent(); + + Q_ASSERT(parentItem); if(parentItem == rootItem) return QModelIndex(); @@ -177,37 +334,54 @@ QModelIndex TreeModel::parent(const QModelIndex &index) const { } int TreeModel::rowCount(const QModelIndex &parent) const { - TreeItem *parentItem; + AbstractTreeItem *parentItem; if(!parent.isValid()) parentItem = rootItem; else - parentItem = static_cast(parent.internalPointer()); - - return parentItem->childCount(); + parentItem = static_cast(parent.internalPointer()); + + return parentItem->childCount(parent.column()); } int TreeModel::columnCount(const QModelIndex &parent) const { - if(parent.isValid()) - return static_cast(parent.internalPointer())->columnCount(); - else - return rootItem->columnCount(); + Q_UNUSED(parent) + return rootItem->columnCount(); + // since there the Qt Views don't draw more columns than the header has columns + // we can be lazy and simply return the count of header columns + // actually this gives us more freedom cause we don't have to ensure that a rows parent + // has equal or more columns than that row + +// AbstractTreeItem *parentItem; +// if(!parent.isValid()) +// parentItem = rootItem; +// else +// parentItem = static_cast(parent.internalPointer()); +// return parentItem->columnCount(); } QVariant TreeModel::data(const QModelIndex &index, int role) const { if(!index.isValid()) return QVariant(); - TreeItem *item = static_cast(index.internalPointer()); + AbstractTreeItem *item = static_cast(index.internalPointer()); return item->data(index.column(), role); } -Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const { - TreeItem *item; +bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(!index.isValid()) - item = rootItem; - else - item = static_cast(index.internalPointer()); - return item->flags(); + return false; + + AbstractTreeItem *item = static_cast(index.internalPointer()); + return item->setData(index.column(), value, role); +} + +Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const { + if(!index.isValid()) { + return rootItem->flags() & Qt::ItemIsDropEnabled; + } else { + AbstractTreeItem *item = static_cast(index.internalPointer()); + return item->flags(); + } } QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const { @@ -217,48 +391,167 @@ QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int rol return QVariant(); } -bool TreeModel::removeRow(int row, const QModelIndex &parent) { - if(row > rowCount(parent)) - return false; +void TreeModel::itemDataChanged(int column) { + AbstractTreeItem *item = qobject_cast(sender()); + QModelIndex leftIndex, rightIndex; + + if(item == rootItem) + return; + + if(column == -1) { + leftIndex = createIndex(item->row(), 0, item); + rightIndex = createIndex(item->row(), item->columnCount() - 1, item); + } else { + leftIndex = createIndex(item->row(), column, item); + rightIndex = leftIndex; + } + + emit dataChanged(leftIndex, rightIndex); +} + +void TreeModel::connectItem(AbstractTreeItem *item) { + connect(item, SIGNAL(dataChanged(int)), + this, SLOT(itemDataChanged(int))); - TreeItem *item; - if(!parent.isValid()) - item = rootItem; - else - item = static_cast(parent.internalPointer()); + connect(item, SIGNAL(beginAppendChilds(int, int)), + this, SLOT(beginAppendChilds(int, int))); + connect(item, SIGNAL(endAppendChilds()), + this, SLOT(endAppendChilds())); - beginRemoveRows(parent, row, row); - item->removeChild(row); - endRemoveRows(); - return true; + connect(item, SIGNAL(beginRemoveChilds(int, int)), + this, SLOT(beginRemoveChilds(int, int))); + connect(item, SIGNAL(endRemoveChilds()), + this, SLOT(endRemoveChilds())); } -bool TreeModel::removeRows(int row, int count, const QModelIndex &parent) { - // check if there is work to be done - if(count == 0) - return true; +void TreeModel::beginAppendChilds(int firstRow, int lastRow) { + AbstractTreeItem *parentItem = qobject_cast(sender()); + if(!parentItem) { + qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent"; + return; + } - // out of range check - if(row + count - 1 > rowCount(parent) || row < 0 || count < 0) - return false; - - TreeItem *item; - if(!parent.isValid()) - item = rootItem; - else - item = static_cast(parent.internalPointer()); + QModelIndex parent = indexByItem(parentItem); + Q_ASSERT(!_aboutToRemoveOrInsert); + _aboutToRemoveOrInsert = true; + _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow); + beginInsertRows(parent, firstRow, lastRow); +} + +void TreeModel::endAppendChilds() { + AbstractTreeItem *parentItem = qobject_cast(sender()); + if(!parentItem) { + qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent"; + return; + } + Q_ASSERT(_aboutToRemoveOrInsert); + ChildStatus cs = _childStatus; + QModelIndex parent = indexByItem(parentItem); + Q_ASSERT(cs.parent == parent); + Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1); + + _aboutToRemoveOrInsert = false; + for(int i = cs.start; i <= cs.end; i++) { + connectItem(parentItem->child(i)); + } + endInsertRows(); +} + +void TreeModel::beginRemoveChilds(int firstRow, int lastRow) { + AbstractTreeItem *parentItem = qobject_cast(sender()); + if(!parentItem) { + qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent"; + return; + } + + for(int i = firstRow; i <= lastRow; i++) { + disconnect(parentItem->child(i), 0, this, 0); + } - beginRemoveRows(parent, row, row + count - 1); + // consitency checks + QModelIndex parent = indexByItem(parentItem); + Q_ASSERT(firstRow <= lastRow); + Q_ASSERT(parentItem->childCount() > lastRow); + Q_ASSERT(!_aboutToRemoveOrInsert); + _aboutToRemoveOrInsert = true; + _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow); + + beginRemoveRows(parent, firstRow, lastRow); +} - for(int i = row + count - 1; i >= 0; i--) { - item->removeChild(i); +void TreeModel::endRemoveChilds() { + AbstractTreeItem *parentItem = qobject_cast(sender()); + if(!parentItem) { + qWarning() << "TreeModel::endRemoveChilds(): cannot remove Childs from unknown parent"; + return; } + + // concistency checks + Q_ASSERT(_aboutToRemoveOrInsert); + ChildStatus cs = _childStatus; + QModelIndex parent = indexByItem(parentItem); + Q_ASSERT(cs.parent == parent); + Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1); + _aboutToRemoveOrInsert = false; + endRemoveRows(); - return true; } void TreeModel::clear() { - removeRows(0, rowCount()); - reset(); + rootItem->removeAllChilds(); +} + +void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) { + qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end; +} + +void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { + AbstractTreeItem *parentItem; + parentItem = static_cast(parent.internalPointer()); + if(!parentItem) + parentItem = rootItem; + qDebug() << "debug_rowsAboutToBeRemoved" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end; + + QModelIndex child; + AbstractTreeItem *childItem; + for(int i = end; i >= start; i--) { + child = parent.child(i, 0); + childItem = parentItem->child(i); + Q_ASSERT(childItem); + qDebug() << ">>>" << i << child << child.data().toString(); + } +} + +void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end) { + AbstractTreeItem *parentItem; + parentItem = static_cast(parent.internalPointer()); + if(!parentItem) + parentItem = rootItem; + qDebug() << "debug_rowsInserted:" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end; + + QModelIndex child; + AbstractTreeItem *childItem; + for(int i = start; i <= end; i++) { + child = parent.child(i, 0); + childItem = parentItem->child(i); + Q_ASSERT(childItem); + qDebug() << "<<<" << i << child << child.data().toString(); + } +} + +void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end) { + qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end; +} + +void TreeModel::debug_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { + qDebug() << "debug_dataChanged" << topLeft << bottomRight; + QStringList displayData; + for(int row = topLeft.row(); row <= bottomRight.row(); row++) { + displayData = QStringList(); + for(int column = topLeft.column(); column <= bottomRight.column(); column++) { + displayData << data(topLeft.sibling(row, column), Qt::DisplayRole).toString(); + } + qDebug() << " row:" << row << displayData; + } }