VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.5 KB
Line 
1/* $Id: QITreeView.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - Qt extensions: QITreeView class implementation.
4 */
5
6/*
7 * Copyright (C) 2009-2024 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 <QDragLeaveEvent>
31#include <QDragMoveEvent>
32#include <QDropEvent>
33#include <QMouseEvent>
34#include <QPainter>
35#include <QSortFilterProxyModel>
36
37/* GUI includes: */
38#include "QITreeView.h"
39
40/* Other VBox includes: */
41#include "iprt/assert.h"
42
43
44/** QAccessibleObject extension used as an accessibility interface for QITreeViewItem. */
45class QIAccessibilityInterfaceForQITreeViewItem : public QAccessibleObject
46{
47public:
48
49 /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
50 static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
51 {
52 /* Creating QITreeViewItem accessibility interface: */
53 if (pObject && strClassname == QLatin1String("QITreeViewItem"))
54 return new QIAccessibilityInterfaceForQITreeViewItem(pObject);
55
56 /* Null by default: */
57 return 0;
58 }
59
60 /** Constructs an accessibility interface passing @a pObject to the base-class. */
61 QIAccessibilityInterfaceForQITreeViewItem(QObject *pObject)
62 : QAccessibleObject(pObject)
63 {}
64
65 /** Returns the parent. */
66 virtual QAccessibleInterface *parent() const RT_OVERRIDE RT_FINAL;
67
68 /** Returns the number of children. */
69 virtual int childCount() const RT_OVERRIDE RT_FINAL;
70 /** Returns the child with the passed @a iIndex. */
71 virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE RT_FINAL;
72 /** Returns the index of the passed @a pChild. */
73 virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE RT_FINAL;
74
75 /** Returns the rect. */
76 virtual QRect rect() const RT_OVERRIDE RT_FINAL;
77 /** Returns a text for the passed @a enmTextRole. */
78 virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE RT_FINAL;
79
80 /** Returns the role. */
81 virtual QAccessible::Role role() const RT_OVERRIDE RT_FINAL;
82 /** Returns the state. */
83 virtual QAccessible::State state() const RT_OVERRIDE RT_FINAL;
84
85private:
86
87 /** Returns corresponding QITreeViewItem. */
88 QITreeViewItem *item() const { return qobject_cast<QITreeViewItem*>(object()); }
89
90 /** Returns item bounding rectangle including all the children. */
91 QRect boundingRect() const;
92};
93
94
95/** QAccessibleWidget extension used as an accessibility interface for QITreeView. */
96class QIAccessibilityInterfaceForQITreeView : public QAccessibleWidget
97{
98public:
99
100 /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
101 static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
102 {
103 /* Creating QITreeView accessibility interface: */
104 if (pObject && strClassname == QLatin1String("QITreeView"))
105 return new QIAccessibilityInterfaceForQITreeView(qobject_cast<QWidget*>(pObject));
106
107 /* Null by default: */
108 return 0;
109 }
110
111 /** Constructs an accessibility interface passing @a pWidget to the base-class. */
112 QIAccessibilityInterfaceForQITreeView(QWidget *pWidget)
113 : QAccessibleWidget(pWidget, QAccessible::List)
114 {}
115
116 /** Returns the number of children. */
117 virtual int childCount() const RT_OVERRIDE RT_FINAL;
118 /** Returns the child with the passed @a iIndex. */
119 virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE RT_FINAL;
120 /** Returns the index of the passed @a pChild. */
121 virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE RT_FINAL;
122
123 /** Returns a text for the passed @a enmTextRole. */
124 virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE RT_FINAL;
125
126private:
127
128 /** Returns corresponding QITreeView. */
129 QITreeView *tree() const { return qobject_cast<QITreeView*>(widget()); }
130};
131
132
133/*********************************************************************************************************************************
134* Class QIAccessibilityInterfaceForQITreeViewItem implementation. *
135*********************************************************************************************************************************/
136
137QAccessibleInterface *QIAccessibilityInterfaceForQITreeViewItem::parent() const
138{
139 /* Sanity check: */
140 AssertPtrReturn(item(), 0);
141
142 /* Return the parent: */
143 return item()->parentItem() ?
144 QAccessible::queryAccessibleInterface(item()->parentItem()) :
145 QAccessible::queryAccessibleInterface(item()->parentTree());
146}
147
148int QIAccessibilityInterfaceForQITreeViewItem::childCount() const
149{
150 /* Sanity check: */
151 AssertPtrReturn(item(), 0);
152 AssertPtrReturn(item()->parentTree(), 0);
153 AssertPtrReturn(item()->parentTree()->model(), 0);
154
155 /* Acquire item model-index: */
156 const QModelIndex itemIndex = item()->modelIndex();
157
158 /* Return the number of children: */
159 return item()->parentTree()->model()->rowCount(itemIndex);
160}
161
162QAccessibleInterface *QIAccessibilityInterfaceForQITreeViewItem::child(int iIndex) const /* override */
163{
164 /* Make sure item still alive: */
165 QITreeViewItem *pThisItem = item();
166 AssertPtrReturn(pThisItem, 0);
167 /* Make sure tree still alive: */
168 QITreeView *pTree = pThisItem->parentTree();
169 AssertPtrReturn(pTree, 0);
170 /* Make sure model still alive: */
171 QAbstractItemModel *pModel = pTree->model();
172 AssertPtrReturn(pModel, 0);
173 /* Make sure index is valid: */
174 AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
175
176 /* Acquire parent model-index: */
177 const QModelIndex parentIndex = pThisItem->modelIndex();
178
179 /* Acquire child-index: */
180 const QModelIndex childIndex = pModel->index(iIndex, 0, parentIndex);
181 /* Check whether we have proxy model set or source one otherwise: */
182 const QSortFilterProxyModel *pProxyModel = qobject_cast<const QSortFilterProxyModel*>(pModel);
183 /* Acquire source-model child-index (can be the same as original if there is no proxy model): */
184 const QModelIndex sourceChildIndex = pProxyModel ? pProxyModel->mapToSource(childIndex) : childIndex;
185
186 /* Acquire child item: */
187 QITreeViewItem *pItem = reinterpret_cast<QITreeViewItem*>(sourceChildIndex.internalPointer());
188 /* Return child item's accessibility interface: */
189 return QAccessible::queryAccessibleInterface(pItem);
190}
191
192int QIAccessibilityInterfaceForQITreeViewItem::indexOfChild(const QAccessibleInterface *pChild) const
193{
194 /* Search for corresponding child: */
195 for (int i = 0; i < childCount(); ++i)
196 if (child(i) == pChild)
197 return i;
198
199 /* -1 by default: */
200 return -1;
201}
202
203QRect QIAccessibilityInterfaceForQITreeViewItem::rect() const
204{
205 /* Sanity check: */
206 AssertPtrReturn(item(), QRect());
207 AssertPtrReturn(item()->parentTree(), QRect());
208 AssertPtrReturn(item()->parentTree()->viewport(), QRect());
209
210 /* Get the local rect: */
211 const QRect itemRectInViewport = boundingRect();
212 const QSize itemSize = itemRectInViewport.size();
213 const QPoint itemPosInViewport = itemRectInViewport.topLeft();
214 const QPoint itemPosInScreen = item()->parentTree()->viewport()->mapToGlobal(itemPosInViewport);
215 const QRect itemRectInScreen = QRect(itemPosInScreen, itemSize);
216
217 /* Return the rect: */
218 return itemRectInScreen;
219}
220
221QString QIAccessibilityInterfaceForQITreeViewItem::text(QAccessible::Text enmTextRole) const
222{
223 /* Sanity check: */
224 AssertPtrReturn(item(), QString());
225
226 /* Return a text for the passed enmTextRole: */
227 switch (enmTextRole)
228 {
229 case QAccessible::Name: return item()->text();
230 default: break;
231 }
232
233 /* Null-string by default: */
234 return QString();
235}
236
237QAccessible::Role QIAccessibilityInterfaceForQITreeViewItem::role() const
238{
239 /* List if there are children, ListItem by default: */
240 return childCount() ? QAccessible::List : QAccessible::ListItem;
241}
242
243QAccessible::State QIAccessibilityInterfaceForQITreeViewItem::state() const
244{
245 /* Empty state by default: */
246 return QAccessible::State();
247}
248
249QRect QIAccessibilityInterfaceForQITreeViewItem::boundingRect() const
250{
251 /* Calculate cumulative region: */
252 QRegion region;
253 /* Append item rectangle itself: */
254 region += item()->rect();
255 /* Do the same for all the children: */
256 for (int i = 0; i < childCount(); ++i)
257 {
258 QIAccessibilityInterfaceForQITreeViewItem *pChild =
259 dynamic_cast<QIAccessibilityInterfaceForQITreeViewItem*>(child(i));
260 AssertPtrReturn(pChild, QRect());
261 region += pChild->boundingRect();
262 }
263 /* Return cumulative bounding rectangle: */
264 return region.boundingRect();
265}
266
267
268/*********************************************************************************************************************************
269* Class QIAccessibilityInterfaceForQITreeView implementation. *
270*********************************************************************************************************************************/
271
272int QIAccessibilityInterfaceForQITreeView::childCount() const
273{
274 /* Make sure tree still alive: */
275 AssertPtrReturn(tree(), 0);
276 /* Make sure model still alive: */
277 AssertPtrReturn(tree()->model(), 0);
278
279 /* Acquire required model-index, that can be root-index if specified
280 * or null index otherwise, in that case we return the amount of top-level children: */
281 const QModelIndex rootIndex = tree()->rootIndex();
282 const QModelIndex requiredIndex = rootIndex.isValid() ? rootIndex : QModelIndex();
283
284 /* Return the number of children: */
285 return tree()->model()->rowCount(requiredIndex);
286}
287
288QAccessibleInterface *QIAccessibilityInterfaceForQITreeView::child(int iIndex) const
289{
290 /* Make sure tree still alive: */
291 QITreeView *pTree = tree();
292 AssertPtrReturn(pTree, 0);
293 /* Make sure model still alive: */
294 QAbstractItemModel *pModel = pTree->model();
295 AssertPtrReturn(pModel, 0);
296 /* Make sure index is valid: */
297 AssertReturn(iIndex >= 0, 0);
298
299 /* Real index might be different: */
300 int iRealIndex = iIndex;
301
302 // WORKAROUND:
303 // For a tree-views Qt accessibility code has a hard-coded architecture which we do not like
304 // but have to live with, this architecture enumerates children of all levels as children of level 0,
305 // so Qt can try to address our interface with index which surely out of bounds by our laws.
306 // Let's assume that's exactly the case and try to enumerate children like they are a part of the list, not tree.
307 if (iRealIndex >= childCount())
308 {
309 // Split delimeter is overall column count:
310 const int iColumnCount = pModel->columnCount();
311
312 // Real row index:
313 iRealIndex = iRealIndex / iColumnCount;
314 }
315
316 /* Make sure index fits the bounds finally: */
317 if (iRealIndex >= childCount())
318 return 0;
319
320 /* Acquire parent model-index, that can be root-index if specified
321 * or null index otherwise, in that case we will return one of top-level children: */
322 const QModelIndex rootIndex = tree()->rootIndex();
323 const QModelIndex parentIndex = rootIndex.isValid() ? rootIndex : QModelIndex();
324
325 /* Acquire child-index: */
326 const QModelIndex childIndex = pModel->index(iRealIndex, 0, parentIndex);
327 /* Check whether we have proxy model set or source one otherwise: */
328 const QSortFilterProxyModel *pProxyModel = qobject_cast<const QSortFilterProxyModel*>(pModel);
329 /* Acquire source-model child-index (can be the same as original if there is no proxy model): */
330 const QModelIndex sourceChildIndex = pProxyModel ? pProxyModel->mapToSource(childIndex) : childIndex;
331
332 /* Acquire child item: */
333 QITreeViewItem *pItem = reinterpret_cast<QITreeViewItem*>(sourceChildIndex.internalPointer());
334 /* Return child item's accessibility interface: */
335 return QAccessible::queryAccessibleInterface(pItem);
336}
337
338int QIAccessibilityInterfaceForQITreeView::indexOfChild(const QAccessibleInterface *pChild) const
339{
340 /* Search for corresponding child: */
341 for (int i = 0; i < childCount(); ++i)
342 if (child(i) == pChild)
343 return i;
344
345 /* -1 by default: */
346 return -1;
347}
348
349QString QIAccessibilityInterfaceForQITreeView::text(QAccessible::Text /* enmTextRole */) const
350{
351 /* Sanity check: */
352 AssertPtrReturn(tree(), QString());
353
354 /* Return tree whats-this: */
355 return tree()->whatsThis();
356}
357
358
359/*********************************************************************************************************************************
360* Class QITreeViewItem implementation. *
361*********************************************************************************************************************************/
362
363QRect QITreeViewItem::rect() const
364{
365 /* We can only ask the parent-tree for a rectangle: */
366 if (parentTree())
367 {
368 /* Acquire parent-tree model: */
369 const QAbstractItemModel *pModel = parentTree()->model();
370 AssertPtrReturn(pModel, QRect());
371
372 /* Acquire zero-column rectangle: */
373 QModelIndex itemIndex = modelIndex();
374 QRect rect = parentTree()->visualRect(itemIndex);
375 /* Enumerate all the remaining columns: */
376 for (int i = 1; i < pModel->columnCount(); ++i)
377 {
378 /* Acquire enumerated column rectangle: */
379 QModelIndex itemIndexI = pModel->index(itemIndex.row(), i, itemIndex.parent());
380 QRegion cumulativeRegion(rect);
381 cumulativeRegion += parentTree()->visualRect(itemIndexI);
382 rect = cumulativeRegion.boundingRect();
383 }
384 /* Total rect finally: */
385 return rect;
386 }
387 /* Empty rect by default: */
388 return QRect();
389}
390
391QModelIndex QITreeViewItem::modelIndex() const
392{
393 /* Acquire model: */
394 const QAbstractItemModel *pModel = parentTree()->model();
395 /* Check whether we have proxy model set or source one otherwise: */
396 const QSortFilterProxyModel *pProxyModel = qobject_cast<const QSortFilterProxyModel*>(pModel);
397
398 /* Acquire root model-index: */
399 const QModelIndex rootIndex = parentTree()->rootIndex();
400 /* Acquire source root model-index, which can be the same as root model-index: */
401 const QModelIndex rootSourceModelIndex = pProxyModel ? pProxyModel->mapToSource(rootIndex) : rootIndex;
402
403 /* Check whether we have root model-index here: */
404 if ( rootSourceModelIndex.internalPointer()
405 && rootSourceModelIndex.internalPointer() == this)
406 return rootIndex;
407
408 /* Determine our parent model-index: */
409 const QModelIndex parentIndex = parentItem() ? parentItem()->modelIndex() : rootIndex;
410
411 /* Determine our position inside parent: */
412 int iPositionInParent = -1;
413 for (int i = 0; i < pModel->rowCount(parentIndex); ++i)
414 {
415 /* Acquire child model-index: */
416 const QModelIndex childIndex = pModel->index(i, 0, parentIndex);
417 /* Acquire source child model-index, which can be the same as child model-index: */
418 const QModelIndex childSourceModelIndex = pProxyModel ? pProxyModel->mapToSource(childIndex) : childIndex;
419
420 /* Check whether we have child model-index here: */
421 if ( childSourceModelIndex.internalPointer()
422 && childSourceModelIndex.internalPointer() == this)
423 {
424 iPositionInParent = i;
425 break;
426 }
427 }
428 /* Make sure we found something: */
429 if (iPositionInParent == -1)
430 return QModelIndex();
431
432 /* Return model-index as child of parent model-index: */
433 return pModel->index(iPositionInParent, 0, parentIndex);
434}
435
436
437/*********************************************************************************************************************************
438* Class QITreeView implementation. *
439*********************************************************************************************************************************/
440
441QITreeView::QITreeView(QWidget *pParent)
442 : QTreeView(pParent)
443{
444 /* Prepare all: */
445 prepare();
446}
447
448void QITreeView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
449{
450 /* Notify listeners about it: */
451 emit currentItemChanged(current, previous);
452 /* Call to base-class: */
453 QTreeView::currentChanged(current, previous);
454}
455
456void QITreeView::drawBranches(QPainter *pPainter, const QRect &rect, const QModelIndex &index) const
457{
458 /* Notify listeners about it: */
459 emit drawItemBranches(pPainter, rect, index);
460 /* Call to base-class: */
461 QTreeView::drawBranches(pPainter, rect, index);
462}
463
464void QITreeView::mouseMoveEvent(QMouseEvent *pEvent)
465{
466 /* Reject event initially: */
467 pEvent->setAccepted(false);
468 /* Notify listeners about event allowing them to handle it: */
469 emit mouseMoved(pEvent);
470 /* Call to base-class only if event was not yet accepted: */
471 if (!pEvent->isAccepted())
472 QTreeView::mouseMoveEvent(pEvent);
473}
474
475void QITreeView::mousePressEvent(QMouseEvent *pEvent)
476{
477 /* Reject event initially: */
478 pEvent->setAccepted(false);
479 /* Notify listeners about event allowing them to handle it: */
480 emit mousePressed(pEvent);
481 /* Call to base-class only if event was not yet accepted: */
482 if (!pEvent->isAccepted())
483 QTreeView::mousePressEvent(pEvent);
484}
485
486void QITreeView::mouseReleaseEvent(QMouseEvent *pEvent)
487{
488 /* Reject event initially: */
489 pEvent->setAccepted(false);
490 /* Notify listeners about event allowing them to handle it: */
491 emit mouseReleased(pEvent);
492 /* Call to base-class only if event was not yet accepted: */
493 if (!pEvent->isAccepted())
494 QTreeView::mouseReleaseEvent(pEvent);
495}
496
497void QITreeView::mouseDoubleClickEvent(QMouseEvent *pEvent)
498{
499 /* Reject event initially: */
500 pEvent->setAccepted(false);
501 /* Notify listeners about event allowing them to handle it: */
502 emit mouseDoubleClicked(pEvent);
503 /* Call to base-class only if event was not yet accepted: */
504 if (!pEvent->isAccepted())
505 QTreeView::mouseDoubleClickEvent(pEvent);
506}
507
508void QITreeView::dragEnterEvent(QDragEnterEvent *pEvent)
509{
510 /* Reject event initially: */
511 pEvent->setAccepted(false);
512 /* Notify listeners about event allowing them to handle it: */
513 emit dragEntered(pEvent);
514 /* Call to base-class only if event was not yet accepted: */
515 if (!pEvent->isAccepted())
516 QTreeView::dragEnterEvent(pEvent);
517}
518
519void QITreeView::dragMoveEvent(QDragMoveEvent *pEvent)
520{
521 /* Reject event initially: */
522 pEvent->setAccepted(false);
523 /* Notify listeners about event allowing them to handle it: */
524 emit dragMoved(pEvent);
525 /* Call to base-class only if event was not yet accepted: */
526 if (!pEvent->isAccepted())
527 QTreeView::dragMoveEvent(pEvent);
528}
529
530void QITreeView::dragLeaveEvent(QDragLeaveEvent *pEvent)
531{
532 /* Reject event initially: */
533 pEvent->setAccepted(false);
534 /* Notify listeners about event allowing them to handle it: */
535 emit dragLeft(pEvent);
536 /* Call to base-class only if event was not yet accepted: */
537 if (!pEvent->isAccepted())
538 QTreeView::dragLeaveEvent(pEvent);
539}
540
541void QITreeView::dropEvent(QDropEvent *pEvent)
542{
543 /* Reject event initially: */
544 pEvent->setAccepted(false);
545 /* Notify listeners about event allowing them to handle it: */
546 emit dragDropped(pEvent);
547 /* Call to base-class only if event was not yet accepted: */
548 if (!pEvent->isAccepted())
549 QTreeView::dropEvent(pEvent);
550}
551
552void QITreeView::prepare()
553{
554 /* Install QITreeViewItem accessibility interface factory: */
555 QAccessible::installFactory(QIAccessibilityInterfaceForQITreeViewItem::pFactory);
556 /* Install QITreeView accessibility interface factory: */
557 QAccessible::installFactory(QIAccessibilityInterfaceForQITreeView::pFactory);
558
559 /* Mark header hidden: */
560 setHeaderHidden(true);
561 /* Mark root hidden: */
562 setRootIsDecorated(false);
563}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette