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