File indexing completed on 2024-04-28 03:59:10

0001 /*
0002     This file is part of the KDE Libraries
0003     SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org>
0004     SPDX-FileCopyrightText: 2007 Rafael Fernández López <ereslibre@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "kpageview.h"
0010 #include "kpageview_p.h"
0011 
0012 #include "kpagemodel.h"
0013 #include "kpagewidgetmodel.h"
0014 #include "loggingcategory.h"
0015 
0016 #include <ktitlewidget.h>
0017 
0018 #include <QAbstractButton>
0019 #include <QAbstractItemView>
0020 #include <QApplication>
0021 #include <QCheckBox>
0022 #include <QComboBox>
0023 #include <QEvent>
0024 #include <QGridLayout>
0025 #include <QLabel>
0026 #include <QPaintEvent>
0027 #include <QPainter>
0028 #include <QSize>
0029 #include <QTimer>
0030 
0031 // Helper class that draws a rect over a matched widget
0032 class SearchMatchOverlay : public QWidget
0033 {
0034 public:
0035     SearchMatchOverlay(QWidget *parent, int tabIdx = -1)
0036         : QWidget(parent)
0037         , m_tabIdx(tabIdx)
0038     {
0039         setAttribute(Qt::WA_TransparentForMouseEvents, true);
0040         resize_impl();
0041         parent->installEventFilter(this);
0042 
0043         show();
0044         raise();
0045     }
0046 
0047 private:
0048     void doResize()
0049     {
0050         QMetaObject::invokeMethod(this, &SearchMatchOverlay::resize_impl, Qt::QueuedConnection);
0051     }
0052 
0053     void resize_impl()
0054     {
0055         if (m_tabIdx >= 0) {
0056             auto tabBar = qobject_cast<QTabBar *>(parentWidget());
0057             if (!tabBar) {
0058                 setVisible(false);
0059                 return;
0060             }
0061             const QRect r = tabBar->tabRect(m_tabIdx);
0062             if (geometry() != r) {
0063                 setGeometry(r);
0064             }
0065             return;
0066         }
0067 
0068         if (parentWidget() && size() != parentWidget()->size()) {
0069             resize(parentWidget()->size());
0070         }
0071     }
0072 
0073     bool eventFilter(QObject *o, QEvent *e) override
0074     {
0075         if (parentWidget() && o == parentWidget() && (e->type() == QEvent::Resize || e->type() == QEvent::Show)) {
0076             doResize();
0077         }
0078         return QWidget::eventFilter(o, e);
0079     }
0080 
0081     void paintEvent(QPaintEvent *e) override
0082     {
0083         QPainter p(this);
0084         p.setClipRegion(e->region());
0085         QColor c = palette().brush(QPalette::Active, QPalette::Highlight).color();
0086         c.setAlpha(110);
0087         p.fillRect(rect(), c);
0088     }
0089 
0090     int m_tabIdx = -1;
0091 };
0092 
0093 void KPageViewPrivate::rebuildGui()
0094 {
0095     // clean up old view
0096     Q_Q(KPageView);
0097 
0098     QModelIndex currentLastIndex;
0099     if (view && view->selectionModel()) {
0100         QObject::disconnect(m_selectionChangedConnection);
0101         currentLastIndex = view->selectionModel()->currentIndex();
0102     }
0103 
0104     delete view;
0105     view = q->createView();
0106 
0107     Q_ASSERT(view);
0108 
0109     view->setSelectionBehavior(QAbstractItemView::SelectItems);
0110     view->setSelectionMode(QAbstractItemView::SingleSelection);
0111 
0112     if (model) {
0113         view->setModel(model);
0114     }
0115 
0116     // setup new view
0117     if (view->selectionModel()) {
0118         m_selectionChangedConnection = QObject::connect(view->selectionModel(),
0119                                                         &QItemSelectionModel::selectionChanged,
0120                                                         q,
0121                                                         [this](const QItemSelection &selected, const QItemSelection &deselected) {
0122                                                             pageSelected(selected, deselected);
0123                                                         });
0124 
0125         if (currentLastIndex.isValid()) {
0126             view->selectionModel()->setCurrentIndex(currentLastIndex, QItemSelectionModel::Select);
0127         } else if (model) {
0128             view->selectionModel()->setCurrentIndex(model->index(0, 0), QItemSelectionModel::Select);
0129         }
0130     }
0131 
0132     if (faceType == KPageView::Tabbed) {
0133         stack->setVisible(false);
0134         layout->removeWidget(stack);
0135     } else {
0136         layout->addWidget(stack, 3, 1);
0137         stack->setVisible(true);
0138     }
0139 
0140     titleWidget->setPalette(qApp->palette(titleWidget));
0141 
0142     if (!hasSearchableView()) {
0143         layout->removeWidget(searchLineEditContainer);
0144         searchLineEditContainer->setVisible(false);
0145         titleWidget->setAutoFillBackground(false);
0146         layout->setSpacing(0);
0147         separatorLine->setVisible(false);
0148         titleWidget->setObjectName("KPageView::TitleWidgetNonSearchable");
0149         titleWidget->setContentsMargins(q_ptr->style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
0150                                         q_ptr->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
0151                                         q_ptr->style()->pixelMetric(QStyle::PM_LayoutRightMargin),
0152                                         q_ptr->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
0153     } else {
0154         titleWidget->setObjectName("KPageView::TitleWidget");
0155         searchLineEditContainer->setVisible(true);
0156         searchLineEditContainer->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
0157         separatorLine->setVisible(true);
0158 
0159         // Adjust margins for a better alignment
0160         searchLineEditContainer->setContentsMargins(4, 3, 4, 3);
0161         titleWidget->setContentsMargins(5, 4, 4, 2);
0162 
0163         // Adjust the search + title background so that it merges into the titlebar
0164         layout->setSpacing(0);
0165         layout->setContentsMargins({});
0166     }
0167 
0168     layout->removeWidget(titleWidget);
0169 
0170     if (pageHeader) {
0171         layout->removeWidget(pageHeader);
0172         pageHeader->setVisible(q->showPageHeader());
0173         titleWidget->setVisible(false);
0174 
0175         if (faceType == KPageView::Tabbed) {
0176             layout->addWidget(pageHeader, 1, 1);
0177         } else {
0178             layout->addWidget(pageHeader, 1, 1, 1, 2);
0179         }
0180     } else {
0181         titleWidget->setVisible(q->showPageHeader());
0182         if (faceType == KPageView::Tabbed) {
0183             layout->addWidget(titleWidget, 1, 1);
0184         } else {
0185             layout->addWidget(titleWidget, 1, 1, 1, 2);
0186         }
0187     }
0188 
0189     Qt::Alignment alignment = q->viewPosition();
0190     if (alignment & Qt::AlignTop) {
0191         layout->addWidget(view, 2, 1);
0192     } else if (alignment & Qt::AlignRight) {
0193         // search line
0194         layout->addWidget(searchLineEditContainer, 1, 2, Qt::AlignVCenter);
0195         // item view below the search line
0196         layout->addWidget(view, 3, 2, 3, 1);
0197     } else if (alignment & Qt::AlignBottom) {
0198         layout->addWidget(view, 4, 1);
0199     } else if (alignment & Qt::AlignLeft) {
0200         // search line
0201         layout->addWidget(searchLineEditContainer, 1, 0, Qt::AlignVCenter);
0202         // item view below the search line
0203         layout->addWidget(view, 3, 0, 3, 1);
0204     }
0205 }
0206 
0207 void KPageViewPrivate::updateSelection()
0208 {
0209     // Select the first item in the view if not done yet.
0210 
0211     if (!model) {
0212         return;
0213     }
0214 
0215     if (!view || !view->selectionModel()) {
0216         return;
0217     }
0218 
0219     const QModelIndex index = view->selectionModel()->currentIndex();
0220     if (!index.isValid()) {
0221         view->selectionModel()->setCurrentIndex(model->index(0, 0), QItemSelectionModel::Select);
0222     }
0223 }
0224 
0225 void KPageViewPrivate::cleanupPages()
0226 {
0227     // Remove all orphan pages from the stacked widget.
0228 
0229     const QList<QWidget *> widgets = collectPages();
0230 
0231     for (int i = 0; i < stack->count(); ++i) {
0232         QWidget *page = stack->widget(i);
0233 
0234         bool found = false;
0235         for (int j = 0; j < widgets.count(); ++j) {
0236             if (widgets[j] == page) {
0237                 found = true;
0238             }
0239         }
0240 
0241         if (!found) {
0242             stack->removeWidget(page);
0243         }
0244     }
0245 }
0246 
0247 QList<QWidget *> KPageViewPrivate::collectPages(const QModelIndex &parentIndex)
0248 {
0249     // Traverse through the model recursive and collect all widgets in
0250     // a list.
0251     QList<QWidget *> retval;
0252 
0253     int rows = model->rowCount(parentIndex);
0254     for (int j = 0; j < rows; ++j) {
0255         const QModelIndex index = model->index(j, 0, parentIndex);
0256         retval.append(qvariant_cast<QWidget *>(model->data(index, KPageModel::WidgetRole)));
0257 
0258         if (model->rowCount(index) > 0) {
0259             retval += collectPages(index);
0260         }
0261     }
0262 
0263     return retval;
0264 }
0265 
0266 KPageView::FaceType KPageViewPrivate::effectiveFaceType() const
0267 {
0268     if (faceType == KPageView::Auto) {
0269         return detectAutoFace();
0270     }
0271 
0272     return faceType;
0273 }
0274 
0275 KPageView::FaceType KPageViewPrivate::detectAutoFace() const
0276 {
0277     if (!model) {
0278         return KPageView::Plain;
0279     }
0280 
0281     // Check whether the model has sub pages.
0282     bool hasSubPages = false;
0283     const int count = model->rowCount();
0284     for (int i = 0; i < count; ++i) {
0285         if (model->rowCount(model->index(i, 0)) > 0) {
0286             hasSubPages = true;
0287             break;
0288         }
0289     }
0290 
0291     if (hasSubPages) {
0292         return KPageView::Tree;
0293     }
0294 
0295     if (model->rowCount() > 1) {
0296         return KPageView::List;
0297     }
0298 
0299     return KPageView::Plain;
0300 }
0301 
0302 void KPageViewPrivate::modelChanged()
0303 {
0304     if (!model) {
0305         return;
0306     }
0307 
0308     // If the face type is Auto, we rebuild the GUI whenever the layout
0309     // of the model changes.
0310     if (faceType == KPageView::Auto) {
0311         rebuildGui();
0312         // If you discover some crashes use the line below instead...
0313         // QTimer::singleShot(0, q, SLOT(rebuildGui()));
0314     }
0315 
0316     // Set the stack to the minimum size of the largest widget.
0317     QSize size = stack->size();
0318     const QList<QWidget *> widgets = collectPages();
0319     for (int i = 0; i < widgets.count(); ++i) {
0320         const QWidget *widget = widgets[i];
0321         if (widget) {
0322             size = size.expandedTo(widget->minimumSizeHint());
0323         }
0324     }
0325     stack->setMinimumSize(size);
0326 
0327     updateSelection();
0328 }
0329 
0330 void KPageViewPrivate::pageSelected(const QItemSelection &index, const QItemSelection &previous)
0331 {
0332     if (!model) {
0333         return;
0334     }
0335 
0336     // Return if the current Index is not valid
0337     if (index.indexes().size() != 1) {
0338         return;
0339     }
0340     QModelIndex currentIndex = index.indexes().first();
0341 
0342     QModelIndex previousIndex;
0343     // The previous index can be invalid
0344     if (previous.indexes().size() == 1) {
0345         previousIndex = previous.indexes().first();
0346     }
0347 
0348     if (faceType != KPageView::Tabbed) {
0349         QWidget *widget = qvariant_cast<QWidget *>(model->data(currentIndex, KPageModel::WidgetRole));
0350 
0351         if (widget) {
0352             if (stack->indexOf(widget) == -1) { // not included yet
0353                 stack->addWidget(widget);
0354             }
0355 
0356             stack->setCurrentWidget(widget);
0357         } else {
0358             stack->setCurrentWidget(defaultWidget);
0359         }
0360 
0361         updateTitleWidget(currentIndex);
0362     }
0363 
0364     Q_Q(KPageView);
0365     Q_EMIT q->currentPageChanged(currentIndex, previousIndex);
0366 }
0367 
0368 void KPageViewPrivate::updateTitleWidget(const QModelIndex &index)
0369 {
0370     Q_Q(KPageView);
0371 
0372     const bool headerVisible = model->data(index, KPageModel::HeaderVisibleRole).toBool();
0373     if (!headerVisible) {
0374         titleWidget->setVisible(false);
0375         return;
0376     }
0377     QString header = model->data(index, KPageModel::HeaderRole).toString();
0378     if (header.isEmpty()) {
0379         header = model->data(index, Qt::DisplayRole).toString();
0380     }
0381 
0382     titleWidget->setText(header);
0383 
0384     titleWidget->setVisible(q->showPageHeader());
0385 }
0386 
0387 void KPageViewPrivate::dataChanged(const QModelIndex &, const QModelIndex &)
0388 {
0389     // When data has changed we update the header and icon for the currently selected
0390     // page.
0391     if (!view) {
0392         return;
0393     }
0394 
0395     QModelIndex index = view->selectionModel()->currentIndex();
0396     if (!index.isValid()) {
0397         return;
0398     }
0399 
0400     updateTitleWidget(index);
0401 }
0402 
0403 KPageViewPrivate::KPageViewPrivate(KPageView *_parent)
0404     : q_ptr(_parent)
0405     , model(nullptr)
0406     , faceType(KPageView::Auto)
0407     , layout(nullptr)
0408     , stack(nullptr)
0409     , titleWidget(nullptr)
0410     , searchLineEditContainer(new QWidget())
0411     , searchLineEdit(new QLineEdit())
0412     , view(nullptr)
0413 {
0414 }
0415 
0416 void KPageViewPrivate::init()
0417 {
0418     Q_Q(KPageView);
0419     layout = new QGridLayout(q);
0420     stack = new KPageStackedWidget(q);
0421     titleWidget = new KTitleWidget(q);
0422     titleWidget->setObjectName("KPageView::TitleWidget");
0423 
0424     separatorLine = new QFrame(q);
0425     separatorLine->setFrameShape(QFrame::HLine);
0426     separatorLine->setFixedHeight(1);
0427     separatorLine->setFrameShadow(QFrame::Sunken);
0428 
0429     // list view under it to the left
0430     layout->addWidget(titleWidget, 1, 1, 1, 2);
0431     // separator
0432     layout->addWidget(separatorLine, 2, 0, 1, 3);
0433     // and then the actual page on the right
0434     layout->addWidget(stack, 3, 1);
0435 
0436     defaultWidget = new QWidget(q);
0437     stack->addWidget(defaultWidget);
0438 
0439     // stack should use most space
0440     layout->setColumnStretch(1, 1);
0441     layout->setRowStretch(3, 1);
0442 
0443     searchTimer.setInterval(400);
0444     searchTimer.setSingleShot(true);
0445     searchTimer.callOnTimeout(q, [this] {
0446         onSearchTextChanged();
0447     });
0448     q->setFocusProxy(searchLineEdit);
0449     searchLineEdit->setPlaceholderText(KPageView::tr("Search..."));
0450     searchLineEdit->setClearButtonEnabled(true);
0451     searchLineEdit->setParent(defaultWidget);
0452     auto a = new QAction(q);
0453     a->setIcon(QIcon::fromTheme(QStringLiteral("search")));
0454     searchLineEdit->addAction(a, QLineEdit::LeadingPosition);
0455     q->connect(searchLineEdit, &QLineEdit::textChanged, &searchTimer, QOverload<>::of(&QTimer::start));
0456     auto containerLayout = new QVBoxLayout(searchLineEditContainer);
0457     containerLayout->setContentsMargins({});
0458     containerLayout->setSpacing(0);
0459     containerLayout->addWidget(searchLineEdit);
0460     searchLineEditContainer->setObjectName("KPageView::Search");
0461 }
0462 
0463 static QList<KPageWidgetItem *> getAllPages(KPageWidgetModel *model, const QModelIndex &parent)
0464 {
0465     const int rc = model->rowCount(parent);
0466     QList<KPageWidgetItem *> ret;
0467     for (int i = 0; i < rc; ++i) {
0468         auto child = model->index(i, 0, parent);
0469         auto item = model->item(child);
0470         if (child.isValid() && item) {
0471             ret << item;
0472             ret << getAllPages(model, child);
0473         }
0474     }
0475     return ret;
0476 }
0477 
0478 template<typename WidgetType>
0479 static QList<QWidget *> hasMatchingText(const QString &text, QWidget *page)
0480 {
0481     QList<QWidget *> ret;
0482     const auto widgets = page->findChildren<WidgetType *>();
0483     for (auto label : widgets) {
0484         if (label->text().contains(text, Qt::CaseInsensitive)) {
0485             ret << label;
0486         }
0487     }
0488     return ret;
0489 }
0490 
0491 template<>
0492 QList<QWidget *> hasMatchingText<QComboBox>(const QString &text, QWidget *page)
0493 {
0494     QList<QWidget *> ret;
0495     const auto comboxBoxes = page->findChildren<QComboBox *>();
0496     for (auto cb : comboxBoxes) {
0497         if (cb->findText(text, Qt::MatchFlag::MatchContains) != -1) {
0498             ret << cb;
0499         }
0500     }
0501     return ret;
0502 }
0503 
0504 template<typename...>
0505 struct FindChildrenHelper {
0506     static QList<QWidget *> hasMatchingTextForTypes(const QString &, QWidget *)
0507     {
0508         return {};
0509     }
0510 };
0511 
0512 template<typename First, typename... Rest>
0513 struct FindChildrenHelper<First, Rest...> {
0514     static QList<QWidget *> hasMatchingTextForTypes(const QString &text, QWidget *page)
0515     {
0516         return hasMatchingText<First>(text, page) << FindChildrenHelper<Rest...>::hasMatchingTextForTypes(text, page);
0517     }
0518 };
0519 
0520 static QModelIndex walkTreeAndHideItems(QTreeView *tree, const QString &searchText, const QSet<QString> &pagesToHide, const QModelIndex &parent)
0521 {
0522     QModelIndex current;
0523     auto model = tree->model();
0524     const int rows = model->rowCount(parent);
0525     for (int i = 0; i < rows; ++i) {
0526         const auto index = model->index(i, 0, parent);
0527         const auto itemName = index.data().toString();
0528         tree->setRowHidden(i, parent, pagesToHide.contains(itemName) && !itemName.contains(searchText, Qt::CaseInsensitive));
0529         if (!searchText.isEmpty() && !tree->isRowHidden(i, parent) && !current.isValid()) {
0530             current = model->index(i, 0, parent);
0531         }
0532         auto curr = walkTreeAndHideItems(tree, searchText, pagesToHide, index);
0533         if (!current.isValid()) {
0534             current = curr;
0535         }
0536     }
0537     return current;
0538 }
0539 
0540 bool KPageViewPrivate::hasSearchableView() const
0541 {
0542     // We support search for only these two types as they can hide rows
0543     return qobject_cast<KDEPrivate::KPageListView *>(view) || qobject_cast<KDEPrivate::KPageTreeView *>(view);
0544 }
0545 
0546 void KPageViewPrivate::onSearchTextChanged()
0547 {
0548     if (!hasSearchableView()) {
0549         return;
0550     }
0551 
0552     const QString text = searchLineEdit->text();
0553     QSet<QString> pagesToHide;
0554     std::vector<QWidget *> matchedWidgets;
0555     if (!text.isEmpty()) {
0556         const auto pages = getAllPages(static_cast<KPageWidgetModel *>(model), {});
0557         for (auto item : pages) {
0558             const auto matchingWidgets = FindChildrenHelper<QLabel, QAbstractButton, QComboBox>::hasMatchingTextForTypes(text, item->widget());
0559             if (matchingWidgets.isEmpty()) {
0560                 pagesToHide << item->name();
0561             }
0562             matchedWidgets.insert(matchedWidgets.end(), matchingWidgets.begin(), matchingWidgets.end());
0563         }
0564     }
0565 
0566     if (model) {
0567         QModelIndex current;
0568         if (auto list = qobject_cast<QListView *>(view)) {
0569             for (int i = 0; i < model->rowCount(); ++i) {
0570                 const auto itemName = model->index(i, 0).data().toString();
0571                 list->setRowHidden(i, pagesToHide.contains(itemName) && !itemName.contains(text, Qt::CaseInsensitive));
0572                 if (!text.isEmpty() && !list->isRowHidden(i) && !current.isValid()) {
0573                     current = model->index(i, 0);
0574                 }
0575             }
0576         } else if (auto tree = qobject_cast<QTreeView *>(view)) {
0577             current = walkTreeAndHideItems(tree, text, pagesToHide, {});
0578             auto parent = current.parent();
0579             while (parent.isValid()) {
0580                 tree->setRowHidden(parent.row(), parent.parent(), false);
0581                 parent = parent.parent();
0582             }
0583         } else {
0584             qWarning() << "Unreacheable, unknown view:" << view;
0585             Q_UNREACHABLE();
0586         }
0587 
0588         if (current.isValid()) {
0589             view->setCurrentIndex(current);
0590         }
0591     }
0592 
0593     qDeleteAll(m_searchMatchOverlays);
0594     m_searchMatchOverlays.clear();
0595 
0596     using TabWidgetAndPage = QPair<QTabWidget *, QWidget *>;
0597     auto tabWidgetParent = [](QWidget *w) {
0598         // Finds if @p w is in a QTabWidget and returns
0599         // The QTabWidget + the widget in the stack where
0600         // @p w lives
0601         auto parent = w->parentWidget();
0602         TabWidgetAndPage p = {nullptr, nullptr};
0603         if (auto tw = qobject_cast<QTabWidget *>(parent)) {
0604             p.first = tw;
0605         }
0606         QVarLengthArray<QWidget *, 8> parentChain;
0607         while (parent) {
0608             if (!p.first) {
0609                 if (auto tw = qobject_cast<QTabWidget *>(parent)) {
0610                     if (parentChain.size() >= 3) {
0611                         // last == QTabWidget
0612                         // second last == QStackedWidget of QTabWidget
0613                         // third last => the widget we want
0614                         p.second = parentChain.value((parentChain.size() - 1) - 2);
0615                     }
0616                     p.first = tw;
0617                     break;
0618                 }
0619             }
0620             parent = parent->parentWidget();
0621             parentChain << parent;
0622         }
0623         return p;
0624     };
0625 
0626     for (auto w : matchedWidgets) {
0627         if (w) {
0628             m_searchMatchOverlays << new SearchMatchOverlay(w);
0629             if (!w->isVisible()) {
0630                 const auto [tabWidget, page] = tabWidgetParent(w);
0631                 if (!tabWidget && !page) {
0632                     continue;
0633                 }
0634                 const int idx = tabWidget->indexOf(page);
0635                 if (idx < 0) {
0636                     // qDebug() << page << tabWidget << "not found" << w;
0637                     continue;
0638                 }
0639                 m_searchMatchOverlays << new SearchMatchOverlay(tabWidget->tabBar(), idx);
0640             }
0641         }
0642     }
0643 }
0644 
0645 // KPageView Implementation
0646 KPageView::KPageView(QWidget *parent)
0647     : KPageView(*new KPageViewPrivate(this), parent)
0648 {
0649 }
0650 
0651 KPageView::KPageView(KPageViewPrivate &dd, QWidget *parent)
0652     : QWidget(parent)
0653     , d_ptr(&dd)
0654 {
0655     d_ptr->init();
0656 }
0657 
0658 KPageView::~KPageView() = default;
0659 
0660 void KPageView::setModel(QAbstractItemModel *model)
0661 {
0662     Q_D(KPageView);
0663     // clean up old model
0664     if (d->model) {
0665         disconnect(d->m_layoutChangedConnection);
0666         disconnect(d->m_dataChangedConnection);
0667     }
0668 
0669     d->model = model;
0670 
0671     if (d->model) {
0672         d->m_layoutChangedConnection = connect(d->model, &QAbstractItemModel::layoutChanged, this, [d]() {
0673             d->modelChanged();
0674         });
0675         d->m_dataChangedConnection = connect(d->model, &QAbstractItemModel::dataChanged, this, [d](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
0676             d->dataChanged(topLeft, bottomRight);
0677         });
0678 
0679         // set new model in navigation view
0680         if (d->view) {
0681             d->view->setModel(model);
0682         }
0683     }
0684 
0685     d->rebuildGui();
0686 }
0687 
0688 QAbstractItemModel *KPageView::model() const
0689 {
0690     Q_D(const KPageView);
0691     return d->model;
0692 }
0693 
0694 void KPageView::setFaceType(FaceType faceType)
0695 {
0696     Q_D(KPageView);
0697     d->faceType = faceType;
0698 
0699     d->rebuildGui();
0700 }
0701 
0702 KPageView::FaceType KPageView::faceType() const
0703 {
0704     Q_D(const KPageView);
0705     return d->faceType;
0706 }
0707 
0708 void KPageView::setCurrentPage(const QModelIndex &index)
0709 {
0710     Q_D(KPageView);
0711     if (!d->view || !d->view->selectionModel()) {
0712         return;
0713     }
0714 
0715     d->view->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent);
0716 }
0717 
0718 QModelIndex KPageView::currentPage() const
0719 {
0720     Q_D(const KPageView);
0721     if (!d->view || !d->view->selectionModel()) {
0722         return QModelIndex();
0723     }
0724 
0725     return d->view->selectionModel()->currentIndex();
0726 }
0727 
0728 void KPageView::setItemDelegate(QAbstractItemDelegate *delegate)
0729 {
0730     Q_D(KPageView);
0731     if (d->view) {
0732         d->view->setItemDelegate(delegate);
0733     }
0734 }
0735 
0736 QAbstractItemDelegate *KPageView::itemDelegate() const
0737 {
0738     Q_D(const KPageView);
0739     if (d->view) {
0740         return d->view->itemDelegate();
0741     } else {
0742         return nullptr;
0743     }
0744 }
0745 
0746 void KPageView::setDefaultWidget(QWidget *widget)
0747 {
0748     Q_D(KPageView);
0749 
0750     Q_ASSERT(widget);
0751 
0752     bool isCurrent = (d->stack->currentIndex() == d->stack->indexOf(d->defaultWidget));
0753 
0754     // remove old default widget
0755     d->stack->removeWidget(d->defaultWidget);
0756     delete d->defaultWidget;
0757 
0758     // add new default widget
0759     d->defaultWidget = widget;
0760     d->stack->addWidget(d->defaultWidget);
0761 
0762     if (isCurrent) {
0763         d->stack->setCurrentWidget(d->defaultWidget);
0764     }
0765 }
0766 
0767 void KPageView::setPageHeader(QWidget *header)
0768 {
0769     Q_D(KPageView);
0770     if (d->pageHeader == header) {
0771         return;
0772     }
0773 
0774     if (d->pageHeader) {
0775         d->layout->removeWidget(d->pageHeader);
0776     }
0777     d->layout->removeWidget(d->titleWidget);
0778 
0779     d->pageHeader = header;
0780 
0781     // Give it a colSpan of 2 to add a margin to the right
0782     if (d->pageHeader) {
0783         d->layout->addWidget(d->pageHeader, 1, 1, 1, 2);
0784         d->pageHeader->setVisible(showPageHeader());
0785     } else {
0786         d->layout->addWidget(d->titleWidget, 1, 1, 1, 2);
0787         d->titleWidget->setVisible(showPageHeader());
0788     }
0789 }
0790 
0791 QWidget *KPageView::pageHeader() const
0792 {
0793     Q_D(const KPageView);
0794     if (!d->pageHeader) {
0795         return d->titleWidget;
0796     }
0797     return d->pageHeader;
0798 }
0799 
0800 void KPageView::setPageFooter(QWidget *footer)
0801 {
0802     Q_D(KPageView);
0803     if (d->pageFooter == footer) {
0804         return;
0805     }
0806 
0807     if (d->pageFooter) {
0808         d->layout->removeWidget(d->pageFooter);
0809     }
0810 
0811     d->pageFooter = footer;
0812 
0813     if (footer) {
0814         d->pageFooter->setContentsMargins(4, 4, 4, 4);
0815         d->layout->addWidget(d->pageFooter, 4, 1);
0816     }
0817 }
0818 
0819 QWidget *KPageView::pageFooter() const
0820 {
0821     Q_D(const KPageView);
0822     return d->pageFooter;
0823 }
0824 
0825 QAbstractItemView *KPageView::createView()
0826 {
0827     Q_D(KPageView);
0828     const FaceType faceType = d->effectiveFaceType();
0829 
0830     if (faceType == Plain) {
0831         return new KDEPrivate::KPagePlainView(this);
0832     }
0833     if (faceType == FlatList) {
0834         return new KDEPrivate::KPageListView(this);
0835     }
0836     if (faceType == List) {
0837         auto view = new KDEPrivate::KPageListView(this);
0838         view->setItemDelegate(new KDEPrivate::KPageListViewDelegate(this));
0839         view->setFlexibleWidth(true);
0840         return view;
0841     }
0842     if (faceType == Tree) {
0843         return new KDEPrivate::KPageTreeView(this);
0844     }
0845     if (faceType == Tabbed) {
0846         return new KDEPrivate::KPageTabbedView(this);
0847     }
0848 
0849     return nullptr;
0850 }
0851 
0852 bool KPageView::showPageHeader() const
0853 {
0854     Q_D(const KPageView);
0855     const FaceType faceType = d->effectiveFaceType();
0856 
0857     if (faceType == Tabbed) {
0858         return false;
0859     } else {
0860         return d->pageHeader || !d->titleWidget->text().isEmpty();
0861     }
0862 }
0863 
0864 Qt::Alignment KPageView::viewPosition() const
0865 {
0866     Q_D(const KPageView);
0867     const FaceType faceType = d->effectiveFaceType();
0868 
0869     if (faceType == Plain || faceType == Tabbed) {
0870         return Qt::AlignTop;
0871     } else {
0872         return Qt::AlignLeft;
0873     }
0874 }
0875 
0876 #include "moc_kpageview.cpp"