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