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 #include <QCoreApplication>
27 /*****************************************
28 * Abstract Items of a TreeModel
29 *****************************************/
30 AbstractTreeItem::AbstractTreeItem(AbstractTreeItem *parent)
32 _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled)
36 AbstractTreeItem::~AbstractTreeItem() {
39 quint64 AbstractTreeItem::id() const {
43 bool AbstractTreeItem::newChild(AbstractTreeItem *item) {
44 // check if a child with that ID is already known
45 Q_ASSERT(childById(item->id()) == 0);
47 int newRow = childCount();
48 emit beginAppendChilds(newRow, newRow);
49 _childItems.append(item);
50 emit endAppendChilds();
55 bool AbstractTreeItem::newChilds(const QList<AbstractTreeItem *> &items) {
59 QList<AbstractTreeItem *>::const_iterator itemIter = items.constBegin();
60 AbstractTreeItem *item;
61 while(itemIter != items.constEnd()) {
63 if(childById(item->id()) != 0) {
64 qWarning() << "AbstractTreeItem::newChilds(): received child that is already attached" << item << item->id();
70 int nextRow = childCount();
71 int lastRow = nextRow + items.count() - 1;
73 emit beginAppendChilds(nextRow, lastRow);
74 itemIter = items.constBegin();
75 while(itemIter != items.constEnd()) {
76 _childItems.append(*itemIter);
79 emit endAppendChilds();
84 bool AbstractTreeItem::removeChild(int row) {
85 if(childCount() <= row)
88 child(row)->removeAllChilds();
89 emit beginRemoveChilds(row, row);
90 AbstractTreeItem *treeitem = _childItems.takeAt(row);
91 treeitem->deleteLater();
92 emit endRemoveChilds();
97 bool AbstractTreeItem::removeChildById(const quint64 &id) {
98 const int numChilds = childCount();
100 for(int i = 0; i < numChilds; i++) {
101 if(_childItems[i]->id() == id)
102 return removeChild(i);
108 void AbstractTreeItem::removeAllChilds() {
109 const int numChilds = childCount();
114 AbstractTreeItem *child;
116 QList<AbstractTreeItem *>::iterator childIter;
118 childIter = _childItems.begin();
119 while(childIter != _childItems.end()) {
121 child->removeAllChilds();
125 emit beginRemoveChilds(0, numChilds - 1);
126 childIter = _childItems.begin();
127 while(childIter != _childItems.end()) {
129 childIter = _childItems.erase(childIter);
130 child->deleteLater();
132 emit endRemoveChilds();
135 bool AbstractTreeItem::reParent(AbstractTreeItem *newParent) {
136 // currently we support only re parenting if the child that's about to be
137 // adopted does not have any children itself.
138 if(childCount() != 0) {
139 qDebug() << "AbstractTreeItem::reParent(): cannot reparent" << this << "with children.";
147 emit parent()->beginRemoveChilds(oldRow, oldRow);
148 parent()->_childItems.removeAt(oldRow);
149 emit parent()->endRemoveChilds();
151 setParent(newParent);
153 bool success = newParent->newChild(this);
155 qWarning() << "AbstractTreeItem::reParent(): failed to attach to new parent after removing from old parent! this:" << this << "new parent:" << newParent;
160 AbstractTreeItem *AbstractTreeItem::child(int row) const {
161 if(childCount() <= row)
164 return _childItems[row];
167 AbstractTreeItem *AbstractTreeItem::childById(const quint64 &id) const {
168 const int numChilds = childCount();
169 for(int i = 0; i < numChilds; i++) {
170 if(_childItems[i]->id() == id)
171 return _childItems[i];
176 int AbstractTreeItem::childCount(int column) const {
180 return _childItems.count();
183 int AbstractTreeItem::row() const {
185 qWarning() << "AbstractTreeItem::row():" << this << "has no parent AbstractTreeItem as it's parent! parent is" << QObject::parent();
189 int row_ = parent()->_childItems.indexOf(const_cast<AbstractTreeItem *>(this));
191 qWarning() << "AbstractTreeItem::row():" << this << "is not in the child list of" << QObject::parent();
195 AbstractTreeItem *AbstractTreeItem::parent() const {
196 return qobject_cast<AbstractTreeItem *>(QObject::parent());
199 Qt::ItemFlags AbstractTreeItem::flags() const {
203 void AbstractTreeItem::setFlags(Qt::ItemFlags flags) {
207 void AbstractTreeItem::dumpChildList() {
208 qDebug() << "==== Childlist for Item:" << this << id() << "====";
209 if(childCount() > 0) {
210 AbstractTreeItem *child;
211 QList<AbstractTreeItem *>::const_iterator childIter = _childItems.constBegin();
212 while(childIter != _childItems.constEnd()) {
214 qDebug() << "Row:" << child->row() << child << child->id() << child->data(0, Qt::DisplayRole);
218 qDebug() << "==== End Of Childlist ====";
221 /*****************************************
223 *****************************************/
224 SimpleTreeItem::SimpleTreeItem(const QList<QVariant> &data, AbstractTreeItem *parent)
225 : AbstractTreeItem(parent),
230 SimpleTreeItem::~SimpleTreeItem() {
233 QVariant SimpleTreeItem::data(int column, int role) const {
234 if(column >= columnCount() || role != Qt::DisplayRole)
237 return _itemData[column];
240 bool SimpleTreeItem::setData(int column, const QVariant &value, int role) {
241 if(column > columnCount() || role != Qt::DisplayRole)
244 if(column == columnCount())
245 _itemData.append(value);
247 _itemData[column] = value;
249 emit dataChanged(column);
253 int SimpleTreeItem::columnCount() const {
254 return _itemData.count();
257 /*****************************************
259 *****************************************/
260 PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent)
261 : AbstractTreeItem(parent),
262 _propertyOrder(propertyOrder)
266 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
267 : AbstractTreeItem(parent),
268 _propertyOrder(QStringList())
273 PropertyMapItem::~PropertyMapItem() {
276 QVariant PropertyMapItem::data(int column, int role) const {
277 if(column >= columnCount())
281 case Qt::ToolTipRole:
282 return toolTip(column);
283 case Qt::DisplayRole:
284 case TreeModel::SortRole: // fallthrough, since SortRole should default to DisplayRole
285 return property(_propertyOrder[column].toAscii());
292 bool PropertyMapItem::setData(int column, const QVariant &value, int role) {
293 if(column >= columnCount() || role != Qt::DisplayRole)
296 emit dataChanged(column);
297 return setProperty(_propertyOrder[column].toAscii(), value);
300 int PropertyMapItem::columnCount() const {
301 return _propertyOrder.count();
304 void PropertyMapItem::appendProperty(const QString &property) {
305 _propertyOrder << property;
310 /*****************************************
312 *****************************************/
313 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
314 : QAbstractItemModel(parent),
315 _childStatus(QModelIndex(), 0, 0, 0),
316 _aboutToRemoveOrInsert(false)
318 rootItem = new SimpleTreeItem(data, 0);
319 connectItem(rootItem);
321 if(Global::parser.isSet("debugmodel")) {
322 connect(this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
323 this, SLOT(debug_rowsAboutToBeInserted(const QModelIndex &, int, int)));
324 connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
325 this, SLOT(debug_rowsAboutToBeRemoved(const QModelIndex &, int, int)));
326 connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
327 this, SLOT(debug_rowsInserted(const QModelIndex &, int, int)));
328 connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
329 this, SLOT(debug_rowsRemoved(const QModelIndex &, int, int)));
330 connect(this, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
331 this, SLOT(debug_dataChanged(const QModelIndex &, const QModelIndex &)));
335 TreeModel::~TreeModel() {
339 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
340 if(!hasIndex(row, column, parent))
341 return QModelIndex();
343 AbstractTreeItem *parentItem;
345 if(!parent.isValid())
346 parentItem = rootItem;
348 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
350 AbstractTreeItem *childItem = parentItem->child(row);
353 return createIndex(row, column, childItem);
355 return QModelIndex();
358 QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
359 AbstractTreeItem *parentItem;
361 if(!parent.isValid())
362 parentItem = rootItem;
364 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
366 AbstractTreeItem *childItem = parentItem->childById(id);
369 return createIndex(childItem->row(), 0, childItem);
371 return QModelIndex();
374 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item) const {
376 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
377 return QModelIndex();
381 return QModelIndex();
383 return createIndex(item->row(), 0, item);
386 QModelIndex TreeModel::parent(const QModelIndex &index) const {
387 if(!index.isValid()) {
388 qWarning() << "TreeModel::parent(): has been asked for the rootItems Parent!";
389 return QModelIndex();
392 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
393 AbstractTreeItem *parentItem = childItem->parent();
395 Q_ASSERT(parentItem);
396 if(parentItem == rootItem)
397 return QModelIndex();
399 return createIndex(parentItem->row(), 0, parentItem);
402 int TreeModel::rowCount(const QModelIndex &parent) const {
403 AbstractTreeItem *parentItem;
404 if(!parent.isValid())
405 parentItem = rootItem;
407 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
409 return parentItem->childCount(parent.column());
412 int TreeModel::columnCount(const QModelIndex &parent) const {
414 return rootItem->columnCount();
415 // since there the Qt Views don't draw more columns than the header has columns
416 // we can be lazy and simply return the count of header columns
417 // actually this gives us more freedom cause we don't have to ensure that a rows parent
418 // has equal or more columns than that row
420 // AbstractTreeItem *parentItem;
421 // if(!parent.isValid())
422 // parentItem = rootItem;
424 // parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
425 // return parentItem->columnCount();
428 QVariant TreeModel::data(const QModelIndex &index, int role) const {
432 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
433 return item->data(index.column(), role);
436 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
440 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
441 return item->setData(index.column(), value, role);
444 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
445 if(!index.isValid()) {
446 return rootItem->flags() & Qt::ItemIsDropEnabled;
448 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
449 return item->flags();
453 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
454 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
455 return rootItem->data(section, role);
460 void TreeModel::itemDataChanged(int column) {
461 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
462 QModelIndex leftIndex, rightIndex;
468 leftIndex = createIndex(item->row(), 0, item);
469 rightIndex = createIndex(item->row(), item->columnCount() - 1, item);
471 leftIndex = createIndex(item->row(), column, item);
472 rightIndex = leftIndex;
475 emit dataChanged(leftIndex, rightIndex);
478 void TreeModel::connectItem(AbstractTreeItem *item) {
479 connect(item, SIGNAL(dataChanged(int)),
480 this, SLOT(itemDataChanged(int)));
482 connect(item, SIGNAL(beginAppendChilds(int, int)),
483 this, SLOT(beginAppendChilds(int, int)));
484 connect(item, SIGNAL(endAppendChilds()),
485 this, SLOT(endAppendChilds()));
487 connect(item, SIGNAL(beginRemoveChilds(int, int)),
488 this, SLOT(beginRemoveChilds(int, int)));
489 connect(item, SIGNAL(endRemoveChilds()),
490 this, SLOT(endRemoveChilds()));
493 void TreeModel::beginAppendChilds(int firstRow, int lastRow) {
494 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
496 qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent";
500 QModelIndex parent = indexByItem(parentItem);
501 Q_ASSERT(!_aboutToRemoveOrInsert);
503 _aboutToRemoveOrInsert = true;
504 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
505 beginInsertRows(parent, firstRow, lastRow);
508 void TreeModel::endAppendChilds() {
509 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
511 qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent";
514 Q_ASSERT(_aboutToRemoveOrInsert);
515 ChildStatus cs = _childStatus;
516 QModelIndex parent = indexByItem(parentItem);
517 Q_ASSERT(cs.parent == parent);
518 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
520 _aboutToRemoveOrInsert = false;
521 for(int i = cs.start; i <= cs.end; i++) {
522 connectItem(parentItem->child(i));
527 void TreeModel::beginRemoveChilds(int firstRow, int lastRow) {
528 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
530 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent";
534 for(int i = firstRow; i <= lastRow; i++) {
535 disconnect(parentItem->child(i), 0, this, 0);
539 QModelIndex parent = indexByItem(parentItem);
540 Q_ASSERT(firstRow <= lastRow);
541 Q_ASSERT(parentItem->childCount() > lastRow);
542 Q_ASSERT(!_aboutToRemoveOrInsert);
543 _aboutToRemoveOrInsert = true;
544 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
546 beginRemoveRows(parent, firstRow, lastRow);
549 void TreeModel::endRemoveChilds() {
550 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
552 qWarning() << "TreeModel::endRemoveChilds(): cannot remove Childs from unknown parent";
556 // concistency checks
557 Q_ASSERT(_aboutToRemoveOrInsert);
558 ChildStatus cs = _childStatus;
559 QModelIndex parent = indexByItem(parentItem);
560 Q_ASSERT(cs.parent == parent);
561 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
562 _aboutToRemoveOrInsert = false;
567 void TreeModel::clear() {
568 rootItem->removeAllChilds();
571 void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) {
572 qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
575 void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) {
576 AbstractTreeItem *parentItem;
577 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
579 parentItem = rootItem;
580 qDebug() << "debug_rowsAboutToBeRemoved" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
583 AbstractTreeItem *childItem;
584 for(int i = end; i >= start; i--) {
585 child = parent.child(i, 0);
586 childItem = parentItem->child(i);
588 qDebug() << ">>>" << i << child << childItem->id() << child.data().toString();
592 void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end) {
593 AbstractTreeItem *parentItem;
594 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
596 parentItem = rootItem;
597 qDebug() << "debug_rowsInserted:" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
600 AbstractTreeItem *childItem;
601 for(int i = start; i <= end; i++) {
602 child = parent.child(i, 0);
603 childItem = parentItem->child(i);
605 qDebug() << "<<<" << i << child << childItem->id() << child.data().toString();
609 void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end) {
610 qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
613 void TreeModel::debug_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) {
614 qDebug() << "debug_dataChanged" << topLeft << bottomRight;
615 QStringList displayData;
616 for(int row = topLeft.row(); row <= bottomRight.row(); row++) {
617 displayData = QStringList();
618 for(int column = topLeft.column(); column <= bottomRight.column(); column++) {
619 displayData << data(topLeft.sibling(row, column), Qt::DisplayRole).toString();
621 qDebug() << " row:" << row << displayData;