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>
29 class RemoveChildLaterEvent : public QEvent
32 RemoveChildLaterEvent(AbstractTreeItem *child) : QEvent(QEvent::User), _child(child) {};
33 inline AbstractTreeItem *child() { return _child; }
35 AbstractTreeItem *_child;
39 /*****************************************
40 * Abstract Items of a TreeModel
41 *****************************************/
42 AbstractTreeItem::AbstractTreeItem(AbstractTreeItem *parent)
44 _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled),
45 _treeItemFlags(nullptr)
50 bool AbstractTreeItem::newChild(AbstractTreeItem *item)
52 int newRow = childCount();
53 emit beginAppendChilds(newRow, newRow);
54 _childItems.append(item);
55 emit endAppendChilds();
60 bool AbstractTreeItem::newChilds(const QList<AbstractTreeItem *> &items)
65 int nextRow = childCount();
66 int lastRow = nextRow + items.count() - 1;
68 emit beginAppendChilds(nextRow, lastRow);
70 emit endAppendChilds();
76 bool AbstractTreeItem::removeChild(int row)
78 if (row < 0 || childCount() <= row)
81 child(row)->removeAllChilds();
82 emit beginRemoveChilds(row, row);
83 AbstractTreeItem *treeitem = _childItems.takeAt(row);
85 emit endRemoveChilds();
93 void AbstractTreeItem::removeAllChilds()
95 const int numChilds = childCount();
100 AbstractTreeItem *child;
102 QList<AbstractTreeItem *>::iterator childIter;
104 childIter = _childItems.begin();
105 while (childIter != _childItems.end()) {
107 child->setTreeItemFlags(nullptr); // disable self deletion, as this would only fuck up consitency and the child gets deleted anyways
108 child->removeAllChilds();
112 emit beginRemoveChilds(0, numChilds - 1);
113 childIter = _childItems.begin();
114 while (childIter != _childItems.end()) {
116 childIter = _childItems.erase(childIter);
119 emit endRemoveChilds();
125 void AbstractTreeItem::removeChildLater(AbstractTreeItem *child)
128 QCoreApplication::postEvent(this, new RemoveChildLaterEvent(child));
132 void AbstractTreeItem::customEvent(QEvent *event)
134 if (event->type() != QEvent::User)
139 RemoveChildLaterEvent *removeEvent = static_cast<RemoveChildLaterEvent *>(event);
140 int childRow = _childItems.indexOf(removeEvent->child());
144 // since we are called asynchronously we have to recheck if the item in question still has no childs
145 if (removeEvent->child()->childCount())
148 removeChild(childRow);
152 bool AbstractTreeItem::reParent(AbstractTreeItem *newParent)
154 // currently we support only re parenting if the child that's about to be
155 // adopted does not have any children itself.
156 if (childCount() != 0) {
157 qDebug() << "AbstractTreeItem::reParent(): cannot reparent" << this << "with children.";
165 emit parent()->beginRemoveChilds(oldRow, oldRow);
166 parent()->_childItems.removeAt(oldRow);
167 emit parent()->endRemoveChilds();
169 AbstractTreeItem *oldParent = parent();
170 setParent(newParent);
172 bool success = newParent->newChild(this);
174 qWarning() << "AbstractTreeItem::reParent(): failed to attach to new parent after removing from old parent! this:" << this << "new parent:" << newParent;
177 oldParent->checkForDeletion();
183 AbstractTreeItem *AbstractTreeItem::child(int row) const
185 if (childCount() <= row)
188 return _childItems[row];
192 int AbstractTreeItem::childCount(int column) const
197 return _childItems.count();
201 int AbstractTreeItem::row() const
204 qWarning() << "AbstractTreeItem::row():" << this << "has no parent AbstractTreeItem as it's parent! parent is" << QObject::parent();
208 int row_ = parent()->_childItems.indexOf(const_cast<AbstractTreeItem *>(this));
210 qWarning() << "AbstractTreeItem::row():" << this << "is not in the child list of" << QObject::parent();
215 void AbstractTreeItem::dumpChildList()
217 qDebug() << "==== Childlist for Item:" << this << "====";
218 if (childCount() > 0) {
219 AbstractTreeItem *child;
220 QList<AbstractTreeItem *>::const_iterator childIter = _childItems.constBegin();
221 while (childIter != _childItems.constEnd()) {
223 qDebug() << "Row:" << child->row() << child << child->data(0, Qt::DisplayRole);
227 qDebug() << "==== End Of Childlist ====";
231 /*****************************************
233 *****************************************/
234 SimpleTreeItem::SimpleTreeItem(QList<QVariant> data, AbstractTreeItem *parent)
235 : AbstractTreeItem(parent),
236 _itemData(std::move(data))
241 SimpleTreeItem::~SimpleTreeItem()
246 QVariant SimpleTreeItem::data(int column, int role) const
248 if (column >= columnCount() || role != Qt::DisplayRole)
251 return _itemData[column];
255 bool SimpleTreeItem::setData(int column, const QVariant &value, int role)
257 if (column > columnCount() || role != Qt::DisplayRole)
260 if (column == columnCount())
261 _itemData.append(value);
263 _itemData[column] = value;
265 emit dataChanged(column);
270 int SimpleTreeItem::columnCount() const
272 return _itemData.count();
276 /*****************************************
278 *****************************************/
279 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
280 : AbstractTreeItem(parent)
285 QVariant PropertyMapItem::data(int column, int role) const
287 if (column >= columnCount())
291 case Qt::ToolTipRole:
292 return toolTip(column);
293 case Qt::DisplayRole:
294 case TreeModel::SortRole: // fallthrough, since SortRole should default to DisplayRole
295 return property(propertyOrder()[column].toLatin1());
302 bool PropertyMapItem::setData(int column, const QVariant &value, int role)
304 if (column >= columnCount() || role != Qt::DisplayRole)
307 setProperty(propertyOrder()[column].toLatin1(), value);
308 emit dataChanged(column);
313 int PropertyMapItem::columnCount() const
315 return propertyOrder().count();
319 /*****************************************
321 *****************************************/
322 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
323 : QAbstractItemModel(parent),
324 _childStatus(QModelIndex(), 0, 0, 0),
325 _aboutToRemoveOrInsert(false)
327 rootItem = new SimpleTreeItem(data, nullptr);
328 connectItem(rootItem);
330 if (Quassel::isOptionSet("debugmodel")) {
331 connect(this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
332 this, SLOT(debug_rowsAboutToBeInserted(const QModelIndex &, int, int)));
333 connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
334 this, SLOT(debug_rowsAboutToBeRemoved(const QModelIndex &, int, int)));
335 connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
336 this, SLOT(debug_rowsInserted(const QModelIndex &, int, int)));
337 connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
338 this, SLOT(debug_rowsRemoved(const QModelIndex &, int, int)));
339 connect(this, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
340 this, SLOT(debug_dataChanged(const QModelIndex &, const QModelIndex &)));
345 TreeModel::~TreeModel()
351 AbstractTreeItem *TreeModel::root() const
357 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
359 if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent))
360 return QModelIndex();
362 AbstractTreeItem *parentItem;
364 if (!parent.isValid())
365 parentItem = rootItem;
367 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
369 AbstractTreeItem *childItem = parentItem->child(row);
372 return createIndex(row, column, childItem);
374 return QModelIndex();
378 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item) const
380 if (item == nullptr) {
381 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
382 return QModelIndex();
385 if (item == rootItem)
386 return QModelIndex();
388 return createIndex(item->row(), 0, item);
392 QModelIndex TreeModel::parent(const QModelIndex &index) const
394 if (!index.isValid()) {
395 // ModelTest does this
396 // qWarning() << "TreeModel::parent(): has been asked for the rootItems Parent!";
397 return QModelIndex();
400 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
401 AbstractTreeItem *parentItem = childItem->parent();
403 Q_ASSERT(parentItem);
404 if (parentItem == rootItem)
405 return QModelIndex();
407 return createIndex(parentItem->row(), 0, parentItem);
411 int TreeModel::rowCount(const QModelIndex &parent) const
413 AbstractTreeItem *parentItem;
414 if (!parent.isValid())
415 parentItem = rootItem;
417 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
419 return parentItem->childCount(parent.column());
423 int TreeModel::columnCount(const QModelIndex &parent) const
426 return rootItem->columnCount();
427 // since there the Qt Views don't draw more columns than the header has columns
428 // we can be lazy and simply return the count of header columns
429 // actually this gives us more freedom cause we don't have to ensure that a rows parent
430 // has equal or more columns than that row
432 // AbstractTreeItem *parentItem;
433 // if(!parent.isValid())
434 // parentItem = rootItem;
436 // parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
437 // return parentItem->columnCount();
441 QVariant TreeModel::data(const QModelIndex &index, int role) const
443 if (!index.isValid())
446 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
447 return item->data(index.column(), role);
451 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
453 if (!index.isValid())
456 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
457 return item->setData(index.column(), value, role);
461 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
463 if (!index.isValid()) {
464 return rootItem->flags() & Qt::ItemIsDropEnabled;
467 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
468 return item->flags();
473 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
475 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
476 return rootItem->data(section, role);
482 void TreeModel::itemDataChanged(int column)
484 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
485 QModelIndex leftIndex, rightIndex;
487 if (item == rootItem)
491 leftIndex = createIndex(item->row(), 0, item);
492 rightIndex = createIndex(item->row(), item->columnCount() - 1, item);
495 leftIndex = createIndex(item->row(), column, item);
496 rightIndex = leftIndex;
499 emit dataChanged(leftIndex, rightIndex);
503 void TreeModel::connectItem(AbstractTreeItem *item)
505 connect(item, SIGNAL(dataChanged(int)),
506 this, SLOT(itemDataChanged(int)));
508 connect(item, SIGNAL(beginAppendChilds(int, int)),
509 this, SLOT(beginAppendChilds(int, int)));
510 connect(item, SIGNAL(endAppendChilds()),
511 this, SLOT(endAppendChilds()));
513 connect(item, SIGNAL(beginRemoveChilds(int, int)),
514 this, SLOT(beginRemoveChilds(int, int)));
515 connect(item, SIGNAL(endRemoveChilds()),
516 this, SLOT(endRemoveChilds()));
520 void TreeModel::beginAppendChilds(int firstRow, int lastRow)
522 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
524 qWarning() << "TreeModel::beginAppendChilds(): cannot append Children to unknown parent";
528 QModelIndex parent = indexByItem(parentItem);
529 Q_ASSERT(!_aboutToRemoveOrInsert);
531 _aboutToRemoveOrInsert = true;
532 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
533 beginInsertRows(parent, firstRow, lastRow);
537 void TreeModel::endAppendChilds()
539 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
541 qWarning() << "TreeModel::endAppendChilds(): cannot append Children to unknown parent";
544 Q_ASSERT(_aboutToRemoveOrInsert);
545 ChildStatus cs = _childStatus;
547 QModelIndex parent = indexByItem(parentItem);
548 Q_ASSERT(cs.parent == parent);
549 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
551 _aboutToRemoveOrInsert = false;
552 for (int i = cs.start; i <= cs.end; i++) {
553 connectItem(parentItem->child(i));
559 void TreeModel::beginRemoveChilds(int firstRow, int lastRow)
561 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
563 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Children to unknown parent";
567 for (int i = firstRow; i <= lastRow; i++) {
568 disconnect(parentItem->child(i), nullptr, this, nullptr);
572 QModelIndex parent = indexByItem(parentItem);
573 Q_ASSERT(firstRow <= lastRow);
574 Q_ASSERT(parentItem->childCount() > lastRow);
575 Q_ASSERT(!_aboutToRemoveOrInsert);
576 _aboutToRemoveOrInsert = true;
577 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
579 beginRemoveRows(parent, firstRow, lastRow);
583 void TreeModel::endRemoveChilds()
585 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
587 qWarning() << "TreeModel::endRemoveChilds(): cannot remove Children from unknown parent";
591 // concistency checks
592 Q_ASSERT(_aboutToRemoveOrInsert);
594 ChildStatus cs = _childStatus;
595 QModelIndex parent = indexByItem(parentItem);
596 Q_ASSERT(cs.parent == parent);
597 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
599 _aboutToRemoveOrInsert = false;
605 void TreeModel::clear()
607 rootItem->removeAllChilds();
611 void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
613 qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
617 void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
619 AbstractTreeItem *parentItem;
620 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
622 parentItem = rootItem;
623 qDebug() << "debug_rowsAboutToBeRemoved" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
626 for (int i = end; i >= start; i--) {
627 child = parent.child(i, 0);
628 Q_ASSERT(parentItem->child(i));
629 qDebug() << ">>>" << i << child << child.data().toString();
634 void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end)
636 AbstractTreeItem *parentItem;
637 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
639 parentItem = rootItem;
640 qDebug() << "debug_rowsInserted:" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
643 for (int i = start; i <= end; i++) {
644 child = parent.child(i, 0);
645 Q_ASSERT(parentItem->child(i));
646 qDebug() << "<<<" << i << child << child.data().toString();
651 void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end)
653 qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
657 void TreeModel::debug_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
659 qDebug() << "debug_dataChanged" << topLeft << bottomRight;
660 QStringList displayData;
661 for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
662 displayData = QStringList();
663 for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
664 displayData << data(topLeft.sibling(row, column), Qt::DisplayRole).toString();
666 qDebug() << " row:" << row << displayData;