1 /*
2     This file is part of the KDE Libraries
3     SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org>
4 
5     SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "kpagewidgetmodel.h"
9 #include "kpagewidgetmodel_p.h"
10 
11 #include "loggingcategory.h"
12 
13 #include <QPointer>
14 #include <QWidget>
15 
16 #include <QIcon>
17 
18 class KPageWidgetItemPrivate
19 {
20 public:
KPageWidgetItemPrivate()21     KPageWidgetItemPrivate()
22         : checkable(false)
23         , checked(false)
24         , enabled(true)
25         , headerVisible(true)
26     {
27     }
28 
~KPageWidgetItemPrivate()29     ~KPageWidgetItemPrivate()
30     {
31         delete widget;
32         widget = nullptr;
33     }
34 
35     QString name;
36     QString header;
37     QIcon icon;
38     QPointer<QWidget> widget;
39     bool checkable : 1;
40     bool checked : 1;
41     bool enabled : 1;
42     bool headerVisible : 1;
43 };
44 
KPageWidgetItem(QWidget * widget)45 KPageWidgetItem::KPageWidgetItem(QWidget *widget)
46     : QObject(nullptr)
47     , d(new KPageWidgetItemPrivate)
48 {
49     d->widget = widget;
50 
51     // Hide the widget, otherwise when the widget has this KPageView as
52     // parent the widget is shown outside the QStackedWidget if the page
53     // was not selected ( and reparented ) yet.
54     if (d->widget) {
55         d->widget->hide();
56     }
57 }
58 
KPageWidgetItem(QWidget * widget,const QString & name)59 KPageWidgetItem::KPageWidgetItem(QWidget *widget, const QString &name)
60     : QObject(nullptr)
61     , d(new KPageWidgetItemPrivate)
62 {
63     d->widget = widget;
64     d->name = name;
65 
66     // Hide the widget, otherwise when the widget has this KPageView as
67     // parent the widget is shown outside the QStackedWidget if the page
68     // was not selected ( and reparented ) yet.
69     if (d->widget) {
70         d->widget->hide();
71     }
72 }
73 
74 KPageWidgetItem::~KPageWidgetItem() = default;
75 
setEnabled(bool enabled)76 void KPageWidgetItem::setEnabled(bool enabled)
77 {
78     d->enabled = enabled;
79     if (d->widget) {
80         d->widget->setEnabled(enabled);
81     }
82     Q_EMIT changed();
83 }
84 
isEnabled() const85 bool KPageWidgetItem::isEnabled() const
86 {
87     return d->enabled;
88 }
89 
isHeaderVisible() const90 bool KPageWidgetItem::isHeaderVisible() const
91 {
92     return d->headerVisible;
93 }
94 
setHeaderVisible(bool visible)95 void KPageWidgetItem::setHeaderVisible(bool visible)
96 {
97     d->headerVisible = visible;
98 
99     Q_EMIT changed();
100 }
101 
widget() const102 QWidget *KPageWidgetItem::widget() const
103 {
104     return d->widget;
105 }
106 
setName(const QString & name)107 void KPageWidgetItem::setName(const QString &name)
108 {
109     d->name = name;
110 
111     Q_EMIT changed();
112 }
113 
name() const114 QString KPageWidgetItem::name() const
115 {
116     return d->name;
117 }
118 
setHeader(const QString & header)119 void KPageWidgetItem::setHeader(const QString &header)
120 {
121     const bool autoHeaderInvisibilityTriggered = header.isEmpty() & !header.isNull();
122     if (autoHeaderInvisibilityTriggered) {
123         qCWarning(KWidgetsAddonsLog)
124             << "KPageWidgetItem::setHeader() called with empty non-null string, which is deprecated. Use KPageWidgetItem::setHeaderVisible(false) instead.";
125     }
126 
127     d->header = header;
128 
129     Q_EMIT changed();
130 }
131 
header() const132 QString KPageWidgetItem::header() const
133 {
134     return d->header;
135 }
136 
setIcon(const QIcon & icon)137 void KPageWidgetItem::setIcon(const QIcon &icon)
138 {
139     d->icon = icon;
140 
141     Q_EMIT changed();
142 }
143 
icon() const144 QIcon KPageWidgetItem::icon() const
145 {
146     return d->icon;
147 }
148 
setCheckable(bool checkable)149 void KPageWidgetItem::setCheckable(bool checkable)
150 {
151     d->checkable = checkable;
152 
153     Q_EMIT changed();
154 }
155 
isCheckable() const156 bool KPageWidgetItem::isCheckable() const
157 {
158     return d->checkable;
159 }
160 
setChecked(bool checked)161 void KPageWidgetItem::setChecked(bool checked)
162 {
163     d->checked = checked;
164 
165     Q_EMIT toggled(checked);
166     Q_EMIT changed();
167 }
168 
isChecked() const169 bool KPageWidgetItem::isChecked() const
170 {
171     return d->checked;
172 }
173 
PageItem(KPageWidgetItem * pageWidgetItem,PageItem * parent)174 PageItem::PageItem(KPageWidgetItem *pageWidgetItem, PageItem *parent)
175     : mPageWidgetItem(pageWidgetItem)
176     , mParentItem(parent)
177 {
178 }
179 
~PageItem()180 PageItem::~PageItem()
181 {
182     delete mPageWidgetItem;
183     mPageWidgetItem = nullptr;
184 
185     qDeleteAll(mChildItems);
186 }
187 
appendChild(PageItem * item)188 void PageItem::appendChild(PageItem *item)
189 {
190     mChildItems.append(item);
191 }
192 
insertChild(int row,PageItem * item)193 void PageItem::insertChild(int row, PageItem *item)
194 {
195     mChildItems.insert(row, item);
196 }
197 
removeChild(int row)198 void PageItem::removeChild(int row)
199 {
200     mChildItems.removeAt(row);
201 }
202 
child(int row)203 PageItem *PageItem::child(int row)
204 {
205     return mChildItems.value(row);
206 }
207 
childCount() const208 int PageItem::childCount() const
209 {
210     return mChildItems.count();
211 }
212 
columnCount() const213 int PageItem::columnCount() const
214 {
215     return 1;
216 }
217 
parent()218 PageItem *PageItem::parent()
219 {
220     return mParentItem;
221 }
222 
row() const223 int PageItem::row() const
224 {
225     if (mParentItem) {
226         return mParentItem->mChildItems.indexOf(const_cast<PageItem *>(this));
227     }
228 
229     return 0;
230 }
231 
pageWidgetItem() const232 KPageWidgetItem *PageItem::pageWidgetItem() const
233 {
234     return mPageWidgetItem;
235 }
236 
findChild(const KPageWidgetItem * item)237 PageItem *PageItem::findChild(const KPageWidgetItem *item)
238 {
239     if (mPageWidgetItem == item) {
240         return this;
241     }
242 
243     for (int i = 0; i < mChildItems.count(); ++i) {
244         PageItem *pageItem = mChildItems[i]->findChild(item);
245         if (pageItem) {
246             return pageItem;
247         }
248     }
249 
250     return nullptr;
251 }
252 
dump(int indent)253 void PageItem::dump(int indent)
254 {
255     const QString indentation(indent, QLatin1Char(' '));
256 
257     const QString name = (mPageWidgetItem ? mPageWidgetItem->name() : QStringLiteral("root"));
258     qCDebug(KWidgetsAddonsLog, "%s (%p)", qPrintable(QString(indentation + name)), (void *)this);
259     for (int i = 0; i < mChildItems.count(); ++i) {
260         mChildItems[i]->dump(indent + 2);
261     }
262 }
263 
KPageWidgetModel(QObject * parent)264 KPageWidgetModel::KPageWidgetModel(QObject *parent)
265     : KPageModel(*new KPageWidgetModelPrivate, parent)
266 {
267 }
268 
~KPageWidgetModel()269 KPageWidgetModel::~KPageWidgetModel()
270 {
271 }
272 
columnCount(const QModelIndex &) const273 int KPageWidgetModel::columnCount(const QModelIndex &) const
274 {
275     return 1;
276 }
277 
data(const QModelIndex & index,int role) const278 QVariant KPageWidgetModel::data(const QModelIndex &index, int role) const
279 {
280     if (!index.isValid()) {
281         return QVariant();
282     }
283 
284     PageItem *item = static_cast<PageItem *>(index.internalPointer());
285 
286     if (role == Qt::DisplayRole) {
287         return QVariant(item->pageWidgetItem()->name());
288     } else if (role == Qt::DecorationRole) {
289         return QVariant(item->pageWidgetItem()->icon());
290     } else if (role == HeaderRole) {
291         return QVariant(item->pageWidgetItem()->header());
292     } else if (role == HeaderVisibleRole) {
293         return item->pageWidgetItem()->isHeaderVisible();
294     } else if (role == WidgetRole) {
295         return QVariant::fromValue(item->pageWidgetItem()->widget());
296     } else if (role == Qt::CheckStateRole) {
297         if (item->pageWidgetItem()->isCheckable()) {
298             return (item->pageWidgetItem()->isChecked() ? Qt::Checked : Qt::Unchecked);
299         } else {
300             return QVariant();
301         }
302     } else {
303         return QVariant();
304     }
305 }
306 
setData(const QModelIndex & index,const QVariant & value,int role)307 bool KPageWidgetModel::setData(const QModelIndex &index, const QVariant &value, int role)
308 {
309     if (!index.isValid()) {
310         return false;
311     }
312 
313     if (role != Qt::CheckStateRole) {
314         return false;
315     }
316 
317     PageItem *item = static_cast<PageItem *>(index.internalPointer());
318     if (!item) {
319         return false;
320     }
321 
322     if (!item->pageWidgetItem()->isCheckable()) {
323         return false;
324     }
325 
326     if (value.toInt() == Qt::Checked) {
327         item->pageWidgetItem()->setChecked(true);
328     } else {
329         item->pageWidgetItem()->setChecked(false);
330     }
331 
332     return true;
333 }
334 
flags(const QModelIndex & index) const335 Qt::ItemFlags KPageWidgetModel::flags(const QModelIndex &index) const
336 {
337     if (!index.isValid()) {
338         return Qt::NoItemFlags;
339     }
340 
341     Qt::ItemFlags flags = Qt::ItemIsSelectable;
342 
343     PageItem *item = static_cast<PageItem *>(index.internalPointer());
344     if (item->pageWidgetItem()->isCheckable()) {
345         flags |= Qt::ItemIsUserCheckable;
346     }
347     if (item->pageWidgetItem()->isEnabled()) {
348         flags |= Qt::ItemIsEnabled;
349     }
350 
351     return flags;
352 }
353 
index(int row,int column,const QModelIndex & parent) const354 QModelIndex KPageWidgetModel::index(int row, int column, const QModelIndex &parent) const
355 {
356     Q_D(const KPageWidgetModel);
357 
358     PageItem *parentItem;
359 
360     if (parent.isValid()) {
361         parentItem = static_cast<PageItem *>(parent.internalPointer());
362     } else {
363         parentItem = d->rootItem;
364     }
365 
366     PageItem *childItem = parentItem->child(row);
367     if (childItem) {
368         return createIndex(row, column, childItem);
369     } else {
370         return QModelIndex();
371     }
372 }
373 
parent(const QModelIndex & index) const374 QModelIndex KPageWidgetModel::parent(const QModelIndex &index) const
375 {
376     Q_D(const KPageWidgetModel);
377 
378     if (!index.isValid()) {
379         return QModelIndex();
380     }
381 
382     PageItem *item = static_cast<PageItem *>(index.internalPointer());
383     PageItem *parentItem = item->parent();
384 
385     if (parentItem == d->rootItem) {
386         return QModelIndex();
387     } else {
388         return createIndex(parentItem->row(), 0, parentItem);
389     }
390 }
391 
rowCount(const QModelIndex & parent) const392 int KPageWidgetModel::rowCount(const QModelIndex &parent) const
393 {
394     Q_D(const KPageWidgetModel);
395 
396     PageItem *parentItem;
397 
398     if (!parent.isValid()) {
399         parentItem = d->rootItem;
400     } else {
401         parentItem = static_cast<PageItem *>(parent.internalPointer());
402     }
403 
404     return parentItem->childCount();
405 }
406 
addPage(QWidget * widget,const QString & name)407 KPageWidgetItem *KPageWidgetModel::addPage(QWidget *widget, const QString &name)
408 {
409     KPageWidgetItem *item = new KPageWidgetItem(widget, name);
410 
411     addPage(item);
412 
413     return item;
414 }
415 
addPage(KPageWidgetItem * item)416 void KPageWidgetModel::addPage(KPageWidgetItem *item)
417 {
418     Q_EMIT layoutAboutToBeChanged();
419 
420     Q_D(KPageWidgetModel);
421     connect(item, SIGNAL(changed()), this, SLOT(_k_itemChanged()));
422     connect(item, SIGNAL(toggled(bool)), this, SLOT(_k_itemToggled(bool)));
423 
424     // The row to be inserted
425     int row = d->rootItem->childCount();
426 
427     beginInsertRows(QModelIndex(), row, row);
428 
429     PageItem *pageItem = new PageItem(item, d->rootItem);
430     d->rootItem->appendChild(pageItem);
431 
432     endInsertRows();
433 
434     Q_EMIT layoutChanged();
435 }
436 
insertPage(KPageWidgetItem * before,QWidget * widget,const QString & name)437 KPageWidgetItem *KPageWidgetModel::insertPage(KPageWidgetItem *before, QWidget *widget, const QString &name)
438 {
439     KPageWidgetItem *item = new KPageWidgetItem(widget, name);
440 
441     insertPage(before, item);
442 
443     return item;
444 }
445 
insertPage(KPageWidgetItem * before,KPageWidgetItem * item)446 void KPageWidgetModel::insertPage(KPageWidgetItem *before, KPageWidgetItem *item)
447 {
448     Q_D(KPageWidgetModel);
449 
450     PageItem *beforePageItem = d->rootItem->findChild(before);
451     if (!beforePageItem) {
452         qCDebug(KWidgetsAddonsLog, "Invalid KPageWidgetItem passed!");
453         return;
454     }
455 
456     Q_EMIT layoutAboutToBeChanged();
457 
458     connect(item, SIGNAL(changed()), this, SLOT(_k_itemChanged()));
459     connect(item, SIGNAL(toggled(bool)), this, SLOT(_k_itemToggled(bool)));
460 
461     PageItem *parent = beforePageItem->parent();
462     // The row to be inserted
463     int row = beforePageItem->row();
464 
465     QModelIndex index;
466     if (parent != d->rootItem) {
467         index = createIndex(parent->row(), 0, parent);
468     }
469 
470     beginInsertRows(index, row, row);
471 
472     PageItem *newPageItem = new PageItem(item, parent);
473     parent->insertChild(row, newPageItem);
474 
475     endInsertRows();
476 
477     Q_EMIT layoutChanged();
478 }
479 
addSubPage(KPageWidgetItem * parent,QWidget * widget,const QString & name)480 KPageWidgetItem *KPageWidgetModel::addSubPage(KPageWidgetItem *parent, QWidget *widget, const QString &name)
481 {
482     KPageWidgetItem *item = new KPageWidgetItem(widget, name);
483 
484     addSubPage(parent, item);
485 
486     return item;
487 }
488 
addSubPage(KPageWidgetItem * parent,KPageWidgetItem * item)489 void KPageWidgetModel::addSubPage(KPageWidgetItem *parent, KPageWidgetItem *item)
490 {
491     Q_D(KPageWidgetModel);
492 
493     PageItem *parentPageItem = d->rootItem->findChild(parent);
494     if (!parentPageItem) {
495         qCDebug(KWidgetsAddonsLog, "Invalid KPageWidgetItem passed!");
496         return;
497     }
498 
499     Q_EMIT layoutAboutToBeChanged();
500 
501     connect(item, SIGNAL(changed()), this, SLOT(_k_itemChanged()));
502     connect(item, SIGNAL(toggled(bool)), this, SLOT(_k_itemToggled(bool)));
503 
504     // The row to be inserted
505     int row = parentPageItem->childCount();
506 
507     QModelIndex index;
508     if (parentPageItem != d->rootItem) {
509         index = createIndex(parentPageItem->row(), 0, parentPageItem);
510     }
511 
512     beginInsertRows(index, row, row);
513 
514     PageItem *newPageItem = new PageItem(item, parentPageItem);
515     parentPageItem->appendChild(newPageItem);
516 
517     endInsertRows();
518 
519     Q_EMIT layoutChanged();
520 }
521 
removePage(KPageWidgetItem * item)522 void KPageWidgetModel::removePage(KPageWidgetItem *item)
523 {
524     if (!item) {
525         return;
526     }
527 
528     Q_D(KPageWidgetModel);
529 
530     PageItem *pageItem = d->rootItem->findChild(item);
531     if (!pageItem) {
532         qCDebug(KWidgetsAddonsLog, "Invalid KPageWidgetItem passed!");
533         return;
534     }
535 
536     Q_EMIT layoutAboutToBeChanged();
537 
538     disconnect(item, SIGNAL(changed()), this, SLOT(_k_itemChanged()));
539     disconnect(item, SIGNAL(toggled(bool)), this, SLOT(_k_itemToggled(bool)));
540 
541     PageItem *parentPageItem = pageItem->parent();
542     int row = parentPageItem->row();
543 
544     QModelIndex index;
545     if (parentPageItem != d->rootItem) {
546         index = createIndex(row, 0, parentPageItem);
547     }
548 
549     beginRemoveRows(index, pageItem->row(), pageItem->row());
550 
551     parentPageItem->removeChild(pageItem->row());
552     delete pageItem;
553 
554     endRemoveRows();
555 
556     Q_EMIT layoutChanged();
557 }
558 
item(const QModelIndex & index) const559 KPageWidgetItem *KPageWidgetModel::item(const QModelIndex &index) const
560 {
561     if (!index.isValid()) {
562         return nullptr;
563     }
564 
565     PageItem *item = static_cast<PageItem *>(index.internalPointer());
566     if (!item) {
567         return nullptr;
568     }
569 
570     return item->pageWidgetItem();
571 }
572 
index(const KPageWidgetItem * item) const573 QModelIndex KPageWidgetModel::index(const KPageWidgetItem *item) const
574 {
575     Q_D(const KPageWidgetModel);
576 
577     if (!item) {
578         return QModelIndex();
579     }
580 
581     const PageItem *pageItem = d->rootItem->findChild(item);
582     if (!pageItem) {
583         return QModelIndex();
584     }
585 
586     return createIndex(pageItem->row(), 0, (void *)pageItem);
587 }
588 
589 #include "moc_kpagewidgetmodel.cpp"
590