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),
44 _treeItemFlags(nullptr)
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(nullptr); // 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(AbstractTreeItem *parent)
279 : AbstractTreeItem(parent)
284 QVariant PropertyMapItem::data(int column, int role) const
286 if (column >= columnCount())
290 case Qt::ToolTipRole:
291 return toolTip(column);
292 case Qt::DisplayRole:
293 case TreeModel::SortRole: // fallthrough, since SortRole should default to DisplayRole
294 return property(propertyOrder()[column].toLatin1());
301 bool PropertyMapItem::setData(int column, const QVariant &value, int role)
303 if (column >= columnCount() || role != Qt::DisplayRole)
306 setProperty(propertyOrder()[column].toLatin1(), value);
307 emit dataChanged(column);
312 int PropertyMapItem::columnCount() const
314 return propertyOrder().count();
318 /*****************************************
320 *****************************************/
321 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
322 : QAbstractItemModel(parent),
323 _childStatus(QModelIndex(), 0, 0, 0),
324 _aboutToRemoveOrInsert(false)
326 rootItem = new SimpleTreeItem(data, nullptr);
327 connectItem(rootItem);
329 if (Quassel::isOptionSet("debugmodel")) {
330 connect(this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
331 this, SLOT(debug_rowsAboutToBeInserted(const QModelIndex &, int, int)));
332 connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
333 this, SLOT(debug_rowsAboutToBeRemoved(const QModelIndex &, int, int)));
334 connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
335 this, SLOT(debug_rowsInserted(const QModelIndex &, int, int)));
336 connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
337 this, SLOT(debug_rowsRemoved(const QModelIndex &, int, int)));
338 connect(this, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
339 this, SLOT(debug_dataChanged(const QModelIndex &, const QModelIndex &)));
344 TreeModel::~TreeModel()
350 AbstractTreeItem *TreeModel::root() const
356 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
358 if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent))
359 return QModelIndex();
361 AbstractTreeItem *parentItem;
363 if (!parent.isValid())
364 parentItem = rootItem;
366 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
368 AbstractTreeItem *childItem = parentItem->child(row);
371 return createIndex(row, column, childItem);
373 return QModelIndex();
377 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item) const
379 if (item == nullptr) {
380 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
381 return QModelIndex();
384 if (item == rootItem)
385 return QModelIndex();
387 return createIndex(item->row(), 0, item);
391 QModelIndex TreeModel::parent(const QModelIndex &index) const
393 if (!index.isValid()) {
394 // ModelTest does this
395 // qWarning() << "TreeModel::parent(): has been asked for the rootItems Parent!";
396 return QModelIndex();
399 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
400 AbstractTreeItem *parentItem = childItem->parent();
402 Q_ASSERT(parentItem);
403 if (parentItem == rootItem)
404 return QModelIndex();
406 return createIndex(parentItem->row(), 0, parentItem);
410 int TreeModel::rowCount(const QModelIndex &parent) const
412 AbstractTreeItem *parentItem;
413 if (!parent.isValid())
414 parentItem = rootItem;
416 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
418 return parentItem->childCount(parent.column());
422 int TreeModel::columnCount(const QModelIndex &parent) const
425 return rootItem->columnCount();
426 // since there the Qt Views don't draw more columns than the header has columns
427 // we can be lazy and simply return the count of header columns
428 // actually this gives us more freedom cause we don't have to ensure that a rows parent
429 // has equal or more columns than that row
431 // AbstractTreeItem *parentItem;
432 // if(!parent.isValid())
433 // parentItem = rootItem;
435 // parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
436 // return parentItem->columnCount();
440 QVariant TreeModel::data(const QModelIndex &index, int role) const
442 if (!index.isValid())
445 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
446 return item->data(index.column(), role);
450 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
452 if (!index.isValid())
455 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
456 return item->setData(index.column(), value, role);
460 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
462 if (!index.isValid()) {
463 return rootItem->flags() & Qt::ItemIsDropEnabled;
466 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
467 return item->flags();
472 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
474 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
475 return rootItem->data(section, role);
481 void TreeModel::itemDataChanged(int column)
483 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
484 QModelIndex leftIndex, rightIndex;
486 if (item == rootItem)
490 leftIndex = createIndex(item->row(), 0, item);
491 rightIndex = createIndex(item->row(), item->columnCount() - 1, item);
494 leftIndex = createIndex(item->row(), column, item);
495 rightIndex = leftIndex;
498 emit dataChanged(leftIndex, rightIndex);
502 void TreeModel::connectItem(AbstractTreeItem *item)
504 connect(item, SIGNAL(dataChanged(int)),
505 this, SLOT(itemDataChanged(int)));
507 connect(item, SIGNAL(beginAppendChilds(int, int)),
508 this, SLOT(beginAppendChilds(int, int)));
509 connect(item, SIGNAL(endAppendChilds()),
510 this, SLOT(endAppendChilds()));
512 connect(item, SIGNAL(beginRemoveChilds(int, int)),
513 this, SLOT(beginRemoveChilds(int, int)));
514 connect(item, SIGNAL(endRemoveChilds()),
515 this, SLOT(endRemoveChilds()));
519 void TreeModel::beginAppendChilds(int firstRow, int lastRow)
521 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
523 qWarning() << "TreeModel::beginAppendChilds(): cannot append Children to unknown parent";
527 QModelIndex parent = indexByItem(parentItem);
528 Q_ASSERT(!_aboutToRemoveOrInsert);
530 _aboutToRemoveOrInsert = true;
531 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
532 beginInsertRows(parent, firstRow, lastRow);
536 void TreeModel::endAppendChilds()
538 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
540 qWarning() << "TreeModel::endAppendChilds(): cannot append Children to unknown parent";
543 Q_ASSERT(_aboutToRemoveOrInsert);
544 ChildStatus cs = _childStatus;
546 QModelIndex parent = indexByItem(parentItem);
547 Q_ASSERT(cs.parent == parent);
548 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
550 _aboutToRemoveOrInsert = false;
551 for (int i = cs.start; i <= cs.end; i++) {
552 connectItem(parentItem->child(i));
558 void TreeModel::beginRemoveChilds(int firstRow, int lastRow)
560 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
562 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Children to unknown parent";
566 for (int i = firstRow; i <= lastRow; i++) {
567 disconnect(parentItem->child(i), nullptr, this, nullptr);
571 QModelIndex parent = indexByItem(parentItem);
572 Q_ASSERT(firstRow <= lastRow);
573 Q_ASSERT(parentItem->childCount() > lastRow);
574 Q_ASSERT(!_aboutToRemoveOrInsert);
575 _aboutToRemoveOrInsert = true;
576 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
578 beginRemoveRows(parent, firstRow, lastRow);
582 void TreeModel::endRemoveChilds()
584 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
586 qWarning() << "TreeModel::endRemoveChilds(): cannot remove Children from unknown parent";
590 // concistency checks
591 Q_ASSERT(_aboutToRemoveOrInsert);
593 ChildStatus cs = _childStatus;
594 QModelIndex parent = indexByItem(parentItem);
595 Q_ASSERT(cs.parent == parent);
596 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
598 _aboutToRemoveOrInsert = false;
604 void TreeModel::clear()
606 rootItem->removeAllChilds();
610 void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
612 qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
616 void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
618 AbstractTreeItem *parentItem;
619 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
621 parentItem = rootItem;
622 qDebug() << "debug_rowsAboutToBeRemoved" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
625 for (int i = end; i >= start; i--) {
626 child = parent.child(i, 0);
627 Q_ASSERT(parentItem->child(i));
628 qDebug() << ">>>" << i << child << child.data().toString();
633 void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end)
635 AbstractTreeItem *parentItem;
636 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
638 parentItem = rootItem;
639 qDebug() << "debug_rowsInserted:" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
642 for (int i = start; i <= end; i++) {
643 child = parent.child(i, 0);
644 Q_ASSERT(parentItem->child(i));
645 qDebug() << "<<<" << i << child << child.data().toString();
650 void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end)
652 qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
656 void TreeModel::debug_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
658 qDebug() << "debug_dataChanged" << topLeft << bottomRight;
659 QStringList displayData;
660 for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
661 displayData = QStringList();
662 for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
663 displayData << data(topLeft.sibling(row, column), Qt::DisplayRole).toString();
665 qDebug() << " row:" << row << displayData;