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(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, 0);
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 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
352 if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent))
353 return QModelIndex();
355 AbstractTreeItem *parentItem;
357 if (!parent.isValid())
358 parentItem = rootItem;
360 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
362 AbstractTreeItem *childItem = parentItem->child(row);
365 return createIndex(row, column, childItem);
367 return QModelIndex();
371 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item) const
374 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
375 return QModelIndex();
378 if (item == rootItem)
379 return QModelIndex();
381 return createIndex(item->row(), 0, item);
385 QModelIndex TreeModel::parent(const QModelIndex &index) const
387 if (!index.isValid()) {
388 // ModelTest does this
389 // qWarning() << "TreeModel::parent(): has been asked for the rootItems Parent!";
390 return QModelIndex();
393 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
394 AbstractTreeItem *parentItem = childItem->parent();
396 Q_ASSERT(parentItem);
397 if (parentItem == rootItem)
398 return QModelIndex();
400 return createIndex(parentItem->row(), 0, parentItem);
404 int TreeModel::rowCount(const QModelIndex &parent) const
406 AbstractTreeItem *parentItem;
407 if (!parent.isValid())
408 parentItem = rootItem;
410 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
412 return parentItem->childCount(parent.column());
416 int TreeModel::columnCount(const QModelIndex &parent) const
419 return rootItem->columnCount();
420 // since there the Qt Views don't draw more columns than the header has columns
421 // we can be lazy and simply return the count of header columns
422 // actually this gives us more freedom cause we don't have to ensure that a rows parent
423 // has equal or more columns than that row
425 // AbstractTreeItem *parentItem;
426 // if(!parent.isValid())
427 // parentItem = rootItem;
429 // parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
430 // return parentItem->columnCount();
434 QVariant TreeModel::data(const QModelIndex &index, int role) const
436 if (!index.isValid())
439 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
440 return item->data(index.column(), role);
444 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
446 if (!index.isValid())
449 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
450 return item->setData(index.column(), value, role);
454 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
456 if (!index.isValid()) {
457 return rootItem->flags() & Qt::ItemIsDropEnabled;
460 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
461 return item->flags();
466 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
468 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
469 return rootItem->data(section, role);
475 void TreeModel::itemDataChanged(int column)
477 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
478 QModelIndex leftIndex, rightIndex;
480 if (item == rootItem)
484 leftIndex = createIndex(item->row(), 0, item);
485 rightIndex = createIndex(item->row(), item->columnCount() - 1, item);
488 leftIndex = createIndex(item->row(), column, item);
489 rightIndex = leftIndex;
492 emit dataChanged(leftIndex, rightIndex);
496 void TreeModel::connectItem(AbstractTreeItem *item)
498 connect(item, SIGNAL(dataChanged(int)),
499 this, SLOT(itemDataChanged(int)));
501 connect(item, SIGNAL(beginAppendChilds(int, int)),
502 this, SLOT(beginAppendChilds(int, int)));
503 connect(item, SIGNAL(endAppendChilds()),
504 this, SLOT(endAppendChilds()));
506 connect(item, SIGNAL(beginRemoveChilds(int, int)),
507 this, SLOT(beginRemoveChilds(int, int)));
508 connect(item, SIGNAL(endRemoveChilds()),
509 this, SLOT(endRemoveChilds()));
513 void TreeModel::beginAppendChilds(int firstRow, int lastRow)
515 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
517 qWarning() << "TreeModel::beginAppendChilds(): cannot append Children to unknown parent";
521 QModelIndex parent = indexByItem(parentItem);
522 Q_ASSERT(!_aboutToRemoveOrInsert);
524 _aboutToRemoveOrInsert = true;
525 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
526 beginInsertRows(parent, firstRow, lastRow);
530 void TreeModel::endAppendChilds()
532 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
534 qWarning() << "TreeModel::endAppendChilds(): cannot append Children to unknown parent";
537 Q_ASSERT(_aboutToRemoveOrInsert);
538 ChildStatus cs = _childStatus;
540 QModelIndex parent = indexByItem(parentItem);
541 Q_ASSERT(cs.parent == parent);
542 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
544 _aboutToRemoveOrInsert = false;
545 for (int i = cs.start; i <= cs.end; i++) {
546 connectItem(parentItem->child(i));
552 void TreeModel::beginRemoveChilds(int firstRow, int lastRow)
554 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
556 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Children to unknown parent";
560 for (int i = firstRow; i <= lastRow; i++) {
561 disconnect(parentItem->child(i), 0, this, 0);
565 QModelIndex parent = indexByItem(parentItem);
566 Q_ASSERT(firstRow <= lastRow);
567 Q_ASSERT(parentItem->childCount() > lastRow);
568 Q_ASSERT(!_aboutToRemoveOrInsert);
569 _aboutToRemoveOrInsert = true;
570 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
572 beginRemoveRows(parent, firstRow, lastRow);
576 void TreeModel::endRemoveChilds()
578 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
580 qWarning() << "TreeModel::endRemoveChilds(): cannot remove Children from unknown parent";
584 // concistency checks
585 Q_ASSERT(_aboutToRemoveOrInsert);
587 ChildStatus cs = _childStatus;
588 QModelIndex parent = indexByItem(parentItem);
589 Q_ASSERT(cs.parent == parent);
590 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
592 _aboutToRemoveOrInsert = false;
598 void TreeModel::clear()
600 rootItem->removeAllChilds();
604 void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
606 qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
610 void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
612 AbstractTreeItem *parentItem;
613 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
615 parentItem = rootItem;
616 qDebug() << "debug_rowsAboutToBeRemoved" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
619 for (int i = end; i >= start; i--) {
620 child = parent.child(i, 0);
621 Q_ASSERT(parentItem->child(i));
622 qDebug() << ">>>" << i << child << child.data().toString();
627 void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end)
629 AbstractTreeItem *parentItem;
630 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
632 parentItem = rootItem;
633 qDebug() << "debug_rowsInserted:" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
636 for (int i = start; i <= end; i++) {
637 child = parent.child(i, 0);
638 Q_ASSERT(parentItem->child(i));
639 qDebug() << "<<<" << i << child << child.data().toString();
644 void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end)
646 qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
650 void TreeModel::debug_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
652 qDebug() << "debug_dataChanged" << topLeft << bottomRight;
653 QStringList displayData;
654 for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
655 displayData = QStringList();
656 for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
657 displayData << data(topLeft.sibling(row, column), Qt::DisplayRole).toString();
659 qDebug() << " row:" << row << displayData;