Qt 4.8
qaccessible_mac_cocoa.mm
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 
43 #include "qaccessible.h"
44 #include "qaccessible_mac_p.h"
45 #include "qdebug.h"
46 #include "qtabwidget.h"
47 
48 #include <private/qt_mac_p.h>
49 #include <private/qcocoaview_mac_p.h>
50 #include <private/qwidget_p.h>
51 
52 
53 #ifndef QT_NO_ACCESSIBILITY
54 
55 #ifdef QT_MAC_USE_COCOA
56 
58 
59 //#define MAC_ACCESSIBILTY_DEVELOPER_MODE
60 
61 #ifndef QT_NO_DEBUG_STREAM
62 #ifdef MAC_ACCESSIBILTY_DEVELOPER_MODE
63 #define MAC_ACCESSIBILTY_DEBUG QT_PREPEND_NAMESPACE(qDebug)
64 #else
65 #define MAC_ACCESSIBILTY_DEBUG if (0) QT_PREPEND_NAMESPACE(qDebug)
66 #endif
67 #else
68 #define MAC_ACCESSIBILTY_DEBUG if (0) QT_PREPEND_NAMESPACE(QNoDebug)
69 #endif
70 
71 typedef QMap<QAccessible::Role, NSString *> QMacAccessibiltyRoleMap;
72 Q_GLOBAL_STATIC(QMacAccessibiltyRoleMap, qMacAccessibiltyRoleMap);
73 
74 static QAInterface interfaceForView(QT_MANGLE_NAMESPACE(QCocoaView) *view)
75 {
76  return QAInterface(QAccessible::queryAccessibleInterface([view qt_qwidget]));
77 }
78 
79 /*
80  Set up mappings from Qt accessibilty roles to Mac accessibilty roles.
81 */
82 static void populateRoleMap()
83 {
84  QMacAccessibiltyRoleMap &roleMap = *qMacAccessibiltyRoleMap();
85  roleMap[QAccessible::MenuItem] = NSAccessibilityMenuItemRole;
86  roleMap[QAccessible::MenuBar] = NSAccessibilityMenuBarRole;
87  roleMap[QAccessible::ScrollBar] = NSAccessibilityScrollBarRole;
88  roleMap[QAccessible::Grip] = NSAccessibilityGrowAreaRole;
89  roleMap[QAccessible::Window] = NSAccessibilityWindowRole;
90  roleMap[QAccessible::Dialog] = NSAccessibilityWindowRole;
91  roleMap[QAccessible::AlertMessage] = NSAccessibilityWindowRole;
92  roleMap[QAccessible::ToolTip] = NSAccessibilityWindowRole;
93  roleMap[QAccessible::HelpBalloon] = NSAccessibilityWindowRole;
94  roleMap[QAccessible::PopupMenu] = NSAccessibilityMenuRole;
95  roleMap[QAccessible::Application] = NSAccessibilityApplicationRole;
96  roleMap[QAccessible::Pane] = NSAccessibilityGroupRole;
97  roleMap[QAccessible::Grouping] = NSAccessibilityGroupRole;
98  roleMap[QAccessible::Separator] = NSAccessibilitySplitterRole;
99  roleMap[QAccessible::ToolBar] = NSAccessibilityToolbarRole;
100  roleMap[QAccessible::PageTab] = NSAccessibilityRadioButtonRole;
101  roleMap[QAccessible::ButtonMenu] = NSAccessibilityMenuButtonRole;
102  roleMap[QAccessible::ButtonDropDown] = NSAccessibilityPopUpButtonRole;
103  roleMap[QAccessible::SpinBox] = NSAccessibilityIncrementorRole;
104  roleMap[QAccessible::Slider] = NSAccessibilitySliderRole;
105  roleMap[QAccessible::ProgressBar] = NSAccessibilityProgressIndicatorRole;
106  roleMap[QAccessible::ComboBox] = NSAccessibilityPopUpButtonRole;
107  roleMap[QAccessible::RadioButton] = NSAccessibilityRadioButtonRole;
108  roleMap[QAccessible::CheckBox] = NSAccessibilityCheckBoxRole;
109  roleMap[QAccessible::StaticText] = NSAccessibilityStaticTextRole;
110  roleMap[QAccessible::Table] = NSAccessibilityTableRole;
111  roleMap[QAccessible::StatusBar] = NSAccessibilityStaticTextRole;
112  roleMap[QAccessible::Column] = NSAccessibilityColumnRole;
113  roleMap[QAccessible::ColumnHeader] = NSAccessibilityColumnRole;
114  roleMap[QAccessible::Row] = NSAccessibilityRowRole;
115  roleMap[QAccessible::RowHeader] = NSAccessibilityRowRole;
116  roleMap[QAccessible::Cell] = NSAccessibilityTextFieldRole;
117  roleMap[QAccessible::PushButton] = NSAccessibilityButtonRole;
118  roleMap[QAccessible::EditableText] = NSAccessibilityTextFieldRole;
119  roleMap[QAccessible::Link] = NSAccessibilityTextFieldRole;
120  roleMap[QAccessible::Indicator] = NSAccessibilityValueIndicatorRole;
121  roleMap[QAccessible::Splitter] = NSAccessibilitySplitGroupRole;
122  roleMap[QAccessible::List] = NSAccessibilityListRole;
123  roleMap[QAccessible::ListItem] = NSAccessibilityStaticTextRole;
124  roleMap[QAccessible::Cell] = NSAccessibilityStaticTextRole;
125 }
126 
127 /*
128  Returns a Mac accessibility role for the given interface, or
129  NSAccessibilityUnknownRole if no role mapping is found.
130 */
131 static NSString *macRoleForInterface(QAInterface interface)
132 {
133  const QAccessible::Role qtRole = interface.role();
134  QMacAccessibiltyRoleMap &roleMap = *qMacAccessibiltyRoleMap();
135 
136  if (roleMap.isEmpty())
137  populateRoleMap();
138 
139  MAC_ACCESSIBILTY_DEBUG() << "role for" << interface.object() << "interface role" << hex << qtRole;
140 
141  if (roleMap.contains(qtRole)) {
142  MAC_ACCESSIBILTY_DEBUG() << "return" << roleMap[qtRole];
143  return roleMap[qtRole];
144  }
145 
146  MAC_ACCESSIBILTY_DEBUG() << "return NSAccessibilityUnknownRole";
147  return NSAccessibilityUnknownRole;
148 }
149 
150 /*
151  Is the interface a QTabBar embedded in a QTabWidget?
152  (as opposed to a stand-alone tab bar)
153 */
154 static bool isEmbeddedTabBar(const QAInterface &interface)
155 {
156  QObject *object = interface.object();
157  if (interface.role() == QAccessible::PageTabList && object)
158  return (qobject_cast<QTabWidget *>(object->parent()));
159 
160  return false;
161 }
162 
163 static bool isInterfaceIgnored(QAInterface interface)
164 {
165  // Mac accessibility does not have an attribute that corresponds to the
166  // Invisible/Offscreen state. Use the ignore facility to disable them.
167  const QAccessible::State state = interface.state();
168  if (state & QAccessible::Invisible ||
169  state & QAccessible::Offscreen )
170  return false;
171 
172  // Hide QTabBars that has a QTabWidget parent (the QTabWidget handles the accessibility)
173  if (isEmbeddedTabBar(interface))
174  return false;
175 
176  if (QObject * const object = interface.object()) {
177  const QString className = QLatin1String(object->metaObject()->className());
178 
179  // Prevent VoiceOver from focusing on tool tips by ignoring those
180  // interfaces. Shifting VoiceOver focus to the tool tip is confusing
181  // and the contents of the tool tip is avalible through the description
182  // attribute anyway.
183  if (className == QLatin1String("QTipLabel"))
184  return false;
185  }
186 
187  // Hide interfaces with an unknown role. When developing it's often useful to disable
188  // this check to see all interfaces in the hierarchy.
189 #ifndef MAC_ACCESSIBILTY_DEVELOPER_MODE
190  return [macRoleForInterface(interface) isEqualToString: NSAccessibilityUnknownRole];
191 #else
192  return NO;
193 #endif
194 }
195 
197 
198 @implementation QT_MANGLE_NAMESPACE(QCocoaView) (Accessibility)
199 
200 - (BOOL)accessibilityIsIgnored
201 {
202  QT_PREPEND_NAMESPACE(QAInterface) interface = QT_PREPEND_NAMESPACE(interfaceForView)(self);
203  return isInterfaceIgnored(interface);
204 }
205 
206 - (NSArray *)accessibilityAttributeNames
207 {
208  QT_PREPEND_NAMESPACE(QAInterface) interface = QT_PREPEND_NAMESPACE(interfaceForView)(self);
209 
210  static NSArray *attributes = nil;
211  if (attributes == nil) {
212  attributes = [super accessibilityAttributeNames];
213 
214  }
215  return attributes;
216 }
217 
218 - (id)accessibilityAttributeValue:(NSString *)attribute
219 {
220  MAC_ACCESSIBILTY_DEBUG() << "accessibilityAttributeValue" << self <<
221  QT_PREPEND_NAMESPACE(QCFString)::toQString(reinterpret_cast<CFStringRef>(attribute));
222 
223  QT_PREPEND_NAMESPACE(QAInterface) interface = QT_PREPEND_NAMESPACE(interfaceForView)(self);
224 
225  // Switch on the attribute name and call the appropriate handler function.
226  // Pass the call on to the NSView class for attributes we don't handle.
227  if ([attribute isEqualToString:@"AXRole"]) {
228  return macRoleForInterface(interface);
229  } else {
230  return [super accessibilityAttributeValue:attribute];
231  }
232 }
233 
234 @end
235 
236 #endif // QT_MAC_USE_COCOA
237 
238 #endif // QT_NO_ACCESSIBILITY
239 
const struct __CFString * CFStringRef
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
static bool isEmbeddedTabBar(const QAInterface &interface)
Role
This enum defines the role of an accessible object.
Definition: qaccessible.h:188
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
The QString class provides a Unicode character string.
Definition: qstring.h:83
The QObject class is the base class of all Qt objects.
Definition: qobject.h:111
const char * className
Definition: qwizard.cpp:137
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
#define Q_GLOBAL_STATIC(TYPE, NAME)
Declares a global static variable with the given type and name.
Definition: qglobal.h:1968
#define QT_PREPEND_NAMESPACE(name)
This macro qualifies identifier with the full namespace.
Definition: qglobal.h:87
State state() const
static QAccessibleInterface * queryAccessibleInterface(QObject *)
If a QAccessibleInterface implementation exists for the given object, this function returns a pointer...
QObject * object() const
Role role() const
State
Definition: qaudio.h:59
#define QT_MANGLE_NAMESPACE(name)
Definition: qglobal.h:106
QObject * parent() const
Returns a pointer to the parent object.
Definition: qobject.h:273
const char * className() const
Returns the class name.
Definition: qobjectdefs.h:491
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
virtual const QMetaObject * metaObject() const
Returns a pointer to the meta-object of this object.
The QMap class is a template class that provides a skip-list-based dictionary.
Definition: qdatastream.h:67