fixed the scrollbar issue in the NickView
[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   emit dataChanged(column);
251   return true;
252 }
253
254 int SimpleTreeItem::columnCount() const {
255   return _itemData.count();
256 }
257
258 /*****************************************
259  * PropertyMapItem
260  *****************************************/
261 PropertyMapItem::PropertyMapItem(const QStringList &propertyOrder, AbstractTreeItem *parent)
262   : AbstractTreeItem(parent),
263     _propertyOrder(propertyOrder)
264 {
265 }
266
267 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
268   : AbstractTreeItem(parent),
269     _propertyOrder(QStringList())
270 {
271 }
272
273
274 PropertyMapItem::~PropertyMapItem() {
275 }
276   
277 QVariant PropertyMapItem::data(int column, int role) const {
278   if(column >= columnCount() || role != Qt::DisplayRole)
279     return QVariant();
280
281   return property(_propertyOrder[column].toAscii());
282 }
283
284 bool PropertyMapItem::setData(int column, const QVariant &value, int role) {
285   if(column >= columnCount() || role != Qt::DisplayRole)
286     return false;
287
288   emit dataChanged(column);
289   return setProperty(_propertyOrder[column].toAscii(), value);
290 }
291
292 int PropertyMapItem::columnCount() const {
293   return _propertyOrder.count();
294 }
295   
296 void PropertyMapItem::appendProperty(const QString &property) {
297   _propertyOrder << property;
298 }
299
300
301
302 /*****************************************
303  * TreeModel
304  *****************************************/
305 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
306   : QAbstractItemModel(parent)
307 {
308   rootItem = new SimpleTreeItem(data, 0);
309
310   connect(rootItem, SIGNAL(dataChanged(int)),
311           this, SLOT(itemDataChanged(int)));
312   
313   connect(rootItem, SIGNAL(newChild(AbstractTreeItem *)),
314           this, SLOT(newChild(AbstractTreeItem *)));
315
316   connect(rootItem, SIGNAL(beginRemoveChilds(int, int)),
317           this, SLOT(beginRemoveChilds(int, int)));
318   
319   connect(rootItem, SIGNAL(endRemoveChilds()),
320           this, SLOT(endRemoveChilds()));
321
322 }
323
324 TreeModel::~TreeModel() {
325   delete rootItem;
326 }
327
328 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const {
329   if(!hasIndex(row, column, parent))
330     return QModelIndex();
331   
332   AbstractTreeItem *parentItem;
333   
334   if(!parent.isValid())
335     parentItem = rootItem;
336   else
337     parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
338   
339   AbstractTreeItem *childItem = parentItem->child(parent.column(), row);
340
341   if(childItem)
342     return createIndex(row, column, childItem);
343   else
344     return QModelIndex();
345 }
346
347 QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
348   AbstractTreeItem *parentItem; 
349   
350   if(!parent.isValid())
351     parentItem = rootItem;
352   else
353     parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
354   
355   AbstractTreeItem *childItem = parentItem->childById(parent.column(), id);
356   
357   if(childItem)
358     return createIndex(childItem->row(), 0, childItem);
359   else
360     return QModelIndex();
361 }
362
363 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item) const {
364   if(item == 0) {
365     qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
366     return QModelIndex();
367   }
368   
369   if(item == rootItem)
370     return QModelIndex();
371   else
372     return createIndex(item->row(), 0, item);
373
374 }
375
376 QModelIndex TreeModel::parent(const QModelIndex &index) const {
377   if(!index.isValid())
378     return QModelIndex();
379   
380   AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
381   AbstractTreeItem *parentItem = static_cast<AbstractTreeItem *>(childItem->parent());
382   
383   if(parentItem == rootItem)
384     return QModelIndex();
385   
386   return createIndex(parentItem->row(), 0, parentItem);
387 }
388
389 int TreeModel::rowCount(const QModelIndex &parent) const {
390   AbstractTreeItem *parentItem;
391   if(!parent.isValid())
392     parentItem = rootItem;
393   else
394     parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
395
396   return parentItem->childCount(parent.column());
397 }
398
399 int TreeModel::columnCount(const QModelIndex &parent) const {
400   Q_UNUSED(parent)
401   // since there the Qt Views don't draw more columns than the header has columns
402   // we can be lazy and simply return the count of header columns
403   // actually this gives us more freedom cause we don't have to ensure that a rows parent
404   // has equal or more columns than that row
405   
406 //   if(parent.isValid()) {
407 //     AbstractTreeItem *child;
408 //     if(child = static_cast<AbstractTreeItem *>(parent.internalPointer())->child(parent.column(), parent.row()))
409 //       return child->columnCount();
410 //     else
411 //       return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
412 //   } else {
413 //     return rootItem->columnCount();
414 //   }
415
416   return rootItem->columnCount();
417 }
418
419 QVariant TreeModel::data(const QModelIndex &index, int role) const {
420   if(!index.isValid())
421     return QVariant();
422
423   AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
424   return item->data(index.column(), role);
425 }
426
427 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
428   if(!index.isValid())
429     return false;
430
431   AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
432   return item->setData(index.column(), value, role);
433 }
434
435 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
436   AbstractTreeItem *item;
437   if(!index.isValid())
438     item = rootItem;
439   else
440     item = static_cast<AbstractTreeItem *>(index.internalPointer());
441   return item->flags();
442 }
443
444 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
445   if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
446     return rootItem->data(section, role);
447   else
448     return QVariant();
449 }
450
451 void TreeModel::itemDataChanged(int column) {
452   AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
453   QModelIndex leftIndex, rightIndex;
454
455   if(item == rootItem)
456     return;
457
458   if(column == -1) {
459     leftIndex = createIndex(item->row(), 0, item);
460     rightIndex = createIndex(item->row(), item->columnCount()-1, item);
461   } else {
462     leftIndex = createIndex(item->row(), column, item);
463     rightIndex = leftIndex;
464   }
465
466   emit dataChanged(leftIndex, rightIndex);
467 }
468
469 void TreeModel::appendChild(AbstractTreeItem *parent, AbstractTreeItem *child) {
470   if(parent == 0 || child == 0) {
471     qWarning() << "TreeModel::appendChild(parent, child) parent and child have to be valid pointers!" << parent << child;
472     return;
473   }
474
475   int nextRow = parent->childCount();
476   beginInsertRows(indexByItem(parent), nextRow, nextRow);
477   parent->appendChild(child);
478   endInsertRows();
479
480   connect(child, SIGNAL(dataChanged(int)),
481           this, SLOT(itemDataChanged(int)));
482   
483   connect(child, SIGNAL(newChild(AbstractTreeItem *)),
484           this, SLOT(newChild(AbstractTreeItem *)));
485
486   connect(child, SIGNAL(beginRemoveChilds(int, int)),
487           this, SLOT(beginRemoveChilds(int, int)));
488   
489   connect(child, SIGNAL(endRemoveChilds()),
490           this, SLOT(endRemoveChilds()));
491 }
492
493 void TreeModel::newChild(AbstractTreeItem *child) {
494   appendChild(static_cast<AbstractTreeItem *>(sender()), child);
495 }
496
497 void TreeModel::beginRemoveChilds(int firstRow, int lastRow) {
498   QModelIndex parent = indexByItem(static_cast<AbstractTreeItem *>(sender()));
499   beginRemoveRows(parent, firstRow, lastRow);
500 }
501
502 void TreeModel::endRemoveChilds() {
503   endRemoveRows();
504 }
505
506 bool TreeModel::removeRow(int row, const QModelIndex &parent) {
507   if(row > rowCount(parent))
508     return false;
509   
510   AbstractTreeItem *item;
511   if(!parent.isValid())
512     item = rootItem;
513   else
514     item = static_cast<AbstractTreeItem*>(parent.internalPointer());
515   
516   beginRemoveRows(parent, row, row);
517   item->removeChild(parent.column(), row);
518   endRemoveRows();
519   return true;
520 }
521
522 bool TreeModel::removeRows(int row, int count, const QModelIndex &parent) {
523   // check if there is work to be done
524   if(count == 0)
525     return true;
526
527   // out of range check
528   if(row + count - 1 > rowCount(parent) || row < 0 || count < 0) 
529     return false;
530   
531   AbstractTreeItem *item;
532   if(!parent.isValid())
533     item = rootItem;
534   else
535     item = static_cast<AbstractTreeItem *>(parent.internalPointer());
536   
537   
538   beginRemoveRows(parent, row, row + count - 1);
539
540   for(int i = row + count - 1; i >= 0; i--) {
541     item->removeChild(parent.column(), i);
542   }
543   endRemoveRows();
544   return true;
545 }
546
547 void TreeModel::clear() {
548   rootItem->removeAllChilds();
549 }