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 connect(this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
317 this, SLOT(debug_rowsAboutToBeInserted(const QModelIndex &, int, int)));
318 connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
319 this, SLOT(debug_rowsAboutToBeRemoved(const QModelIndex &, int, int)));
320 connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
321 this, SLOT(debug_rowsInserted(const QModelIndex &, int, int)));
322 connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
323 this, SLOT(debug_rowsRemoved(const QModelIndex &, int, int)));
327 TreeModel::~TreeModel() {
331 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
332 if(!hasIndex(row, column, parent))
333 return QModelIndex();
335 AbstractTreeItem *parentItem;
337 if(!parent.isValid())
338 parentItem = rootItem;
340 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
342 AbstractTreeItem *childItem = parentItem->child(parent.column(), row);
345 return createIndex(row, column, childItem);
347 return QModelIndex();
350 QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
351 AbstractTreeItem *parentItem;
353 if(!parent.isValid())
354 parentItem = rootItem;
356 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
358 AbstractTreeItem *childItem = parentItem->childById(parent.column(), id);
361 return createIndex(childItem->row(), 0, childItem);
363 return QModelIndex();
366 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item, int column) const {
368 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
369 return QModelIndex();
373 return QModelIndex();
375 return createIndex(item->row(), column, item);
379 QModelIndex TreeModel::parent(const QModelIndex &index) const {
381 return QModelIndex();
383 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
384 AbstractTreeItem *parentItem = static_cast<AbstractTreeItem *>(childItem->parent());
386 if(parentItem == rootItem)
387 return QModelIndex();
389 return createIndex(parentItem->row(), childItem->column(), parentItem);
392 int TreeModel::rowCount(const QModelIndex &parent) const {
393 AbstractTreeItem *parentItem;
394 if(!parent.isValid())
395 parentItem = rootItem;
397 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
399 return parentItem->childCount(parent.column());
402 int TreeModel::columnCount(const QModelIndex &parent) const {
404 // since there the Qt Views don't draw more columns than the header has columns
405 // we can be lazy and simply return the count of header columns
406 // actually this gives us more freedom cause we don't have to ensure that a rows parent
407 // has equal or more columns than that row
409 // if(parent.isValid()) {
410 // AbstractTreeItem *child;
411 // if(child = static_cast<AbstractTreeItem *>(parent.internalPointer())->child(parent.column(), parent.row()))
412 // return child->columnCount();
414 // return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
416 // return rootItem->columnCount();
419 return rootItem->columnCount();
422 QVariant TreeModel::data(const QModelIndex &index, int role) const {
426 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
427 return item->data(index.column(), role);
430 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
434 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
435 return item->setData(index.column(), value, role);
438 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
439 AbstractTreeItem *item;
443 item = static_cast<AbstractTreeItem *>(index.internalPointer());
444 return item->flags();
447 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
448 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
449 return rootItem->data(section, role);
454 void TreeModel::itemDataChanged(int column) {
455 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
456 QModelIndex leftIndex, rightIndex;
462 leftIndex = createIndex(item->row(), 0, item);
463 rightIndex = createIndex(item->row(), item->columnCount()-1, item);
465 leftIndex = createIndex(item->row(), column, item);
466 rightIndex = leftIndex;
469 emit dataChanged(leftIndex, rightIndex);
472 void TreeModel::connectItem(AbstractTreeItem *item) {
473 connect(item, SIGNAL(dataChanged(int)),
474 this, SLOT(itemDataChanged(int)));
476 connect(item, SIGNAL(beginAppendChilds(int, int, int)),
477 this, SLOT(beginAppendChilds(int, int, int)));
478 connect(item, SIGNAL(endAppendChilds()),
479 this, SLOT(endAppendChilds()));
481 connect(item, SIGNAL(beginRemoveChilds(int, int, int)),
482 this, SLOT(beginRemoveChilds(int, int, int)));
483 connect(item, SIGNAL(endRemoveChilds()),
484 this, SLOT(endRemoveChilds()));
487 void TreeModel::beginAppendChilds(int column, int firstRow, int lastRow) {
488 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
490 qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent";
493 QModelIndex parent = indexByItem(parentItem, column);
494 Q_ASSERT(!_aboutToRemoveOrInsert);
496 _aboutToRemoveOrInsert = true;
497 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
498 beginInsertRows(parent, firstRow, lastRow);
501 void TreeModel::endAppendChilds() {
502 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
504 qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent";
507 Q_ASSERT(_aboutToRemoveOrInsert);
508 ChildStatus cs = _childStatus;
509 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
510 Q_ASSERT(cs.parent == parent);
511 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
513 _aboutToRemoveOrInsert = false;
514 for(int i = cs.start; i <= cs.end; i++) {
515 connectItem(parentItem->child(parent.column(), i));
520 void TreeModel::beginRemoveChilds(int column, int firstRow, int lastRow) {
521 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
523 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent";
526 QModelIndex parent = indexByItem(parentItem, column);
527 Q_ASSERT(firstRow <= lastRow);
528 Q_ASSERT(parentItem->childCount(column) > lastRow);
529 Q_ASSERT(!_aboutToRemoveOrInsert);
531 _aboutToRemoveOrInsert = true;
532 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
533 beginRemoveRows(parent, firstRow, lastRow);
536 void TreeModel::endRemoveChilds() {
537 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
539 qWarning() << "TreeModel::endRemoveChilds(): cannot append Childs to unknown parent";
542 Q_ASSERT(_aboutToRemoveOrInsert);
543 ChildStatus cs = _childStatus;
544 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
545 Q_ASSERT(cs.parent == parent);
546 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
548 _aboutToRemoveOrInsert = false;
552 void TreeModel::clear() {
553 rootItem->removeAllChilds();
556 void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) {
557 qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
560 void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) {
561 qDebug() << "debug_rowsAboutToBeRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
563 for(int i = start; i <= end; i++) {
564 child = parent.child(i, 0);
565 qDebug() << " " << child << child.data().toString(); // << static_cast<AbstractTreeItem *>(parent.child(i, 0).internalPointer())->id();
569 void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end) {
570 qDebug() << "debug_rowsInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
572 for(int i = start; i <= end; i++) {
573 child = parent.child(i, 0);
574 qDebug() << " " << child << child.data().toString(); // << static_cast<AbstractTreeItem *>(parent.child(i, 0).internalPointer())->id();
578 void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end) {
579 qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;