1 /***************************************************************************
2 * Copyright (C) 2005-2013 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 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include "treemodel.h"
23 #include <QCoreApplication>
28 class RemoveChildLaterEvent : public QEvent
31 RemoveChildLaterEvent(AbstractTreeItem *child) : QEvent(QEvent::User), _child(child) {};
32 inline AbstractTreeItem *child() { return _child; }
34 AbstractTreeItem *_child;
38 /*****************************************
39 * Abstract Items of a TreeModel
40 *****************************************/
41 AbstractTreeItem::AbstractTreeItem(AbstractTreeItem *parent)
43 _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled),
49 bool AbstractTreeItem::newChild(AbstractTreeItem *item)
51 int newRow = childCount();
52 emit beginAppendChilds(newRow, newRow);
53 _childItems.append(item);
54 emit endAppendChilds();
59 bool AbstractTreeItem::newChilds(const QList<AbstractTreeItem *> &items)
64 int nextRow = childCount();
65 int lastRow = nextRow + items.count() - 1;
67 emit beginAppendChilds(nextRow, lastRow);
69 emit endAppendChilds();
75 bool AbstractTreeItem::removeChild(int row)
77 if (row < 0 || childCount() <= row)
80 child(row)->removeAllChilds();
81 emit beginRemoveChilds(row, row);
82 AbstractTreeItem *treeitem = _childItems.takeAt(row);
84 emit endRemoveChilds();
92 void AbstractTreeItem::removeAllChilds()
94 const int numChilds = childCount();
99 AbstractTreeItem *child;
101 QList<AbstractTreeItem *>::iterator childIter;
103 childIter = _childItems.begin();
104 while (childIter != _childItems.end()) {
106 child->setTreeItemFlags(0); // disable self deletion, as this would only fuck up consitency and the child gets deleted anyways
107 child->removeAllChilds();
111 emit beginRemoveChilds(0, numChilds - 1);
112 childIter = _childItems.begin();
113 while (childIter != _childItems.end()) {
115 childIter = _childItems.erase(childIter);
118 emit endRemoveChilds();
124 void AbstractTreeItem::removeChildLater(AbstractTreeItem *child)
127 QCoreApplication::postEvent(this, new RemoveChildLaterEvent(child));
131 void AbstractTreeItem::customEvent(QEvent *event)
133 if (event->type() != QEvent::User)
138 RemoveChildLaterEvent *removeEvent = static_cast<RemoveChildLaterEvent *>(event);
139 int childRow = _childItems.indexOf(removeEvent->child());
143 // since we are called asynchronously we have to recheck if the item in question still has no childs
144 if (removeEvent->child()->childCount())
147 removeChild(childRow);
151 bool AbstractTreeItem::reParent(AbstractTreeItem *newParent)
153 // currently we support only re parenting if the child that's about to be
154 // adopted does not have any children itself.
155 if (childCount() != 0) {
156 qDebug() << "AbstractTreeItem::reParent(): cannot reparent" << this << "with children.";
164 emit parent()->beginRemoveChilds(oldRow, oldRow);
165 parent()->_childItems.removeAt(oldRow);
166 emit parent()->endRemoveChilds();
168 AbstractTreeItem *oldParent = parent();
169 setParent(newParent);
171 bool success = newParent->newChild(this);
173 qWarning() << "AbstractTreeItem::reParent(): failed to attach to new parent after removing from old parent! this:" << this << "new parent:" << newParent;
176 oldParent->checkForDeletion();
182 AbstractTreeItem *AbstractTreeItem::child(int row) const
184 if (childCount() <= row)
187 return _childItems[row];
191 int AbstractTreeItem::childCount(int column) const
196 return _childItems.count();
200 int AbstractTreeItem::row() const
203 qWarning() << "AbstractTreeItem::row():" << this << "has no parent AbstractTreeItem as it's parent! parent is" << QObject::parent();
207 int row_ = parent()->_childItems.indexOf(const_cast<AbstractTreeItem *>(this));
209 qWarning() << "AbstractTreeItem::row():" << this << "is not in the child list of" << QObject::parent();
214 void AbstractTreeItem::dumpChildList()
216 qDebug() << "==== Childlist for Item:" << this << "====";
217 if (childCount() > 0) {
218 AbstractTreeItem *child;
219 QList<AbstractTreeItem *>::const_iterator childIter = _childItems.constBegin();
220 while (childIter != _childItems.constEnd()) {
222 qDebug() << "Row:" << child->row() << child << child->data(0, Qt::DisplayRole);
226 qDebug() << "==== End Of Childlist ====";
230 /*****************************************
232 *****************************************/
233 SimpleTreeItem::SimpleTreeItem(const QList<QVariant> &data, AbstractTreeItem *parent)
234 : AbstractTreeItem(parent),
240 SimpleTreeItem::~SimpleTreeItem()
245 QVariant SimpleTreeItem::data(int column, int role) const
247 if (column >= columnCount() || role != Qt::DisplayRole)
250 return _itemData[column];
254 bool SimpleTreeItem::setData(int column, const QVariant &value, int role)
256 if (column > columnCount() || role != Qt::DisplayRole)
259 if (column == columnCount())
260 _itemData.append(value);
262 _itemData[column] = value;
264 emit dataChanged(column);
269 int SimpleTreeItem::columnCount() const
271 return _itemData.count();
275 /*****************************************
277 *****************************************/
278 PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent)
279 : AbstractTreeItem(parent),
280 _propertyOrder(propertyOrder)
285 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
286 : AbstractTreeItem(parent),
287 _propertyOrder(QStringList())
292 PropertyMapItem::~PropertyMapItem()
297 QVariant PropertyMapItem::data(int column, int role) const
299 if (column >= columnCount())
303 case Qt::ToolTipRole:
304 return toolTip(column);
305 case Qt::DisplayRole:
306 case TreeModel::SortRole: // fallthrough, since SortRole should default to DisplayRole
307 return property(_propertyOrder[column].toAscii());
314 bool PropertyMapItem::setData(int column, const QVariant &value, int role)
316 if (column >= columnCount() || role != Qt::DisplayRole)
319 emit dataChanged(column);
320 return setProperty(_propertyOrder[column].toAscii(), value);
324 int PropertyMapItem::columnCount() const
326 return _propertyOrder.count();
330 void PropertyMapItem::appendProperty(const QString &property)
332 _propertyOrder << property;
336 /*****************************************
338 *****************************************/
339 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
340 : QAbstractItemModel(parent),
341 _childStatus(QModelIndex(), 0, 0, 0),
342 _aboutToRemoveOrInsert(false)
344 rootItem = new SimpleTreeItem(data, 0);
345 connectItem(rootItem);
347 if (Quassel::isOptionSet("debugmodel")) {
348 connect(this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
349 this, SLOT(debug_rowsAboutToBeInserted(const QModelIndex &, int, int)));
350 connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
351 this, SLOT(debug_rowsAboutToBeRemoved(const QModelIndex &, int, int)));
352 connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
353 this, SLOT(debug_rowsInserted(const QModelIndex &, int, int)));
354 connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
355 this, SLOT(debug_rowsRemoved(const QModelIndex &, int, int)));
356 connect(this, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
357 this, SLOT(debug_dataChanged(const QModelIndex &, const QModelIndex &)));
362 TreeModel::~TreeModel()
368 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
370 if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent))
371 return QModelIndex();
373 AbstractTreeItem *parentItem;
375 if (!parent.isValid())
376 parentItem = rootItem;
378 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
380 AbstractTreeItem *childItem = parentItem->child(row);
383 return createIndex(row, column, childItem);
385 return QModelIndex();
389 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item) const
392 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
393 return QModelIndex();
396 if (item == rootItem)
397 return QModelIndex();
399 return createIndex(item->row(), 0, item);
403 QModelIndex TreeModel::parent(const QModelIndex &index) const
405 if (!index.isValid()) {
406 // ModelTest does this
407 // qWarning() << "TreeModel::parent(): has been asked for the rootItems Parent!";
408 return QModelIndex();
411 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
412 AbstractTreeItem *parentItem = childItem->parent();
414 Q_ASSERT(parentItem);
415 if (parentItem == rootItem)
416 return QModelIndex();
418 return createIndex(parentItem->row(), 0, parentItem);
422 int TreeModel::rowCount(const QModelIndex &parent) const
424 AbstractTreeItem *parentItem;
425 if (!parent.isValid())
426 parentItem = rootItem;
428 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
430 return parentItem->childCount(parent.column());
434 int TreeModel::columnCount(const QModelIndex &parent) const
437 return rootItem->columnCount();
438 // since there the Qt Views don't draw more columns than the header has columns
439 // we can be lazy and simply return the count of header columns
440 // actually this gives us more freedom cause we don't have to ensure that a rows parent
441 // has equal or more columns than that row
443 // AbstractTreeItem *parentItem;
444 // if(!parent.isValid())
445 // parentItem = rootItem;
447 // parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
448 // return parentItem->columnCount();
452 QVariant TreeModel::data(const QModelIndex &index, int role) const
454 if (!index.isValid())
457 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
458 return item->data(index.column(), role);
462 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
464 if (!index.isValid())
467 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
468 return item->setData(index.column(), value, role);
472 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
474 if (!index.isValid()) {
475 return rootItem->flags() & Qt::ItemIsDropEnabled;
478 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
479 return item->flags();
484 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
486 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
487 return rootItem->data(section, role);
493 void TreeModel::itemDataChanged(int column)
495 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
496 QModelIndex leftIndex, rightIndex;
498 if (item == rootItem)
502 leftIndex = createIndex(item->row(), 0, item);
503 rightIndex = createIndex(item->row(), item->columnCount() - 1, item);
506 leftIndex = createIndex(item->row(), column, item);
507 rightIndex = leftIndex;
510 emit dataChanged(leftIndex, rightIndex);
514 void TreeModel::connectItem(AbstractTreeItem *item)
516 connect(item, SIGNAL(dataChanged(int)),
517 this, SLOT(itemDataChanged(int)));
519 connect(item, SIGNAL(beginAppendChilds(int, int)),
520 this, SLOT(beginAppendChilds(int, int)));
521 connect(item, SIGNAL(endAppendChilds()),
522 this, SLOT(endAppendChilds()));
524 connect(item, SIGNAL(beginRemoveChilds(int, int)),
525 this, SLOT(beginRemoveChilds(int, int)));
526 connect(item, SIGNAL(endRemoveChilds()),
527 this, SLOT(endRemoveChilds()));
531 void TreeModel::beginAppendChilds(int firstRow, int lastRow)
533 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
535 qWarning() << "TreeModel::beginAppendChilds(): cannot append Children to unknown parent";
539 QModelIndex parent = indexByItem(parentItem);
540 Q_ASSERT(!_aboutToRemoveOrInsert);
542 _aboutToRemoveOrInsert = true;
543 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
544 beginInsertRows(parent, firstRow, lastRow);
548 void TreeModel::endAppendChilds()
550 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
552 qWarning() << "TreeModel::endAppendChilds(): cannot append Children to unknown parent";
555 Q_ASSERT(_aboutToRemoveOrInsert);
556 ChildStatus cs = _childStatus;
557 QModelIndex parent = indexByItem(parentItem);
558 Q_ASSERT(cs.parent == parent);
559 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
561 _aboutToRemoveOrInsert = false;
562 for (int i = cs.start; i <= cs.end; i++) {
563 connectItem(parentItem->child(i));
569 void TreeModel::beginRemoveChilds(int firstRow, int lastRow)
571 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
573 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Children to unknown parent";
577 for (int i = firstRow; i <= lastRow; i++) {
578 disconnect(parentItem->child(i), 0, this, 0);
582 QModelIndex parent = indexByItem(parentItem);
583 Q_ASSERT(firstRow <= lastRow);
584 Q_ASSERT(parentItem->childCount() > lastRow);
585 Q_ASSERT(!_aboutToRemoveOrInsert);
586 _aboutToRemoveOrInsert = true;
587 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
589 beginRemoveRows(parent, firstRow, lastRow);
593 void TreeModel::endRemoveChilds()
595 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
597 qWarning() << "TreeModel::endRemoveChilds(): cannot remove Children from unknown parent";
601 // concistency checks
602 Q_ASSERT(_aboutToRemoveOrInsert);
603 ChildStatus cs = _childStatus;
604 QModelIndex parent = indexByItem(parentItem);
605 Q_ASSERT(cs.parent == parent);
606 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
607 _aboutToRemoveOrInsert = false;
613 void TreeModel::clear()
615 rootItem->removeAllChilds();
619 void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
621 qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
625 void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
627 AbstractTreeItem *parentItem;
628 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
630 parentItem = rootItem;
631 qDebug() << "debug_rowsAboutToBeRemoved" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
634 for (int i = end; i >= start; i--) {
635 child = parent.child(i, 0);
636 Q_ASSERT(parentItem->child(i));
637 qDebug() << ">>>" << i << child << child.data().toString();
642 void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end)
644 AbstractTreeItem *parentItem;
645 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
647 parentItem = rootItem;
648 qDebug() << "debug_rowsInserted:" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
651 for (int i = start; i <= end; i++) {
652 child = parent.child(i, 0);
653 Q_ASSERT(parentItem->child(i));
654 qDebug() << "<<<" << i << child << child.data().toString();
659 void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end)
661 qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
665 void TreeModel::debug_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
667 qDebug() << "debug_dataChanged" << topLeft << bottomRight;
668 QStringList displayData;
669 for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
670 displayData = QStringList();
671 for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
672 displayData << data(topLeft.sibling(row, column), Qt::DisplayRole).toString();
674 qDebug() << " row:" << row << displayData;