1 /***************************************************************************
2 * Copyright (C) 2005-08 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include "treemodel.h"
25 /*****************************************
26 * Abstract Items of a TreeModel
27 *****************************************/
28 AbstractTreeItem::AbstractTreeItem(AbstractTreeItem *parent)
30 _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled)
34 AbstractTreeItem::~AbstractTreeItem() {
37 quint64 AbstractTreeItem::id() const {
41 int AbstractTreeItem::defaultColumn() const {
42 // invalid QModelIndexes aka rootNodes get their Childs stuffed into column -1
50 bool AbstractTreeItem::newChild(int column, AbstractTreeItem *item) {
51 if(column >= columnCount()) {
52 qWarning() << "AbstractTreeItem::newChild() cannot append Child to not existing column!" << this << column;
56 if(!_childItems.contains(column)) {
57 _childItems[column] = QList<AbstractTreeItem *>();
60 // check if a child with that ID is already known
61 Q_ASSERT(childById(item->id()) == 0);
63 int newRow = _childItems[column].count();
64 emit beginAppendChilds(column, newRow, newRow);
65 _childItems[column].append(item);
66 emit endAppendChilds();
71 bool AbstractTreeItem::newChild(AbstractTreeItem *item) {
72 return newChild(defaultColumn(), item);
75 bool AbstractTreeItem::removeChild(int column, int row) {
76 if(!_childItems.contains(column) || row >= childCount(column))
79 emit beginRemoveChilds(column, row, row);
80 AbstractTreeItem *treeitem = _childItems[column].takeAt(row);
81 treeitem->deleteLater();
82 emit endRemoveChilds();
87 bool AbstractTreeItem::removeChild(int row) {
88 return removeChild(defaultColumn(), row);
91 bool AbstractTreeItem::removeChildById(int column, const quint64 &id) {
92 if(!_childItems.contains(column))
95 for(int i = 0; i < _childItems[column].count(); i++) {
96 if(_childItems[column][i]->id() == id)
97 return removeChild(column, i);
102 bool AbstractTreeItem::removeChildById(const quint64 &id) {
103 return removeChildById(defaultColumn(), id);
106 void AbstractTreeItem::removeAllChilds() {
107 AbstractTreeItem *child;
109 QHash<int, QList<AbstractTreeItem *> >::iterator columnIter = _childItems.begin();
110 while(columnIter != _childItems.end()) {
111 if(columnIter->count() > 0) {
112 emit beginRemoveChilds(columnIter.key(), 0, columnIter->count() - 1);
113 QList<AbstractTreeItem *>::iterator childIter = columnIter->begin();
114 while(childIter != columnIter->end()) {
116 // child->removeAllChilds();
117 childIter = columnIter->erase(childIter);
118 child->deleteLater();
120 emit endRemoveChilds();
126 AbstractTreeItem *AbstractTreeItem::child(int column, int row) const {
127 if(!_childItems.contains(column) || _childItems[column].size() <= row)
130 return _childItems[column].value(row);
133 AbstractTreeItem *AbstractTreeItem::child(int row) const {
134 return child(defaultColumn(), row);
137 AbstractTreeItem *AbstractTreeItem::childById(int column, const quint64 &id) const {
138 if(!_childItems.contains(column))
141 for(int i = 0; i < _childItems[column].count(); i++) {
142 if(_childItems[column][i]->id() == id)
143 return _childItems[column][i];
148 AbstractTreeItem *AbstractTreeItem::childById(const quint64 &id) const {
149 return childById(defaultColumn(), id);
152 int AbstractTreeItem::childCount(int column) const {
153 if(!_childItems.contains(column))
156 return _childItems[column].count();
159 int AbstractTreeItem::childCount() const {
160 return childCount(defaultColumn());
163 int AbstractTreeItem::column() const {
167 QHash<int, QList<AbstractTreeItem*> >::const_iterator iter = parent()->_childItems.constBegin();
168 while(iter != parent()->_childItems.constEnd()) {
169 if(iter->contains(const_cast<AbstractTreeItem *>(this)))
174 qWarning() << "AbstractTreeItem::column(): unable to determine the Column of" << this;
175 return parent()->defaultColumn();
178 int AbstractTreeItem::row() const {
182 return parent()->_childItems[column()].indexOf(const_cast<AbstractTreeItem *>(this));
185 AbstractTreeItem *AbstractTreeItem::parent() const {
186 return qobject_cast<AbstractTreeItem *>(QObject::parent());
189 Qt::ItemFlags AbstractTreeItem::flags() const {
193 void AbstractTreeItem::setFlags(Qt::ItemFlags flags) {
197 void AbstractTreeItem::dumpChildList() {
198 AbstractTreeItem *child;
200 qDebug() << "==== Childlist for Item:" << this << id() << "====";
201 QHash<int, QList<AbstractTreeItem *> >::iterator columnIter = _childItems.begin();
202 while(columnIter != _childItems.end()) {
203 if(columnIter->count() > 0) {
204 QList<AbstractTreeItem *>::const_iterator childIter = columnIter->constBegin();
205 while(childIter != columnIter->constEnd()) {
207 qDebug() << "Column:" << columnIter.key() << "Row:" << child->row() << child << child->id() << child->data(0, Qt::DisplayRole);
213 qDebug() << "==== End Of Childlist ====";
216 /*****************************************
218 *****************************************/
219 SimpleTreeItem::SimpleTreeItem(const QList<QVariant> &data, AbstractTreeItem *parent)
220 : AbstractTreeItem(parent),
225 SimpleTreeItem::~SimpleTreeItem() {
228 QVariant SimpleTreeItem::data(int column, int role) const {
229 if(column >= columnCount() || role != Qt::DisplayRole)
232 return _itemData[column];
235 bool SimpleTreeItem::setData(int column, const QVariant &value, int role) {
236 if(column > columnCount() || role != Qt::DisplayRole)
239 if(column == columnCount())
240 _itemData.append(value);
242 _itemData[column] = value;
244 emit dataChanged(column);
248 int SimpleTreeItem::columnCount() const {
249 return _itemData.count();
252 /*****************************************
254 *****************************************/
255 PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent)
256 : AbstractTreeItem(parent),
257 _propertyOrder(propertyOrder)
261 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
262 : AbstractTreeItem(parent),
263 _propertyOrder(QStringList())
268 PropertyMapItem::~PropertyMapItem() {
271 QVariant PropertyMapItem::data(int column, int role) const {
272 if(column >= columnCount())
276 case Qt::ToolTipRole:
277 return toolTip(column);
278 case Qt::DisplayRole:
279 return property(_propertyOrder[column].toAscii());
286 bool PropertyMapItem::setData(int column, const QVariant &value, int role) {
287 if(column >= columnCount() || role != Qt::DisplayRole)
290 emit dataChanged(column);
291 return setProperty(_propertyOrder[column].toAscii(), value);
294 int PropertyMapItem::columnCount() const {
295 return _propertyOrder.count();
298 void PropertyMapItem::appendProperty(const QString &property) {
299 _propertyOrder << property;
304 /*****************************************
306 *****************************************/
307 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
308 : QAbstractItemModel(parent),
309 _childStatus(QModelIndex(), 0, 0, 0),
310 _aboutToRemoveOrInsert(false)
312 rootItem = new SimpleTreeItem(data, 0);
313 connectItem(rootItem);
316 TreeModel::~TreeModel() {
320 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
321 if(!hasIndex(row, column, parent))
322 return QModelIndex();
324 AbstractTreeItem *parentItem;
326 if(!parent.isValid())
327 parentItem = rootItem;
329 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
331 AbstractTreeItem *childItem = parentItem->child(parent.column(), row);
334 return createIndex(row, column, childItem);
336 return QModelIndex();
339 QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
340 AbstractTreeItem *parentItem;
342 if(!parent.isValid())
343 parentItem = rootItem;
345 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
347 AbstractTreeItem *childItem = parentItem->childById(parent.column(), id);
350 return createIndex(childItem->row(), 0, childItem);
352 return QModelIndex();
355 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item, int column) const {
357 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
358 return QModelIndex();
362 return QModelIndex();
364 return createIndex(item->row(), column, item);
368 QModelIndex TreeModel::parent(const QModelIndex &index) const {
370 return QModelIndex();
372 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
373 AbstractTreeItem *parentItem = static_cast<AbstractTreeItem *>(childItem->parent());
375 if(parentItem == rootItem)
376 return QModelIndex();
378 return createIndex(parentItem->row(), childItem->column(), parentItem);
381 int TreeModel::rowCount(const QModelIndex &parent) const {
382 AbstractTreeItem *parentItem;
383 if(!parent.isValid())
384 parentItem = rootItem;
386 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
388 return parentItem->childCount(parent.column());
391 int TreeModel::columnCount(const QModelIndex &parent) const {
393 // since there the Qt Views don't draw more columns than the header has columns
394 // we can be lazy and simply return the count of header columns
395 // actually this gives us more freedom cause we don't have to ensure that a rows parent
396 // has equal or more columns than that row
398 // if(parent.isValid()) {
399 // AbstractTreeItem *child;
400 // if(child = static_cast<AbstractTreeItem *>(parent.internalPointer())->child(parent.column(), parent.row()))
401 // return child->columnCount();
403 // return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
405 // return rootItem->columnCount();
408 return rootItem->columnCount();
411 QVariant TreeModel::data(const QModelIndex &index, int role) const {
415 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
416 return item->data(index.column(), role);
419 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
423 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
424 return item->setData(index.column(), value, role);
427 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
428 AbstractTreeItem *item;
432 item = static_cast<AbstractTreeItem *>(index.internalPointer());
433 return item->flags();
436 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
437 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
438 return rootItem->data(section, role);
443 void TreeModel::itemDataChanged(int column) {
444 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
445 QModelIndex leftIndex, rightIndex;
451 leftIndex = createIndex(item->row(), 0, item);
452 rightIndex = createIndex(item->row(), item->columnCount()-1, item);
454 leftIndex = createIndex(item->row(), column, item);
455 rightIndex = leftIndex;
458 emit dataChanged(leftIndex, rightIndex);
461 void TreeModel::connectItem(AbstractTreeItem *item) {
462 connect(item, SIGNAL(dataChanged(int)),
463 this, SLOT(itemDataChanged(int)));
465 connect(item, SIGNAL(beginAppendChilds(int, int, int)),
466 this, SLOT(beginAppendChilds(int, int, int)));
467 connect(item, SIGNAL(endAppendChilds()),
468 this, SLOT(endAppendChilds()));
470 connect(item, SIGNAL(beginRemoveChilds(int, int, int)),
471 this, SLOT(beginRemoveChilds(int, int, int)));
472 connect(item, SIGNAL(endRemoveChilds()),
473 this, SLOT(endRemoveChilds()));
476 void TreeModel::beginAppendChilds(int column, int firstRow, int lastRow) {
477 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
479 qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent";
482 QModelIndex parent = indexByItem(parentItem, column);
483 Q_ASSERT(!_aboutToRemoveOrInsert);
485 _aboutToRemoveOrInsert = true;
486 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
487 beginInsertRows(parent, firstRow, lastRow);
490 void TreeModel::endAppendChilds() {
491 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
493 qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent";
496 Q_ASSERT(_aboutToRemoveOrInsert);
497 ChildStatus cs = _childStatus;
498 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
499 Q_ASSERT(cs.parent == parent);
500 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
502 _aboutToRemoveOrInsert = false;
503 for(int i = cs.start; i <= cs.end; i++) {
504 connectItem(parentItem->child(parent.column(), i));
509 void TreeModel::beginRemoveChilds(int column, int firstRow, int lastRow) {
510 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
512 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent";
515 QModelIndex parent = indexByItem(parentItem, column);
516 Q_ASSERT(firstRow <= lastRow);
517 Q_ASSERT(parentItem->childCount(column) > lastRow);
518 Q_ASSERT(!_aboutToRemoveOrInsert);
520 _aboutToRemoveOrInsert = true;
521 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
522 beginRemoveRows(parent, firstRow, lastRow);
525 void TreeModel::endRemoveChilds() {
526 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
528 qWarning() << "TreeModel::endRemoveChilds(): cannot append Childs to unknown parent";
531 Q_ASSERT(_aboutToRemoveOrInsert);
532 ChildStatus cs = _childStatus;
533 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
534 Q_ASSERT(cs.parent == parent);
535 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
537 _aboutToRemoveOrInsert = false;
541 void TreeModel::clear() {
542 rootItem->removeAllChilds();