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 /*****************************************
26 * Abstract Items of a TreeModel
27 *****************************************/
28 AbstractTreeItem::AbstractTreeItem(AbstractTreeItem *parent)
30 _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled)
34 AbstractTreeItem::~AbstractTreeItem() {
37 quint64 AbstractTreeItem::id() const {
41 int AbstractTreeItem::defaultColumn() const {
42 // invalid QModelIndexes aka rootNodes get their Childs stuffed into column -1
50 bool AbstractTreeItem::newChild(int column, AbstractTreeItem *item) {
51 if(column >= columnCount()) {
52 qWarning() << "AbstractTreeItem::newChild() cannot append Child to not existing column!" << this << column;
56 if(!_childItems.contains(column)) {
57 _childItems[column] = QList<AbstractTreeItem *>();
60 int newRow = _childItems[column].count();
61 emit beginAppendChilds(column, newRow, newRow);
62 _childItems[column].append(item);
63 emit endAppendChilds();
68 bool AbstractTreeItem::newChild(AbstractTreeItem *item) {
69 return newChild(defaultColumn(), item);
72 bool AbstractTreeItem::removeChild(int column, int row) {
73 if(!_childItems.contains(column) || row >= childCount(column))
76 emit beginRemoveChilds(column, row, row);
77 AbstractTreeItem *treeitem = _childItems[column].takeAt(row);
78 treeitem->deleteLater();
79 emit endRemoveChilds();
84 bool AbstractTreeItem::removeChild(int row) {
85 return removeChild(defaultColumn(), row);
88 bool AbstractTreeItem::removeChildById(int column, const quint64 &id) {
89 if(!_childItems.contains(column))
92 for(int i = 0; i < _childItems[column].count(); i++) {
93 if(_childItems[column][i]->id() == id)
94 return removeChild(column, i);
99 bool AbstractTreeItem::removeChildById(const quint64 &id) {
100 return removeChildById(defaultColumn(), id);
103 void AbstractTreeItem::removeAllChilds() {
104 AbstractTreeItem *child;
106 QHash<int, QList<AbstractTreeItem *> >::iterator columnIter = _childItems.begin();
107 while(columnIter != _childItems.end()) {
108 if(columnIter->count() > 0) {
109 emit beginRemoveChilds(columnIter.key(), 0, columnIter->count() - 1);
110 QList<AbstractTreeItem *>::iterator childIter = columnIter->begin();
111 while(childIter != columnIter->end()) {
113 // child->removeAllChilds();
114 childIter = columnIter->erase(childIter);
115 child->deleteLater();
117 emit endRemoveChilds();
123 AbstractTreeItem *AbstractTreeItem::child(int column, int row) const {
124 if(!_childItems.contains(column) || _childItems[column].size() <= row)
127 return _childItems[column].value(row);
130 AbstractTreeItem *AbstractTreeItem::child(int row) const {
131 return child(defaultColumn(), row);
134 AbstractTreeItem *AbstractTreeItem::childById(int column, const quint64 &id) const {
135 if(!_childItems.contains(column))
138 for(int i = 0; i < _childItems[column].count(); i++) {
139 if(_childItems[column][i]->id() == id)
140 return _childItems[column][i];
145 AbstractTreeItem *AbstractTreeItem::childById(const quint64 &id) const {
146 return childById(defaultColumn(), id);
149 int AbstractTreeItem::childCount(int column) const {
150 if(!_childItems.contains(column))
153 return _childItems[column].count();
156 int AbstractTreeItem::childCount() const {
157 return childCount(defaultColumn());
160 int AbstractTreeItem::column() const {
164 QHash<int, QList<AbstractTreeItem*> >::const_iterator iter = parent()->_childItems.constBegin();
165 while(iter != parent()->_childItems.constEnd()) {
166 if(iter->contains(const_cast<AbstractTreeItem *>(this)))
171 qWarning() << "AbstractTreeItem::column(): unable to determine the Column of" << this;
172 return parent()->defaultColumn();
175 int AbstractTreeItem::row() const {
179 return parent()->_childItems[column()].indexOf(const_cast<AbstractTreeItem *>(this));
182 AbstractTreeItem *AbstractTreeItem::parent() const {
183 return qobject_cast<AbstractTreeItem *>(QObject::parent());
186 Qt::ItemFlags AbstractTreeItem::flags() const {
190 void AbstractTreeItem::setFlags(Qt::ItemFlags flags) {
194 void AbstractTreeItem::dumpChildList() {
195 AbstractTreeItem *child;
197 qDebug() << "==== Childlist for Item:" << this << id() << "====";
198 QHash<int, QList<AbstractTreeItem *> >::iterator columnIter = _childItems.begin();
199 while(columnIter != _childItems.end()) {
200 if(columnIter->count() > 0) {
201 QList<AbstractTreeItem *>::const_iterator childIter = columnIter->constBegin();
202 while(childIter != columnIter->constEnd()) {
204 qDebug() << "Column:" << columnIter.key() << "Row:" << child->row() << child << child->id() << child->data(0, Qt::DisplayRole);
210 qDebug() << "==== End Of Childlist ====";
213 /*****************************************
215 *****************************************/
216 SimpleTreeItem::SimpleTreeItem(const QList<QVariant> &data, AbstractTreeItem *parent)
217 : AbstractTreeItem(parent),
222 SimpleTreeItem::~SimpleTreeItem() {
225 QVariant SimpleTreeItem::data(int column, int role) const {
226 if(column >= columnCount() || role != Qt::DisplayRole)
229 return _itemData[column];
232 bool SimpleTreeItem::setData(int column, const QVariant &value, int role) {
233 if(column > columnCount() || role != Qt::DisplayRole)
236 if(column == columnCount())
237 _itemData.append(value);
239 _itemData[column] = value;
241 emit dataChanged(column);
245 int SimpleTreeItem::columnCount() const {
246 return _itemData.count();
249 /*****************************************
251 *****************************************/
252 PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent)
253 : AbstractTreeItem(parent),
254 _propertyOrder(propertyOrder)
258 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
259 : AbstractTreeItem(parent),
260 _propertyOrder(QStringList())
265 PropertyMapItem::~PropertyMapItem() {
268 QVariant PropertyMapItem::data(int column, int role) const {
269 if(column >= columnCount())
273 case Qt::ToolTipRole:
274 return toolTip(column);
275 case Qt::DisplayRole:
276 return property(_propertyOrder[column].toAscii());
283 bool PropertyMapItem::setData(int column, const QVariant &value, int role) {
284 if(column >= columnCount() || role != Qt::DisplayRole)
287 emit dataChanged(column);
288 return setProperty(_propertyOrder[column].toAscii(), value);
291 int PropertyMapItem::columnCount() const {
292 return _propertyOrder.count();
295 void PropertyMapItem::appendProperty(const QString &property) {
296 _propertyOrder << property;
301 /*****************************************
303 *****************************************/
304 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
305 : QAbstractItemModel(parent),
306 _childStatus(QModelIndex(), 0, 0, 0),
307 _aboutToRemoveOrInsert(false)
309 rootItem = new SimpleTreeItem(data, 0);
310 connectItem(rootItem);
313 TreeModel::~TreeModel() {
317 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
318 if(!hasIndex(row, column, parent))
319 return QModelIndex();
321 AbstractTreeItem *parentItem;
323 if(!parent.isValid())
324 parentItem = rootItem;
326 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
328 AbstractTreeItem *childItem = parentItem->child(parent.column(), row);
331 return createIndex(row, column, childItem);
333 return QModelIndex();
336 QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
337 AbstractTreeItem *parentItem;
339 if(!parent.isValid())
340 parentItem = rootItem;
342 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
344 AbstractTreeItem *childItem = parentItem->childById(parent.column(), id);
347 return createIndex(childItem->row(), 0, childItem);
349 return QModelIndex();
352 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item, int column) const {
354 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
355 return QModelIndex();
359 return QModelIndex();
361 return createIndex(item->row(), column, item);
365 QModelIndex TreeModel::parent(const QModelIndex &index) const {
367 return QModelIndex();
369 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
370 AbstractTreeItem *parentItem = static_cast<AbstractTreeItem *>(childItem->parent());
372 if(parentItem == rootItem)
373 return QModelIndex();
375 return createIndex(parentItem->row(), childItem->column(), parentItem);
378 int TreeModel::rowCount(const QModelIndex &parent) const {
379 AbstractTreeItem *parentItem;
380 if(!parent.isValid())
381 parentItem = rootItem;
383 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
385 return parentItem->childCount(parent.column());
388 int TreeModel::columnCount(const QModelIndex &parent) const {
390 // since there the Qt Views don't draw more columns than the header has columns
391 // we can be lazy and simply return the count of header columns
392 // actually this gives us more freedom cause we don't have to ensure that a rows parent
393 // has equal or more columns than that row
395 // if(parent.isValid()) {
396 // AbstractTreeItem *child;
397 // if(child = static_cast<AbstractTreeItem *>(parent.internalPointer())->child(parent.column(), parent.row()))
398 // return child->columnCount();
400 // return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
402 // return rootItem->columnCount();
405 return rootItem->columnCount();
408 QVariant TreeModel::data(const QModelIndex &index, int role) const {
412 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
413 return item->data(index.column(), role);
416 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
420 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
421 return item->setData(index.column(), value, role);
424 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
425 AbstractTreeItem *item;
429 item = static_cast<AbstractTreeItem *>(index.internalPointer());
430 return item->flags();
433 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
434 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
435 return rootItem->data(section, role);
440 void TreeModel::itemDataChanged(int column) {
441 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
442 QModelIndex leftIndex, rightIndex;
448 leftIndex = createIndex(item->row(), 0, item);
449 rightIndex = createIndex(item->row(), item->columnCount()-1, item);
451 leftIndex = createIndex(item->row(), column, item);
452 rightIndex = leftIndex;
455 emit dataChanged(leftIndex, rightIndex);
458 void TreeModel::connectItem(AbstractTreeItem *item) {
459 connect(item, SIGNAL(dataChanged(int)),
460 this, SLOT(itemDataChanged(int)));
462 connect(item, SIGNAL(beginAppendChilds(int, int, int)),
463 this, SLOT(beginAppendChilds(int, int, int)));
464 connect(item, SIGNAL(endAppendChilds()),
465 this, SLOT(endAppendChilds()));
467 connect(item, SIGNAL(beginRemoveChilds(int, int, int)),
468 this, SLOT(beginRemoveChilds(int, int, int)));
469 connect(item, SIGNAL(endRemoveChilds()),
470 this, SLOT(endRemoveChilds()));
473 void TreeModel::beginAppendChilds(int column, int firstRow, int lastRow) {
474 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
476 qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent";
479 QModelIndex parent = indexByItem(parentItem, column);
480 Q_ASSERT(!_aboutToRemoveOrInsert);
482 _aboutToRemoveOrInsert = true;
483 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
484 beginInsertRows(parent, firstRow, lastRow);
487 void TreeModel::endAppendChilds() {
488 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
490 qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent";
493 Q_ASSERT(_aboutToRemoveOrInsert);
494 ChildStatus cs = _childStatus;
495 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
496 Q_ASSERT(cs.parent == parent);
497 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
499 _aboutToRemoveOrInsert = false;
500 for(int i = cs.start; i <= cs.end; i++) {
501 connectItem(parentItem->child(parent.column(), i));
506 void TreeModel::beginRemoveChilds(int column, int firstRow, int lastRow) {
507 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
509 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent";
512 QModelIndex parent = indexByItem(parentItem, column);
513 Q_ASSERT(firstRow <= lastRow);
514 Q_ASSERT(parentItem->childCount(column) > lastRow);
515 Q_ASSERT(!_aboutToRemoveOrInsert);
517 _aboutToRemoveOrInsert = true;
518 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
519 beginRemoveRows(parent, firstRow, lastRow);
522 void TreeModel::endRemoveChilds() {
523 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
525 qWarning() << "TreeModel::endRemoveChilds(): cannot append Childs to unknown parent";
528 Q_ASSERT(_aboutToRemoveOrInsert);
529 ChildStatus cs = _childStatus;
530 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
531 Q_ASSERT(cs.parent == parent);
532 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
534 _aboutToRemoveOrInsert = false;
538 void TreeModel::clear() {
539 rootItem->removeAllChilds();