/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtGui module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qaccessible.h" | |
#ifndef QT_NO_ACCESSIBILITY | |
#include "qaccessible_mac_p.h" | |
#include "qhash.h" | |
#include "qset.h" | |
#include "qpointer.h" | |
#include "qapplication.h" | |
#include "qmainwindow.h" | |
#include "qtextdocument.h" | |
#include "qdebug.h" | |
#include "qabstractslider.h" | |
#include "qsplitter.h" | |
#include "qtabwidget.h" | |
#include "qlistview.h" | |
#include "qtableview.h" | |
#include "qdockwidget.h" | |
#include <private/qt_mac_p.h> | |
#include <private/qwidget_p.h> | |
#include <CoreFoundation/CoreFoundation.h> | |
QT_BEGIN_NAMESPACE | |
/* | |
Set up platform defines. There is a one-to-one correspondence between the | |
Carbon and Cocoa roles and attributes, but the prefix and type changes. | |
*/ | |
#ifdef QT_MAC_USE_COCOA | |
typedef NSString * const QAXRoleType; | |
#define QAXApplicationRole NSAccessibilityApplicationRole | |
#define QAXButtonRole NSAccessibilityButtonRole | |
#define QAXCancelAction NSAccessibilityCancelAction | |
#define QAXCheckBoxRole NSAccessibilityCheckBoxRole | |
#define QAXChildrenAttribute NSAccessibilityChildrenAttribute | |
#define QAXCloseButtonAttribute NSAccessibilityCloseButtonAttribute | |
#define QAXCloseButtonAttribute NSAccessibilityCloseButtonAttribute | |
#define QAXColumnRole NSAccessibilityColumnRole | |
#define QAXConfirmAction NSAccessibilityConfirmAction | |
#define QAXContentsAttribute NSAccessibilityContentsAttribute | |
#define QAXDecrementAction NSAccessibilityDecrementAction | |
#define QAXDecrementArrowSubrole NSAccessibilityDecrementArrowSubrole | |
#define QAXDecrementPageSubrole NSAccessibilityDecrementPageSubrole | |
#define QAXDescriptionAttribute NSAccessibilityDescriptionAttribute | |
#define QAXEnabledAttribute NSAccessibilityEnabledAttribute | |
#define QAXExpandedAttribute NSAccessibilityExpandedAttribute | |
#define QAXFocusedAttribute NSAccessibilityFocusedAttribute | |
#define QAXFocusedUIElementChangedNotification NSAccessibilityFocusedUIElementChangedNotification | |
#define QAXFocusedWindowChangedNotification NSAccessibilityFocusedWindowChangedNotification | |
#define QAXGroupRole NSAccessibilityGroupRole | |
#define QAXGrowAreaAttribute NSAccessibilityGrowAreaAttribute | |
#define QAXGrowAreaRole NSAccessibilityGrowAreaRole | |
#define QAXHelpAttribute NSAccessibilityHelpAttribute | |
#define QAXHorizontalOrientationValue NSAccessibilityHorizontalOrientationValue | |
#define QAXHorizontalScrollBarAttribute NSAccessibilityHorizontalScrollBarAttribute | |
#define QAXIncrementAction NSAccessibilityIncrementAction | |
#define QAXIncrementArrowSubrole NSAccessibilityIncrementArrowSubrole | |
#define QAXIncrementPageSubrole NSAccessibilityIncrementPageSubrole | |
#define QAXIncrementorRole NSAccessibilityIncrementorRole | |
#define QAXLinkedUIElementsAttribute NSAccessibilityLinkedUIElementsAttribute | |
#define QAXListRole NSAccessibilityListRole | |
#define QAXMainAttribute NSAccessibilityMainAttribute | |
#define QAXMaxValueAttribute NSAccessibilityMaxValueAttribute | |
#define QAXMenuBarRole NSAccessibilityMenuBarRole | |
#define QAXMenuButtonRole NSAccessibilityMenuButtonRole | |
#define QAXMenuClosedNotification NSAccessibilityMenuClosedNotification | |
#define QAXMenuItemRole NSAccessibilityMenuItemRole | |
#define QAXMenuOpenedNotification NSAccessibilityMenuOpenedNotification | |
#define QAXMenuRole NSAccessibilityMenuRole | |
#define QAXMinValueAttribute NSAccessibilityMinValueAttribute | |
#define QAXMinimizeButtonAttribute NSAccessibilityMinimizeButtonAttribute | |
#define QAXMinimizedAttribute NSAccessibilityMinimizedAttribute | |
#define QAXNextContentsAttribute NSAccessibilityNextContentsAttribute | |
#define QAXOrientationAttribute NSAccessibilityOrientationAttribute | |
#define QAXParentAttribute NSAccessibilityParentAttribute | |
#define QAXPickAction NSAccessibilityPickAction | |
#define QAXPopUpButtonRole NSAccessibilityPopUpButtonRole | |
#define QAXPositionAttribute NSAccessibilityPositionAttribute | |
#define QAXPressAction NSAccessibilityPressAction | |
#define QAXPreviousContentsAttribute NSAccessibilityPreviousContentsAttribute | |
#define QAXProgressIndicatorRole NSAccessibilityProgressIndicatorRole | |
#define QAXRadioButtonRole NSAccessibilityRadioButtonRole | |
#define QAXRoleAttribute NSAccessibilityRoleAttribute | |
#define QAXRoleDescriptionAttribute NSAccessibilityRoleDescriptionAttribute | |
#define QAXRowRole NSAccessibilityRowRole | |
#define QAXRowsAttribute NSAccessibilityRowsAttribute | |
#define QAXScrollAreaRole NSAccessibilityScrollAreaRole | |
#define QAXScrollBarRole NSAccessibilityScrollBarRole | |
#define QAXSelectedAttribute NSAccessibilitySelectedAttribute | |
#define QAXSelectedChildrenAttribute NSAccessibilitySelectedChildrenAttribute | |
#define QAXSelectedRowsAttribute NSAccessibilitySelectedRowsAttribute | |
#define QAXSizeAttribute NSAccessibilitySizeAttribute | |
#define QAXSliderRole NSAccessibilitySliderRole | |
#define QAXSplitGroupRole NSAccessibilitySplitGroupRole | |
#define QAXSplitterRole NSAccessibilitySplitterRole | |
#define QAXSplittersAttribute NSAccessibilitySplittersAttribute | |
#define QAXStaticTextRole NSAccessibilityStaticTextRole | |
#define QAXSubroleAttribute NSAccessibilitySubroleAttribute | |
#define QAXSubroleAttribute NSAccessibilitySubroleAttribute | |
#define QAXTabGroupRole NSAccessibilityTabGroupRole | |
#define QAXTableRole NSAccessibilityTableRole | |
#define QAXTabsAttribute NSAccessibilityTabsAttribute | |
#define QAXTextFieldRole NSAccessibilityTextFieldRole | |
#define QAXTitleAttribute NSAccessibilityTitleAttribute | |
#define QAXTitleUIElementAttribute NSAccessibilityTitleUIElementAttribute | |
#define QAXToolbarButtonAttribute NSAccessibilityToolbarButtonAttribute | |
#define QAXToolbarRole NSAccessibilityToolbarRole | |
#define QAXTopLevelUIElementAttribute NSAccessibilityTopLevelUIElementAttribute | |
#define QAXUnknownRole NSAccessibilityUnknownRole | |
#define QAXValueAttribute NSAccessibilityValueAttribute | |
#define QAXValueChangedNotification NSAccessibilityValueChangedNotification | |
#define QAXValueIndicatorRole NSAccessibilityValueIndicatorRole | |
#define QAXVerticalOrientationValue NSAccessibilityVerticalOrientationValue | |
#define QAXVerticalScrollBarAttribute NSAccessibilityVerticalScrollBarAttribute | |
#define QAXVisibleRowsAttribute NSAccessibilityVisibleRowsAttribute | |
#define QAXWindowAttribute NSAccessibilityWindowAttribute | |
#define QAXWindowCreatedNotification NSAccessibilityWindowCreatedNotification | |
#define QAXWindowMovedNotification NSAccessibilityWindowMovedNotification | |
#define QAXWindowRole NSAccessibilityWindowRole | |
#define QAXZoomButtonAttribute NSAccessibilityZoomButtonAttribute | |
#else | |
typedef CFStringRef const QAXRoleType; | |
#define QAXApplicationRole kAXApplicationRole | |
#define QAXButtonRole kAXButtonRole | |
#define QAXCancelAction kAXCancelAction | |
#define QAXCheckBoxRole kAXCheckBoxRole | |
#define QAXChildrenAttribute kAXChildrenAttribute | |
#define QAXCloseButtonAttribute kAXCloseButtonAttribute | |
#define QAXColumnRole kAXColumnRole | |
#define QAXConfirmAction kAXConfirmAction | |
#define QAXContentsAttribute kAXContentsAttribute | |
#define QAXDecrementAction kAXDecrementAction | |
#define QAXDecrementArrowSubrole kAXDecrementArrowSubrole | |
#define QAXDecrementPageSubrole kAXDecrementPageSubrole | |
#define QAXDescriptionAttribute kAXDescriptionAttribute | |
#define QAXEnabledAttribute kAXEnabledAttribute | |
#define QAXExpandedAttribute kAXExpandedAttribute | |
#define QAXFocusedAttribute kAXFocusedAttribute | |
#define QAXFocusedUIElementChangedNotification kAXFocusedUIElementChangedNotification | |
#define QAXFocusedWindowChangedNotification kAXFocusedWindowChangedNotification | |
#define QAXGroupRole kAXGroupRole | |
#define QAXGrowAreaAttribute kAXGrowAreaAttribute | |
#define QAXGrowAreaRole kAXGrowAreaRole | |
#define QAXHelpAttribute kAXHelpAttribute | |
#define QAXHorizontalOrientationValue kAXHorizontalOrientationValue | |
#define QAXHorizontalScrollBarAttribute kAXHorizontalScrollBarAttribute | |
#define QAXIncrementAction kAXIncrementAction | |
#define QAXIncrementArrowSubrole kAXIncrementArrowSubrole | |
#define QAXIncrementPageSubrole kAXIncrementPageSubrole | |
#define QAXIncrementorRole kAXIncrementorRole | |
#define QAXLinkedUIElementsAttribute kAXLinkedUIElementsAttribute | |
#define QAXListRole kAXListRole | |
#define QAXMainAttribute kAXMainAttribute | |
#define QAXMaxValueAttribute kAXMaxValueAttribute | |
#define QAXMenuBarRole kAXMenuBarRole | |
#define QAXMenuButtonRole kAXMenuButtonRole | |
#define QAXMenuClosedNotification kAXMenuClosedNotification | |
#define QAXMenuItemRole kAXMenuItemRole | |
#define QAXMenuOpenedNotification kAXMenuOpenedNotification | |
#define QAXMenuRole kAXMenuRole | |
#define QAXMinValueAttribute kAXMinValueAttribute | |
#define QAXMinimizeButtonAttribute kAXMinimizeButtonAttribute | |
#define QAXMinimizedAttribute kAXMinimizedAttribute | |
#define QAXNextContentsAttribute kAXNextContentsAttribute | |
#define QAXOrientationAttribute kAXOrientationAttribute | |
#define QAXParentAttribute kAXParentAttribute | |
#define QAXPickAction kAXPickAction | |
#define QAXPopUpButtonRole kAXPopUpButtonRole | |
#define QAXPositionAttribute kAXPositionAttribute | |
#define QAXPressAction kAXPressAction | |
#define QAXPreviousContentsAttribute kAXPreviousContentsAttribute | |
#define QAXProgressIndicatorRole kAXProgressIndicatorRole | |
#define QAXRadioButtonRole kAXRadioButtonRole | |
#define QAXRoleAttribute kAXRoleAttribute | |
#define QAXRoleDescriptionAttribute kAXRoleDescriptionAttribute | |
#define QAXRowRole kAXRowRole | |
#define QAXRowsAttribute kAXRowsAttribute | |
#define QAXScrollAreaRole kAXScrollAreaRole | |
#define QAXScrollBarRole kAXScrollBarRole | |
#define QAXSelectedAttribute kAXSelectedAttribute | |
#define QAXSelectedChildrenAttribute kAXSelectedChildrenAttribute | |
#define QAXSelectedRowsAttribute kAXSelectedRowsAttribute | |
#define QAXSizeAttribute kAXSizeAttribute | |
#define QAXSliderRole kAXSliderRole | |
#define QAXSplitGroupRole kAXSplitGroupRole | |
#define QAXSplitterRole kAXSplitterRole | |
#define QAXSplittersAttribute kAXSplittersAttribute | |
#define QAXStaticTextRole kAXStaticTextRole | |
#define QAXSubroleAttribute kAXSubroleAttribute | |
#define QAXTabGroupRole kAXTabGroupRole | |
#define QAXTableRole kAXTableRole | |
#define QAXTabsAttribute kAXTabsAttribute | |
#define QAXTextFieldRole kAXTextFieldRole | |
#define QAXTitleAttribute kAXTitleAttribute | |
#define QAXTitleUIElementAttribute kAXTitleUIElementAttribute | |
#define QAXToolbarButtonAttribute kAXToolbarButtonAttribute | |
#define QAXToolbarRole kAXToolbarRole | |
#define QAXTopLevelUIElementAttribute kAXTopLevelUIElementAttribute | |
#define QAXUnknownRole kAXUnknownRole | |
#define QAXValueAttribute kAXValueAttribute | |
#define QAXValueChangedNotification kAXValueChangedNotification | |
#define QAXValueIndicatorRole kAXValueIndicatorRole | |
#define QAXVerticalOrientationValue kAXVerticalOrientationValue | |
#define QAXVerticalScrollBarAttribute kAXVerticalScrollBarAttribute | |
#define QAXVisibleRowsAttribute kAXVisibleRowsAttribute | |
#define QAXWindowAttribute kAXWindowAttribute | |
#define QAXWindowCreatedNotification kAXWindowCreatedNotification | |
#define QAXWindowMovedNotification kAXWindowMovedNotification | |
#define QAXWindowRole kAXWindowRole | |
#define QAXZoomButtonAttribute kAXZoomButtonAttribute | |
#endif | |
/***************************************************************************** | |
Externals | |
*****************************************************************************/ | |
extern bool qt_mac_is_macsheet(const QWidget *w); //qwidget_mac.cpp | |
extern bool qt_mac_is_macdrawer(const QWidget *w); //qwidget_mac.cpp | |
/***************************************************************************** | |
QAccessible Bindings | |
*****************************************************************************/ | |
//hardcoded bindings between control info and (known) QWidgets | |
struct QAccessibleTextBinding { | |
int qt; | |
QAXRoleType mac; | |
bool settable; | |
} text_bindings[][10] = { | |
{ { QAccessible::MenuItem, QAXMenuItemRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::MenuBar, QAXMenuBarRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::ScrollBar, QAXScrollBarRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Grip, QAXGrowAreaRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Window, QAXWindowRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Dialog, QAXWindowRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::AlertMessage, QAXWindowRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::ToolTip, QAXWindowRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::HelpBalloon, QAXWindowRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::PopupMenu, QAXMenuRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Application, QAXApplicationRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Pane, QAXGroupRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Grouping, QAXGroupRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Separator, QAXSplitterRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::ToolBar, QAXToolbarRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::PageTab, QAXRadioButtonRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::ButtonMenu, QAXMenuButtonRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::ButtonDropDown, QAXPopUpButtonRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::SpinBox, QAXIncrementorRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Slider, QAXSliderRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::ProgressBar, QAXProgressIndicatorRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::ComboBox, QAXPopUpButtonRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::RadioButton, QAXRadioButtonRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::CheckBox, QAXCheckBoxRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::StaticText, QAXStaticTextRole, false }, | |
{ QAccessible::Name, QAXValueAttribute, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Table, QAXTableRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::StatusBar, QAXStaticTextRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Column, QAXColumnRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::ColumnHeader, QAXColumnRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Row, QAXRowRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::RowHeader, QAXRowRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Cell, QAXTextFieldRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::PushButton, QAXButtonRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::EditableText, QAXTextFieldRole, true }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Link, QAXTextFieldRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Indicator, QAXValueIndicatorRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Splitter, QAXSplitGroupRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::List, QAXListRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::ListItem, QAXStaticTextRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { QAccessible::Cell, QAXStaticTextRole, false }, | |
{ -1, 0, false } | |
}, | |
{ { -1, 0, false } } | |
}; | |
class QAInterface; | |
static CFStringRef macRole(const QAInterface &interface); | |
QDebug operator<<(QDebug debug, const QAInterface &interface) | |
{ | |
if (interface.isValid() == false) | |
debug << "invalid interface"; | |
else | |
debug << interface.object() << "id" << interface.id() << "role" << hex << interface.role(); | |
return debug; | |
} | |
// The root of the Qt accessible hiearchy. | |
static QObject *rootObject = 0; | |
bool QAInterface::operator==(const QAInterface &other) const | |
{ | |
if (isValid() == false || other.isValid() == false) | |
return (isValid() && other.isValid()); | |
// walk up the parent chain, comparing child indexes, until we reach | |
// an interface that has a QObject. | |
QAInterface currentThis = *this; | |
QAInterface currentOther = other; | |
while (currentThis.object() == 0) { | |
if (currentOther.object() != 0) | |
return false; | |
// fail if the child indexes in the two hirearchies don't match. | |
if (currentThis.parent().indexOfChild(currentThis) != | |
currentOther.parent().indexOfChild(currentOther)) | |
return false; | |
currentThis = currentThis.parent(); | |
currentOther = currentOther.parent(); | |
} | |
return (currentThis.object() == currentOther.object() && currentThis.id() == currentOther.id()); | |
} | |
bool QAInterface::operator!=(const QAInterface &other) const | |
{ | |
return !operator==(other); | |
} | |
uint qHash(const QAInterface &item) | |
{ | |
if (item.isValid()) | |
return qHash(item.object()) + qHash(item.id()); | |
else | |
return qHash(item.cachedObject()) + qHash(item.id()); | |
} | |
QAInterface QAInterface::navigate(RelationFlag relation, int entry) const | |
{ | |
if (!checkValid()) | |
return QAInterface(); | |
// On a QAccessibleInterface that handles its own children we can short-circut | |
// the navigation if this QAInterface refers to one of the children: | |
if (child != 0) { | |
// The Ancestor interface will always be the same QAccessibleInterface with | |
// a child value of 0. | |
if (relation == QAccessible::Ancestor) | |
return QAInterface(*this, 0); | |
// The child hiearchy is only one level deep, so navigating to a child | |
// of a child is not possible. | |
if (relation == QAccessible::Child) { | |
return QAInterface(); | |
} | |
} | |
QAccessibleInterface *child_iface = 0; | |
const int status = base.interface->navigate(relation, entry, &child_iface); | |
if (status == -1) | |
return QAInterface(); // not found; | |
// Check if target is a child of this interface. | |
if (!child_iface) { | |
return QAInterface(*this, status); | |
} else { | |
// Target is child_iface or a child of that (status decides). | |
return QAInterface(child_iface, status); | |
} | |
} | |
QAElement::QAElement() | |
:elementRef(0) | |
{} | |
QAElement::QAElement(AXUIElementRef elementRef) | |
:elementRef(elementRef) | |
{ | |
if (elementRef != 0) { | |
CFRetain(elementRef); | |
CFRetain(object()); | |
} | |
} | |
QAElement::QAElement(const QAElement &element) | |
:elementRef(element.elementRef) | |
{ | |
if (elementRef != 0) { | |
CFRetain(elementRef); | |
CFRetain(object()); | |
} | |
} | |
QAElement::QAElement(HIObjectRef object, int child) | |
{ | |
#ifndef QT_MAC_USE_COCOA | |
if (object == 0) { | |
elementRef = 0; // Create invalid QAElement. | |
} else { | |
elementRef = AXUIElementCreateWithHIObjectAndIdentifier(object, child); | |
CFRetain(object); | |
} | |
#else | |
Q_UNUSED(object); | |
Q_UNUSED(child); | |
#endif | |
} | |
QAElement::~QAElement() | |
{ | |
if (elementRef != 0) { | |
CFRelease(object()); | |
CFRelease(elementRef); | |
} | |
} | |
void QAElement::operator=(const QAElement &other) | |
{ | |
if (*this == other) | |
return; | |
if (elementRef != 0) { | |
CFRelease(object()); | |
CFRelease(elementRef); | |
} | |
elementRef = other.elementRef; | |
if (elementRef != 0) { | |
CFRetain(elementRef); | |
CFRetain(object()); | |
} | |
} | |
bool QAElement::operator==(const QAElement &other) const | |
{ | |
if (elementRef == 0 || other.elementRef == 0) | |
return (elementRef == other.elementRef); | |
return CFEqual(elementRef, other.elementRef); | |
} | |
uint qHash(QAElement element) | |
{ | |
return qHash(element.object()) + qHash(element.id()); | |
} | |
#ifndef QT_MAC_USE_COCOA | |
static QInterfaceFactory *createFactory(const QAInterface &interface); | |
#endif | |
Q_GLOBAL_STATIC(QAccessibleHierarchyManager, accessibleHierarchyManager); | |
/* | |
Reomves all accessibility info accosiated with the sender object. | |
*/ | |
void QAccessibleHierarchyManager::objectDestroyed(QObject *object) | |
{ | |
HIObjectRef hiObject = qobjectHiobjectHash.value(object); | |
delete qobjectElementHash.value(object); | |
qobjectElementHash.remove(object); | |
hiobjectInterfaceHash.remove(hiObject); | |
} | |
/* | |
Removes all stored items. | |
*/ | |
void QAccessibleHierarchyManager::reset() | |
{ | |
qDeleteAll(qobjectElementHash); | |
qobjectElementHash.clear(); | |
hiobjectInterfaceHash.clear(); | |
qobjectHiobjectHash.clear(); | |
} | |
QAccessibleHierarchyManager *QAccessibleHierarchyManager::instance() | |
{ | |
return accessibleHierarchyManager(); | |
} | |
#ifndef QT_MAC_USE_COCOA | |
static bool isItemView(const QAInterface &interface) | |
{ | |
QObject *object = interface.object(); | |
return (interface.role() == QAccessible::List || interface.role() == QAccessible::Table | |
|| (object && qobject_cast<QAbstractItemView *>(interface.object())) | |
|| (object && object->objectName() == QLatin1String("qt_scrollarea_viewport") | |
&& qobject_cast<QAbstractItemView *>(object->parent()))); | |
} | |
#endif | |
static bool isTabWidget(const QAInterface &interface) | |
{ | |
if (QObject *object = interface.object()) | |
return (object->inherits("QTabWidget") && interface.id() == 0); | |
return false; | |
} | |
static bool isStandaloneTabBar(const QAInterface &interface) | |
{ | |
QObject *object = interface.object(); | |
if (interface.role() == QAccessible::PageTabList && object) | |
return (qobject_cast<QTabWidget *>(object->parent()) == 0); | |
return false; | |
} | |
static bool isEmbeddedTabBar(const QAInterface &interface) | |
{ | |
QObject *object = interface.object(); | |
if (interface.role() == QAccessible::PageTabList && object) | |
return (qobject_cast<QTabWidget *>(object->parent())); | |
return false; | |
} | |
/* | |
Decides if a QAInterface is interesting from an accessibility users point of view. | |
*/ | |
bool isItInteresting(const QAInterface &interface) | |
{ | |
// Mac accessibility does not have an attribute that corresponds to the Invisible/Offscreen | |
// state, so we disable the interface here. | |
const QAccessible::State state = interface.state(); | |
if (state & QAccessible::Invisible || | |
state & QAccessible::Offscreen ) | |
return false; | |
const QAccessible::Role role = interface.role(); | |
if (QObject * const object = interface.object()) { | |
const QString className = QLatin1String(object->metaObject()->className()); | |
// VoiceOver focusing on tool tips can be confusing. The contents of the | |
// tool tip is avalible through the description attribute anyway, so | |
// we disable accessibility for tool tips. | |
if (className == QLatin1String("QTipLabel")) | |
return false; | |
// Hide TabBars that has a QTabWidget parent (the tab widget handles the accessibility) | |
if (isEmbeddedTabBar(interface)) | |
return false; | |
// Hide docked dockwidgets. ### causes infinitie loop in the apple accessibility code. | |
/* if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(object)) { | |
if (dockWidget->isFloating() == false) | |
return false; | |
} | |
*/ | |
} | |
// Client is a generic role returned by plain QWidgets or other | |
// widgets that does not have separate QAccessible interface, such | |
// as the TabWidget. Return false unless macRole gives the interface | |
// a special role. | |
if (role == QAccessible::Client && macRole(interface) == CFStringRef(QAXUnknownRole)) | |
return false; | |
// Some roles are not interesting: | |
if (role == QAccessible::Border || // QFrame | |
role == QAccessible::Application || // We use the system-provided application element. | |
role == QAccessible::MenuItem) // The system also provides the menu items. | |
return false; | |
// It is probably better to access the toolbar buttons directly than having | |
// to navigate through the toolbar. | |
if (role == QAccessible::ToolBar) | |
return false; | |
return true; | |
} | |
QAElement QAccessibleHierarchyManager::registerInterface(QObject *object, int child) | |
{ | |
#ifndef QT_MAC_USE_COCOA | |
return registerInterface(QAInterface(QAccessible::queryAccessibleInterface(object), child)); | |
#else | |
Q_UNUSED(object); | |
Q_UNUSED(child); | |
return QAElement(); | |
#endif | |
} | |
/* | |
Creates a QAXUIelement that corresponds to the given QAInterface. | |
*/ | |
QAElement QAccessibleHierarchyManager::registerInterface(const QAInterface &interface) | |
{ | |
#ifndef QT_MAC_USE_COCOA | |
if (interface.isValid() == false) | |
return QAElement(); | |
QAInterface objectInterface = interface.objectInterface(); | |
QObject * qobject = objectInterface.object(); | |
HIObjectRef hiobject = objectInterface.hiObject(); | |
if (qobject == 0 || hiobject == 0) | |
return QAElement(); | |
if (qobjectElementHash.contains(qobject) == false) { | |
registerInterface(qobject, hiobject, createFactory(interface)); | |
HIObjectSetAccessibilityIgnored(hiobject, !isItInteresting(interface)); | |
} | |
return QAElement(hiobject, interface.id()); | |
#else | |
Q_UNUSED(interface); | |
return QAElement(); | |
#endif | |
} | |
#ifndef QT_MAC_USE_COCOA | |
#include "qaccessible_mac_carbon.cpp" | |
#endif | |
void QAccessibleHierarchyManager::registerInterface(QObject * qobject, HIObjectRef hiobject, QInterfaceFactory *interfaceFactory) | |
{ | |
#ifndef QT_MAC_USE_COCOA | |
if (qobjectElementHash.contains(qobject) == false) { | |
qobjectElementHash.insert(qobject, interfaceFactory); | |
qobjectHiobjectHash.insert(qobject, hiobject); | |
connect(qobject, SIGNAL(destroyed(QObject *)), SLOT(objectDestroyed(QObject *))); | |
} | |
if (hiobjectInterfaceHash.contains(hiobject) == false) { | |
hiobjectInterfaceHash.insert(hiobject, interfaceFactory); | |
installAcessibilityEventHandler(hiobject); | |
} | |
#else | |
Q_UNUSED(qobject); | |
Q_UNUSED(hiobject); | |
Q_UNUSED(interfaceFactory); | |
#endif | |
} | |
void QAccessibleHierarchyManager::registerChildren(const QAInterface &interface) | |
{ | |
QObject * const object = interface.object(); | |
if (object == 0) | |
return; | |
QInterfaceFactory *interfaceFactory = qobjectElementHash.value(object); | |
if (interfaceFactory == 0) | |
return; | |
interfaceFactory->registerChildren(); | |
} | |
QAInterface QAccessibleHierarchyManager::lookup(const AXUIElementRef &element) | |
{ | |
if (element == 0) | |
return QAInterface(); | |
#ifndef QT_MAC_USE_COCOA | |
HIObjectRef hiObject = AXUIElementGetHIObject(element); | |
QInterfaceFactory *factory = hiobjectInterfaceHash.value(hiObject); | |
if (factory == 0) { | |
return QAInterface(); | |
} | |
UInt64 id; | |
AXUIElementGetIdentifier(element, &id); | |
return factory->interface(id); | |
#else | |
return QAInterface(); | |
#endif; | |
} | |
QAInterface QAccessibleHierarchyManager::lookup(const QAElement &element) | |
{ | |
return lookup(element.element()); | |
} | |
QAElement QAccessibleHierarchyManager::lookup(const QAInterface &interface) | |
{ | |
if (interface.isValid() == false) | |
return QAElement(); | |
QInterfaceFactory *factory = qobjectElementHash.value(interface.objectInterface().object()); | |
if (factory == 0) | |
return QAElement(); | |
return factory->element(interface); | |
} | |
QAElement QAccessibleHierarchyManager::lookup(QObject * const object, int id) | |
{ | |
QInterfaceFactory *factory = qobjectElementHash.value(object); | |
if (factory == 0) | |
return QAElement(); | |
return factory->element(id); | |
} | |
/* | |
Standard interface mapping, return the stored interface | |
or HIObjectRef, and there is an one-to-one mapping between | |
the identifier and child. | |
*/ | |
class QStandardInterfaceFactory : public QInterfaceFactory | |
{ | |
public: | |
QStandardInterfaceFactory(const QAInterface &interface) | |
: m_interface(interface), object(interface.hiObject()) | |
{ | |
CFRetain(object); | |
} | |
~QStandardInterfaceFactory() | |
{ | |
CFRelease(object); | |
} | |
QAInterface interface(UInt64 identifier) | |
{ | |
const int child = identifier; | |
return QAInterface(m_interface, child); | |
} | |
QAElement element(int id) | |
{ | |
return QAElement(object, id); | |
} | |
QAElement element(const QAInterface &interface) | |
{ | |
if (interface.object() == 0) | |
return QAElement(); | |
return QAElement(object, interface.id()); | |
} | |
void registerChildren() | |
{ | |
const int childCount = m_interface.childCount(); | |
for (int i = 1; i <= childCount; ++i) { | |
accessibleHierarchyManager()->registerInterface(m_interface.navigate(QAccessible::Child, i)); | |
} | |
} | |
private: | |
QAInterface m_interface; | |
HIObjectRef object; | |
}; | |
/* | |
Interface mapping where that creates one HIObject for each interface child. | |
*/ | |
class QMultipleHIObjectFactory : public QInterfaceFactory | |
{ | |
public: | |
QMultipleHIObjectFactory(const QAInterface &interface) | |
: m_interface(interface) | |
{ } | |
~QMultipleHIObjectFactory() | |
{ | |
foreach (HIObjectRef object, objects) { | |
CFRelease(object); | |
} | |
} | |
QAInterface interface(UInt64 identifier) | |
{ | |
const int child = identifier; | |
return QAInterface(m_interface, child); | |
} | |
QAElement element(int child) | |
{ | |
if (child == 0) | |
return QAElement(m_interface.hiObject(), 0); | |
if (child > objects.count()) | |
return QAElement(); | |
return QAElement(objects.at(child - 1), child); | |
} | |
void registerChildren() | |
{ | |
#ifndef QT_MAC_USE_COCOA | |
const int childCount = m_interface.childCount(); | |
for (int i = 1; i <= childCount; ++i) { | |
HIObjectRef hiobject; | |
HIObjectCreate(kObjectQtAccessibility, 0, &hiobject); | |
objects.append(hiobject); | |
accessibleHierarchyManager()->registerInterface(m_interface.object(), hiobject, this); | |
HIObjectSetAccessibilityIgnored(hiobject, !isItInteresting(m_interface.navigate(QAccessible::Child, i))); | |
} | |
#endif | |
} | |
private: | |
QAInterface m_interface; | |
QList<HIObjectRef> objects; | |
}; | |
class QItemViewInterfaceFactory : public QInterfaceFactory | |
{ | |
public: | |
QItemViewInterfaceFactory(const QAInterface &interface) | |
: m_interface(interface), object(interface.hiObject()) | |
{ | |
CFRetain(object); | |
columnCount = 0; | |
if (QTableView * tableView = qobject_cast<QTableView *>(interface.parent().object())) { | |
if (tableView->model()) | |
columnCount = tableView->model()->columnCount(); | |
if (tableView->verticalHeader()) | |
++columnCount; | |
} | |
} | |
~QItemViewInterfaceFactory() | |
{ | |
CFRelease(object); | |
} | |
QAInterface interface(UInt64 identifier) | |
{ | |
if (identifier == 0) | |
return m_interface; | |
if (m_interface.role() == QAccessible::List) | |
return m_interface.childAt(identifier); | |
if (m_interface.role() == QAccessible::Table) { | |
const int index = identifier; | |
if (index == 0) | |
return m_interface; // return the item view interface. | |
const int rowIndex = (index - 1) / (columnCount + 1); | |
const int cellIndex = (index - 1) % (columnCount + 1); | |
/* | |
qDebug() << "index" << index; | |
qDebug() << "rowIndex" << rowIndex; | |
qDebug() << "cellIndex" << cellIndex; | |
*/ | |
const QAInterface rowInterface = m_interface.childAt(rowIndex + 1); | |
if ((cellIndex) == 0) // Is it a row? | |
return rowInterface; | |
else { | |
return rowInterface.childAt(cellIndex); | |
} | |
} | |
return QAInterface(); | |
} | |
QAElement element(int id) | |
{ | |
if (id != 0) { | |
return QAElement(); | |
} | |
return QAElement(object, 0); | |
} | |
QAElement element(const QAInterface &interface) | |
{ | |
if (interface.object() && interface.object() == m_interface.object()) { | |
return QAElement(object, 0); | |
} else if (m_interface.role() == QAccessible::List) { | |
if (interface.parent().object() && interface.parent().object() == m_interface.object()) | |
return QAElement(object, m_interface.indexOfChild(interface)); | |
} else if (m_interface.role() == QAccessible::Table) { | |
QAInterface currentInterface = interface; | |
int index = 0; | |
while (currentInterface.isValid() && currentInterface.object() == 0) { | |
const QAInterface parentInterface = currentInterface.parent(); | |
/* | |
qDebug() << "current index" << index; | |
qDebug() << "current interface" << interface; | |
qDebug() << "parent interface" << parentInterface; | |
qDebug() << "grandparent interface" << parentInterface.parent(); | |
qDebug() << "childCount" << interface.childCount(); | |
qDebug() << "index of child" << parentInterface.indexOfChild(currentInterface); | |
*/ | |
index += ((parentInterface.indexOfChild(currentInterface) - 1) * (currentInterface.childCount() + 1)) + 1; | |
currentInterface = parentInterface; | |
// qDebug() << "new current interface" << currentInterface; | |
} | |
if (currentInterface.object() == m_interface.object()) | |
return QAElement(object, index); | |
} | |
return QAElement(); | |
} | |
void registerChildren() | |
{ | |
// Item view child interfraces don't have their own qobjects, so there is nothing to register here. | |
} | |
private: | |
QAInterface m_interface; | |
HIObjectRef object; | |
int columnCount; // for table views; | |
}; | |
#ifndef QT_MAC_USE_COCOA | |
static bool managesChildren(const QAInterface &interface) | |
{ | |
return (interface.childCount() > 0 && interface.childAt(1).id() > 0); | |
} | |
static QInterfaceFactory *createFactory(const QAInterface &interface) | |
{ | |
if (isItemView(interface)) { | |
return new QItemViewInterfaceFactory(interface); | |
} if (managesChildren(interface)) { | |
return new QMultipleHIObjectFactory(interface); | |
} | |
return new QStandardInterfaceFactory(interface); | |
} | |
#endif | |
QList<QAElement> lookup(const QList<QAInterface> &interfaces) | |
{ | |
QList<QAElement> elements; | |
foreach (const QAInterface &interface, interfaces) | |
if (interface.isValid()) { | |
const QAElement element = accessibleHierarchyManager()->lookup(interface); | |
if (element.isValid()) | |
elements.append(element); | |
} | |
return elements; | |
} | |
// Debug output helpers: | |
/* | |
static QString nameForEventKind(UInt32 kind) | |
{ | |
switch(kind) { | |
case kEventAccessibleGetChildAtPoint: return QString("GetChildAtPoint"); break; | |
case kEventAccessibleGetAllAttributeNames: return QString("GetAllAttributeNames"); break; | |
case kEventAccessibleGetNamedAttribute: return QString("GetNamedAttribute"); break; | |
case kEventAccessibleSetNamedAttribute: return QString("SetNamedAttribute"); break; | |
case kEventAccessibleGetAllActionNames: return QString("GetAllActionNames"); break; | |
case kEventAccessibleGetFocusedChild: return QString("GetFocusedChild"); break; | |
default: | |
return QString("Unknown accessibility event type: %1").arg(kind); | |
break; | |
}; | |
} | |
*/ | |
#ifndef QT_MAC_USE_COCOA | |
static bool qt_mac_append_cf_uniq(CFMutableArrayRef array, CFTypeRef value) | |
{ | |
if (value == 0) | |
return false; | |
CFRange range; | |
range.location = 0; | |
range.length = CFArrayGetCount(array); | |
if(!CFArrayContainsValue(array, range, value)) { | |
CFArrayAppendValue(array, value); | |
return true; | |
} | |
return false; | |
} | |
static OSStatus setAttributeValue(EventRef event, const QList<QAElement> &elements) | |
{ | |
CFMutableArrayRef array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); | |
foreach (const QAElement &element, elements) { | |
if (element.isValid()) | |
CFArrayAppendValue(array, element.element()); | |
} | |
const OSStatus err = SetEventParameter(event, kEventParamAccessibleAttributeValue, | |
typeCFTypeRef, sizeof(array), &array); | |
CFRelease(array); | |
return err; | |
} | |
#endif //QT_MAC_USE_COCOA | |
/* | |
Gets the AccessibleObject parameter from an event. | |
*/ | |
static inline AXUIElementRef getAccessibleObjectParameter(EventRef event) | |
{ | |
AXUIElementRef element; | |
GetEventParameter(event, kEventParamAccessibleObject, typeCFTypeRef, 0, | |
sizeof(element), 0, &element); | |
return element; | |
} | |
/* | |
The application event handler makes sure that all top-level qt windows are registered | |
before any accessibility events are handeled. | |
*/ | |
#ifndef QT_MAC_USE_COCOA | |
static OSStatus applicationEventHandler(EventHandlerCallRef next_ref, EventRef event, void *) | |
{ | |
QAInterface rootInterface(QAccessible::queryAccessibleInterface(rootObject ? rootObject : qApp), 0); | |
accessibleHierarchyManager()->registerChildren(rootInterface); | |
return CallNextEventHandler(next_ref, event); | |
} | |
/* | |
Returns the value for element by combining the QAccessibility::Checked and | |
QAccessibility::Mixed flags into an int value that the Mac accessibilty | |
system understands. This works for check boxes, radio buttons, and the like. | |
The return values are: | |
0: unchecked | |
1: checked | |
2: undecided | |
*/ | |
static int buttonValue(QAInterface element) | |
{ | |
const QAccessible::State state = element.state(); | |
if (state & QAccessible::Mixed) | |
return 2; | |
else if(state & QAccessible::Checked) | |
return 1; | |
else | |
return 0; | |
} | |
static QString getValue(const QAInterface &interface) | |
{ | |
const QAccessible::Role role = interface.role(); | |
if (role == QAccessible::RadioButton || role == QAccessible::CheckBox) | |
return QString::number(buttonValue(interface)); | |
else | |
return interface.text(QAccessible::Value); | |
} | |
#endif //QT_MAC_USE_COCOA | |
/* | |
Translates a QAccessible::Role into a mac accessibility role. | |
*/ | |
static CFStringRef macRole(const QAInterface &interface) | |
{ | |
const QAccessible::Role qtRole = interface.role(); | |
// qDebug() << "role for" << interface.object() << "interface role" << hex << qtRole; | |
// Qt accessibility: QAccessible::Splitter contains QAccessible::Grip. | |
// Mac accessibility: AXSplitGroup contains AXSplitter. | |
if (qtRole == QAccessible::Grip) { | |
const QAInterface parent = interface.parent(); | |
if (parent.isValid() && parent.role() == QAccessible::Splitter) | |
return CFStringRef(QAXSplitterRole); | |
} | |
// Tab widgets and standalone tab bars get the kAXTabGroupRole. Accessibility | |
// for tab bars emebedded in a tab widget is handled by the tab widget. | |
if (isTabWidget(interface) || isStandaloneTabBar(interface)) | |
return kAXTabGroupRole; | |
if (QObject *object = interface.object()) { | |
// ### The interface for an abstract scroll area returns the generic "Client" | |
// role, so we have to to an extra detect on the QObject here. | |
if (object->inherits("QAbstractScrollArea") && interface.id() == 0) | |
return CFStringRef(QAXScrollAreaRole); | |
if (object->inherits("QDockWidget")) | |
return CFStringRef(QAXUnknownRole); | |
} | |
int i = 0; | |
int testRole = text_bindings[i][0].qt; | |
while (testRole != -1) { | |
if (testRole == qtRole) | |
return CFStringRef(text_bindings[i][0].mac); | |
++i; | |
testRole = text_bindings[i][0].qt; | |
} | |
// qDebug() << "got unknown role!" << interface << interface.parent(); | |
return CFStringRef(QAXUnknownRole); | |
} | |
/* | |
Translates a QAccessible::Role and an attribute name into a QAccessible::Text, taking into | |
account execptions listed in text_bindings. | |
*/ | |
#ifndef QT_MAC_USE_COCOA | |
static int textForRoleAndAttribute(QAccessible::Role role, CFStringRef attribute) | |
{ | |
// Search for exception, return it if found. | |
int testRole = text_bindings[0][0].qt; | |
int i = 0; | |
while (testRole != -1) { | |
if (testRole == role) { | |
int j = 1; | |
int qtRole = text_bindings[i][j].qt; | |
CFStringRef testAttribute = CFStringRef(text_bindings[i][j].mac); | |
while (qtRole != -1) { | |
if (CFStringCompare(attribute, testAttribute, 0) == kCFCompareEqualTo) { | |
return (QAccessible::Text)qtRole; | |
} | |
++j; | |
testAttribute = CFStringRef(text_bindings[i][j].mac); /// ### custom compare | |
qtRole = text_bindings[i][j].qt; /// ### custom compare | |
} | |
break; | |
} | |
++i; | |
testRole = text_bindings[i][0].qt; | |
} | |
// Return default mappping | |
if (CFStringCompare(attribute, CFStringRef(QAXTitleAttribute), 0) == kCFCompareEqualTo) | |
return QAccessible::Name; | |
else if (CFStringCompare(attribute, CFStringRef(QAXValueAttribute), 0) == kCFCompareEqualTo) | |
return QAccessible::Value; | |
else if (CFStringCompare(attribute, CFStringRef(QAXHelpAttribute), 0) == kCFCompareEqualTo) | |
return QAccessible::Help; | |
else if (CFStringCompare(attribute, CFStringRef(QAXDescriptionAttribute), 0) == kCFCompareEqualTo) | |
return QAccessible::Description; | |
else | |
return -1; | |
} | |
/* | |
Returns the subrole string constant for the interface if it has one, | |
else returns an empty string. | |
*/ | |
static QCFString subrole(const QAInterface &interface) | |
{ | |
const QAInterface parent = interface.parent(); | |
if (parent.isValid() == false) | |
return QCFString(); | |
if (parent.role() == QAccessible::ScrollBar) { | |
QCFString subrole; | |
switch(interface.id()) { | |
case 1: subrole = CFStringRef(QAXDecrementArrowSubrole); break; | |
case 2: subrole = CFStringRef(QAXDecrementPageSubrole); break; | |
case 4: subrole = CFStringRef(QAXIncrementPageSubrole); break; | |
case 5: subrole = CFStringRef(QAXIncrementArrowSubrole); break; | |
default: | |
break; | |
} | |
return subrole; | |
} | |
return QCFString(); | |
} | |
// Gets the scroll bar orientation by asking the QAbstractSlider object directly. | |
static Qt::Orientation scrollBarOrientation(const QAInterface &scrollBar) | |
{ | |
QObject *const object = scrollBar.object(); | |
if (QAbstractSlider * const sliderObject = qobject_cast<QAbstractSlider * const>(object)) | |
return sliderObject->orientation(); | |
return Qt::Vertical; // D'oh! The interface wasn't a scroll bar. | |
} | |
static QAInterface scrollAreaGetScrollBarInterface(const QAInterface &scrollArea, Qt::Orientation orientation) | |
{ | |
if (macRole(scrollArea) != CFStringRef(CFStringRef(QAXScrollAreaRole))) | |
return QAInterface(); | |
// Child 1 is the contents widget, 2 and 3 are the scroll bar containers wich contains possible scroll bars. | |
for (int i = 2; i <= 3; ++i) { | |
QAInterface scrollBarContainer = scrollArea.childAt(i); | |
for (int i = 1; i <= scrollBarContainer.childCount(); ++i) { | |
QAInterface scrollBar = scrollBarContainer.childAt(i); | |
if (scrollBar.isValid() && | |
scrollBar.role() == QAccessible::ScrollBar && | |
scrollBarOrientation(scrollBar) == orientation) | |
return scrollBar; | |
} | |
} | |
return QAInterface(); | |
} | |
static bool scrollAreaHasScrollBar(const QAInterface &scrollArea, Qt::Orientation orientation) | |
{ | |
return scrollAreaGetScrollBarInterface(scrollArea, orientation).isValid(); | |
} | |
static QAElement scrollAreaGetScrollBar(const QAInterface &scrollArea, Qt::Orientation orientation) | |
{ | |
return accessibleHierarchyManager()->lookup(scrollAreaGetScrollBarInterface(scrollArea, orientation)); | |
} | |
static QAElement scrollAreaGetContents(const QAInterface &scrollArea) | |
{ | |
// Child 1 is the contents widget, | |
return accessibleHierarchyManager()->lookup(scrollArea.navigate(QAccessible::Child, 1)); | |
} | |
static QAElement tabWidgetGetContents(const QAInterface &interface) | |
{ | |
// A kAXTabGroup has a kAXContents attribute, which consists of the | |
// ui elements for the current tab page. Get the current tab page | |
// from the QStackedWidget, where the current visible page can | |
// be found at index 1. | |
QAInterface stackedWidget = interface.childAt(1); | |
accessibleHierarchyManager()->registerChildren(stackedWidget); | |
QAInterface tabPageInterface = stackedWidget.childAt(1); | |
return accessibleHierarchyManager()->lookup(tabPageInterface); | |
} | |
static QList<QAElement> tabBarGetTabs(const QAInterface &interface) | |
{ | |
// Get the tabs by searching for children with the "PageTab" role. | |
// This filters out the left/right navigation buttons. | |
accessibleHierarchyManager()->registerChildren(interface); | |
QList<QAElement> tabs; | |
const int numChildren = interface.childCount(); | |
for (int i = 1; i < numChildren + 1; ++i) { | |
QAInterface child = interface.navigate(QAccessible::Child, i); | |
if (child.isValid() && child.role() == QAccessible::PageTab) { | |
tabs.append(accessibleHierarchyManager()->lookup(child)); | |
} | |
} | |
return tabs; | |
} | |
static QList<QAElement> tabWidgetGetTabs(const QAInterface &interface) | |
{ | |
// Each QTabWidget has two children, a QStackedWidget and a QTabBar. | |
// Get the tabs from the QTabBar. | |
return tabBarGetTabs(interface.childAt(2)); | |
} | |
static QList<QAElement> tabWidgetGetChildren(const QAInterface &interface) | |
{ | |
// The children for a kAXTabGroup should consist of the tabs and the | |
// contents of the current open tab page. | |
QList<QAElement> children = tabWidgetGetTabs(interface); | |
children += tabWidgetGetContents(interface); | |
return children; | |
} | |
#endif //QT_MAC_USE_COCOA | |
/* | |
Returns the label (buddy) interface for interface, or 0 if it has none. | |
*/ | |
/* | |
static QAInterface findLabel(const QAInterface &interface) | |
{ | |
return interface.navigate(QAccessible::Label, 1); | |
} | |
*/ | |
/* | |
Returns a list of interfaces this interface labels, or an empty list if it doesn't label any. | |
*/ | |
/* | |
static QList<QAInterface> findLabelled(const QAInterface &interface) | |
{ | |
QList<QAInterface> interfaceList; | |
int count = 1; | |
const QAInterface labelled = interface.navigate(QAccessible::Labelled, count); | |
while (labelled.isValid()) { | |
interfaceList.append(labelled); | |
++count; | |
} | |
return interfaceList; | |
} | |
*/ | |
/* | |
Tests if the given QAInterface has data for a mac attribute. | |
*/ | |
#ifndef QT_MAC_USE_COCOA | |
static bool supportsAttribute(CFStringRef attribute, const QAInterface &interface) | |
{ | |
const int text = textForRoleAndAttribute(interface.role(), attribute); | |
// Special case: Static texts don't have a title. | |
if (interface.role() == QAccessible::StaticText && attribute == CFStringRef(QAXTitleAttribute)) | |
return false; | |
// Return true if we the attribute matched a QAccessible::Role and we get text for that role from the interface. | |
if (text != -1) { | |
if (text == QAccessible::Value) // Special case for Value, see getValue() | |
return !getValue(interface).isEmpty(); | |
else | |
return !interface.text((QAccessible::Text)text).isEmpty(); | |
} | |
if (CFStringCompare(attribute, CFStringRef(QAXChildrenAttribute), 0) == kCFCompareEqualTo) { | |
if (interface.childCount() > 0) | |
return true; | |
} | |
if (CFStringCompare(attribute, CFStringRef(QAXSubroleAttribute), 0) == kCFCompareEqualTo) { | |
return (subrole(interface) != QCFString()); | |
} | |
return false; | |
} | |
static void appendIfSupported(CFMutableArrayRef array, CFStringRef attribute, const QAInterface &interface) | |
{ | |
if (supportsAttribute(attribute, interface)) | |
qt_mac_append_cf_uniq(array, attribute); | |
} | |
/* | |
Returns the names of the attributes the give QAInterface supports. | |
*/ | |
static OSStatus getAllAttributeNames(EventRef event, const QAInterface &interface, EventHandlerCallRef next_ref) | |
{ | |
// Call system event handler. | |
OSStatus err = CallNextEventHandler(next_ref, event); | |
if(err != noErr && err != eventNotHandledErr) | |
return err; | |
CFMutableArrayRef attrs = 0; | |
GetEventParameter(event, kEventParamAccessibleAttributeNames, typeCFMutableArrayRef, 0, | |
sizeof(attrs), 0, &attrs); | |
if (!attrs) | |
return eventNotHandledErr; | |
// Append attribute names that are always supported. | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXPositionAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSizeAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXRoleAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXEnabledAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXWindowAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXTopLevelUIElementAttribute)); | |
// Append these names if the QInterafceItem returns any data for them. | |
appendIfSupported(attrs, CFStringRef(QAXTitleAttribute), interface); | |
appendIfSupported(attrs, CFStringRef(QAXValueAttribute), interface); | |
appendIfSupported(attrs, CFStringRef(QAXDescriptionAttribute), interface); | |
appendIfSupported(attrs, CFStringRef(QAXLinkedUIElementsAttribute), interface); | |
appendIfSupported(attrs, CFStringRef(QAXHelpAttribute), interface); | |
appendIfSupported(attrs, CFStringRef(QAXTitleUIElementAttribute), interface); | |
appendIfSupported(attrs, CFStringRef(QAXChildrenAttribute), interface); | |
appendIfSupported(attrs, CFStringRef(QAXSubroleAttribute), interface); | |
// Append attribute names based on the interaface role. | |
switch (interface.role()) { | |
case QAccessible::Window: | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMainAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinimizedAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXCloseButtonAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXZoomButtonAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinimizeButtonAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXToolbarButtonAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXGrowAreaAttribute)); | |
break; | |
case QAccessible::RadioButton: | |
case QAccessible::CheckBox: | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinValueAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMaxValueAttribute)); | |
break; | |
case QAccessible::ScrollBar: | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXOrientationAttribute)); | |
break; | |
case QAccessible::Splitter: | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSplittersAttribute)); | |
break; | |
case QAccessible::Table: | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXRowsAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXVisibleRowsAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSelectedRowsAttribute)); | |
break; | |
default: | |
break; | |
} | |
// Append attribute names based on the mac accessibility role. | |
const QCFString mac_role = macRole(interface); | |
if (mac_role == CFStringRef(QAXSplitterRole)) { | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXPreviousContentsAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXNextContentsAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXOrientationAttribute)); | |
} else if (mac_role == CFStringRef(QAXScrollAreaRole)) { | |
if (scrollAreaHasScrollBar(interface, Qt::Horizontal)) | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXHorizontalScrollBarAttribute)); | |
if (scrollAreaHasScrollBar(interface, Qt::Vertical)) | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXVerticalScrollBarAttribute)); | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXContentsAttribute)); | |
} else if (mac_role == CFStringRef(QAXTabGroupRole)) { | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXTabsAttribute)); | |
// Only tab widgets can have the contents attribute, there is no way of getting | |
// the contents from a QTabBar. | |
if (isTabWidget(interface)) | |
qt_mac_append_cf_uniq(attrs, CFStringRef(QAXContentsAttribute)); | |
} | |
return noErr; | |
} | |
static void handleStringAttribute(EventRef event, QAccessible::Text text, const QAInterface &interface) | |
{ | |
QString str = interface.text(text); | |
if (str.isEmpty()) | |
return; | |
// Remove any html markup from the text string, or VoiceOver will read the html tags. | |
static QTextDocument document; | |
document.setHtml(str); | |
str = document.toPlainText(); | |
CFStringRef cfstr = QCFString::toCFStringRef(str); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, sizeof(cfstr), &cfstr); | |
} | |
/* | |
Handles the parent attribute for a interface. | |
There are basically three cases here: | |
1. interface is a HIView and has only HIView children. | |
2. interface is a HIView but has children that is not a HIView | |
3. interface is not a HIView. | |
*/ | |
static OSStatus handleChildrenAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) | |
{ | |
// Add the children for this interface to the global QAccessibelHierachyManager. | |
accessibleHierarchyManager()->registerChildren(interface); | |
if (isTabWidget(interface)) { | |
QList<QAElement> children = tabWidgetGetChildren(interface); | |
const int childCount = children.count(); | |
CFMutableArrayRef array = 0; | |
array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); | |
for (int i = 0; i < childCount; ++i) { | |
qt_mac_append_cf_uniq(array, children.at(i).element()); | |
} | |
OSStatus err; | |
err = SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFArrayRef, sizeof(array), &array); | |
if (err != noErr) | |
qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__); | |
return noErr; | |
} | |
const QList<QAElement> children = lookup(interface.children()); | |
const int childCount = children.count(); | |
OSStatus err = eventNotHandledErr; | |
if (interface.isHIView()) | |
err = CallNextEventHandler(next_ref, event); | |
CFMutableArrayRef array = 0; | |
int arraySize = 0; | |
if (err == noErr) { | |
CFTypeRef obj = 0; | |
err = GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, NULL , sizeof(obj), NULL, &obj); | |
if (err == noErr && obj != 0) { | |
array = (CFMutableArrayRef)obj; | |
arraySize = CFArrayGetCount(array); | |
} | |
} | |
if (array) { | |
CFArrayRemoveAllValues(array); | |
for (int i = 0; i < childCount; ++i) { | |
qt_mac_append_cf_uniq(array, children.at(i).element()); | |
} | |
} else { | |
array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); | |
for (int i = 0; i < childCount; ++i) { | |
qt_mac_append_cf_uniq(array, children.at(i).element()); | |
} | |
err = SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFArrayRef, sizeof(array), &array); | |
if (err != noErr) | |
qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__); | |
} | |
return noErr; | |
} | |
/* | |
*/ | |
static OSStatus handleParentAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) | |
{ | |
OSStatus err = eventNotHandledErr; | |
if (interface.isHIView()) { | |
err = CallNextEventHandler(next_ref, event); | |
} | |
if (err == noErr) | |
return err; | |
const QAInterface parentInterface = interface.navigate(QAccessible::Ancestor, 1); | |
const QAElement parentElement = accessibleHierarchyManager()->lookup(parentInterface); | |
if (parentElement.isValid() == false) | |
return eventNotHandledErr; | |
AXUIElementRef elementRef = parentElement.element(); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(elementRef), &elementRef); | |
return noErr; | |
} | |
#endif | |
struct IsWindowTest | |
{ | |
static inline bool test(const QAInterface &interface) | |
{ | |
return (interface.role() == QAccessible::Window); | |
} | |
}; | |
struct IsWindowAndNotDrawerOrSheetTest | |
{ | |
static inline bool test(const QAInterface &interface) | |
{ | |
QWidget * const widget = qobject_cast<QWidget*>(interface.object()); | |
return (interface.role() == QAccessible::Window && | |
widget && widget->isWindow() && | |
!qt_mac_is_macdrawer(widget) && | |
!qt_mac_is_macsheet(widget)); | |
} | |
}; | |
/* | |
Navigates up the iterfaces ancestor hierachy until a QAccessibleInterface that | |
passes the Test is found. If we reach a interface that is a HIView we stop the | |
search and call AXUIElementCopyAttributeValue. | |
*/ | |
template <typename TestType> | |
OSStatus navigateAncestors(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface, CFStringRef attribute) | |
{ | |
if (interface.isHIView()) | |
return CallNextEventHandler(next_ref, event); | |
QAInterface current = interface; | |
QAElement element; | |
while (current.isValid()) { | |
if (TestType::test(interface)) { | |
element = accessibleHierarchyManager()->lookup(current); | |
break; | |
} | |
// If we reach an InterfaceItem that is a HiView we can hand of the search to | |
// the system event handler. This is the common case. | |
if (current.isHIView()) { | |
CFTypeRef value = 0; | |
const QAElement currentElement = accessibleHierarchyManager()->lookup(current); | |
AXError err = AXUIElementCopyAttributeValue(currentElement.element(), attribute, &value); | |
AXUIElementRef newElement = (AXUIElementRef)value; | |
if (err == noErr) | |
element = QAElement(newElement); | |
if (newElement != 0) | |
CFRelease(newElement); | |
break; | |
} | |
QAInterface next = current.parent(); | |
if (next.isValid() == false) | |
break; | |
if (next == current) | |
break; | |
current = next; | |
} | |
if (element.isValid() == false) | |
return eventNotHandledErr; | |
AXUIElementRef elementRef = element.element(); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, | |
sizeof(elementRef), &elementRef); | |
return noErr; | |
} | |
/* | |
Returns the top-level window for an interface, which is the closest ancestor interface that | |
has the Window role, but is not a sheet or a drawer. | |
*/ | |
#ifndef QT_MAC_USE_COCOA | |
static OSStatus handleWindowAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) | |
{ | |
return navigateAncestors<IsWindowAndNotDrawerOrSheetTest>(next_ref, event, interface, CFStringRef(QAXWindowAttribute)); | |
} | |
/* | |
Returns the top-level window for an interface, which is the closest ancestor interface that | |
has the Window role. (Can also be a sheet or a drawer) | |
*/ | |
static OSStatus handleTopLevelUIElementAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) | |
{ | |
return navigateAncestors<IsWindowTest>(next_ref, event, interface, CFStringRef(QAXTopLevelUIElementAttribute)); | |
} | |
/* | |
Returns the tab buttons for an interface. | |
*/ | |
static OSStatus handleTabsAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) | |
{ | |
Q_UNUSED(next_ref); | |
if (isTabWidget(interface)) | |
return setAttributeValue(event, tabWidgetGetTabs(interface)); | |
else | |
return setAttributeValue(event, tabBarGetTabs(interface)); | |
} | |
static OSStatus handlePositionAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface) | |
{ | |
QPoint qpoint(interface.rect().topLeft()); | |
HIPoint point; | |
point.x = qpoint.x(); | |
point.y = qpoint.y(); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(point), &point); | |
return noErr; | |
} | |
static OSStatus handleSizeAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface) | |
{ | |
QSize qSize(interface.rect().size()); | |
HISize size; | |
size.width = qSize.width(); | |
size.height = qSize.height(); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeHISize, sizeof(size), &size); | |
return noErr; | |
} | |
static OSStatus handleSubroleAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface) | |
{ | |
const QCFString role = subrole(interface); | |
CFStringRef rolestr = (CFStringRef)role; | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(rolestr), &rolestr); | |
return noErr; | |
} | |
static OSStatus handleOrientationAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) | |
{ | |
QObject *const object = interface.object(); | |
Qt::Orientation orientation; | |
if (interface.role() == QAccessible::ScrollBar) { | |
orientation = scrollBarOrientation(interface); | |
} else if (QSplitterHandle * const splitter = qobject_cast<QSplitterHandle * const>(object)) { | |
// Qt reports the layout orientation, but we want the splitter handle orientation. | |
orientation = (splitter->orientation() == Qt::Horizontal) ? Qt::Vertical : Qt::Horizontal; | |
} else { | |
return CallNextEventHandler(next_ref, event); | |
} | |
const CFStringRef orientationString = (orientation == Qt::Vertical) | |
? CFStringRef(QAXVerticalOrientationValue) : CFStringRef(QAXHorizontalOrientationValue); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, sizeof(orientationString), &orientationString); | |
return noErr; | |
} | |
/* | |
Figures out the next or previous contents for a splitter. | |
*/ | |
static OSStatus handleSplitterContentsAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface, QCFString nextOrPrev) | |
{ | |
if (interface.isValid() == false || interface.role() != QAccessible::Grip) | |
return eventNotHandledErr; | |
const QAInterface parent = interface.parent(); | |
if (parent.isValid() == false) | |
return CallNextEventHandler(next_ref, event); | |
if (parent.role() != QAccessible::Splitter) | |
return CallNextEventHandler(next_ref, event); | |
const QSplitter * const splitter = qobject_cast<const QSplitter * const>(parent.object()); | |
if (splitter == 0) | |
return CallNextEventHandler(next_ref, event); | |
QWidget * const splitterHandle = qobject_cast<QWidget * const>(interface.object()); | |
const int splitterHandleIndex = splitter->indexOf(splitterHandle); | |
const int widgetIndex = (nextOrPrev == QCFString(CFStringRef(QAXPreviousContentsAttribute))) ? splitterHandleIndex - 1 : splitterHandleIndex; | |
const QAElement contentsElement = accessibleHierarchyManager()->lookup(splitter->widget(widgetIndex), 0); | |
return setAttributeValue(event, QList<QAElement>() << contentsElement); | |
} | |
/* | |
Creates a list of all splitter handles the splitter contains. | |
*/ | |
static OSStatus handleSplittersAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) | |
{ | |
const QSplitter * const splitter = qobject_cast<const QSplitter * const>(interface.object()); | |
if (splitter == 0) | |
return CallNextEventHandler(next_ref, event); | |
accessibleHierarchyManager()->registerChildren(interface); | |
QList<QAElement> handles; | |
const int visibleSplitterCount = splitter->count() -1; // skip first handle, it's always invisible. | |
for (int i = 0; i < visibleSplitterCount; ++i) | |
handles.append(accessibleHierarchyManager()->lookup(splitter->handle(i + 1), 0)); | |
return setAttributeValue(event, handles); | |
} | |
// This handler gets the scroll bars for a scroll area | |
static OSStatus handleScrollBarAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &scrollArea, Qt::Orientation orientation) | |
{ | |
QAElement scrollBar = scrollAreaGetScrollBar(scrollArea, orientation); | |
if (scrollBar.isValid() == false) | |
return CallNextEventHandler(next_ref, event); | |
AXUIElementRef elementRef = scrollBar.element(); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(elementRef), &elementRef); | |
return noErr; | |
} | |
// This handler gets the contents for a scroll area or tab widget. | |
static OSStatus handleContentsAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) | |
{ | |
const QCFString mac_role = macRole(interface); | |
QAElement contents; | |
if (mac_role == kAXTabGroupRole) { | |
contents = tabWidgetGetContents(interface); | |
} else { | |
contents = scrollAreaGetContents(interface); | |
if (contents.isValid() == false) | |
return CallNextEventHandler(next_ref, event); | |
} | |
return setAttributeValue(event, QList<QAElement>() << contents); | |
} | |
static OSStatus handleRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView) | |
{ | |
QList<QAElement> rows = lookup(tableView.children()); | |
// kill the first row which is the horizontal header. | |
rows.removeAt(0); | |
return setAttributeValue(event, rows); | |
} | |
static OSStatus handleVisibleRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView) | |
{ | |
QList<QAElement> visibleRows; | |
QList<QAInterface> rows = tableView.children(); | |
// kill the first row which is the horizontal header. | |
rows.removeAt(0); | |
foreach (const QAInterface &interface, rows) | |
if ((interface.state() & QAccessible::Invisible) == false) | |
visibleRows.append(accessibleHierarchyManager()->lookup(interface)); | |
return setAttributeValue(event, visibleRows); | |
} | |
static OSStatus handleSelectedRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView) | |
{ | |
QList<QAElement> selectedRows; | |
foreach (const QAInterface &interface, tableView.children()) | |
if ((interface.state() & QAccessible::Selected)) | |
selectedRows.append(accessibleHierarchyManager()->lookup(interface)); | |
return setAttributeValue(event, selectedRows); | |
} | |
static OSStatus getNamedAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) | |
{ | |
CFStringRef var; | |
GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0, | |
sizeof(var), 0, &var); | |
if (CFStringCompare(var, CFStringRef(QAXChildrenAttribute), 0) == kCFCompareEqualTo) { | |
return handleChildrenAttribute(next_ref, event, interface); | |
} else if(CFStringCompare(var, CFStringRef(QAXTopLevelUIElementAttribute), 0) == kCFCompareEqualTo) { | |
return handleTopLevelUIElementAttribute(next_ref, event, interface); | |
} else if(CFStringCompare(var, CFStringRef(QAXWindowAttribute), 0) == kCFCompareEqualTo) { | |
return handleWindowAttribute(next_ref, event, interface); | |
} else if(CFStringCompare(var, CFStringRef(QAXParentAttribute), 0) == kCFCompareEqualTo) { | |
return handleParentAttribute(next_ref, event, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXPositionAttribute), 0) == kCFCompareEqualTo) { | |
return handlePositionAttribute(next_ref, event, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXSizeAttribute), 0) == kCFCompareEqualTo) { | |
return handleSizeAttribute(next_ref, event, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXRoleAttribute), 0) == kCFCompareEqualTo) { | |
CFStringRef role = macRole(interface); | |
// ### | |
// QWidget * const widget = qobject_cast<QWidget *>(interface.object()); | |
// if (role == CFStringRef(QAXUnknownRole) && widget && widget->isWindow()) | |
// role = CFStringRef(QAXWindowRole); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, | |
sizeof(role), &role); | |
} else if (CFStringCompare(var, CFStringRef(QAXEnabledAttribute), 0) == kCFCompareEqualTo) { | |
Boolean val = !((interface.state() & QAccessible::Unavailable)) | |
&& !((interface.state() & QAccessible::Invisible)); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, | |
sizeof(val), &val); | |
} else if (CFStringCompare(var, CFStringRef(QAXExpandedAttribute), 0) == kCFCompareEqualTo) { | |
Boolean val = (interface.state() & QAccessible::Expanded); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, | |
sizeof(val), &val); | |
} else if (CFStringCompare(var, CFStringRef(QAXSelectedAttribute), 0) == kCFCompareEqualTo) { | |
Boolean val = (interface.state() & QAccessible::Selection); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, | |
sizeof(val), &val); | |
} else if (CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) { | |
Boolean val = (interface.state() & QAccessible::Focus); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, | |
sizeof(val), &val); | |
} else if (CFStringCompare(var, CFStringRef(QAXSelectedChildrenAttribute), 0) == kCFCompareEqualTo) { | |
const int cc = interface.childCount(); | |
QList<QAElement> selected; | |
for (int i = 1; i <= cc; ++i) { | |
const QAInterface child_iface = interface.navigate(QAccessible::Child, i); | |
if (child_iface.isValid() && child_iface.state() & QAccessible::Selected) | |
selected.append(accessibleHierarchyManager()->lookup(child_iface)); | |
} | |
return setAttributeValue(event, selected); | |
} else if (CFStringCompare(var, CFStringRef(QAXCloseButtonAttribute), 0) == kCFCompareEqualTo) { | |
if(interface.object() && interface.object()->isWidgetType()) { | |
Boolean val = true; //do we want to add a WState for this? | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, | |
sizeof(val), &val); | |
} | |
} else if (CFStringCompare(var, CFStringRef(QAXZoomButtonAttribute), 0) == kCFCompareEqualTo) { | |
if(interface.object() && interface.object()->isWidgetType()) { | |
QWidget *widget = (QWidget*)interface.object(); | |
Boolean val = (widget->windowFlags() & Qt::WindowMaximizeButtonHint); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, | |
sizeof(val), &val); | |
} | |
} else if (CFStringCompare(var, CFStringRef(QAXMinimizeButtonAttribute), 0) == kCFCompareEqualTo) { | |
if(interface.object() && interface.object()->isWidgetType()) { | |
QWidget *widget = (QWidget*)interface.object(); | |
Boolean val = (widget->windowFlags() & Qt::WindowMinimizeButtonHint); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, | |
sizeof(val), &val); | |
} | |
} else if (CFStringCompare(var, CFStringRef(QAXToolbarButtonAttribute), 0) == kCFCompareEqualTo) { | |
if(interface.object() && interface.object()->isWidgetType()) { | |
QWidget *widget = (QWidget*)interface.object(); | |
Boolean val = qobject_cast<QMainWindow *>(widget) != 0; | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, | |
sizeof(val), &val); | |
} | |
} else if (CFStringCompare(var, CFStringRef(QAXGrowAreaAttribute), 0) == kCFCompareEqualTo) { | |
if(interface.object() && interface.object()->isWidgetType()) { | |
Boolean val = true; //do we want to add a WState for this? | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, | |
sizeof(val), &val); | |
} | |
} else if (CFStringCompare(var, CFStringRef(QAXMinimizedAttribute), 0) == kCFCompareEqualTo) { | |
if (interface.object() && interface.object()->isWidgetType()) { | |
QWidget *widget = (QWidget*)interface.object(); | |
Boolean val = (widget->windowState() & Qt::WindowMinimized); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, | |
sizeof(val), &val); | |
} | |
} else if (CFStringCompare(var, CFStringRef(QAXSubroleAttribute), 0) == kCFCompareEqualTo) { | |
return handleSubroleAttribute(next_ref, event, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXRoleDescriptionAttribute), 0) == kCFCompareEqualTo) { | |
#if !defined(QT_MAC_USE_COCOA) | |
if (HICopyAccessibilityRoleDescription) { | |
const CFStringRef roleDescription = HICopyAccessibilityRoleDescription(macRole(interface), 0); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, | |
sizeof(roleDescription), &roleDescription); | |
} else | |
#endif | |
{ | |
// Just use Qt::Description on 10.3 | |
handleStringAttribute(event, QAccessible::Description, interface); | |
} | |
} else if (CFStringCompare(var, CFStringRef(QAXTitleAttribute), 0) == kCFCompareEqualTo) { | |
const QAccessible::Role role = interface.role(); | |
const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); | |
handleStringAttribute(event, text, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXValueAttribute), 0) == kCFCompareEqualTo) { | |
const QAccessible::Role role = interface.role(); | |
const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); | |
if (role == QAccessible::CheckBox || role == QAccessible::RadioButton) { | |
int value = buttonValue(interface); | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value); | |
} else { | |
handleStringAttribute(event, text, interface); | |
} | |
} else if (CFStringCompare(var, CFStringRef(QAXDescriptionAttribute), 0) == kCFCompareEqualTo) { | |
const QAccessible::Role role = interface.role(); | |
const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); | |
handleStringAttribute(event, text, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXLinkedUIElementsAttribute), 0) == kCFCompareEqualTo) { | |
return CallNextEventHandler(next_ref, event); | |
} else if (CFStringCompare(var, CFStringRef(QAXHelpAttribute), 0) == kCFCompareEqualTo) { | |
const QAccessible::Role role = interface.role(); | |
const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); | |
handleStringAttribute(event, text, interface); | |
} else if (CFStringCompare(var, kAXTitleUIElementAttribute, 0) == kCFCompareEqualTo) { | |
return CallNextEventHandler(next_ref, event); | |
} else if (CFStringCompare(var, CFStringRef(QAXTabsAttribute), 0) == kCFCompareEqualTo) { | |
return handleTabsAttribute(next_ref, event, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXMinValueAttribute), 0) == kCFCompareEqualTo) { | |
// tabs we first go to the tab bar which is child #2. | |
QAInterface tabBarInterface = interface.childAt(2); | |
return handleTabsAttribute(next_ref, event, tabBarInterface); | |
} else if (CFStringCompare(var, CFStringRef(QAXMinValueAttribute), 0) == kCFCompareEqualTo) { | |
if (interface.role() == QAccessible::RadioButton || interface.role() == QAccessible::CheckBox) { | |
uint value = 0; | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value); | |
} else { | |
return CallNextEventHandler(next_ref, event); | |
} | |
} else if (CFStringCompare(var, CFStringRef(QAXMaxValueAttribute), 0) == kCFCompareEqualTo) { | |
if (interface.role() == QAccessible::RadioButton || interface.role() == QAccessible::CheckBox) { | |
uint value = 2; | |
SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value); | |
} else { | |
return CallNextEventHandler(next_ref, event); | |
} | |
} else if (CFStringCompare(var, CFStringRef(QAXOrientationAttribute), 0) == kCFCompareEqualTo) { | |
return handleOrientationAttribute(next_ref, event, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXPreviousContentsAttribute), 0) == kCFCompareEqualTo) { | |
return handleSplitterContentsAttribute(next_ref, event, interface, CFStringRef(QAXPreviousContentsAttribute)); | |
} else if (CFStringCompare(var, CFStringRef(QAXNextContentsAttribute), 0) == kCFCompareEqualTo) { | |
return handleSplitterContentsAttribute(next_ref, event, interface, CFStringRef(QAXNextContentsAttribute)); | |
} else if (CFStringCompare(var, CFStringRef(QAXSplittersAttribute), 0) == kCFCompareEqualTo) { | |
return handleSplittersAttribute(next_ref, event, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXHorizontalScrollBarAttribute), 0) == kCFCompareEqualTo) { | |
return handleScrollBarAttribute(next_ref, event, interface, Qt::Horizontal); | |
} else if (CFStringCompare(var, CFStringRef(QAXVerticalScrollBarAttribute), 0) == kCFCompareEqualTo) { | |
return handleScrollBarAttribute(next_ref, event, interface, Qt::Vertical); | |
} else if (CFStringCompare(var, CFStringRef(QAXContentsAttribute), 0) == kCFCompareEqualTo) { | |
return handleContentsAttribute(next_ref, event, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXRowsAttribute), 0) == kCFCompareEqualTo) { | |
return handleRowsAttribute(next_ref, event, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXVisibleRowsAttribute), 0) == kCFCompareEqualTo) { | |
return handleVisibleRowsAttribute(next_ref, event, interface); | |
} else if (CFStringCompare(var, CFStringRef(QAXSelectedRowsAttribute), 0) == kCFCompareEqualTo) { | |
return handleSelectedRowsAttribute(next_ref, event, interface); | |
} else { | |
return CallNextEventHandler(next_ref, event); | |
} | |
return noErr; | |
} | |
static OSStatus isNamedAttributeSettable(EventRef event, const QAInterface &interface) | |
{ | |
CFStringRef var; | |
GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0, | |
sizeof(var), 0, &var); | |
Boolean settable = false; | |
if (CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) { | |
settable = true; | |
} else { | |
for (int r = 0; text_bindings[r][0].qt != -1; r++) { | |
if(interface.role() == (QAccessible::Role)text_bindings[r][0].qt) { | |
for (int a = 1; text_bindings[r][a].qt != -1; a++) { | |
if (CFStringCompare(var, CFStringRef(text_bindings[r][a].mac), 0) == kCFCompareEqualTo) { | |
settable = text_bindings[r][a].settable; | |
break; | |
} | |
} | |
} | |
} | |
} | |
SetEventParameter(event, kEventParamAccessibleAttributeSettable, typeBoolean, | |
sizeof(settable), &settable); | |
return noErr; | |
} | |
static OSStatus getChildAtPoint(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) | |
{ | |
Q_UNUSED(next_ref); | |
if (interface.isValid() == false) | |
return eventNotHandledErr; | |
// Add the children for this interface to the global QAccessibelHierachyManager. | |
accessibleHierarchyManager()->registerChildren(interface); | |
Point where; | |
GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(where), 0, &where); | |
const QAInterface childInterface = interface.childAt(where.h, where.v); | |
if (childInterface.isValid() == false || childInterface == interface) | |
return eventNotHandledErr; | |
const QAElement element = accessibleHierarchyManager()->lookup(childInterface); | |
if (element.isValid() == false) | |
return eventNotHandledErr; | |
AXUIElementRef elementRef = element.element(); | |
CFRetain(elementRef); | |
SetEventParameter(event, kEventParamAccessibleChild, typeCFTypeRef, | |
sizeof(elementRef), &elementRef); | |
return noErr; | |
} | |
/* | |
Returns a list of actions the given interface supports. | |
Currently implemented by getting the interface role and deciding based on that. | |
*/ | |
static QList<QAccessible::Action> supportedPredefinedActions(const QAInterface &interface) | |
{ | |
QList<QAccessible::Action> actions; | |
switch (interface.role()) { | |
default: | |
// Most things can be pressed. | |
actions.append(QAccessible::Press); | |
break; | |
} | |
return actions; | |
} | |
/* | |
Translates a predefined QAccessible::Action to a Mac action constant. | |
Returns an empty string if the Qt Action has no mac equivalent. | |
*/ | |
static QCFString translateAction(const QAccessible::Action action) | |
{ | |
switch (action) { | |
case QAccessible::Press: | |
return CFStringRef(QAXPressAction); | |
break; | |
case QAccessible::Increase: | |
return CFStringRef(QAXIncrementAction); | |
break; | |
case QAccessible::Decrease: | |
return CFStringRef(QAXDecrementAction); | |
break; | |
case QAccessible::Accept: | |
return CFStringRef(QAXConfirmAction); | |
break; | |
case QAccessible::Select: | |
return CFStringRef(QAXPickAction); | |
break; | |
case QAccessible::Cancel: | |
return CFStringRef(QAXCancelAction); | |
break; | |
default: | |
return QCFString(); | |
break; | |
} | |
} | |
/* | |
Translates between a Mac action constant and a QAccessible::Action. | |
Returns QAccessible::Default action if there is no Qt predefined equivalent. | |
*/ | |
static QAccessible::Action translateAction(const CFStringRef actionName) | |
{ | |
if(CFStringCompare(actionName, CFStringRef(QAXPressAction), 0) == kCFCompareEqualTo) { | |
return QAccessible::Press; | |
} else if(CFStringCompare(actionName, CFStringRef(QAXIncrementAction), 0) == kCFCompareEqualTo) { | |
return QAccessible::Increase; | |
} else if(CFStringCompare(actionName, CFStringRef(QAXDecrementAction), 0) == kCFCompareEqualTo) { | |
return QAccessible::Decrease; | |
} else if(CFStringCompare(actionName, CFStringRef(QAXConfirmAction), 0) == kCFCompareEqualTo) { | |
return QAccessible::Accept; | |
} else if(CFStringCompare(actionName, CFStringRef(QAXPickAction), 0) == kCFCompareEqualTo) { | |
return QAccessible::Select; | |
} else if(CFStringCompare(actionName, CFStringRef(QAXCancelAction), 0) == kCFCompareEqualTo) { | |
return QAccessible::Cancel; | |
} else { | |
return QAccessible::DefaultAction; | |
} | |
} | |
#endif // QT_MAC_USE_COCOA | |
/* | |
Copies the translated names all supported actions for an interface into the kEventParamAccessibleActionNames | |
event parameter. | |
*/ | |
#ifndef QT_MAC_USE_COCOA | |
static OSStatus getAllActionNames(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) | |
{ | |
Q_UNUSED(next_ref); | |
CFMutableArrayRef actions = 0; | |
GetEventParameter(event, kEventParamAccessibleActionNames, typeCFMutableArrayRef, 0, | |
sizeof(actions), 0, &actions); | |
// Add supported predefined actions. | |
const QList<QAccessible::Action> predefinedActions = supportedPredefinedActions(interface); | |
for (int i = 0; i < predefinedActions.count(); ++i) { | |
const QCFString action = translateAction(predefinedActions.at(i)); | |
if (action != QCFString()) | |
qt_mac_append_cf_uniq(actions, action); | |
} | |
// Add user actions | |
const int actionCount = interface.userActionCount(); | |
for (int i = 0; i < actionCount; ++i) { | |
const QString actionName = interface.actionText(i, QAccessible::Name); | |
qt_mac_append_cf_uniq(actions, QCFString::toCFStringRef(actionName)); | |
} | |
return noErr; | |
} | |
#endif | |
/* | |
Handles the perforNamedAction event. | |
*/ | |
#ifndef QT_MAC_USE_COCOA | |
static OSStatus performNamedAction(EventHandlerCallRef next_ref, EventRef event, const QAInterface& interface) | |
{ | |
Q_UNUSED(next_ref); | |
CFStringRef act; | |
GetEventParameter(event, kEventParamAccessibleActionName, typeCFStringRef, 0, | |
sizeof(act), 0, &act); | |
const QAccessible::Action action = translateAction(act); | |
// Perform built-in action | |
if (action != QAccessible::DefaultAction) { | |
interface.doAction(action, QVariantList()); | |
return noErr; | |
} | |
// Search for user-defined actions and perform it if found. | |
const int actCount = interface.userActionCount(); | |
const QString qAct = QCFString::toQString(act); | |
for(int i = 0; i < actCount; i++) { | |
if(interface.actionText(i, QAccessible::Name) == qAct) { | |
interface.doAction(i, QVariantList()); | |
break; | |
} | |
} | |
return noErr; | |
} | |
static OSStatus setNamedAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) | |
{ | |
Q_UNUSED(next_ref); | |
Q_UNUSED(event); | |
CFStringRef var; | |
GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0, | |
sizeof(var), 0, &var); | |
if(CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) { | |
CFTypeRef val; | |
if(GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, 0, | |
sizeof(val), 0, &val) == noErr) { | |
if(CFGetTypeID(val) == CFBooleanGetTypeID() && | |
CFEqual(static_cast<CFBooleanRef>(val), kCFBooleanTrue)) { | |
interface.doAction(QAccessible::SetFocus); | |
} | |
} | |
} else { | |
bool found = false; | |
for(int r = 0; text_bindings[r][0].qt != -1; r++) { | |
if(interface.role() == (QAccessible::Role)text_bindings[r][0].qt) { | |
for(int a = 1; text_bindings[r][a].qt != -1; a++) { | |
if(CFStringCompare(var, CFStringRef(text_bindings[r][a].mac), 0) == kCFCompareEqualTo) { | |
if(!text_bindings[r][a].settable) { | |
} else { | |
CFTypeRef val; | |
if(GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, 0, | |
sizeof(val), 0, &val) == noErr) { | |
if(CFGetTypeID(val) == CFStringGetTypeID()) | |
interface.setText((QAccessible::Text)text_bindings[r][a].qt, | |
QCFString::toQString(static_cast<CFStringRef>(val))); | |
} | |
} | |
found = true; | |
break; | |
} | |
} | |
break; | |
} | |
} | |
} | |
return noErr; | |
} | |
/* | |
This is the main accessibility event handler. | |
*/ | |
static OSStatus accessibilityEventHandler(EventHandlerCallRef next_ref, EventRef event, void *data) | |
{ | |
Q_UNUSED(data) | |
// Return if this event is not a AccessibleGetNamedAttribute event. | |
const UInt32 eclass = GetEventClass(event); | |
if (eclass != kEventClassAccessibility) | |
return eventNotHandledErr; | |
// Get the AXUIElementRef and QAInterface pointer | |
AXUIElementRef element = 0; | |
GetEventParameter(event, kEventParamAccessibleObject, typeCFTypeRef, 0, sizeof(element), 0, &element); | |
QAInterface interface = accessibleHierarchyManager()->lookup(element); | |
if (interface.isValid() == false) | |
return eventNotHandledErr; | |
const UInt32 ekind = GetEventKind(event); | |
OSStatus status = noErr; | |
switch (ekind) { | |
case kEventAccessibleGetAllAttributeNames: | |
status = getAllAttributeNames(event, interface, next_ref); | |
break; | |
case kEventAccessibleGetNamedAttribute: | |
status = getNamedAttribute(next_ref, event, interface); | |
break; | |
case kEventAccessibleIsNamedAttributeSettable: | |
status = isNamedAttributeSettable(event, interface); | |
break; | |
case kEventAccessibleGetChildAtPoint: | |
status = getChildAtPoint(next_ref, event, interface); | |
break; | |
case kEventAccessibleGetAllActionNames: | |
status = getAllActionNames(next_ref, event, interface); | |
break; | |
case kEventAccessibleGetFocusedChild: | |
status = CallNextEventHandler(next_ref, event); | |
break; | |
case kEventAccessibleSetNamedAttribute: | |
status = setNamedAttribute(next_ref, event, interface); | |
break; | |
case kEventAccessiblePerformNamedAction: | |
status = performNamedAction(next_ref, event, interface); | |
break; | |
default: | |
status = CallNextEventHandler(next_ref, event); | |
break; | |
}; | |
return status; | |
} | |
#endif | |
void QAccessible::initialize() | |
{ | |
#ifndef QT_MAC_USE_COCOA | |
registerQtAccessibilityHIObjectSubclass(); | |
installApplicationEventhandler(); | |
#endif | |
} | |
// Sets thre root object for the application | |
void QAccessible::setRootObject(QObject *object) | |
{ | |
// Call installed root object handler if we have one | |
if (rootObjectHandler) { | |
rootObjectHandler(object); | |
return; | |
} | |
rootObject = object; | |
} | |
void QAccessible::cleanup() | |
{ | |
accessibleHierarchyManager()->reset(); | |
#ifndef QT_MAC_USE_COCOA | |
removeEventhandler(applicationEventHandlerUPP); | |
removeEventhandler(objectCreateEventHandlerUPP); | |
removeEventhandler(accessibilityEventHandlerUPP); | |
#endif | |
} | |
void QAccessible::updateAccessibility(QObject *object, int child, Event reason) | |
{ | |
// Call installed update handler if we have one. | |
if (updateHandler) { | |
updateHandler(object, child, reason); | |
return; | |
} | |
#ifndef QT_MAC_USE_COCOA | |
// Return if the mac accessibility is not enabled. | |
if(!AXAPIEnabled()) | |
return; | |
// Work around crash, disable accessiblity for focus frames. | |
if (qstrcmp(object->metaObject()->className(), "QFocusFrame") == 0) | |
return; | |
// qDebug() << "updateAccessibility" << object << child << hex << reason; | |
if (reason == ObjectShow) { | |
QAInterface interface = QAInterface(QAccessible::queryAccessibleInterface(object), child); | |
accessibleHierarchyManager()->registerInterface(interface); | |
} | |
const QAElement element = accessibleHierarchyManager()->lookup(object, child); | |
if (element.isValid() == false) | |
return; | |
CFStringRef notification = 0; | |
if(object && object->isWidgetType() && reason == ObjectCreated) { | |
notification = CFStringRef(QAXWindowCreatedNotification); | |
} else if(reason == ValueChanged) { | |
notification = CFStringRef(QAXValueChangedNotification); | |
} else if(reason == MenuStart) { | |
notification = CFStringRef(QAXMenuOpenedNotification); | |
} else if(reason == MenuEnd) { | |
notification = CFStringRef(QAXMenuClosedNotification); | |
} else if(reason == LocationChanged) { | |
notification = CFStringRef(QAXWindowMovedNotification); | |
} else if(reason == ObjectShow || reason == ObjectHide ) { | |
// When a widget is deleted we get a ObjectHide before the destroyed(QObject *) | |
// signal is emitted (which makes sense). However, at this point we are in the | |
// middle of the QWidget destructor which means that we have to be careful when | |
// using the widget pointer. Since we can't control what the accessibilty interfaces | |
// does when navigate() is called below we ignore the hide update in this case. | |
// (the widget will be deleted soon anyway.) | |
extern QWidgetPrivate * qt_widget_private(QWidget *); | |
if (QWidget *widget = qobject_cast<QWidget*>(object)) { | |
if (qt_widget_private(widget)->data.in_destructor) | |
return; | |
// Check widget parent as well, special case for preventing crash | |
// when the viewport() of an abstract scroll area is hidden when | |
// the QWidget destructor hides all its children. | |
QWidget *parentWidget = widget->parentWidget(); | |
if (parentWidget && qt_widget_private(parentWidget)->data.in_destructor) | |
return; | |
} | |
// 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 | |
// QAccessible::Invisible state bit is set. | |
QAInterface interface = accessibleHierarchyManager()->lookup(element); | |
if (interface.isValid()) { | |
HIObjectSetAccessibilityIgnored(element.object(), !isItInteresting(interface)); | |
} | |
// If the interface manages its own children, also check if we should ignore those. | |
if (isItemView(interface) == false && managesChildren(interface)) { | |
for (int i = 1; i <= interface.childCount(); ++i) { | |
QAInterface childInterface = interface.navigate(QAccessible::Child, i); | |
if (childInterface.isValid() && childInterface.isHIView() == false) { | |
const QAElement element = accessibleHierarchyManager()->lookup(childInterface); | |
if (element.isValid()) { | |
HIObjectSetAccessibilityIgnored(element.object(), !isItInteresting(childInterface)); | |
} | |
} | |
} | |
} | |
} else if(reason == Focus) { | |
if(object && object->isWidgetType()) { | |
QWidget *w = static_cast<QWidget*>(object); | |
if(w->isWindow()) | |
notification = CFStringRef(QAXFocusedWindowChangedNotification); | |
else | |
notification = CFStringRef(QAXFocusedUIElementChangedNotification); | |
} | |
} | |
if (!notification) | |
return; | |
AXNotificationHIObjectNotify(notification, element.object(), element.id()); | |
#endif | |
} | |
QT_END_NAMESPACE | |
#endif // QT_NO_ACCESSIBILITY |