diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/gui/accessible/qaccessible_mac.mm | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/gui/accessible/qaccessible_mac.mm')
-rw-r--r-- | src/gui/accessible/qaccessible_mac.mm | 2608 |
1 files changed, 2608 insertions, 0 deletions
diff --git a/src/gui/accessible/qaccessible_mac.mm b/src/gui/accessible/qaccessible_mac.mm new file mode 100644 index 0000000..b6412c2 --- /dev/null +++ b/src/gui/accessible/qaccessible_mac.mm @@ -0,0 +1,2608 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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 QAXCancelAction NSAccessibilityCancelAction +#define QAXCheckBoxRole NSAccessibilityCheckBoxRole +#define QAXChildrenAttribute NSAccessibilityChildrenAttribute +#define QAXChildrenAttribute NSAccessibilityChildrenAttribute +#define QAXChildrenAttribute NSAccessibilityChildrenAttribute +#define QAXCloseButtonAttribute NSAccessibilityCloseButtonAttribute +#define QAXCloseButtonAttribute NSAccessibilityCloseButtonAttribute +#define QAXColumnRole NSAccessibilityColumnRole +#define QAXConfirmAction NSAccessibilityConfirmAction +#define QAXConfirmAction NSAccessibilityConfirmAction +#define QAXContentsAttribute NSAccessibilityContentsAttribute +#define QAXContentsAttribute NSAccessibilityContentsAttribute +#define QAXDecrementAction NSAccessibilityDecrementAction +#define QAXDecrementAction NSAccessibilityDecrementAction +#define QAXDecrementArrowSubrole NSAccessibilityDecrementArrowSubrole +#define QAXDecrementPageSubrole NSAccessibilityDecrementPageSubrole +#define QAXDescriptionAttribute NSAccessibilityDescriptionAttribute +#define QAXDescriptionAttribute NSAccessibilityDescriptionAttribute +#define QAXDescriptionAttribute NSAccessibilityDescriptionAttribute +#define QAXDescriptionAttribute NSAccessibilityDescriptionAttribute +#define QAXEnabledAttribute NSAccessibilityEnabledAttribute +#define QAXEnabledAttribute NSAccessibilityEnabledAttribute +#define QAXExpandedAttribute NSAccessibilityExpandedAttribute +#define QAXFocusedAttribute NSAccessibilityFocusedAttribute +#define QAXFocusedAttribute NSAccessibilityFocusedAttribute +#define QAXFocusedAttribute NSAccessibilityFocusedAttribute +#define QAXFocusedUIElementChangedNotification NSAccessibilityFocusedUIElementChangedNotification +#define QAXFocusedWindowChangedNotification NSAccessibilityFocusedWindowChangedNotification +#define QAXGroupRole NSAccessibilityGroupRole +#define QAXGrowAreaAttribute NSAccessibilityGrowAreaAttribute +#define QAXGrowAreaAttribute NSAccessibilityGrowAreaAttribute +#define QAXGrowAreaRole NSAccessibilityGrowAreaRole +#define QAXHelpAttribute NSAccessibilityHelpAttribute +#define QAXHelpAttribute NSAccessibilityHelpAttribute +#define QAXHelpAttribute NSAccessibilityHelpAttribute +#define QAXHorizontalOrientationValue NSAccessibilityHorizontalOrientationValue +#define QAXHorizontalScrollBarAttribute NSAccessibilityHorizontalScrollBarAttribute +#define QAXHorizontalScrollBarAttribute NSAccessibilityHorizontalScrollBarAttribute +#define QAXIncrementAction NSAccessibilityIncrementAction +#define QAXIncrementAction NSAccessibilityIncrementAction +#define QAXIncrementArrowSubrole NSAccessibilityIncrementArrowSubrole +#define QAXIncrementPageSubrole NSAccessibilityIncrementPageSubrole +#define QAXIncrementorRole NSAccessibilityIncrementorRole +#define QAXLinkedUIElementsAttribute NSAccessibilityLinkedUIElementsAttribute +#define QAXLinkedUIElementsAttribute NSAccessibilityLinkedUIElementsAttribute +#define QAXListRole NSAccessibilityListRole +#define QAXMainAttribute NSAccessibilityMainAttribute +#define QAXMaxValueAttribute NSAccessibilityMaxValueAttribute +#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 QAXMinValueAttribute NSAccessibilityMinValueAttribute +#define QAXMinimizeButtonAttribute NSAccessibilityMinimizeButtonAttribute +#define QAXMinimizeButtonAttribute NSAccessibilityMinimizeButtonAttribute +#define QAXMinimizedAttribute NSAccessibilityMinimizedAttribute +#define QAXMinimizedAttribute NSAccessibilityMinimizedAttribute +#define QAXNextContentsAttribute NSAccessibilityNextContentsAttribute +#define QAXNextContentsAttribute NSAccessibilityNextContentsAttribute +#define QAXOrientationAttribute NSAccessibilityOrientationAttribute +#define QAXOrientationAttribute NSAccessibilityOrientationAttribute +#define QAXOrientationAttribute NSAccessibilityOrientationAttribute +#define QAXParentAttribute NSAccessibilityParentAttribute +#define QAXPickAction NSAccessibilityPickAction +#define QAXPickAction NSAccessibilityPickAction +#define QAXPopUpButtonRole NSAccessibilityPopUpButtonRole +#define QAXPositionAttribute NSAccessibilityPositionAttribute +#define QAXPositionAttribute NSAccessibilityPositionAttribute +#define QAXPressAction NSAccessibilityPressAction +#define QAXPressAction NSAccessibilityPressAction +#define QAXPreviousContentsAttribute NSAccessibilityPreviousContentsAttribute +#define QAXPreviousContentsAttribute NSAccessibilityPreviousContentsAttribute +#define QAXPreviousContentsAttribute NSAccessibilityPreviousContentsAttribute +#define QAXProgressIndicatorRole NSAccessibilityProgressIndicatorRole +#define QAXRadioButtonRole NSAccessibilityRadioButtonRole +#define QAXRoleAttribute NSAccessibilityRoleAttribute +#define QAXRoleAttribute NSAccessibilityRoleAttribute +#define QAXRoleDescriptionAttribute NSAccessibilityRoleDescriptionAttribute +#define QAXRowRole NSAccessibilityRowRole +#define QAXRowsAttribute NSAccessibilityRowsAttribute +#define QAXRowsAttribute NSAccessibilityRowsAttribute +#define QAXScrollAreaRole NSAccessibilityScrollAreaRole +#define QAXScrollAreaRole NSAccessibilityScrollAreaRole +#define QAXScrollAreaRole NSAccessibilityScrollAreaRole +#define QAXScrollBarRole NSAccessibilityScrollBarRole +#define QAXSelectedAttribute NSAccessibilitySelectedAttribute +#define QAXSelectedChildrenAttribute NSAccessibilitySelectedChildrenAttribute +#define QAXSelectedRowsAttribute NSAccessibilitySelectedRowsAttribute +#define QAXSelectedRowsAttribute NSAccessibilitySelectedRowsAttribute +#define QAXSizeAttribute NSAccessibilitySizeAttribute +#define QAXSizeAttribute NSAccessibilitySizeAttribute +#define QAXSliderRole NSAccessibilitySliderRole +#define QAXSplitGroupRole NSAccessibilitySplitGroupRole +#define QAXSplitterRole NSAccessibilitySplitterRole +#define QAXSplitterRole NSAccessibilitySplitterRole +#define QAXSplitterRole NSAccessibilitySplitterRole +#define QAXSplittersAttribute NSAccessibilitySplittersAttribute +#define QAXSplittersAttribute NSAccessibilitySplittersAttribute +#define QAXStaticTextRole NSAccessibilityStaticTextRole +#define QAXStaticTextRole NSAccessibilityStaticTextRole +#define QAXSubroleAttribute NSAccessibilitySubroleAttribute +#define QAXSubroleAttribute NSAccessibilitySubroleAttribute +#define QAXSubroleAttribute NSAccessibilitySubroleAttribute +#define QAXTabGroupRole NSAccessibilityTabGroupRole +#define QAXTabGroupRole NSAccessibilityTabGroupRole +#define QAXTableRole NSAccessibilityTableRole +#define QAXTabsAttribute NSAccessibilityTabsAttribute +#define QAXTabsAttribute NSAccessibilityTabsAttribute +#define QAXTextFieldRole NSAccessibilityTextFieldRole +#define QAXTextFieldRole NSAccessibilityTextFieldRole +#define QAXTitleAttribute NSAccessibilityTitleAttribute +#define QAXTitleAttribute NSAccessibilityTitleAttribute +#define QAXTitleAttribute NSAccessibilityTitleAttribute +#define QAXTitleAttribute NSAccessibilityTitleAttribute +#define QAXTitleUIElementAttribute NSAccessibilityTitleUIElementAttribute +#define QAXTitleUIElementAttribute NSAccessibilityTitleUIElementAttribute +#define QAXToolbarButtonAttribute NSAccessibilityToolbarButtonAttribute +#define QAXToolbarButtonAttribute NSAccessibilityToolbarButtonAttribute +#define QAXToolbarRole NSAccessibilityToolbarRole +#define QAXTopLevelUIElementAttribute NSAccessibilityTopLevelUIElementAttribute +#define QAXTopLevelUIElementAttribute NSAccessibilityTopLevelUIElementAttribute +#define QAXTopLevelUIElementAttribute NSAccessibilityTopLevelUIElementAttribute +#define QAXUnknownRole NSAccessibilityUnknownRole +#define QAXUnknownRole NSAccessibilityUnknownRole +#define QAXValueAttribute NSAccessibilityValueAttribute +#define QAXValueAttribute NSAccessibilityValueAttribute +#define QAXValueAttribute NSAccessibilityValueAttribute +#define QAXValueAttribute NSAccessibilityValueAttribute +#define QAXValueChangedNotification NSAccessibilityValueChangedNotification +#define QAXValueIndicatorRole NSAccessibilityValueIndicatorRole +#define QAXVerticalOrientationValue NSAccessibilityVerticalOrientationValue +#define QAXVerticalScrollBarAttribute NSAccessibilityVerticalScrollBarAttribute +#define QAXVerticalScrollBarAttribute NSAccessibilityVerticalScrollBarAttribute +#define QAXVisibleRowsAttribute NSAccessibilityVisibleRowsAttribute +#define QAXVisibleRowsAttribute NSAccessibilityVisibleRowsAttribute +#define QAXWindowAttribute NSAccessibilityWindowAttribute +#define QAXWindowAttribute NSAccessibilityWindowAttribute +#define QAXWindowAttribute NSAccessibilityWindowAttribute +#define QAXWindowCreatedNotification NSAccessibilityWindowCreatedNotification +#define QAXWindowMovedNotification NSAccessibilityWindowMovedNotification +#define QAXWindowRole NSAccessibilityWindowRole +#define QAXZoomButtonAttribute NSAccessibilityZoomButtonAttribute +#define QAXZoomButtonAttribute NSAccessibilityZoomButtonAttribute +#else +typedef CFStringRef const QAXRoleType; +#define QAXApplicationRole kAXApplicationRole +#define QAXButtonRole kAXButtonRole +#define QAXCancelAction kAXCancelAction +#define QAXCancelAction kAXCancelAction +#define QAXCheckBoxRole kAXCheckBoxRole +#define QAXChildrenAttribute kAXChildrenAttribute +#define QAXChildrenAttribute kAXChildrenAttribute +#define QAXChildrenAttribute kAXChildrenAttribute +#define QAXCloseButtonAttribute kAXCloseButtonAttribute +#define QAXCloseButtonAttribute kAXCloseButtonAttribute +#define QAXColumnRole kAXColumnRole +#define QAXConfirmAction kAXConfirmAction +#define QAXConfirmAction kAXConfirmAction +#define QAXContentsAttribute kAXContentsAttribute +#define QAXContentsAttribute kAXContentsAttribute +#define QAXDecrementAction kAXDecrementAction +#define QAXDecrementAction kAXDecrementAction +#define QAXDecrementArrowSubrole kAXDecrementArrowSubrole +#define QAXDecrementPageSubrole kAXDecrementPageSubrole +#define QAXDescriptionAttribute kAXDescriptionAttribute +#define QAXDescriptionAttribute kAXDescriptionAttribute +#define QAXDescriptionAttribute kAXDescriptionAttribute +#define QAXDescriptionAttribute kAXDescriptionAttribute +#define QAXEnabledAttribute kAXEnabledAttribute +#define QAXEnabledAttribute kAXEnabledAttribute +#define QAXExpandedAttribute kAXExpandedAttribute +#define QAXFocusedAttribute kAXFocusedAttribute +#define QAXFocusedAttribute kAXFocusedAttribute +#define QAXFocusedAttribute kAXFocusedAttribute +#define QAXFocusedUIElementChangedNotification kAXFocusedUIElementChangedNotification +#define QAXFocusedWindowChangedNotification kAXFocusedWindowChangedNotification +#define QAXGroupRole kAXGroupRole +#define QAXGrowAreaAttribute kAXGrowAreaAttribute +#define QAXGrowAreaAttribute kAXGrowAreaAttribute +#define QAXGrowAreaRole kAXGrowAreaRole +#define QAXHelpAttribute kAXHelpAttribute +#define QAXHelpAttribute kAXHelpAttribute +#define QAXHelpAttribute kAXHelpAttribute +#define QAXHorizontalOrientationValue kAXHorizontalOrientationValue +#define QAXHorizontalScrollBarAttribute kAXHorizontalScrollBarAttribute +#define QAXHorizontalScrollBarAttribute kAXHorizontalScrollBarAttribute +#define QAXIncrementAction kAXIncrementAction +#define QAXIncrementAction kAXIncrementAction +#define QAXIncrementArrowSubrole kAXIncrementArrowSubrole +#define QAXIncrementPageSubrole kAXIncrementPageSubrole +#define QAXIncrementorRole kAXIncrementorRole +#define QAXLinkedUIElementsAttribute kAXLinkedUIElementsAttribute +#define QAXLinkedUIElementsAttribute kAXLinkedUIElementsAttribute +#define QAXListRole kAXListRole +#define QAXMainAttribute kAXMainAttribute +#define QAXMaxValueAttribute kAXMaxValueAttribute +#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 QAXMinValueAttribute kAXMinValueAttribute +#define QAXMinimizeButtonAttribute kAXMinimizeButtonAttribute +#define QAXMinimizeButtonAttribute kAXMinimizeButtonAttribute +#define QAXMinimizedAttribute kAXMinimizedAttribute +#define QAXMinimizedAttribute kAXMinimizedAttribute +#define QAXNextContentsAttribute kAXNextContentsAttribute +#define QAXNextContentsAttribute kAXNextContentsAttribute +#define QAXOrientationAttribute kAXOrientationAttribute +#define QAXOrientationAttribute kAXOrientationAttribute +#define QAXOrientationAttribute kAXOrientationAttribute +#define QAXParentAttribute kAXParentAttribute +#define QAXPickAction kAXPickAction +#define QAXPickAction kAXPickAction +#define QAXPopUpButtonRole kAXPopUpButtonRole +#define QAXPositionAttribute kAXPositionAttribute +#define QAXPositionAttribute kAXPositionAttribute +#define QAXPressAction kAXPressAction +#define QAXPressAction kAXPressAction +#define QAXPreviousContentsAttribute kAXPreviousContentsAttribute +#define QAXPreviousContentsAttribute kAXPreviousContentsAttribute +#define QAXPreviousContentsAttribute kAXPreviousContentsAttribute +#define QAXProgressIndicatorRole kAXProgressIndicatorRole +#define QAXRadioButtonRole kAXRadioButtonRole +#define QAXRoleAttribute kAXRoleAttribute +#define QAXRoleAttribute kAXRoleAttribute +#define QAXRoleDescriptionAttribute kAXRoleDescriptionAttribute +#define QAXRowRole kAXRowRole +#define QAXRowsAttribute kAXRowsAttribute +#define QAXRowsAttribute kAXRowsAttribute +#define QAXScrollAreaRole kAXScrollAreaRole +#define QAXScrollAreaRole kAXScrollAreaRole +#define QAXScrollAreaRole kAXScrollAreaRole +#define QAXScrollBarRole kAXScrollBarRole +#define QAXSelectedAttribute kAXSelectedAttribute +#define QAXSelectedChildrenAttribute kAXSelectedChildrenAttribute +#define QAXSelectedRowsAttribute kAXSelectedRowsAttribute +#define QAXSelectedRowsAttribute kAXSelectedRowsAttribute +#define QAXSizeAttribute kAXSizeAttribute +#define QAXSizeAttribute kAXSizeAttribute +#define QAXSliderRole kAXSliderRole +#define QAXSplitGroupRole kAXSplitGroupRole +#define QAXSplitterRole kAXSplitterRole +#define QAXSplitterRole kAXSplitterRole +#define QAXSplitterRole kAXSplitterRole +#define QAXSplittersAttribute kAXSplittersAttribute +#define QAXSplittersAttribute kAXSplittersAttribute +#define QAXStaticTextRole kAXStaticTextRole +#define QAXStaticTextRole kAXStaticTextRole +#define QAXSubroleAttribute kAXSubroleAttribute +#define QAXSubroleAttribute kAXSubroleAttribute +#define QAXSubroleAttribute kAXSubroleAttribute +#define QAXTabGroupRole kAXTabGroupRole +#define QAXTabGroupRole kAXTabGroupRole +#define QAXTableRole kAXTableRole +#define QAXTabsAttribute kAXTabsAttribute +#define QAXTabsAttribute kAXTabsAttribute +#define QAXTextFieldRole kAXTextFieldRole +#define QAXTextFieldRole kAXTextFieldRole +#define QAXTitleAttribute kAXTitleAttribute +#define QAXTitleAttribute kAXTitleAttribute +#define QAXTitleAttribute kAXTitleAttribute +#define QAXTitleAttribute kAXTitleAttribute +#define QAXTitleUIElementAttribute kAXTitleUIElementAttribute +#define QAXTitleUIElementAttribute kAXTitleUIElementAttribute +#define QAXToolbarButtonAttribute kAXToolbarButtonAttribute +#define QAXToolbarButtonAttribute kAXToolbarButtonAttribute +#define QAXToolbarRole kAXToolbarRole +#define QAXTopLevelUIElementAttribute kAXTopLevelUIElementAttribute +#define QAXTopLevelUIElementAttribute kAXTopLevelUIElementAttribute +#define QAXTopLevelUIElementAttribute kAXTopLevelUIElementAttribute +#define QAXUnknownRole kAXUnknownRole +#define QAXUnknownRole kAXUnknownRole +#define QAXValueAttribute kAXValueAttribute +#define QAXValueAttribute kAXValueAttribute +#define QAXValueAttribute kAXValueAttribute +#define QAXValueAttribute kAXValueAttribute +#define QAXValueChangedNotification kAXValueChangedNotification +#define QAXValueIndicatorRole kAXValueIndicatorRole +#define QAXVerticalOrientationValue kAXVerticalOrientationValue +#define QAXVerticalScrollBarAttribute kAXVerticalScrollBarAttribute +#define QAXVerticalScrollBarAttribute kAXVerticalScrollBarAttribute +#define QAXVisibleRowsAttribute kAXVisibleRowsAttribute +#define QAXVisibleRowsAttribute kAXVisibleRowsAttribute +#define QAXWindowAttribute kAXWindowAttribute +#define QAXWindowAttribute kAXWindowAttribute +#define QAXWindowAttribute kAXWindowAttribute +#define QAXWindowCreatedNotification kAXWindowCreatedNotification +#define QAXWindowMovedNotification kAXWindowMovedNotification +#define QAXWindowRole kAXWindowRole +#define QAXZoomButtonAttribute kAXZoomButtonAttribute +#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) + :elementRef( +#ifndef QT_MAC_USE_COCOA + AXUIElementCreateWithHIObjectAndIdentifier(object, child) +#endif +) +{ +#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; +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + else if (CFStringCompare(attribute, CFStringRef(QAXDescriptionAttribute), 0) == kCFCompareEqualTo) + return QAccessible::Description; +#endif + 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)); +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXTopLevelUIElementAttribute)); +#endif + + // Append these names if the QInterafceItem returns any data for them. + appendIfSupported(attrs, CFStringRef(QAXTitleAttribute), interface); + appendIfSupported(attrs, CFStringRef(QAXValueAttribute), interface); +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + appendIfSupported(attrs, CFStringRef(QAXDescriptionAttribute), interface); + appendIfSupported(attrs, CFStringRef(QAXLinkedUIElementsAttribute), interface); +#endif + 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) +*/ +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) +static OSStatus handleTopLevelUIElementAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) +{ + return navigateAncestors<IsWindowTest>(next_ref, event, interface, CFStringRef(QAXTopLevelUIElementAttribute)); +} +#endif + +/* + 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); +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + } else if(CFStringCompare(var, CFStringRef(QAXTopLevelUIElementAttribute), 0) == kCFCompareEqualTo) { + return handleTopLevelUIElementAttribute(next_ref, event, interface); +#endif + } 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 (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) && !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); + } +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + } 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); +#endif + } 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 |