VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/settings/UISettingsSelector.cpp

Last change on this file was 104313, checked in by vboxsync, 5 weeks ago

FE/Qt. bugref:10622. Using new UITranslationEventListener in the settings related GUI classes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.3 KB
Line 
1/* $Id: UISettingsSelector.cpp 104313 2024-04-12 13:10:30Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UISettingsSelector class implementation.
4 */
5
6/*
7 * Copyright (C) 2008-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QAbstractItemModel>
30#include <QAccessibleWidget>
31#include <QAction>
32#include <QActionGroup>
33#include <QApplication>
34#include <QItemDelegate>
35#include <QGuiApplication>
36#include <QLayout>
37#include <QPainter>
38#include <QPainterPath>
39#include <QSortFilterProxyModel>
40#include <QTabWidget>
41#include <QToolButton>
42
43/* GUI includes: */
44#include "QITabWidget.h"
45#include "QITreeView.h"
46#include "UICommon.h"
47#include "UIDesktopWidgetWatchdog.h"
48#include "UIIconPool.h"
49#include "UIImageTools.h"
50#include "UISettingsPage.h"
51#include "UISettingsSelector.h"
52#include "QIToolBar.h"
53
54
55/** QAccessibleWidget extension used as an accessibility interface for UISettingsSelectorToolBar buttons. */
56class UIAccessibilityInterfaceForUISettingsSelectorToolBarButton : public QAccessibleWidget
57{
58public:
59
60 /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
61 static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
62 {
63 /* Creating toolbar button accessibility interface: */
64 if ( pObject
65 && strClassname == QLatin1String("QToolButton")
66 && pObject->property("Belongs to") == "UISettingsSelectorToolBar")
67 return new UIAccessibilityInterfaceForUISettingsSelectorToolBarButton(qobject_cast<QWidget*>(pObject));
68
69 /* Null by default: */
70 return 0;
71 }
72
73 /** Constructs an accessibility interface passing @a pWidget to the base-class. */
74 UIAccessibilityInterfaceForUISettingsSelectorToolBarButton(QWidget *pWidget)
75 : QAccessibleWidget(pWidget, QAccessible::Button)
76 {}
77
78 /** Returns the role. */
79 virtual QAccessible::Role role() const RT_OVERRIDE
80 {
81 /* Make sure button still alive: */
82 AssertPtrReturn(button(), QAccessible::NoRole);
83
84 /* Return role for checkable button: */
85 if (button()->isCheckable())
86 return QAccessible::RadioButton;
87
88 /* Return default role: */
89 return QAccessible::Button;
90 }
91
92 /** Returns the state. */
93 virtual QAccessible::State state() const RT_OVERRIDE
94 {
95 /* Prepare the button state: */
96 QAccessible::State state;
97
98 /* Make sure button still alive: */
99 AssertPtrReturn(button(), state);
100
101 /* Compose the button state: */
102 state.checkable = button()->isCheckable();
103 state.checked = button()->isChecked();
104
105 /* Return the button state: */
106 return state;
107 }
108
109private:
110
111 /** Returns corresponding toolbar button. */
112 QToolButton *button() const { return qobject_cast<QToolButton*>(widget()); }
113};
114
115
116/** QITreeViewItem subclass used as selector tree-view item. */
117class UISelectorTreeViewItem : public QITreeViewItem
118{
119 Q_OBJECT;
120
121public:
122
123 /** Constructs selector item passing @a pParent to the base-class.
124 * This is constructor for root-item only. */
125 UISelectorTreeViewItem(QITreeView *pParent);
126 /** Constructs selector item passing @a pParentItem to the base-class.
127 * This is constructor for rest of items we need.
128 * @param iID Brings the item ID.
129 * @param icon Brings the item icon.
130 * @param strLink Brings the item link. */
131 UISelectorTreeViewItem(UISelectorTreeViewItem *pParentItem,
132 int iID,
133 const QIcon &icon,
134 const QString &strLink);
135
136 /** Returns item ID. */
137 int id() const { return m_iID; }
138 /** Returns item icon. */
139 QIcon icon() const { return m_icon; }
140 /** Returns item link. */
141 QString link() const { return m_strLink; }
142
143 /** Returns item text. */
144 virtual QString text() const RT_OVERRIDE { return m_strText; }
145 /** Defines item @a strText. */
146 virtual void setText(const QString &strText) { m_strText = strText; }
147
148 /** Returns whether item is hidden. */
149 virtual bool isHidden() const { return m_fHidden; }
150 /** Defines item is @a fHidden. */
151 virtual void setHidden(bool fHidden) { m_fHidden = fHidden; }
152
153 /** Returns the number of children. */
154 virtual int childCount() const RT_OVERRIDE { return m_children.size(); }
155 /** Returns the child item with @a iIndex. */
156 virtual UISelectorTreeViewItem *childItem(int iIndex) const RT_OVERRIDE { return m_children.at(iIndex); }
157
158 /** Adds @a pChild to this item. */
159 void addChild(UISelectorTreeViewItem *pChild) { m_children << pChild; }
160 /** Assigns @a children for this item. */
161 void setChildren(const QList<UISelectorTreeViewItem*> &children) { m_children = children; }
162 /** Returns position for the passed @a pChild. */
163 int posOfChild(UISelectorTreeViewItem *pChild) const { return m_children.indexOf(pChild); }
164
165 /** Searches for the child with @a iID specified. */
166 UISelectorTreeViewItem *childItemById(int iID) const;
167 /** Searches for the child with @a strLink specified. */
168 UISelectorTreeViewItem *childItemByLink(const QString &strLink) const;
169
170private:
171
172 /** Holds the item ID. */
173 int m_iID;
174 /** Holds the item icon. */
175 QIcon m_icon;
176 /** Holds the item link. */
177 QString m_strLink;
178 /** Holds the item text. */
179 QString m_strText;
180 /** Holds whether item is hidden. */
181 bool m_fHidden;
182
183 /** Holds the list of children. */
184 QList<UISelectorTreeViewItem*> m_children;
185};
186
187
188/** QItemDelegate subclass used as selector tree-view item delegate. */
189class UISelectorDelegate : public QItemDelegate
190{
191 Q_OBJECT;
192
193public:
194
195 /** Constructs selector item delegate passing @a pParent to the base-class. */
196 UISelectorDelegate(QObject *pParent);
197
198private:
199
200 /** Paints @a index item with specified @a option using specified @a pPainter. */
201 void paint(QPainter *pPainter, const QStyleOptionViewItem &option, const QModelIndex &index) const RT_OVERRIDE RT_FINAL;
202};
203
204
205/** QAbstractItemModel subclass used as selector model. */
206class UISelectorModel : public QAbstractItemModel
207{
208 Q_OBJECT;
209
210public:
211
212 /** Data roles. */
213 enum DataRole
214 {
215 R_Margin = Qt::UserRole + 1,
216 R_Spacing,
217 R_IconSize,
218
219 R_ItemId,
220 R_ItemPixmap,
221 R_ItemPixmapRect,
222 R_ItemName,
223 R_ItemNamePoint,
224 R_ItemHidden,
225 };
226
227 /** Constructs selector model passing @a pParent to the base-class. */
228 UISelectorModel(QITreeView *pParent);
229 /** Destructs selector model. */
230 virtual ~UISelectorModel() RT_OVERRIDE;
231
232 /** Returns row count for the passed @a parentIndex. */
233 virtual int rowCount(const QModelIndex &parentIndex = QModelIndex()) const RT_OVERRIDE;
234 /** Returns column count for the passed @a parentIndex. */
235 virtual int columnCount(const QModelIndex &parentIndex = QModelIndex()) const RT_OVERRIDE;
236
237 /** Returns parent item of @a specifiedIndex item. */
238 virtual QModelIndex parent(const QModelIndex &specifiedIndex) const RT_OVERRIDE;
239 /** Returns item specified by @a iRow, @a iColum and @a parentIndex. */
240 virtual QModelIndex index(int iRow, int iColumn, const QModelIndex &parentIndex = QModelIndex()) const RT_OVERRIDE;
241 /** Returns root item. */
242 QModelIndex root() const;
243
244 /** Returns model data for @a specifiedIndex and @a iRole. */
245 virtual QVariant data(const QModelIndex &specifiedIndex, int iRole) const RT_OVERRIDE;
246 /** Defines model data for @a specifiedIndex and @a iRole as @a value. */
247 virtual bool setData(const QModelIndex &specifiedIndex, const QVariant &value, int iRole) RT_OVERRIDE;
248
249 /** Adds item with certain @a strId. */
250 QModelIndex addItem(int iID, const QIcon &icon, const QString &strLink);
251 /** Deletes item with certain @a iID. */
252 void delItem(int iID);
253
254 /** Returns item with certain @a iID. */
255 QModelIndex findItem(int iID);
256 /** Returns item with certain @a strLink. */
257 QModelIndex findItem(const QString &strLink);
258
259private:
260
261 /** Returns model flags for @a specifiedIndex. */
262 virtual Qt::ItemFlags flags(const QModelIndex &specifiedIndex) const RT_OVERRIDE;
263
264 /** Returns a pointer to item containing within @a specifiedIndex. */
265 static UISelectorTreeViewItem *indexToItem(const QModelIndex &specifiedIndex);
266
267 /** Holds the parent tree-view reference. */
268 QITreeView *m_pParentTree;
269
270 /** Holds the root item instance. */
271 UISelectorTreeViewItem *m_pRootItem;
272};
273
274
275/** QITreeView extension used as selector tree-view. */
276class UISelectorTreeView : public QITreeView
277{
278 Q_OBJECT;
279
280public:
281
282 /** Constructs selector tree-view passing @a pParent to the base-class. */
283 UISelectorTreeView(QWidget *pParent);
284
285 /** Calculates widget minimum size-hint. */
286 virtual QSize minimumSizeHint() const RT_OVERRIDE;
287 /** Calculates widget size-hint. */
288 virtual QSize sizeHint() const RT_OVERRIDE;
289
290private:
291
292 /** Prepares all. */
293 void prepare();
294};
295
296
297/** Tree-widget column sections. */
298enum TreeWidgetSection
299{
300 TreeWidgetSection_Category = 0,
301 TreeWidgetSection_Id,
302 TreeWidgetSection_Link,
303 TreeWidgetSection_Max
304};
305
306
307/** Simple container of all the selector item data. */
308class UISelectorItem
309{
310public:
311
312 /** Constructs selector item.
313 * @param icon Brings the item icon.
314 * @param iID Brings the item ID.
315 * @param strLink Brings the item link.
316 * @param pPage Brings the item page reference.
317 * @param iParentID Brings the item parent ID. */
318 UISelectorItem(const QIcon &icon, int iID, const QString &strLink, UISettingsPage *pPage, int iParentID)
319 : m_icon(icon)
320 , m_iID(iID)
321 , m_strLink(strLink)
322 , m_pPage(pPage)
323 , m_iParentID(iParentID)
324 {}
325
326 /** Destructs selector item. */
327 virtual ~UISelectorItem() {}
328
329 /** Returns the item icon. */
330 QIcon icon() const { return m_icon; }
331 /** Returns the item text. */
332 QString text() const { return m_strText; }
333 /** Defines the item @a strText. */
334 void setText(const QString &strText) { m_strText = strText; }
335 /** Returns the item ID. */
336 int id() const { return m_iID; }
337 /** Returns the item link. */
338 QString link() const { return m_strLink; }
339 /** Returns the item page reference. */
340 UISettingsPage *page() const { return m_pPage; }
341 /** Returns the item parent ID. */
342 int parentID() const { return m_iParentID; }
343
344protected:
345
346 /** Holds the item icon. */
347 QIcon m_icon;
348 /** Holds the item text. */
349 QString m_strText;
350 /** Holds the item ID. */
351 int m_iID;
352 /** Holds the item link. */
353 QString m_strLink;
354 /** Holds the item page reference. */
355 UISettingsPage *m_pPage;
356 /** Holds the item parent ID. */
357 int m_iParentID;
358};
359
360
361/** UISelectorItem subclass used as tab-widget selector item. */
362class UISelectorActionItem : public UISelectorItem
363{
364public:
365
366 /** Constructs selector item.
367 * @param icon Brings the item icon.
368 * @param iID Brings the item ID.
369 * @param strLink Brings the item link.
370 * @param pPage Brings the item page reference.
371 * @param iParentID Brings the item parent ID.
372 * @param pParent Brings the item parent. */
373 UISelectorActionItem(const QIcon &icon, int iID, const QString &strLink, UISettingsPage *pPage, int iParentID, QObject *pParent)
374 : UISelectorItem(icon, iID, strLink, pPage, iParentID)
375 , m_pAction(new QAction(icon, QString(), pParent))
376 , m_pTabWidget(0)
377 {
378 m_pAction->setCheckable(true);
379 }
380
381 /** Returns the action instance. */
382 QAction *action() const { return m_pAction; }
383
384 /** Defines the @a pTabWidget instance. */
385 void setTabWidget(QTabWidget *pTabWidget) { m_pTabWidget = pTabWidget; }
386 /** Returns the tab-widget instance. */
387 QTabWidget *tabWidget() const { return m_pTabWidget; }
388
389protected:
390
391 /** Holds the action instance. */
392 QAction *m_pAction;
393 /** Holds the tab-widget instance. */
394 QTabWidget *m_pTabWidget;
395};
396
397
398/*********************************************************************************************************************************
399* Class UISelectorTreeViewItem implementation. *
400*********************************************************************************************************************************/
401
402UISelectorTreeViewItem::UISelectorTreeViewItem(QITreeView *pParent)
403 : QITreeViewItem(pParent)
404 , m_fHidden(false)
405{
406}
407
408UISelectorTreeViewItem::UISelectorTreeViewItem(UISelectorTreeViewItem *pParentItem,
409 int iID,
410 const QIcon &icon,
411 const QString &strLink)
412 : QITreeViewItem(pParentItem)
413 , m_iID(iID)
414 , m_icon(icon)
415 , m_strLink(strLink)
416 , m_fHidden(false)
417{
418 if (pParentItem)
419 pParentItem->addChild(this);
420}
421
422UISelectorTreeViewItem *UISelectorTreeViewItem::childItemById(int iID) const
423{
424 for (int i = 0; i < childCount(); ++i)
425 if (UISelectorTreeViewItem *pItem = childItem(i))
426 if (pItem->id() == iID)
427 return pItem;
428 return 0;
429}
430
431UISelectorTreeViewItem *UISelectorTreeViewItem::childItemByLink(const QString &strLink) const
432{
433 for (int i = 0; i < childCount(); ++i)
434 if (UISelectorTreeViewItem *pItem = childItem(i))
435 if (pItem->link() == strLink)
436 return pItem;
437 return 0;
438}
439
440
441/*********************************************************************************************************************************
442* Class UISelectorDelegate implementation. *
443*********************************************************************************************************************************/
444
445UISelectorDelegate::UISelectorDelegate(QObject *pParent)
446 : QItemDelegate(pParent)
447{
448}
449
450void UISelectorDelegate::paint(QPainter *pPainter, const QStyleOptionViewItem &option, const QModelIndex &proxyIndex) const
451{
452 /* Sanity checks: */
453 AssertPtrReturnVoid(pPainter);
454 AssertReturnVoid(proxyIndex.isValid());
455
456 /* Acquire model: */
457 const QSortFilterProxyModel *pProxyModel = qobject_cast<const QSortFilterProxyModel*>(proxyIndex.model());
458 AssertPtrReturnVoid(pProxyModel);
459 const UISelectorModel *pModel = qobject_cast<const UISelectorModel*>(pProxyModel->sourceModel());
460 AssertPtrReturnVoid(pModel);
461
462 /* Acquire model index: */
463 const QModelIndex index = pProxyModel->mapToSource(proxyIndex);
464 AssertReturnVoid(index.isValid());
465
466 /* Acquire item properties: */
467 const QPalette palette = QGuiApplication::palette();
468 const QRect itemRectangle = option.rect;
469 const QStyle::State enmState = option.state;
470 const bool fChosen = enmState & QStyle::State_Selected;
471
472 /* Draw background: */
473 QColor backColor;
474 if (fChosen)
475 {
476 /* Prepare painter path: */
477 QPainterPath painterPath;
478 painterPath.lineTo(itemRectangle.width() - itemRectangle.height(), 0);
479 painterPath.lineTo(itemRectangle.width(), itemRectangle.height());
480 painterPath.lineTo(0, itemRectangle.height());
481 painterPath.closeSubpath();
482 painterPath.translate(itemRectangle.topLeft());
483
484 /* Prepare painting gradient: */
485 backColor = palette.color(QPalette::Active, QPalette::Highlight);
486 const QColor bcTone1 = backColor.lighter(100);
487 const QColor bcTone2 = backColor.lighter(120);
488 QLinearGradient grad(itemRectangle.topLeft(), itemRectangle.bottomRight());
489 grad.setColorAt(0, bcTone1);
490 grad.setColorAt(1, bcTone2);
491
492 /* Paint fancy shape: */
493 pPainter->save();
494 pPainter->setClipPath(painterPath);
495 pPainter->setRenderHint(QPainter::Antialiasing);
496 pPainter->fillPath(painterPath, grad);
497 pPainter->strokePath(painterPath, uiCommon().isInDarkMode() ? backColor.lighter(120) : backColor.darker(110));
498 pPainter->restore();
499 }
500 else
501 {
502 /* Just init painting color: */
503 backColor = palette.color(QPalette::Active, QPalette::Window);
504 }
505
506 /* Draw icon: */
507 const QRect itemPixmapRect = pModel->data(index, UISelectorModel::R_ItemPixmapRect).toRect();
508 const QPixmap itemPixmap = pModel->data(index, UISelectorModel::R_ItemPixmap).value<QPixmap>();
509 pPainter->save();
510 pPainter->translate(itemRectangle.topLeft());
511 pPainter->drawPixmap(itemPixmapRect, itemPixmap);
512 pPainter->restore();
513
514 /* Draw name: */
515 const QColor foreground = suitableForegroundColor(palette, backColor);
516 const QFont font = pModel->data(index, Qt::FontRole).value<QFont>();
517 const QPoint namePoint = pModel->data(index, UISelectorModel::R_ItemNamePoint).toPoint();
518 const QString strName = pModel->data(index, UISelectorModel::R_ItemName).toString();
519 pPainter->save();
520 pPainter->translate(itemRectangle.topLeft());
521 pPainter->setPen(foreground);
522 pPainter->setFont(font);
523 pPainter->drawText(namePoint, strName);
524 pPainter->restore();
525}
526
527
528/*********************************************************************************************************************************
529* Class UISelectorModel implementation. *
530*********************************************************************************************************************************/
531
532UISelectorModel::UISelectorModel(QITreeView *pParentTree)
533 : QAbstractItemModel(pParentTree)
534 , m_pParentTree(pParentTree)
535 , m_pRootItem(new UISelectorTreeViewItem(pParentTree))
536{
537 AssertPtr(m_pParentTree);
538}
539
540UISelectorModel::~UISelectorModel()
541{
542 delete m_pRootItem;
543}
544
545int UISelectorModel::rowCount(const QModelIndex &parentIndex) const
546{
547 UISelectorTreeViewItem *pParentItem = indexToItem(parentIndex);
548 return pParentItem ? pParentItem->childCount() : 1 /* root item */;
549}
550
551int UISelectorModel::columnCount(const QModelIndex & /* parentIndex */) const
552{
553 return 1;
554}
555
556QModelIndex UISelectorModel::parent(const QModelIndex &specifiedIndex) const
557{
558 if (!specifiedIndex.isValid())
559 return QModelIndex();
560
561 UISelectorTreeViewItem *pItem = indexToItem(specifiedIndex);
562 UISelectorTreeViewItem *pParentOfItem =
563 pItem ? qobject_cast<UISelectorTreeViewItem*>(pItem->parentItem()) : 0;
564 UISelectorTreeViewItem *pParentOfParent =
565 pParentOfItem ? qobject_cast<UISelectorTreeViewItem*>(pParentOfItem->parentItem()) : 0;
566 const int iParentPosition = pParentOfParent ? pParentOfParent->posOfChild(pParentOfItem) : 0;
567
568 return pParentOfItem ? createIndex(iParentPosition, 0, pParentOfItem) : QModelIndex();
569}
570
571QModelIndex UISelectorModel::index(int iRow, int iColumn, const QModelIndex &parentIndex) const
572{
573 if (!hasIndex(iRow, iColumn, parentIndex))
574 return QModelIndex();
575
576 UISelectorTreeViewItem *pItem = !parentIndex.isValid()
577 ? m_pRootItem
578 : indexToItem(parentIndex)->childItem(iRow);
579
580 return pItem ? createIndex(iRow, iColumn, pItem) : QModelIndex();
581}
582
583QModelIndex UISelectorModel::root() const
584{
585 return index(0, 0);
586}
587
588QVariant UISelectorModel::data(const QModelIndex &specifiedIndex, int iRole) const
589{
590 if (!specifiedIndex.isValid())
591 return QVariant();
592
593 switch (iRole)
594 {
595 /* Basic Attributes: */
596 case Qt::FontRole:
597 {
598 QFont font = m_pParentTree->font();
599 font.setBold(true);
600 return font;
601 }
602 case Qt::SizeHintRole:
603 {
604 const QFontMetrics fm(data(specifiedIndex, Qt::FontRole).value<QFont>());
605 const int iMargin = data(specifiedIndex, R_Margin).toInt();
606 const int iSpacing = data(specifiedIndex, R_Spacing).toInt();
607 const int iIconSize = data(specifiedIndex, R_IconSize).toInt();
608 const QString strName = data(specifiedIndex, R_ItemName).toString();
609 const int iMinimumContentWidth = iIconSize /* icon width */
610 + iSpacing
611 + fm.horizontalAdvance(strName) /* name width */;
612 const int iMinimumContentHeight = qMax(fm.height() /* font height */,
613 iIconSize /* icon height */);
614 const int iMinimumWidth = iMinimumContentWidth + iMargin * 2;
615 const int iMinimumHeight = iMinimumContentHeight + iMargin * 2;
616 return QSize(iMinimumWidth, iMinimumHeight);
617 }
618
619 /* Advanced Attributes: */
620 case R_Margin:
621 {
622 return QApplication::style()->pixelMetric(QStyle::PM_LayoutLeftMargin) / 1.5;
623 }
624 case R_Spacing:
625 {
626 return qMax(QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing), 5) * 2;
627 }
628 case R_IconSize:
629 {
630 return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) * 1.5;
631 }
632 case R_ItemId:
633 {
634 if (UISelectorTreeViewItem *pItem = indexToItem(specifiedIndex))
635 return pItem->id();
636 return 0;
637 }
638 case R_ItemPixmap:
639 {
640 if (UISelectorTreeViewItem *pItem = indexToItem(specifiedIndex))
641 {
642 const QIcon icon = pItem->icon();
643 const int iIconSize = data(specifiedIndex, R_IconSize).toInt();
644 const qreal fDpr = m_pParentTree->window() && m_pParentTree->window()->windowHandle()
645 ? m_pParentTree->window()->windowHandle()->devicePixelRatio() : 1;
646 return icon.pixmap(QSize(iIconSize, iIconSize), fDpr);
647 }
648 return QPixmap();
649 }
650 case R_ItemPixmapRect:
651 {
652 const int iMargin = data(specifiedIndex, R_Margin).toInt();
653 const int iWidth = data(specifiedIndex, R_IconSize).toInt();
654 return QRect(iMargin, iMargin, iWidth, iWidth);
655 }
656 case R_ItemName:
657 {
658 if (UISelectorTreeViewItem *pItem = indexToItem(specifiedIndex))
659 return pItem->text();
660 return QString();
661 }
662 case R_ItemNamePoint:
663 {
664 const int iMargin = data(specifiedIndex, R_Margin).toInt();
665 const int iSpacing = data(specifiedIndex, R_Spacing).toInt();
666 const int iIconSize = data(specifiedIndex, R_IconSize).toInt();
667 const QFontMetrics fm(data(specifiedIndex, Qt::FontRole).value<QFont>());
668 const QSize sizeHint = data(specifiedIndex, Qt::SizeHintRole).toSize();
669 return QPoint(iMargin + iIconSize + iSpacing,
670 sizeHint.height() / 2 + fm.ascent() / 2 - 1 /* base line */);
671 }
672 case R_ItemHidden:
673 {
674 if (UISelectorTreeViewItem *pItem = indexToItem(specifiedIndex))
675 return pItem->isHidden() ? "hide" : "show";
676 return QString();
677 }
678
679 default:
680 break;
681 }
682
683 return QVariant();
684}
685
686bool UISelectorModel::setData(const QModelIndex &specifiedIndex, const QVariant &aValue, int iRole)
687{
688 if (!specifiedIndex.isValid())
689 return QAbstractItemModel::setData(specifiedIndex, aValue, iRole);
690
691 switch (iRole)
692 {
693 /* Advanced Attributes: */
694 case R_ItemName:
695 {
696 if (UISelectorTreeViewItem *pItem = indexToItem(specifiedIndex))
697 {
698 pItem->setText(aValue.toString());
699 emit dataChanged(specifiedIndex, specifiedIndex);
700 return true;
701 }
702 return false;
703 }
704 case R_ItemHidden:
705 {
706 if (UISelectorTreeViewItem *pItem = indexToItem(specifiedIndex))
707 {
708 pItem->setHidden(aValue.toBool());
709 emit dataChanged(specifiedIndex, specifiedIndex);
710 return true;
711 }
712 return false;
713 }
714
715 default:
716 break;
717 }
718
719 return false;
720}
721
722QModelIndex UISelectorModel::addItem(int iID, const QIcon &icon, const QString &strLink)
723{
724 beginInsertRows(root(), m_pRootItem->childCount(), m_pRootItem->childCount());
725 new UISelectorTreeViewItem(m_pRootItem, iID, icon, strLink);
726 endInsertRows();
727 return index(m_pRootItem->childCount() - 1, 0, root());
728}
729
730void UISelectorModel::delItem(int iID)
731{
732 if (UISelectorTreeViewItem *pItem = m_pRootItem->childItemById(iID))
733 {
734 const int iItemPosition = m_pRootItem->posOfChild(pItem);
735 beginRemoveRows(root(), iItemPosition, iItemPosition);
736 delete pItem;
737 endRemoveRows();
738 }
739}
740
741QModelIndex UISelectorModel::findItem(int iID)
742{
743 UISelectorTreeViewItem *pItem = m_pRootItem->childItemById(iID);
744 if (!pItem)
745 return QModelIndex();
746
747 const int iItemPosition = m_pRootItem->posOfChild(pItem);
748 return pItem ? createIndex(iItemPosition, 0, pItem) : QModelIndex();
749}
750
751QModelIndex UISelectorModel::findItem(const QString &strLink)
752{
753 UISelectorTreeViewItem *pItem = m_pRootItem->childItemByLink(strLink);
754 if (!pItem)
755 return QModelIndex();
756
757 const int iItemPosition = m_pRootItem->posOfChild(pItem);
758 return pItem ? createIndex(iItemPosition, 0, pItem) : QModelIndex();
759}
760
761Qt::ItemFlags UISelectorModel::flags(const QModelIndex &specifiedIndex) const
762{
763 return !specifiedIndex.isValid() ? QAbstractItemModel::flags(specifiedIndex)
764 : Qt::ItemIsEnabled | Qt::ItemIsSelectable;
765}
766
767/* static */
768UISelectorTreeViewItem *UISelectorModel::indexToItem(const QModelIndex &specifiedIndex)
769{
770 if (!specifiedIndex.isValid())
771 return 0;
772 return static_cast<UISelectorTreeViewItem*>(specifiedIndex.internalPointer());
773}
774
775
776/*********************************************************************************************************************************
777* Class UISelectorTreeView implementation. *
778*********************************************************************************************************************************/
779
780UISelectorTreeView::UISelectorTreeView(QWidget *pParent)
781 : QITreeView(pParent)
782{
783 prepare();
784}
785
786QSize UISelectorTreeView::minimumSizeHint() const
787{
788 /* Sanity check: */
789 QSortFilterProxyModel *pProxyModel = qobject_cast<QSortFilterProxyModel*>(model());
790 AssertPtrReturn(pProxyModel, QITreeView::minimumSizeHint());
791 UISelectorModel *pModel = qobject_cast<UISelectorModel*>(pProxyModel->sourceModel());
792 AssertPtrReturn(pModel, QITreeView::minimumSizeHint());
793
794 /* Calculate largest column width: */
795 int iMaximumColumnWidth = 0;
796 int iCumulativeColumnHeight = 0;
797 for (int i = 0; i < pModel->rowCount(pModel->root()); ++i)
798 {
799 const QModelIndex iteratedIndex = pModel->index(i, 0, pModel->root());
800 const QSize sizeHint = pModel->data(iteratedIndex, Qt::SizeHintRole).toSize();
801 const int iHeightHint = sizeHint.height();
802 iMaximumColumnWidth = qMax(iMaximumColumnWidth, sizeHint.width() + iHeightHint /* to get the fancy shape */);
803 iCumulativeColumnHeight += iHeightHint;
804 }
805
806 /* Return selector size-hint: */
807 return QSize(iMaximumColumnWidth, iCumulativeColumnHeight);
808}
809
810QSize UISelectorTreeView::sizeHint() const
811{
812 // WORKAROUND:
813 // By default QTreeView uses own size-hint
814 // which we don't like and want to ignore:
815 return minimumSizeHint();
816}
817
818void UISelectorTreeView::prepare()
819{
820 /* Configure tree-view: */
821 setSortingEnabled(true);
822 sortByColumn(0, Qt::AscendingOrder);
823 setFrameShape(QFrame::NoFrame);
824 viewport()->setAutoFillBackground(false);
825 setContextMenuPolicy(Qt::PreventContextMenu);
826 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
827 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
828 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
829
830 /* Prepare selector delegate: */
831 UISelectorDelegate *pDelegate = new UISelectorDelegate(this);
832 if (pDelegate)
833 setItemDelegate(pDelegate);
834}
835
836
837/*********************************************************************************************************************************
838* Class UISettingsSelector implementation. *
839*********************************************************************************************************************************/
840
841UISettingsSelector::UISettingsSelector(QWidget *pParent /* = 0 */)
842 : QObject(pParent)
843{
844}
845
846UISettingsSelector::~UISettingsSelector()
847{
848 qDeleteAll(m_list);
849 m_list.clear();
850}
851
852void UISettingsSelector::setItemText(int iID, const QString &strText)
853{
854 if (UISelectorItem *pTtem = findItem(iID))
855 pTtem->setText(strText);
856}
857
858QString UISettingsSelector::itemTextByPage(UISettingsPage *pPage) const
859{
860 QString strText;
861 if (UISelectorItem *pItem = findItemByPage(pPage))
862 strText = pItem->text();
863 return strText;
864}
865
866QWidget *UISettingsSelector::idToPage(int iID) const
867{
868 UISettingsPage *pPage = 0;
869 if (UISelectorItem *pItem = findItem(iID))
870 pPage = pItem->page();
871 return pPage;
872}
873
874QList<UISettingsPage*> UISettingsSelector::settingPages() const
875{
876 QList<UISettingsPage*> list;
877 foreach (UISelectorItem *pItem, m_list)
878 if (pItem->page())
879 list << pItem->page();
880 return list;
881}
882
883UISelectorItem *UISettingsSelector::findItem(int iID) const
884{
885 UISelectorItem *pResult = 0;
886 foreach (UISelectorItem *pItem, m_list)
887 if (pItem->id() == iID)
888 {
889 pResult = pItem;
890 break;
891 }
892 return pResult;
893}
894
895UISelectorItem *UISettingsSelector::findItemByLink(const QString &strLink) const
896{
897 UISelectorItem *pResult = 0;
898 foreach (UISelectorItem *pItem, m_list)
899 if (pItem->link() == strLink)
900 {
901 pResult = pItem;
902 break;
903 }
904 return pResult;
905}
906
907UISelectorItem *UISettingsSelector::findItemByPage(UISettingsPage *pPage) const
908{
909 UISelectorItem *pResult = 0;
910 foreach (UISelectorItem *pItem, m_list)
911 if (pItem->page() == pPage)
912 {
913 pResult = pItem;
914 break;
915 }
916 return pResult;
917}
918
919
920/*********************************************************************************************************************************
921* Class UISettingsSelectorTreeView implementation. *
922*********************************************************************************************************************************/
923
924UISettingsSelectorTreeView::UISettingsSelectorTreeView(QWidget *pParent /* = 0 */)
925 : UISettingsSelector(pParent)
926 , m_pTreeView(0)
927 , m_pModel(0)
928 , m_pModelProxy(0)
929{
930 prepare();
931}
932
933UISettingsSelectorTreeView::~UISettingsSelectorTreeView()
934{
935 cleanup();
936}
937
938QWidget *UISettingsSelectorTreeView::widget() const
939{
940 return m_pTreeView;
941}
942
943QWidget *UISettingsSelectorTreeView::addItem(const QString & /* strBigIcon */,
944 const QString &strMediumIcon ,
945 const QString & /* strSmallIcon */,
946 int iID,
947 const QString &strLink,
948 UISettingsPage *pPage /* = 0 */,
949 int iParentID /* = -1 */)
950{
951 QWidget *pResult = 0;
952 if (pPage)
953 {
954 /* Adjust page a bit: */
955 pPage->setContentsMargins(0, 0, 0, 0);
956 if (pPage->layout())
957 pPage->layout()->setContentsMargins(0, 0, 0, 0);
958 pResult = pPage;
959
960 /* Add selector-item object: */
961 const QIcon icon = UIIconPool::iconSet(strMediumIcon);
962 UISelectorItem *pItem = new UISelectorItem(icon, iID, strLink, pPage, iParentID);
963 if (pItem)
964 m_list.append(pItem);
965
966 /* Add model-item: */
967 m_pModel->addItem(iID, pItem->icon(), strLink);
968 }
969 return pResult;
970}
971
972void UISettingsSelectorTreeView::setItemVisible(int iID, bool fVisible)
973{
974 /* Look for the tree-view item to assign the text: */
975 QModelIndex specifiedIndex = m_pModel->findItem(iID);
976 if (specifiedIndex.isValid())
977 m_pModel->setData(specifiedIndex, !fVisible, UISelectorModel::R_ItemHidden);
978}
979
980void UISettingsSelectorTreeView::setItemText(int iID, const QString &strText)
981{
982 /* Call to base-class: */
983 UISettingsSelector::setItemText(iID, strText);
984
985 /* Look for the tree-view item to assign the text: */
986 QModelIndex specifiedIndex = m_pModel->findItem(iID);
987 if (specifiedIndex.isValid())
988 m_pModel->setData(specifiedIndex, strText, UISelectorModel::R_ItemName);
989}
990
991QString UISettingsSelectorTreeView::itemText(int iID) const
992{
993 QString strResult;
994 if (UISelectorItem *pItem = findItem(iID))
995 strResult = pItem->text();
996 return strResult;
997}
998
999int UISettingsSelectorTreeView::currentId() const
1000{
1001 int iID = -1;
1002 const QModelIndex proxyIndex = m_pTreeView->currentIndex();
1003 const QModelIndex index = m_pModelProxy->mapToSource(proxyIndex);
1004 if (index.isValid())
1005 iID = m_pModel->data(index, UISelectorModel::R_ItemId).toString().toInt();
1006 return iID;
1007}
1008
1009int UISettingsSelectorTreeView::linkToId(const QString &strLink) const
1010{
1011 int iID = -1;
1012 const QModelIndex index = m_pModel->findItem(strLink);
1013 if (index.isValid())
1014 iID = m_pModel->data(index, UISelectorModel::R_ItemId).toString().toInt();
1015 return iID;
1016}
1017
1018void UISettingsSelectorTreeView::selectById(int iID, bool fSilently)
1019{
1020 const QModelIndex index = m_pModel->findItem(iID);
1021 const QModelIndex proxyIndex = m_pModelProxy->mapFromSource(index);
1022 if (proxyIndex.isValid())
1023 {
1024 if (fSilently)
1025 m_pTreeView->blockSignals(true);
1026 m_pTreeView->setCurrentIndex(proxyIndex);
1027 if (fSilently)
1028 m_pTreeView->blockSignals(false);
1029 }
1030}
1031
1032void UISettingsSelectorTreeView::sltHandleCurrentChanged(const QModelIndex &proxyIndex,
1033 const QModelIndex & /* previousIndex */)
1034{
1035 const QModelIndex index = m_pModelProxy->mapToSource(proxyIndex);
1036 if (index.isValid())
1037 {
1038 const int iID = m_pModel->data(index, UISelectorModel::R_ItemId).toString().toInt();
1039 Assert(iID >= 0);
1040 emit sigCategoryChanged(iID);
1041 }
1042}
1043
1044void UISettingsSelectorTreeView::prepare()
1045{
1046 /* Prepare the tree-widget: */
1047 m_pTreeView = new UISelectorTreeView(qobject_cast<QWidget*>(parent()));
1048 if (m_pTreeView)
1049 {
1050 /* Prepare the model: */
1051 m_pModel = new UISelectorModel(m_pTreeView);
1052 if (m_pModel)
1053 {
1054 /* Create proxy-model: */
1055 m_pModelProxy = new QSortFilterProxyModel(m_pTreeView);
1056 if (m_pModelProxy)
1057 {
1058 /* Configure proxy-model: */
1059 m_pModelProxy->setSortRole(UISelectorModel::R_ItemId);
1060 m_pModelProxy->setFilterRole(UISelectorModel::R_ItemHidden);
1061 m_pModelProxy->setFilterFixedString("show");
1062 m_pModelProxy->setSourceModel(m_pModel);
1063 m_pTreeView->setModel(m_pModelProxy);
1064 }
1065 const QModelIndex proxyIndex = m_pModelProxy->mapFromSource(m_pModel->root());
1066 m_pTreeView->setRootIndex(proxyIndex);
1067 }
1068
1069 /* Setup connections: */
1070 connect(m_pTreeView, &QITreeView::currentItemChanged,
1071 this, &UISettingsSelectorTreeView::sltHandleCurrentChanged);
1072 }
1073}
1074
1075void UISettingsSelectorTreeView::cleanup()
1076{
1077 /* Cleanup the model: */
1078 delete m_pModel;
1079 m_pModel = 0;
1080 /* Cleanup the tree-view: */
1081 delete m_pTreeView;
1082 m_pTreeView = 0;
1083}
1084
1085
1086#include "UISettingsSelector.moc"
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use