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 qDebug() << "# new Child:" << this << newRow << item << item->id() << item->data(0, Qt::DisplayRole);
65 emit beginAppendChilds(column, newRow, newRow);
66 _childItems[column].append(item);
67 emit endAppendChilds();
72 bool AbstractTreeItem::newChild(AbstractTreeItem *item) {
73 return newChild(defaultColumn(), item);
76 bool AbstractTreeItem::removeChild(int column, int row) {
77 if(!_childItems.contains(column) || row >= childCount(column))
80 AbstractTreeItem *item = _childItems[column][row];
81 qDebug() << "# Remove Child:" << this << row << item << item->id() << item->data(0, Qt::DisplayRole);
82 emit beginRemoveChilds(column, row, row);
83 AbstractTreeItem *treeitem = _childItems[column].takeAt(row);
84 treeitem->deleteLater();
85 emit endRemoveChilds();
90 bool AbstractTreeItem::removeChild(int row) {
91 return removeChild(defaultColumn(), row);
94 bool AbstractTreeItem::removeChildById(int column, const quint64 &id) {
95 if(!_childItems.contains(column))
98 qDebug() << "removeChildyById" << id;
99 for(int i = 0; i < _childItems[column].count(); i++) {
100 if(_childItems[column][i]->id() == id)
101 return removeChild(column, i);
106 bool AbstractTreeItem::removeChildById(const quint64 &id) {
107 return removeChildById(defaultColumn(), id);
110 void AbstractTreeItem::removeAllChilds() {
111 AbstractTreeItem *child;
113 QHash<int, QList<AbstractTreeItem *> >::iterator columnIter = _childItems.begin();
114 while(columnIter != _childItems.end()) {
115 if(columnIter->count() > 0) {
116 emit beginRemoveChilds(columnIter.key(), 0, columnIter->count() - 1);
117 QList<AbstractTreeItem *>::iterator childIter = columnIter->begin();
118 while(childIter != columnIter->end()) {
120 // child->removeAllChilds();
121 childIter = columnIter->erase(childIter);
122 child->deleteLater();
124 emit endRemoveChilds();
130 AbstractTreeItem *AbstractTreeItem::child(int column, int row) const {
131 if(!_childItems.contains(column) || _childItems[column].size() <= row)
134 return _childItems[column].value(row);
137 AbstractTreeItem *AbstractTreeItem::child(int row) const {
138 return child(defaultColumn(), row);
141 AbstractTreeItem *AbstractTreeItem::childById(int column, const quint64 &id) const {
142 if(!_childItems.contains(column))
145 for(int i = 0; i < _childItems[column].count(); i++) {
146 if(_childItems[column][i]->id() == id)
147 return _childItems[column][i];
152 AbstractTreeItem *AbstractTreeItem::childById(const quint64 &id) const {
153 return childById(defaultColumn(), id);
156 int AbstractTreeItem::childCount(int column) const {
157 if(!_childItems.contains(column))
160 return _childItems[column].count();
163 int AbstractTreeItem::childCount() const {
164 return childCount(defaultColumn());
167 int AbstractTreeItem::column() const {
171 QHash<int, QList<AbstractTreeItem*> >::const_iterator iter = parent()->_childItems.constBegin();
172 while(iter != parent()->_childItems.constEnd()) {
173 if(iter->contains(const_cast<AbstractTreeItem *>(this)))
178 qWarning() << "AbstractTreeItem::column(): unable to determine the Column of" << this;
179 return parent()->defaultColumn();
182 int AbstractTreeItem::row() const {
186 return parent()->_childItems[column()].indexOf(const_cast<AbstractTreeItem *>(this));
189 AbstractTreeItem *AbstractTreeItem::parent() const {
190 return qobject_cast<AbstractTreeItem *>(QObject::parent());
193 Qt::ItemFlags AbstractTreeItem::flags() const {
197 void AbstractTreeItem::setFlags(Qt::ItemFlags flags) {
201 void AbstractTreeItem::dumpChildList() {
202 AbstractTreeItem *child;
204 qDebug() << "==== Childlist for Item:" << this << id() << "====";
205 QHash<int, QList<AbstractTreeItem *> >::iterator columnIter = _childItems.begin();
206 while(columnIter != _childItems.end()) {
207 if(columnIter->count() > 0) {
208 QList<AbstractTreeItem *>::const_iterator childIter = columnIter->constBegin();
209 while(childIter != columnIter->constEnd()) {
211 qDebug() << "Column:" << columnIter.key() << "Row:" << child->row() << child << child->id() << child->data(0, Qt::DisplayRole);
217 qDebug() << "==== End Of Childlist ====";
220 /*****************************************
222 *****************************************/
223 SimpleTreeItem::SimpleTreeItem(const QList<QVariant> &data, AbstractTreeItem *parent)
224 : AbstractTreeItem(parent),
229 SimpleTreeItem::~SimpleTreeItem() {
232 QVariant SimpleTreeItem::data(int column, int role) const {
233 if(column >= columnCount() || role != Qt::DisplayRole)
236 return _itemData[column];
239 bool SimpleTreeItem::setData(int column, const QVariant &value, int role) {
240 if(column > columnCount() || role != Qt::DisplayRole)
243 if(column == columnCount())
244 _itemData.append(value);
246 _itemData[column] = value;
248 emit dataChanged(column);
252 int SimpleTreeItem::columnCount() const {
253 return _itemData.count();
256 /*****************************************
258 *****************************************/
259 PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent)
260 : AbstractTreeItem(parent),
261 _propertyOrder(propertyOrder)
265 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
266 : AbstractTreeItem(parent),
267 _propertyOrder(QStringList())
272 PropertyMapItem::~PropertyMapItem() {
275 QVariant PropertyMapItem::data(int column, int role) const {
276 if(column >= columnCount())
280 case Qt::ToolTipRole:
281 return toolTip(column);
282 case Qt::DisplayRole:
283 return property(_propertyOrder[column].toAscii());
290 bool PropertyMapItem::setData(int column, const QVariant &value, int role) {
291 if(column >= columnCount() || role != Qt::DisplayRole)
294 emit dataChanged(column);
295 return setProperty(_propertyOrder[column].toAscii(), value);
298 int PropertyMapItem::columnCount() const {
299 return _propertyOrder.count();
302 void PropertyMapItem::appendProperty(const QString &property) {
303 _propertyOrder << property;
308 /*****************************************
310 *****************************************/
311 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
312 : QAbstractItemModel(parent),
313 _childStatus(QModelIndex(), 0, 0, 0),
314 _aboutToRemoveOrInsert(false)
316 rootItem = new SimpleTreeItem(data, 0);
317 rootItem->setObjectName("rootItem");
318 connectItem(rootItem);
320 connect(this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
321 this, SLOT(debug_rowsAboutToBeInserted(const QModelIndex &, int, int)));
322 connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
323 this, SLOT(debug_rowsAboutToBeRemoved(const QModelIndex &, int, int)));
324 connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
325 this, SLOT(debug_rowsInserted(const QModelIndex &, int, int)));
326 connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
327 this, SLOT(debug_rowsRemoved(const QModelIndex &, int, int)));
330 TreeModel::~TreeModel() {
334 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
335 if(!hasIndex(row, column, parent))
336 return QModelIndex();
338 AbstractTreeItem *parentItem;
340 if(!parent.isValid())
341 parentItem = rootItem;
343 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
345 AbstractTreeItem *childItem = parentItem->child(parent.column(), row);
348 return createIndex(row, column, childItem);
350 return QModelIndex();
353 QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
354 AbstractTreeItem *parentItem;
356 if(!parent.isValid())
357 parentItem = rootItem;
359 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
361 AbstractTreeItem *childItem = parentItem->childById(parent.column(), id);
364 return createIndex(childItem->row(), 0, childItem);
366 return QModelIndex();
369 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item, int column) const {
371 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
372 return QModelIndex();
376 return QModelIndex();
378 return createIndex(item->row(), column, item);
382 QModelIndex TreeModel::parent(const QModelIndex &index) const {
384 return QModelIndex();
386 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
387 AbstractTreeItem *parentItem = static_cast<AbstractTreeItem *>(childItem->parent());
389 if(parentItem == rootItem)
390 return QModelIndex();
392 return createIndex(parentItem->row(), childItem->column(), parentItem);
395 int TreeModel::rowCount(const QModelIndex &parent) const {
396 AbstractTreeItem *parentItem;
397 if(!parent.isValid())
398 parentItem = rootItem;
400 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
402 return parentItem->childCount(parent.column());
405 int TreeModel::columnCount(const QModelIndex &parent) const {
407 // since there the Qt Views don't draw more columns than the header has columns
408 // we can be lazy and simply return the count of header columns
409 // actually this gives us more freedom cause we don't have to ensure that a rows parent
410 // has equal or more columns than that row
412 // if(parent.isValid()) {
413 // AbstractTreeItem *child;
414 // if(child = static_cast<AbstractTreeItem *>(parent.internalPointer())->child(parent.column(), parent.row()))
415 // return child->columnCount();
417 // return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
419 // return rootItem->columnCount();
422 return rootItem->columnCount();
425 QVariant TreeModel::data(const QModelIndex &index, int role) const {
429 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
430 return item->data(index.column(), role);
433 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
437 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
438 return item->setData(index.column(), value, role);
441 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
442 AbstractTreeItem *item;
446 item = static_cast<AbstractTreeItem *>(index.internalPointer());
447 return item->flags();
450 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
451 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
452 return rootItem->data(section, role);
457 void TreeModel::itemDataChanged(int column) {
458 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
459 QModelIndex leftIndex, rightIndex;
465 leftIndex = createIndex(item->row(), 0, item);
466 rightIndex = createIndex(item->row(), item->columnCount()-1, item);
468 leftIndex = createIndex(item->row(), column, item);
469 rightIndex = leftIndex;
472 emit dataChanged(leftIndex, rightIndex);
475 void TreeModel::connectItem(AbstractTreeItem *item) {
476 connect(item, SIGNAL(dataChanged(int)),
477 this, SLOT(itemDataChanged(int)));
479 connect(item, SIGNAL(beginAppendChilds(int, int, int)),
480 this, SLOT(beginAppendChilds(int, int, int)));
481 connect(item, SIGNAL(endAppendChilds()),
482 this, SLOT(endAppendChilds()));
484 connect(item, SIGNAL(beginRemoveChilds(int, int, int)),
485 this, SLOT(beginRemoveChilds(int, int, int)));
486 connect(item, SIGNAL(endRemoveChilds()),
487 this, SLOT(endRemoveChilds()));
490 void TreeModel::beginAppendChilds(int column, int firstRow, int lastRow) {
491 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
493 qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent";
496 QModelIndex parent = indexByItem(parentItem, column);
497 Q_ASSERT(!_aboutToRemoveOrInsert);
499 _aboutToRemoveOrInsert = true;
500 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
501 beginInsertRows(parent, firstRow, lastRow);
504 void TreeModel::endAppendChilds() {
505 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
507 qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent";
510 Q_ASSERT(_aboutToRemoveOrInsert);
511 ChildStatus cs = _childStatus;
512 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
513 Q_ASSERT(cs.parent == parent);
514 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
516 _aboutToRemoveOrInsert = false;
517 for(int i = cs.start; i <= cs.end; i++) {
518 connectItem(parentItem->child(parent.column(), i));
523 void TreeModel::beginRemoveChilds(int column, int firstRow, int lastRow) {
524 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
526 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent";
529 QModelIndex parent = indexByItem(parentItem, column);
530 Q_ASSERT(firstRow <= lastRow);
531 Q_ASSERT(parentItem->childCount(column) > lastRow);
532 Q_ASSERT(!_aboutToRemoveOrInsert);
534 _aboutToRemoveOrInsert = true;
535 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
536 beginRemoveRows(parent, firstRow, lastRow);
539 void TreeModel::endRemoveChilds() {
540 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
542 qWarning() << "TreeModel::endRemoveChilds(): cannot append Childs to unknown parent";
545 Q_ASSERT(_aboutToRemoveOrInsert);
546 ChildStatus cs = _childStatus;
547 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
548 Q_ASSERT(cs.parent == parent);
549 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
551 _aboutToRemoveOrInsert = false;
555 void TreeModel::clear() {
556 rootItem->removeAllChilds();
559 void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) {
560 // qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
563 void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) {
564 AbstractTreeItem *parentItem;
565 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
567 parentItem = rootItem;
568 qDebug() << "#" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
570 if(!_childLists.contains(parentItem))
571 _childLists[parentItem] = QList<AbstractTreeItem *>();
573 QList<AbstractTreeItem *> &childList = _childLists[parentItem];
575 AbstractTreeItem *childItem;
576 for(int i = end; i >= start; i--) {
577 if(childList.count() <= i) {
578 qDebug() << "#! not removing existing item! lastItem:" << childList.count() - 1 << "Delpos:" << i;
581 child = parent.child(i, 0);
582 childItem = parentItem->child(i);
584 childList.removeAt(i);
585 qDebug() << ">>>" << i << child << childItem->id() << child.data().toString();
590 void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end) {
591 AbstractTreeItem *parentItem;
592 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
594 parentItem = rootItem;
595 qDebug() << "#" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
597 if(!_childLists.contains(parentItem))
598 _childLists[parentItem] = QList<AbstractTreeItem *>();
600 QList<AbstractTreeItem *> &childList = _childLists[parentItem];
602 AbstractTreeItem *childItem;
603 for(int i = start; i <= end; i++) {
604 if(childList.count() != i) {
605 qDebug() << "#! not appending at the End! End:" << childList.count() << "Insertpos:" << i;
608 child = parent.child(i, 0);
609 childItem = parentItem->child(i);
611 childList.append(childItem);
612 qDebug() << "<<<" << i << child << childItem->id() << child.data().toString();
617 void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end) {
618 // qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;