VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/extensions/QITreeWidget.cpp

Last change on this file was 101399, checked in by vboxsync, 7 months ago

FE/Qt: Advancing QITreeWidget with optional possibility to full delegate item painting to sub-class.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 KB
Line 
1/* $Id: QITreeWidget.cpp 101399 2023-10-10 06:13:03Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - Qt extensions: QITreeWidget 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 <QAccessibleWidget>
30#include <QPainter>
31#include <QResizeEvent>
32
33/* GUI includes: */
34#include "QITreeWidget.h"
35
36/* Other VBox includes: */
37#include "iprt/assert.h"
38
39
40/** QAccessibleObject extension used as an accessibility interface for QITreeWidgetItem. */
41class QIAccessibilityInterfaceForQITreeWidgetItem : public QAccessibleObject
42{
43public:
44
45 /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
46 static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
47 {
48 /* Creating QITreeWidgetItem accessibility interface: */
49 if (pObject && strClassname == QLatin1String("QITreeWidgetItem"))
50 return new QIAccessibilityInterfaceForQITreeWidgetItem(pObject);
51
52 /* Null by default: */
53 return 0;
54 }
55
56 /** Constructs an accessibility interface passing @a pObject to the base-class. */
57 QIAccessibilityInterfaceForQITreeWidgetItem(QObject *pObject)
58 : QAccessibleObject(pObject)
59 {}
60
61 /** Returns the parent. */
62 virtual QAccessibleInterface *parent() const RT_OVERRIDE;
63
64 /** Returns the number of children. */
65 virtual int childCount() const RT_OVERRIDE;
66 /** Returns the child with the passed @a iIndex. */
67 virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE;
68 /** Returns the index of the passed @a pChild. */
69 virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE;
70
71 /** Returns the rect. */
72 virtual QRect rect() const RT_OVERRIDE;
73 /** Returns a text for the passed @a enmTextRole. */
74 virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
75
76 /** Returns the role. */
77 virtual QAccessible::Role role() const RT_OVERRIDE;
78 /** Returns the state. */
79 virtual QAccessible::State state() const RT_OVERRIDE;
80
81private:
82
83 /** Returns corresponding QITreeWidgetItem. */
84 QITreeWidgetItem *item() const { return qobject_cast<QITreeWidgetItem*>(object()); }
85};
86
87
88/** QAccessibleWidget extension used as an accessibility interface for QITreeWidget. */
89class QIAccessibilityInterfaceForQITreeWidget : public QAccessibleWidget
90{
91public:
92
93 /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
94 static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
95 {
96 /* Creating QITreeWidget accessibility interface: */
97 if (pObject && strClassname == QLatin1String("QITreeWidget"))
98 return new QIAccessibilityInterfaceForQITreeWidget(qobject_cast<QWidget*>(pObject));
99
100 /* Null by default: */
101 return 0;
102 }
103
104 /** Constructs an accessibility interface passing @a pWidget to the base-class. */
105 QIAccessibilityInterfaceForQITreeWidget(QWidget *pWidget)
106 : QAccessibleWidget(pWidget, QAccessible::List)
107 {}
108
109 /** Returns the number of children. */
110 virtual int childCount() const RT_OVERRIDE;
111 /** Returns the child with the passed @a iIndex. */
112 virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE;
113 /** Returns the index of the passed @a pChild. */
114 virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE;
115
116 /** Returns a text for the passed @a enmTextRole. */
117 virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
118
119private:
120
121 /** Returns corresponding QITreeWidget. */
122 QITreeWidget *tree() const { return qobject_cast<QITreeWidget*>(widget()); }
123};
124
125
126/*********************************************************************************************************************************
127* Class QIAccessibilityInterfaceForQITreeWidgetItem implementation. *
128*********************************************************************************************************************************/
129
130QAccessibleInterface *QIAccessibilityInterfaceForQITreeWidgetItem::parent() const
131{
132 /* Make sure item still alive: */
133 AssertPtrReturn(item(), 0);
134
135 /* Return the parent: */
136 return item()->parentItem() ?
137 QAccessible::queryAccessibleInterface(item()->parentItem()) :
138 QAccessible::queryAccessibleInterface(item()->parentTree());
139}
140
141int QIAccessibilityInterfaceForQITreeWidgetItem::childCount() const
142{
143 /* Make sure item still alive: */
144 AssertPtrReturn(item(), 0);
145
146 /* Return the number of children: */
147 return item()->childCount();
148}
149
150QAccessibleInterface *QIAccessibilityInterfaceForQITreeWidgetItem::child(int iIndex) const
151{
152 /* Make sure item still alive: */
153 AssertPtrReturn(item(), 0);
154 /* Make sure index is valid: */
155 AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
156
157 /* Return the child with the passed iIndex: */
158 return QAccessible::queryAccessibleInterface(item()->childItem(iIndex));
159}
160
161int QIAccessibilityInterfaceForQITreeWidgetItem::indexOfChild(const QAccessibleInterface *pChild) const
162{
163 /* Search for corresponding child: */
164 for (int iIndex = 0; iIndex < childCount(); ++iIndex)
165 if (child(iIndex) == pChild)
166 return iIndex;
167
168 /* -1 by default: */
169 return -1;
170}
171
172QRect QIAccessibilityInterfaceForQITreeWidgetItem::rect() const
173{
174 /* Make sure item still alive: */
175 AssertPtrReturn(item(), QRect());
176
177 /* Compose common region: */
178 QRegion region;
179
180 /* Append item rectangle: */
181 const QRect itemRectInViewport = item()->parentTree()->visualItemRect(item());
182 const QSize itemSize = itemRectInViewport.size();
183 const QPoint itemPosInViewport = itemRectInViewport.topLeft();
184 const QPoint itemPosInScreen = item()->parentTree()->viewport()->mapToGlobal(itemPosInViewport);
185 const QRect itemRectInScreen = QRect(itemPosInScreen, itemSize);
186 region += itemRectInScreen;
187
188 /* Append children rectangles: */
189 for (int i = 0; i < childCount(); ++i)
190 region += child(i)->rect();
191
192 /* Return common region bounding rectangle: */
193 return region.boundingRect();
194}
195
196QString QIAccessibilityInterfaceForQITreeWidgetItem::text(QAccessible::Text enmTextRole) const
197{
198 /* Make sure item still alive: */
199 AssertPtrReturn(item(), QString());
200
201 /* Return a text for the passed enmTextRole: */
202 switch (enmTextRole)
203 {
204 case QAccessible::Name: return item()->defaultText();
205 default: break;
206 }
207
208 /* Null-string by default: */
209 return QString();
210}
211
212QAccessible::Role QIAccessibilityInterfaceForQITreeWidgetItem::role() const
213{
214 /* Return the role of item with children: */
215 if (childCount() > 0)
216 return QAccessible::List;
217
218 /* ListItem by default: */
219 return QAccessible::ListItem;
220}
221
222QAccessible::State QIAccessibilityInterfaceForQITreeWidgetItem::state() const
223{
224 /* Make sure item still alive: */
225 AssertPtrReturn(item(), QAccessible::State());
226
227 /* Compose the state: */
228 QAccessible::State state;
229 state.focusable = true;
230 state.selectable = true;
231
232 /* Compose the state of current item: */
233 if ( item()
234 && item() == QITreeWidgetItem::toItem(item()->treeWidget()->currentItem()))
235 {
236 state.active = true;
237 state.focused = true;
238 state.selected = true;
239 }
240
241 /* Compose the state of checked item: */
242 if ( item()
243 && item()->checkState(0) != Qt::Unchecked)
244 {
245 state.checked = true;
246 if (item()->checkState(0) == Qt::PartiallyChecked)
247 state.checkStateMixed = true;
248 }
249
250 /* Return the state: */
251 return state;
252}
253
254
255/*********************************************************************************************************************************
256* Class QIAccessibilityInterfaceForQITreeWidget implementation. *
257*********************************************************************************************************************************/
258
259int QIAccessibilityInterfaceForQITreeWidget::childCount() const
260{
261 /* Make sure tree still alive: */
262 AssertPtrReturn(tree(), 0);
263
264 /* Return the number of children: */
265 return tree()->childCount();
266}
267
268QAccessibleInterface *QIAccessibilityInterfaceForQITreeWidget::child(int iIndex) const
269{
270 /* Make sure tree still alive: */
271 AssertPtrReturn(tree(), 0);
272 /* Make sure index is valid: */
273 AssertReturn(iIndex >= 0, 0);
274 if (iIndex >= childCount())
275 {
276 // WORKAROUND:
277 // Normally I would assert here, but Qt5 accessibility code has
278 // a hard-coded architecture for a tree-widgets which we do not like
279 // but have to live with and this architecture enumerates children
280 // of all levels as children of level 0, so Qt5 can try to address
281 // our interface with index which surely out of bounds by our laws.
282 // So let's assume that's exactly such case and try to enumerate
283 // visible children like they are a part of the list, not tree.
284 // printf("Invalid index: %d\n", iIndex);
285
286 // Take into account we also have header with 'column count' indexes,
287 // so we should start enumerating tree indexes since 'column count'.
288 const int iColumnCount = tree()->columnCount();
289 int iCurrentIndex = iColumnCount;
290
291 // Do some sanity check as well, enough?
292 AssertReturn(iIndex >= iColumnCount, 0);
293
294 // Search for sibling with corresponding index:
295 QTreeWidgetItem *pItem = tree()->topLevelItem(0);
296 while (pItem && iCurrentIndex < iIndex)
297 {
298 ++iCurrentIndex;
299 if (iCurrentIndex % iColumnCount == 0)
300 pItem = tree()->itemBelow(pItem);
301 }
302
303 // Return what we found:
304 // if (pItem)
305 // printf("Item found: [%s]\n", pItem->text(0).toUtf8().constData());
306 // else
307 // printf("Item not found\n");
308 return pItem ? QAccessible::queryAccessibleInterface(QITreeWidgetItem::toItem(pItem)) : 0;
309 }
310
311 /* Return the child with the passed iIndex: */
312 return QAccessible::queryAccessibleInterface(tree()->childItem(iIndex));
313}
314
315int QIAccessibilityInterfaceForQITreeWidget::indexOfChild(const QAccessibleInterface *pChild) const
316{
317 /* Make sure tree still alive: */
318 AssertPtrReturn(tree(), -1);
319 /* Make sure child is valid: */
320 AssertReturn(pChild, -1);
321
322 // WORKAROUND:
323 // Not yet sure how to handle this for tree widget with multiple columns, so this is a simple hack:
324 const QModelIndex index = tree()->itemIndex(qobject_cast<QITreeWidgetItem*>(pChild->object()));
325 const int iIndex = index.row();
326 return iIndex;
327}
328
329QString QIAccessibilityInterfaceForQITreeWidget::text(QAccessible::Text /* enmTextRole */) const
330{
331 /* Make sure tree still alive: */
332 AssertPtrReturn(tree(), QString());
333
334 /* Gather suitable text: */
335 QString strText = tree()->toolTip();
336 if (strText.isEmpty())
337 strText = tree()->whatsThis();
338 return strText;
339}
340
341
342/*********************************************************************************************************************************
343* Class QITreeWidgetItem implementation. *
344*********************************************************************************************************************************/
345
346/* static */
347QITreeWidgetItem *QITreeWidgetItem::toItem(QTreeWidgetItem *pItem)
348{
349 /* Make sure alive QITreeWidgetItem passed: */
350 if (!pItem || pItem->type() != ItemType)
351 return 0;
352
353 /* Return casted QITreeWidgetItem: */
354 return static_cast<QITreeWidgetItem*>(pItem);
355}
356
357/* static */
358const QITreeWidgetItem *QITreeWidgetItem::toItem(const QTreeWidgetItem *pItem)
359{
360 /* Make sure alive QITreeWidgetItem passed: */
361 if (!pItem || pItem->type() != ItemType)
362 return 0;
363
364 /* Return casted QITreeWidgetItem: */
365 return static_cast<const QITreeWidgetItem*>(pItem);
366}
367
368QITreeWidgetItem::QITreeWidgetItem()
369 : QTreeWidgetItem(ItemType)
370{
371}
372
373QITreeWidgetItem::QITreeWidgetItem(QITreeWidget *pTreeWidget)
374 : QTreeWidgetItem(pTreeWidget, ItemType)
375{
376}
377
378QITreeWidgetItem::QITreeWidgetItem(QITreeWidgetItem *pTreeWidgetItem)
379 : QTreeWidgetItem(pTreeWidgetItem, ItemType)
380{
381}
382
383QITreeWidgetItem::QITreeWidgetItem(QITreeWidget *pTreeWidget, const QStringList &strings)
384 : QTreeWidgetItem(pTreeWidget, strings, ItemType)
385{
386}
387
388QITreeWidgetItem::QITreeWidgetItem(QITreeWidgetItem *pTreeWidgetItem, const QStringList &strings)
389 : QTreeWidgetItem(pTreeWidgetItem, strings, ItemType)
390{
391}
392
393QITreeWidget *QITreeWidgetItem::parentTree() const
394{
395 return treeWidget() ? qobject_cast<QITreeWidget*>(treeWidget()) : 0;
396}
397
398QITreeWidgetItem *QITreeWidgetItem::parentItem() const
399{
400 return QTreeWidgetItem::parent() ? toItem(QTreeWidgetItem::parent()) : 0;
401}
402
403QITreeWidgetItem *QITreeWidgetItem::childItem(int iIndex) const
404{
405 return QTreeWidgetItem::child(iIndex) ? toItem(QTreeWidgetItem::child(iIndex)) : 0;
406}
407
408QString QITreeWidgetItem::defaultText() const
409{
410 /* Return 1st cell text as default: */
411 return text(0);
412}
413
414
415/*********************************************************************************************************************************
416* Class QITreeWidget implementation. *
417*********************************************************************************************************************************/
418
419QITreeWidget::QITreeWidget(QWidget *pParent /* = 0 */, bool fDelegatePaintingToSubclass /* = false */)
420 : QTreeWidget(pParent)
421 , m_fDelegatePaintingToSubclass(fDelegatePaintingToSubclass)
422{
423 /* Install QITreeWidget accessibility interface factory: */
424 QAccessible::installFactory(QIAccessibilityInterfaceForQITreeWidget::pFactory);
425 /* Install QITreeWidgetItem accessibility interface factory: */
426 QAccessible::installFactory(QIAccessibilityInterfaceForQITreeWidgetItem::pFactory);
427
428 // WORKAROUND:
429 // Ok, what do we have here..
430 // There is a bug in QAccessible framework which might be just treated like
431 // a functionality flaw. It consist in fact that if an accessibility client
432 // is enabled, base-class can request an accessibility interface in own
433 // constructor before the sub-class registers own factory, so we have to
434 // recreate interface after we finished with our own initialization.
435 QAccessibleInterface *pInterface = QAccessible::queryAccessibleInterface(this);
436 if (pInterface)
437 {
438 QAccessible::deleteAccessibleInterface(QAccessible::uniqueId(pInterface));
439 QAccessible::queryAccessibleInterface(this); // <= new one, proper..
440 }
441
442 /* Do not paint frame and background unless requested: */
443 if (m_fDelegatePaintingToSubclass)
444 {
445 setFrameShape(QFrame::NoFrame);
446 viewport()->setAutoFillBackground(false);
447 }
448}
449
450void QITreeWidget::setSizeHintForItems(const QSize &sizeHint)
451{
452 /* Pass the sizeHint to all the top-level items: */
453 for (int i = 0; i < topLevelItemCount(); ++i)
454 topLevelItem(i)->setSizeHint(0, sizeHint);
455}
456
457int QITreeWidget::childCount() const
458{
459 return invisibleRootItem()->childCount();
460}
461
462QITreeWidgetItem *QITreeWidget::childItem(int iIndex) const
463{
464 return invisibleRootItem()->child(iIndex) ? QITreeWidgetItem::toItem(invisibleRootItem()->child(iIndex)) : 0;
465}
466
467QModelIndex QITreeWidget::itemIndex(QTreeWidgetItem *pItem)
468{
469 return indexFromItem(pItem);
470}
471
472QList<QTreeWidgetItem*> QITreeWidget::filterItems(const QITreeWidgetItemFilter &filter, QTreeWidgetItem *pParent /* = 0 */)
473{
474 QList<QTreeWidgetItem*> filteredItemList;
475 filterItemsInternal(filter, pParent ? pParent : invisibleRootItem(), filteredItemList);
476 return filteredItemList;
477}
478
479void QITreeWidget::paintEvent(QPaintEvent *pEvent)
480{
481 /* Call to base-class if allowed: */
482 if (!m_fDelegatePaintingToSubclass)
483 QTreeWidget::paintEvent(pEvent);
484
485 /* Create item painter: */
486 QPainter painter;
487 painter.begin(viewport());
488
489 /* Notify listeners about painting: */
490 QTreeWidgetItemIterator it(this);
491 while (*it)
492 {
493 emit painted(*it, &painter);
494 ++it;
495 }
496
497 /* Close item painter: */
498 painter.end();
499}
500
501void QITreeWidget::resizeEvent(QResizeEvent *pEvent)
502{
503 /* Call to base-class: */
504 QTreeWidget::resizeEvent(pEvent);
505
506 /* Notify listeners about resizing: */
507 emit resized(pEvent->size(), pEvent->oldSize());
508}
509
510void QITreeWidget::filterItemsInternal(const QITreeWidgetItemFilter &filter, QTreeWidgetItem *pParent,
511 QList<QTreeWidgetItem*> &filteredItemList)
512{
513 if (!pParent)
514 return;
515 if (filter(pParent))
516 filteredItemList.append(pParent);
517
518 for (int i = 0; i < pParent->childCount(); ++i)
519 filterItemsInternal(filter, pParent->child(i), filteredItemList);
520}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use