debug build for adam
[quassel.git] / src / client / treemodel.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 by the Quassel Project                          *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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.                                           *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #include "treemodel.h"
22
23 #include <QDebug>
24
25 /*****************************************
26  *  Abstract Items of a TreeModel
27  *****************************************/
28 AbstractTreeItem::AbstractTreeItem(AbstractTreeItem *parent)
29   : QObject(parent),
30     _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled)
31 {
32 }
33
34 AbstractTreeItem::~AbstractTreeItem() {
35 }
36
37 quint64 AbstractTreeItem::id() const {
38   return (quint64)this;
39 }
40
41 int AbstractTreeItem::defaultColumn() const {
42   // invalid QModelIndexes aka rootNodes get their Childs stuffed into column -1
43   // all others to 0
44   if(parent() == 0)
45     return -1;
46   else
47     return 0;
48 }
49
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;
53     return false;
54   }
55   
56   if(!_childItems.contains(column)) {
57     _childItems[column] = QList<AbstractTreeItem *>();
58   }
59
60   int newRow = _childItems[column].count();
61   emit beginAppendChilds(column, newRow, newRow);
62   _childItems[column].append(item);
63   emit endAppendChilds();
64   
65   return true;
66 }
67
68 bool AbstractTreeItem::newChild(AbstractTreeItem *item) {
69   return newChild(defaultColumn(), item);
70 }
71
72 bool AbstractTreeItem::removeChild(int column, int row) {
73   if(!_childItems.contains(column) || row >= childCount(column))
74     return false;
75
76   emit beginRemoveChilds(column, row, row);
77   AbstractTreeItem *treeitem = _childItems[column].takeAt(row);
78   treeitem->deleteLater();
79   emit endRemoveChilds();
80
81   return true;
82 }
83
84 bool AbstractTreeItem::removeChild(int row) {
85   return removeChild(defaultColumn(), row);
86 }
87
88 bool AbstractTreeItem::removeChildById(int column, const quint64 &id) {
89   if(!_childItems.contains(column))
90     return false;
91
92   for(int i = 0; i < _childItems[column].count(); i++) {
93     if(_childItems[column][i]->id() == id)
94       return removeChild(column, i);
95   }
96   return false;
97 }
98
99 bool AbstractTreeItem::removeChildById(const quint64 &id) {
100   return removeChildById(defaultColumn(), id);
101 }
102
103 void AbstractTreeItem::removeAllChilds() {
104   AbstractTreeItem *child;
105
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()) {
112         child = *childIter;
113         // child->removeAllChilds();
114         childIter = columnIter->erase(childIter);
115         child->deleteLater();
116       }
117       emit endRemoveChilds();
118     }
119     columnIter++;
120   }
121 }
122
123 AbstractTreeItem *AbstractTreeItem::child(int column, int row) const {
124   if(!_childItems.contains(column) || _childItems[column].size() <= row)
125     return 0;
126   else
127     return _childItems[column].value(row);
128 }
129
130 AbstractTreeItem *AbstractTreeItem::child(int row) const {
131   return child(defaultColumn(), row);
132 }
133
134 AbstractTreeItem *AbstractTreeItem::childById(int column, const quint64 &id) const {
135   if(!_childItems.contains(column))
136     return 0;
137
138   for(int i = 0; i < _childItems[column].count(); i++) {
139     if(_childItems[column][i]->id() == id)
140       return _childItems[column][i];
141   }
142   return 0;
143 }
144
145 AbstractTreeItem *AbstractTreeItem::childById(const quint64 &id) const {
146   return childById(defaultColumn(), id);
147 }
148
149 int AbstractTreeItem::childCount(int column) const {
150   if(!_childItems.contains(column))
151     return 0;
152   else
153     return _childItems[column].count();
154 }
155
156 int AbstractTreeItem::childCount() const {
157   return childCount(defaultColumn());
158 }
159
160 int AbstractTreeItem::column() const {
161   if(!parent())
162     return -1;
163
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)))
167       return iter.key();
168     iter++;
169   }
170
171   qWarning() << "AbstractTreeItem::column(): unable to determine the Column of" << this;
172   return parent()->defaultColumn();
173 }
174
175 int AbstractTreeItem::row() const {
176   if(!parent())
177     return -1;
178   else
179     return parent()->_childItems[column()].indexOf(const_cast<AbstractTreeItem *>(this));
180 }
181
182 AbstractTreeItem *AbstractTreeItem::parent() const {
183   return qobject_cast<AbstractTreeItem *>(QObject::parent());
184 }
185
186 Qt::ItemFlags AbstractTreeItem::flags() const {
187   return _flags;
188 }
189
190 void AbstractTreeItem::setFlags(Qt::ItemFlags flags) {
191   _flags = flags;
192 }
193
194 void AbstractTreeItem::dumpChildList() {
195   AbstractTreeItem *child;
196
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()) {
203         child = *childIter;
204         qDebug() << "Column:" << columnIter.key() << "Row:" << child->row() << child << child->id() << child->data(0, Qt::DisplayRole);
205         childIter++;
206       }
207     }
208     columnIter++;
209   }
210   qDebug() << "==== End Of Childlist ====";  
211 }
212
213 /*****************************************
214  * SimpleTreeItem
215  *****************************************/
216 SimpleTreeItem::SimpleTreeItem(const QList<QVariant> &data, AbstractTreeItem *parent)
217   : AbstractTreeItem(parent),
218     _itemData(data)
219 {
220 }
221
222 SimpleTreeItem::~SimpleTreeItem() {
223 }
224
225 QVariant SimpleTreeItem::data(int column, int role) const {
226   if(column >= columnCount() || role != Qt::DisplayRole)
227     return QVariant();
228   else
229     return _itemData[column];
230 }
231
232 bool SimpleTreeItem::setData(int column, const QVariant &value, int role) {
233   if(column > columnCount() || role != Qt::DisplayRole)
234     return false;
235
236   if(column == columnCount())
237     _itemData.append(value);
238   else
239     _itemData[column] = value;
240
241   emit dataChanged(column);
242   return true;
243 }
244
245 int SimpleTreeItem::columnCount() const {
246   return _itemData.count();
247 }
248
249 /*****************************************
250  * PropertyMapItem
251  *****************************************/
252 PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent)
253   : AbstractTreeItem(parent),
254     _propertyOrder(propertyOrder)
255 {
256 }
257
258 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
259   : AbstractTreeItem(parent),
260     _propertyOrder(QStringList())
261 {
262 }
263
264
265 PropertyMapItem::~PropertyMapItem() {
266 }
267   
268 QVariant PropertyMapItem::data(int column, int role) const {
269   if(column >= columnCount())
270     return QVariant();
271
272   switch(role) {
273   case Qt::ToolTipRole:
274     return toolTip(column);
275   case Qt::DisplayRole:
276     return property(_propertyOrder[column].toAscii());
277   default:
278     return QVariant();
279   }
280   
281 }
282
283 bool PropertyMapItem::setData(int column, const QVariant &value, int role) {
284   if(column >= columnCount() || role != Qt::DisplayRole)
285     return false;
286
287   emit dataChanged(column);
288   return setProperty(_propertyOrder[column].toAscii(), value);
289 }
290
291 int PropertyMapItem::columnCount() const {
292   return _propertyOrder.count();
293 }
294   
295 void PropertyMapItem::appendProperty(const QString &property) {
296   _propertyOrder << property;
297 }
298
299
300
301 /*****************************************
302  * TreeModel
303  *****************************************/
304 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
305   : QAbstractItemModel(parent),
306     _childStatus(QModelIndex(), 0, 0, 0),
307     _aboutToRemoveOrInsert(false)
308 {
309   rootItem = new SimpleTreeItem(data, 0);
310   connectItem(rootItem);
311 }
312
313 TreeModel::~TreeModel() {
314   delete rootItem;
315 }
316
317 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
318   if(!hasIndex(row, column, parent))
319     return QModelIndex();
320   
321   AbstractTreeItem *parentItem;
322   
323   if(!parent.isValid())
324     parentItem = rootItem;
325   else
326     parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
327   
328   AbstractTreeItem *childItem = parentItem->child(parent.column(), row);
329
330   if(childItem)
331     return createIndex(row, column, childItem);
332   else
333     return QModelIndex();
334 }
335
336 QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
337   AbstractTreeItem *parentItem; 
338   
339   if(!parent.isValid())
340     parentItem = rootItem;
341   else
342     parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
343   
344   AbstractTreeItem *childItem = parentItem->childById(parent.column(), id);
345   
346   if(childItem)
347     return createIndex(childItem->row(), 0, childItem);
348   else
349     return QModelIndex();
350 }
351
352 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item, int column) const {
353   if(item == 0) {
354     qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
355     return QModelIndex();
356   }
357   
358   if(item == rootItem)
359     return QModelIndex();
360   else
361     return createIndex(item->row(), column, item);
362
363 }
364
365 QModelIndex TreeModel::parent(const QModelIndex &index) const {
366   if(!index.isValid())
367     return QModelIndex();
368   
369   AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
370   AbstractTreeItem *parentItem = static_cast<AbstractTreeItem *>(childItem->parent());
371   
372   if(parentItem == rootItem)
373     return QModelIndex();
374   
375   return createIndex(parentItem->row(), childItem->column(), parentItem);
376 }
377
378 int TreeModel::rowCount(const QModelIndex &parent) const {
379   AbstractTreeItem *parentItem;
380   if(!parent.isValid())
381     parentItem = rootItem;
382   else
383     parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
384
385   return parentItem->childCount(parent.column());
386 }
387
388 int TreeModel::columnCount(const QModelIndex &parent) const {
389   Q_UNUSED(parent)
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
394   
395 //   if(parent.isValid()) {
396 //     AbstractTreeItem *child;
397 //     if(child = static_cast<AbstractTreeItem *>(parent.internalPointer())->child(parent.column(), parent.row()))
398 //       return child->columnCount();
399 //     else
400 //       return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
401 //   } else {
402 //     return rootItem->columnCount();
403 //   }
404
405   return rootItem->columnCount();
406 }
407
408 QVariant TreeModel::data(const QModelIndex &index, int role) const {
409   if(!index.isValid())
410     return QVariant();
411
412   AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
413   return item->data(index.column(), role);
414 }
415
416 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
417   if(!index.isValid())
418     return false;
419
420   AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
421   return item->setData(index.column(), value, role);
422 }
423
424 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
425   AbstractTreeItem *item;
426   if(!index.isValid())
427     item = rootItem;
428   else
429     item = static_cast<AbstractTreeItem *>(index.internalPointer());
430   return item->flags();
431 }
432
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);
436   else
437     return QVariant();
438 }
439
440 void TreeModel::itemDataChanged(int column) {
441   AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
442   QModelIndex leftIndex, rightIndex;
443
444   if(item == rootItem)
445     return;
446
447   if(column == -1) {
448     leftIndex = createIndex(item->row(), 0, item);
449     rightIndex = createIndex(item->row(), item->columnCount()-1, item);
450   } else {
451     leftIndex = createIndex(item->row(), column, item);
452     rightIndex = leftIndex;
453   }
454
455   emit dataChanged(leftIndex, rightIndex);
456 }
457
458 void TreeModel::connectItem(AbstractTreeItem *item) {
459   connect(item, SIGNAL(dataChanged(int)),
460           this, SLOT(itemDataChanged(int)));
461   
462   connect(item, SIGNAL(beginAppendChilds(int, int, int)),
463           this, SLOT(beginAppendChilds(int, int, int)));
464   connect(item, SIGNAL(endAppendChilds()),
465           this, SLOT(endAppendChilds()));
466   
467   connect(item, SIGNAL(beginRemoveChilds(int, int, int)),
468           this, SLOT(beginRemoveChilds(int, int, int)));
469   connect(item, SIGNAL(endRemoveChilds()),
470           this, SLOT(endRemoveChilds()));
471 }
472
473 void TreeModel::beginAppendChilds(int column, int firstRow, int lastRow) {
474   AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
475   if(!parentItem) {
476     qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent";
477     return;
478   }
479   QModelIndex parent = indexByItem(parentItem, column);
480   Q_ASSERT(!_aboutToRemoveOrInsert);
481   
482   _aboutToRemoveOrInsert = true;
483   _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
484   beginInsertRows(parent, firstRow, lastRow);
485 }
486
487 void TreeModel::endAppendChilds() {
488   AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
489   if(!parentItem) {
490     qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent";
491     return;
492   }
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);
498
499   _aboutToRemoveOrInsert = false;
500   for(int i = cs.start; i <= cs.end; i++) {
501     connectItem(parentItem->child(parent.column(), i));
502   }
503   endInsertRows();
504 }
505
506 void TreeModel::beginRemoveChilds(int column, int firstRow, int lastRow) {
507   AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
508   if(!parentItem) {
509     qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent";
510     return;
511   }
512   QModelIndex parent = indexByItem(parentItem, column);
513   Q_ASSERT(firstRow <= lastRow);
514   Q_ASSERT(parentItem->childCount(column) > lastRow);
515   Q_ASSERT(!_aboutToRemoveOrInsert);
516   
517   _aboutToRemoveOrInsert = true;
518   _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
519   beginRemoveRows(parent, firstRow, lastRow);
520 }
521
522 void TreeModel::endRemoveChilds() {
523   AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
524   if(!parentItem) {
525     qWarning() << "TreeModel::endRemoveChilds(): cannot append Childs to unknown parent";
526     return;
527   }
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);
533   
534   _aboutToRemoveOrInsert = false;
535   endRemoveRows();
536 }
537
538 void TreeModel::clear() {
539   rootItem->removeAllChilds();
540 }