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 /*****************************************
196 *****************************************/
197 SimpleTreeItem::SimpleTreeItem(const QList<QVariant> &data, AbstractTreeItem *parent)
198 : AbstractTreeItem(parent),
203 SimpleTreeItem::~SimpleTreeItem() {
206 QVariant SimpleTreeItem::data(int column, int role) const {
207 if(column >= columnCount() || role != Qt::DisplayRole)
210 return _itemData[column];
213 bool SimpleTreeItem::setData(int column, const QVariant &value, int role) {
214 if(column > columnCount() || role != Qt::DisplayRole)
217 if(column == columnCount())
218 _itemData.append(value);
220 _itemData[column] = value;
222 emit dataChanged(column);
226 int SimpleTreeItem::columnCount() const {
227 return _itemData.count();
230 /*****************************************
232 *****************************************/
233 PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent)
234 : AbstractTreeItem(parent),
235 _propertyOrder(propertyOrder)
239 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
240 : AbstractTreeItem(parent),
241 _propertyOrder(QStringList())
246 PropertyMapItem::~PropertyMapItem() {
249 QVariant PropertyMapItem::data(int column, int role) const {
250 if(column >= columnCount() || role != Qt::DisplayRole)
253 return property(_propertyOrder[column].toAscii());
256 bool PropertyMapItem::setData(int column, const QVariant &value, int role) {
257 if(column >= columnCount() || role != Qt::DisplayRole)
260 emit dataChanged(column);
261 return setProperty(_propertyOrder[column].toAscii(), value);
264 int PropertyMapItem::columnCount() const {
265 return _propertyOrder.count();
268 void PropertyMapItem::appendProperty(const QString &property) {
269 _propertyOrder << property;
274 /*****************************************
276 *****************************************/
277 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
278 : QAbstractItemModel(parent),
279 _childStatus(QModelIndex(), 0, 0, 0),
280 _aboutToRemoveOrInsert(false)
282 rootItem = new SimpleTreeItem(data, 0);
283 connectItem(rootItem);
286 TreeModel::~TreeModel() {
290 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
291 if(!hasIndex(row, column, parent))
292 return QModelIndex();
294 AbstractTreeItem *parentItem;
296 if(!parent.isValid())
297 parentItem = rootItem;
299 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
301 AbstractTreeItem *childItem = parentItem->child(parent.column(), row);
304 return createIndex(row, column, childItem);
306 return QModelIndex();
309 QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
310 AbstractTreeItem *parentItem;
312 if(!parent.isValid())
313 parentItem = rootItem;
315 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
317 AbstractTreeItem *childItem = parentItem->childById(parent.column(), id);
320 return createIndex(childItem->row(), 0, childItem);
322 return QModelIndex();
325 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item, int column) const {
327 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
328 return QModelIndex();
332 return QModelIndex();
334 return createIndex(item->row(), column, item);
338 QModelIndex TreeModel::parent(const QModelIndex &index) const {
340 return QModelIndex();
342 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
343 AbstractTreeItem *parentItem = static_cast<AbstractTreeItem *>(childItem->parent());
345 if(parentItem == rootItem)
346 return QModelIndex();
348 return createIndex(parentItem->row(), childItem->column(), parentItem);
351 int TreeModel::rowCount(const QModelIndex &parent) const {
352 AbstractTreeItem *parentItem;
353 if(!parent.isValid())
354 parentItem = rootItem;
356 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
358 return parentItem->childCount(parent.column());
361 int TreeModel::columnCount(const QModelIndex &parent) const {
363 // since there the Qt Views don't draw more columns than the header has columns
364 // we can be lazy and simply return the count of header columns
365 // actually this gives us more freedom cause we don't have to ensure that a rows parent
366 // has equal or more columns than that row
368 // if(parent.isValid()) {
369 // AbstractTreeItem *child;
370 // if(child = static_cast<AbstractTreeItem *>(parent.internalPointer())->child(parent.column(), parent.row()))
371 // return child->columnCount();
373 // return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
375 // return rootItem->columnCount();
378 return rootItem->columnCount();
381 QVariant TreeModel::data(const QModelIndex &index, int role) const {
385 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
386 return item->data(index.column(), role);
389 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
393 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
394 return item->setData(index.column(), value, role);
397 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
398 AbstractTreeItem *item;
402 item = static_cast<AbstractTreeItem *>(index.internalPointer());
403 return item->flags();
406 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
407 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
408 return rootItem->data(section, role);
413 void TreeModel::itemDataChanged(int column) {
414 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
415 QModelIndex leftIndex, rightIndex;
421 leftIndex = createIndex(item->row(), 0, item);
422 rightIndex = createIndex(item->row(), item->columnCount()-1, item);
424 leftIndex = createIndex(item->row(), column, item);
425 rightIndex = leftIndex;
428 emit dataChanged(leftIndex, rightIndex);
431 void TreeModel::connectItem(AbstractTreeItem *item) {
432 connect(item, SIGNAL(dataChanged(int)),
433 this, SLOT(itemDataChanged(int)));
435 connect(item, SIGNAL(beginAppendChilds(int, int, int)),
436 this, SLOT(beginAppendChilds(int, int, int)));
437 connect(item, SIGNAL(endAppendChilds()),
438 this, SLOT(endAppendChilds()));
440 connect(item, SIGNAL(beginRemoveChilds(int, int, int)),
441 this, SLOT(beginRemoveChilds(int, int, int)));
442 connect(item, SIGNAL(endRemoveChilds()),
443 this, SLOT(endRemoveChilds()));
446 void TreeModel::beginAppendChilds(int column, int firstRow, int lastRow) {
447 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
449 qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent";
452 QModelIndex parent = indexByItem(parentItem, column);
453 Q_ASSERT(!_aboutToRemoveOrInsert);
455 _aboutToRemoveOrInsert = true;
456 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
457 beginInsertRows(parent, firstRow, lastRow);
460 void TreeModel::endAppendChilds() {
461 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
463 qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent";
466 Q_ASSERT(_aboutToRemoveOrInsert);
467 ChildStatus cs = _childStatus;
468 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
469 Q_ASSERT(cs.parent == parent);
470 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
472 _aboutToRemoveOrInsert = false;
473 for(int i = cs.start; i <= cs.end; i++) {
474 connectItem(parentItem->child(parent.column(), i));
479 void TreeModel::beginRemoveChilds(int column, int firstRow, int lastRow) {
480 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
482 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent";
485 QModelIndex parent = indexByItem(parentItem, column);
486 Q_ASSERT(firstRow <= lastRow);
487 Q_ASSERT(parentItem->childCount(column) > lastRow);
488 Q_ASSERT(!_aboutToRemoveOrInsert);
490 _aboutToRemoveOrInsert = true;
491 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
492 beginRemoveRows(parent, firstRow, lastRow);
495 void TreeModel::endRemoveChilds() {
496 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
498 qWarning() << "TreeModel::endRemoveChilds(): cannot append Childs to unknown parent";
501 Q_ASSERT(_aboutToRemoveOrInsert);
502 ChildStatus cs = _childStatus;
503 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
504 Q_ASSERT(cs.parent == parent);
505 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
507 _aboutToRemoveOrInsert = false;
511 void TreeModel::clear() {
512 rootItem->removeAllChilds();