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"
24 #include <QCoreApplication>
26 /*****************************************
27 * Abstract Items of a TreeModel
28 *****************************************/
29 AbstractTreeItem::AbstractTreeItem(AbstractTreeItem *parent)
31 _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled)
35 AbstractTreeItem::~AbstractTreeItem() {
38 quint64 AbstractTreeItem::id() const {
42 bool AbstractTreeItem::newChild(AbstractTreeItem *item) {
43 // check if a child with that ID is already known
44 Q_ASSERT(childById(item->id()) == 0);
46 int newRow = childCount();
47 emit beginAppendChilds(newRow, newRow);
48 _childItems.append(item);
49 emit endAppendChilds();
54 bool AbstractTreeItem::removeChild(int row) {
55 if(childCount() <= row)
58 emit beginRemoveChilds(row, row);
59 AbstractTreeItem *treeitem = _childItems.takeAt(row);
60 treeitem->deleteLater();
61 emit endRemoveChilds();
66 bool AbstractTreeItem::removeChildById(const quint64 &id) {
67 const int numChilds = childCount();
69 for(int i = 0; i < numChilds; i++) {
70 if(_childItems[i]->id() == id)
71 return removeChild(i);
77 void AbstractTreeItem::removeAllChilds() {
78 const int numChilds = childCount();
83 AbstractTreeItem *child;
85 emit beginRemoveChilds(0, numChilds - 1);
86 QList<AbstractTreeItem *>::iterator childIter = _childItems.begin();
87 while(childIter != _childItems.end()) {
89 childIter = _childItems.erase(childIter);
92 emit endRemoveChilds();
95 AbstractTreeItem *AbstractTreeItem::child(int row) const {
96 if(childCount() <= row)
99 return _childItems[row];
102 AbstractTreeItem *AbstractTreeItem::childById(const quint64 &id) const {
103 const int numChilds = childCount();
104 for(int i = 0; i < numChilds; i++) {
105 if(_childItems[i]->id() == id)
106 return _childItems[i];
111 int AbstractTreeItem::childCount() const {
112 return _childItems.count();
115 int AbstractTreeItem::row() const {
119 return parent()->_childItems.indexOf(const_cast<AbstractTreeItem *>(this));
122 AbstractTreeItem *AbstractTreeItem::parent() const {
123 return qobject_cast<AbstractTreeItem *>(QObject::parent());
126 Qt::ItemFlags AbstractTreeItem::flags() const {
130 void AbstractTreeItem::setFlags(Qt::ItemFlags flags) {
134 void AbstractTreeItem::dumpChildList() {
135 qDebug() << "==== Childlist for Item:" << this << id() << "====";
136 if(childCount() > 0) {
137 AbstractTreeItem *child;
138 QList<AbstractTreeItem *>::const_iterator childIter = _childItems.constBegin();
139 while(childIter != _childItems.constEnd()) {
141 qDebug() << "Row:" << child->row() << child << child->id() << child->data(0, Qt::DisplayRole);
145 qDebug() << "==== End Of Childlist ====";
148 /*****************************************
150 *****************************************/
151 SimpleTreeItem::SimpleTreeItem(const QList<QVariant> &data, AbstractTreeItem *parent)
152 : AbstractTreeItem(parent),
157 SimpleTreeItem::~SimpleTreeItem() {
160 QVariant SimpleTreeItem::data(int column, int role) const {
161 if(column >= columnCount() || role != Qt::DisplayRole)
164 return _itemData[column];
167 bool SimpleTreeItem::setData(int column, const QVariant &value, int role) {
168 if(column > columnCount() || role != Qt::DisplayRole)
171 if(column == columnCount())
172 _itemData.append(value);
174 _itemData[column] = value;
176 emit dataChanged(column);
180 int SimpleTreeItem::columnCount() const {
181 return _itemData.count();
184 /*****************************************
186 *****************************************/
187 PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent)
188 : AbstractTreeItem(parent),
189 _propertyOrder(propertyOrder)
193 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
194 : AbstractTreeItem(parent),
195 _propertyOrder(QStringList())
200 PropertyMapItem::~PropertyMapItem() {
203 QVariant PropertyMapItem::data(int column, int role) const {
204 if(column >= columnCount())
208 case Qt::ToolTipRole:
209 return toolTip(column);
210 case Qt::DisplayRole:
211 return property(_propertyOrder[column].toAscii());
218 bool PropertyMapItem::setData(int column, const QVariant &value, int role) {
219 if(column >= columnCount() || role != Qt::DisplayRole)
222 emit dataChanged(column);
223 return setProperty(_propertyOrder[column].toAscii(), value);
226 int PropertyMapItem::columnCount() const {
227 return _propertyOrder.count();
230 void PropertyMapItem::appendProperty(const QString &property) {
231 _propertyOrder << property;
236 /*****************************************
238 *****************************************/
239 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
240 : QAbstractItemModel(parent),
241 _childStatus(QModelIndex(), 0, 0, 0),
242 _aboutToRemoveOrInsert(false)
244 rootItem = new SimpleTreeItem(data, 0);
245 connectItem(rootItem);
247 if(QCoreApplication::instance()->arguments().contains("--debugmodel")) {
248 connect(this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
249 this, SLOT(debug_rowsAboutToBeInserted(const QModelIndex &, int, int)));
250 connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
251 this, SLOT(debug_rowsAboutToBeRemoved(const QModelIndex &, int, int)));
252 connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
253 this, SLOT(debug_rowsInserted(const QModelIndex &, int, int)));
254 connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
255 this, SLOT(debug_rowsRemoved(const QModelIndex &, int, int)));
259 TreeModel::~TreeModel() {
263 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
264 if(!hasIndex(row, column, parent))
265 return QModelIndex();
267 AbstractTreeItem *parentItem;
269 if(!parent.isValid())
270 parentItem = rootItem;
272 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
274 AbstractTreeItem *childItem = parentItem->child(row);
277 return createIndex(row, column, childItem);
279 return QModelIndex();
282 QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
283 AbstractTreeItem *parentItem;
285 if(!parent.isValid())
286 parentItem = rootItem;
288 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
290 AbstractTreeItem *childItem = parentItem->childById(id);
293 return createIndex(childItem->row(), 0, childItem);
295 return QModelIndex();
298 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item) const {
300 qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
301 return QModelIndex();
305 return QModelIndex();
307 return createIndex(item->row(), 0, item);
310 QModelIndex TreeModel::parent(const QModelIndex &index) const {
311 if(!index.isValid()) {
312 qWarning() << "TreeModel::parent(): has been asked for the rootItems Parent!";
313 return QModelIndex();
316 AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
317 AbstractTreeItem *parentItem = childItem->parent();
319 Q_ASSERT(parentItem);
320 if(parentItem == rootItem)
321 return QModelIndex();
323 return createIndex(parentItem->row(), 0, parentItem);
326 int TreeModel::rowCount(const QModelIndex &parent) const {
327 AbstractTreeItem *parentItem;
328 if(!parent.isValid())
329 parentItem = rootItem;
331 parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
333 return parentItem->childCount();
336 int TreeModel::columnCount(const QModelIndex &parent) const {
338 // since there the Qt Views don't draw more columns than the header has columns
339 // we can be lazy and simply return the count of header columns
340 // actually this gives us more freedom cause we don't have to ensure that a rows parent
341 // has equal or more columns than that row
343 // if(parent.isValid()) {
344 // AbstractTreeItem *child;
345 // if(child = static_cast<AbstractTreeItem *>(parent.internalPointer())->child(parent.column(), parent.row()))
346 // return child->columnCount();
348 // return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
350 // return rootItem->columnCount();
353 return rootItem->columnCount();
356 QVariant TreeModel::data(const QModelIndex &index, int role) const {
360 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
361 return item->data(index.column(), role);
364 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
368 AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
369 return item->setData(index.column(), value, role);
372 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
373 AbstractTreeItem *item;
377 item = static_cast<AbstractTreeItem *>(index.internalPointer());
378 return item->flags();
381 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
382 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
383 return rootItem->data(section, role);
388 void TreeModel::itemDataChanged(int column) {
389 AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
390 QModelIndex leftIndex, rightIndex;
396 leftIndex = createIndex(item->row(), 0, item);
397 rightIndex = createIndex(item->row(), item->columnCount() - 1, item);
399 leftIndex = createIndex(item->row(), column, item);
400 rightIndex = leftIndex;
403 emit dataChanged(leftIndex, rightIndex);
406 void TreeModel::connectItem(AbstractTreeItem *item) {
407 connect(item, SIGNAL(dataChanged(int)),
408 this, SLOT(itemDataChanged(int)));
410 connect(item, SIGNAL(beginAppendChilds(int, int)),
411 this, SLOT(beginAppendChilds(int, int)));
412 connect(item, SIGNAL(endAppendChilds()),
413 this, SLOT(endAppendChilds()));
415 connect(item, SIGNAL(beginRemoveChilds(int, int)),
416 this, SLOT(beginRemoveChilds(int, int)));
417 connect(item, SIGNAL(endRemoveChilds()),
418 this, SLOT(endRemoveChilds()));
421 void TreeModel::beginAppendChilds(int firstRow, int lastRow) {
422 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
424 qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent";
428 QModelIndex parent = indexByItem(parentItem);
429 Q_ASSERT(!_aboutToRemoveOrInsert);
431 _aboutToRemoveOrInsert = true;
432 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
433 beginInsertRows(parent, firstRow, lastRow);
436 void TreeModel::endAppendChilds() {
437 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
439 qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent";
442 Q_ASSERT(_aboutToRemoveOrInsert);
443 ChildStatus cs = _childStatus;
444 QModelIndex parent = indexByItem(parentItem);
445 Q_ASSERT(cs.parent == parent);
446 Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
448 _aboutToRemoveOrInsert = false;
449 for(int i = cs.start; i <= cs.end; i++) {
450 connectItem(parentItem->child(i));
455 void TreeModel::beginRemoveChilds(int firstRow, int lastRow) {
456 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
458 qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent";
461 QModelIndex parent = indexByItem(parentItem);
462 Q_ASSERT(firstRow <= lastRow);
463 Q_ASSERT(parentItem->childCount() > lastRow);
464 Q_ASSERT(!_aboutToRemoveOrInsert);
466 _aboutToRemoveOrInsert = true;
467 _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
468 beginRemoveRows(parent, firstRow, lastRow);
471 void TreeModel::endRemoveChilds() {
472 AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
474 qWarning() << "TreeModel::endRemoveChilds(): cannot append Childs to unknown parent";
477 Q_ASSERT(_aboutToRemoveOrInsert);
478 ChildStatus cs = _childStatus;
479 QModelIndex parent = indexByItem(parentItem);
480 Q_ASSERT(cs.parent == parent);
481 Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
483 _aboutToRemoveOrInsert = false;
487 void TreeModel::clear() {
488 rootItem->removeAllChilds();
491 void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) {
492 // qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
495 void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) {
496 AbstractTreeItem *parentItem;
497 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
499 parentItem = rootItem;
500 qDebug() << "#" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
503 AbstractTreeItem *childItem;
504 for(int i = end; i >= start; i--) {
505 child = parent.child(i, 0);
506 childItem = parentItem->child(i);
508 qDebug() << ">>>" << i << child << childItem->id() << child.data().toString();
512 void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end) {
513 AbstractTreeItem *parentItem;
514 parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
516 parentItem = rootItem;
517 qDebug() << "#" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
520 AbstractTreeItem *childItem;
521 for(int i = start; i <= end; i++) {
522 child = parent.child(i, 0);
523 childItem = parentItem->child(i);
525 qDebug() << "<<<" << i << child << childItem->id() << child.data().toString();
529 void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end) {
530 // qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;