diff options
Diffstat (limited to 'src/gui/accessible')
-rw-r--r-- | src/gui/accessible/accessible.pri | 2 | ||||
-rw-r--r-- | src/gui/accessible/qaccessible.cpp | 153 | ||||
-rw-r--r-- | src/gui/accessible/qaccessible.h | 46 | ||||
-rw-r--r-- | src/gui/accessible/qaccessible2.cpp | 112 | ||||
-rw-r--r-- | src/gui/accessible/qaccessible2.h | 112 | ||||
-rw-r--r-- | src/gui/accessible/qaccessible_mac.mm | 4 | ||||
-rw-r--r-- | src/gui/accessible/qaccessible_unix.cpp | 13 | ||||
-rw-r--r-- | src/gui/accessible/qaccessible_win.cpp | 321 | ||||
-rw-r--r-- | src/gui/accessible/qaccessibleobject.cpp | 6 | ||||
-rw-r--r-- | src/gui/accessible/qaccessiblewidget.cpp | 13 |
10 files changed, 692 insertions, 90 deletions
diff --git a/src/gui/accessible/accessible.pri b/src/gui/accessible/accessible.pri index ad2fb4c..31362ff 100644 --- a/src/gui/accessible/accessible.pri +++ b/src/gui/accessible/accessible.pri @@ -12,7 +12,7 @@ contains(QT_CONFIG, accessibility) { accessible/qaccessiblewidget.cpp \ accessible/qaccessibleplugin.cpp - mac:!embedded { + mac:!embedded:!qpa { HEADERS += accessible/qaccessible_mac_p.h OBJECTIVE_SOURCES += accessible/qaccessible_mac.mm \ accessible/qaccessible_mac_cocoa.mm diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp index aed7b00..10e5785 100644 --- a/src/gui/accessible/qaccessible.cpp +++ b/src/gui/accessible/qaccessible.cpp @@ -212,42 +212,101 @@ QT_BEGIN_NAMESPACE This enum type defines accessible event types. - \value AcceleratorChanged - \value Alert A system alert (e.g., a message from a QMessageBox) - \value ContextHelpEnd Context help (QWhatsThis) for an object is finished. - \value ContextHelpStart Context help (QWhatsThis) for an object is initiated. - \value DefaultActionChanged The default QAccessible::Action for the accessible object changed - \value DescriptionChanged The objects QAccessible::Description changed. - \value DialogEnd A dialog (QDialog) is been hidden - \value DialogStart A dialog (QDialog) has been set visible. - \value DragDropEnd A Drag & Drop operation is about to finished. - \value DragDropStart A Drag & Drop operation is about to be initiated. - \value Focus An object has gained keyboard focus. - \value ForegroundChanged A window has been activated (i.e., a new window has gained focus on the desktop) - \value HelpChanged The QAccessible::Help text property of an object has changed - \value LocationChanged An objects location on the screen changed - \value MenuCommand A menu item is triggered. - \value MenuEnd A menu has been closed (Qt uses PopupMenuEnd for all menus) - \value MenuStart A menu has been opened on the menubar (Qt uses PopupMenuStart for all menus) - \value NameChanged The QAccessible::Name property of an object has changed - \value ObjectCreated A new object is created. - \value ObjectDestroyed An object is deleted. - \value ObjectHide An object is hidden (i.e., with QWidget::hide()). Any children the object that is hidden has do not send this event. - It is not send when an object is hidden as it is being obcured by others. - \value ObjectReorder A layout or item view has added, removed, or moved an object (Qt does not use this event). - \value ObjectShow An object is displayed (i.e., with QWidget::show()). - \value ParentChanged An objects parent object changed. - \value PopupMenuEnd A popup menu has closed. - \value PopupMenuStart A popupmenu has opened. - \value ScrollingEnd A scrollbar scroll operation has ended (the mouse has released the slider handle) - \value ScrollingStart A scrollbar scroll operation is about to start (i.e., the mouse has pressed on the slider handle) - \value Selection The selection has changed in a menu or item view. - \value SelectionAdd An item has been added to the selection in an item view. - \value SelectionRemove An item has been removed from an item view selection. - \value SelectionWithin Several changes to a selection has occurred in an item view. - \value SoundPlayed A sound has been played by an object - \value StateChanged The QAccessible::State of an object has changed. - \value ValueChanged The QAccessible::Value of an object has changed. + \value AcceleratorChanged The keyboard accelerator for an action has been changed. + \value ActionChanged An action has been changed. + \value ActiveDescendantChanged + \value Alert A system alert (e.g., a message from a QMessageBox) + \value AttributeChanged + \value ContextHelpEnd Context help (QWhatsThis) for an object is finished. + \value ContextHelpStart Context help (QWhatsThis) for an object is initiated. + \value DefaultActionChanged The default QAccessible::Action for the accessible + object has changed. + \value DescriptionChanged The object's QAccessible::Description changed. + \value DialogEnd A dialog (QDialog) has been hidden + \value DialogStart A dialog (QDialog) has been set visible. + \value DocumentContentChanged The contents of a text document have changed. + \value DocumentLoadComplete A document has been loaded. + \value DocumentLoadStopped A document load has been stopped. + \value DocumentReload A document reload has been initiated. + \value DragDropEnd A drag and drop operation is about to finished. + \value DragDropStart A drag and drop operation is about to be initiated. + \value Focus An object has gained keyboard focus. + \value ForegroundChanged A window has been activated (i.e., a new window has + gained focus on the desktop). + \value HelpChanged The QAccessible::Help text property of an object has + changed. + \value HyperlinkEndIndexChanged The end position of the display text for a hypertext + link has changed. + \value HyperlinkNumberOfAnchorsChanged The number of anchors in a hypertext link has changed, + perhaps because the display text has been split to + provide more than one link. + \value HyperlinkSelectedLinkChanged The link for the selected hypertext link has changed. + \value HyperlinkStartIndexChanged The start position of the display text for a hypertext + link has changed. + \value HypertextChanged The display text for a hypertext link has changed. + \value HypertextLinkActivated A hypertext link has been activated, perhaps by being + clicked or via a key press. + \value HypertextLinkSelected A hypertext link has been selected. + \value HypertextNLinksChanged + \value LocationChanged An object's location on the screen has changed. + \value MenuCommand A menu item is triggered. + \value MenuEnd A menu has been closed (Qt uses PopupMenuEnd for all + menus). + \value MenuStart A menu has been opened on the menubar (Qt uses + PopupMenuStart for all menus). + \value NameChanged The QAccessible::Name property of an object has changed. + \value ObjectAttributeChanged + \value ObjectCreated A new object is created. + \value ObjectDestroyed An object is deleted. + \value ObjectHide An object is hidden; for example, with QWidget::hide(). + Any children the object that is hidden has do not send + this event. It is not sent when an object is hidden as + it is being obcured by others. + \value ObjectReorder A layout or item view has added, removed, or moved an + object (Qt does not use this event). + \value ObjectShow An object is displayed; for example, with + QWidget::show(). + \value PageChanged + \value ParentChanged An object's parent object changed. + \value PopupMenuEnd A pop-up menu has closed. + \value PopupMenuStart A pop-up menu has opened. + \value ScrollingEnd A scrollbar scroll operation has ended (the mouse has + released the slider handle). + \value ScrollingStart A scrollbar scroll operation is about to start; this may + be caused by a mouse press on the slider handle, for + example. + \value SectionChanged + \value SelectionAdd An item has been added to the selection in an item view. + \value SelectionRemove An item has been removed from an item view selection. + \value Selection The selection has changed in a menu or item view. + \value SelectionWithin Several changes to a selection has occurred in an item + view. + \value SoundPlayed A sound has been played by an object + \value StateChanged The QAccessible::State of an object has changed. + \value TableCaptionChanged A table caption has been changed. + \value TableColumnDescriptionChanged The description of a table column, typically found in + the column's header, has been changed. + \value TableColumnHeaderChanged A table column header has been changed. + \value TableModelChanged The model providing data for a table has been changed. + \value TableRowDescriptionChanged The description of a table row, typically found in the + row's header, has been changed. + \value TableRowHeaderChanged A table row header has been changed. + \value TableSummaryChanged The summary of a table has been changed. + \value TextAttributeChanged + \value TextCaretMoved The caret has moved in an editable widget. + The caret represents the cursor position in an editable + widget with the input focus. + \value TextColumnChanged A text column has been changed. + \value TextInserted Text has been inserted into an editable widget. + \value TextRemoved Text has been removed from an editable widget. + \value TextSelectionChanged The selected text has changed in an editable widget. + \value TextUpdated The text has been update in an editable widget. + \value ValueChanged The QAccessible::Value of an object has changed. + \value VisibleDataChanged + + The values for this enum are defined to be the same as those defined in the + \l{AccessibleEventID.idl File Reference}{IAccessible2} and + \l{Microsoft Active Accessibility Event Constants}{MSAA} specifications. */ /*! @@ -409,13 +468,18 @@ static void qAccessibleCleanup() /*! \typedef QAccessible::InterfaceFactory - A function pointer type. Use a function with this prototype to install - interface factories with installFactory(). + This is a typedef for a pointer to a function with the following + signature: - The function receives a QObject pointer. If the QObject - provides a QAccessibleInterface, it sets the second parameter to - point to the corresponding QAccessibleInterface, and returns true; - otherwise returns false. + \snippet doc/src/snippets/code/src_gui_accessible_qaccessible.cpp 1 + + The function receives a QString and a QObject pointer, where the + QString is the key identifying the interface. The QObject is used + to pass on to the QAccessibleInterface so that it can hold a reference + to it. + + If the key and the QObject does not have a corresponding + QAccessibleInterface, a null-pointer will be returned. Installed factories are called by queryAccessibilityInterface() until one provides an interface. @@ -984,6 +1048,11 @@ const QAccessibleInterface *other, int otherChild) const */ /*! + \fn QAccessibleTable2Interface *QAccessibleInterface::table2Interface() + \internal +*/ + +/*! \fn QAccessibleActionInterface *QAccessibleInterface::actionInterface() \internal */ diff --git a/src/gui/accessible/qaccessible.h b/src/gui/accessible/qaccessible.h index bc8a851..24a6744 100644 --- a/src/gui/accessible/qaccessible.h +++ b/src/gui/accessible/qaccessible.h @@ -83,6 +83,42 @@ public: MenuCommand = 0x0018, + // Values from IAccessible2 + ActionChanged = 0x0101, + ActiveDescendantChanged = 0x0102, + AttributeChanged = 0x0103, + DocumentContentChanged = 0x0104, + DocumentLoadComplete = 0x0105, + DocumentLoadStopped = 0x0106, + DocumentReload = 0x0107, + HyperlinkEndIndexChanged = 0x0108, + HyperlinkNumberOfAnchorsChanged = 0x0109, + HyperlinkSelectedLinkChanged = 0x010A, + HypertextLinkActivated = 0x010B, + HypertextLinkSelected = 0x010C, + HyperlinkStartIndexChanged = 0x010D, + HypertextChanged = 0x010E, + HypertextNLinksChanged = 0x010F, + ObjectAttributeChanged = 0x0110, + PageChanged = 0x0111, + SectionChanged = 0x0112, + TableCaptionChanged = 0x0113, + TableColumnDescriptionChanged = 0x0114, + TableColumnHeaderChanged = 0x0115, + TableModelChanged = 0x0116, + TableRowDescriptionChanged = 0x0117, + TableRowHeaderChanged = 0x0118, + TableSummaryChanged = 0x0119, + TextAttributeChanged = 0x011A, + TextCaretMoved = 0x011B, + // TextChanged = 0x011C, is deprecated in IA2, use TextUpdated + TextColumnChanged = 0x011D, + TextInserted = 0x011E, + TextRemoved = 0x011F, + TextUpdated = 0x0120, + TextSelectionChanged = 0x0121, + VisibleDataChanged = 0x0122, + ObjectCreated = 0x8000, ObjectDestroyed = 0x8001, ObjectShow = 0x8002, @@ -115,6 +151,7 @@ public: ReadOnly = 0x00000040, HotTracked = 0x00000080, DefaultButton = 0x00000100, + // #### Qt5 Expandable Expanded = 0x00000200, Collapsed = 0x00000400, Busy = 0x00000800, @@ -142,6 +179,8 @@ public: HasPopup = 0x40000000, Modal = 0x80000000, + // #### Qt5 ManagesDescendants + // #### Qt5 remove HasInvokeExtension HasInvokeExtension = 0x10000000 // internal }; Q_DECLARE_FLAGS(State, StateFlag) @@ -312,7 +351,8 @@ namespace QAccessible2 ValueInterface, TableInterface, ActionInterface, - ImageInterface + ImageInterface, + Table2Interface }; } @@ -323,6 +363,7 @@ class QAccessibleValueInterface; class QAccessibleTableInterface; class QAccessibleActionInterface; class QAccessibleImageInterface; +class QAccessibleTable2Interface; class Q_GUI_EXPORT QAccessibleInterface : public QAccessible { @@ -386,6 +427,9 @@ public: inline QAccessibleImageInterface *imageInterface() { return reinterpret_cast<QAccessibleImageInterface *>(cast_helper(QAccessible2::ImageInterface)); } + inline QAccessibleTable2Interface *table2Interface() + { return reinterpret_cast<QAccessibleTable2Interface *>(cast_helper(QAccessible2::Table2Interface)); } + private: QAccessible2Interface *cast_helper(QAccessible2::InterfaceType); }; diff --git a/src/gui/accessible/qaccessible2.cpp b/src/gui/accessible/qaccessible2.cpp index 35b24f6..078ff13 100644 --- a/src/gui/accessible/qaccessible2.cpp +++ b/src/gui/accessible/qaccessible2.cpp @@ -42,6 +42,7 @@ #include "qaccessible2.h" #include "qapplication.h" #include "qclipboard.h" +#include "qtextboundaryfinder.h" #ifndef QT_NO_ACCESSIBILITY @@ -132,6 +133,117 @@ QT_BEGIN_NAMESPACE \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink */ + +/*! + \internal +*/ +QString Q_GUI_EXPORT qTextBeforeOffsetFromString(int offset, QAccessible2::BoundaryType boundaryType, + int *startOffset, int *endOffset, const QString& text) +{ + QTextBoundaryFinder::BoundaryType type; + switch (boundaryType) { + case QAccessible2::CharBoundary: + type = QTextBoundaryFinder::Grapheme; + break; + case QAccessible2::WordBoundary: + type = QTextBoundaryFinder::Word; + break; + case QAccessible2::SentenceBoundary: + type = QTextBoundaryFinder::Sentence; + break; + default: + // in any other case return the whole line + *startOffset = 0; + *endOffset = text.length(); + return text; + } + + QTextBoundaryFinder boundary(type, text); + boundary.setPosition(offset); + + if (!boundary.isAtBoundary()) { + boundary.toPreviousBoundary(); + } + boundary.toPreviousBoundary(); + *startOffset = boundary.position(); + boundary.toNextBoundary(); + *endOffset = boundary.position(); + + return text.mid(*startOffset, *endOffset - *startOffset); +} + +/*! + \internal +*/ +QString Q_GUI_EXPORT qTextAfterOffsetFromString(int offset, QAccessible2::BoundaryType boundaryType, + int *startOffset, int *endOffset, const QString& text) +{ + QTextBoundaryFinder::BoundaryType type; + switch (boundaryType) { + case QAccessible2::CharBoundary: + type = QTextBoundaryFinder::Grapheme; + break; + case QAccessible2::WordBoundary: + type = QTextBoundaryFinder::Word; + break; + case QAccessible2::SentenceBoundary: + type = QTextBoundaryFinder::Sentence; + break; + default: + // in any other case return the whole line + *startOffset = 0; + *endOffset = text.length(); + return text; + } + + QTextBoundaryFinder boundary(type, text); + boundary.setPosition(offset); + + boundary.toNextBoundary(); + *startOffset = boundary.position(); + boundary.toNextBoundary(); + *endOffset = boundary.position(); + + return text.mid(*startOffset, *endOffset - *startOffset); +} + +/*! + \internal +*/ +QString Q_GUI_EXPORT qTextAtOffsetFromString(int offset, QAccessible2::BoundaryType boundaryType, + int *startOffset, int *endOffset, const QString& text) +{ + QTextBoundaryFinder::BoundaryType type; + switch (boundaryType) { + case QAccessible2::CharBoundary: + type = QTextBoundaryFinder::Grapheme; + break; + case QAccessible2::WordBoundary: + type = QTextBoundaryFinder::Word; + break; + case QAccessible2::SentenceBoundary: + type = QTextBoundaryFinder::Sentence; + break; + default: + // in any other case return the whole line + *startOffset = 0; + *endOffset = text.length(); + return text; + } + + QTextBoundaryFinder boundary(type, text); + boundary.setPosition(offset); + + if (!boundary.isAtBoundary()) { + boundary.toPreviousBoundary(); + } + *startOffset = boundary.position(); + boundary.toNextBoundary(); + *endOffset = boundary.position(); + + return text.mid(*startOffset, *endOffset - *startOffset); +} + QAccessibleSimpleEditableTextInterface::QAccessibleSimpleEditableTextInterface( QAccessibleInterface *accessibleInterface) : iface(accessibleInterface) diff --git a/src/gui/accessible/qaccessible2.h b/src/gui/accessible/qaccessible2.h index 5cb0323..106b69e 100644 --- a/src/gui/accessible/qaccessible2.h +++ b/src/gui/accessible/qaccessible2.h @@ -52,6 +52,8 @@ QT_MODULE(Gui) #ifndef QT_NO_ACCESSIBILITY +class QModelIndex; + namespace QAccessible2 { enum CoordinateType @@ -68,6 +70,24 @@ namespace QAccessible2 LineBoundary, NoBoundary }; + + enum TableModelChangeType { + TableModelChangeInsert, + TableModelChangeDelete, + TableModelChangeUpdate + }; + + struct TableModelChange { + int firstColumn; + int firstRow; + int lastColumn; + int lastRow; + TableModelChangeType type; + + TableModelChange() + : firstColumn(0), firstRow(0), lastColumn(0), lastRow(0), type(TableModelChangeUpdate) + {} + }; } class Q_GUI_EXPORT QAccessible2Interface @@ -83,6 +103,7 @@ inline QAccessible2Interface *qAccessibleEditableTextCastHelper() { return 0; } inline QAccessible2Interface *qAccessibleTableCastHelper() { return 0; } inline QAccessible2Interface *qAccessibleActionCastHelper() { return 0; } inline QAccessible2Interface *qAccessibleImageCastHelper() { return 0; } +inline QAccessible2Interface *qAccessibleTable2CastHelper() { return 0; } #define Q_ACCESSIBLE_OBJECT \ public: \ @@ -101,6 +122,8 @@ inline QAccessible2Interface *qAccessibleImageCastHelper() { return 0; } return qAccessibleActionCastHelper(); \ case QAccessible2::ImageInterface: \ return qAccessibleImageCastHelper(); \ + case QAccessible2::Table2Interface: \ + return qAccessibleTable2CastHelper(); \ } \ return 0; \ } \ @@ -214,6 +237,95 @@ public: int *columnSpan, bool *isSelected) = 0; }; +class Q_GUI_EXPORT QAccessibleTable2CellInterface: public QAccessibleInterface +{ +public: + // Returns the number of columns occupied by this cell accessible. + virtual int columnExtent() const = 0; + + // Returns the column headers as an array of cell accessibles. + virtual QList<QAccessibleInterface*> columnHeaderCells() const = 0; + + // Translates this cell accessible into the corresponding column index. + virtual int columnIndex() const = 0; + // Returns the number of rows occupied by this cell accessible. + virtual int rowExtent() const = 0; + // Returns the row headers as an array of cell accessibles. + virtual QList<QAccessibleInterface*> rowHeaderCells() const = 0; + // Translates this cell accessible into the corresponding row index. + virtual int rowIndex() const = 0; + // Returns a boolean value indicating whether this cell is selected. + virtual bool isSelected() const = 0; + + // Gets the row and column indexes and extents of this cell accessible and whether or not it is selected. + virtual void rowColumnExtents(int *row, int *column, int *rowExtents, int *columnExtents, bool *selected) const = 0; + // Returns a reference to the accessbile of the containing table. + virtual QAccessibleTable2Interface* table() const = 0; + + // #### Qt5 this should not be here but part of the state + virtual bool isExpandable() const = 0; +}; + +class Q_GUI_EXPORT QAccessibleTable2Interface: public QAccessible2Interface +{ +public: + inline QAccessible2Interface *qAccessibleTable2CastHelper() { return this; } + + // Returns the cell at the specified row and column in the table. + virtual QAccessibleTable2CellInterface *cellAt (int row, int column) const = 0; + // Returns the caption for the table. + virtual QAccessibleInterface *caption() const = 0; + // Returns the description text of the specified column in the table. + virtual QString columnDescription(int column) const = 0; + // Returns the total number of columns in table. + virtual int columnCount() const = 0; + // Returns the total number of rows in table. + virtual int rowCount() const = 0; + // Returns the total number of selected cells. + virtual int selectedCellCount() const = 0; + // Returns the total number of selected columns. + virtual int selectedColumnCount() const = 0; + // Returns the total number of selected rows. + virtual int selectedRowCount() const = 0; + // Returns the description text of the specified row in the table. + virtual QString rowDescription(int row) const = 0; + // Returns a list of accessibles currently selected. + virtual QList<QAccessibleTable2CellInterface*> selectedCells() const = 0; + // Returns a list of column indexes currently selected (0 based). + virtual QList<int> selectedColumns() const = 0; + // Returns a list of row indexes currently selected (0 based). + virtual QList<int> selectedRows() const = 0; + // Returns the summary description of the table. + virtual QAccessibleInterface *summary() const = 0; + // Returns a boolean value indicating whether the specified column is completely selected. + virtual bool isColumnSelected(int column) const = 0; + // Returns a boolean value indicating whether the specified row is completely selected. + virtual bool isRowSelected(int row) const = 0; + // Selects a row and unselects all previously selected rows. + virtual bool selectRow(int row) = 0; + // Selects a column and unselects all previously selected columns. + virtual bool selectColumn(int column) = 0; + // Unselects one row, leaving other selected rows selected (if any). + virtual bool unselectRow(int row) = 0; + // Unselects one column, leaving other selected columns selected (if any). + virtual bool unselectColumn(int column) = 0; + // Returns the type and extents describing how a table changed. + virtual QAccessible2::TableModelChange modelChange() const = 0; + +protected: + // These functions are called when the model changes. + virtual void modelReset() = 0; + virtual void rowsInserted(const QModelIndex &parent, int first, int last) = 0; + virtual void rowsRemoved(const QModelIndex &parent, int first, int last) = 0; + virtual void columnsInserted(const QModelIndex &parent, int first, int last) = 0; + virtual void columnsRemoved(const QModelIndex &parent, int first, int last) = 0; + virtual void rowsMoved( const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row) = 0; + virtual void columnsMoved( const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column) = 0; + +friend class QAbstractItemView; +friend class QAbstractItemViewPrivate; +}; + class Q_GUI_EXPORT QAccessibleActionInterface : public QAccessible2Interface { public: diff --git a/src/gui/accessible/qaccessible_mac.mm b/src/gui/accessible/qaccessible_mac.mm index c405eee..a250730 100644 --- a/src/gui/accessible/qaccessible_mac.mm +++ b/src/gui/accessible/qaccessible_mac.mm @@ -774,7 +774,7 @@ QAInterface QAccessibleHierarchyManager::lookup(const AXUIElementRef &element) return factory->interface(id); #else return QAInterface(); -#endif; +#endif } QAInterface QAccessibleHierarchyManager::lookup(const QAElement &element) @@ -2427,7 +2427,7 @@ void QAccessible::updateAccessibility(QObject *object, int child, Event reason) } // There is no equivalent Mac notification for ObjectShow/Hide, so we call HIObjectSetAccessibilityIgnored - // and isItIntersting which will mark the HIObject accociated with the element as ignored if the + // and isItInteresting which will mark the HIObject accociated with the element as ignored if the // QAccessible::Invisible state bit is set. QAInterface interface = accessibleHierarchyManager()->lookup(element); if (interface.isValid()) { diff --git a/src/gui/accessible/qaccessible_unix.cpp b/src/gui/accessible/qaccessible_unix.cpp index a6b7ec3..1c1eb2a 100644 --- a/src/gui/accessible/qaccessible_unix.cpp +++ b/src/gui/accessible/qaccessible_unix.cpp @@ -96,13 +96,24 @@ void QAccessible::updateAccessibility(QObject *o, int who, Event reason) } initialize(); - if (bridges()->isEmpty()) + if (!bridges() || bridges()->isEmpty()) return; QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(o); if (!iface) return; + // updates for List/Table/Tree should send child + if (who) { + QAccessibleInterface *child; + iface->navigate(QAccessible::Child, who, &child); + if (child) { + delete iface; + iface = child; + who = 0; + } + } + for (int i = 0; i < bridges()->count(); ++i) bridges()->at(i)->notifyAccessibilityUpdate(reason, iface, who); delete iface; diff --git a/src/gui/accessible/qaccessible_win.cpp b/src/gui/accessible/qaccessible_win.cpp index 3c42864..caabae5 100644 --- a/src/gui/accessible/qaccessible_win.cpp +++ b/src/gui/accessible/qaccessible_win.cpp @@ -47,6 +47,11 @@ #include "qt_windows.h" #include "qwidget.h" #include "qsettings.h" +#include <QtCore/qmap.h> +#include <QtCore/qpair.h> +#include <QtGui/qgraphicsitem.h> +#include <QtGui/qgraphicsscene.h> +#include <QtGui/qgraphicsview.h> #include <winuser.h> #if !defined(WINABLEAPI) @@ -148,6 +153,106 @@ static const char *roleString(QAccessible::Role role) return roles[int(role)]; } +static const char *eventString(QAccessible::Event ev) +{ + static const char *events[] = { + "null", // 0 + "SoundPlayed" /*= 0x0001*/, + "Alert" /*= 0x0002*/, + "ForegroundChanged" /*= 0x0003*/, + "MenuStart" /*= 0x0004*/, + "MenuEnd" /*= 0x0005*/, + "PopupMenuStart" /*= 0x0006*/, + "PopupMenuEnd" /*= 0x0007*/, + "ContextHelpStart" /*= 0x000C*/, // 8 + "ContextHelpEnd" /*= 0x000D*/, + "DragDropStart" /*= 0x000E*/, + "DragDropEnd" /*= 0x000F*/, + "DialogStart" /*= 0x0010*/, + "DialogEnd" /*= 0x0011*/, + "ScrollingStart" /*= 0x0012*/, + "ScrollingEnd" /*= 0x0013*/, + "MenuCommand" /*= 0x0018*/, // 16 + + // Values from IAccessible2 + "ActionChanged" /*= 0x0101*/, // 17 + "ActiveDescendantChanged", + "AttributeChanged", + "DocumentContentChanged", + "DocumentLoadComplete", + "DocumentLoadStopped", + "DocumentReload", + "HyperlinkEndIndexChanged", + "HyperlinkNumberOfAnchorsChanged", + "HyperlinkSelectedLinkChanged", + "HypertextLinkActivated", + "HypertextLinkSelected", + "HyperlinkStartIndexChanged", + "HypertextChanged", + "HypertextNLinksChanged", + "ObjectAttributeChanged", + "PageChanged", + "SectionChanged", + "TableCaptionChanged", + "TableColumnDescriptionChanged", + "TableColumnHeaderChanged", + "TableModelChanged", + "TableRowDescriptionChanged", + "TableRowHeaderChanged", + "TableSummaryChanged", + "TextAttributeChanged", + "TextCaretMoved", + // TextChanged, deprecated, use TextUpdated + //TextColumnChanged = TextCaretMoved + 2, + "TextInserted", + "TextRemoved", + "TextUpdated", + "TextSelectionChanged", + "VisibleDataChanged", /*= 0x0101+32*/ + "ObjectCreated" /*= 0x8000*/, // 49 + "ObjectDestroyed" /*= 0x8001*/, + "ObjectShow" /*= 0x8002*/, + "ObjectHide" /*= 0x8003*/, + "ObjectReorder" /*= 0x8004*/, + "Focus" /*= 0x8005*/, + "Selection" /*= 0x8006*/, + "SelectionAdd" /*= 0x8007*/, + "SelectionRemove" /*= 0x8008*/, + "SelectionWithin" /*= 0x8009*/, + "StateChanged" /*= 0x800A*/, + "LocationChanged" /*= 0x800B*/, + "NameChanged" /*= 0x800C*/, + "DescriptionChanged" /*= 0x800D*/, + "ValueChanged" /*= 0x800E*/, + "ParentChanged" /*= 0x800F*/, + "HelpChanged" /*= 0x80A0*/, + "DefaultActionChanged" /*= 0x80B0*/, + "AcceleratorChanged" /*= 0x80C0*/ + }; + int e = int(ev); + if (e <= 0x80c0) { + const int last = sizeof(events)/sizeof(char*) - 1; + + if (e <= 0x07) + return events[e]; + else if (e <= 0x13) + return events[e - 0x0c + 8]; + else if (e == 0x18) + return events[16]; + else if (e <= 0x0101 + 32) + return events[e - 0x101 + 17]; + else if (e <= 0x800f) + return events[e - 0x8000 + 49]; + else if (e == 0x80a0) + return events[last - 2]; + else if (e == 0x80b0) + return events[last - 1]; + else if (e == 0x80c0) + return events[last]; + } + return "unknown"; +}; + void showDebug(const char* funcName, const QAccessibleInterface *iface) { qDebug() << "Role:" << roleString(iface->role(0)) @@ -159,6 +264,12 @@ void showDebug(const char* funcName, const QAccessibleInterface *iface) # define showDebug(f, iface) #endif +// This stuff is used for widgets/items with no window handle: +typedef QMap<int, QPair<QObject*,int> > NotifyMap; +Q_GLOBAL_STATIC(NotifyMap, qAccessibleRecentSentEvents) +static int eventNum = 0; + + void QAccessible::initialize() { @@ -251,18 +362,35 @@ void QAccessible::updateAccessibility(QObject *o, int who, Event reason) // An event has to be associated with a window, // so find the first parent that is a widget. QWidget *w = 0; - if (o->isWidgetType()) { - w = (QWidget*)o; - } else { - QObject *p = o; - while ((p = p->parent()) != 0) { - if (p->isWidgetType()) { - w = (QWidget*)p; + QObject *p = o; + do { + if (p->isWidgetType()) { + w = static_cast<QWidget*>(p); + if (w->internalWinId()) break; + } + if (QGraphicsObject *gfxObj = qobject_cast<QGraphicsObject*>(p)) { + QGraphicsItem *parentItem = gfxObj->parentItem(); + if (parentItem) { + p = parentItem->toGraphicsObject(); + } else { + QGraphicsView *view = 0; + if (QGraphicsScene *scene = gfxObj->scene()) { + QWidget *fw = QApplication::focusWidget(); + const QList<QGraphicsView*> views = scene->views(); + for (int i = 0 ; i < views.count() && view != fw; ++i) { + view = views.at(i); + } + } + p = view; } + } else { + p = p->parent(); } - } + } while (p); + + //qDebug() << "updateAccessibility(), hwnd:" << w << ", object:" << o << "," << eventString(reason); if (!w) { if (reason != QAccessible::ContextHelpStart && reason != QAccessible::ContextHelpEnd) @@ -282,12 +410,81 @@ void QAccessible::updateAccessibility(QObject *o, int who, Event reason) } } + WId wid = w->internalWinId(); if (reason != MenuCommand) { // MenuCommand is faked - ptrNotifyWinEvent(reason, w->winId(), OBJID_CLIENT, who); + if (w != o) { + // See comment "SENDING EVENTS TO OBJECTS WITH NO WINDOW HANDLE" + eventNum %= 50; //[0..49] + int eventId = - eventNum - 1; + + qAccessibleRecentSentEvents()->insert(eventId, qMakePair(o,who)); + ptrNotifyWinEvent(reason, wid, OBJID_CLIENT, eventId ); + + ++eventNum; + } else { + ptrNotifyWinEvent(reason, wid, OBJID_CLIENT, who); + } } #endif // Q_WS_WINCE } +/* == SENDING EVENTS TO OBJECTS WITH NO WINDOW HANDLE == + + If the user requested to send the event to a widget with no window, + we need to send an event to an object with no hwnd. + The way we do that is to send it to the *first* ancestor widget + with a window. + Then we'll need a way of identifying the child: + We'll just keep a list of the most recent events that we have sent, + where each entry in the list is identified by a negative value + between [-50,-1]. This negative value we will pass on to + NotifyWinEvent() as the child id. When the negative value have + reached -50, it will wrap around to -1. This seems to be enough + + Now, when the client receives that event, he will first call + AccessibleObjectFromEvent() where dwChildID is the special + negative value. AccessibleObjectFromEvent does two steps: + 1. It will first sent a WM_GETOBJECT to the server, asking + for the IAccessible interface for the HWND. + 2. With the IAccessible interface it got hold of it will call + acc_getChild where the child id argument is the special + negative identifier. In our reimplementation of get_accChild + we check for this if the child id is negative. If it is, then + we'll look up in our table for the entry that is associated + with that value. + The entry will then contain a pointer to the QObject /QWidget + that we can use to call queryAccessibleInterface() on. + + + The following figure shows how the interaction between server and + client is in the case when the server is sending an event. + +SERVER (Qt) | CLIENT | +--------------------------------------------+---------------------------------------+ + | +acc->updateAccessibility(obj, childIndex) | + | +recentEvents()->insert(- 1 - eventNum, | + qMakePair(obj, childIndex) | +NotifyWinEvent(hwnd, childId) => | + | AccessibleObjectFromEvent(event, hwnd, OBJID_CLIENT, childId ) + | will do: + <=== 1. send WM_GETOBJECT(hwnd, OBJID_CLIENT) +widget ~= hwnd +iface = queryAccessibleInteface(widget) +(create IAccessible interface wrapper for + iface) + return iface ===> IAccessible* iface; (for hwnd) + | + <=== call iface->get_accChild(childId) +get_accChild() { | + if (varChildID.lVal < 0) { + QPair ref = recentEvents().value(varChildID.lVal); + [...] + } +*/ + + void QAccessible::setRootObject(QObject *o) { if (rootObjectHandler) { @@ -403,6 +600,35 @@ HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Skip(unsigned long celt) return S_OK; } +struct AccessibleElement { + AccessibleElement(int entryId, QAccessibleInterface *accessible) { + if (entryId < 0) { + QPair<QObject*, int> ref = qAccessibleRecentSentEvents()->value(entryId); + iface = QAccessible::queryAccessibleInterface(ref.first); + entry = ref.second; + cleanupInterface = true; + } else { + iface = accessible; + entry = entryId; + cleanupInterface = false; + } + } + + QString text(QAccessible::Text t) const { + return iface ? iface->text(t, entry) : QString(); + } + + ~AccessibleElement() { + if (cleanupInterface) + delete iface; + } + + QAccessibleInterface *iface; + int entry; +private: + bool cleanupInterface; +}; + /* */ class QWindowsAccessible : public IAccessible, IOleWindow, QAccessible @@ -418,15 +644,18 @@ public: delete accessible; } + /* IUnknown */ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); + /* IDispatch */ HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int *); HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int, unsigned long, ITypeInfo **); HRESULT STDMETHODCALLTYPE GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *); HRESULT STDMETHODCALLTYPE Invoke(long, const _GUID &, unsigned long, unsigned short, tagDISPPARAMS *, tagVARIANT *, tagEXCEPINFO *, unsigned int *); + /* IAccessible */ HRESULT STDMETHODCALLTYPE accHitTest(long xLeft, long yTop, VARIANT *pvarID); HRESULT STDMETHODCALLTYPE accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID); HRESULT STDMETHODCALLTYPE accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd); @@ -451,6 +680,7 @@ public: HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT *pvarID); HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT *pvarChildren); + /* IOleWindow */ HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd); HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode); @@ -797,7 +1027,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::accLocation(long *pxLeft, long *py if (!accessible->isValid()) return E_FAIL; - QRect rect = accessible->rect(varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QRect rect = elem.iface ? elem.iface->rect(elem.entry) : QRect(); if (rect.isValid()) { *pxLeft = rect.x(); *pyTop = rect.y(); @@ -896,9 +1127,17 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, I if (varChildID.vt == VT_EMPTY) return E_INVALIDARG; + + int childIndex = varChildID.lVal; QAccessibleInterface *acc = 0; - RelationFlag rel = varChildID.lVal ? Child : Self; - accessible->navigate(rel, varChildID.lVal, &acc); + + AccessibleElement elem(childIndex, accessible); + if (elem.iface) { + RelationFlag rel = elem.entry ? Child : Self; + int index = elem.iface->navigate(rel, elem.entry, &acc); + if (index == -1) + return E_INVALIDARG; + } if (acc) { QWindowsAccessible* wacc = new QWindowsAccessible(acc); @@ -949,7 +1188,9 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::accDoDefaultAction(VARIANT varID) if (!accessible->isValid()) return E_FAIL; - return accessible->doAction(DefaultAction, varID.lVal, QVariantList()) ? S_OK : S_FALSE; + AccessibleElement elem(varID.lVal, accessible); + const bool res = elem.iface ? elem.iface->doAction(DefaultAction, elem.entry, QVariantList()) : false; + return res ? S_OK : S_FALSE; } HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction) @@ -958,7 +1199,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDefaultAction(VARIANT varID if (!accessible->isValid()) return E_FAIL; - QString def = accessible->actionText(DefaultAction, Name, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString def = elem.iface ? elem.iface->actionText(DefaultAction, Name, elem.entry) : QString(); if (def.isEmpty()) { *pszDefaultAction = 0; return S_FALSE; @@ -974,7 +1216,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDescription(VARIANT varID, if (!accessible->isValid()) return E_FAIL; - QString descr = accessible->text(Description, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString descr = elem.text(Description); if (descr.size()) { *pszDescription = QStringToBSTR(descr); return S_OK; @@ -990,7 +1233,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accHelp(VARIANT varID, BSTR *p if (!accessible->isValid()) return E_FAIL; - QString help = accessible->text(Help, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString help = elem.text(Help); if (help.size()) { *pszHelp = QStringToBSTR(help); return S_OK; @@ -1011,7 +1255,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accKeyboardShortcut(VARIANT va if (!accessible->isValid()) return E_FAIL; - QString sc = accessible->text(Accelerator, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString sc = elem.text(Accelerator); if (sc.size()) { *pszKeyboardShortcut = QStringToBSTR(sc); return S_OK; @@ -1027,7 +1272,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accName(VARIANT varID, BSTR* p if (!accessible->isValid()) return E_FAIL; - QString n = accessible->text(Name, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString n = elem.text(Name); if (n.size()) { *pszName = QStringToBSTR(n); return S_OK; @@ -1049,7 +1295,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accRole(VARIANT varID, VARIANT if (!accessible->isValid()) return E_FAIL; - Role role = accessible->role(varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + Role role = elem.iface ? elem.iface->role(elem.entry) : NoRole; if (role != NoRole) { if (role == LayeredPane) role = QAccessible::Pane; @@ -1068,7 +1315,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accState(VARIANT varID, VARIAN return E_FAIL; (*pvarState).vt = VT_I4; - (*pvarState).lVal = accessible->state(varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + (*pvarState).lVal = elem.iface ? elem.iface->state(elem.entry) : 0; return S_OK; } @@ -1078,7 +1326,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accValue(VARIANT varID, BSTR* if (!accessible->isValid()) return E_FAIL; - QString value = accessible->text(Value, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString value = elem.text(Value); if (!value.isNull()) { *pszValue = QStringToBSTR(value); return S_OK; @@ -1102,19 +1351,23 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::accSelect(long flagsSelect, VARIAN bool res = false; - if (flagsSelect & SELFLAG_TAKEFOCUS) - res = accessible->doAction(SetFocus, varID.lVal, QVariantList()); - if (flagsSelect & SELFLAG_TAKESELECTION) { - accessible->doAction(ClearSelection, 0, QVariantList()); - res = accessible->doAction(AddToSelection, varID.lVal, QVariantList()); + AccessibleElement elem(varID.lVal, accessible); + QAccessibleInterface *acc = elem.iface; + if (acc) { + const int entry = elem.entry; + if (flagsSelect & SELFLAG_TAKEFOCUS) + res = acc->doAction(SetFocus, entry, QVariantList()); + if (flagsSelect & SELFLAG_TAKESELECTION) { + acc->doAction(ClearSelection, 0, QVariantList()); //### bug, 0 should be entry?? + res = acc->doAction(AddToSelection, entry, QVariantList()); + } + if (flagsSelect & SELFLAG_EXTENDSELECTION) + res = acc->doAction(ExtendSelection, entry, QVariantList()); + if (flagsSelect & SELFLAG_ADDSELECTION) + res = acc->doAction(AddToSelection, entry, QVariantList()); + if (flagsSelect & SELFLAG_REMOVESELECTION) + res = acc->doAction(RemoveSelection, entry, QVariantList()); } - if (flagsSelect & SELFLAG_EXTENDSELECTION) - res = accessible->doAction(ExtendSelection, varID.lVal, QVariantList()); - if (flagsSelect & SELFLAG_ADDSELECTION) - res = accessible->doAction(AddToSelection, varID.lVal, QVariantList()); - if (flagsSelect & SELFLAG_REMOVESELECTION) - res = accessible->doAction(RemoveSelection, varID.lVal, QVariantList()); - return res ? S_OK : S_FALSE; } @@ -1203,7 +1456,7 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetWindow(HWND *phwnd) if (!o || !o->isWidgetType()) return E_FAIL; - *phwnd = static_cast<QWidget*>(o)->winId(); + *phwnd = static_cast<QWidget*>(o)->effectiveWinId(); return S_OK; } diff --git a/src/gui/accessible/qaccessibleobject.cpp b/src/gui/accessible/qaccessibleobject.cpp index baaf129..20f3a15 100644 --- a/src/gui/accessible/qaccessibleobject.cpp +++ b/src/gui/accessible/qaccessibleobject.cpp @@ -276,7 +276,7 @@ QAccessible::Relation QAccessibleApplication::relationTo(int child, const for (int i = 0; i < tlw.count(); ++i) { QWidget *w = tlw.at(i); - QObjectList cl = qFindChildren<QObject *>(w, QString()); + QObjectList cl = w->findChildren<QObject *>(QString()); if (cl.contains(o)) return Ancestor; } @@ -322,9 +322,7 @@ QString QAccessibleApplication::text(Text t, int) const { switch (t) { case Name: - if (QApplication::activeWindow()) - return QApplication::activeWindow()->windowTitle(); - break; + return QApplication::applicationName(); case Description: return QApplication::applicationFilePath(); default: diff --git a/src/gui/accessible/qaccessiblewidget.cpp b/src/gui/accessible/qaccessiblewidget.cpp index d42826e..6a7d7e9 100644 --- a/src/gui/accessible/qaccessiblewidget.cpp +++ b/src/gui/accessible/qaccessiblewidget.cpp @@ -704,13 +704,16 @@ int QAccessibleWidget::navigate(RelationFlag relation, int entry, int sibCount = pIface->childCount(); QAccessibleInterface *candidate = 0; for (int i = 0; i < sibCount && entry; ++i) { - pIface->navigate(Child, i+1, &candidate); - Q_ASSERT(candidate); - if (candidate->relationTo(0, this, 0) & Label) + const int childId = pIface->navigate(Child, i+1, &candidate); + Q_ASSERT(childId >= 0); + if (childId > 0) + candidate = pIface; + if (candidate->relationTo(childId, this, 0) & Label) --entry; if (!entry) break; - delete candidate; + if (candidate != pIface) + delete candidate; candidate = 0; } if (!candidate) { @@ -1015,7 +1018,7 @@ QVariant QAccessibleWidgetEx::invokeMethodEx(Method method, int child, const QVa case ListSupportedMethods: { QSet<QAccessible::Method> set; set << ListSupportedMethods << ForegroundColor << BackgroundColor; - return qVariantFromValue(set); + return QVariant::fromValue(set); } case ForegroundColor: return widget()->palette().color(widget()->foregroundRole()); |