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())
254 case Qt::ToolTipRole:
255 return toolTip(column);
256 case Qt::DisplayRole:
257 return property(_propertyOrder[column].toAscii());
264 bool PropertyMapItem::setData(int column, const QVariant &value, int role) {
265 if(column >= columnCount() || role != Qt::DisplayRole)
268 emit dataChanged(column);
269 return setProperty(_propertyOrder[column].toAscii(), value);
272 int PropertyMapItem::columnCount() const {
273 return _propertyOrder.count();
276 void PropertyMapItem::appendProperty(const QString &property) {
277 _propertyOrder << property;
282 /*****************************************
284 *****************************************/
285 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
286 : QAbstractItemModel(parent),
287 _childStatus(QModelIndex(), 0, 0, 0),
288 _aboutToRemoveOrInsert(false)
290 rootItem = new SimpleTreeItem(data, 0);
291 connectItem(rootItem);
294 TreeModel::~TreeModel() {
298 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
299 if(!hasIndex(row, column, parent))
300 return QModelIndex();
302 AbstractTreeItem *parentItem;
304 if(!parent.isValid())
305 parentItem = rootItem;
307 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
309 AbstractTreeItem *childItem = parentItem->child(parent.column(), row);
312 return createIndex(row, column, childItem);
314 return QModelIndex();
317 QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
318 AbstractTreeItem *parentItem;
320 if(!parent.isValid())
321 parentItem = rootItem;
323 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
325 AbstractTreeItem *childItem = parentItem->childById(parent.column(), id);
328 return createIndex(childItem->row(), 0, childItem);
330 return QModelIndex();
333 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item, int column) const {
335 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
336 return QModelIndex();
340 return QModelIndex();
342 return createIndex(item->row(), column, item);
346 QModelIndex TreeModel::parent(const QModelIndex &index) const {
348 return QModelIndex();
350 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
351 AbstractTreeItem *parentItem = static_cast<AbstractTreeItem *>(childItem->parent());
353 if(parentItem == rootItem)
354 return QModelIndex();
356 return createIndex(parentItem->row(), childItem->column(), parentItem);
359 int TreeModel::rowCount(const QModelIndex &parent) const {
360 AbstractTreeItem *parentItem;
361 if(!parent.isValid())
362 parentItem = rootItem;
364 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
366 return parentItem->childCount(parent.column());
369 int TreeModel::columnCount(const QModelIndex &parent) const {
371 // since there the Qt Views don't draw more columns than the header has columns
372 // we can be lazy and simply return the count of header columns
373 // actually this gives us more freedom cause we don't have to ensure that a rows parent
374 // has equal or more columns than that row
376 // if(parent.isValid()) {
377 // AbstractTreeItem *child;
378 // if(child = static_cast<AbstractTreeItem *>(parent.internalPointer())->child(parent.column(), parent.row()))
379 // return child->columnCount();
381 // return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
383 // return rootItem->columnCount();
386 return rootItem->columnCount();
389 QVariant TreeModel::data(const QModelIndex &index, int role) const {
393 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
394 return item->data(index.column(), role);
397 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
401 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
402 return item->setData(index.column(), value, role);
405 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
406 AbstractTreeItem *item;
410 item = static_cast<AbstractTreeItem *>(index.internalPointer());
411 return item->flags();
414 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
415 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
416 return rootItem->data(section, role);
421 void TreeModel::itemDataChanged(int column) {
422 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
423 QModelIndex leftIndex, rightIndex;
429 leftIndex = createIndex(item->row(), 0, item);
430 rightIndex = createIndex(item->row(), item->columnCount()-1, item);
432 leftIndex = createIndex(item->row(), column, item);
433 rightIndex = leftIndex;
436 emit dataChanged(leftIndex, rightIndex);
439 void TreeModel::connectItem(AbstractTreeItem *item) {
440 connect(item, SIGNAL(dataChanged(int)),
441 this, SLOT(itemDataChanged(int)));
443 connect(item, SIGNAL(beginAppendChilds(int, int, int)),
444 this, SLOT(beginAppendChilds(int, int, int)));
445 connect(item, SIGNAL(endAppendChilds()),
446 this, SLOT(endAppendChilds()));
448 connect(item, SIGNAL(beginRemoveChilds(int, int, int)),
449 this, SLOT(beginRemoveChilds(int, int, int)));
450 connect(item, SIGNAL(endRemoveChilds()),
451 this, SLOT(endRemoveChilds()));
454 void TreeModel::beginAppendChilds(int column, int firstRow, int lastRow) {
455 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
457 qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent";
460 QModelIndex parent = indexByItem(parentItem, column);
461 Q_ASSERT(!_aboutToRemoveOrInsert);
463 _aboutToRemoveOrInsert = true;
464 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
465 beginInsertRows(parent, firstRow, lastRow);
468 void TreeModel::endAppendChilds() {
469 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
471 qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent";
474 Q_ASSERT(_aboutToRemoveOrInsert);
475 ChildStatus cs = _childStatus;
476 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
477 Q_ASSERT(cs.parent == parent);
478 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
480 _aboutToRemoveOrInsert = false;
481 for(int i = cs.start; i <= cs.end; i++) {
482 connectItem(parentItem->child(parent.column(), i));
487 void TreeModel::beginRemoveChilds(int column, int firstRow, int lastRow) {
488 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
490 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent";
493 QModelIndex parent = indexByItem(parentItem, column);
494 Q_ASSERT(firstRow <= lastRow);
495 Q_ASSERT(parentItem->childCount(column) > lastRow);
496 Q_ASSERT(!_aboutToRemoveOrInsert);
498 _aboutToRemoveOrInsert = true;
499 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
500 beginRemoveRows(parent, firstRow, lastRow);
503 void TreeModel::endRemoveChilds() {
504 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
506 qWarning() << "TreeModel::endRemoveChilds(): cannot append Childs to unknown parent";
509 Q_ASSERT(_aboutToRemoveOrInsert);
510 ChildStatus cs = _childStatus;
511 QModelIndex parent = indexByItem(parentItem, cs.parent.column());
512 Q_ASSERT(cs.parent == parent);
513 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
515 _aboutToRemoveOrInsert = false;
519 void TreeModel::clear() {
520 rootItem->removeAllChilds();