1 /***************************************************************************
2 * Copyright (C) 2005-08 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 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include "treemodel.h"
25 #include <QCoreApplication>
27 /*****************************************
28 * Abstract Items of a TreeModel
29 *****************************************/
30 AbstractTreeItem::AbstractTreeItem(AbstractTreeItem *parent)
32 _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled),
37 bool AbstractTreeItem::newChild(AbstractTreeItem *item) {
38 int newRow = childCount();
39 emit beginAppendChilds(newRow, newRow);
40 _childItems.append(item);
41 emit endAppendChilds();
45 bool AbstractTreeItem::newChilds(const QList<AbstractTreeItem *> &items) {
49 int nextRow = childCount();
50 int lastRow = nextRow + items.count() - 1;
52 emit beginAppendChilds(nextRow, lastRow);
54 emit endAppendChilds();
59 bool AbstractTreeItem::removeChild(int row) {
60 if(childCount() <= row)
63 child(row)->removeAllChilds();
64 emit beginRemoveChilds(row, row);
65 AbstractTreeItem *treeitem = _childItems.takeAt(row);
66 treeitem->deleteLater();
67 emit endRemoveChilds();
74 void AbstractTreeItem::removeAllChilds() {
75 const int numChilds = childCount();
80 AbstractTreeItem *child;
82 QList<AbstractTreeItem *>::iterator childIter;
84 childIter = _childItems.begin();
85 while(childIter != _childItems.end()) {
87 child->removeAllChilds();
91 emit beginRemoveChilds(0, numChilds - 1);
92 childIter = _childItems.begin();
93 while(childIter != _childItems.end()) {
95 childIter = _childItems.erase(childIter);
98 emit endRemoveChilds();
103 bool AbstractTreeItem::reParent(AbstractTreeItem *newParent) {
104 // currently we support only re parenting if the child that's about to be
105 // adopted does not have any children itself.
106 if(childCount() != 0) {
107 qDebug() << "AbstractTreeItem::reParent(): cannot reparent" << this << "with children.";
115 emit parent()->beginRemoveChilds(oldRow, oldRow);
116 parent()->_childItems.removeAt(oldRow);
117 emit parent()->endRemoveChilds();
119 parent()->checkForDeletion();
121 setParent(newParent);
123 bool success = newParent->newChild(this);
125 qWarning() << "AbstractTreeItem::reParent(): failed to attach to new parent after removing from old parent! this:" << this << "new parent:" << newParent;
130 AbstractTreeItem *AbstractTreeItem::child(int row) const {
131 if(childCount() <= row)
134 return _childItems[row];
137 int AbstractTreeItem::childCount(int column) const {
141 return _childItems.count();
144 int AbstractTreeItem::row() const {
146 qWarning() << "AbstractTreeItem::row():" << this << "has no parent AbstractTreeItem as it's parent! parent is" << QObject::parent();
150 int row_ = parent()->_childItems.indexOf(const_cast<AbstractTreeItem *>(this));
152 qWarning() << "AbstractTreeItem::row():" << this << "is not in the child list of" << QObject::parent();
156 void AbstractTreeItem::dumpChildList() {
157 qDebug() << "==== Childlist for Item:" << this << "====";
158 if(childCount() > 0) {
159 AbstractTreeItem *child;
160 QList<AbstractTreeItem *>::const_iterator childIter = _childItems.constBegin();
161 while(childIter != _childItems.constEnd()) {
163 qDebug() << "Row:" << child->row() << child << child->data(0, Qt::DisplayRole);
167 qDebug() << "==== End Of Childlist ====";
170 /*****************************************
172 *****************************************/
173 SimpleTreeItem::SimpleTreeItem(const QList<QVariant> &data, AbstractTreeItem *parent)
174 : AbstractTreeItem(parent),
179 SimpleTreeItem::~SimpleTreeItem() {
182 QVariant SimpleTreeItem::data(int column, int role) const {
183 if(column >= columnCount() || role != Qt::DisplayRole)
186 return _itemData[column];
189 bool SimpleTreeItem::setData(int column, const QVariant &value, int role) {
190 if(column > columnCount() || role != Qt::DisplayRole)
193 if(column == columnCount())
194 _itemData.append(value);
196 _itemData[column] = value;
198 emit dataChanged(column);
202 int SimpleTreeItem::columnCount() const {
203 return _itemData.count();
206 /*****************************************
208 *****************************************/
209 PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent)
210 : AbstractTreeItem(parent),
211 _propertyOrder(propertyOrder)
215 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
216 : AbstractTreeItem(parent),
217 _propertyOrder(QStringList())
222 PropertyMapItem::~PropertyMapItem() {
225 QVariant PropertyMapItem::data(int column, int role) const {
226 if(column >= columnCount())
230 case Qt::ToolTipRole:
231 return toolTip(column);
232 case Qt::DisplayRole:
233 case TreeModel::SortRole: // fallthrough, since SortRole should default to DisplayRole
234 return property(_propertyOrder[column].toAscii());
241 bool PropertyMapItem::setData(int column, const QVariant &value, int role) {
242 if(column >= columnCount() || role != Qt::DisplayRole)
245 emit dataChanged(column);
246 return setProperty(_propertyOrder[column].toAscii(), value);
249 int PropertyMapItem::columnCount() const {
250 return _propertyOrder.count();
253 void PropertyMapItem::appendProperty(const QString &property) {
254 _propertyOrder << property;
259 /*****************************************
261 *****************************************/
262 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
263 : QAbstractItemModel(parent),
264 _childStatus(QModelIndex(), 0, 0, 0),
265 _aboutToRemoveOrInsert(false)
267 rootItem = new SimpleTreeItem(data, 0);
268 connectItem(rootItem);
270 if(Global::parser.isSet("debugmodel")) {
271 connect(this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
272 this, SLOT(debug_rowsAboutToBeInserted(const QModelIndex &, int, int)));
273 connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
274 this, SLOT(debug_rowsAboutToBeRemoved(const QModelIndex &, int, int)));
275 connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
276 this, SLOT(debug_rowsInserted(const QModelIndex &, int, int)));
277 connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
278 this, SLOT(debug_rowsRemoved(const QModelIndex &, int, int)));
279 connect(this, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
280 this, SLOT(debug_dataChanged(const QModelIndex &, const QModelIndex &)));
284 TreeModel::~TreeModel() {
288 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
289 if(!hasIndex(row, column, parent))
290 return QModelIndex();
292 AbstractTreeItem *parentItem;
294 if(!parent.isValid())
295 parentItem = rootItem;
297 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
299 AbstractTreeItem *childItem = parentItem->child(row);
302 return createIndex(row, column, childItem);
304 return QModelIndex();
307 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item) const {
309 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
310 return QModelIndex();
314 return QModelIndex();
316 return createIndex(item->row(), 0, item);
319 QModelIndex TreeModel::parent(const QModelIndex &index) const {
320 if(!index.isValid()) {
321 qWarning() << "TreeModel::parent(): has been asked for the rootItems Parent!";
322 return QModelIndex();
325 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
326 AbstractTreeItem *parentItem = childItem->parent();
328 Q_ASSERT(parentItem);
329 if(parentItem == rootItem)
330 return QModelIndex();
332 return createIndex(parentItem->row(), 0, parentItem);
335 int TreeModel::rowCount(const QModelIndex &parent) const {
336 AbstractTreeItem *parentItem;
337 if(!parent.isValid())
338 parentItem = rootItem;
340 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
342 return parentItem->childCount(parent.column());
345 int TreeModel::columnCount(const QModelIndex &parent) const {
347 return rootItem->columnCount();
348 // since there the Qt Views don't draw more columns than the header has columns
349 // we can be lazy and simply return the count of header columns
350 // actually this gives us more freedom cause we don't have to ensure that a rows parent
351 // has equal or more columns than that row
353 // AbstractTreeItem *parentItem;
354 // if(!parent.isValid())
355 // parentItem = rootItem;
357 // parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
358 // return parentItem->columnCount();
361 QVariant TreeModel::data(const QModelIndex &index, int role) const {
365 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
366 return item->data(index.column(), role);
369 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
373 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
374 return item->setData(index.column(), value, role);
377 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
378 if(!index.isValid()) {
379 return rootItem->flags() & Qt::ItemIsDropEnabled;
381 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
382 return item->flags();
386 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
387 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
388 return rootItem->data(section, role);
393 void TreeModel::itemDataChanged(int column) {
394 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
395 QModelIndex leftIndex, rightIndex;
401 leftIndex = createIndex(item->row(), 0, item);
402 rightIndex = createIndex(item->row(), item->columnCount() - 1, item);
404 leftIndex = createIndex(item->row(), column, item);
405 rightIndex = leftIndex;
408 emit dataChanged(leftIndex, rightIndex);
411 void TreeModel::connectItem(AbstractTreeItem *item) {
412 connect(item, SIGNAL(dataChanged(int)),
413 this, SLOT(itemDataChanged(int)));
415 connect(item, SIGNAL(beginAppendChilds(int, int)),
416 this, SLOT(beginAppendChilds(int, int)));
417 connect(item, SIGNAL(endAppendChilds()),
418 this, SLOT(endAppendChilds()));
420 connect(item, SIGNAL(beginRemoveChilds(int, int)),
421 this, SLOT(beginRemoveChilds(int, int)));
422 connect(item, SIGNAL(endRemoveChilds()),
423 this, SLOT(endRemoveChilds()));
426 void TreeModel::beginAppendChilds(int firstRow, int lastRow) {
427 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
429 qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent";
433 QModelIndex parent = indexByItem(parentItem);
434 Q_ASSERT(!_aboutToRemoveOrInsert);
436 _aboutToRemoveOrInsert = true;
437 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
438 beginInsertRows(parent, firstRow, lastRow);
441 void TreeModel::endAppendChilds() {
442 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
444 qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent";
447 Q_ASSERT(_aboutToRemoveOrInsert);
448 ChildStatus cs = _childStatus;
449 QModelIndex parent = indexByItem(parentItem);
450 Q_ASSERT(cs.parent == parent);
451 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
453 _aboutToRemoveOrInsert = false;
454 for(int i = cs.start; i <= cs.end; i++) {
455 connectItem(parentItem->child(i));
460 void TreeModel::beginRemoveChilds(int firstRow, int lastRow) {
461 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
463 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent";
467 for(int i = firstRow; i <= lastRow; i++) {
468 disconnect(parentItem->child(i), 0, this, 0);
472 QModelIndex parent = indexByItem(parentItem);
473 Q_ASSERT(firstRow <= lastRow);
474 Q_ASSERT(parentItem->childCount() > lastRow);
475 Q_ASSERT(!_aboutToRemoveOrInsert);
476 _aboutToRemoveOrInsert = true;
477 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
479 beginRemoveRows(parent, firstRow, lastRow);
482 void TreeModel::endRemoveChilds() {
483 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
485 qWarning() << "TreeModel::endRemoveChilds(): cannot remove Childs from unknown parent";
489 // concistency checks
490 Q_ASSERT(_aboutToRemoveOrInsert);
491 ChildStatus cs = _childStatus;
492 QModelIndex parent = indexByItem(parentItem);
493 Q_ASSERT(cs.parent == parent);
494 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
495 _aboutToRemoveOrInsert = false;
500 void TreeModel::clear() {
501 rootItem->removeAllChilds();
504 void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) {
505 qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
508 void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) {
509 AbstractTreeItem *parentItem;
510 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
512 parentItem = rootItem;
513 qDebug() << "debug_rowsAboutToBeRemoved" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
516 AbstractTreeItem *childItem;
517 for(int i = end; i >= start; i--) {
518 child = parent.child(i, 0);
519 childItem = parentItem->child(i);
521 qDebug() << ">>>" << i << child << child.data().toString();
525 void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end) {
526 AbstractTreeItem *parentItem;
527 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
529 parentItem = rootItem;
530 qDebug() << "debug_rowsInserted:" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
533 AbstractTreeItem *childItem;
534 for(int i = start; i <= end; i++) {
535 child = parent.child(i, 0);
536 childItem = parentItem->child(i);
538 qDebug() << "<<<" << i << child << child.data().toString();
542 void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end) {
543 qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
546 void TreeModel::debug_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) {
547 qDebug() << "debug_dataChanged" << topLeft << bottomRight;
548 QStringList displayData;
549 for(int row = topLeft.row(); row <= bottomRight.row(); row++) {
550 displayData = QStringList();
551 for(int column = topLeft.column(); column <= bottomRight.column(); column++) {
552 displayData << data(topLeft.sibling(row, column), Qt::DisplayRole).toString();
554 qDebug() << " row:" << row << displayData;