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