sessionData--
[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   // check if a child with that ID is already known
61   Q_ASSERT(childById(item->id()) == 0);
62     
63   int newRow = _childItems[column].count();
64   emit beginAppendChilds(column, newRow, newRow);
65   _childItems[column].append(item);
66   emit endAppendChilds();
67   
68   return true;
69 }
70
71 bool AbstractTreeItem::newChild(AbstractTreeItem *item) {
72   return newChild(defaultColumn(), item);
73 }
74
75 bool AbstractTreeItem::removeChild(int column, int row) {
76   if(!_childItems.contains(column) || row >= childCount(column))
77     return false;
78
79   emit beginRemoveChilds(column, row, row);
80   AbstractTreeItem *treeitem = _childItems[column].takeAt(row);
81   treeitem->deleteLater();
82   emit endRemoveChilds();
83
84   return true;
85 }
86
87 bool AbstractTreeItem::removeChild(int row) {
88   return removeChild(defaultColumn(), row);
89 }
90
91 bool AbstractTreeItem::removeChildById(int column, const quint64 &id) {
92   if(!_childItems.contains(column))
93     return false;
94
95   for(int i = 0; i < _childItems[column].count(); i++) {
96     if(_childItems[column][i]->id() == id)
97       return removeChild(column, i);
98   }
99   return false;
100 }
101
102 bool AbstractTreeItem::removeChildById(const quint64 &id) {
103   return removeChildById(defaultColumn(), id);
104 }
105
106 void AbstractTreeItem::removeAllChilds() {
107   AbstractTreeItem *child;
108
109   QHash<int, QList<AbstractTreeItem *> >::iterator columnIter = _childItems.begin();
110   while(columnIter != _childItems.end()) {
111     if(columnIter->count() > 0) {
112       emit beginRemoveChilds(columnIter.key(), 0, columnIter->count() - 1);
113       QList<AbstractTreeItem *>::iterator childIter = columnIter->begin();
114       while(childIter != columnIter->end()) {
115         child = *childIter;
116         // child->removeAllChilds();
117         childIter = columnIter->erase(childIter);
118         child->deleteLater();
119       }
120       emit endRemoveChilds();
121     }
122     columnIter++;
123   }
124 }
125
126 AbstractTreeItem *AbstractTreeItem::child(int column, int row) const {
127   if(!_childItems.contains(column) || _childItems[column].size() <= row)
128     return 0;
129   else
130     return _childItems[column].value(row);
131 }
132
133 AbstractTreeItem *AbstractTreeItem::child(int row) const {
134   return child(defaultColumn(), row);
135 }
136
137 AbstractTreeItem *AbstractTreeItem::childById(int column, const quint64 &id) const {
138   if(!_childItems.contains(column))
139     return 0;
140
141   for(int i = 0; i < _childItems[column].count(); i++) {
142     if(_childItems[column][i]->id() == id)
143       return _childItems[column][i];
144   }
145   return 0;
146 }
147
148 AbstractTreeItem *AbstractTreeItem::childById(const quint64 &id) const {
149   return childById(defaultColumn(), id);
150 }
151
152 int AbstractTreeItem::childCount(int column) const {
153   if(!_childItems.contains(column))
154     return 0;
155   else
156     return _childItems[column].count();
157 }
158
159 int AbstractTreeItem::childCount() const {
160   return childCount(defaultColumn());
161 }
162
163 int AbstractTreeItem::column() const {
164   if(!parent())
165     return -1;
166
167   QHash<int, QList<AbstractTreeItem*> >::const_iterator iter = parent()->_childItems.constBegin();
168   while(iter != parent()->_childItems.constEnd()) {
169     if(iter->contains(const_cast<AbstractTreeItem *>(this)))
170       return iter.key();
171     iter++;
172   }
173
174   qWarning() << "AbstractTreeItem::column(): unable to determine the Column of" << this;
175   return parent()->defaultColumn();
176 }
177
178 int AbstractTreeItem::row() const {
179   if(!parent())
180     return -1;
181   else
182     return parent()->_childItems[column()].indexOf(const_cast<AbstractTreeItem *>(this));
183 }
184
185 AbstractTreeItem *AbstractTreeItem::parent() const {
186   return qobject_cast<AbstractTreeItem *>(QObject::parent());
187 }
188
189 Qt::ItemFlags AbstractTreeItem::flags() const {
190   return _flags;
191 }
192
193 void AbstractTreeItem::setFlags(Qt::ItemFlags flags) {
194   _flags = flags;
195 }
196
197 void AbstractTreeItem::dumpChildList() {
198   AbstractTreeItem *child;
199
200   qDebug() << "==== Childlist for Item:" << this << id() << "====";
201   QHash<int, QList<AbstractTreeItem *> >::iterator columnIter = _childItems.begin();
202   while(columnIter != _childItems.end()) {
203     if(columnIter->count() > 0) {
204       QList<AbstractTreeItem *>::const_iterator childIter = columnIter->constBegin();
205       while(childIter != columnIter->constEnd()) {
206         child = *childIter;
207         qDebug() << "Column:" << columnIter.key() << "Row:" << child->row() << child << child->id() << child->data(0, Qt::DisplayRole);
208         childIter++;
209       }
210     }
211     columnIter++;
212   }
213   qDebug() << "==== End Of Childlist ====";  
214 }
215
216 /*****************************************
217  * SimpleTreeItem
218  *****************************************/
219 SimpleTreeItem::SimpleTreeItem(const QList<QVariant> &data, AbstractTreeItem *parent)
220   : AbstractTreeItem(parent),
221     _itemData(data)
222 {
223 }
224
225 SimpleTreeItem::~SimpleTreeItem() {
226 }
227
228 QVariant SimpleTreeItem::data(int column, int role) const {
229   if(column >= columnCount() || role != Qt::DisplayRole)
230     return QVariant();
231   else
232     return _itemData[column];
233 }
234
235 bool SimpleTreeItem::setData(int column, const QVariant &value, int role) {
236   if(column > columnCount() || role != Qt::DisplayRole)
237     return false;
238
239   if(column == columnCount())
240     _itemData.append(value);
241   else
242     _itemData[column] = value;
243
244   emit dataChanged(column);
245   return true;
246 }
247
248 int SimpleTreeItem::columnCount() const {
249   return _itemData.count();
250 }
251
252 /*****************************************
253  * PropertyMapItem
254  *****************************************/
255 PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent)
256   : AbstractTreeItem(parent),
257     _propertyOrder(propertyOrder)
258 {
259 }
260
261 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
262   : AbstractTreeItem(parent),
263     _propertyOrder(QStringList())
264 {
265 }
266
267
268 PropertyMapItem::~PropertyMapItem() {
269 }
270   
271 QVariant PropertyMapItem::data(int column, int role) const {
272   if(column >= columnCount())
273     return QVariant();
274
275   switch(role) {
276   case Qt::ToolTipRole:
277     return toolTip(column);
278   case Qt::DisplayRole:
279     return property(_propertyOrder[column].toAscii());
280   default:
281     return QVariant();
282   }
283   
284 }
285
286 bool PropertyMapItem::setData(int column, const QVariant &value, int role) {
287   if(column >= columnCount() || role != Qt::DisplayRole)
288     return false;
289
290   emit dataChanged(column);
291   return setProperty(_propertyOrder[column].toAscii(), value);
292 }
293
294 int PropertyMapItem::columnCount() const {
295   return _propertyOrder.count();
296 }
297   
298 void PropertyMapItem::appendProperty(const QString &property) {
299   _propertyOrder << property;
300 }
301
302
303
304 /*****************************************
305  * TreeModel
306  *****************************************/
307 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
308   : QAbstractItemModel(parent),
309     _childStatus(QModelIndex(), 0, 0, 0),
310     _aboutToRemoveOrInsert(false)
311 {
312   rootItem = new SimpleTreeItem(data, 0);
313   connectItem(rootItem);
314 }
315
316 TreeModel::~TreeModel() {
317   delete rootItem;
318 }
319
320 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
321   if(!hasIndex(row, column, parent))
322     return QModelIndex();
323   
324   AbstractTreeItem *parentItem;
325   
326   if(!parent.isValid())
327     parentItem = rootItem;
328   else
329     parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
330   
331   AbstractTreeItem *childItem = parentItem->child(parent.column(), row);
332
333   if(childItem)
334     return createIndex(row, column, childItem);
335   else
336     return QModelIndex();
337 }
338
339 QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
340   AbstractTreeItem *parentItem; 
341   
342   if(!parent.isValid())
343     parentItem = rootItem;
344   else
345     parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
346   
347   AbstractTreeItem *childItem = parentItem->childById(parent.column(), id);
348   
349   if(childItem)
350     return createIndex(childItem->row(), 0, childItem);
351   else
352     return QModelIndex();
353 }
354
355 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item, int column) const {
356   if(item == 0) {
357     qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
358     return QModelIndex();
359   }
360   
361   if(item == rootItem)
362     return QModelIndex();
363   else
364     return createIndex(item->row(), column, item);
365
366 }
367
368 QModelIndex TreeModel::parent(const QModelIndex &index) const {
369   if(!index.isValid())
370     return QModelIndex();
371   
372   AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
373   AbstractTreeItem *parentItem = static_cast<AbstractTreeItem *>(childItem->parent());
374   
375   if(parentItem == rootItem)
376     return QModelIndex();
377   
378   return createIndex(parentItem->row(), childItem->column(), parentItem);
379 }
380
381 int TreeModel::rowCount(const QModelIndex &parent) const {
382   AbstractTreeItem *parentItem;
383   if(!parent.isValid())
384     parentItem = rootItem;
385   else
386     parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
387
388   return parentItem->childCount(parent.column());
389 }
390
391 int TreeModel::columnCount(const QModelIndex &parent) const {
392   Q_UNUSED(parent)
393   // since there the Qt Views don't draw more columns than the header has columns
394   // we can be lazy and simply return the count of header columns
395   // actually this gives us more freedom cause we don't have to ensure that a rows parent
396   // has equal or more columns than that row
397   
398 //   if(parent.isValid()) {
399 //     AbstractTreeItem *child;
400 //     if(child = static_cast<AbstractTreeItem *>(parent.internalPointer())->child(parent.column(), parent.row()))
401 //       return child->columnCount();
402 //     else
403 //       return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
404 //   } else {
405 //     return rootItem->columnCount();
406 //   }
407
408   return rootItem->columnCount();
409 }
410
411 QVariant TreeModel::data(const QModelIndex &index, int role) const {
412   if(!index.isValid())
413     return QVariant();
414
415   AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
416   return item->data(index.column(), role);
417 }
418
419 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
420   if(!index.isValid())
421     return false;
422
423   AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
424   return item->setData(index.column(), value, role);
425 }
426
427 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
428   AbstractTreeItem *item;
429   if(!index.isValid())
430     item = rootItem;
431   else
432     item = static_cast<AbstractTreeItem *>(index.internalPointer());
433   return item->flags();
434 }
435
436 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
437   if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
438     return rootItem->data(section, role);
439   else
440     return QVariant();
441 }
442
443 void TreeModel::itemDataChanged(int column) {
444   AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
445   QModelIndex leftIndex, rightIndex;
446
447   if(item == rootItem)
448     return;
449
450   if(column == -1) {
451     leftIndex = createIndex(item->row(), 0, item);
452     rightIndex = createIndex(item->row(), item->columnCount()-1, item);
453   } else {
454     leftIndex = createIndex(item->row(), column, item);
455     rightIndex = leftIndex;
456   }
457
458   emit dataChanged(leftIndex, rightIndex);
459 }
460
461 void TreeModel::connectItem(AbstractTreeItem *item) {
462   connect(item, SIGNAL(dataChanged(int)),
463           this, SLOT(itemDataChanged(int)));
464   
465   connect(item, SIGNAL(beginAppendChilds(int, int, int)),
466           this, SLOT(beginAppendChilds(int, int, int)));
467   connect(item, SIGNAL(endAppendChilds()),
468           this, SLOT(endAppendChilds()));
469   
470   connect(item, SIGNAL(beginRemoveChilds(int, int, int)),
471           this, SLOT(beginRemoveChilds(int, int, int)));
472   connect(item, SIGNAL(endRemoveChilds()),
473           this, SLOT(endRemoveChilds()));
474 }
475
476 void TreeModel::beginAppendChilds(int column, int firstRow, int lastRow) {
477   AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
478   if(!parentItem) {
479     qWarning() << "TreeModel::beginAppendChilds(): cannot append Childs to unknown parent";
480     return;
481   }
482   QModelIndex parent = indexByItem(parentItem, column);
483   Q_ASSERT(!_aboutToRemoveOrInsert);
484   
485   _aboutToRemoveOrInsert = true;
486   _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
487   beginInsertRows(parent, firstRow, lastRow);
488 }
489
490 void TreeModel::endAppendChilds() {
491   AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
492   if(!parentItem) {
493     qWarning() << "TreeModel::endAppendChilds(): cannot append Childs to unknown parent";
494     return;
495   }
496   Q_ASSERT(_aboutToRemoveOrInsert);
497   ChildStatus cs = _childStatus;
498   QModelIndex parent = indexByItem(parentItem, cs.parent.column());
499   Q_ASSERT(cs.parent == parent);
500   Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
501
502   _aboutToRemoveOrInsert = false;
503   for(int i = cs.start; i <= cs.end; i++) {
504     connectItem(parentItem->child(parent.column(), i));
505   }
506   endInsertRows();
507 }
508
509 void TreeModel::beginRemoveChilds(int column, int firstRow, int lastRow) {
510   AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
511   if(!parentItem) {
512     qWarning() << "TreeModel::beginRemoveChilds(): cannot append Childs to unknown parent";
513     return;
514   }
515   QModelIndex parent = indexByItem(parentItem, column);
516   Q_ASSERT(firstRow <= lastRow);
517   Q_ASSERT(parentItem->childCount(column) > lastRow);
518   Q_ASSERT(!_aboutToRemoveOrInsert);
519   
520   _aboutToRemoveOrInsert = true;
521   _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
522   beginRemoveRows(parent, firstRow, lastRow);
523 }
524
525 void TreeModel::endRemoveChilds() {
526   AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
527   if(!parentItem) {
528     qWarning() << "TreeModel::endRemoveChilds(): cannot append Childs to unknown parent";
529     return;
530   }
531   Q_ASSERT(_aboutToRemoveOrInsert);
532   ChildStatus cs = _childStatus;
533   QModelIndex parent = indexByItem(parentItem, cs.parent.column());
534   Q_ASSERT(cs.parent == parent);
535   Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
536   
537   _aboutToRemoveOrInsert = false;
538   endRemoveRows();
539 }
540
541 void TreeModel::clear() {
542   rootItem->removeAllChilds();
543 }