Warning, file /frameworks/kwidgetsaddons/src/kpageview.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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 "loggingcategory.h" 0014 0015 #include <ktitlewidget.h> 0016 0017 #include <QAbstractItemView> 0018 #include <QGridLayout> 0019 #include <QSize> 0020 #include <QTimer> 0021 0022 void KPageViewPrivate::rebuildGui() 0023 { 0024 // clean up old view 0025 Q_Q(KPageView); 0026 0027 QModelIndex currentLastIndex; 0028 if (view && view->selectionModel()) { 0029 QObject::disconnect(m_selectionChangedConnection); 0030 currentLastIndex = view->selectionModel()->currentIndex(); 0031 } 0032 0033 delete view; 0034 view = q->createView(); 0035 0036 Q_ASSERT(view); 0037 0038 view->setSelectionBehavior(QAbstractItemView::SelectItems); 0039 view->setSelectionMode(QAbstractItemView::SingleSelection); 0040 0041 if (model) { 0042 view->setModel(model); 0043 } 0044 0045 // setup new view 0046 if (view->selectionModel()) { 0047 m_selectionChangedConnection = QObject::connect(view->selectionModel(), 0048 &QItemSelectionModel::selectionChanged, 0049 q, 0050 [this](const QItemSelection &selected, const QItemSelection &deselected) { 0051 pageSelected(selected, deselected); 0052 }); 0053 0054 if (currentLastIndex.isValid()) { 0055 view->selectionModel()->setCurrentIndex(currentLastIndex, QItemSelectionModel::Select); 0056 } else if (model) { 0057 view->selectionModel()->setCurrentIndex(model->index(0, 0), QItemSelectionModel::Select); 0058 } 0059 } 0060 0061 if (faceType == KPageView::Tabbed) { 0062 stack->setVisible(false); 0063 layout->removeWidget(stack); 0064 } else { 0065 layout->addWidget(stack, 2, 1); 0066 stack->setVisible(true); 0067 } 0068 0069 layout->removeWidget(titleWidget); 0070 0071 if (pageHeader) { 0072 layout->removeWidget(pageHeader); 0073 pageHeader->setVisible(q->showPageHeader()); 0074 titleWidget->setVisible(false); 0075 0076 if (faceType == KPageView::Tabbed) { 0077 layout->addWidget(pageHeader, 1, 1); 0078 } else { 0079 layout->addWidget(pageHeader, 1, 1, 1, 2); 0080 } 0081 } else { 0082 titleWidget->setVisible(q->showPageHeader()); 0083 if (faceType == KPageView::Tabbed) { 0084 layout->addWidget(titleWidget, 1, 1); 0085 } else { 0086 layout->addWidget(titleWidget, 1, 1, 1, 2); 0087 } 0088 } 0089 0090 Qt::Alignment alignment = q->viewPosition(); 0091 if (alignment & Qt::AlignTop) { 0092 layout->addWidget(view, 2, 1); 0093 } else if (alignment & Qt::AlignRight) { 0094 layout->addWidget(view, 1, 2, 4, 1); 0095 } else if (alignment & Qt::AlignBottom) { 0096 layout->addWidget(view, 4, 1); 0097 } else if (alignment & Qt::AlignLeft) { 0098 layout->addWidget(view, 1, 0, 4, 1); 0099 } 0100 } 0101 0102 void KPageViewPrivate::updateSelection() 0103 { 0104 // Select the first item in the view if not done yet. 0105 0106 if (!model) { 0107 return; 0108 } 0109 0110 if (!view || !view->selectionModel()) { 0111 return; 0112 } 0113 0114 const QModelIndex index = view->selectionModel()->currentIndex(); 0115 if (!index.isValid()) { 0116 view->selectionModel()->setCurrentIndex(model->index(0, 0), QItemSelectionModel::Select); 0117 } 0118 } 0119 0120 void KPageViewPrivate::cleanupPages() 0121 { 0122 // Remove all orphan pages from the stacked widget. 0123 0124 const QList<QWidget *> widgets = collectPages(); 0125 0126 for (int i = 0; i < stack->count(); ++i) { 0127 QWidget *page = stack->widget(i); 0128 0129 bool found = false; 0130 for (int j = 0; j < widgets.count(); ++j) { 0131 if (widgets[j] == page) { 0132 found = true; 0133 } 0134 } 0135 0136 if (!found) { 0137 stack->removeWidget(page); 0138 } 0139 } 0140 } 0141 0142 QList<QWidget *> KPageViewPrivate::collectPages(const QModelIndex &parentIndex) 0143 { 0144 // Traverse through the model recursive and collect all widgets in 0145 // a list. 0146 QList<QWidget *> retval; 0147 0148 int rows = model->rowCount(parentIndex); 0149 for (int j = 0; j < rows; ++j) { 0150 const QModelIndex index = model->index(j, 0, parentIndex); 0151 retval.append(qvariant_cast<QWidget *>(model->data(index, KPageModel::WidgetRole))); 0152 0153 if (model->rowCount(index) > 0) { 0154 retval += collectPages(index); 0155 } 0156 } 0157 0158 return retval; 0159 } 0160 0161 KPageView::FaceType KPageViewPrivate::effectiveFaceType() const 0162 { 0163 if (faceType == KPageView::Auto) { 0164 return detectAutoFace(); 0165 } 0166 0167 return faceType; 0168 } 0169 0170 KPageView::FaceType KPageViewPrivate::detectAutoFace() const 0171 { 0172 if (!model) { 0173 return KPageView::Plain; 0174 } 0175 0176 // Check whether the model has sub pages. 0177 bool hasSubPages = false; 0178 const int count = model->rowCount(); 0179 for (int i = 0; i < count; ++i) { 0180 if (model->rowCount(model->index(i, 0)) > 0) { 0181 hasSubPages = true; 0182 break; 0183 } 0184 } 0185 0186 if (hasSubPages) { 0187 return KPageView::Tree; 0188 } 0189 0190 if (model->rowCount() > 1) { 0191 return KPageView::List; 0192 } 0193 0194 return KPageView::Plain; 0195 } 0196 0197 void KPageViewPrivate::modelChanged() 0198 { 0199 if (!model) { 0200 return; 0201 } 0202 0203 // If the face type is Auto, we rebuild the GUI whenever the layout 0204 // of the model changes. 0205 if (faceType == KPageView::Auto) { 0206 rebuildGui(); 0207 // If you discover some crashes use the line below instead... 0208 // QTimer::singleShot(0, q, SLOT(rebuildGui())); 0209 } 0210 0211 // Set the stack to the minimum size of the largest widget. 0212 QSize size = stack->size(); 0213 const QList<QWidget *> widgets = collectPages(); 0214 for (int i = 0; i < widgets.count(); ++i) { 0215 const QWidget *widget = widgets[i]; 0216 if (widget) { 0217 size = size.expandedTo(widget->minimumSizeHint()); 0218 } 0219 } 0220 stack->setMinimumSize(size); 0221 0222 updateSelection(); 0223 } 0224 0225 void KPageViewPrivate::pageSelected(const QItemSelection &index, const QItemSelection &previous) 0226 { 0227 if (!model) { 0228 return; 0229 } 0230 0231 // Return if the current Index is not valid 0232 if (index.indexes().size() != 1) { 0233 return; 0234 } 0235 QModelIndex currentIndex = index.indexes().first(); 0236 0237 QModelIndex previousIndex; 0238 // The previous index can be invalid 0239 if (previous.indexes().size() == 1) { 0240 previousIndex = previous.indexes().first(); 0241 } 0242 0243 if (faceType != KPageView::Tabbed) { 0244 QWidget *widget = qvariant_cast<QWidget *>(model->data(currentIndex, KPageModel::WidgetRole)); 0245 0246 if (widget) { 0247 if (stack->indexOf(widget) == -1) { // not included yet 0248 stack->addWidget(widget); 0249 } 0250 0251 stack->setCurrentWidget(widget); 0252 } else { 0253 stack->setCurrentWidget(defaultWidget); 0254 } 0255 0256 updateTitleWidget(currentIndex); 0257 } 0258 0259 Q_Q(KPageView); 0260 Q_EMIT q->currentPageChanged(currentIndex, previousIndex); 0261 } 0262 0263 void KPageViewPrivate::updateTitleWidget(const QModelIndex &index) 0264 { 0265 Q_Q(KPageView); 0266 0267 const bool headerVisible = model->data(index, KPageModel::HeaderVisibleRole).toBool(); 0268 if (!headerVisible) { 0269 titleWidget->setVisible(false); 0270 return; 0271 } 0272 QString header = model->data(index, KPageModel::HeaderRole).toString(); 0273 if (header.isNull()) { // TODO KF6 remove that ugly logic, see also doxy-comments in KPageWidgetItem::setHeader() 0274 header = model->data(index, Qt::DisplayRole).toString(); 0275 } 0276 0277 titleWidget->setText(header); 0278 0279 titleWidget->setVisible(q->showPageHeader()); 0280 } 0281 0282 void KPageViewPrivate::dataChanged(const QModelIndex &, const QModelIndex &) 0283 { 0284 // When data has changed we update the header and icon for the currently selected 0285 // page. 0286 if (!view) { 0287 return; 0288 } 0289 0290 QModelIndex index = view->selectionModel()->currentIndex(); 0291 if (!index.isValid()) { 0292 return; 0293 } 0294 0295 updateTitleWidget(index); 0296 } 0297 0298 KPageViewPrivate::KPageViewPrivate(KPageView *_parent) 0299 : q_ptr(_parent) 0300 , model(nullptr) 0301 , faceType(KPageView::Auto) 0302 , layout(nullptr) 0303 , stack(nullptr) 0304 , titleWidget(nullptr) 0305 , view(nullptr) 0306 { 0307 } 0308 0309 void KPageViewPrivate::init() 0310 { 0311 Q_Q(KPageView); 0312 layout = new QGridLayout(q); 0313 stack = new KPageStackedWidget(q); 0314 titleWidget = new KTitleWidget(q); 0315 layout->addWidget(titleWidget, 1, 1, 1, 2); 0316 layout->addWidget(stack, 2, 1); 0317 0318 defaultWidget = new QWidget(q); 0319 stack->addWidget(defaultWidget); 0320 0321 // stack should use most space 0322 layout->setColumnStretch(1, 1); 0323 layout->setRowStretch(2, 1); 0324 } 0325 0326 // KPageView Implementation 0327 KPageView::KPageView(QWidget *parent) 0328 : KPageView(*new KPageViewPrivate(this), parent) 0329 { 0330 } 0331 0332 KPageView::KPageView(KPageViewPrivate &dd, QWidget *parent) 0333 : QWidget(parent) 0334 , d_ptr(&dd) 0335 { 0336 d_ptr->init(); 0337 } 0338 0339 KPageView::~KPageView() = default; 0340 0341 void KPageView::setModel(QAbstractItemModel *model) 0342 { 0343 Q_D(KPageView); 0344 // clean up old model 0345 if (d->model) { 0346 disconnect(d->m_layoutChangedConnection); 0347 disconnect(d->m_dataChangedConnection); 0348 } 0349 0350 d->model = model; 0351 0352 if (d->model) { 0353 d->m_layoutChangedConnection = connect(d->model, &QAbstractItemModel::layoutChanged, this, [d]() { 0354 d->modelChanged(); 0355 }); 0356 d->m_dataChangedConnection = connect(d->model, &QAbstractItemModel::dataChanged, this, [d](const QModelIndex &topLeft, const QModelIndex &bottomRight) { 0357 d->dataChanged(topLeft, bottomRight); 0358 }); 0359 0360 // set new model in navigation view 0361 if (d->view) { 0362 d->view->setModel(model); 0363 } 0364 } 0365 0366 d->rebuildGui(); 0367 } 0368 0369 QAbstractItemModel *KPageView::model() const 0370 { 0371 Q_D(const KPageView); 0372 return d->model; 0373 } 0374 0375 void KPageView::setFaceType(FaceType faceType) 0376 { 0377 Q_D(KPageView); 0378 d->faceType = faceType; 0379 0380 d->rebuildGui(); 0381 } 0382 0383 KPageView::FaceType KPageView::faceType() const 0384 { 0385 Q_D(const KPageView); 0386 return d->faceType; 0387 } 0388 0389 void KPageView::setCurrentPage(const QModelIndex &index) 0390 { 0391 Q_D(KPageView); 0392 if (!d->view || !d->view->selectionModel()) { 0393 return; 0394 } 0395 0396 d->view->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent); 0397 } 0398 0399 QModelIndex KPageView::currentPage() const 0400 { 0401 Q_D(const KPageView); 0402 if (!d->view || !d->view->selectionModel()) { 0403 return QModelIndex(); 0404 } 0405 0406 return d->view->selectionModel()->currentIndex(); 0407 } 0408 0409 void KPageView::setItemDelegate(QAbstractItemDelegate *delegate) 0410 { 0411 Q_D(KPageView); 0412 if (d->view) { 0413 d->view->setItemDelegate(delegate); 0414 } 0415 } 0416 0417 QAbstractItemDelegate *KPageView::itemDelegate() const 0418 { 0419 Q_D(const KPageView); 0420 if (d->view) { 0421 return d->view->itemDelegate(); 0422 } else { 0423 return nullptr; 0424 } 0425 } 0426 0427 void KPageView::setDefaultWidget(QWidget *widget) 0428 { 0429 Q_D(KPageView); 0430 0431 Q_ASSERT(widget); 0432 0433 bool isCurrent = (d->stack->currentIndex() == d->stack->indexOf(d->defaultWidget)); 0434 0435 // remove old default widget 0436 d->stack->removeWidget(d->defaultWidget); 0437 delete d->defaultWidget; 0438 0439 // add new default widget 0440 d->defaultWidget = widget; 0441 d->stack->addWidget(d->defaultWidget); 0442 0443 if (isCurrent) { 0444 d->stack->setCurrentWidget(d->defaultWidget); 0445 } 0446 } 0447 0448 void KPageView::setPageHeader(QWidget *header) 0449 { 0450 Q_D(KPageView); 0451 if (d->pageHeader == header) { 0452 return; 0453 } 0454 0455 if (d->pageHeader) { 0456 d->layout->removeWidget(d->pageHeader); 0457 } 0458 d->layout->removeWidget(d->titleWidget); 0459 0460 d->pageHeader = header; 0461 0462 // Give it a colSpan of 2 to add a margin to the right 0463 if (d->pageHeader) { 0464 d->layout->addWidget(d->pageHeader, 1, 1, 1, 2); 0465 d->pageHeader->setVisible(showPageHeader()); 0466 } else { 0467 d->layout->addWidget(d->titleWidget, 1, 1, 1, 2); 0468 d->titleWidget->setVisible(showPageHeader()); 0469 } 0470 } 0471 0472 QWidget *KPageView::pageHeader() const 0473 { 0474 Q_D(const KPageView); 0475 if (!d->pageHeader) { 0476 return d->titleWidget; 0477 } 0478 return d->pageHeader; 0479 } 0480 0481 void KPageView::setPageFooter(QWidget *footer) 0482 { 0483 Q_D(KPageView); 0484 if (d->pageFooter == footer) { 0485 return; 0486 } 0487 0488 if (d->pageFooter) { 0489 d->layout->removeWidget(d->pageFooter); 0490 } 0491 0492 d->pageFooter = footer; 0493 0494 if (footer) { 0495 d->layout->addWidget(d->pageFooter, 3, 1); 0496 } 0497 } 0498 0499 QWidget *KPageView::pageFooter() const 0500 { 0501 Q_D(const KPageView); 0502 return d->pageFooter; 0503 } 0504 0505 QAbstractItemView *KPageView::createView() 0506 { 0507 Q_D(KPageView); 0508 const FaceType faceType = d->effectiveFaceType(); 0509 0510 if (faceType == Plain) { 0511 return new KDEPrivate::KPagePlainView(this); 0512 } 0513 if (faceType == FlatList) { 0514 return new KDEPrivate::KPageListView(this); 0515 } 0516 if (faceType == List) { 0517 auto view = new KDEPrivate::KPageListView(this); 0518 view->setItemDelegate(new KDEPrivate::KPageListViewDelegate(this)); 0519 return view; 0520 } 0521 if (faceType == Tree) { 0522 return new KDEPrivate::KPageTreeView(this); 0523 } 0524 if (faceType == Tabbed) { 0525 return new KDEPrivate::KPageTabbedView(this); 0526 } 0527 0528 return nullptr; 0529 } 0530 0531 bool KPageView::showPageHeader() const 0532 { 0533 Q_D(const KPageView); 0534 const FaceType faceType = d->effectiveFaceType(); 0535 0536 if (faceType == Tabbed) { 0537 return false; 0538 } else { 0539 return d->pageHeader || !d->titleWidget->text().isEmpty(); 0540 } 0541 } 0542 0543 Qt::Alignment KPageView::viewPosition() const 0544 { 0545 Q_D(const KPageView); 0546 const FaceType faceType = d->effectiveFaceType(); 0547 0548 if (faceType == Plain || faceType == Tabbed) { 0549 return Qt::AlignTop; 0550 } else { 0551 return Qt::AlignLeft; 0552 } 0553 } 0554 0555 #include "moc_kpageview.cpp"