KWidgetsAddons

kpageview.cpp
1 /*
2  This file is part of the KDE Libraries
3  SPDX-FileCopyrightText: 2006 Tobias Koenig <[email protected]>
4  SPDX-FileCopyrightText: 2007 Rafael Fernández López <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "kpageview.h"
10 #include "kpageview_p.h"
11 
12 #include "kpagemodel.h"
13 #include "loggingcategory.h"
14 
15 #include <ktitlewidget.h>
16 
17 #include <QAbstractItemView>
18 #include <QGridLayout>
19 #include <QSize>
20 #include <QTimer>
21 
22 void KPageViewPrivate::_k_rebuildGui()
23 {
24  // clean up old view
25  Q_Q(KPageView);
26 
27  QModelIndex currentLastIndex;
28  if (view && view->selectionModel()) {
29  QObject::disconnect(view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
30  q, SLOT(_k_pageSelected(QItemSelection,QItemSelection)));
31  currentLastIndex = view->selectionModel()->currentIndex();
32  }
33 
34  delete view;
35  view = q->createView();
36 
37  Q_ASSERT(view);
38 
39  view->setSelectionBehavior(QAbstractItemView::SelectItems);
40  view->setSelectionMode(QAbstractItemView::SingleSelection);
41 
42  if (model) {
43  view->setModel(model);
44  }
45 
46  // setup new view
47  if (view->selectionModel()) {
48  QObject::connect(view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(_k_pageSelected(QItemSelection,QItemSelection)));
49 
50  if (currentLastIndex.isValid()) {
51  view->selectionModel()->setCurrentIndex(currentLastIndex, QItemSelectionModel::Select);
52  } else if (model) {
53  view->selectionModel()->setCurrentIndex(model->index(0, 0), QItemSelectionModel::Select);
54  }
55  }
56 
57  if (faceType == KPageView::Tabbed) {
58  stack->setVisible(false);
59  layout->removeWidget(stack);
60  } else {
61  layout->addWidget(stack, 2, 1);
62  stack->setVisible(true);
63  }
64 
65  layout->removeWidget(titleWidget);
66 
67  if (pageHeader) {
68  layout->removeWidget(pageHeader);
69  pageHeader->setVisible(q->showPageHeader());
70  titleWidget->setVisible(false);
71 
72  if (faceType == KPageView::Tabbed) {
73  layout->addWidget(pageHeader, 1, 1);
74  } else {
75  layout->addWidget(pageHeader, 1, 1, 1, 2);
76  }
77  } else {
78  titleWidget->setVisible(q->showPageHeader());
79  if (faceType == KPageView::Tabbed) {
80  layout->addWidget(titleWidget, 1, 1);
81  } else {
82  layout->addWidget(titleWidget, 1, 1, 1, 2);
83  }
84  }
85 
86  Qt::Alignment alignment = q->viewPosition();
87  if (alignment & Qt::AlignTop) {
88  layout->addWidget(view, 2, 1);
89  } else if (alignment & Qt::AlignRight) {
90  layout->addWidget(view, 1, 2, 4, 1);
91  } else if (alignment & Qt::AlignBottom) {
92  layout->addWidget(view, 4, 1);
93  } else if (alignment & Qt::AlignLeft) {
94  layout->addWidget(view, 1, 0, 4, 1);
95  }
96 }
97 
98 void KPageViewPrivate::updateSelection()
99 {
104  if (!model) {
105  return;
106  }
107 
108  if (!view || !view->selectionModel()) {
109  return;
110  }
111 
112  const QModelIndex index = view->selectionModel()->currentIndex();
113  if (!index.isValid()) {
114  view->selectionModel()->setCurrentIndex(model->index(0, 0), QItemSelectionModel::Select);
115  }
116 }
117 
118 void KPageViewPrivate::cleanupPages()
119 {
124  const QList<QWidget *> widgets = collectPages();
125 
126  for (int i = 0; i < stack->count(); ++i) {
127  QWidget *page = stack->widget(i);
128 
129  bool found = false;
130  for (int j = 0; j < widgets.count(); ++j) {
131  if (widgets[ j ] == page) {
132  found = true;
133  }
134  }
135 
136  if (!found) {
137  stack->removeWidget(page);
138  }
139  }
140 }
141 
142 QList<QWidget *> KPageViewPrivate::collectPages(const QModelIndex &parentIndex)
143 {
148  QList<QWidget *> retval;
149 
150  int rows = model->rowCount(parentIndex);
151  for (int j = 0; j < rows; ++j) {
152  const QModelIndex index = model->index(j, 0, parentIndex);
153  retval.append(qvariant_cast<QWidget *>(model->data(index, KPageModel::WidgetRole)));
154 
155  if (model->rowCount(index) > 0) {
156  retval += collectPages(index);
157  }
158  }
159 
160  return retval;
161 }
162 
163 KPageView::FaceType KPageViewPrivate::effectiveFaceType() const
164 {
165  if (faceType == KPageView::Auto) {
166  return detectAutoFace();
167  }
168 
169  return faceType;
170 }
171 
172 KPageView::FaceType KPageViewPrivate::detectAutoFace() const
173 {
174  if (!model) {
175  return KPageView::Plain;
176  }
177 
181  bool hasSubPages = false;
182  const int count = model->rowCount();
183  for (int i = 0; i < count; ++i) {
184  if (model->rowCount(model->index(i, 0)) > 0) {
185  hasSubPages = true;
186  break;
187  }
188  }
189 
190  if (hasSubPages) {
191  return KPageView::Tree;
192  }
193 
194  if (model->rowCount() > 1) {
195  return KPageView::List;
196  }
197 
198  return KPageView::Plain;
199 }
200 
201 void KPageViewPrivate::_k_modelChanged()
202 {
203  if (!model) {
204  return;
205  }
206 
211  if (faceType == KPageView::Auto) {
212  _k_rebuildGui();
213  // If you discover some crashes use the line below instead...
214  //QTimer::singleShot(0, q, SLOT(_k_rebuildGui()));
215  }
216 
220  QSize size = stack->size();
221  const QList<QWidget *> widgets = collectPages();
222  for (int i = 0; i < widgets.count(); ++i) {
223  const QWidget *widget = widgets[ i ];
224  if (widget) {
225  size = size.expandedTo(widget->minimumSizeHint());
226  }
227  }
228  stack->setMinimumSize(size);
229 
230  updateSelection();
231 }
232 
233 void KPageViewPrivate::_k_pageSelected(const QItemSelection &index, const QItemSelection &previous)
234 {
235  if (!model) {
236  return;
237  }
238 
239  // Return if the current Index is not valid
240  if (index.indexes().size() != 1) {
241  return;
242  }
243  QModelIndex currentIndex = index.indexes().first();
244 
245  QModelIndex previousIndex;
246  // The previous index can be invalid
247  if (previous.indexes().size() == 1) {
248  previousIndex = previous.indexes().first();
249  }
250 
251  if (faceType != KPageView::Tabbed) {
252  QWidget *widget = qvariant_cast<QWidget *>(model->data(currentIndex, KPageModel::WidgetRole));
253 
254  if (widget) {
255  if (stack->indexOf(widget) == -1) { // not included yet
256  stack->addWidget(widget);
257  }
258 
259  stack->setCurrentWidget(widget);
260  } else {
261  stack->setCurrentWidget(defaultWidget);
262  }
263 
264  updateTitleWidget(currentIndex);
265  }
266 
267  Q_Q(KPageView);
268  emit q->currentPageChanged(currentIndex, previousIndex);
269 }
270 
271 void KPageViewPrivate::updateTitleWidget(const QModelIndex &index)
272 {
273  Q_Q(KPageView);
274 
275  const bool headerVisible = model->data(index, KPageModel::HeaderVisibleRole).toBool();
276  if (!headerVisible) {
277  titleWidget->setVisible(false);
278  return;
279  }
280  QString header = model->data(index, KPageModel::HeaderRole).toString();
281  if (header.isNull()) { //TODO KDE5 remove that ugly logic, see also doxy-comments in KPageWidgetItem::setHeader()
282  header = model->data(index, Qt::DisplayRole).toString();
283  }
284 
285  titleWidget->setText(header);
286 
287  titleWidget->setVisible(q->showPageHeader());
288 }
289 
290 void KPageViewPrivate::_k_dataChanged(const QModelIndex &, const QModelIndex &)
291 {
296  if (!view) {
297  return;
298  }
299 
300  QModelIndex index = view->selectionModel()->currentIndex();
301  if (!index.isValid()) {
302  return;
303  }
304 
305  updateTitleWidget(index);
306 }
307 
308 KPageViewPrivate::KPageViewPrivate(KPageView *_parent)
309  : q_ptr(_parent), model(nullptr), faceType(KPageView::Auto),
310  layout(nullptr), stack(nullptr), titleWidget(nullptr), view(nullptr)
311 {
312 }
313 
314 void KPageViewPrivate::init()
315 {
316  Q_Q(KPageView);
317  layout = new QGridLayout(q);
318  stack = new KPageStackedWidget(q);
319  titleWidget = new KTitleWidget(q);
320  layout->addWidget(titleWidget, 1, 1, 1, 2);
321  layout->addWidget(stack, 2, 1);
322 
323  defaultWidget = new QWidget(q);
324  stack->addWidget(defaultWidget);
325 
326  // stack should use most space
327  layout->setColumnStretch(1, 1);
328  layout->setRowStretch(2, 1);
329 }
330 
335  : KPageView(*new KPageViewPrivate(this), parent)
336 {
337 }
338 
339 KPageView::KPageView(KPageViewPrivate &dd, QWidget *parent)
340  : QWidget(parent), d_ptr(&dd)
341 {
342  d_ptr->init();
343 }
344 
346 {
347  delete d_ptr;
348 }
349 
351 {
352  Q_D(KPageView);
353  // clean up old model
354  if (d->model) {
355  disconnect(d->model, SIGNAL(layoutChanged()), this, SLOT(_k_modelChanged()));
356  disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
357  this, SLOT(_k_dataChanged(QModelIndex,QModelIndex)));
358  }
359 
360  d->model = model;
361 
362  if (d->model) {
363  connect(d->model, SIGNAL(layoutChanged()), this, SLOT(_k_modelChanged()));
364  connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
365  this, SLOT(_k_dataChanged(QModelIndex,QModelIndex)));
366 
367  // set new model in navigation view
368  if (d->view) {
369  d->view->setModel(model);
370  }
371  }
372 
373  d->_k_rebuildGui();
374 }
375 
377 {
378  Q_D(const KPageView);
379  return d->model;
380 }
381 
383 {
384  Q_D(KPageView);
385  d->faceType = faceType;
386 
387  d->_k_rebuildGui();
388 }
389 
391 {
392  Q_D(const KPageView);
393  return d->faceType;
394 }
395 
397 {
398  Q_D(KPageView);
399  if (!d->view || !d->view->selectionModel()) {
400  return;
401  }
402 
403  d->view->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent);
404 }
405 
407 {
408  Q_D(const KPageView);
409  if (!d->view || !d->view->selectionModel()) {
410  return QModelIndex();
411  }
412 
413  return d->view->selectionModel()->currentIndex();
414 }
415 
417 {
418  Q_D(KPageView);
419  if (d->view) {
420  d->view->setItemDelegate(delegate);
421  }
422 }
423 
425 {
426  Q_D(const KPageView);
427  if (d->view) {
428  return d->view->itemDelegate();
429  } else {
430  return nullptr;
431  }
432 }
433 
435 {
436  Q_D(KPageView);
437 
438  Q_ASSERT(widget);
439 
440  bool isCurrent = (d->stack->currentIndex() == d->stack->indexOf(d->defaultWidget));
441 
442  // remove old default widget
443  d->stack->removeWidget(d->defaultWidget);
444  delete d->defaultWidget;
445 
446  // add new default widget
447  d->defaultWidget = widget;
448  d->stack->addWidget(d->defaultWidget);
449 
450  if (isCurrent) {
451  d->stack->setCurrentWidget(d->defaultWidget);
452  }
453 }
454 
456 {
457  Q_D(KPageView);
458  if (d->pageHeader == header) {
459  return;
460  }
461 
462  if (d->pageHeader) {
463  d->layout->removeWidget(d->pageHeader);
464  }
465  d->layout->removeWidget(d->titleWidget);
466 
467  d->pageHeader = header;
468 
469  // Give it a colSpan of 2 to add a margin to the right
470  if (d->pageHeader) {
471  d->layout->addWidget(d->pageHeader, 1, 1, 1, 2);
472  d->pageHeader->setVisible(showPageHeader());
473  } else {
474  d->layout->addWidget(d->titleWidget, 1, 1, 1, 2);
475  d->titleWidget->setVisible(showPageHeader());
476  }
477 }
478 
480 {
481  Q_D(const KPageView);
482  return d->pageHeader;
483 }
484 
486 {
487  Q_D(KPageView);
488  if (d->pageFooter == footer) {
489  return;
490  }
491 
492  if (d->pageFooter) {
493  d->layout->removeWidget(d->pageFooter);
494  }
495 
496  d->pageFooter = footer;
497 
498  if (footer) {
499  d->layout->addWidget(d->pageFooter, 3, 1);
500  }
501 }
502 
504 {
505  Q_D(const KPageView);
506  return d->pageFooter;
507 }
508 
510 {
511  Q_D(KPageView);
512  const FaceType faceType = d->effectiveFaceType();
513 
514  if (faceType == Plain) {
515  return new KDEPrivate::KPagePlainView(this);
516  }
517  if (faceType == List) {
518  return new KDEPrivate::KPageListView(this);
519  }
520  if (faceType == Tree) {
521  return new KDEPrivate::KPageTreeView(this);
522  }
523  if (faceType == Tabbed) {
524  return new KDEPrivate::KPageTabbedView(this);
525  }
526 
527  return nullptr;
528 }
529 
531 {
532  Q_D(const KPageView);
533  const FaceType faceType = d->effectiveFaceType();
534 
535  if (faceType == Tabbed) {
536  return false;
537  } else {
538  return d->pageHeader || !d->titleWidget->text().isEmpty();
539  }
540 }
541 
543 {
544  Q_D(const KPageView);
545  const FaceType faceType = d->effectiveFaceType();
546 
547  if (faceType == Plain || faceType == Tabbed) {
548  return Qt::AlignTop;
549  } else {
550  return Qt::AlignLeft;
551  }
552 }
553 
554 #include "moc_kpageview.cpp"
QLayout * layout() const const
QModelIndexList indexes() const const
A tree list is used as navigation view.
Definition: kpageview.h:79
QWidget * pageHeader() const
Widget of the header for this page view.
Definition: kpageview.cpp:479
virtual ~KPageView()
Destroys the page view.
Definition: kpageview.cpp:345
FaceType
This enum is used to decide which type of navigation view shall be used in the page view...
Definition: kpageview.h:59
void setPageHeader(QWidget *header)
Set a widget as the header for this Page view It will replace the standard page title.
Definition: kpageview.cpp:455
virtual QAbstractItemView * createView()
Returns the navigation view, depending on the current face type.
Definition: kpageview.cpp:509
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
void setFaceType(FaceType faceType)
Sets the face type of the page view.
Definition: kpageview.cpp:382
An icon list is used as navigation view.
Definition: kpageview.h:75
typedef Alignment
bool isNull() const const
bool isValid() const const
A base class which can handle multiple pages.
Definition: kpageview.h:48
int count(const T &value) const const
void append(const T &value)
void setModel(QAbstractItemModel *model)
Sets the model of the page view.
Definition: kpageview.cpp:350
when true, show the page header, if false don&#39;t
Definition: kpagemodel.h:74
DisplayRole
void setPageFooter(QWidget *footer)
Set a widget as the footer for this Page view.
Definition: kpageview.cpp:485
QModelIndex currentPage() const
Returns the index for the current page or an invalid index if no current page exists.
Definition: kpageview.cpp:406
A string to be rendered as page header.
Definition: kpagemodel.h:58
No navigation view will be visible and only the first page of the model will be shown.
Definition: kpageview.h:71
QAbstractItemModel * model() const
Returns the model of the page view.
Definition: kpageview.cpp:376
FaceType faceType() const
Returns the face type of the page view.
void addWidget(QWidget *w)
QWidget * pageFooter() const
Widget of the footer for this page view.
Definition: kpageview.cpp:503
A tab widget is used as navigation view.
Definition: kpageview.h:83
A pointer to the page widget.
Definition: kpagemodel.h:69
void setItemDelegate(QAbstractItemDelegate *delegate)
Sets the item.
Definition: kpageview.cpp:416
virtual bool showPageHeader() const
Returns whether the page header should be visible.
Definition: kpageview.cpp:530
QSize expandedTo(const QSize &otherSize) const const
KPageView(QWidget *parent=nullptr)
Creates a page view with given parent.
Definition: kpageview.cpp:334
QAbstractItemDelegate * itemDelegate() const
Returns the item delegate of the page view.
Definition: kpageview.cpp:424
void setCurrentPage(const QModelIndex &index)
Sets the page with.
Definition: kpageview.cpp:396
QWidget(QWidget *parent, Qt::WindowFlags f)
Standard title widget.
Definition: ktitlewidget.h:55
virtual Qt::Alignment viewPosition() const
Returns the position where the navigation view should be located according to the page stack...
Definition: kpageview.cpp:542
Depending on the number of pages in the model, the Plain (one page), the List (several pages) or the ...
Definition: kpageview.h:66
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
QChar * data()
void setDefaultWidget(QWidget *widget)
Sets the widget which will be shown when a page is selected that has no own widget set...
Definition: kpageview.cpp:434
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sun Jul 5 2020 22:44:06 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.