Warning: That file was not part of the compilation database. It may have many parsing errors.
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 | #include "qaccessible.h" |
43 | |
44 | #ifndef QT_NO_ACCESSIBILITY |
45 | #include "qaccessible_mac_p.h" |
46 | #include "qhash.h" |
47 | #include "qset.h" |
48 | #include "qpointer.h" |
49 | #include "qapplication.h" |
50 | #include "qmainwindow.h" |
51 | #include "qtextdocument.h" |
52 | #include "qdebug.h" |
53 | #include "qabstractslider.h" |
54 | #include "qsplitter.h" |
55 | #include "qtabwidget.h" |
56 | #include "qlistview.h" |
57 | #include "qtableview.h" |
58 | #include "qdockwidget.h" |
59 | |
60 | #include <private/qt_mac_p.h> |
61 | #include <private/qwidget_p.h> |
62 | #include <CoreFoundation/CoreFoundation.h> |
63 | |
64 | QT_BEGIN_NAMESPACE |
65 | |
66 | /* |
67 | Set up platform defines. There is a one-to-one correspondence between the |
68 | Carbon and Cocoa roles and attributes, but the prefix and type changes. |
69 | */ |
70 | #ifdef QT_MAC_USE_COCOA |
71 | typedef NSString * const QAXRoleType; |
72 | #define QAXApplicationRole NSAccessibilityApplicationRole |
73 | #define QAXButtonRole NSAccessibilityButtonRole |
74 | #define QAXCancelAction NSAccessibilityCancelAction |
75 | #define QAXCheckBoxRole NSAccessibilityCheckBoxRole |
76 | #define QAXChildrenAttribute NSAccessibilityChildrenAttribute |
77 | #define QAXCloseButtonAttribute NSAccessibilityCloseButtonAttribute |
78 | #define QAXCloseButtonAttribute NSAccessibilityCloseButtonAttribute |
79 | #define QAXColumnRole NSAccessibilityColumnRole |
80 | #define QAXConfirmAction NSAccessibilityConfirmAction |
81 | #define QAXContentsAttribute NSAccessibilityContentsAttribute |
82 | #define QAXDecrementAction NSAccessibilityDecrementAction |
83 | #define QAXDecrementArrowSubrole NSAccessibilityDecrementArrowSubrole |
84 | #define QAXDecrementPageSubrole NSAccessibilityDecrementPageSubrole |
85 | #define QAXDescriptionAttribute NSAccessibilityDescriptionAttribute |
86 | #define QAXEnabledAttribute NSAccessibilityEnabledAttribute |
87 | #define QAXExpandedAttribute NSAccessibilityExpandedAttribute |
88 | #define QAXFocusedAttribute NSAccessibilityFocusedAttribute |
89 | #define QAXFocusedUIElementChangedNotification NSAccessibilityFocusedUIElementChangedNotification |
90 | #define QAXFocusedWindowChangedNotification NSAccessibilityFocusedWindowChangedNotification |
91 | #define QAXGroupRole NSAccessibilityGroupRole |
92 | #define QAXGrowAreaAttribute NSAccessibilityGrowAreaAttribute |
93 | #define QAXGrowAreaRole NSAccessibilityGrowAreaRole |
94 | #define QAXHelpAttribute NSAccessibilityHelpAttribute |
95 | #define QAXHorizontalOrientationValue NSAccessibilityHorizontalOrientationValue |
96 | #define QAXHorizontalScrollBarAttribute NSAccessibilityHorizontalScrollBarAttribute |
97 | #define QAXIncrementAction NSAccessibilityIncrementAction |
98 | #define QAXIncrementArrowSubrole NSAccessibilityIncrementArrowSubrole |
99 | #define QAXIncrementPageSubrole NSAccessibilityIncrementPageSubrole |
100 | #define QAXIncrementorRole NSAccessibilityIncrementorRole |
101 | #define QAXLinkedUIElementsAttribute NSAccessibilityLinkedUIElementsAttribute |
102 | #define QAXListRole NSAccessibilityListRole |
103 | #define QAXMainAttribute NSAccessibilityMainAttribute |
104 | #define QAXMaxValueAttribute NSAccessibilityMaxValueAttribute |
105 | #define QAXMenuBarRole NSAccessibilityMenuBarRole |
106 | #define QAXMenuButtonRole NSAccessibilityMenuButtonRole |
107 | #define QAXMenuClosedNotification NSAccessibilityMenuClosedNotification |
108 | #define QAXMenuItemRole NSAccessibilityMenuItemRole |
109 | #define QAXMenuOpenedNotification NSAccessibilityMenuOpenedNotification |
110 | #define QAXMenuRole NSAccessibilityMenuRole |
111 | #define QAXMinValueAttribute NSAccessibilityMinValueAttribute |
112 | #define QAXMinimizeButtonAttribute NSAccessibilityMinimizeButtonAttribute |
113 | #define QAXMinimizedAttribute NSAccessibilityMinimizedAttribute |
114 | #define QAXNextContentsAttribute NSAccessibilityNextContentsAttribute |
115 | #define QAXOrientationAttribute NSAccessibilityOrientationAttribute |
116 | #define QAXParentAttribute NSAccessibilityParentAttribute |
117 | #define QAXPickAction NSAccessibilityPickAction |
118 | #define QAXPopUpButtonRole NSAccessibilityPopUpButtonRole |
119 | #define QAXPositionAttribute NSAccessibilityPositionAttribute |
120 | #define QAXPressAction NSAccessibilityPressAction |
121 | #define QAXPreviousContentsAttribute NSAccessibilityPreviousContentsAttribute |
122 | #define QAXProgressIndicatorRole NSAccessibilityProgressIndicatorRole |
123 | #define QAXRadioButtonRole NSAccessibilityRadioButtonRole |
124 | #define QAXRoleAttribute NSAccessibilityRoleAttribute |
125 | #define QAXRoleDescriptionAttribute NSAccessibilityRoleDescriptionAttribute |
126 | #define QAXRowRole NSAccessibilityRowRole |
127 | #define QAXRowsAttribute NSAccessibilityRowsAttribute |
128 | #define QAXScrollAreaRole NSAccessibilityScrollAreaRole |
129 | #define QAXScrollBarRole NSAccessibilityScrollBarRole |
130 | #define QAXSelectedAttribute NSAccessibilitySelectedAttribute |
131 | #define QAXSelectedChildrenAttribute NSAccessibilitySelectedChildrenAttribute |
132 | #define QAXSelectedRowsAttribute NSAccessibilitySelectedRowsAttribute |
133 | #define QAXSizeAttribute NSAccessibilitySizeAttribute |
134 | #define QAXSliderRole NSAccessibilitySliderRole |
135 | #define QAXSplitGroupRole NSAccessibilitySplitGroupRole |
136 | #define QAXSplitterRole NSAccessibilitySplitterRole |
137 | #define QAXSplittersAttribute NSAccessibilitySplittersAttribute |
138 | #define QAXStaticTextRole NSAccessibilityStaticTextRole |
139 | #define QAXSubroleAttribute NSAccessibilitySubroleAttribute |
140 | #define QAXSubroleAttribute NSAccessibilitySubroleAttribute |
141 | #define QAXTabGroupRole NSAccessibilityTabGroupRole |
142 | #define QAXTableRole NSAccessibilityTableRole |
143 | #define QAXTabsAttribute NSAccessibilityTabsAttribute |
144 | #define QAXTextFieldRole NSAccessibilityTextFieldRole |
145 | #define QAXTitleAttribute NSAccessibilityTitleAttribute |
146 | #define QAXTitleUIElementAttribute NSAccessibilityTitleUIElementAttribute |
147 | #define QAXToolbarButtonAttribute NSAccessibilityToolbarButtonAttribute |
148 | #define QAXToolbarRole NSAccessibilityToolbarRole |
149 | #define QAXTopLevelUIElementAttribute NSAccessibilityTopLevelUIElementAttribute |
150 | #define QAXUnknownRole NSAccessibilityUnknownRole |
151 | #define QAXValueAttribute NSAccessibilityValueAttribute |
152 | #define QAXValueChangedNotification NSAccessibilityValueChangedNotification |
153 | #define QAXValueIndicatorRole NSAccessibilityValueIndicatorRole |
154 | #define QAXVerticalOrientationValue NSAccessibilityVerticalOrientationValue |
155 | #define QAXVerticalScrollBarAttribute NSAccessibilityVerticalScrollBarAttribute |
156 | #define QAXVisibleRowsAttribute NSAccessibilityVisibleRowsAttribute |
157 | #define QAXWindowAttribute NSAccessibilityWindowAttribute |
158 | #define QAXWindowCreatedNotification NSAccessibilityWindowCreatedNotification |
159 | #define QAXWindowMovedNotification NSAccessibilityWindowMovedNotification |
160 | #define QAXWindowRole NSAccessibilityWindowRole |
161 | #define QAXZoomButtonAttribute NSAccessibilityZoomButtonAttribute |
162 | #else |
163 | typedef CFStringRef const QAXRoleType; |
164 | #define QAXApplicationRole kAXApplicationRole |
165 | #define QAXButtonRole kAXButtonRole |
166 | #define QAXCancelAction kAXCancelAction |
167 | #define QAXCheckBoxRole kAXCheckBoxRole |
168 | #define QAXChildrenAttribute kAXChildrenAttribute |
169 | #define QAXCloseButtonAttribute kAXCloseButtonAttribute |
170 | #define QAXColumnRole kAXColumnRole |
171 | #define QAXConfirmAction kAXConfirmAction |
172 | #define QAXContentsAttribute kAXContentsAttribute |
173 | #define QAXDecrementAction kAXDecrementAction |
174 | #define QAXDecrementArrowSubrole kAXDecrementArrowSubrole |
175 | #define QAXDecrementPageSubrole kAXDecrementPageSubrole |
176 | #define QAXDescriptionAttribute kAXDescriptionAttribute |
177 | #define QAXEnabledAttribute kAXEnabledAttribute |
178 | #define QAXExpandedAttribute kAXExpandedAttribute |
179 | #define QAXFocusedAttribute kAXFocusedAttribute |
180 | #define QAXFocusedUIElementChangedNotification kAXFocusedUIElementChangedNotification |
181 | #define QAXFocusedWindowChangedNotification kAXFocusedWindowChangedNotification |
182 | #define QAXGroupRole kAXGroupRole |
183 | #define QAXGrowAreaAttribute kAXGrowAreaAttribute |
184 | #define QAXGrowAreaRole kAXGrowAreaRole |
185 | #define QAXHelpAttribute kAXHelpAttribute |
186 | #define QAXHorizontalOrientationValue kAXHorizontalOrientationValue |
187 | #define QAXHorizontalScrollBarAttribute kAXHorizontalScrollBarAttribute |
188 | #define QAXIncrementAction kAXIncrementAction |
189 | #define QAXIncrementArrowSubrole kAXIncrementArrowSubrole |
190 | #define QAXIncrementPageSubrole kAXIncrementPageSubrole |
191 | #define QAXIncrementorRole kAXIncrementorRole |
192 | #define QAXLinkedUIElementsAttribute kAXLinkedUIElementsAttribute |
193 | #define QAXListRole kAXListRole |
194 | #define QAXMainAttribute kAXMainAttribute |
195 | #define QAXMaxValueAttribute kAXMaxValueAttribute |
196 | #define QAXMenuBarRole kAXMenuBarRole |
197 | #define QAXMenuButtonRole kAXMenuButtonRole |
198 | #define QAXMenuClosedNotification kAXMenuClosedNotification |
199 | #define QAXMenuItemRole kAXMenuItemRole |
200 | #define QAXMenuOpenedNotification kAXMenuOpenedNotification |
201 | #define QAXMenuRole kAXMenuRole |
202 | #define QAXMinValueAttribute kAXMinValueAttribute |
203 | #define QAXMinimizeButtonAttribute kAXMinimizeButtonAttribute |
204 | #define QAXMinimizedAttribute kAXMinimizedAttribute |
205 | #define QAXNextContentsAttribute kAXNextContentsAttribute |
206 | #define QAXOrientationAttribute kAXOrientationAttribute |
207 | #define QAXParentAttribute kAXParentAttribute |
208 | #define QAXPickAction kAXPickAction |
209 | #define QAXPopUpButtonRole kAXPopUpButtonRole |
210 | #define QAXPositionAttribute kAXPositionAttribute |
211 | #define QAXPressAction kAXPressAction |
212 | #define QAXPreviousContentsAttribute kAXPreviousContentsAttribute |
213 | #define QAXProgressIndicatorRole kAXProgressIndicatorRole |
214 | #define QAXRadioButtonRole kAXRadioButtonRole |
215 | #define QAXRoleAttribute kAXRoleAttribute |
216 | #define QAXRoleDescriptionAttribute kAXRoleDescriptionAttribute |
217 | #define QAXRowRole kAXRowRole |
218 | #define QAXRowsAttribute kAXRowsAttribute |
219 | #define QAXScrollAreaRole kAXScrollAreaRole |
220 | #define QAXScrollBarRole kAXScrollBarRole |
221 | #define QAXSelectedAttribute kAXSelectedAttribute |
222 | #define QAXSelectedChildrenAttribute kAXSelectedChildrenAttribute |
223 | #define QAXSelectedRowsAttribute kAXSelectedRowsAttribute |
224 | #define QAXSizeAttribute kAXSizeAttribute |
225 | #define QAXSliderRole kAXSliderRole |
226 | #define QAXSplitGroupRole kAXSplitGroupRole |
227 | #define QAXSplitterRole kAXSplitterRole |
228 | #define QAXSplittersAttribute kAXSplittersAttribute |
229 | #define QAXStaticTextRole kAXStaticTextRole |
230 | #define QAXSubroleAttribute kAXSubroleAttribute |
231 | #define QAXTabGroupRole kAXTabGroupRole |
232 | #define QAXTableRole kAXTableRole |
233 | #define QAXTabsAttribute kAXTabsAttribute |
234 | #define QAXTextFieldRole kAXTextFieldRole |
235 | #define QAXTitleAttribute kAXTitleAttribute |
236 | #define QAXTitleUIElementAttribute kAXTitleUIElementAttribute |
237 | #define QAXToolbarButtonAttribute kAXToolbarButtonAttribute |
238 | #define QAXToolbarRole kAXToolbarRole |
239 | #define QAXTopLevelUIElementAttribute kAXTopLevelUIElementAttribute |
240 | #define QAXUnknownRole kAXUnknownRole |
241 | #define QAXValueAttribute kAXValueAttribute |
242 | #define QAXValueChangedNotification kAXValueChangedNotification |
243 | #define QAXValueIndicatorRole kAXValueIndicatorRole |
244 | #define QAXVerticalOrientationValue kAXVerticalOrientationValue |
245 | #define QAXVerticalScrollBarAttribute kAXVerticalScrollBarAttribute |
246 | #define QAXVisibleRowsAttribute kAXVisibleRowsAttribute |
247 | #define QAXWindowAttribute kAXWindowAttribute |
248 | #define QAXWindowCreatedNotification kAXWindowCreatedNotification |
249 | #define QAXWindowMovedNotification kAXWindowMovedNotification |
250 | #define QAXWindowRole kAXWindowRole |
251 | #define QAXZoomButtonAttribute kAXZoomButtonAttribute |
252 | #endif |
253 | |
254 | |
255 | /***************************************************************************** |
256 | Externals |
257 | *****************************************************************************/ |
258 | extern bool qt_mac_is_macsheet(const QWidget *w); //qwidget_mac.cpp |
259 | extern bool qt_mac_is_macdrawer(const QWidget *w); //qwidget_mac.cpp |
260 | |
261 | /***************************************************************************** |
262 | QAccessible Bindings |
263 | *****************************************************************************/ |
264 | //hardcoded bindings between control info and (known) QWidgets |
265 | struct QAccessibleTextBinding { |
266 | int qt; |
267 | QAXRoleType mac; |
268 | bool settable; |
269 | } text_bindings[][10] = { |
270 | { { QAccessible::MenuItem, QAXMenuItemRole, false }, |
271 | { -1, 0, false } |
272 | }, |
273 | { { QAccessible::MenuBar, QAXMenuBarRole, false }, |
274 | { -1, 0, false } |
275 | }, |
276 | { { QAccessible::ScrollBar, QAXScrollBarRole, false }, |
277 | { -1, 0, false } |
278 | }, |
279 | { { QAccessible::Grip, QAXGrowAreaRole, false }, |
280 | { -1, 0, false } |
281 | }, |
282 | { { QAccessible::Window, QAXWindowRole, false }, |
283 | { -1, 0, false } |
284 | }, |
285 | { { QAccessible::Dialog, QAXWindowRole, false }, |
286 | { -1, 0, false } |
287 | }, |
288 | { { QAccessible::AlertMessage, QAXWindowRole, false }, |
289 | { -1, 0, false } |
290 | }, |
291 | { { QAccessible::ToolTip, QAXWindowRole, false }, |
292 | { -1, 0, false } |
293 | }, |
294 | { { QAccessible::HelpBalloon, QAXWindowRole, false }, |
295 | { -1, 0, false } |
296 | }, |
297 | { { QAccessible::PopupMenu, QAXMenuRole, false }, |
298 | { -1, 0, false } |
299 | }, |
300 | { { QAccessible::Application, QAXApplicationRole, false }, |
301 | { -1, 0, false } |
302 | }, |
303 | { { QAccessible::Pane, QAXGroupRole, false }, |
304 | { -1, 0, false } |
305 | }, |
306 | { { QAccessible::Grouping, QAXGroupRole, false }, |
307 | { -1, 0, false } |
308 | }, |
309 | { { QAccessible::Separator, QAXSplitterRole, false }, |
310 | { -1, 0, false } |
311 | }, |
312 | { { QAccessible::ToolBar, QAXToolbarRole, false }, |
313 | { -1, 0, false } |
314 | }, |
315 | { { QAccessible::PageTab, QAXRadioButtonRole, false }, |
316 | { -1, 0, false } |
317 | }, |
318 | { { QAccessible::ButtonMenu, QAXMenuButtonRole, false }, |
319 | { -1, 0, false } |
320 | }, |
321 | { { QAccessible::ButtonDropDown, QAXPopUpButtonRole, false }, |
322 | { -1, 0, false } |
323 | }, |
324 | { { QAccessible::SpinBox, QAXIncrementorRole, false }, |
325 | { -1, 0, false } |
326 | }, |
327 | { { QAccessible::Slider, QAXSliderRole, false }, |
328 | { -1, 0, false } |
329 | }, |
330 | { { QAccessible::ProgressBar, QAXProgressIndicatorRole, false }, |
331 | { -1, 0, false } |
332 | }, |
333 | { { QAccessible::ComboBox, QAXPopUpButtonRole, false }, |
334 | { -1, 0, false } |
335 | }, |
336 | { { QAccessible::RadioButton, QAXRadioButtonRole, false }, |
337 | { -1, 0, false } |
338 | }, |
339 | { { QAccessible::CheckBox, QAXCheckBoxRole, false }, |
340 | { -1, 0, false } |
341 | }, |
342 | { { QAccessible::StaticText, QAXStaticTextRole, false }, |
343 | { QAccessible::Name, QAXValueAttribute, false }, |
344 | { -1, 0, false } |
345 | }, |
346 | { { QAccessible::Table, QAXTableRole, false }, |
347 | { -1, 0, false } |
348 | }, |
349 | { { QAccessible::StatusBar, QAXStaticTextRole, false }, |
350 | { -1, 0, false } |
351 | }, |
352 | { { QAccessible::Column, QAXColumnRole, false }, |
353 | { -1, 0, false } |
354 | }, |
355 | { { QAccessible::ColumnHeader, QAXColumnRole, false }, |
356 | { -1, 0, false } |
357 | }, |
358 | { { QAccessible::Row, QAXRowRole, false }, |
359 | { -1, 0, false } |
360 | }, |
361 | { { QAccessible::RowHeader, QAXRowRole, false }, |
362 | { -1, 0, false } |
363 | }, |
364 | { { QAccessible::Cell, QAXTextFieldRole, false }, |
365 | { -1, 0, false } |
366 | }, |
367 | { { QAccessible::PushButton, QAXButtonRole, false }, |
368 | { -1, 0, false } |
369 | }, |
370 | { { QAccessible::EditableText, QAXTextFieldRole, true }, |
371 | { -1, 0, false } |
372 | }, |
373 | { { QAccessible::Link, QAXTextFieldRole, false }, |
374 | { -1, 0, false } |
375 | }, |
376 | { { QAccessible::Indicator, QAXValueIndicatorRole, false }, |
377 | { -1, 0, false } |
378 | }, |
379 | { { QAccessible::Splitter, QAXSplitGroupRole, false }, |
380 | { -1, 0, false } |
381 | }, |
382 | { { QAccessible::List, QAXListRole, false }, |
383 | { -1, 0, false } |
384 | }, |
385 | { { QAccessible::ListItem, QAXStaticTextRole, false }, |
386 | { -1, 0, false } |
387 | }, |
388 | { { QAccessible::Cell, QAXStaticTextRole, false }, |
389 | { -1, 0, false } |
390 | }, |
391 | { { -1, 0, false } } |
392 | }; |
393 | |
394 | class QAInterface; |
395 | static CFStringRef macRole(const QAInterface &interface); |
396 | |
397 | QDebug operator<<(QDebug debug, const QAInterface &interface) |
398 | { |
399 | if (interface.isValid() == false) |
400 | debug << "invalid interface"; |
401 | else |
402 | debug << interface.object() << "id"<< interface.id() << "role"<< hex << interface.role(); |
403 | return debug; |
404 | } |
405 | |
406 | // The root of the Qt accessible hiearchy. |
407 | static QObject *rootObject = 0; |
408 | |
409 | |
410 | bool QAInterface::operator==(const QAInterface &other) const |
411 | { |
412 | if (isValid() == false || other.isValid() == false) |
413 | return (isValid() && other.isValid()); |
414 | |
415 | // walk up the parent chain, comparing child indexes, until we reach |
416 | // an interface that has a QObject. |
417 | QAInterface currentThis = *this; |
418 | QAInterface currentOther = other; |
419 | |
420 | while (currentThis.object() == 0) { |
421 | if (currentOther.object() != 0) |
422 | return false; |
423 | |
424 | // fail if the child indexes in the two hirearchies don't match. |
425 | if (currentThis.parent().indexOfChild(currentThis) != |
426 | currentOther.parent().indexOfChild(currentOther)) |
427 | return false; |
428 | |
429 | currentThis = currentThis.parent(); |
430 | currentOther = currentOther.parent(); |
431 | } |
432 | |
433 | return (currentThis.object() == currentOther.object() && currentThis.id() == currentOther.id()); |
434 | } |
435 | |
436 | bool QAInterface::operator!=(const QAInterface &other) const |
437 | { |
438 | return !operator==(other); |
439 | } |
440 | |
441 | uint qHash(const QAInterface &item) |
442 | { |
443 | if (item.isValid()) |
444 | return qHash(item.object()) + qHash(item.id()); |
445 | else |
446 | return qHash(item.cachedObject()) + qHash(item.id()); |
447 | } |
448 | |
449 | QAInterface QAInterface::navigate(RelationFlag relation, int entry) const |
450 | { |
451 | if (!checkValid()) |
452 | return QAInterface(); |
453 | |
454 | // On a QAccessibleInterface that handles its own children we can short-circut |
455 | // the navigation if this QAInterface refers to one of the children: |
456 | if (child != 0) { |
457 | // The Ancestor interface will always be the same QAccessibleInterface with |
458 | // a child value of 0. |
459 | if (relation == QAccessible::Ancestor) |
460 | return QAInterface(*this, 0); |
461 | |
462 | // The child hiearchy is only one level deep, so navigating to a child |
463 | // of a child is not possible. |
464 | if (relation == QAccessible::Child) { |
465 | return QAInterface(); |
466 | } |
467 | } |
468 | QAccessibleInterface *child_iface = 0; |
469 | |
470 | const int status = base.interface->navigate(relation, entry, &child_iface); |
471 | |
472 | if (status == -1) |
473 | return QAInterface(); // not found; |
474 | |
475 | // Check if target is a child of this interface. |
476 | if (!child_iface) { |
477 | return QAInterface(*this, status); |
478 | } else { |
479 | // Target is child_iface or a child of that (status decides). |
480 | return QAInterface(child_iface, status); |
481 | } |
482 | } |
483 | |
484 | QAElement::QAElement() |
485 | :elementRef(0) |
486 | {} |
487 | |
488 | QAElement::QAElement(AXUIElementRef elementRef) |
489 | :elementRef(elementRef) |
490 | { |
491 | if (elementRef != 0) { |
492 | CFRetain(elementRef); |
493 | CFRetain(object()); |
494 | } |
495 | } |
496 | |
497 | QAElement::QAElement(const QAElement &element) |
498 | :elementRef(element.elementRef) |
499 | { |
500 | if (elementRef != 0) { |
501 | CFRetain(elementRef); |
502 | CFRetain(object()); |
503 | } |
504 | } |
505 | |
506 | QAElement::QAElement(HIObjectRef object, int child) |
507 | { |
508 | #ifndef QT_MAC_USE_COCOA |
509 | if (object == 0) { |
510 | elementRef = 0; // Create invalid QAElement. |
511 | } else { |
512 | elementRef = AXUIElementCreateWithHIObjectAndIdentifier(object, child); |
513 | CFRetain(object); |
514 | } |
515 | #else |
516 | Q_UNUSED(object); |
517 | Q_UNUSED(child); |
518 | #endif |
519 | } |
520 | |
521 | QAElement::~QAElement() |
522 | { |
523 | if (elementRef != 0) { |
524 | CFRelease(object()); |
525 | CFRelease(elementRef); |
526 | } |
527 | } |
528 | |
529 | void QAElement::operator=(const QAElement &other) |
530 | { |
531 | if (*this == other) |
532 | return; |
533 | |
534 | if (elementRef != 0) { |
535 | CFRelease(object()); |
536 | CFRelease(elementRef); |
537 | } |
538 | |
539 | elementRef = other.elementRef; |
540 | |
541 | if (elementRef != 0) { |
542 | CFRetain(elementRef); |
543 | CFRetain(object()); |
544 | } |
545 | } |
546 | |
547 | bool QAElement::operator==(const QAElement &other) const |
548 | { |
549 | if (elementRef == 0 || other.elementRef == 0) |
550 | return (elementRef == other.elementRef); |
551 | |
552 | return CFEqual(elementRef, other.elementRef); |
553 | } |
554 | |
555 | uint qHash(QAElement element) |
556 | { |
557 | return qHash(element.object()) + qHash(element.id()); |
558 | } |
559 | |
560 | #ifndef QT_MAC_USE_COCOA |
561 | static QInterfaceFactory *createFactory(const QAInterface &interface); |
562 | #endif |
563 | Q_GLOBAL_STATIC(QAccessibleHierarchyManager, accessibleHierarchyManager); |
564 | |
565 | /* |
566 | Reomves all accessibility info accosiated with the sender object. |
567 | */ |
568 | void QAccessibleHierarchyManager::objectDestroyed(QObject *object) |
569 | { |
570 | HIObjectRef hiObject = qobjectHiobjectHash.value(object); |
571 | delete qobjectElementHash.value(object); |
572 | qobjectElementHash.remove(object); |
573 | hiobjectInterfaceHash.remove(hiObject); |
574 | } |
575 | |
576 | /* |
577 | Removes all stored items. |
578 | */ |
579 | void QAccessibleHierarchyManager::reset() |
580 | { |
581 | qDeleteAll(qobjectElementHash); |
582 | qobjectElementHash.clear(); |
583 | hiobjectInterfaceHash.clear(); |
584 | qobjectHiobjectHash.clear(); |
585 | } |
586 | |
587 | QAccessibleHierarchyManager *QAccessibleHierarchyManager::instance() |
588 | { |
589 | return accessibleHierarchyManager(); |
590 | } |
591 | |
592 | #ifndef QT_MAC_USE_COCOA |
593 | static bool isItemView(const QAInterface &interface) |
594 | { |
595 | QObject *object = interface.object(); |
596 | return (interface.role() == QAccessible::List || interface.role() == QAccessible::Table |
597 | || (object && qobject_cast<QAbstractItemView *>(interface.object())) |
598 | || (object && object->objectName() == QLatin1String("qt_scrollarea_viewport") |
599 | && qobject_cast<QAbstractItemView *>(object->parent()))); |
600 | } |
601 | #endif |
602 | |
603 | static bool isTabWidget(const QAInterface &interface) |
604 | { |
605 | if (QObject *object = interface.object()) |
606 | return (object->inherits("QTabWidget") && interface.id() == 0); |
607 | return false; |
608 | } |
609 | |
610 | static bool isStandaloneTabBar(const QAInterface &interface) |
611 | { |
612 | QObject *object = interface.object(); |
613 | if (interface.role() == QAccessible::PageTabList && object) |
614 | return (qobject_cast<QTabWidget *>(object->parent()) == 0); |
615 | |
616 | return false; |
617 | } |
618 | |
619 | static bool isEmbeddedTabBar(const QAInterface &interface) |
620 | { |
621 | QObject *object = interface.object(); |
622 | if (interface.role() == QAccessible::PageTabList && object) |
623 | return (qobject_cast<QTabWidget *>(object->parent())); |
624 | |
625 | return false; |
626 | } |
627 | |
628 | /* |
629 | Decides if a QAInterface is interesting from an accessibility users point of view. |
630 | */ |
631 | bool isItInteresting(const QAInterface &interface) |
632 | { |
633 | // Mac accessibility does not have an attribute that corresponds to the Invisible/Offscreen |
634 | // state, so we disable the interface here. |
635 | const QAccessible::State state = interface.state(); |
636 | if (state & QAccessible::Invisible || |
637 | state & QAccessible::Offscreen ) |
638 | return false; |
639 | |
640 | const QAccessible::Role role = interface.role(); |
641 | |
642 | if (QObject * const object = interface.object()) { |
643 | const QString className = QLatin1String(object->metaObject()->className()); |
644 | |
645 | // VoiceOver focusing on tool tips can be confusing. The contents of the |
646 | // tool tip is avalible through the description attribute anyway, so |
647 | // we disable accessibility for tool tips. |
648 | if (className == QLatin1String("QTipLabel")) |
649 | return false; |
650 | |
651 | // Hide TabBars that has a QTabWidget parent (the tab widget handles the accessibility) |
652 | if (isEmbeddedTabBar(interface)) |
653 | return false; |
654 | |
655 | // Hide docked dockwidgets. ### causes infinitie loop in the apple accessibility code. |
656 | /* if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(object)) { |
657 | if (dockWidget->isFloating() == false) |
658 | return false; |
659 | } |
660 | */ |
661 | } |
662 | |
663 | // Client is a generic role returned by plain QWidgets or other |
664 | // widgets that does not have separate QAccessible interface, such |
665 | // as the TabWidget. Return false unless macRole gives the interface |
666 | // a special role. |
667 | if (role == QAccessible::Client && macRole(interface) == CFStringRef(QAXUnknownRole)) |
668 | return false; |
669 | |
670 | // Some roles are not interesting: |
671 | if (role == QAccessible::Border || // QFrame |
672 | role == QAccessible::Application || // We use the system-provided application element. |
673 | role == QAccessible::MenuItem) // The system also provides the menu items. |
674 | return false; |
675 | |
676 | // It is probably better to access the toolbar buttons directly than having |
677 | // to navigate through the toolbar. |
678 | if (role == QAccessible::ToolBar) |
679 | return false; |
680 | |
681 | return true; |
682 | } |
683 | |
684 | QAElement QAccessibleHierarchyManager::registerInterface(QObject *object, int child) |
685 | { |
686 | #ifndef QT_MAC_USE_COCOA |
687 | return registerInterface(QAInterface(QAccessible::queryAccessibleInterface(object), child)); |
688 | #else |
689 | Q_UNUSED(object); |
690 | Q_UNUSED(child); |
691 | return QAElement(); |
692 | #endif |
693 | } |
694 | |
695 | /* |
696 | Creates a QAXUIelement that corresponds to the given QAInterface. |
697 | */ |
698 | QAElement QAccessibleHierarchyManager::registerInterface(const QAInterface &interface) |
699 | { |
700 | #ifndef QT_MAC_USE_COCOA |
701 | if (interface.isValid() == false) |
702 | return QAElement(); |
703 | QAInterface objectInterface = interface.objectInterface(); |
704 | |
705 | QObject * qobject = objectInterface.object(); |
706 | HIObjectRef hiobject = objectInterface.hiObject(); |
707 | if (qobject == 0 || hiobject == 0) |
708 | return QAElement(); |
709 | |
710 | if (qobjectElementHash.contains(qobject) == false) { |
711 | registerInterface(qobject, hiobject, createFactory(interface)); |
712 | HIObjectSetAccessibilityIgnored(hiobject, !isItInteresting(interface)); |
713 | } |
714 | |
715 | return QAElement(hiobject, interface.id()); |
716 | #else |
717 | Q_UNUSED(interface); |
718 | return QAElement(); |
719 | #endif |
720 | } |
721 | |
722 | #ifndef QT_MAC_USE_COCOA |
723 | #include "qaccessible_mac_carbon.cpp" |
724 | #endif |
725 | |
726 | void QAccessibleHierarchyManager::registerInterface(QObject * qobject, HIObjectRef hiobject, QInterfaceFactory *interfaceFactory) |
727 | { |
728 | #ifndef QT_MAC_USE_COCOA |
729 | if (qobjectElementHash.contains(qobject) == false) { |
730 | qobjectElementHash.insert(qobject, interfaceFactory); |
731 | qobjectHiobjectHash.insert(qobject, hiobject); |
732 | connect(qobject, SIGNAL(destroyed(QObject *)), SLOT(objectDestroyed(QObject *))); |
733 | } |
734 | |
735 | if (hiobjectInterfaceHash.contains(hiobject) == false) { |
736 | hiobjectInterfaceHash.insert(hiobject, interfaceFactory); |
737 | installAcessibilityEventHandler(hiobject); |
738 | } |
739 | #else |
740 | Q_UNUSED(qobject); |
741 | Q_UNUSED(hiobject); |
742 | Q_UNUSED(interfaceFactory); |
743 | #endif |
744 | } |
745 | |
746 | void QAccessibleHierarchyManager::registerChildren(const QAInterface &interface) |
747 | { |
748 | QObject * const object = interface.object(); |
749 | if (object == 0) |
750 | return; |
751 | |
752 | QInterfaceFactory *interfaceFactory = qobjectElementHash.value(object); |
753 | |
754 | if (interfaceFactory == 0) |
755 | return; |
756 | |
757 | interfaceFactory->registerChildren(); |
758 | } |
759 | |
760 | QAInterface QAccessibleHierarchyManager::lookup(const AXUIElementRef &element) |
761 | { |
762 | if (element == 0) |
763 | return QAInterface(); |
764 | #ifndef QT_MAC_USE_COCOA |
765 | HIObjectRef hiObject = AXUIElementGetHIObject(element); |
766 | |
767 | QInterfaceFactory *factory = hiobjectInterfaceHash.value(hiObject); |
768 | if (factory == 0) { |
769 | return QAInterface(); |
770 | } |
771 | |
772 | UInt64 id; |
773 | AXUIElementGetIdentifier(element, &id); |
774 | return factory->interface(id); |
775 | #else |
776 | return QAInterface(); |
777 | #endif |
778 | } |
779 | |
780 | QAInterface QAccessibleHierarchyManager::lookup(const QAElement &element) |
781 | { |
782 | return lookup(element.element()); |
783 | } |
784 | |
785 | QAElement QAccessibleHierarchyManager::lookup(const QAInterface &interface) |
786 | { |
787 | if (interface.isValid() == false) |
788 | return QAElement(); |
789 | |
790 | QInterfaceFactory *factory = qobjectElementHash.value(interface.objectInterface().object()); |
791 | if (factory == 0) |
792 | return QAElement(); |
793 | |
794 | return factory->element(interface); |
795 | } |
796 | |
797 | QAElement QAccessibleHierarchyManager::lookup(QObject * const object, int id) |
798 | { |
799 | QInterfaceFactory *factory = qobjectElementHash.value(object); |
800 | if (factory == 0) |
801 | return QAElement(); |
802 | |
803 | return factory->element(id); |
804 | } |
805 | |
806 | /* |
807 | Standard interface mapping, return the stored interface |
808 | or HIObjectRef, and there is an one-to-one mapping between |
809 | the identifier and child. |
810 | */ |
811 | class QStandardInterfaceFactory : public QInterfaceFactory |
812 | { |
813 | public: |
814 | QStandardInterfaceFactory(const QAInterface &interface) |
815 | : m_interface(interface), object(interface.hiObject()) |
816 | { |
817 | CFRetain(object); |
818 | } |
819 | |
820 | ~QStandardInterfaceFactory() |
821 | { |
822 | CFRelease(object); |
823 | } |
824 | |
825 | |
826 | QAInterface interface(UInt64 identifier) |
827 | { |
828 | const int child = identifier; |
829 | return QAInterface(m_interface, child); |
830 | } |
831 | |
832 | QAElement element(int id) |
833 | { |
834 | return QAElement(object, id); |
835 | } |
836 | |
837 | QAElement element(const QAInterface &interface) |
838 | { |
839 | if (interface.object() == 0) |
840 | return QAElement(); |
841 | return QAElement(object, interface.id()); |
842 | } |
843 | |
844 | void registerChildren() |
845 | { |
846 | const int childCount = m_interface.childCount(); |
847 | for (int i = 1; i <= childCount; ++i) { |
848 | accessibleHierarchyManager()->registerInterface(m_interface.navigate(QAccessible::Child, i)); |
849 | } |
850 | } |
851 | |
852 | private: |
853 | QAInterface m_interface; |
854 | HIObjectRef object; |
855 | }; |
856 | |
857 | /* |
858 | Interface mapping where that creates one HIObject for each interface child. |
859 | */ |
860 | class QMultipleHIObjectFactory : public QInterfaceFactory |
861 | { |
862 | public: |
863 | QMultipleHIObjectFactory(const QAInterface &interface) |
864 | : m_interface(interface) |
865 | { } |
866 | |
867 | ~QMultipleHIObjectFactory() |
868 | { |
869 | foreach (HIObjectRef object, objects) { |
870 | CFRelease(object); |
871 | } |
872 | } |
873 | |
874 | QAInterface interface(UInt64 identifier) |
875 | { |
876 | const int child = identifier; |
877 | return QAInterface(m_interface, child); |
878 | } |
879 | |
880 | QAElement element(int child) |
881 | { |
882 | if (child == 0) |
883 | return QAElement(m_interface.hiObject(), 0); |
884 | |
885 | if (child > objects.count()) |
886 | return QAElement(); |
887 | |
888 | return QAElement(objects.at(child - 1), child); |
889 | } |
890 | |
891 | void registerChildren() |
892 | { |
893 | #ifndef QT_MAC_USE_COCOA |
894 | const int childCount = m_interface.childCount(); |
895 | for (int i = 1; i <= childCount; ++i) { |
896 | HIObjectRef hiobject; |
897 | HIObjectCreate(kObjectQtAccessibility, 0, &hiobject); |
898 | objects.append(hiobject); |
899 | accessibleHierarchyManager()->registerInterface(m_interface.object(), hiobject, this); |
900 | HIObjectSetAccessibilityIgnored(hiobject, !isItInteresting(m_interface.navigate(QAccessible::Child, i))); |
901 | } |
902 | #endif |
903 | } |
904 | |
905 | private: |
906 | QAInterface m_interface; |
907 | QList<HIObjectRef> objects; |
908 | }; |
909 | |
910 | class QItemViewInterfaceFactory : public QInterfaceFactory |
911 | { |
912 | public: |
913 | QItemViewInterfaceFactory(const QAInterface &interface) |
914 | : m_interface(interface), object(interface.hiObject()) |
915 | { |
916 | CFRetain(object); |
917 | columnCount = 0; |
918 | if (QTableView * tableView = qobject_cast<QTableView *>(interface.parent().object())) { |
919 | if (tableView->model()) |
920 | columnCount = tableView->model()->columnCount(); |
921 | if (tableView->verticalHeader()) |
922 | ++columnCount; |
923 | } |
924 | } |
925 | |
926 | ~QItemViewInterfaceFactory() |
927 | { |
928 | CFRelease(object); |
929 | } |
930 | |
931 | QAInterface interface(UInt64 identifier) |
932 | { |
933 | if (identifier == 0) |
934 | return m_interface; |
935 | |
936 | if (m_interface.role() == QAccessible::List) |
937 | return m_interface.childAt(identifier); |
938 | |
939 | if (m_interface.role() == QAccessible::Table) { |
940 | const int index = identifier; |
941 | if (index == 0) |
942 | return m_interface; // return the item view interface. |
943 | |
944 | const int rowIndex = (index - 1) / (columnCount + 1); |
945 | const int cellIndex = (index - 1) % (columnCount + 1); |
946 | /* |
947 | qDebug() << "index" << index; |
948 | qDebug() << "rowIndex" << rowIndex; |
949 | qDebug() << "cellIndex" << cellIndex; |
950 | */ |
951 | const QAInterface rowInterface = m_interface.childAt(rowIndex + 1); |
952 | |
953 | if ((cellIndex) == 0) // Is it a row? |
954 | return rowInterface; |
955 | else { |
956 | return rowInterface.childAt(cellIndex); |
957 | } |
958 | } |
959 | |
960 | return QAInterface(); |
961 | } |
962 | |
963 | QAElement element(int id) |
964 | { |
965 | if (id != 0) { |
966 | return QAElement(); |
967 | } |
968 | return QAElement(object, 0); |
969 | } |
970 | |
971 | QAElement element(const QAInterface &interface) |
972 | { |
973 | if (interface.object() && interface.object() == m_interface.object()) { |
974 | return QAElement(object, 0); |
975 | } else if (m_interface.role() == QAccessible::List) { |
976 | if (interface.parent().object() && interface.parent().object() == m_interface.object()) |
977 | return QAElement(object, m_interface.indexOfChild(interface)); |
978 | } else if (m_interface.role() == QAccessible::Table) { |
979 | QAInterface currentInterface = interface; |
980 | int index = 0; |
981 | |
982 | while (currentInterface.isValid() && currentInterface.object() == 0) { |
983 | const QAInterface parentInterface = currentInterface.parent(); |
984 | /* |
985 | qDebug() << "current index" << index; |
986 | qDebug() << "current interface" << interface; |
987 | |
988 | qDebug() << "parent interface" << parentInterface; |
989 | qDebug() << "grandparent interface" << parentInterface.parent(); |
990 | qDebug() << "childCount" << interface.childCount(); |
991 | qDebug() << "index of child" << parentInterface.indexOfChild(currentInterface); |
992 | */ |
993 | index += ((parentInterface.indexOfChild(currentInterface) - 1) * (currentInterface.childCount() + 1)) + 1; |
994 | currentInterface = parentInterface; |
995 | // qDebug() << "new current interface" << currentInterface; |
996 | } |
997 | if (currentInterface.object() == m_interface.object()) |
998 | return QAElement(object, index); |
999 | |
1000 | |
1001 | } |
1002 | return QAElement(); |
1003 | } |
1004 | |
1005 | void registerChildren() |
1006 | { |
1007 | // Item view child interfraces don't have their own qobjects, so there is nothing to register here. |
1008 | } |
1009 | |
1010 | private: |
1011 | QAInterface m_interface; |
1012 | HIObjectRef object; |
1013 | int columnCount; // for table views; |
1014 | }; |
1015 | |
1016 | #ifndef QT_MAC_USE_COCOA |
1017 | static bool managesChildren(const QAInterface &interface) |
1018 | { |
1019 | return (interface.childCount() > 0 && interface.childAt(1).id() > 0); |
1020 | } |
1021 | |
1022 | static QInterfaceFactory *createFactory(const QAInterface &interface) |
1023 | { |
1024 | if (isItemView(interface)) { |
1025 | return new QItemViewInterfaceFactory(interface); |
1026 | } if (managesChildren(interface)) { |
1027 | return new QMultipleHIObjectFactory(interface); |
1028 | } |
1029 | |
1030 | return new QStandardInterfaceFactory(interface); |
1031 | } |
1032 | #endif |
1033 | |
1034 | QList<QAElement> lookup(const QList<QAInterface> &interfaces) |
1035 | { |
1036 | QList<QAElement> elements; |
1037 | foreach (const QAInterface &interface, interfaces) |
1038 | if (interface.isValid()) { |
1039 | const QAElement element = accessibleHierarchyManager()->lookup(interface); |
1040 | if (element.isValid()) |
1041 | elements.append(element); |
1042 | } |
1043 | return elements; |
1044 | } |
1045 | |
1046 | // Debug output helpers: |
1047 | /* |
1048 | static QString nameForEventKind(UInt32 kind) |
1049 | { |
1050 | switch(kind) { |
1051 | case kEventAccessibleGetChildAtPoint: return QString("GetChildAtPoint"); break; |
1052 | case kEventAccessibleGetAllAttributeNames: return QString("GetAllAttributeNames"); break; |
1053 | case kEventAccessibleGetNamedAttribute: return QString("GetNamedAttribute"); break; |
1054 | case kEventAccessibleSetNamedAttribute: return QString("SetNamedAttribute"); break; |
1055 | case kEventAccessibleGetAllActionNames: return QString("GetAllActionNames"); break; |
1056 | case kEventAccessibleGetFocusedChild: return QString("GetFocusedChild"); break; |
1057 | default: |
1058 | return QString("Unknown accessibility event type: %1").arg(kind); |
1059 | break; |
1060 | }; |
1061 | } |
1062 | */ |
1063 | #ifndef QT_MAC_USE_COCOA |
1064 | static bool qt_mac_append_cf_uniq(CFMutableArrayRef array, CFTypeRef value) |
1065 | { |
1066 | if (value == 0) |
1067 | return false; |
1068 | |
1069 | CFRange range; |
1070 | range.location = 0; |
1071 | range.length = CFArrayGetCount(array); |
1072 | if(!CFArrayContainsValue(array, range, value)) { |
1073 | CFArrayAppendValue(array, value); |
1074 | return true; |
1075 | } |
1076 | return false; |
1077 | } |
1078 | |
1079 | static OSStatus setAttributeValue(EventRef event, const QList<QAElement> &elements) |
1080 | { |
1081 | CFMutableArrayRef array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); |
1082 | foreach (const QAElement &element, elements) { |
1083 | if (element.isValid()) |
1084 | CFArrayAppendValue(array, element.element()); |
1085 | } |
1086 | |
1087 | const OSStatus err = SetEventParameter(event, kEventParamAccessibleAttributeValue, |
1088 | typeCFTypeRef, sizeof(array), &array); |
1089 | CFRelease(array); |
1090 | return err; |
1091 | } |
1092 | #endif //QT_MAC_USE_COCOA |
1093 | |
1094 | /* |
1095 | Gets the AccessibleObject parameter from an event. |
1096 | */ |
1097 | static inline AXUIElementRef getAccessibleObjectParameter(EventRef event) |
1098 | { |
1099 | AXUIElementRef element; |
1100 | GetEventParameter(event, kEventParamAccessibleObject, typeCFTypeRef, 0, |
1101 | sizeof(element), 0, &element); |
1102 | return element; |
1103 | } |
1104 | |
1105 | /* |
1106 | The application event handler makes sure that all top-level qt windows are registered |
1107 | before any accessibility events are handeled. |
1108 | */ |
1109 | #ifndef QT_MAC_USE_COCOA |
1110 | static OSStatus applicationEventHandler(EventHandlerCallRef next_ref, EventRef event, void *) |
1111 | { |
1112 | QAInterface rootInterface(QAccessible::queryAccessibleInterface(rootObject ? rootObject : qApp), 0); |
1113 | accessibleHierarchyManager()->registerChildren(rootInterface); |
1114 | |
1115 | return CallNextEventHandler(next_ref, event); |
1116 | } |
1117 | |
1118 | /* |
1119 | Returns the value for element by combining the QAccessibility::Checked and |
1120 | QAccessibility::Mixed flags into an int value that the Mac accessibilty |
1121 | system understands. This works for check boxes, radio buttons, and the like. |
1122 | The return values are: |
1123 | 0: unchecked |
1124 | 1: checked |
1125 | 2: undecided |
1126 | */ |
1127 | static int buttonValue(QAInterface element) |
1128 | { |
1129 | const QAccessible::State state = element.state(); |
1130 | if (state & QAccessible::Mixed) |
1131 | return 2; |
1132 | else if(state & QAccessible::Checked) |
1133 | return 1; |
1134 | else |
1135 | return 0; |
1136 | } |
1137 | |
1138 | static QString getValue(const QAInterface &interface) |
1139 | { |
1140 | const QAccessible::Role role = interface.role(); |
1141 | if (role == QAccessible::RadioButton || role == QAccessible::CheckBox) |
1142 | return QString::number(buttonValue(interface)); |
1143 | else |
1144 | return interface.text(QAccessible::Value); |
1145 | } |
1146 | #endif //QT_MAC_USE_COCOA |
1147 | |
1148 | /* |
1149 | Translates a QAccessible::Role into a mac accessibility role. |
1150 | */ |
1151 | static CFStringRef macRole(const QAInterface &interface) |
1152 | { |
1153 | const QAccessible::Role qtRole = interface.role(); |
1154 | |
1155 | // qDebug() << "role for" << interface.object() << "interface role" << hex << qtRole; |
1156 | |
1157 | // Qt accessibility: QAccessible::Splitter contains QAccessible::Grip. |
1158 | // Mac accessibility: AXSplitGroup contains AXSplitter. |
1159 | if (qtRole == QAccessible::Grip) { |
1160 | const QAInterface parent = interface.parent(); |
1161 | if (parent.isValid() && parent.role() == QAccessible::Splitter) |
1162 | return CFStringRef(QAXSplitterRole); |
1163 | } |
1164 | |
1165 | // Tab widgets and standalone tab bars get the kAXTabGroupRole. Accessibility |
1166 | // for tab bars emebedded in a tab widget is handled by the tab widget. |
1167 | if (isTabWidget(interface) || isStandaloneTabBar(interface)) |
1168 | return kAXTabGroupRole; |
1169 | |
1170 | if (QObject *object = interface.object()) { |
1171 | // ### The interface for an abstract scroll area returns the generic "Client" |
1172 | // role, so we have to to an extra detect on the QObject here. |
1173 | if (object->inherits("QAbstractScrollArea") && interface.id() == 0) |
1174 | return CFStringRef(QAXScrollAreaRole); |
1175 | |
1176 | if (object->inherits("QDockWidget")) |
1177 | return CFStringRef(QAXUnknownRole); |
1178 | } |
1179 | |
1180 | int i = 0; |
1181 | int testRole = text_bindings[i][0].qt; |
1182 | while (testRole != -1) { |
1183 | if (testRole == qtRole) |
1184 | return CFStringRef(text_bindings[i][0].mac); |
1185 | ++i; |
1186 | testRole = text_bindings[i][0].qt; |
1187 | } |
1188 | |
1189 | // qDebug() << "got unknown role!" << interface << interface.parent(); |
1190 | |
1191 | return CFStringRef(QAXUnknownRole); |
1192 | } |
1193 | |
1194 | /* |
1195 | Translates a QAccessible::Role and an attribute name into a QAccessible::Text, taking into |
1196 | account execptions listed in text_bindings. |
1197 | */ |
1198 | #ifndef QT_MAC_USE_COCOA |
1199 | static int textForRoleAndAttribute(QAccessible::Role role, CFStringRef attribute) |
1200 | { |
1201 | // Search for exception, return it if found. |
1202 | int testRole = text_bindings[0][0].qt; |
1203 | int i = 0; |
1204 | while (testRole != -1) { |
1205 | if (testRole == role) { |
1206 | int j = 1; |
1207 | int qtRole = text_bindings[i][j].qt; |
1208 | CFStringRef testAttribute = CFStringRef(text_bindings[i][j].mac); |
1209 | while (qtRole != -1) { |
1210 | if (CFStringCompare(attribute, testAttribute, 0) == kCFCompareEqualTo) { |
1211 | return (QAccessible::Text)qtRole; |
1212 | } |
1213 | ++j; |
1214 | testAttribute = CFStringRef(text_bindings[i][j].mac); /// ### custom compare |
1215 | qtRole = text_bindings[i][j].qt; /// ### custom compare |
1216 | } |
1217 | break; |
1218 | } |
1219 | ++i; |
1220 | testRole = text_bindings[i][0].qt; |
1221 | } |
1222 | |
1223 | // Return default mappping |
1224 | if (CFStringCompare(attribute, CFStringRef(QAXTitleAttribute), 0) == kCFCompareEqualTo) |
1225 | return QAccessible::Name; |
1226 | else if (CFStringCompare(attribute, CFStringRef(QAXValueAttribute), 0) == kCFCompareEqualTo) |
1227 | return QAccessible::Value; |
1228 | else if (CFStringCompare(attribute, CFStringRef(QAXHelpAttribute), 0) == kCFCompareEqualTo) |
1229 | return QAccessible::Help; |
1230 | else if (CFStringCompare(attribute, CFStringRef(QAXDescriptionAttribute), 0) == kCFCompareEqualTo) |
1231 | return QAccessible::Description; |
1232 | else |
1233 | return -1; |
1234 | } |
1235 | |
1236 | /* |
1237 | Returns the subrole string constant for the interface if it has one, |
1238 | else returns an empty string. |
1239 | */ |
1240 | static QCFString subrole(const QAInterface &interface) |
1241 | { |
1242 | const QAInterface parent = interface.parent(); |
1243 | if (parent.isValid() == false) |
1244 | return QCFString(); |
1245 | |
1246 | if (parent.role() == QAccessible::ScrollBar) { |
1247 | QCFString subrole; |
1248 | switch(interface.id()) { |
1249 | case 1: subrole = CFStringRef(QAXDecrementArrowSubrole); break; |
1250 | case 2: subrole = CFStringRef(QAXDecrementPageSubrole); break; |
1251 | case 4: subrole = CFStringRef(QAXIncrementPageSubrole); break; |
1252 | case 5: subrole = CFStringRef(QAXIncrementArrowSubrole); break; |
1253 | default: |
1254 | break; |
1255 | } |
1256 | return subrole; |
1257 | } |
1258 | return QCFString(); |
1259 | } |
1260 | |
1261 | // Gets the scroll bar orientation by asking the QAbstractSlider object directly. |
1262 | static Qt::Orientation scrollBarOrientation(const QAInterface &scrollBar) |
1263 | { |
1264 | QObject *const object = scrollBar.object(); |
1265 | if (QAbstractSlider * const sliderObject = qobject_cast<QAbstractSlider * const>(object)) |
1266 | return sliderObject->orientation(); |
1267 | |
1268 | return Qt::Vertical; // D'oh! The interface wasn't a scroll bar. |
1269 | } |
1270 | |
1271 | static QAInterface scrollAreaGetScrollBarInterface(const QAInterface &scrollArea, Qt::Orientation orientation) |
1272 | { |
1273 | if (macRole(scrollArea) != CFStringRef(CFStringRef(QAXScrollAreaRole))) |
1274 | return QAInterface(); |
1275 | |
1276 | // Child 1 is the contents widget, 2 and 3 are the scroll bar containers wich contains possible scroll bars. |
1277 | for (int i = 2; i <= 3; ++i) { |
1278 | QAInterface scrollBarContainer = scrollArea.childAt(i); |
1279 | for (int i = 1; i <= scrollBarContainer.childCount(); ++i) { |
1280 | QAInterface scrollBar = scrollBarContainer.childAt(i); |
1281 | if (scrollBar.isValid() && |
1282 | scrollBar.role() == QAccessible::ScrollBar && |
1283 | scrollBarOrientation(scrollBar) == orientation) |
1284 | return scrollBar; |
1285 | } |
1286 | } |
1287 | |
1288 | return QAInterface(); |
1289 | } |
1290 | |
1291 | static bool scrollAreaHasScrollBar(const QAInterface &scrollArea, Qt::Orientation orientation) |
1292 | { |
1293 | return scrollAreaGetScrollBarInterface(scrollArea, orientation).isValid(); |
1294 | } |
1295 | |
1296 | static QAElement scrollAreaGetScrollBar(const QAInterface &scrollArea, Qt::Orientation orientation) |
1297 | { |
1298 | return accessibleHierarchyManager()->lookup(scrollAreaGetScrollBarInterface(scrollArea, orientation)); |
1299 | } |
1300 | |
1301 | static QAElement scrollAreaGetContents(const QAInterface &scrollArea) |
1302 | { |
1303 | // Child 1 is the contents widget, |
1304 | return accessibleHierarchyManager()->lookup(scrollArea.navigate(QAccessible::Child, 1)); |
1305 | } |
1306 | |
1307 | static QAElement tabWidgetGetContents(const QAInterface &interface) |
1308 | { |
1309 | // A kAXTabGroup has a kAXContents attribute, which consists of the |
1310 | // ui elements for the current tab page. Get the current tab page |
1311 | // from the QStackedWidget, where the current visible page can |
1312 | // be found at index 1. |
1313 | QAInterface stackedWidget = interface.childAt(1); |
1314 | accessibleHierarchyManager()->registerChildren(stackedWidget); |
1315 | QAInterface tabPageInterface = stackedWidget.childAt(1); |
1316 | return accessibleHierarchyManager()->lookup(tabPageInterface); |
1317 | } |
1318 | |
1319 | static QList<QAElement> tabBarGetTabs(const QAInterface &interface) |
1320 | { |
1321 | // Get the tabs by searching for children with the "PageTab" role. |
1322 | // This filters out the left/right navigation buttons. |
1323 | accessibleHierarchyManager()->registerChildren(interface); |
1324 | QList<QAElement> tabs; |
1325 | const int numChildren = interface.childCount(); |
1326 | for (int i = 1; i < numChildren + 1; ++i) { |
1327 | QAInterface child = interface.navigate(QAccessible::Child, i); |
1328 | if (child.isValid() && child.role() == QAccessible::PageTab) { |
1329 | tabs.append(accessibleHierarchyManager()->lookup(child)); |
1330 | } |
1331 | } |
1332 | return tabs; |
1333 | } |
1334 | |
1335 | static QList<QAElement> tabWidgetGetTabs(const QAInterface &interface) |
1336 | { |
1337 | // Each QTabWidget has two children, a QStackedWidget and a QTabBar. |
1338 | // Get the tabs from the QTabBar. |
1339 | return tabBarGetTabs(interface.childAt(2)); |
1340 | } |
1341 | |
1342 | static QList<QAElement> tabWidgetGetChildren(const QAInterface &interface) |
1343 | { |
1344 | // The children for a kAXTabGroup should consist of the tabs and the |
1345 | // contents of the current open tab page. |
1346 | QList<QAElement> children = tabWidgetGetTabs(interface); |
1347 | children += tabWidgetGetContents(interface); |
1348 | return children; |
1349 | } |
1350 | #endif //QT_MAC_USE_COCOA |
1351 | |
1352 | /* |
1353 | Returns the label (buddy) interface for interface, or 0 if it has none. |
1354 | */ |
1355 | /* |
1356 | static QAInterface findLabel(const QAInterface &interface) |
1357 | { |
1358 | return interface.navigate(QAccessible::Label, 1); |
1359 | } |
1360 | */ |
1361 | /* |
1362 | Returns a list of interfaces this interface labels, or an empty list if it doesn't label any. |
1363 | */ |
1364 | /* |
1365 | static QList<QAInterface> findLabelled(const QAInterface &interface) |
1366 | { |
1367 | QList<QAInterface> interfaceList; |
1368 | |
1369 | int count = 1; |
1370 | const QAInterface labelled = interface.navigate(QAccessible::Labelled, count); |
1371 | while (labelled.isValid()) { |
1372 | interfaceList.append(labelled); |
1373 | ++count; |
1374 | } |
1375 | return interfaceList; |
1376 | } |
1377 | */ |
1378 | /* |
1379 | Tests if the given QAInterface has data for a mac attribute. |
1380 | */ |
1381 | #ifndef QT_MAC_USE_COCOA |
1382 | static bool supportsAttribute(CFStringRef attribute, const QAInterface &interface) |
1383 | { |
1384 | const int text = textForRoleAndAttribute(interface.role(), attribute); |
1385 | |
1386 | // Special case: Static texts don't have a title. |
1387 | if (interface.role() == QAccessible::StaticText && attribute == CFStringRef(QAXTitleAttribute)) |
1388 | return false; |
1389 | |
1390 | // Return true if we the attribute matched a QAccessible::Role and we get text for that role from the interface. |
1391 | if (text != -1) { |
1392 | if (text == QAccessible::Value) // Special case for Value, see getValue() |
1393 | return !getValue(interface).isEmpty(); |
1394 | else |
1395 | return !interface.text((QAccessible::Text)text).isEmpty(); |
1396 | } |
1397 | |
1398 | if (CFStringCompare(attribute, CFStringRef(QAXChildrenAttribute), 0) == kCFCompareEqualTo) { |
1399 | if (interface.childCount() > 0) |
1400 | return true; |
1401 | } |
1402 | |
1403 | if (CFStringCompare(attribute, CFStringRef(QAXSubroleAttribute), 0) == kCFCompareEqualTo) { |
1404 | return (subrole(interface) != QCFString()); |
1405 | } |
1406 | |
1407 | return false; |
1408 | } |
1409 | |
1410 | static void appendIfSupported(CFMutableArrayRef array, CFStringRef attribute, const QAInterface &interface) |
1411 | { |
1412 | if (supportsAttribute(attribute, interface)) |
1413 | qt_mac_append_cf_uniq(array, attribute); |
1414 | } |
1415 | |
1416 | /* |
1417 | Returns the names of the attributes the give QAInterface supports. |
1418 | */ |
1419 | static OSStatus getAllAttributeNames(EventRef event, const QAInterface &interface, EventHandlerCallRef next_ref) |
1420 | { |
1421 | // Call system event handler. |
1422 | OSStatus err = CallNextEventHandler(next_ref, event); |
1423 | if(err != noErr && err != eventNotHandledErr) |
1424 | return err; |
1425 | CFMutableArrayRef attrs = 0; |
1426 | GetEventParameter(event, kEventParamAccessibleAttributeNames, typeCFMutableArrayRef, 0, |
1427 | sizeof(attrs), 0, &attrs); |
1428 | |
1429 | if (!attrs) |
1430 | return eventNotHandledErr; |
1431 | |
1432 | // Append attribute names that are always supported. |
1433 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXPositionAttribute)); |
1434 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSizeAttribute)); |
1435 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXRoleAttribute)); |
1436 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXEnabledAttribute)); |
1437 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXWindowAttribute)); |
1438 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXTopLevelUIElementAttribute)); |
1439 | |
1440 | // Append these names if the QInterafceItem returns any data for them. |
1441 | appendIfSupported(attrs, CFStringRef(QAXTitleAttribute), interface); |
1442 | appendIfSupported(attrs, CFStringRef(QAXValueAttribute), interface); |
1443 | appendIfSupported(attrs, CFStringRef(QAXDescriptionAttribute), interface); |
1444 | appendIfSupported(attrs, CFStringRef(QAXLinkedUIElementsAttribute), interface); |
1445 | appendIfSupported(attrs, CFStringRef(QAXHelpAttribute), interface); |
1446 | appendIfSupported(attrs, CFStringRef(QAXTitleUIElementAttribute), interface); |
1447 | appendIfSupported(attrs, CFStringRef(QAXChildrenAttribute), interface); |
1448 | appendIfSupported(attrs, CFStringRef(QAXSubroleAttribute), interface); |
1449 | |
1450 | // Append attribute names based on the interaface role. |
1451 | switch (interface.role()) { |
1452 | case QAccessible::Window: |
1453 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMainAttribute)); |
1454 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinimizedAttribute)); |
1455 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXCloseButtonAttribute)); |
1456 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXZoomButtonAttribute)); |
1457 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinimizeButtonAttribute)); |
1458 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXToolbarButtonAttribute)); |
1459 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXGrowAreaAttribute)); |
1460 | break; |
1461 | case QAccessible::RadioButton: |
1462 | case QAccessible::CheckBox: |
1463 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinValueAttribute)); |
1464 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMaxValueAttribute)); |
1465 | break; |
1466 | case QAccessible::ScrollBar: |
1467 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXOrientationAttribute)); |
1468 | break; |
1469 | case QAccessible::Splitter: |
1470 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSplittersAttribute)); |
1471 | break; |
1472 | case QAccessible::Table: |
1473 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXRowsAttribute)); |
1474 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXVisibleRowsAttribute)); |
1475 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSelectedRowsAttribute)); |
1476 | break; |
1477 | default: |
1478 | break; |
1479 | } |
1480 | |
1481 | // Append attribute names based on the mac accessibility role. |
1482 | const QCFString mac_role = macRole(interface); |
1483 | if (mac_role == CFStringRef(QAXSplitterRole)) { |
1484 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXPreviousContentsAttribute)); |
1485 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXNextContentsAttribute)); |
1486 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXOrientationAttribute)); |
1487 | } else if (mac_role == CFStringRef(QAXScrollAreaRole)) { |
1488 | if (scrollAreaHasScrollBar(interface, Qt::Horizontal)) |
1489 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXHorizontalScrollBarAttribute)); |
1490 | if (scrollAreaHasScrollBar(interface, Qt::Vertical)) |
1491 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXVerticalScrollBarAttribute)); |
1492 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXContentsAttribute)); |
1493 | } else if (mac_role == CFStringRef(QAXTabGroupRole)) { |
1494 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXTabsAttribute)); |
1495 | // Only tab widgets can have the contents attribute, there is no way of getting |
1496 | // the contents from a QTabBar. |
1497 | if (isTabWidget(interface)) |
1498 | qt_mac_append_cf_uniq(attrs, CFStringRef(QAXContentsAttribute)); |
1499 | } |
1500 | |
1501 | return noErr; |
1502 | } |
1503 | |
1504 | static void handleStringAttribute(EventRef event, QAccessible::Text text, const QAInterface &interface) |
1505 | { |
1506 | QString str = interface.text(text); |
1507 | if (str.isEmpty()) |
1508 | return; |
1509 | |
1510 | // Remove any html markup from the text string, or VoiceOver will read the html tags. |
1511 | static QTextDocument document; |
1512 | document.setHtml(str); |
1513 | str = document.toPlainText(); |
1514 | |
1515 | CFStringRef cfstr = QCFString::toCFStringRef(str); |
1516 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, sizeof(cfstr), &cfstr); |
1517 | } |
1518 | |
1519 | /* |
1520 | Handles the parent attribute for a interface. |
1521 | There are basically three cases here: |
1522 | 1. interface is a HIView and has only HIView children. |
1523 | 2. interface is a HIView but has children that is not a HIView |
1524 | 3. interface is not a HIView. |
1525 | */ |
1526 | static OSStatus handleChildrenAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) |
1527 | { |
1528 | // Add the children for this interface to the global QAccessibelHierachyManager. |
1529 | accessibleHierarchyManager()->registerChildren(interface); |
1530 | |
1531 | if (isTabWidget(interface)) { |
1532 | QList<QAElement> children = tabWidgetGetChildren(interface); |
1533 | const int childCount = children.count(); |
1534 | |
1535 | CFMutableArrayRef array = 0; |
1536 | array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); |
1537 | for (int i = 0; i < childCount; ++i) { |
1538 | qt_mac_append_cf_uniq(array, children.at(i).element()); |
1539 | } |
1540 | |
1541 | OSStatus err; |
1542 | err = SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFArrayRef, sizeof(array), &array); |
1543 | if (err != noErr) |
1544 | qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__); |
1545 | |
1546 | return noErr; |
1547 | } |
1548 | |
1549 | const QList<QAElement> children = lookup(interface.children()); |
1550 | const int childCount = children.count(); |
1551 | |
1552 | OSStatus err = eventNotHandledErr; |
1553 | if (interface.isHIView()) |
1554 | err = CallNextEventHandler(next_ref, event); |
1555 | |
1556 | CFMutableArrayRef array = 0; |
1557 | int arraySize = 0; |
1558 | if (err == noErr) { |
1559 | CFTypeRef obj = 0; |
1560 | err = GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, NULL , sizeof(obj), NULL, &obj); |
1561 | if (err == noErr && obj != 0) { |
1562 | array = (CFMutableArrayRef)obj; |
1563 | arraySize = CFArrayGetCount(array); |
1564 | } |
1565 | } |
1566 | |
1567 | if (array) { |
1568 | CFArrayRemoveAllValues(array); |
1569 | for (int i = 0; i < childCount; ++i) { |
1570 | qt_mac_append_cf_uniq(array, children.at(i).element()); |
1571 | } |
1572 | } else { |
1573 | array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); |
1574 | for (int i = 0; i < childCount; ++i) { |
1575 | qt_mac_append_cf_uniq(array, children.at(i).element()); |
1576 | } |
1577 | |
1578 | err = SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFArrayRef, sizeof(array), &array); |
1579 | if (err != noErr) |
1580 | qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__); |
1581 | } |
1582 | |
1583 | return noErr; |
1584 | } |
1585 | |
1586 | /* |
1587 | |
1588 | */ |
1589 | static OSStatus handleParentAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) |
1590 | { |
1591 | OSStatus err = eventNotHandledErr; |
1592 | if (interface.isHIView()) { |
1593 | err = CallNextEventHandler(next_ref, event); |
1594 | } |
1595 | if (err == noErr) |
1596 | return err; |
1597 | |
1598 | const QAInterface parentInterface = interface.navigate(QAccessible::Ancestor, 1); |
1599 | const QAElement parentElement = accessibleHierarchyManager()->lookup(parentInterface); |
1600 | |
1601 | if (parentElement.isValid() == false) |
1602 | return eventNotHandledErr; |
1603 | |
1604 | AXUIElementRef elementRef = parentElement.element(); |
1605 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(elementRef), &elementRef); |
1606 | return noErr; |
1607 | } |
1608 | #endif |
1609 | |
1610 | struct IsWindowTest |
1611 | { |
1612 | static inline bool test(const QAInterface &interface) |
1613 | { |
1614 | return (interface.role() == QAccessible::Window); |
1615 | } |
1616 | }; |
1617 | |
1618 | struct IsWindowAndNotDrawerOrSheetTest |
1619 | { |
1620 | static inline bool test(const QAInterface &interface) |
1621 | { |
1622 | QWidget * const widget = qobject_cast<QWidget*>(interface.object()); |
1623 | return (interface.role() == QAccessible::Window && |
1624 | widget && widget->isWindow() && |
1625 | !qt_mac_is_macdrawer(widget) && |
1626 | !qt_mac_is_macsheet(widget)); |
1627 | } |
1628 | }; |
1629 | |
1630 | /* |
1631 | Navigates up the iterfaces ancestor hierachy until a QAccessibleInterface that |
1632 | passes the Test is found. If we reach a interface that is a HIView we stop the |
1633 | search and call AXUIElementCopyAttributeValue. |
1634 | */ |
1635 | template <typename TestType> |
1636 | OSStatus navigateAncestors(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface, CFStringRef attribute) |
1637 | { |
1638 | if (interface.isHIView()) |
1639 | return CallNextEventHandler(next_ref, event); |
1640 | |
1641 | QAInterface current = interface; |
1642 | QAElement element; |
1643 | while (current.isValid()) { |
1644 | if (TestType::test(interface)) { |
1645 | element = accessibleHierarchyManager()->lookup(current); |
1646 | break; |
1647 | } |
1648 | |
1649 | // If we reach an InterfaceItem that is a HiView we can hand of the search to |
1650 | // the system event handler. This is the common case. |
1651 | if (current.isHIView()) { |
1652 | CFTypeRef value = 0; |
1653 | const QAElement currentElement = accessibleHierarchyManager()->lookup(current); |
1654 | AXError err = AXUIElementCopyAttributeValue(currentElement.element(), attribute, &value); |
1655 | AXUIElementRef newElement = (AXUIElementRef)value; |
1656 | |
1657 | if (err == noErr) |
1658 | element = QAElement(newElement); |
1659 | |
1660 | if (newElement != 0) |
1661 | CFRelease(newElement); |
1662 | break; |
1663 | } |
1664 | |
1665 | QAInterface next = current.parent(); |
1666 | if (next.isValid() == false) |
1667 | break; |
1668 | if (next == current) |
1669 | break; |
1670 | current = next; |
1671 | } |
1672 | |
1673 | if (element.isValid() == false) |
1674 | return eventNotHandledErr; |
1675 | |
1676 | |
1677 | AXUIElementRef elementRef = element.element(); |
1678 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, |
1679 | sizeof(elementRef), &elementRef); |
1680 | return noErr; |
1681 | } |
1682 | |
1683 | /* |
1684 | Returns the top-level window for an interface, which is the closest ancestor interface that |
1685 | has the Window role, but is not a sheet or a drawer. |
1686 | */ |
1687 | #ifndef QT_MAC_USE_COCOA |
1688 | static OSStatus handleWindowAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) |
1689 | { |
1690 | return navigateAncestors<IsWindowAndNotDrawerOrSheetTest>(next_ref, event, interface, CFStringRef(QAXWindowAttribute)); |
1691 | } |
1692 | |
1693 | /* |
1694 | Returns the top-level window for an interface, which is the closest ancestor interface that |
1695 | has the Window role. (Can also be a sheet or a drawer) |
1696 | */ |
1697 | static OSStatus handleTopLevelUIElementAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) |
1698 | { |
1699 | return navigateAncestors<IsWindowTest>(next_ref, event, interface, CFStringRef(QAXTopLevelUIElementAttribute)); |
1700 | } |
1701 | |
1702 | /* |
1703 | Returns the tab buttons for an interface. |
1704 | */ |
1705 | static OSStatus handleTabsAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) |
1706 | { |
1707 | Q_UNUSED(next_ref); |
1708 | if (isTabWidget(interface)) |
1709 | return setAttributeValue(event, tabWidgetGetTabs(interface)); |
1710 | else |
1711 | return setAttributeValue(event, tabBarGetTabs(interface)); |
1712 | } |
1713 | |
1714 | static OSStatus handlePositionAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface) |
1715 | { |
1716 | QPoint qpoint(interface.rect().topLeft()); |
1717 | HIPoint point; |
1718 | point.x = qpoint.x(); |
1719 | point.y = qpoint.y(); |
1720 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(point), &point); |
1721 | return noErr; |
1722 | } |
1723 | |
1724 | static OSStatus handleSizeAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface) |
1725 | { |
1726 | QSize qSize(interface.rect().size()); |
1727 | HISize size; |
1728 | size.width = qSize.width(); |
1729 | size.height = qSize.height(); |
1730 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeHISize, sizeof(size), &size); |
1731 | return noErr; |
1732 | } |
1733 | |
1734 | static OSStatus handleSubroleAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface) |
1735 | { |
1736 | const QCFString role = subrole(interface); |
1737 | CFStringRef rolestr = (CFStringRef)role; |
1738 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(rolestr), &rolestr); |
1739 | return noErr; |
1740 | } |
1741 | |
1742 | static OSStatus handleOrientationAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) |
1743 | { |
1744 | QObject *const object = interface.object(); |
1745 | Qt::Orientation orientation; |
1746 | if (interface.role() == QAccessible::ScrollBar) { |
1747 | orientation = scrollBarOrientation(interface); |
1748 | } else if (QSplitterHandle * const splitter = qobject_cast<QSplitterHandle * const>(object)) { |
1749 | // Qt reports the layout orientation, but we want the splitter handle orientation. |
1750 | orientation = (splitter->orientation() == Qt::Horizontal) ? Qt::Vertical : Qt::Horizontal; |
1751 | } else { |
1752 | return CallNextEventHandler(next_ref, event); |
1753 | } |
1754 | const CFStringRef orientationString = (orientation == Qt::Vertical) |
1755 | ? CFStringRef(QAXVerticalOrientationValue) : CFStringRef(QAXHorizontalOrientationValue); |
1756 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, sizeof(orientationString), &orientationString); |
1757 | return noErr; |
1758 | } |
1759 | |
1760 | /* |
1761 | Figures out the next or previous contents for a splitter. |
1762 | */ |
1763 | static OSStatus handleSplitterContentsAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface, QCFString nextOrPrev) |
1764 | { |
1765 | if (interface.isValid() == false || interface.role() != QAccessible::Grip) |
1766 | return eventNotHandledErr; |
1767 | |
1768 | const QAInterface parent = interface.parent(); |
1769 | if (parent.isValid() == false) |
1770 | return CallNextEventHandler(next_ref, event); |
1771 | |
1772 | if (parent.role() != QAccessible::Splitter) |
1773 | return CallNextEventHandler(next_ref, event); |
1774 | |
1775 | const QSplitter * const splitter = qobject_cast<const QSplitter * const>(parent.object()); |
1776 | if (splitter == 0) |
1777 | return CallNextEventHandler(next_ref, event); |
1778 | |
1779 | QWidget * const splitterHandle = qobject_cast<QWidget * const>(interface.object()); |
1780 | const int splitterHandleIndex = splitter->indexOf(splitterHandle); |
1781 | const int widgetIndex = (nextOrPrev == QCFString(CFStringRef(QAXPreviousContentsAttribute))) ? splitterHandleIndex - 1 : splitterHandleIndex; |
1782 | const QAElement contentsElement = accessibleHierarchyManager()->lookup(splitter->widget(widgetIndex), 0); |
1783 | return setAttributeValue(event, QList<QAElement>() << contentsElement); |
1784 | } |
1785 | |
1786 | /* |
1787 | Creates a list of all splitter handles the splitter contains. |
1788 | */ |
1789 | static OSStatus handleSplittersAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) |
1790 | { |
1791 | const QSplitter * const splitter = qobject_cast<const QSplitter * const>(interface.object()); |
1792 | if (splitter == 0) |
1793 | return CallNextEventHandler(next_ref, event); |
1794 | |
1795 | accessibleHierarchyManager()->registerChildren(interface); |
1796 | |
1797 | QList<QAElement> handles; |
1798 | const int visibleSplitterCount = splitter->count() -1; // skip first handle, it's always invisible. |
1799 | for (int i = 0; i < visibleSplitterCount; ++i) |
1800 | handles.append(accessibleHierarchyManager()->lookup(splitter->handle(i + 1), 0)); |
1801 | |
1802 | return setAttributeValue(event, handles); |
1803 | } |
1804 | |
1805 | // This handler gets the scroll bars for a scroll area |
1806 | static OSStatus handleScrollBarAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &scrollArea, Qt::Orientation orientation) |
1807 | { |
1808 | QAElement scrollBar = scrollAreaGetScrollBar(scrollArea, orientation); |
1809 | if (scrollBar.isValid() == false) |
1810 | return CallNextEventHandler(next_ref, event); |
1811 | |
1812 | AXUIElementRef elementRef = scrollBar.element(); |
1813 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(elementRef), &elementRef); |
1814 | return noErr; |
1815 | } |
1816 | |
1817 | // This handler gets the contents for a scroll area or tab widget. |
1818 | static OSStatus handleContentsAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) |
1819 | { |
1820 | const QCFString mac_role = macRole(interface); |
1821 | |
1822 | QAElement contents; |
1823 | |
1824 | if (mac_role == kAXTabGroupRole) { |
1825 | contents = tabWidgetGetContents(interface); |
1826 | } else { |
1827 | contents = scrollAreaGetContents(interface); |
1828 | if (contents.isValid() == false) |
1829 | return CallNextEventHandler(next_ref, event); |
1830 | } |
1831 | |
1832 | return setAttributeValue(event, QList<QAElement>() << contents); |
1833 | } |
1834 | |
1835 | static OSStatus handleRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView) |
1836 | { |
1837 | QList<QAElement> rows = lookup(tableView.children()); |
1838 | |
1839 | // kill the first row which is the horizontal header. |
1840 | rows.removeAt(0); |
1841 | |
1842 | return setAttributeValue(event, rows); |
1843 | } |
1844 | |
1845 | static OSStatus handleVisibleRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView) |
1846 | { |
1847 | QList<QAElement> visibleRows; |
1848 | |
1849 | QList<QAInterface> rows = tableView.children(); |
1850 | // kill the first row which is the horizontal header. |
1851 | rows.removeAt(0); |
1852 | |
1853 | foreach (const QAInterface &interface, rows) |
1854 | if ((interface.state() & QAccessible::Invisible) == false) |
1855 | visibleRows.append(accessibleHierarchyManager()->lookup(interface)); |
1856 | |
1857 | return setAttributeValue(event, visibleRows); |
1858 | } |
1859 | |
1860 | static OSStatus handleSelectedRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView) |
1861 | { |
1862 | QList<QAElement> selectedRows; |
1863 | foreach (const QAInterface &interface, tableView.children()) |
1864 | if ((interface.state() & QAccessible::Selected)) |
1865 | selectedRows.append(accessibleHierarchyManager()->lookup(interface)); |
1866 | |
1867 | return setAttributeValue(event, selectedRows); |
1868 | } |
1869 | |
1870 | static OSStatus getNamedAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) |
1871 | { |
1872 | CFStringRef var; |
1873 | GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0, |
1874 | sizeof(var), 0, &var); |
1875 | |
1876 | if (CFStringCompare(var, CFStringRef(QAXChildrenAttribute), 0) == kCFCompareEqualTo) { |
1877 | return handleChildrenAttribute(next_ref, event, interface); |
1878 | } else if(CFStringCompare(var, CFStringRef(QAXTopLevelUIElementAttribute), 0) == kCFCompareEqualTo) { |
1879 | return handleTopLevelUIElementAttribute(next_ref, event, interface); |
1880 | } else if(CFStringCompare(var, CFStringRef(QAXWindowAttribute), 0) == kCFCompareEqualTo) { |
1881 | return handleWindowAttribute(next_ref, event, interface); |
1882 | } else if(CFStringCompare(var, CFStringRef(QAXParentAttribute), 0) == kCFCompareEqualTo) { |
1883 | return handleParentAttribute(next_ref, event, interface); |
1884 | } else if (CFStringCompare(var, CFStringRef(QAXPositionAttribute), 0) == kCFCompareEqualTo) { |
1885 | return handlePositionAttribute(next_ref, event, interface); |
1886 | } else if (CFStringCompare(var, CFStringRef(QAXSizeAttribute), 0) == kCFCompareEqualTo) { |
1887 | return handleSizeAttribute(next_ref, event, interface); |
1888 | } else if (CFStringCompare(var, CFStringRef(QAXRoleAttribute), 0) == kCFCompareEqualTo) { |
1889 | CFStringRef role = macRole(interface); |
1890 | // ### |
1891 | // QWidget * const widget = qobject_cast<QWidget *>(interface.object()); |
1892 | // if (role == CFStringRef(QAXUnknownRole) && widget && widget->isWindow()) |
1893 | // role = CFStringRef(QAXWindowRole); |
1894 | |
1895 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, |
1896 | sizeof(role), &role); |
1897 | |
1898 | } else if (CFStringCompare(var, CFStringRef(QAXEnabledAttribute), 0) == kCFCompareEqualTo) { |
1899 | Boolean val = !((interface.state() & QAccessible::Unavailable)) |
1900 | && !((interface.state() & QAccessible::Invisible)); |
1901 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, |
1902 | sizeof(val), &val); |
1903 | } else if (CFStringCompare(var, CFStringRef(QAXExpandedAttribute), 0) == kCFCompareEqualTo) { |
1904 | Boolean val = (interface.state() & QAccessible::Expanded); |
1905 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, |
1906 | sizeof(val), &val); |
1907 | } else if (CFStringCompare(var, CFStringRef(QAXSelectedAttribute), 0) == kCFCompareEqualTo) { |
1908 | Boolean val = (interface.state() & QAccessible::Selection); |
1909 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, |
1910 | sizeof(val), &val); |
1911 | } else if (CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) { |
1912 | Boolean val = (interface.state() & QAccessible::Focus); |
1913 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, |
1914 | sizeof(val), &val); |
1915 | } else if (CFStringCompare(var, CFStringRef(QAXSelectedChildrenAttribute), 0) == kCFCompareEqualTo) { |
1916 | const int cc = interface.childCount(); |
1917 | QList<QAElement> selected; |
1918 | for (int i = 1; i <= cc; ++i) { |
1919 | const QAInterface child_iface = interface.navigate(QAccessible::Child, i); |
1920 | if (child_iface.isValid() && child_iface.state() & QAccessible::Selected) |
1921 | selected.append(accessibleHierarchyManager()->lookup(child_iface)); |
1922 | } |
1923 | |
1924 | return setAttributeValue(event, selected); |
1925 | |
1926 | } else if (CFStringCompare(var, CFStringRef(QAXCloseButtonAttribute), 0) == kCFCompareEqualTo) { |
1927 | if(interface.object() && interface.object()->isWidgetType()) { |
1928 | Boolean val = true; //do we want to add a WState for this? |
1929 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, |
1930 | sizeof(val), &val); |
1931 | } |
1932 | } else if (CFStringCompare(var, CFStringRef(QAXZoomButtonAttribute), 0) == kCFCompareEqualTo) { |
1933 | if(interface.object() && interface.object()->isWidgetType()) { |
1934 | QWidget *widget = (QWidget*)interface.object(); |
1935 | Boolean val = (widget->windowFlags() & Qt::WindowMaximizeButtonHint); |
1936 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, |
1937 | sizeof(val), &val); |
1938 | } |
1939 | } else if (CFStringCompare(var, CFStringRef(QAXMinimizeButtonAttribute), 0) == kCFCompareEqualTo) { |
1940 | if(interface.object() && interface.object()->isWidgetType()) { |
1941 | QWidget *widget = (QWidget*)interface.object(); |
1942 | Boolean val = (widget->windowFlags() & Qt::WindowMinimizeButtonHint); |
1943 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, |
1944 | sizeof(val), &val); |
1945 | } |
1946 | } else if (CFStringCompare(var, CFStringRef(QAXToolbarButtonAttribute), 0) == kCFCompareEqualTo) { |
1947 | if(interface.object() && interface.object()->isWidgetType()) { |
1948 | QWidget *widget = (QWidget*)interface.object(); |
1949 | Boolean val = qobject_cast<QMainWindow *>(widget) != 0; |
1950 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, |
1951 | sizeof(val), &val); |
1952 | } |
1953 | } else if (CFStringCompare(var, CFStringRef(QAXGrowAreaAttribute), 0) == kCFCompareEqualTo) { |
1954 | if(interface.object() && interface.object()->isWidgetType()) { |
1955 | Boolean val = true; //do we want to add a WState for this? |
1956 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, |
1957 | sizeof(val), &val); |
1958 | } |
1959 | } else if (CFStringCompare(var, CFStringRef(QAXMinimizedAttribute), 0) == kCFCompareEqualTo) { |
1960 | if (interface.object() && interface.object()->isWidgetType()) { |
1961 | QWidget *widget = (QWidget*)interface.object(); |
1962 | Boolean val = (widget->windowState() & Qt::WindowMinimized); |
1963 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, |
1964 | sizeof(val), &val); |
1965 | } |
1966 | } else if (CFStringCompare(var, CFStringRef(QAXSubroleAttribute), 0) == kCFCompareEqualTo) { |
1967 | return handleSubroleAttribute(next_ref, event, interface); |
1968 | } else if (CFStringCompare(var, CFStringRef(QAXRoleDescriptionAttribute), 0) == kCFCompareEqualTo) { |
1969 | #if !defined(QT_MAC_USE_COCOA) |
1970 | if (HICopyAccessibilityRoleDescription) { |
1971 | const CFStringRef roleDescription = HICopyAccessibilityRoleDescription(macRole(interface), 0); |
1972 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, |
1973 | sizeof(roleDescription), &roleDescription); |
1974 | } else |
1975 | #endif |
1976 | { |
1977 | // Just use Qt::Description on 10.3 |
1978 | handleStringAttribute(event, QAccessible::Description, interface); |
1979 | } |
1980 | } else if (CFStringCompare(var, CFStringRef(QAXTitleAttribute), 0) == kCFCompareEqualTo) { |
1981 | const QAccessible::Role role = interface.role(); |
1982 | const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); |
1983 | handleStringAttribute(event, text, interface); |
1984 | } else if (CFStringCompare(var, CFStringRef(QAXValueAttribute), 0) == kCFCompareEqualTo) { |
1985 | const QAccessible::Role role = interface.role(); |
1986 | const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); |
1987 | if (role == QAccessible::CheckBox || role == QAccessible::RadioButton) { |
1988 | int value = buttonValue(interface); |
1989 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value); |
1990 | } else { |
1991 | handleStringAttribute(event, text, interface); |
1992 | } |
1993 | } else if (CFStringCompare(var, CFStringRef(QAXDescriptionAttribute), 0) == kCFCompareEqualTo) { |
1994 | const QAccessible::Role role = interface.role(); |
1995 | const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); |
1996 | handleStringAttribute(event, text, interface); |
1997 | } else if (CFStringCompare(var, CFStringRef(QAXLinkedUIElementsAttribute), 0) == kCFCompareEqualTo) { |
1998 | return CallNextEventHandler(next_ref, event); |
1999 | } else if (CFStringCompare(var, CFStringRef(QAXHelpAttribute), 0) == kCFCompareEqualTo) { |
2000 | const QAccessible::Role role = interface.role(); |
2001 | const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); |
2002 | handleStringAttribute(event, text, interface); |
2003 | } else if (CFStringCompare(var, kAXTitleUIElementAttribute, 0) == kCFCompareEqualTo) { |
2004 | return CallNextEventHandler(next_ref, event); |
2005 | } else if (CFStringCompare(var, CFStringRef(QAXTabsAttribute), 0) == kCFCompareEqualTo) { |
2006 | return handleTabsAttribute(next_ref, event, interface); |
2007 | } else if (CFStringCompare(var, CFStringRef(QAXMinValueAttribute), 0) == kCFCompareEqualTo) { |
2008 | // tabs we first go to the tab bar which is child #2. |
2009 | QAInterface tabBarInterface = interface.childAt(2); |
2010 | return handleTabsAttribute(next_ref, event, tabBarInterface); |
2011 | } else if (CFStringCompare(var, CFStringRef(QAXMinValueAttribute), 0) == kCFCompareEqualTo) { |
2012 | if (interface.role() == QAccessible::RadioButton || interface.role() == QAccessible::CheckBox) { |
2013 | uint value = 0; |
2014 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value); |
2015 | } else { |
2016 | return CallNextEventHandler(next_ref, event); |
2017 | } |
2018 | } else if (CFStringCompare(var, CFStringRef(QAXMaxValueAttribute), 0) == kCFCompareEqualTo) { |
2019 | if (interface.role() == QAccessible::RadioButton || interface.role() == QAccessible::CheckBox) { |
2020 | uint value = 2; |
2021 | SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value); |
2022 | } else { |
2023 | return CallNextEventHandler(next_ref, event); |
2024 | } |
2025 | } else if (CFStringCompare(var, CFStringRef(QAXOrientationAttribute), 0) == kCFCompareEqualTo) { |
2026 | return handleOrientationAttribute(next_ref, event, interface); |
2027 | } else if (CFStringCompare(var, CFStringRef(QAXPreviousContentsAttribute), 0) == kCFCompareEqualTo) { |
2028 | return handleSplitterContentsAttribute(next_ref, event, interface, CFStringRef(QAXPreviousContentsAttribute)); |
2029 | } else if (CFStringCompare(var, CFStringRef(QAXNextContentsAttribute), 0) == kCFCompareEqualTo) { |
2030 | return handleSplitterContentsAttribute(next_ref, event, interface, CFStringRef(QAXNextContentsAttribute)); |
2031 | } else if (CFStringCompare(var, CFStringRef(QAXSplittersAttribute), 0) == kCFCompareEqualTo) { |
2032 | return handleSplittersAttribute(next_ref, event, interface); |
2033 | } else if (CFStringCompare(var, CFStringRef(QAXHorizontalScrollBarAttribute), 0) == kCFCompareEqualTo) { |
2034 | return handleScrollBarAttribute(next_ref, event, interface, Qt::Horizontal); |
2035 | } else if (CFStringCompare(var, CFStringRef(QAXVerticalScrollBarAttribute), 0) == kCFCompareEqualTo) { |
2036 | return handleScrollBarAttribute(next_ref, event, interface, Qt::Vertical); |
2037 | } else if (CFStringCompare(var, CFStringRef(QAXContentsAttribute), 0) == kCFCompareEqualTo) { |
2038 | return handleContentsAttribute(next_ref, event, interface); |
2039 | } else if (CFStringCompare(var, CFStringRef(QAXRowsAttribute), 0) == kCFCompareEqualTo) { |
2040 | return handleRowsAttribute(next_ref, event, interface); |
2041 | } else if (CFStringCompare(var, CFStringRef(QAXVisibleRowsAttribute), 0) == kCFCompareEqualTo) { |
2042 | return handleVisibleRowsAttribute(next_ref, event, interface); |
2043 | } else if (CFStringCompare(var, CFStringRef(QAXSelectedRowsAttribute), 0) == kCFCompareEqualTo) { |
2044 | return handleSelectedRowsAttribute(next_ref, event, interface); |
2045 | } else { |
2046 | return CallNextEventHandler(next_ref, event); |
2047 | } |
2048 | return noErr; |
2049 | } |
2050 | |
2051 | static OSStatus isNamedAttributeSettable(EventRef event, const QAInterface &interface) |
2052 | { |
2053 | CFStringRef var; |
2054 | GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0, |
2055 | sizeof(var), 0, &var); |
2056 | Boolean settable = false; |
2057 | if (CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) { |
2058 | settable = true; |
2059 | } else { |
2060 | for (int r = 0; text_bindings[r][0].qt != -1; r++) { |
2061 | if(interface.role() == (QAccessible::Role)text_bindings[r][0].qt) { |
2062 | for (int a = 1; text_bindings[r][a].qt != -1; a++) { |
2063 | if (CFStringCompare(var, CFStringRef(text_bindings[r][a].mac), 0) == kCFCompareEqualTo) { |
2064 | settable = text_bindings[r][a].settable; |
2065 | break; |
2066 | } |
2067 | } |
2068 | } |
2069 | } |
2070 | } |
2071 | SetEventParameter(event, kEventParamAccessibleAttributeSettable, typeBoolean, |
2072 | sizeof(settable), &settable); |
2073 | return noErr; |
2074 | } |
2075 | |
2076 | static OSStatus getChildAtPoint(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) |
2077 | { |
2078 | Q_UNUSED(next_ref); |
2079 | if (interface.isValid() == false) |
2080 | return eventNotHandledErr; |
2081 | |
2082 | // Add the children for this interface to the global QAccessibelHierachyManager. |
2083 | accessibleHierarchyManager()->registerChildren(interface); |
2084 | |
2085 | Point where; |
2086 | GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(where), 0, &where); |
2087 | const QAInterface childInterface = interface.childAt(where.h, where.v); |
2088 | |
2089 | if (childInterface.isValid() == false || childInterface == interface) |
2090 | return eventNotHandledErr; |
2091 | |
2092 | const QAElement element = accessibleHierarchyManager()->lookup(childInterface); |
2093 | if (element.isValid() == false) |
2094 | return eventNotHandledErr; |
2095 | |
2096 | AXUIElementRef elementRef = element.element(); |
2097 | CFRetain(elementRef); |
2098 | SetEventParameter(event, kEventParamAccessibleChild, typeCFTypeRef, |
2099 | sizeof(elementRef), &elementRef); |
2100 | |
2101 | return noErr; |
2102 | } |
2103 | |
2104 | /* |
2105 | Returns a list of actions the given interface supports. |
2106 | Currently implemented by getting the interface role and deciding based on that. |
2107 | */ |
2108 | static QList<QAccessible::Action> supportedPredefinedActions(const QAInterface &interface) |
2109 | { |
2110 | QList<QAccessible::Action> actions; |
2111 | switch (interface.role()) { |
2112 | default: |
2113 | // Most things can be pressed. |
2114 | actions.append(QAccessible::Press); |
2115 | break; |
2116 | } |
2117 | |
2118 | return actions; |
2119 | } |
2120 | |
2121 | /* |
2122 | Translates a predefined QAccessible::Action to a Mac action constant. |
2123 | Returns an empty string if the Qt Action has no mac equivalent. |
2124 | */ |
2125 | static QCFString translateAction(const QAccessible::Action action) |
2126 | { |
2127 | switch (action) { |
2128 | case QAccessible::Press: |
2129 | return CFStringRef(QAXPressAction); |
2130 | break; |
2131 | case QAccessible::Increase: |
2132 | return CFStringRef(QAXIncrementAction); |
2133 | break; |
2134 | case QAccessible::Decrease: |
2135 | return CFStringRef(QAXDecrementAction); |
2136 | break; |
2137 | case QAccessible::Accept: |
2138 | return CFStringRef(QAXConfirmAction); |
2139 | break; |
2140 | case QAccessible::Select: |
2141 | return CFStringRef(QAXPickAction); |
2142 | break; |
2143 | case QAccessible::Cancel: |
2144 | return CFStringRef(QAXCancelAction); |
2145 | break; |
2146 | default: |
2147 | return QCFString(); |
2148 | break; |
2149 | } |
2150 | } |
2151 | |
2152 | /* |
2153 | Translates between a Mac action constant and a QAccessible::Action. |
2154 | Returns QAccessible::Default action if there is no Qt predefined equivalent. |
2155 | */ |
2156 | static QAccessible::Action translateAction(const CFStringRef actionName) |
2157 | { |
2158 | if(CFStringCompare(actionName, CFStringRef(QAXPressAction), 0) == kCFCompareEqualTo) { |
2159 | return QAccessible::Press; |
2160 | } else if(CFStringCompare(actionName, CFStringRef(QAXIncrementAction), 0) == kCFCompareEqualTo) { |
2161 | return QAccessible::Increase; |
2162 | } else if(CFStringCompare(actionName, CFStringRef(QAXDecrementAction), 0) == kCFCompareEqualTo) { |
2163 | return QAccessible::Decrease; |
2164 | } else if(CFStringCompare(actionName, CFStringRef(QAXConfirmAction), 0) == kCFCompareEqualTo) { |
2165 | return QAccessible::Accept; |
2166 | } else if(CFStringCompare(actionName, CFStringRef(QAXPickAction), 0) == kCFCompareEqualTo) { |
2167 | return QAccessible::Select; |
2168 | } else if(CFStringCompare(actionName, CFStringRef(QAXCancelAction), 0) == kCFCompareEqualTo) { |
2169 | return QAccessible::Cancel; |
2170 | } else { |
2171 | return QAccessible::DefaultAction; |
2172 | } |
2173 | } |
2174 | #endif // QT_MAC_USE_COCOA |
2175 | |
2176 | /* |
2177 | Copies the translated names all supported actions for an interface into the kEventParamAccessibleActionNames |
2178 | event parameter. |
2179 | */ |
2180 | #ifndef QT_MAC_USE_COCOA |
2181 | static OSStatus getAllActionNames(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) |
2182 | { |
2183 | Q_UNUSED(next_ref); |
2184 | |
2185 | CFMutableArrayRef actions = 0; |
2186 | GetEventParameter(event, kEventParamAccessibleActionNames, typeCFMutableArrayRef, 0, |
2187 | sizeof(actions), 0, &actions); |
2188 | |
2189 | // Add supported predefined actions. |
2190 | const QList<QAccessible::Action> predefinedActions = supportedPredefinedActions(interface); |
2191 | for (int i = 0; i < predefinedActions.count(); ++i) { |
2192 | const QCFString action = translateAction(predefinedActions.at(i)); |
2193 | if (action != QCFString()) |
2194 | qt_mac_append_cf_uniq(actions, action); |
2195 | } |
2196 | |
2197 | // Add user actions |
2198 | const int actionCount = interface.userActionCount(); |
2199 | for (int i = 0; i < actionCount; ++i) { |
2200 | const QString actionName = interface.actionText(i, QAccessible::Name); |
2201 | qt_mac_append_cf_uniq(actions, QCFString::toCFStringRef(actionName)); |
2202 | } |
2203 | |
2204 | return noErr; |
2205 | } |
2206 | #endif |
2207 | |
2208 | /* |
2209 | Handles the perforNamedAction event. |
2210 | */ |
2211 | #ifndef QT_MAC_USE_COCOA |
2212 | static OSStatus performNamedAction(EventHandlerCallRef next_ref, EventRef event, const QAInterface& interface) |
2213 | { |
2214 | Q_UNUSED(next_ref); |
2215 | |
2216 | CFStringRef act; |
2217 | GetEventParameter(event, kEventParamAccessibleActionName, typeCFStringRef, 0, |
2218 | sizeof(act), 0, &act); |
2219 | |
2220 | const QAccessible::Action action = translateAction(act); |
2221 | |
2222 | // Perform built-in action |
2223 | if (action != QAccessible::DefaultAction) { |
2224 | interface.doAction(action, QVariantList()); |
2225 | return noErr; |
2226 | } |
2227 | |
2228 | // Search for user-defined actions and perform it if found. |
2229 | const int actCount = interface.userActionCount(); |
2230 | const QString qAct = QCFString::toQString(act); |
2231 | for(int i = 0; i < actCount; i++) { |
2232 | if(interface.actionText(i, QAccessible::Name) == qAct) { |
2233 | interface.doAction(i, QVariantList()); |
2234 | break; |
2235 | } |
2236 | } |
2237 | return noErr; |
2238 | } |
2239 | |
2240 | static OSStatus setNamedAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) |
2241 | { |
2242 | Q_UNUSED(next_ref); |
2243 | Q_UNUSED(event); |
2244 | |
2245 | CFStringRef var; |
2246 | GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0, |
2247 | sizeof(var), 0, &var); |
2248 | if(CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) { |
2249 | CFTypeRef val; |
2250 | if(GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, 0, |
2251 | sizeof(val), 0, &val) == noErr) { |
2252 | if(CFGetTypeID(val) == CFBooleanGetTypeID() && |
2253 | CFEqual(static_cast<CFBooleanRef>(val), kCFBooleanTrue)) { |
2254 | interface.doAction(QAccessible::SetFocus); |
2255 | } |
2256 | } |
2257 | } else { |
2258 | bool found = false; |
2259 | for(int r = 0; text_bindings[r][0].qt != -1; r++) { |
2260 | if(interface.role() == (QAccessible::Role)text_bindings[r][0].qt) { |
2261 | for(int a = 1; text_bindings[r][a].qt != -1; a++) { |
2262 | if(CFStringCompare(var, CFStringRef(text_bindings[r][a].mac), 0) == kCFCompareEqualTo) { |
2263 | if(!text_bindings[r][a].settable) { |
2264 | } else { |
2265 | CFTypeRef val; |
2266 | if(GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, 0, |
2267 | sizeof(val), 0, &val) == noErr) { |
2268 | if(CFGetTypeID(val) == CFStringGetTypeID()) |
2269 | interface.setText((QAccessible::Text)text_bindings[r][a].qt, |
2270 | QCFString::toQString(static_cast<CFStringRef>(val))); |
2271 | |
2272 | } |
2273 | } |
2274 | found = true; |
2275 | break; |
2276 | } |
2277 | } |
2278 | break; |
2279 | } |
2280 | } |
2281 | } |
2282 | return noErr; |
2283 | } |
2284 | |
2285 | /* |
2286 | This is the main accessibility event handler. |
2287 | */ |
2288 | static OSStatus accessibilityEventHandler(EventHandlerCallRef next_ref, EventRef event, void *data) |
2289 | { |
2290 | Q_UNUSED(data) |
2291 | |
2292 | // Return if this event is not a AccessibleGetNamedAttribute event. |
2293 | const UInt32 eclass = GetEventClass(event); |
2294 | if (eclass != kEventClassAccessibility) |
2295 | return eventNotHandledErr; |
2296 | |
2297 | // Get the AXUIElementRef and QAInterface pointer |
2298 | AXUIElementRef element = 0; |
2299 | GetEventParameter(event, kEventParamAccessibleObject, typeCFTypeRef, 0, sizeof(element), 0, &element); |
2300 | QAInterface interface = accessibleHierarchyManager()->lookup(element); |
2301 | if (interface.isValid() == false) |
2302 | return eventNotHandledErr; |
2303 | |
2304 | const UInt32 ekind = GetEventKind(event); |
2305 | OSStatus status = noErr; |
2306 | switch (ekind) { |
2307 | case kEventAccessibleGetAllAttributeNames: |
2308 | status = getAllAttributeNames(event, interface, next_ref); |
2309 | break; |
2310 | case kEventAccessibleGetNamedAttribute: |
2311 | status = getNamedAttribute(next_ref, event, interface); |
2312 | break; |
2313 | case kEventAccessibleIsNamedAttributeSettable: |
2314 | status = isNamedAttributeSettable(event, interface); |
2315 | break; |
2316 | case kEventAccessibleGetChildAtPoint: |
2317 | status = getChildAtPoint(next_ref, event, interface); |
2318 | break; |
2319 | case kEventAccessibleGetAllActionNames: |
2320 | status = getAllActionNames(next_ref, event, interface); |
2321 | break; |
2322 | case kEventAccessibleGetFocusedChild: |
2323 | status = CallNextEventHandler(next_ref, event); |
2324 | break; |
2325 | case kEventAccessibleSetNamedAttribute: |
2326 | status = setNamedAttribute(next_ref, event, interface); |
2327 | break; |
2328 | case kEventAccessiblePerformNamedAction: |
2329 | status = performNamedAction(next_ref, event, interface); |
2330 | break; |
2331 | default: |
2332 | status = CallNextEventHandler(next_ref, event); |
2333 | break; |
2334 | }; |
2335 | return status; |
2336 | } |
2337 | #endif |
2338 | |
2339 | void QAccessible::initialize() |
2340 | { |
2341 | #ifndef QT_MAC_USE_COCOA |
2342 | registerQtAccessibilityHIObjectSubclass(); |
2343 | installApplicationEventhandler(); |
2344 | #endif |
2345 | } |
2346 | |
2347 | // Sets thre root object for the application |
2348 | void QAccessible::setRootObject(QObject *object) |
2349 | { |
2350 | // Call installed root object handler if we have one |
2351 | if (rootObjectHandler) { |
2352 | rootObjectHandler(object); |
2353 | return; |
2354 | } |
2355 | |
2356 | rootObject = object; |
2357 | } |
2358 | |
2359 | void QAccessible::cleanup() |
2360 | { |
2361 | accessibleHierarchyManager()->reset(); |
2362 | #ifndef QT_MAC_USE_COCOA |
2363 | removeEventhandler(applicationEventHandlerUPP); |
2364 | removeEventhandler(objectCreateEventHandlerUPP); |
2365 | removeEventhandler(accessibilityEventHandlerUPP); |
2366 | #endif |
2367 | } |
2368 | |
2369 | void QAccessible::updateAccessibility(QObject *object, int child, Event reason) |
2370 | { |
2371 | // Call installed update handler if we have one. |
2372 | if (updateHandler) { |
2373 | updateHandler(object, child, reason); |
2374 | return; |
2375 | } |
2376 | |
2377 | #ifndef QT_MAC_USE_COCOA |
2378 | // Return if the mac accessibility is not enabled. |
2379 | if(!AXAPIEnabled()) |
2380 | return; |
2381 | |
2382 | // Work around crash, disable accessiblity for focus frames. |
2383 | if (qstrcmp(object->metaObject()->className(), "QFocusFrame") == 0) |
2384 | return; |
2385 | |
2386 | // qDebug() << "updateAccessibility" << object << child << hex << reason; |
2387 | |
2388 | if (reason == ObjectShow) { |
2389 | QAInterface interface = QAInterface(QAccessible::queryAccessibleInterface(object), child); |
2390 | accessibleHierarchyManager()->registerInterface(interface); |
2391 | } |
2392 | |
2393 | const QAElement element = accessibleHierarchyManager()->lookup(object, child); |
2394 | if (element.isValid() == false) |
2395 | return; |
2396 | |
2397 | |
2398 | CFStringRef notification = 0; |
2399 | if(object && object->isWidgetType() && reason == ObjectCreated) { |
2400 | notification = CFStringRef(QAXWindowCreatedNotification); |
2401 | } else if(reason == ValueChanged) { |
2402 | notification = CFStringRef(QAXValueChangedNotification); |
2403 | } else if(reason == MenuStart) { |
2404 | notification = CFStringRef(QAXMenuOpenedNotification); |
2405 | } else if(reason == MenuEnd) { |
2406 | notification = CFStringRef(QAXMenuClosedNotification); |
2407 | } else if(reason == LocationChanged) { |
2408 | notification = CFStringRef(QAXWindowMovedNotification); |
2409 | } else if(reason == ObjectShow || reason == ObjectHide ) { |
2410 | // When a widget is deleted we get a ObjectHide before the destroyed(QObject *) |
2411 | // signal is emitted (which makes sense). However, at this point we are in the |
2412 | // middle of the QWidget destructor which means that we have to be careful when |
2413 | // using the widget pointer. Since we can't control what the accessibilty interfaces |
2414 | // does when navigate() is called below we ignore the hide update in this case. |
2415 | // (the widget will be deleted soon anyway.) |
2416 | extern QWidgetPrivate * qt_widget_private(QWidget *); |
2417 | if (QWidget *widget = qobject_cast<QWidget*>(object)) { |
2418 | if (qt_widget_private(widget)->data.in_destructor) |
2419 | return; |
2420 | |
2421 | // Check widget parent as well, special case for preventing crash |
2422 | // when the viewport() of an abstract scroll area is hidden when |
2423 | // the QWidget destructor hides all its children. |
2424 | QWidget *parentWidget = widget->parentWidget(); |
2425 | if (parentWidget && qt_widget_private(parentWidget)->data.in_destructor) |
2426 | return; |
2427 | } |
2428 | |
2429 | // There is no equivalent Mac notification for ObjectShow/Hide, so we call HIObjectSetAccessibilityIgnored |
2430 | // and isItInteresting which will mark the HIObject accociated with the element as ignored if the |
2431 | // QAccessible::Invisible state bit is set. |
2432 | QAInterface interface = accessibleHierarchyManager()->lookup(element); |
2433 | if (interface.isValid()) { |
2434 | HIObjectSetAccessibilityIgnored(element.object(), !isItInteresting(interface)); |
2435 | } |
2436 | |
2437 | // If the interface manages its own children, also check if we should ignore those. |
2438 | if (isItemView(interface) == false && managesChildren(interface)) { |
2439 | for (int i = 1; i <= interface.childCount(); ++i) { |
2440 | QAInterface childInterface = interface.navigate(QAccessible::Child, i); |
2441 | if (childInterface.isValid() && childInterface.isHIView() == false) { |
2442 | const QAElement element = accessibleHierarchyManager()->lookup(childInterface); |
2443 | if (element.isValid()) { |
2444 | HIObjectSetAccessibilityIgnored(element.object(), !isItInteresting(childInterface)); |
2445 | } |
2446 | } |
2447 | } |
2448 | } |
2449 | |
2450 | } else if(reason == Focus) { |
2451 | if(object && object->isWidgetType()) { |
2452 | QWidget *w = static_cast<QWidget*>(object); |
2453 | if(w->isWindow()) |
2454 | notification = CFStringRef(QAXFocusedWindowChangedNotification); |
2455 | else |
2456 | notification = CFStringRef(QAXFocusedUIElementChangedNotification); |
2457 | } |
2458 | } |
2459 | |
2460 | if (!notification) |
2461 | return; |
2462 | |
2463 | AXNotificationHIObjectNotify(notification, element.object(), element.id()); |
2464 | #endif |
2465 | } |
2466 | |
2467 | QT_END_NAMESPACE |
2468 | |
2469 | #endif // QT_NO_ACCESSIBILITY |
2470 |
Warning: That file was not part of the compilation database. It may have many parsing errors.