modernize: Pass arguments by value and move in constructors
[quassel.git] / src / client / treemodel.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 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  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include "treemodel.h"
22
23 #include <QCoreApplication>
24 #include <QDebug>
25 #include <utility>
26
27 #include "quassel.h"
28
29 class RemoveChildLaterEvent : public QEvent
30 {
31 public:
32     RemoveChildLaterEvent(AbstractTreeItem *child) : QEvent(QEvent::User), _child(child) {};
33     inline AbstractTreeItem *child() { return _child; }
34 private:
35     AbstractTreeItem *_child;
36 };
37
38
39 /*****************************************
40  *  Abstract Items of a TreeModel
41  *****************************************/
42 AbstractTreeItem::AbstractTreeItem(AbstractTreeItem *parent)
43     : QObject(parent),
44     _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled),
45     _treeItemFlags(nullptr)
46 {
47 }
48
49
50 bool AbstractTreeItem::newChild(AbstractTreeItem *item)
51 {
52     int newRow = childCount();
53     emit beginAppendChilds(newRow, newRow);
54     _childItems.append(item);
55     emit endAppendChilds();
56     return true;
57 }
58
59
60 bool AbstractTreeItem::newChilds(const QList<AbstractTreeItem *> &items)
61 {
62     if (items.isEmpty())
63         return false;
64
65     int nextRow = childCount();
66     int lastRow = nextRow + items.count() - 1;
67
68     emit beginAppendChilds(nextRow, lastRow);
69     _childItems << items;
70     emit endAppendChilds();
71
72     return true;
73 }
74
75
76 bool AbstractTreeItem::removeChild(int row)
77 {
78     if (row < 0 || childCount() <= row)
79         return false;
80
81     child(row)->removeAllChilds();
82     emit beginRemoveChilds(row, row);
83     AbstractTreeItem *treeitem = _childItems.takeAt(row);
84     delete treeitem;
85     emit endRemoveChilds();
86
87     checkForDeletion();
88
89     return true;
90 }
91
92
93 void AbstractTreeItem::removeAllChilds()
94 {
95     const int numChilds = childCount();
96
97     if (numChilds == 0)
98         return;
99
100     AbstractTreeItem *child;
101
102     QList<AbstractTreeItem *>::iterator childIter;
103
104     childIter = _childItems.begin();
105     while (childIter != _childItems.end()) {
106         child = *childIter;
107         child->setTreeItemFlags(nullptr); // disable self deletion, as this would only fuck up consitency and the child gets deleted anyways
108         child->removeAllChilds();
109         ++childIter;
110     }
111
112     emit beginRemoveChilds(0, numChilds - 1);
113     childIter = _childItems.begin();
114     while (childIter != _childItems.end()) {
115         child = *childIter;
116         childIter = _childItems.erase(childIter);
117         delete child;
118     }
119     emit endRemoveChilds();
120
121     checkForDeletion();
122 }
123
124
125 void AbstractTreeItem::removeChildLater(AbstractTreeItem *child)
126 {
127     Q_ASSERT(child);
128     QCoreApplication::postEvent(this, new RemoveChildLaterEvent(child));
129 }
130
131
132 void AbstractTreeItem::customEvent(QEvent *event)
133 {
134     if (event->type() != QEvent::User)
135         return;
136
137     event->accept();
138
139     RemoveChildLaterEvent *removeEvent = static_cast<RemoveChildLaterEvent *>(event);
140     int childRow = _childItems.indexOf(removeEvent->child());
141     if (childRow == -1)
142         return;
143
144     // since we are called asynchronously we have to recheck if the item in question still has no childs
145     if (removeEvent->child()->childCount())
146         return;
147
148     removeChild(childRow);
149 }
150
151
152 bool AbstractTreeItem::reParent(AbstractTreeItem *newParent)
153 {
154     // currently we support only re parenting if the child that's about to be
155     // adopted does not have any children itself.
156     if (childCount() != 0) {
157         qDebug() << "AbstractTreeItem::reParent(): cannot reparent"  << this << "with children.";
158         return false;
159     }
160
161     int oldRow = row();
162     if (oldRow == -1)
163         return false;
164
165     emit parent()->beginRemoveChilds(oldRow, oldRow);
166     parent()->_childItems.removeAt(oldRow);
167     emit parent()->endRemoveChilds();
168
169     AbstractTreeItem *oldParent = parent();
170     setParent(newParent);
171
172     bool success = newParent->newChild(this);
173     if (!success)
174         qWarning() << "AbstractTreeItem::reParent(): failed to attach to new parent after removing from old parent! this:" << this << "new parent:" << newParent;
175
176     if (oldParent)
177         oldParent->checkForDeletion();
178
179     return success;
180 }
181
182
183 AbstractTreeItem *AbstractTreeItem::child(int row) const
184 {
185     if (childCount() <= row)
186         return nullptr;
187     else
188         return _childItems[row];
189 }
190
191
192 int AbstractTreeItem::childCount(int column) const
193 {
194     if (column > 0)
195         return 0;
196     else
197         return _childItems.count();
198 }
199
200
201 int AbstractTreeItem::row() const
202 {
203     if (!parent()) {
204         qWarning() << "AbstractTreeItem::row():" << this << "has no parent AbstractTreeItem as it's parent! parent is" << QObject::parent();
205         return -1;
206     }
207
208     int row_ = parent()->_childItems.indexOf(const_cast<AbstractTreeItem *>(this));
209     if (row_ == -1)
210         qWarning() << "AbstractTreeItem::row():" << this << "is not in the child list of" << QObject::parent();
211     return row_;
212 }
213
214
215 void AbstractTreeItem::dumpChildList()
216 {
217     qDebug() << "==== Childlist for Item:" << this << "====";
218     if (childCount() > 0) {
219         AbstractTreeItem *child;
220         QList<AbstractTreeItem *>::const_iterator childIter = _childItems.constBegin();
221         while (childIter != _childItems.constEnd()) {
222             child = *childIter;
223             qDebug() << "Row:" << child->row() << child << child->data(0, Qt::DisplayRole);
224             ++childIter;
225         }
226     }
227     qDebug() << "==== End Of Childlist ====";
228 }
229
230
231 /*****************************************
232  * SimpleTreeItem
233  *****************************************/
234 SimpleTreeItem::SimpleTreeItem(QList<QVariant> data, AbstractTreeItem *parent)
235     : AbstractTreeItem(parent),
236     _itemData(std::move(data))
237 {
238 }
239
240
241 SimpleTreeItem::~SimpleTreeItem()
242 {
243 }
244
245
246 QVariant SimpleTreeItem::data(int column, int role) const
247 {
248     if (column >= columnCount() || role != Qt::DisplayRole)
249         return QVariant();
250     else
251         return _itemData[column];
252 }
253
254
255 bool SimpleTreeItem::setData(int column, const QVariant &value, int role)
256 {
257     if (column > columnCount() || role != Qt::DisplayRole)
258         return false;
259
260     if (column == columnCount())
261         _itemData.append(value);
262     else
263         _itemData[column] = value;
264
265     emit dataChanged(column);
266     return true;
267 }
268
269
270 int SimpleTreeItem::columnCount() const
271 {
272     return _itemData.count();
273 }
274
275
276 /*****************************************
277  * PropertyMapItem
278  *****************************************/
279 PropertyMapItem::PropertyMapItem(AbstractTreeItem *parent)
280     : AbstractTreeItem(parent)
281 {
282 }
283
284
285 QVariant PropertyMapItem::data(int column, int role) const
286 {
287     if (column >= columnCount())
288         return QVariant();
289
290     switch (role) {
291     case Qt::ToolTipRole:
292         return toolTip(column);
293     case Qt::DisplayRole:
294     case TreeModel::SortRole: // fallthrough, since SortRole should default to DisplayRole
295         return property(propertyOrder()[column].toLatin1());
296     default:
297         return QVariant();
298     }
299 }
300
301
302 bool PropertyMapItem::setData(int column, const QVariant &value, int role)
303 {
304     if (column >= columnCount() || role != Qt::DisplayRole)
305         return false;
306
307     setProperty(propertyOrder()[column].toLatin1(), value);
308     emit dataChanged(column);
309     return true;
310 }
311
312
313 int PropertyMapItem::columnCount() const
314 {
315     return propertyOrder().count();
316 }
317
318
319 /*****************************************
320  * TreeModel
321  *****************************************/
322 TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
323     : QAbstractItemModel(parent),
324     _childStatus(QModelIndex(), 0, 0, 0),
325     _aboutToRemoveOrInsert(false)
326 {
327     rootItem = new SimpleTreeItem(data, nullptr);
328     connectItem(rootItem);
329
330     if (Quassel::isOptionSet("debugmodel")) {
331         connect(this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
332             this, SLOT(debug_rowsAboutToBeInserted(const QModelIndex &, int, int)));
333         connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
334             this, SLOT(debug_rowsAboutToBeRemoved(const QModelIndex &, int, int)));
335         connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
336             this, SLOT(debug_rowsInserted(const QModelIndex &, int, int)));
337         connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
338             this, SLOT(debug_rowsRemoved(const QModelIndex &, int, int)));
339         connect(this, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
340             this, SLOT(debug_dataChanged(const QModelIndex &, const QModelIndex &)));
341     }
342 }
343
344
345 TreeModel::~TreeModel()
346 {
347     delete rootItem;
348 }
349
350
351 AbstractTreeItem *TreeModel::root() const
352 {
353     return rootItem;
354 }
355
356
357 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
358 {
359     if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent))
360         return QModelIndex();
361
362     AbstractTreeItem *parentItem;
363
364     if (!parent.isValid())
365         parentItem = rootItem;
366     else
367         parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
368
369     AbstractTreeItem *childItem = parentItem->child(row);
370
371     if (childItem)
372         return createIndex(row, column, childItem);
373     else
374         return QModelIndex();
375 }
376
377
378 QModelIndex TreeModel::indexByItem(AbstractTreeItem *item) const
379 {
380     if (item == nullptr) {
381         qWarning() << "TreeModel::indexByItem(AbstractTreeItem *item) received NULL-Pointer";
382         return QModelIndex();
383     }
384
385     if (item == rootItem)
386         return QModelIndex();
387     else
388         return createIndex(item->row(), 0, item);
389 }
390
391
392 QModelIndex TreeModel::parent(const QModelIndex &index) const
393 {
394     if (!index.isValid()) {
395         // ModelTest does this
396         // qWarning() << "TreeModel::parent(): has been asked for the rootItems Parent!";
397         return QModelIndex();
398     }
399
400     AbstractTreeItem *childItem = static_cast<AbstractTreeItem *>(index.internalPointer());
401     AbstractTreeItem *parentItem = childItem->parent();
402
403     Q_ASSERT(parentItem);
404     if (parentItem == rootItem)
405         return QModelIndex();
406
407     return createIndex(parentItem->row(), 0, parentItem);
408 }
409
410
411 int TreeModel::rowCount(const QModelIndex &parent) const
412 {
413     AbstractTreeItem *parentItem;
414     if (!parent.isValid())
415         parentItem = rootItem;
416     else
417         parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
418
419     return parentItem->childCount(parent.column());
420 }
421
422
423 int TreeModel::columnCount(const QModelIndex &parent) const
424 {
425     Q_UNUSED(parent)
426     return rootItem->columnCount();
427     // since there the Qt Views don't draw more columns than the header has columns
428     // we can be lazy and simply return the count of header columns
429     // actually this gives us more freedom cause we don't have to ensure that a rows parent
430     // has equal or more columns than that row
431
432 //   AbstractTreeItem *parentItem;
433 //   if(!parent.isValid())
434 //     parentItem = rootItem;
435 //   else
436 //     parentItem = static_cast<AbstractTreeItem*>(parent.internalPointer());
437 //   return parentItem->columnCount();
438 }
439
440
441 QVariant TreeModel::data(const QModelIndex &index, int role) const
442 {
443     if (!index.isValid())
444         return QVariant();
445
446     AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
447     return item->data(index.column(), role);
448 }
449
450
451 bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
452 {
453     if (!index.isValid())
454         return false;
455
456     AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
457     return item->setData(index.column(), value, role);
458 }
459
460
461 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
462 {
463     if (!index.isValid()) {
464         return rootItem->flags() & Qt::ItemIsDropEnabled;
465     }
466     else {
467         AbstractTreeItem *item = static_cast<AbstractTreeItem *>(index.internalPointer());
468         return item->flags();
469     }
470 }
471
472
473 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
474 {
475     if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
476         return rootItem->data(section, role);
477     else
478         return QVariant();
479 }
480
481
482 void TreeModel::itemDataChanged(int column)
483 {
484     AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
485     QModelIndex leftIndex, rightIndex;
486
487     if (item == rootItem)
488         return;
489
490     if (column == -1) {
491         leftIndex = createIndex(item->row(), 0, item);
492         rightIndex = createIndex(item->row(), item->columnCount() - 1, item);
493     }
494     else {
495         leftIndex = createIndex(item->row(), column, item);
496         rightIndex = leftIndex;
497     }
498
499     emit dataChanged(leftIndex, rightIndex);
500 }
501
502
503 void TreeModel::connectItem(AbstractTreeItem *item)
504 {
505     connect(item, SIGNAL(dataChanged(int)),
506         this, SLOT(itemDataChanged(int)));
507
508     connect(item, SIGNAL(beginAppendChilds(int, int)),
509         this, SLOT(beginAppendChilds(int, int)));
510     connect(item, SIGNAL(endAppendChilds()),
511         this, SLOT(endAppendChilds()));
512
513     connect(item, SIGNAL(beginRemoveChilds(int, int)),
514         this, SLOT(beginRemoveChilds(int, int)));
515     connect(item, SIGNAL(endRemoveChilds()),
516         this, SLOT(endRemoveChilds()));
517 }
518
519
520 void TreeModel::beginAppendChilds(int firstRow, int lastRow)
521 {
522     AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
523     if (!parentItem) {
524         qWarning() << "TreeModel::beginAppendChilds(): cannot append Children to unknown parent";
525         return;
526     }
527
528     QModelIndex parent = indexByItem(parentItem);
529     Q_ASSERT(!_aboutToRemoveOrInsert);
530
531     _aboutToRemoveOrInsert = true;
532     _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
533     beginInsertRows(parent, firstRow, lastRow);
534 }
535
536
537 void TreeModel::endAppendChilds()
538 {
539     AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
540     if (!parentItem) {
541         qWarning() << "TreeModel::endAppendChilds(): cannot append Children to unknown parent";
542         return;
543     }
544     Q_ASSERT(_aboutToRemoveOrInsert);
545     ChildStatus cs = _childStatus;
546 #ifndef QT_NO_DEBUG
547     QModelIndex parent = indexByItem(parentItem);
548     Q_ASSERT(cs.parent == parent);
549     Q_ASSERT(rowCount(parent) == cs.childCount + cs.end - cs.start + 1);
550 #endif
551     _aboutToRemoveOrInsert = false;
552     for (int i = cs.start; i <= cs.end; i++) {
553         connectItem(parentItem->child(i));
554     }
555     endInsertRows();
556 }
557
558
559 void TreeModel::beginRemoveChilds(int firstRow, int lastRow)
560 {
561     AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
562     if (!parentItem) {
563         qWarning() << "TreeModel::beginRemoveChilds(): cannot append Children to unknown parent";
564         return;
565     }
566
567     for (int i = firstRow; i <= lastRow; i++) {
568         disconnect(parentItem->child(i), nullptr, this, nullptr);
569     }
570
571     // consitency checks
572     QModelIndex parent = indexByItem(parentItem);
573     Q_ASSERT(firstRow <= lastRow);
574     Q_ASSERT(parentItem->childCount() > lastRow);
575     Q_ASSERT(!_aboutToRemoveOrInsert);
576     _aboutToRemoveOrInsert = true;
577     _childStatus = ChildStatus(parent, rowCount(parent), firstRow, lastRow);
578
579     beginRemoveRows(parent, firstRow, lastRow);
580 }
581
582
583 void TreeModel::endRemoveChilds()
584 {
585     AbstractTreeItem *parentItem = qobject_cast<AbstractTreeItem *>(sender());
586     if (!parentItem) {
587         qWarning() << "TreeModel::endRemoveChilds(): cannot remove Children from unknown parent";
588         return;
589     }
590
591     // concistency checks
592     Q_ASSERT(_aboutToRemoveOrInsert);
593 #ifndef QT_NO_DEBUG
594     ChildStatus cs = _childStatus;
595     QModelIndex parent = indexByItem(parentItem);
596     Q_ASSERT(cs.parent == parent);
597     Q_ASSERT(rowCount(parent) == cs.childCount - cs.end + cs.start - 1);
598 #endif
599     _aboutToRemoveOrInsert = false;
600
601     endRemoveRows();
602 }
603
604
605 void TreeModel::clear()
606 {
607     rootItem->removeAllChilds();
608 }
609
610
611 void TreeModel::debug_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
612 {
613     qDebug() << "debug_rowsAboutToBeInserted" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
614 }
615
616
617 void TreeModel::debug_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
618 {
619     AbstractTreeItem *parentItem;
620     parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
621     if (!parentItem)
622         parentItem = rootItem;
623     qDebug() << "debug_rowsAboutToBeRemoved" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
624
625     QModelIndex child;
626     for (int i = end; i >= start; i--) {
627         child = parent.child(i, 0);
628         Q_ASSERT(parentItem->child(i));
629         qDebug() << ">>>" << i << child << child.data().toString();
630     }
631 }
632
633
634 void TreeModel::debug_rowsInserted(const QModelIndex &parent, int start, int end)
635 {
636     AbstractTreeItem *parentItem;
637     parentItem = static_cast<AbstractTreeItem *>(parent.internalPointer());
638     if (!parentItem)
639         parentItem = rootItem;
640     qDebug() << "debug_rowsInserted:" << parent << parentItem << parent.data().toString() << rowCount(parent) << start << end;
641
642     QModelIndex child;
643     for (int i = start; i <= end; i++) {
644         child = parent.child(i, 0);
645         Q_ASSERT(parentItem->child(i));
646         qDebug() << "<<<" << i << child << child.data().toString();
647     }
648 }
649
650
651 void TreeModel::debug_rowsRemoved(const QModelIndex &parent, int start, int end)
652 {
653     qDebug() << "debug_rowsRemoved" << parent << parent.internalPointer() << parent.data().toString() << rowCount(parent) << start << end;
654 }
655
656
657 void TreeModel::debug_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
658 {
659     qDebug() << "debug_dataChanged" << topLeft << bottomRight;
660     QStringList displayData;
661     for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
662         displayData = QStringList();
663         for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
664             displayData << data(topLeft.sibling(row, column), Qt::DisplayRole).toString();
665         }
666         qDebug() << "  row:" << row << displayData;
667     }
668 }