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