1 /***************************************************************************
2 * Copyright (C) 2005-2018 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].toLatin1());
314 bool PropertyMapItem::setData(int column, const QVariant &value, int role)
316 if (column >= columnCount() || role != Qt::DisplayRole)
319 setProperty(_propertyOrder[column].toLatin1(), value);
320 emit dataChanged(column);
325 int PropertyMapItem::columnCount() const
327 return _propertyOrder.count();
331 void PropertyMapItem::appendProperty(const QString &property)
333 _propertyOrder << property;
337 /*****************************************
339 *****************************************/
340 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
341 : QAbstractItemModel(parent),
342 _childStatus(QModelIndex(), 0, 0, 0),
343 _aboutToRemoveOrInsert(false)
345 rootItem = new SimpleTreeItem(data, 0);
346 connectItem(rootItem);
348 if (Quassel::isOptionSet("debugmodel")) {
349 connect(this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
350 this, SLOT(debug_rowsAboutToBeInserted(const QModelIndex &, int, int)));
351 connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
352 this, SLOT(debug_rowsAboutToBeRemoved(const QModelIndex &, int, int)));
353 connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
354 this, SLOT(debug_rowsInserted(const QModelIndex &, int, int)));
355 connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
356 this, SLOT(debug_rowsRemoved(const QModelIndex &, int, int)));
357 connect(this, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
358 this, SLOT(debug_dataChanged(const QModelIndex &, const QModelIndex &)));
363 TreeModel::~TreeModel()
369 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
371 if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent))
372 return QModelIndex();
374 AbstractTreeItem *parentItem;
376 if (!parent.isValid())
377 parentItem = rootItem;
379 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
381 AbstractTreeItem *childItem = parentItem->child(row);
384 return createIndex(row, column, childItem);
386 return QModelIndex();
390 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item) const
393 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
394 return QModelIndex();
397 if (item == rootItem)
398 return QModelIndex();
400 return createIndex(item->row(), 0, item);
404 QModelIndex TreeModel::parent(const QModelIndex &index) const
406 if (!index.isValid()) {
407 // ModelTest does this
408 // qWarning() << "TreeModel::parent(): has been asked for the rootItems Parent!";
409 return QModelIndex();
412 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
413 AbstractTreeItem *parentItem = childItem->parent();
415 Q_ASSERT(parentItem);
416 if (parentItem == rootItem)
417 return QModelIndex();
419 return createIndex(parentItem->row(), 0, parentItem);
423 int TreeModel::rowCount(const QModelIndex &parent) const
425 AbstractTreeItem *parentItem;
426 if (!parent.isValid())
427 parentItem = rootItem;
429 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
431 return parentItem->childCount(parent.column());
435 int TreeModel::columnCount(const QModelIndex &parent) const
438 return rootItem->columnCount();
439 // since there the Qt Views don't draw more columns than the header has columns
440 // we can be lazy and simply return the count of header columns
441 // actually this gives us more freedom cause we don't have to ensure that a rows parent
442 // has equal or more columns than that row
444 // AbstractTreeItem *parentItem;
445 // if(!parent.isValid())
446 // parentItem = rootItem;
448 // parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
449 // return parentItem->columnCount();
453 QVariant TreeModel::data(const QModelIndex &index, int role) const
455 if (!index.isValid())
458 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
459 return item->data(index.column(), role);
463 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
465 if (!index.isValid())
468 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
469 return item->setData(index.column(), value, role);
473 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
475 if (!index.isValid()) {
476 return rootItem->flags() & Qt::ItemIsDropEnabled;
479 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
480 return item->flags();
485 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
487 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
488 return rootItem->data(section, role);
494 void TreeModel::itemDataChanged(int column)
496 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
497 QModelIndex leftIndex, rightIndex;
499 if (item == rootItem)
503 leftIndex = createIndex(item->row(), 0, item);
504 rightIndex = createIndex(item->row(), item->columnCount() - 1, item);
507 leftIndex = createIndex(item->row(), column, item);
508 rightIndex = leftIndex;
511 emit dataChanged(leftIndex, rightIndex);
515 void TreeModel::connectItem(AbstractTreeItem *item)
517 connect(item, SIGNAL(dataChanged(int)),
518 this, SLOT(itemDataChanged(int)));
520 connect(item, SIGNAL(beginAppendChilds(int, int)),
521 this, SLOT(beginAppendChilds(int, int)));
522 connect(item, SIGNAL(endAppendChilds()),
523 this, SLOT(endAppendChilds()));
525 connect(item, SIGNAL(beginRemoveChilds(int, int)),
526 this, SLOT(beginRemoveChilds(int, int)));
527 connect(item, SIGNAL(endRemoveChilds()),
528 this, SLOT(endRemoveChilds()));
532 void TreeModel::beginAppendChilds(int firstRow, int lastRow)
534 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
536 qWarning() << "TreeModel::beginAppendChilds(): cannot append Children to unknown parent";
540 QModelIndex parent = indexByItem(parentItem);
541 Q_ASSERT(!_aboutToRemoveOrInsert);
543 _aboutToRemoveOrInsert = true;
544 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
545 beginInsertRows(parent, firstRow, lastRow);
549 void TreeModel::endAppendChilds()
551 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
553 qWarning() << "TreeModel::endAppendChilds(): cannot append Children to unknown parent";
556 Q_ASSERT(_aboutToRemoveOrInsert);
557 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);
563 _aboutToRemoveOrInsert = false;
564 for (int i = cs.start; i <= cs.end; i++) {
565 connectItem(parentItem->child(i));
571 void TreeModel::beginRemoveChilds(int firstRow, int lastRow)
573 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
575 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Children to unknown parent";
579 for (int i = firstRow; i <= lastRow; i++) {
580 disconnect(parentItem->child(i), 0, this, 0);
584 QModelIndex parent = indexByItem(parentItem);
585 Q_ASSERT(firstRow <= lastRow);
586 Q_ASSERT(parentItem->childCount() > lastRow);
587 Q_ASSERT(!_aboutToRemoveOrInsert);
588 _aboutToRemoveOrInsert = true;
589 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
591 beginRemoveRows(parent, firstRow, lastRow);
595 void TreeModel::endRemoveChilds()
597 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
599 qWarning() << "TreeModel::endRemoveChilds(): cannot remove Children from unknown parent";
603 // concistency checks
604 Q_ASSERT(_aboutToRemoveOrInsert);
606 ChildStatus cs = _childStatus;
607 QModelIndex parent = indexByItem(parentItem);
608 Q_ASSERT(cs.parent == parent);
609 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
611 _aboutToRemoveOrInsert = false;
617 void TreeModel::clear()
619 rootItem->removeAllChilds();
623 void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
625 qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
629 void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
631 AbstractTreeItem *parentItem;
632 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
634 parentItem = rootItem;
635 qDebug() << "debug_rowsAboutToBeRemoved" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
638 for (int i = end; i >= start; i--) {
639 child = parent.child(i, 0);
640 Q_ASSERT(parentItem->child(i));
641 qDebug() << ">>>" << i << child << child.data().toString();
646 void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end)
648 AbstractTreeItem *parentItem;
649 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
651 parentItem = rootItem;
652 qDebug() << "debug_rowsInserted:" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
655 for (int i = start; i <= end; i++) {
656 child = parent.child(i, 0);
657 Q_ASSERT(parentItem->child(i));
658 qDebug() << "<<<" << i << child << child.data().toString();
663 void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end)
665 qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
669 void TreeModel::debug_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
671 qDebug() << "debug_dataChanged" << topLeft << bottomRight;
672 QStringList displayData;
673 for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
674 displayData = QStringList();
675 for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
676 displayData << data(topLeft.sibling(row, column), Qt::DisplayRole).toString();
678 qDebug() << " row:" << row << displayData;