VirtualBox

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

Last change on this file was 105063, checked in by vboxsync, 3 months ago

FE/Qt: bugref:10681: QITableView: Reworking accessibility interface to support proxy-models.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.1 KB
Line 
1/* $Id: QITableView.cpp 105063 2024-06-27 14:04:43Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - Qt extensions: QITableView class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-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 <QSortFilterProxyModel>
31
32/* GUI includes: */
33#include "QIStyledItemDelegate.h"
34#include "QITableView.h"
35
36/* Other VBox includes: */
37#include "iprt/assert.h"
38
39
40/** QAccessibleObject extension used as an accessibility interface for QITableViewCell. */
41class QIAccessibilityInterfaceForQITableViewCell : 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 QITableViewCell accessibility interface: */
49 if (pObject && strClassname == QLatin1String("QITableViewCell"))
50 return new QIAccessibilityInterfaceForQITableViewCell(pObject);
51
52 /* Null by default: */
53 return 0;
54 }
55
56 /** Constructs an accessibility interface passing @a pObject to the base-class. */
57 QIAccessibilityInterfaceForQITableViewCell(QObject *pObject)
58 : QAccessibleObject(pObject)
59 {}
60
61 /** Returns the parent. */
62 virtual QAccessibleInterface *parent() const RT_OVERRIDE RT_FINAL;
63
64 /** Returns the number of children. */
65 virtual int childCount() const RT_OVERRIDE { return 0; }
66 /** Returns the child with the passed @a iIndex. */
67 virtual QAccessibleInterface *child(int /* iIndex */) const RT_OVERRIDE { return 0; }
68 /** Returns the index of the passed @a pChild. */
69 virtual int indexOfChild(const QAccessibleInterface * /* pChild */) const RT_OVERRIDE { return -1; }
70
71 /** Returns the rect. */
72 virtual QRect rect() const RT_OVERRIDE RT_FINAL;
73 /** Returns a text for the passed @a enmTextRole. */
74 virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE RT_FINAL;
75
76 /** Returns the role. */
77 virtual QAccessible::Role role() const RT_OVERRIDE RT_FINAL;
78 /** Returns the state. */
79 virtual QAccessible::State state() const RT_OVERRIDE RT_FINAL;
80
81private:
82
83 /** Returns corresponding QITableViewCell. */
84 QITableViewCell *cell() const { return qobject_cast<QITableViewCell*>(object()); }
85};
86
87
88/** QAccessibleObject extension used as an accessibility interface for QITableViewRow. */
89class QIAccessibilityInterfaceForQITableViewRow : public QAccessibleObject
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 QITableViewRow accessibility interface: */
97 if (pObject && strClassname == QLatin1String("QITableViewRow"))
98 return new QIAccessibilityInterfaceForQITableViewRow(pObject);
99
100 /* Null by default: */
101 return 0;
102 }
103
104 /** Constructs an accessibility interface passing @a pObject to the base-class. */
105 QIAccessibilityInterfaceForQITableViewRow(QObject *pObject)
106 : QAccessibleObject(pObject)
107 {}
108
109 /** Returns the parent. */
110 virtual QAccessibleInterface *parent() const RT_OVERRIDE RT_FINAL;
111
112 /** Returns the number of children. */
113 virtual int childCount() const RT_OVERRIDE RT_FINAL;
114 /** Returns the child with the passed @a iIndex. */
115 virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE RT_FINAL;
116 /** Returns the index of the passed @a pChild. */
117 virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE RT_FINAL;
118
119 /** Returns the rect. */
120 virtual QRect rect() const RT_OVERRIDE RT_FINAL;
121 /** Returns a text for the passed @a enmTextRole. */
122 virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE RT_FINAL;
123
124 /** Returns the role. */
125 virtual QAccessible::Role role() const RT_OVERRIDE RT_FINAL;
126 /** Returns the state. */
127 virtual QAccessible::State state() const RT_OVERRIDE RT_FINAL;
128
129private:
130
131 /** Returns corresponding QITableViewRow. */
132 QITableViewRow *row() const { return qobject_cast<QITableViewRow*>(object()); }
133};
134
135
136/** QAccessibleWidget extension used as an accessibility interface for QITableView. */
137class QIAccessibilityInterfaceForQITableView : public QAccessibleWidget
138{
139public:
140
141 /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
142 static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
143 {
144 /* Creating QITableView accessibility interface: */
145 if (pObject && strClassname == QLatin1String("QITableView"))
146 return new QIAccessibilityInterfaceForQITableView(qobject_cast<QWidget*>(pObject));
147
148 /* Null by default: */
149 return 0;
150 }
151
152 /** Constructs an accessibility interface passing @a pWidget to the base-class. */
153 QIAccessibilityInterfaceForQITableView(QWidget *pWidget)
154 : QAccessibleWidget(pWidget, QAccessible::List)
155 {}
156
157 /** Returns the number of children. */
158 virtual int childCount() const RT_OVERRIDE RT_FINAL;
159 /** Returns the child with the passed @a iIndex. */
160 virtual QAccessibleInterface *child(int iIndex) const RT_OVERRIDE RT_FINAL;
161 /** Returns the index of the passed @a pChild. */
162 virtual int indexOfChild(const QAccessibleInterface *pChild) const RT_OVERRIDE RT_FINAL;
163
164 /** Returns a text for the passed @a enmTextRole. */
165 virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE RT_FINAL;
166
167private:
168
169 /** Returns corresponding QITableView. */
170 QITableView *table() const { return qobject_cast<QITableView*>(widget()); }
171};
172
173
174/*********************************************************************************************************************************
175* Class QIAccessibilityInterfaceForQITableViewCell implementation. *
176*********************************************************************************************************************************/
177
178QAccessibleInterface *QIAccessibilityInterfaceForQITableViewCell::parent() const
179{
180 /* Make sure cell still alive: */
181 AssertPtrReturn(cell(), 0);
182
183 /* Return the parent: */
184 return QAccessible::queryAccessibleInterface(cell()->row());
185}
186
187QRect QIAccessibilityInterfaceForQITableViewCell::rect() const
188{
189 /* Make sure cell still alive: */
190 AssertPtrReturn(cell(), QRect());
191 AssertPtrReturn(cell()->row(), QRect());
192 AssertPtrReturn(cell()->row()->table(), QRect());
193
194 /* Calculate local item coordinates: */
195 const int iIndexInParent = parent()->indexOfChild(this);
196 const int iParentIndexInParent = parent()->parent()->indexOfChild(parent());
197 const int iX = cell()->row()->table()->columnViewportPosition(iIndexInParent);
198 const int iY = cell()->row()->table()->rowViewportPosition(iParentIndexInParent);
199 const int iWidth = cell()->row()->table()->columnWidth(iIndexInParent);
200 const int iHeight = cell()->row()->table()->rowHeight(iParentIndexInParent);
201
202 /* Map local item coordinates to global: */
203 const QPoint itemPosInScreen = cell()->row()->table()->viewport()->mapToGlobal(QPoint(iX, iY));
204
205 /* Return item rectangle: */
206 return QRect(itemPosInScreen, QSize(iWidth, iHeight));
207}
208
209QString QIAccessibilityInterfaceForQITableViewCell::text(QAccessible::Text enmTextRole) const
210{
211 /* Make sure cell still alive: */
212 AssertPtrReturn(cell(), QString());
213
214 /* Return a text for the passed enmTextRole: */
215 switch (enmTextRole)
216 {
217 case QAccessible::Name: return cell()->text();
218 default: break;
219 }
220
221 /* Null-string by default: */
222 return QString();
223}
224
225QAccessible::Role QIAccessibilityInterfaceForQITableViewCell::role() const
226{
227 /* Cell by default: */
228 return QAccessible::Cell;
229}
230
231QAccessible::State QIAccessibilityInterfaceForQITableViewCell::state() const
232{
233 /* Make sure cell still alive: */
234 AssertPtrReturn(cell(), QAccessible::State());
235
236 /* Empty state by default: */
237 return QAccessible::State();
238}
239
240
241/*********************************************************************************************************************************
242* Class QIAccessibilityInterfaceForQITableViewRow implementation. *
243*********************************************************************************************************************************/
244
245QAccessibleInterface *QIAccessibilityInterfaceForQITableViewRow::parent() const
246{
247 /* Make sure row still alive: */
248 AssertPtrReturn(row(), 0);
249
250 /* Return the parent: */
251 return QAccessible::queryAccessibleInterface(row()->table());
252}
253
254int QIAccessibilityInterfaceForQITableViewRow::childCount() const
255{
256 /* Make sure row still alive: */
257 AssertPtrReturn(row(), 0);
258
259 /* Return the number of children: */
260 return row()->childCount();
261}
262
263QAccessibleInterface *QIAccessibilityInterfaceForQITableViewRow::child(int iIndex) const
264{
265 /* Make sure row still alive: */
266 AssertPtrReturn(row(), 0);
267 /* Make sure index is valid: */
268 AssertReturn(iIndex >= 0 && iIndex < childCount(), 0);
269
270 /* Return the child with the passed iIndex: */
271 return QAccessible::queryAccessibleInterface(row()->childItem(iIndex));
272}
273
274int QIAccessibilityInterfaceForQITableViewRow::indexOfChild(const QAccessibleInterface *pChild) const
275{
276 /* Search for corresponding child: */
277 for (int i = 0; i < childCount(); ++i)
278 if (child(i) == pChild)
279 return i;
280
281 /* -1 by default: */
282 return -1;
283}
284
285QRect QIAccessibilityInterfaceForQITableViewRow::rect() const
286{
287 /* Make sure row still alive: */
288 AssertPtrReturn(row(), QRect());
289 AssertPtrReturn(row()->table(), QRect());
290
291 /* Calculate local item coordinates: */
292 const int iIndexInParent = parent()->indexOfChild(this);
293 const int iX = row()->table()->columnViewportPosition(0);
294 const int iY = row()->table()->rowViewportPosition(iIndexInParent);
295 int iWidth = 0;
296 int iHeight = 0;
297 for (int i = 0; i < childCount(); ++i)
298 iWidth += row()->table()->columnWidth(i);
299 iHeight += row()->table()->rowHeight(iIndexInParent);
300
301 /* Map local item coordinates to global: */
302 const QPoint itemPosInScreen = row()->table()->viewport()->mapToGlobal(QPoint(iX, iY));
303
304 /* Return item rectangle: */
305 return QRect(itemPosInScreen, QSize(iWidth, iHeight));
306}
307
308QString QIAccessibilityInterfaceForQITableViewRow::text(QAccessible::Text enmTextRole) const
309{
310 /* Make sure row still alive: */
311 AssertPtrReturn(row(), QString());
312
313 /* Return a text for the passed enmTextRole: */
314 switch (enmTextRole)
315 {
316 case QAccessible::Name: return childCount() > 0 && child(0) ? child(0)->text(enmTextRole) : QString();
317 default: break;
318 }
319
320 /* Null-string by default: */
321 return QString();
322}
323
324QAccessible::Role QIAccessibilityInterfaceForQITableViewRow::role() const
325{
326 /* Row by default: */
327 return QAccessible::Row;
328}
329
330QAccessible::State QIAccessibilityInterfaceForQITableViewRow::state() const
331{
332 /* Make sure row still alive: */
333 AssertPtrReturn(row(), QAccessible::State());
334
335 /* Empty state by default: */
336 return QAccessible::State();
337}
338
339
340/*********************************************************************************************************************************
341* Class QIAccessibilityInterfaceForQITableView implementation. *
342*********************************************************************************************************************************/
343
344int QIAccessibilityInterfaceForQITableView::childCount() const
345{
346 /* Make sure table still alive: */
347 AssertPtrReturn(table(), 0);
348 /* Make sure model still alive: */
349 AssertPtrReturn(table()->model(), 0);
350
351 /* Return the number of children: */
352 return table()->model()->rowCount();
353}
354
355QAccessibleInterface *QIAccessibilityInterfaceForQITableView::child(int iIndex) const
356{
357 /* Make sure table still alive: */
358 QITableView *pTable = table();
359 AssertPtrReturn(pTable, 0);
360 /* Make sure model still alive: */
361 QAbstractItemModel *pModel = pTable->model();
362 AssertPtrReturn(pModel, 0);
363 /* Make sure index is valid: */
364 AssertReturn(iIndex >= 0, 0);
365
366 /* Real index might be different: */
367 int iRealRowIndex = iIndex;
368
369 // WORKAROUND:
370 // For a table-views Qt accessibility code has a hard-coded architecture which we do not like
371 // but have to live with, this architecture enumerates cells including header column and row,
372 // so Qt can try to address our interface with index which surely out of bounds by our laws.
373 // Let's assume that's exactly the case and try to enumerate cells including header column and row.
374 if (iRealRowIndex >= childCount())
375 {
376 // Split delimeter is overall column count, including vertical header:
377 const int iColumnCount = pModel->columnCount() + 1 /* v_header */;
378 // Real index is zero-based, incoming is 1-based:
379 const int iRealIndex = iIndex - 1;
380 // Real row index, excluding horizontal header:
381 iRealRowIndex = iRealIndex / iColumnCount - 1 /* h_header */;
382 // printf("Invalid index: %d, Actual index: %d\n", iIndex, iRealRowIndex);
383 }
384
385 /* Make sure index fits the bounds finally: */
386 if (iRealRowIndex >= childCount())
387 return 0;
388
389 /* Acquire child-index: */
390 const QModelIndex childIndex = pModel->index(iRealRowIndex, 0);
391 /* Check whether we have proxy model set or source one otherwise: */
392 const QSortFilterProxyModel *pProxyModel = qobject_cast<const QSortFilterProxyModel*>(pModel);
393 /* Acquire source-model child-index (can be the same as original if there is no proxy model): */
394 const QModelIndex sourceChildIndex = pProxyModel ? pProxyModel->mapToSource(childIndex) : childIndex;
395
396 /* Acquire row item: */
397 QITableViewRow *pRow = static_cast<QITableViewRow*>(sourceChildIndex.internalPointer());
398 /* Return row's accessibility interface: */
399 return QAccessible::queryAccessibleInterface(pRow);
400}
401
402int QIAccessibilityInterfaceForQITableView::indexOfChild(const QAccessibleInterface *pChild) const
403{
404 /* Search for corresponding child: */
405 for (int i = 0; i < childCount(); ++i)
406 if (child(i) == pChild)
407 return i;
408
409 /* -1 by default: */
410 return -1;
411}
412
413QString QIAccessibilityInterfaceForQITableView::text(QAccessible::Text /* enmTextRole */) const
414{
415 /* Make sure table still alive: */
416 AssertPtrReturn(table(), QString());
417
418 /* Return table whats-this: */
419 return table()->whatsThis();
420}
421
422
423/*********************************************************************************************************************************
424* Class QITableView implementation. *
425*********************************************************************************************************************************/
426
427QITableView::QITableView(QWidget *pParent)
428 : QTableView(pParent)
429{
430 /* Prepare: */
431 prepare();
432}
433
434QITableView::~QITableView()
435{
436 /* Cleanup: */
437 cleanup();
438}
439
440void QITableView::makeSureEditorDataCommitted()
441{
442 /* Do we have current editor at all? */
443 QObject *pEditorObject = m_editors.value(currentIndex());
444 if (pEditorObject && pEditorObject->isWidgetType())
445 {
446 /* Cast the editor to widget type: */
447 QWidget *pEditor = qobject_cast<QWidget*>(pEditorObject);
448 AssertPtrReturnVoid(pEditor);
449 {
450 /* Commit the editor data and closes it: */
451 commitData(pEditor);
452 closeEditor(pEditor, QAbstractItemDelegate::SubmitModelCache);
453 }
454 }
455}
456
457void QITableView::sltEditorCreated(QWidget *pEditor, const QModelIndex &index)
458{
459 /* Connect created editor to the table and store it: */
460 connect(pEditor, &QWidget::destroyed, this, &QITableView::sltEditorDestroyed);
461 m_editors[index] = pEditor;
462}
463
464void QITableView::sltEditorDestroyed(QObject *pEditor)
465{
466 /* Clear destroyed editor from the table: */
467 const QModelIndex index = m_editors.key(pEditor);
468 AssertReturnVoid(index.isValid());
469 m_editors.remove(index);
470}
471
472void QITableView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
473{
474 /* Notify listeners about index changed: */
475 emit sigCurrentChanged(current, previous);
476 /* Call to base-class: */
477 QTableView::currentChanged(current, previous);
478}
479
480void QITableView::prepare()
481{
482 /* Install QITableViewCell accessibility interface factory: */
483 QAccessible::installFactory(QIAccessibilityInterfaceForQITableViewCell::pFactory);
484 /* Install QITableViewRow accessibility interface factory: */
485 QAccessible::installFactory(QIAccessibilityInterfaceForQITableViewRow::pFactory);
486 /* Install QITableView accessibility interface factory: */
487 QAccessible::installFactory(QIAccessibilityInterfaceForQITableView::pFactory);
488
489 /* Delete old delegate: */
490 delete itemDelegate();
491 /* Create new delegate: */
492 QIStyledItemDelegate *pStyledItemDelegate = new QIStyledItemDelegate(this);
493 AssertPtrReturnVoid(pStyledItemDelegate);
494 {
495 /* Assign newly created delegate to the table: */
496 setItemDelegate(pStyledItemDelegate);
497 /* Connect newly created delegate to the table: */
498 connect(pStyledItemDelegate, &QIStyledItemDelegate::sigEditorCreated,
499 this, &QITableView::sltEditorCreated);
500 }
501}
502
503void QITableView::cleanup()
504{
505 /* Disconnect all the editors prematurelly: */
506 foreach (QObject *pEditor, m_editors.values())
507 disconnect(pEditor, 0, this, 0);
508}
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