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"