source: trunk/src/gui/accessible/qaccessible_mac.mm@ 441

Last change on this file since 441 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 93.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
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
64QT_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
71typedef 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
163typedef 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 *****************************************************************************/
258extern bool qt_mac_is_macsheet(const QWidget *w); //qwidget_mac.cpp
259extern 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
265struct 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
394class QAInterface;
395static CFStringRef macRole(const QAInterface &interface);
396
397QDebug 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.
407static QObject *rootObject = 0;
408
409
410bool 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
436bool QAInterface::operator!=(const QAInterface &other) const
437{
438 return !operator==(other);
439}
440
441uint 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
449QAInterface 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
484QAElement::QAElement()
485:elementRef(0)
486{}
487
488QAElement::QAElement(AXUIElementRef elementRef)
489:elementRef(elementRef)
490{
491 if (elementRef != 0) {
492 CFRetain(elementRef);
493 CFRetain(object());
494 }
495}
496
497QAElement::QAElement(const QAElement &element)
498:elementRef(element.elementRef)
499{
500 if (elementRef != 0) {
501 CFRetain(elementRef);
502 CFRetain(object());
503 }
504}
505
506QAElement::QAElement(HIObjectRef object, int child)
507 :elementRef(
508#ifndef QT_MAC_USE_COCOA
509 AXUIElementCreateWithHIObjectAndIdentifier(object, child)
510#endif
511)
512{
513#ifndef QT_MAC_USE_COCOA
514 if (object == 0) {
515 elementRef = 0; // Create invalid QAElement.
516 } else {
517 elementRef = AXUIElementCreateWithHIObjectAndIdentifier(object, child);
518 CFRetain(object);
519 }
520#else
521 Q_UNUSED(object);
522 Q_UNUSED(child);
523#endif
524}
525
526QAElement::~QAElement()
527{
528 if (elementRef != 0) {
529 CFRelease(object());
530 CFRelease(elementRef);
531 }
532}
533
534void QAElement::operator=(const QAElement &other)
535{
536 if (*this == other)
537 return;
538
539 if (elementRef != 0) {
540 CFRelease(object());
541 CFRelease(elementRef);
542 }
543
544 elementRef = other.elementRef;
545
546 if (elementRef != 0) {
547 CFRetain(elementRef);
548 CFRetain(object());
549 }
550}
551
552bool QAElement::operator==(const QAElement &other) const
553{
554 if (elementRef == 0 || other.elementRef == 0)
555 return (elementRef == other.elementRef);
556
557 return CFEqual(elementRef, other.elementRef);
558}
559
560uint qHash(QAElement element)
561{
562 return qHash(element.object()) + qHash(element.id());
563}
564
565#ifndef QT_MAC_USE_COCOA
566static QInterfaceFactory *createFactory(const QAInterface &interface);
567#endif
568Q_GLOBAL_STATIC(QAccessibleHierarchyManager, accessibleHierarchyManager);
569
570/*
571 Reomves all accessibility info accosiated with the sender object.
572*/
573void QAccessibleHierarchyManager::objectDestroyed(QObject *object)
574{
575 HIObjectRef hiObject = qobjectHiobjectHash.value(object);
576 delete qobjectElementHash.value(object);
577 qobjectElementHash.remove(object);
578 hiobjectInterfaceHash.remove(hiObject);
579}
580
581/*
582 Removes all stored items.
583*/
584void QAccessibleHierarchyManager::reset()
585{
586 qDeleteAll(qobjectElementHash);
587 qobjectElementHash.clear();
588 hiobjectInterfaceHash.clear();
589 qobjectHiobjectHash.clear();
590}
591
592QAccessibleHierarchyManager *QAccessibleHierarchyManager::instance()
593{
594 return accessibleHierarchyManager();
595}
596
597#ifndef QT_MAC_USE_COCOA
598static bool isItemView(const QAInterface &interface)
599{
600 QObject *object = interface.object();
601 return (interface.role() == QAccessible::List || interface.role() == QAccessible::Table
602 || (object && qobject_cast<QAbstractItemView *>(interface.object()))
603 || (object && object->objectName() == QLatin1String("qt_scrollarea_viewport")
604 && qobject_cast<QAbstractItemView *>(object->parent())));
605}
606#endif
607
608static bool isTabWidget(const QAInterface &interface)
609{
610 if (QObject *object = interface.object())
611 return (object->inherits("QTabWidget") && interface.id() == 0);
612 return false;
613}
614
615static bool isStandaloneTabBar(const QAInterface &interface)
616{
617 QObject *object = interface.object();
618 if (interface.role() == QAccessible::PageTabList && object)
619 return (qobject_cast<QTabWidget *>(object->parent()) == 0);
620
621 return false;
622}
623
624static bool isEmbeddedTabBar(const QAInterface &interface)
625{
626 QObject *object = interface.object();
627 if (interface.role() == QAccessible::PageTabList && object)
628 return (qobject_cast<QTabWidget *>(object->parent()));
629
630 return false;
631}
632
633/*
634 Decides if a QAInterface is interesting from an accessibility users point of view.
635*/
636bool isItInteresting(const QAInterface &interface)
637{
638 // Mac accessibility does not have an attribute that corresponds to the Invisible/Offscreen
639 // state, so we disable the interface here.
640 const QAccessible::State state = interface.state();
641 if (state & QAccessible::Invisible ||
642 state & QAccessible::Offscreen )
643 return false;
644
645 const QAccessible::Role role = interface.role();
646
647 if (QObject * const object = interface.object()) {
648 const QString className = QLatin1String(object->metaObject()->className());
649
650 // VoiceOver focusing on tool tips can be confusing. The contents of the
651 // tool tip is avalible through the description attribute anyway, so
652 // we disable accessibility for tool tips.
653 if (className == QLatin1String("QTipLabel"))
654 return false;
655
656 // Hide TabBars that has a QTabWidget parent (the tab widget handles the accessibility)
657 if (isEmbeddedTabBar(interface))
658 return false;
659
660 // Hide docked dockwidgets. ### causes infinitie loop in the apple accessibility code.
661 /* if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(object)) {
662 if (dockWidget->isFloating() == false)
663 return false;
664 }
665 */
666 }
667
668 // Client is a generic role returned by plain QWidgets or other
669 // widgets that does not have separate QAccessible interface, such
670 // as the TabWidget. Return false unless macRole gives the interface
671 // a special role.
672 if (role == QAccessible::Client && macRole(interface) == CFStringRef(QAXUnknownRole))
673 return false;
674
675 // Some roles are not interesting:
676 if (role == QAccessible::Border || // QFrame
677 role == QAccessible::Application || // We use the system-provided application element.
678 role == QAccessible::MenuItem) // The system also provides the menu items.
679 return false;
680
681 // It is probably better to access the toolbar buttons directly than having
682 // to navigate through the toolbar.
683 if (role == QAccessible::ToolBar)
684 return false;
685
686 return true;
687}
688
689QAElement QAccessibleHierarchyManager::registerInterface(QObject *object, int child)
690{
691#ifndef QT_MAC_USE_COCOA
692 return registerInterface(QAInterface(QAccessible::queryAccessibleInterface(object), child));
693#else
694 Q_UNUSED(object);
695 Q_UNUSED(child);
696 return QAElement();
697#endif
698}
699
700/*
701 Creates a QAXUIelement that corresponds to the given QAInterface.
702*/
703QAElement QAccessibleHierarchyManager::registerInterface(const QAInterface &interface)
704{
705#ifndef QT_MAC_USE_COCOA
706 if (interface.isValid() == false)
707 return QAElement();
708 QAInterface objectInterface = interface.objectInterface();
709
710 QObject * qobject = objectInterface.object();
711 HIObjectRef hiobject = objectInterface.hiObject();
712 if (qobject == 0 || hiobject == 0)
713 return QAElement();
714
715 if (qobjectElementHash.contains(qobject) == false) {
716 registerInterface(qobject, hiobject, createFactory(interface));
717 HIObjectSetAccessibilityIgnored(hiobject, !isItInteresting(interface));
718 }
719
720 return QAElement(hiobject, interface.id());
721#else
722 Q_UNUSED(interface);
723 return QAElement();
724#endif
725}
726
727#ifndef QT_MAC_USE_COCOA
728#include "qaccessible_mac_carbon.cpp"
729#endif
730
731void QAccessibleHierarchyManager::registerInterface(QObject * qobject, HIObjectRef hiobject, QInterfaceFactory *interfaceFactory)
732{
733#ifndef QT_MAC_USE_COCOA
734 if (qobjectElementHash.contains(qobject) == false) {
735 qobjectElementHash.insert(qobject, interfaceFactory);
736 qobjectHiobjectHash.insert(qobject, hiobject);
737 connect(qobject, SIGNAL(destroyed(QObject *)), SLOT(objectDestroyed(QObject *)));
738 }
739
740 if (hiobjectInterfaceHash.contains(hiobject) == false) {
741 hiobjectInterfaceHash.insert(hiobject, interfaceFactory);
742 installAcessibilityEventHandler(hiobject);
743 }
744#else
745 Q_UNUSED(qobject);
746 Q_UNUSED(hiobject);
747 Q_UNUSED(interfaceFactory);
748#endif
749}
750
751void QAccessibleHierarchyManager::registerChildren(const QAInterface &interface)
752{
753 QObject * const object = interface.object();
754 if (object == 0)
755 return;
756
757 QInterfaceFactory *interfaceFactory = qobjectElementHash.value(object);
758
759 if (interfaceFactory == 0)
760 return;
761
762 interfaceFactory->registerChildren();
763}
764
765QAInterface QAccessibleHierarchyManager::lookup(const AXUIElementRef &element)
766{
767 if (element == 0)
768 return QAInterface();
769#ifndef QT_MAC_USE_COCOA
770 HIObjectRef hiObject = AXUIElementGetHIObject(element);
771
772 QInterfaceFactory *factory = hiobjectInterfaceHash.value(hiObject);
773 if (factory == 0) {
774 return QAInterface();
775 }
776
777 UInt64 id;
778 AXUIElementGetIdentifier(element, &id);
779 return factory->interface(id);
780#else
781 return QAInterface();
782#endif;
783}
784
785QAInterface QAccessibleHierarchyManager::lookup(const QAElement &element)
786{
787 return lookup(element.element());
788}
789
790QAElement QAccessibleHierarchyManager::lookup(const QAInterface &interface)
791{
792 if (interface.isValid() == false)
793 return QAElement();
794
795 QInterfaceFactory *factory = qobjectElementHash.value(interface.objectInterface().object());
796 if (factory == 0)
797 return QAElement();
798
799 return factory->element(interface);
800}
801
802QAElement QAccessibleHierarchyManager::lookup(QObject * const object, int id)
803{
804 QInterfaceFactory *factory = qobjectElementHash.value(object);
805 if (factory == 0)
806 return QAElement();
807
808 return factory->element(id);
809}
810
811/*
812 Standard interface mapping, return the stored interface
813 or HIObjectRef, and there is an one-to-one mapping between
814 the identifier and child.
815*/
816class QStandardInterfaceFactory : public QInterfaceFactory
817{
818public:
819 QStandardInterfaceFactory(const QAInterface &interface)
820 : m_interface(interface), object(interface.hiObject())
821 {
822 CFRetain(object);
823 }
824
825 ~QStandardInterfaceFactory()
826 {
827 CFRelease(object);
828 }
829
830
831 QAInterface interface(UInt64 identifier)
832 {
833 const int child = identifier;
834 return QAInterface(m_interface, child);
835 }
836
837 QAElement element(int id)
838 {
839 return QAElement(object, id);
840 }
841
842 QAElement element(const QAInterface &interface)
843 {
844 if (interface.object() == 0)
845 return QAElement();
846 return QAElement(object, interface.id());
847 }
848
849 void registerChildren()
850 {
851 const int childCount = m_interface.childCount();
852 for (int i = 1; i <= childCount; ++i) {
853 accessibleHierarchyManager()->registerInterface(m_interface.navigate(QAccessible::Child, i));
854 }
855 }
856
857private:
858 QAInterface m_interface;
859 HIObjectRef object;
860};
861
862/*
863 Interface mapping where that creates one HIObject for each interface child.
864*/
865class QMultipleHIObjectFactory : public QInterfaceFactory
866{
867public:
868 QMultipleHIObjectFactory(const QAInterface &interface)
869 : m_interface(interface)
870 { }
871
872 ~QMultipleHIObjectFactory()
873 {
874 foreach (HIObjectRef object, objects) {
875 CFRelease(object);
876 }
877 }
878
879 QAInterface interface(UInt64 identifier)
880 {
881 const int child = identifier;
882 return QAInterface(m_interface, child);
883 }
884
885 QAElement element(int child)
886 {
887 if (child == 0)
888 return QAElement(m_interface.hiObject(), 0);
889
890 if (child > objects.count())
891 return QAElement();
892
893 return QAElement(objects.at(child - 1), child);
894 }
895
896 void registerChildren()
897 {
898#ifndef QT_MAC_USE_COCOA
899 const int childCount = m_interface.childCount();
900 for (int i = 1; i <= childCount; ++i) {
901 HIObjectRef hiobject;
902 HIObjectCreate(kObjectQtAccessibility, 0, &hiobject);
903 objects.append(hiobject);
904 accessibleHierarchyManager()->registerInterface(m_interface.object(), hiobject, this);
905 HIObjectSetAccessibilityIgnored(hiobject, !isItInteresting(m_interface.navigate(QAccessible::Child, i)));
906 }
907#endif
908 }
909
910private:
911 QAInterface m_interface;
912 QList<HIObjectRef> objects;
913};
914
915class QItemViewInterfaceFactory : public QInterfaceFactory
916{
917public:
918 QItemViewInterfaceFactory(const QAInterface &interface)
919 : m_interface(interface), object(interface.hiObject())
920 {
921 CFRetain(object);
922 columnCount = 0;
923 if (QTableView * tableView = qobject_cast<QTableView *>(interface.parent().object())) {
924 if (tableView->model())
925 columnCount = tableView->model()->columnCount();
926 if (tableView->verticalHeader())
927 ++columnCount;
928 }
929 }
930
931 ~QItemViewInterfaceFactory()
932 {
933 CFRelease(object);
934 }
935
936 QAInterface interface(UInt64 identifier)
937 {
938 if (identifier == 0)
939 return m_interface;
940
941 if (m_interface.role() == QAccessible::List)
942 return m_interface.childAt(identifier);
943
944 if (m_interface.role() == QAccessible::Table) {
945 const int index = identifier;
946 if (index == 0)
947 return m_interface; // return the item view interface.
948
949 const int rowIndex = (index - 1) / (columnCount + 1);
950 const int cellIndex = (index - 1) % (columnCount + 1);
951/*
952 qDebug() << "index" << index;
953 qDebug() << "rowIndex" << rowIndex;
954 qDebug() << "cellIndex" << cellIndex;
955*/
956 const QAInterface rowInterface = m_interface.childAt(rowIndex + 1);
957
958 if ((cellIndex) == 0) // Is it a row?
959 return rowInterface;
960 else {
961 return rowInterface.childAt(cellIndex);
962 }
963 }
964
965 return QAInterface();
966 }
967
968 QAElement element(int id)
969 {
970 if (id != 0) {
971 return QAElement();
972 }
973 return QAElement(object, 0);
974 }
975
976 QAElement element(const QAInterface &interface)
977 {
978 if (interface.object() && interface.object() == m_interface.object()) {
979 return QAElement(object, 0);
980 } else if (m_interface.role() == QAccessible::List) {
981 if (interface.parent().object() && interface.parent().object() == m_interface.object())
982 return QAElement(object, m_interface.indexOfChild(interface));
983 } else if (m_interface.role() == QAccessible::Table) {
984 QAInterface currentInterface = interface;
985 int index = 0;
986
987 while (currentInterface.isValid() && currentInterface.object() == 0) {
988 const QAInterface parentInterface = currentInterface.parent();
989/*
990 qDebug() << "current index" << index;
991 qDebug() << "current interface" << interface;
992
993 qDebug() << "parent interface" << parentInterface;
994 qDebug() << "grandparent interface" << parentInterface.parent();
995 qDebug() << "childCount" << interface.childCount();
996 qDebug() << "index of child" << parentInterface.indexOfChild(currentInterface);
997*/
998 index += ((parentInterface.indexOfChild(currentInterface) - 1) * (currentInterface.childCount() + 1)) + 1;
999 currentInterface = parentInterface;
1000// qDebug() << "new current interface" << currentInterface;
1001 }
1002 if (currentInterface.object() == m_interface.object())
1003 return QAElement(object, index);
1004
1005
1006 }
1007 return QAElement();
1008 }
1009
1010 void registerChildren()
1011 {
1012 // Item view child interfraces don't have their own qobjects, so there is nothing to register here.
1013 }
1014
1015private:
1016 QAInterface m_interface;
1017 HIObjectRef object;
1018 int columnCount; // for table views;
1019};
1020
1021#ifndef QT_MAC_USE_COCOA
1022static bool managesChildren(const QAInterface &interface)
1023{
1024 return (interface.childCount() > 0 && interface.childAt(1).id() > 0);
1025}
1026
1027static QInterfaceFactory *createFactory(const QAInterface &interface)
1028{
1029 if (isItemView(interface)) {
1030 return new QItemViewInterfaceFactory(interface);
1031 } if (managesChildren(interface)) {
1032 return new QMultipleHIObjectFactory(interface);
1033 }
1034
1035 return new QStandardInterfaceFactory(interface);
1036}
1037#endif
1038
1039QList<QAElement> lookup(const QList<QAInterface> &interfaces)
1040{
1041 QList<QAElement> elements;
1042 foreach (const QAInterface &interface, interfaces)
1043 if (interface.isValid()) {
1044 const QAElement element = accessibleHierarchyManager()->lookup(interface);
1045 if (element.isValid())
1046 elements.append(element);
1047 }
1048 return elements;
1049}
1050
1051// Debug output helpers:
1052/*
1053static QString nameForEventKind(UInt32 kind)
1054{
1055 switch(kind) {
1056 case kEventAccessibleGetChildAtPoint: return QString("GetChildAtPoint"); break;
1057 case kEventAccessibleGetAllAttributeNames: return QString("GetAllAttributeNames"); break;
1058 case kEventAccessibleGetNamedAttribute: return QString("GetNamedAttribute"); break;
1059 case kEventAccessibleSetNamedAttribute: return QString("SetNamedAttribute"); break;
1060 case kEventAccessibleGetAllActionNames: return QString("GetAllActionNames"); break;
1061 case kEventAccessibleGetFocusedChild: return QString("GetFocusedChild"); break;
1062 default:
1063 return QString("Unknown accessibility event type: %1").arg(kind);
1064 break;
1065 };
1066}
1067*/
1068#ifndef QT_MAC_USE_COCOA
1069static bool qt_mac_append_cf_uniq(CFMutableArrayRef array, CFTypeRef value)
1070{
1071 if (value == 0)
1072 return false;
1073
1074 CFRange range;
1075 range.location = 0;
1076 range.length = CFArrayGetCount(array);
1077 if(!CFArrayContainsValue(array, range, value)) {
1078 CFArrayAppendValue(array, value);
1079 return true;
1080 }
1081 return false;
1082}
1083
1084static OSStatus setAttributeValue(EventRef event, const QList<QAElement> &elements)
1085{
1086 CFMutableArrayRef array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
1087 foreach (const QAElement &element, elements) {
1088 if (element.isValid())
1089 CFArrayAppendValue(array, element.element());
1090 }
1091
1092 const OSStatus err = SetEventParameter(event, kEventParamAccessibleAttributeValue,
1093 typeCFTypeRef, sizeof(array), &array);
1094 CFRelease(array);
1095 return err;
1096}
1097#endif //QT_MAC_USE_COCOA
1098
1099/*
1100 Gets the AccessibleObject parameter from an event.
1101*/
1102static inline AXUIElementRef getAccessibleObjectParameter(EventRef event)
1103{
1104 AXUIElementRef element;
1105 GetEventParameter(event, kEventParamAccessibleObject, typeCFTypeRef, 0,
1106 sizeof(element), 0, &element);
1107 return element;
1108}
1109
1110/*
1111 The application event handler makes sure that all top-level qt windows are registered
1112 before any accessibility events are handeled.
1113*/
1114#ifndef QT_MAC_USE_COCOA
1115static OSStatus applicationEventHandler(EventHandlerCallRef next_ref, EventRef event, void *)
1116{
1117 QAInterface rootInterface(QAccessible::queryAccessibleInterface(rootObject ? rootObject : qApp), 0);
1118 accessibleHierarchyManager()->registerChildren(rootInterface);
1119
1120 return CallNextEventHandler(next_ref, event);
1121}
1122
1123/*
1124 Returns the value for element by combining the QAccessibility::Checked and
1125 QAccessibility::Mixed flags into an int value that the Mac accessibilty
1126 system understands. This works for check boxes, radio buttons, and the like.
1127 The return values are:
1128 0: unchecked
1129 1: checked
1130 2: undecided
1131*/
1132static int buttonValue(QAInterface element)
1133{
1134 const QAccessible::State state = element.state();
1135 if (state & QAccessible::Mixed)
1136 return 2;
1137 else if(state & QAccessible::Checked)
1138 return 1;
1139 else
1140 return 0;
1141}
1142
1143static QString getValue(const QAInterface &interface)
1144{
1145 const QAccessible::Role role = interface.role();
1146 if (role == QAccessible::RadioButton || role == QAccessible::CheckBox)
1147 return QString::number(buttonValue(interface));
1148 else
1149 return interface.text(QAccessible::Value);
1150}
1151#endif //QT_MAC_USE_COCOA
1152
1153/*
1154 Translates a QAccessible::Role into a mac accessibility role.
1155*/
1156static CFStringRef macRole(const QAInterface &interface)
1157{
1158 const QAccessible::Role qtRole = interface.role();
1159
1160// qDebug() << "role for" << interface.object() << "interface role" << hex << qtRole;
1161
1162 // Qt accessibility: QAccessible::Splitter contains QAccessible::Grip.
1163 // Mac accessibility: AXSplitGroup contains AXSplitter.
1164 if (qtRole == QAccessible::Grip) {
1165 const QAInterface parent = interface.parent();
1166 if (parent.isValid() && parent.role() == QAccessible::Splitter)
1167 return CFStringRef(QAXSplitterRole);
1168 }
1169
1170 // Tab widgets and standalone tab bars get the kAXTabGroupRole. Accessibility
1171 // for tab bars emebedded in a tab widget is handled by the tab widget.
1172 if (isTabWidget(interface) || isStandaloneTabBar(interface))
1173 return kAXTabGroupRole;
1174
1175 if (QObject *object = interface.object()) {
1176 // ### The interface for an abstract scroll area returns the generic "Client"
1177 // role, so we have to to an extra detect on the QObject here.
1178 if (object->inherits("QAbstractScrollArea") && interface.id() == 0)
1179 return CFStringRef(QAXScrollAreaRole);
1180
1181 if (object->inherits("QDockWidget"))
1182 return CFStringRef(QAXUnknownRole);
1183 }
1184
1185 int i = 0;
1186 int testRole = text_bindings[i][0].qt;
1187 while (testRole != -1) {
1188 if (testRole == qtRole)
1189 return CFStringRef(text_bindings[i][0].mac);
1190 ++i;
1191 testRole = text_bindings[i][0].qt;
1192 }
1193
1194// qDebug() << "got unknown role!" << interface << interface.parent();
1195
1196 return CFStringRef(QAXUnknownRole);
1197}
1198
1199/*
1200 Translates a QAccessible::Role and an attribute name into a QAccessible::Text, taking into
1201 account execptions listed in text_bindings.
1202*/
1203#ifndef QT_MAC_USE_COCOA
1204static int textForRoleAndAttribute(QAccessible::Role role, CFStringRef attribute)
1205{
1206 // Search for exception, return it if found.
1207 int testRole = text_bindings[0][0].qt;
1208 int i = 0;
1209 while (testRole != -1) {
1210 if (testRole == role) {
1211 int j = 1;
1212 int qtRole = text_bindings[i][j].qt;
1213 CFStringRef testAttribute = CFStringRef(text_bindings[i][j].mac);
1214 while (qtRole != -1) {
1215 if (CFStringCompare(attribute, testAttribute, 0) == kCFCompareEqualTo) {
1216 return (QAccessible::Text)qtRole;
1217 }
1218 ++j;
1219 testAttribute = CFStringRef(text_bindings[i][j].mac); /// ### custom compare
1220 qtRole = text_bindings[i][j].qt; /// ### custom compare
1221 }
1222 break;
1223 }
1224 ++i;
1225 testRole = text_bindings[i][0].qt;
1226 }
1227
1228 // Return default mappping
1229 if (CFStringCompare(attribute, CFStringRef(QAXTitleAttribute), 0) == kCFCompareEqualTo)
1230 return QAccessible::Name;
1231 else if (CFStringCompare(attribute, CFStringRef(QAXValueAttribute), 0) == kCFCompareEqualTo)
1232 return QAccessible::Value;
1233 else if (CFStringCompare(attribute, CFStringRef(QAXHelpAttribute), 0) == kCFCompareEqualTo)
1234 return QAccessible::Help;
1235#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
1236 else if (CFStringCompare(attribute, CFStringRef(QAXDescriptionAttribute), 0) == kCFCompareEqualTo)
1237 return QAccessible::Description;
1238#endif
1239 else
1240 return -1;
1241}
1242
1243/*
1244 Returns the subrole string constant for the interface if it has one,
1245 else returns an empty string.
1246*/
1247static QCFString subrole(const QAInterface &interface)
1248{
1249 const QAInterface parent = interface.parent();
1250 if (parent.isValid() == false)
1251 return QCFString();
1252
1253 if (parent.role() == QAccessible::ScrollBar) {
1254 QCFString subrole;
1255 switch(interface.id()) {
1256 case 1: subrole = CFStringRef(QAXDecrementArrowSubrole); break;
1257 case 2: subrole = CFStringRef(QAXDecrementPageSubrole); break;
1258 case 4: subrole = CFStringRef(QAXIncrementPageSubrole); break;
1259 case 5: subrole = CFStringRef(QAXIncrementArrowSubrole); break;
1260 default:
1261 break;
1262 }
1263 return subrole;
1264 }
1265 return QCFString();
1266}
1267
1268// Gets the scroll bar orientation by asking the QAbstractSlider object directly.
1269static Qt::Orientation scrollBarOrientation(const QAInterface &scrollBar)
1270{
1271 QObject *const object = scrollBar.object();
1272 if (QAbstractSlider * const sliderObject = qobject_cast<QAbstractSlider * const>(object))
1273 return sliderObject->orientation();
1274
1275 return Qt::Vertical; // D'oh! The interface wasn't a scroll bar.
1276}
1277
1278static QAInterface scrollAreaGetScrollBarInterface(const QAInterface &scrollArea, Qt::Orientation orientation)
1279{
1280 if (macRole(scrollArea) != CFStringRef(CFStringRef(QAXScrollAreaRole)))
1281 return QAInterface();
1282
1283 // Child 1 is the contents widget, 2 and 3 are the scroll bar containers wich contains possible scroll bars.
1284 for (int i = 2; i <= 3; ++i) {
1285 QAInterface scrollBarContainer = scrollArea.childAt(i);
1286 for (int i = 1; i <= scrollBarContainer.childCount(); ++i) {
1287 QAInterface scrollBar = scrollBarContainer.childAt(i);
1288 if (scrollBar.isValid() &&
1289 scrollBar.role() == QAccessible::ScrollBar &&
1290 scrollBarOrientation(scrollBar) == orientation)
1291 return scrollBar;
1292 }
1293 }
1294
1295 return QAInterface();
1296}
1297
1298static bool scrollAreaHasScrollBar(const QAInterface &scrollArea, Qt::Orientation orientation)
1299{
1300 return scrollAreaGetScrollBarInterface(scrollArea, orientation).isValid();
1301}
1302
1303static QAElement scrollAreaGetScrollBar(const QAInterface &scrollArea, Qt::Orientation orientation)
1304{
1305 return accessibleHierarchyManager()->lookup(scrollAreaGetScrollBarInterface(scrollArea, orientation));
1306}
1307
1308static QAElement scrollAreaGetContents(const QAInterface &scrollArea)
1309{
1310 // Child 1 is the contents widget,
1311 return accessibleHierarchyManager()->lookup(scrollArea.navigate(QAccessible::Child, 1));
1312}
1313
1314static QAElement tabWidgetGetContents(const QAInterface &interface)
1315{
1316 // A kAXTabGroup has a kAXContents attribute, which consists of the
1317 // ui elements for the current tab page. Get the current tab page
1318 // from the QStackedWidget, where the current visible page can
1319 // be found at index 1.
1320 QAInterface stackedWidget = interface.childAt(1);
1321 accessibleHierarchyManager()->registerChildren(stackedWidget);
1322 QAInterface tabPageInterface = stackedWidget.childAt(1);
1323 return accessibleHierarchyManager()->lookup(tabPageInterface);
1324}
1325
1326static QList<QAElement> tabBarGetTabs(const QAInterface &interface)
1327{
1328 // Get the tabs by searching for children with the "PageTab" role.
1329 // This filters out the left/right navigation buttons.
1330 accessibleHierarchyManager()->registerChildren(interface);
1331 QList<QAElement> tabs;
1332 const int numChildren = interface.childCount();
1333 for (int i = 1; i < numChildren + 1; ++i) {
1334 QAInterface child = interface.navigate(QAccessible::Child, i);
1335 if (child.isValid() && child.role() == QAccessible::PageTab) {
1336 tabs.append(accessibleHierarchyManager()->lookup(child));
1337 }
1338 }
1339 return tabs;
1340}
1341
1342static QList<QAElement> tabWidgetGetTabs(const QAInterface &interface)
1343{
1344 // Each QTabWidget has two children, a QStackedWidget and a QTabBar.
1345 // Get the tabs from the QTabBar.
1346 return tabBarGetTabs(interface.childAt(2));
1347}
1348
1349static QList<QAElement> tabWidgetGetChildren(const QAInterface &interface)
1350{
1351 // The children for a kAXTabGroup should consist of the tabs and the
1352 // contents of the current open tab page.
1353 QList<QAElement> children = tabWidgetGetTabs(interface);
1354 children += tabWidgetGetContents(interface);
1355 return children;
1356}
1357#endif //QT_MAC_USE_COCOA
1358
1359/*
1360 Returns the label (buddy) interface for interface, or 0 if it has none.
1361*/
1362/*
1363static QAInterface findLabel(const QAInterface &interface)
1364{
1365 return interface.navigate(QAccessible::Label, 1);
1366}
1367*/
1368/*
1369 Returns a list of interfaces this interface labels, or an empty list if it doesn't label any.
1370*/
1371/*
1372static QList<QAInterface> findLabelled(const QAInterface &interface)
1373{
1374 QList<QAInterface> interfaceList;
1375
1376 int count = 1;
1377 const QAInterface labelled = interface.navigate(QAccessible::Labelled, count);
1378 while (labelled.isValid()) {
1379 interfaceList.append(labelled);
1380 ++count;
1381 }
1382 return interfaceList;
1383}
1384*/
1385/*
1386 Tests if the given QAInterface has data for a mac attribute.
1387*/
1388#ifndef QT_MAC_USE_COCOA
1389static bool supportsAttribute(CFStringRef attribute, const QAInterface &interface)
1390{
1391 const int text = textForRoleAndAttribute(interface.role(), attribute);
1392
1393 // Special case: Static texts don't have a title.
1394 if (interface.role() == QAccessible::StaticText && attribute == CFStringRef(QAXTitleAttribute))
1395 return false;
1396
1397 // Return true if we the attribute matched a QAccessible::Role and we get text for that role from the interface.
1398 if (text != -1) {
1399 if (text == QAccessible::Value) // Special case for Value, see getValue()
1400 return !getValue(interface).isEmpty();
1401 else
1402 return !interface.text((QAccessible::Text)text).isEmpty();
1403 }
1404
1405 if (CFStringCompare(attribute, CFStringRef(QAXChildrenAttribute), 0) == kCFCompareEqualTo) {
1406 if (interface.childCount() > 0)
1407 return true;
1408 }
1409
1410 if (CFStringCompare(attribute, CFStringRef(QAXSubroleAttribute), 0) == kCFCompareEqualTo) {
1411 return (subrole(interface) != QCFString());
1412 }
1413
1414 return false;
1415}
1416
1417static void appendIfSupported(CFMutableArrayRef array, CFStringRef attribute, const QAInterface &interface)
1418{
1419 if (supportsAttribute(attribute, interface))
1420 qt_mac_append_cf_uniq(array, attribute);
1421}
1422
1423/*
1424 Returns the names of the attributes the give QAInterface supports.
1425*/
1426static OSStatus getAllAttributeNames(EventRef event, const QAInterface &interface, EventHandlerCallRef next_ref)
1427{
1428 // Call system event handler.
1429 OSStatus err = CallNextEventHandler(next_ref, event);
1430 if(err != noErr && err != eventNotHandledErr)
1431 return err;
1432 CFMutableArrayRef attrs = 0;
1433 GetEventParameter(event, kEventParamAccessibleAttributeNames, typeCFMutableArrayRef, 0,
1434 sizeof(attrs), 0, &attrs);
1435
1436 if (!attrs)
1437 return eventNotHandledErr;
1438
1439 // Append attribute names that are always supported.
1440 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXPositionAttribute));
1441 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSizeAttribute));
1442 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXRoleAttribute));
1443 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXEnabledAttribute));
1444 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXWindowAttribute));
1445#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
1446 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXTopLevelUIElementAttribute));
1447#endif
1448
1449 // Append these names if the QInterafceItem returns any data for them.
1450 appendIfSupported(attrs, CFStringRef(QAXTitleAttribute), interface);
1451 appendIfSupported(attrs, CFStringRef(QAXValueAttribute), interface);
1452#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
1453 appendIfSupported(attrs, CFStringRef(QAXDescriptionAttribute), interface);
1454 appendIfSupported(attrs, CFStringRef(QAXLinkedUIElementsAttribute), interface);
1455#endif
1456 appendIfSupported(attrs, CFStringRef(QAXHelpAttribute), interface);
1457 appendIfSupported(attrs, CFStringRef(QAXTitleUIElementAttribute), interface);
1458 appendIfSupported(attrs, CFStringRef(QAXChildrenAttribute), interface);
1459 appendIfSupported(attrs, CFStringRef(QAXSubroleAttribute), interface);
1460
1461 // Append attribute names based on the interaface role.
1462 switch (interface.role()) {
1463 case QAccessible::Window:
1464 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMainAttribute));
1465 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinimizedAttribute));
1466 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXCloseButtonAttribute));
1467 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXZoomButtonAttribute));
1468 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinimizeButtonAttribute));
1469 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXToolbarButtonAttribute));
1470 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXGrowAreaAttribute));
1471 break;
1472 case QAccessible::RadioButton:
1473 case QAccessible::CheckBox:
1474 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinValueAttribute));
1475 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMaxValueAttribute));
1476 break;
1477 case QAccessible::ScrollBar:
1478 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXOrientationAttribute));
1479 break;
1480 case QAccessible::Splitter:
1481 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSplittersAttribute));
1482 break;
1483 case QAccessible::Table:
1484 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXRowsAttribute));
1485 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXVisibleRowsAttribute));
1486 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSelectedRowsAttribute));
1487 break;
1488 default:
1489 break;
1490 }
1491
1492 // Append attribute names based on the mac accessibility role.
1493 const QCFString mac_role = macRole(interface);
1494 if (mac_role == CFStringRef(QAXSplitterRole)) {
1495 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXPreviousContentsAttribute));
1496 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXNextContentsAttribute));
1497 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXOrientationAttribute));
1498 } else if (mac_role == CFStringRef(QAXScrollAreaRole)) {
1499 if (scrollAreaHasScrollBar(interface, Qt::Horizontal))
1500 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXHorizontalScrollBarAttribute));
1501 if (scrollAreaHasScrollBar(interface, Qt::Vertical))
1502 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXVerticalScrollBarAttribute));
1503 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXContentsAttribute));
1504 } else if (mac_role == CFStringRef(QAXTabGroupRole)) {
1505 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXTabsAttribute));
1506 // Only tab widgets can have the contents attribute, there is no way of getting
1507 // the contents from a QTabBar.
1508 if (isTabWidget(interface))
1509 qt_mac_append_cf_uniq(attrs, CFStringRef(QAXContentsAttribute));
1510 }
1511
1512 return noErr;
1513}
1514
1515static void handleStringAttribute(EventRef event, QAccessible::Text text, const QAInterface &interface)
1516{
1517 QString str = interface.text(text);
1518 if (str.isEmpty())
1519 return;
1520
1521 // Remove any html markup from the text string, or VoiceOver will read the html tags.
1522 static QTextDocument document;
1523 document.setHtml(str);
1524 str = document.toPlainText();
1525
1526 CFStringRef cfstr = QCFString::toCFStringRef(str);
1527 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, sizeof(cfstr), &cfstr);
1528}
1529
1530/*
1531 Handles the parent attribute for a interface.
1532 There are basically three cases here:
1533 1. interface is a HIView and has only HIView children.
1534 2. interface is a HIView but has children that is not a HIView
1535 3. interface is not a HIView.
1536*/
1537static OSStatus handleChildrenAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
1538{
1539 // Add the children for this interface to the global QAccessibelHierachyManager.
1540 accessibleHierarchyManager()->registerChildren(interface);
1541
1542 if (isTabWidget(interface)) {
1543 QList<QAElement> children = tabWidgetGetChildren(interface);
1544 const int childCount = children.count();
1545
1546 CFMutableArrayRef array = 0;
1547 array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
1548 for (int i = 0; i < childCount; ++i) {
1549 qt_mac_append_cf_uniq(array, children.at(i).element());
1550 }
1551
1552 OSStatus err;
1553 err = SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFArrayRef, sizeof(array), &array);
1554 if (err != noErr)
1555 qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__);
1556
1557 return noErr;
1558 }
1559
1560 const QList<QAElement> children = lookup(interface.children());
1561 const int childCount = children.count();
1562
1563 OSStatus err = eventNotHandledErr;
1564 if (interface.isHIView())
1565 err = CallNextEventHandler(next_ref, event);
1566
1567 CFMutableArrayRef array = 0;
1568 int arraySize = 0;
1569 if (err == noErr) {
1570 CFTypeRef obj = 0;
1571 err = GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, NULL , sizeof(obj), NULL, &obj);
1572 if (err == noErr && obj != 0) {
1573 array = (CFMutableArrayRef)obj;
1574 arraySize = CFArrayGetCount(array);
1575 }
1576 }
1577
1578 if (array) {
1579 CFArrayRemoveAllValues(array);
1580 for (int i = 0; i < childCount; ++i) {
1581 qt_mac_append_cf_uniq(array, children.at(i).element());
1582 }
1583 } else {
1584 array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
1585 for (int i = 0; i < childCount; ++i) {
1586 qt_mac_append_cf_uniq(array, children.at(i).element());
1587 }
1588
1589 err = SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFArrayRef, sizeof(array), &array);
1590 if (err != noErr)
1591 qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__);
1592 }
1593
1594 return noErr;
1595}
1596
1597/*
1598
1599*/
1600static OSStatus handleParentAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
1601{
1602 OSStatus err = eventNotHandledErr;
1603 if (interface.isHIView()) {
1604 err = CallNextEventHandler(next_ref, event);
1605 }
1606 if (err == noErr)
1607 return err;
1608
1609 const QAInterface parentInterface = interface.navigate(QAccessible::Ancestor, 1);
1610 const QAElement parentElement = accessibleHierarchyManager()->lookup(parentInterface);
1611
1612 if (parentElement.isValid() == false)
1613 return eventNotHandledErr;
1614
1615 AXUIElementRef elementRef = parentElement.element();
1616 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(elementRef), &elementRef);
1617 return noErr;
1618}
1619#endif
1620
1621struct IsWindowTest
1622{
1623 static inline bool test(const QAInterface &interface)
1624 {
1625 return (interface.role() == QAccessible::Window);
1626 }
1627};
1628
1629struct IsWindowAndNotDrawerOrSheetTest
1630{
1631 static inline bool test(const QAInterface &interface)
1632 {
1633 QWidget * const widget = qobject_cast<QWidget*>(interface.object());
1634 return (interface.role() == QAccessible::Window &&
1635 widget && widget->isWindow() &&
1636 !qt_mac_is_macdrawer(widget) &&
1637 !qt_mac_is_macsheet(widget));
1638 }
1639};
1640
1641/*
1642 Navigates up the iterfaces ancestor hierachy until a QAccessibleInterface that
1643 passes the Test is found. If we reach a interface that is a HIView we stop the
1644 search and call AXUIElementCopyAttributeValue.
1645*/
1646template <typename TestType>
1647OSStatus navigateAncestors(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface, CFStringRef attribute)
1648{
1649 if (interface.isHIView())
1650 return CallNextEventHandler(next_ref, event);
1651
1652 QAInterface current = interface;
1653 QAElement element;
1654 while (current.isValid()) {
1655 if (TestType::test(interface)) {
1656 element = accessibleHierarchyManager()->lookup(current);
1657 break;
1658 }
1659
1660 // If we reach an InterfaceItem that is a HiView we can hand of the search to
1661 // the system event handler. This is the common case.
1662 if (current.isHIView()) {
1663 CFTypeRef value = 0;
1664 const QAElement currentElement = accessibleHierarchyManager()->lookup(current);
1665 AXError err = AXUIElementCopyAttributeValue(currentElement.element(), attribute, &value);
1666 AXUIElementRef newElement = (AXUIElementRef)value;
1667
1668 if (err == noErr)
1669 element = QAElement(newElement);
1670
1671 if (newElement != 0)
1672 CFRelease(newElement);
1673 break;
1674 }
1675
1676 QAInterface next = current.parent();
1677 if (next.isValid() == false)
1678 break;
1679 if (next == current)
1680 break;
1681 current = next;
1682 }
1683
1684 if (element.isValid() == false)
1685 return eventNotHandledErr;
1686
1687
1688 AXUIElementRef elementRef = element.element();
1689 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef,
1690 sizeof(elementRef), &elementRef);
1691 return noErr;
1692}
1693
1694/*
1695 Returns the top-level window for an interface, which is the closest ancestor interface that
1696 has the Window role, but is not a sheet or a drawer.
1697*/
1698#ifndef QT_MAC_USE_COCOA
1699static OSStatus handleWindowAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
1700{
1701 return navigateAncestors<IsWindowAndNotDrawerOrSheetTest>(next_ref, event, interface, CFStringRef(QAXWindowAttribute));
1702}
1703
1704/*
1705 Returns the top-level window for an interface, which is the closest ancestor interface that
1706 has the Window role. (Can also be a sheet or a drawer)
1707*/
1708#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
1709static OSStatus handleTopLevelUIElementAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
1710{
1711 return navigateAncestors<IsWindowTest>(next_ref, event, interface, CFStringRef(QAXTopLevelUIElementAttribute));
1712}
1713#endif
1714
1715/*
1716 Returns the tab buttons for an interface.
1717*/
1718static OSStatus handleTabsAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
1719{
1720 Q_UNUSED(next_ref);
1721 if (isTabWidget(interface))
1722 return setAttributeValue(event, tabWidgetGetTabs(interface));
1723 else
1724 return setAttributeValue(event, tabBarGetTabs(interface));
1725}
1726
1727static OSStatus handlePositionAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface)
1728{
1729 QPoint qpoint(interface.rect().topLeft());
1730 HIPoint point;
1731 point.x = qpoint.x();
1732 point.y = qpoint.y();
1733 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(point), &point);
1734 return noErr;
1735}
1736
1737static OSStatus handleSizeAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface)
1738{
1739 QSize qSize(interface.rect().size());
1740 HISize size;
1741 size.width = qSize.width();
1742 size.height = qSize.height();
1743 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeHISize, sizeof(size), &size);
1744 return noErr;
1745}
1746
1747static OSStatus handleSubroleAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface)
1748{
1749 const QCFString role = subrole(interface);
1750 CFStringRef rolestr = (CFStringRef)role;
1751 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(rolestr), &rolestr);
1752 return noErr;
1753}
1754
1755static OSStatus handleOrientationAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
1756{
1757 QObject *const object = interface.object();
1758 Qt::Orientation orientation;
1759 if (interface.role() == QAccessible::ScrollBar) {
1760 orientation = scrollBarOrientation(interface);
1761 } else if (QSplitterHandle * const splitter = qobject_cast<QSplitterHandle * const>(object)) {
1762 // Qt reports the layout orientation, but we want the splitter handle orientation.
1763 orientation = (splitter->orientation() == Qt::Horizontal) ? Qt::Vertical : Qt::Horizontal;
1764 } else {
1765 return CallNextEventHandler(next_ref, event);
1766 }
1767 const CFStringRef orientationString = (orientation == Qt::Vertical)
1768 ? CFStringRef(QAXVerticalOrientationValue) : CFStringRef(QAXHorizontalOrientationValue);
1769 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, sizeof(orientationString), &orientationString);
1770 return noErr;
1771}
1772
1773/*
1774 Figures out the next or previous contents for a splitter.
1775*/
1776static OSStatus handleSplitterContentsAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface, QCFString nextOrPrev)
1777{
1778 if (interface.isValid() == false || interface.role() != QAccessible::Grip)
1779 return eventNotHandledErr;
1780
1781 const QAInterface parent = interface.parent();
1782 if (parent.isValid() == false)
1783 return CallNextEventHandler(next_ref, event);
1784
1785 if (parent.role() != QAccessible::Splitter)
1786 return CallNextEventHandler(next_ref, event);
1787
1788 const QSplitter * const splitter = qobject_cast<const QSplitter * const>(parent.object());
1789 if (splitter == 0)
1790 return CallNextEventHandler(next_ref, event);
1791
1792 QWidget * const splitterHandle = qobject_cast<QWidget * const>(interface.object());
1793 const int splitterHandleIndex = splitter->indexOf(splitterHandle);
1794 const int widgetIndex = (nextOrPrev == QCFString(CFStringRef(QAXPreviousContentsAttribute))) ? splitterHandleIndex - 1 : splitterHandleIndex;
1795 const QAElement contentsElement = accessibleHierarchyManager()->lookup(splitter->widget(widgetIndex), 0);
1796 return setAttributeValue(event, QList<QAElement>() << contentsElement);
1797}
1798
1799/*
1800 Creates a list of all splitter handles the splitter contains.
1801*/
1802static OSStatus handleSplittersAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
1803{
1804 const QSplitter * const splitter = qobject_cast<const QSplitter * const>(interface.object());
1805 if (splitter == 0)
1806 return CallNextEventHandler(next_ref, event);
1807
1808 accessibleHierarchyManager()->registerChildren(interface);
1809
1810 QList<QAElement> handles;
1811 const int visibleSplitterCount = splitter->count() -1; // skip first handle, it's always invisible.
1812 for (int i = 0; i < visibleSplitterCount; ++i)
1813 handles.append(accessibleHierarchyManager()->lookup(splitter->handle(i + 1), 0));
1814
1815 return setAttributeValue(event, handles);
1816}
1817
1818// This handler gets the scroll bars for a scroll area
1819static OSStatus handleScrollBarAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &scrollArea, Qt::Orientation orientation)
1820{
1821 QAElement scrollBar = scrollAreaGetScrollBar(scrollArea, orientation);
1822 if (scrollBar.isValid() == false)
1823 return CallNextEventHandler(next_ref, event);
1824
1825 AXUIElementRef elementRef = scrollBar.element();
1826 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(elementRef), &elementRef);
1827 return noErr;
1828}
1829
1830// This handler gets the contents for a scroll area or tab widget.
1831static OSStatus handleContentsAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
1832{
1833 const QCFString mac_role = macRole(interface);
1834
1835 QAElement contents;
1836
1837 if (mac_role == kAXTabGroupRole) {
1838 contents = tabWidgetGetContents(interface);
1839 } else {
1840 contents = scrollAreaGetContents(interface);
1841 if (contents.isValid() == false)
1842 return CallNextEventHandler(next_ref, event);
1843 }
1844
1845 return setAttributeValue(event, QList<QAElement>() << contents);
1846}
1847
1848static OSStatus handleRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView)
1849{
1850 QList<QAElement> rows = lookup(tableView.children());
1851
1852 // kill the first row which is the horizontal header.
1853 rows.removeAt(0);
1854
1855 return setAttributeValue(event, rows);
1856}
1857
1858static OSStatus handleVisibleRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView)
1859{
1860 QList<QAElement> visibleRows;
1861
1862 QList<QAInterface> rows = tableView.children();
1863 // kill the first row which is the horizontal header.
1864 rows.removeAt(0);
1865
1866 foreach (const QAInterface &interface, rows)
1867 if ((interface.state() & QAccessible::Invisible) == false)
1868 visibleRows.append(accessibleHierarchyManager()->lookup(interface));
1869
1870 return setAttributeValue(event, visibleRows);
1871}
1872
1873static OSStatus handleSelectedRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView)
1874{
1875 QList<QAElement> selectedRows;
1876 foreach (const QAInterface &interface, tableView.children())
1877 if ((interface.state() & QAccessible::Selected))
1878 selectedRows.append(accessibleHierarchyManager()->lookup(interface));
1879
1880 return setAttributeValue(event, selectedRows);
1881}
1882
1883static OSStatus getNamedAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
1884{
1885 CFStringRef var;
1886 GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0,
1887 sizeof(var), 0, &var);
1888
1889 if (CFStringCompare(var, CFStringRef(QAXChildrenAttribute), 0) == kCFCompareEqualTo) {
1890 return handleChildrenAttribute(next_ref, event, interface);
1891#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
1892 } else if(CFStringCompare(var, CFStringRef(QAXTopLevelUIElementAttribute), 0) == kCFCompareEqualTo) {
1893 return handleTopLevelUIElementAttribute(next_ref, event, interface);
1894#endif
1895 } else if(CFStringCompare(var, CFStringRef(QAXWindowAttribute), 0) == kCFCompareEqualTo) {
1896 return handleWindowAttribute(next_ref, event, interface);
1897 } else if(CFStringCompare(var, CFStringRef(QAXParentAttribute), 0) == kCFCompareEqualTo) {
1898 return handleParentAttribute(next_ref, event, interface);
1899 } else if (CFStringCompare(var, CFStringRef(QAXPositionAttribute), 0) == kCFCompareEqualTo) {
1900 return handlePositionAttribute(next_ref, event, interface);
1901 } else if (CFStringCompare(var, CFStringRef(QAXSizeAttribute), 0) == kCFCompareEqualTo) {
1902 return handleSizeAttribute(next_ref, event, interface);
1903 } else if (CFStringCompare(var, CFStringRef(QAXRoleAttribute), 0) == kCFCompareEqualTo) {
1904 CFStringRef role = macRole(interface);
1905// ###
1906// QWidget * const widget = qobject_cast<QWidget *>(interface.object());
1907// if (role == CFStringRef(QAXUnknownRole) && widget && widget->isWindow())
1908// role = CFStringRef(QAXWindowRole);
1909
1910 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef,
1911 sizeof(role), &role);
1912
1913 } else if (CFStringCompare(var, CFStringRef(QAXEnabledAttribute), 0) == kCFCompareEqualTo) {
1914 Boolean val = !((interface.state() & QAccessible::Unavailable))
1915 && !((interface.state() & QAccessible::Invisible));
1916 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
1917 sizeof(val), &val);
1918 } else if (CFStringCompare(var, CFStringRef(QAXExpandedAttribute), 0) == kCFCompareEqualTo) {
1919 Boolean val = (interface.state() & QAccessible::Expanded);
1920 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
1921 sizeof(val), &val);
1922 } else if (CFStringCompare(var, CFStringRef(QAXSelectedAttribute), 0) == kCFCompareEqualTo) {
1923 Boolean val = (interface.state() & QAccessible::Selection);
1924 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
1925 sizeof(val), &val);
1926 } else if (CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) {
1927 Boolean val = (interface.state() & QAccessible::Focus);
1928 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
1929 sizeof(val), &val);
1930 } else if (CFStringCompare(var, CFStringRef(QAXSelectedChildrenAttribute), 0) == kCFCompareEqualTo) {
1931 const int cc = interface.childCount();
1932 QList<QAElement> selected;
1933 for (int i = 1; i <= cc; ++i) {
1934 const QAInterface child_iface = interface.navigate(QAccessible::Child, i);
1935 if (child_iface.isValid() && child_iface.state() & QAccessible::Selected)
1936 selected.append(accessibleHierarchyManager()->lookup(child_iface));
1937 }
1938
1939 return setAttributeValue(event, selected);
1940
1941 } else if (CFStringCompare(var, CFStringRef(QAXCloseButtonAttribute), 0) == kCFCompareEqualTo) {
1942 if(interface.object() && interface.object()->isWidgetType()) {
1943 Boolean val = true; //do we want to add a WState for this?
1944 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
1945 sizeof(val), &val);
1946 }
1947 } else if (CFStringCompare(var, CFStringRef(QAXZoomButtonAttribute), 0) == kCFCompareEqualTo) {
1948 if(interface.object() && interface.object()->isWidgetType()) {
1949 QWidget *widget = (QWidget*)interface.object();
1950 Boolean val = (widget->windowFlags() & Qt::WindowMaximizeButtonHint);
1951 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
1952 sizeof(val), &val);
1953 }
1954 } else if (CFStringCompare(var, CFStringRef(QAXMinimizeButtonAttribute), 0) == kCFCompareEqualTo) {
1955 if(interface.object() && interface.object()->isWidgetType()) {
1956 QWidget *widget = (QWidget*)interface.object();
1957 Boolean val = (widget->windowFlags() & Qt::WindowMinimizeButtonHint);
1958 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
1959 sizeof(val), &val);
1960 }
1961 } else if (CFStringCompare(var, CFStringRef(QAXToolbarButtonAttribute), 0) == kCFCompareEqualTo) {
1962 if(interface.object() && interface.object()->isWidgetType()) {
1963 QWidget *widget = (QWidget*)interface.object();
1964 Boolean val = qobject_cast<QMainWindow *>(widget) != 0;
1965 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
1966 sizeof(val), &val);
1967 }
1968 } else if (CFStringCompare(var, CFStringRef(QAXGrowAreaAttribute), 0) == kCFCompareEqualTo) {
1969 if(interface.object() && interface.object()->isWidgetType()) {
1970 Boolean val = true; //do we want to add a WState for this?
1971 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
1972 sizeof(val), &val);
1973 }
1974 } else if (CFStringCompare(var, CFStringRef(QAXMinimizedAttribute), 0) == kCFCompareEqualTo) {
1975 if (interface.object() && interface.object()->isWidgetType()) {
1976 QWidget *widget = (QWidget*)interface.object();
1977 Boolean val = (widget->windowState() & Qt::WindowMinimized);
1978 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
1979 sizeof(val), &val);
1980 }
1981 } else if (CFStringCompare(var, CFStringRef(QAXSubroleAttribute), 0) == kCFCompareEqualTo) {
1982 return handleSubroleAttribute(next_ref, event, interface);
1983 } else if (CFStringCompare(var, CFStringRef(QAXRoleDescriptionAttribute), 0) == kCFCompareEqualTo) {
1984#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) && !defined(QT_MAC_USE_COCOA)
1985 if (HICopyAccessibilityRoleDescription) {
1986 const CFStringRef roleDescription = HICopyAccessibilityRoleDescription(macRole(interface), 0);
1987 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef,
1988 sizeof(roleDescription), &roleDescription);
1989 } else
1990#endif
1991 {
1992 // Just use Qt::Description on 10.3
1993 handleStringAttribute(event, QAccessible::Description, interface);
1994 }
1995 } else if (CFStringCompare(var, CFStringRef(QAXTitleAttribute), 0) == kCFCompareEqualTo) {
1996 const QAccessible::Role role = interface.role();
1997 const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var);
1998 handleStringAttribute(event, text, interface);
1999 } else if (CFStringCompare(var, CFStringRef(QAXValueAttribute), 0) == kCFCompareEqualTo) {
2000 const QAccessible::Role role = interface.role();
2001 const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var);
2002 if (role == QAccessible::CheckBox || role == QAccessible::RadioButton) {
2003 int value = buttonValue(interface);
2004 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value);
2005 } else {
2006 handleStringAttribute(event, text, interface);
2007 }
2008#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
2009 } else if (CFStringCompare(var, CFStringRef(QAXDescriptionAttribute), 0) == kCFCompareEqualTo) {
2010 const QAccessible::Role role = interface.role();
2011 const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var);
2012 handleStringAttribute(event, text, interface);
2013 } else if (CFStringCompare(var, CFStringRef(QAXLinkedUIElementsAttribute), 0) == kCFCompareEqualTo) {
2014 return CallNextEventHandler(next_ref, event);
2015#endif
2016 } else if (CFStringCompare(var, CFStringRef(QAXHelpAttribute), 0) == kCFCompareEqualTo) {
2017 const QAccessible::Role role = interface.role();
2018 const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var);
2019 handleStringAttribute(event, text, interface);
2020 } else if (CFStringCompare(var, kAXTitleUIElementAttribute, 0) == kCFCompareEqualTo) {
2021 return CallNextEventHandler(next_ref, event);
2022 } else if (CFStringCompare(var, CFStringRef(QAXTabsAttribute), 0) == kCFCompareEqualTo) {
2023 return handleTabsAttribute(next_ref, event, interface);
2024 } else if (CFStringCompare(var, CFStringRef(QAXMinValueAttribute), 0) == kCFCompareEqualTo) {
2025 // tabs we first go to the tab bar which is child #2.
2026 QAInterface tabBarInterface = interface.childAt(2);
2027 return handleTabsAttribute(next_ref, event, tabBarInterface);
2028 } else if (CFStringCompare(var, CFStringRef(QAXMinValueAttribute), 0) == kCFCompareEqualTo) {
2029 if (interface.role() == QAccessible::RadioButton || interface.role() == QAccessible::CheckBox) {
2030 uint value = 0;
2031 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value);
2032 } else {
2033 return CallNextEventHandler(next_ref, event);
2034 }
2035 } else if (CFStringCompare(var, CFStringRef(QAXMaxValueAttribute), 0) == kCFCompareEqualTo) {
2036 if (interface.role() == QAccessible::RadioButton || interface.role() == QAccessible::CheckBox) {
2037 uint value = 2;
2038 SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value);
2039 } else {
2040 return CallNextEventHandler(next_ref, event);
2041 }
2042 } else if (CFStringCompare(var, CFStringRef(QAXOrientationAttribute), 0) == kCFCompareEqualTo) {
2043 return handleOrientationAttribute(next_ref, event, interface);
2044 } else if (CFStringCompare(var, CFStringRef(QAXPreviousContentsAttribute), 0) == kCFCompareEqualTo) {
2045 return handleSplitterContentsAttribute(next_ref, event, interface, CFStringRef(QAXPreviousContentsAttribute));
2046 } else if (CFStringCompare(var, CFStringRef(QAXNextContentsAttribute), 0) == kCFCompareEqualTo) {
2047 return handleSplitterContentsAttribute(next_ref, event, interface, CFStringRef(QAXNextContentsAttribute));
2048 } else if (CFStringCompare(var, CFStringRef(QAXSplittersAttribute), 0) == kCFCompareEqualTo) {
2049 return handleSplittersAttribute(next_ref, event, interface);
2050 } else if (CFStringCompare(var, CFStringRef(QAXHorizontalScrollBarAttribute), 0) == kCFCompareEqualTo) {
2051 return handleScrollBarAttribute(next_ref, event, interface, Qt::Horizontal);
2052 } else if (CFStringCompare(var, CFStringRef(QAXVerticalScrollBarAttribute), 0) == kCFCompareEqualTo) {
2053 return handleScrollBarAttribute(next_ref, event, interface, Qt::Vertical);
2054 } else if (CFStringCompare(var, CFStringRef(QAXContentsAttribute), 0) == kCFCompareEqualTo) {
2055 return handleContentsAttribute(next_ref, event, interface);
2056 } else if (CFStringCompare(var, CFStringRef(QAXRowsAttribute), 0) == kCFCompareEqualTo) {
2057 return handleRowsAttribute(next_ref, event, interface);
2058 } else if (CFStringCompare(var, CFStringRef(QAXVisibleRowsAttribute), 0) == kCFCompareEqualTo) {
2059 return handleVisibleRowsAttribute(next_ref, event, interface);
2060 } else if (CFStringCompare(var, CFStringRef(QAXSelectedRowsAttribute), 0) == kCFCompareEqualTo) {
2061 return handleSelectedRowsAttribute(next_ref, event, interface);
2062 } else {
2063 return CallNextEventHandler(next_ref, event);
2064 }
2065 return noErr;
2066}
2067
2068static OSStatus isNamedAttributeSettable(EventRef event, const QAInterface &interface)
2069{
2070 CFStringRef var;
2071 GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0,
2072 sizeof(var), 0, &var);
2073 Boolean settable = false;
2074 if (CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) {
2075 settable = true;
2076 } else {
2077 for (int r = 0; text_bindings[r][0].qt != -1; r++) {
2078 if(interface.role() == (QAccessible::Role)text_bindings[r][0].qt) {
2079 for (int a = 1; text_bindings[r][a].qt != -1; a++) {
2080 if (CFStringCompare(var, CFStringRef(text_bindings[r][a].mac), 0) == kCFCompareEqualTo) {
2081 settable = text_bindings[r][a].settable;
2082 break;
2083 }
2084 }
2085 }
2086 }
2087 }
2088 SetEventParameter(event, kEventParamAccessibleAttributeSettable, typeBoolean,
2089 sizeof(settable), &settable);
2090 return noErr;
2091}
2092
2093static OSStatus getChildAtPoint(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
2094{
2095 Q_UNUSED(next_ref);
2096 if (interface.isValid() == false)
2097 return eventNotHandledErr;
2098
2099 // Add the children for this interface to the global QAccessibelHierachyManager.
2100 accessibleHierarchyManager()->registerChildren(interface);
2101
2102 Point where;
2103 GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(where), 0, &where);
2104 const QAInterface childInterface = interface.childAt(where.h, where.v);
2105
2106 if (childInterface.isValid() == false || childInterface == interface)
2107 return eventNotHandledErr;
2108
2109 const QAElement element = accessibleHierarchyManager()->lookup(childInterface);
2110 if (element.isValid() == false)
2111 return eventNotHandledErr;
2112
2113 AXUIElementRef elementRef = element.element();
2114 CFRetain(elementRef);
2115 SetEventParameter(event, kEventParamAccessibleChild, typeCFTypeRef,
2116 sizeof(elementRef), &elementRef);
2117
2118 return noErr;
2119}
2120
2121/*
2122 Returns a list of actions the given interface supports.
2123 Currently implemented by getting the interface role and deciding based on that.
2124*/
2125static QList<QAccessible::Action> supportedPredefinedActions(const QAInterface &interface)
2126{
2127 QList<QAccessible::Action> actions;
2128 switch (interface.role()) {
2129 default:
2130 // Most things can be pressed.
2131 actions.append(QAccessible::Press);
2132 break;
2133 }
2134
2135 return actions;
2136}
2137
2138/*
2139 Translates a predefined QAccessible::Action to a Mac action constant.
2140 Returns an empty string if the Qt Action has no mac equivalent.
2141*/
2142static QCFString translateAction(const QAccessible::Action action)
2143{
2144 switch (action) {
2145 case QAccessible::Press:
2146 return CFStringRef(QAXPressAction);
2147 break;
2148 case QAccessible::Increase:
2149 return CFStringRef(QAXIncrementAction);
2150 break;
2151 case QAccessible::Decrease:
2152 return CFStringRef(QAXDecrementAction);
2153 break;
2154 case QAccessible::Accept:
2155 return CFStringRef(QAXConfirmAction);
2156 break;
2157 case QAccessible::Select:
2158 return CFStringRef(QAXPickAction);
2159 break;
2160 case QAccessible::Cancel:
2161 return CFStringRef(QAXCancelAction);
2162 break;
2163 default:
2164 return QCFString();
2165 break;
2166 }
2167}
2168
2169/*
2170 Translates between a Mac action constant and a QAccessible::Action.
2171 Returns QAccessible::Default action if there is no Qt predefined equivalent.
2172*/
2173static QAccessible::Action translateAction(const CFStringRef actionName)
2174{
2175 if(CFStringCompare(actionName, CFStringRef(QAXPressAction), 0) == kCFCompareEqualTo) {
2176 return QAccessible::Press;
2177 } else if(CFStringCompare(actionName, CFStringRef(QAXIncrementAction), 0) == kCFCompareEqualTo) {
2178 return QAccessible::Increase;
2179 } else if(CFStringCompare(actionName, CFStringRef(QAXDecrementAction), 0) == kCFCompareEqualTo) {
2180 return QAccessible::Decrease;
2181 } else if(CFStringCompare(actionName, CFStringRef(QAXConfirmAction), 0) == kCFCompareEqualTo) {
2182 return QAccessible::Accept;
2183 } else if(CFStringCompare(actionName, CFStringRef(QAXPickAction), 0) == kCFCompareEqualTo) {
2184 return QAccessible::Select;
2185 } else if(CFStringCompare(actionName, CFStringRef(QAXCancelAction), 0) == kCFCompareEqualTo) {
2186 return QAccessible::Cancel;
2187 } else {
2188 return QAccessible::DefaultAction;
2189 }
2190}
2191#endif // QT_MAC_USE_COCOA
2192
2193/*
2194 Copies the translated names all supported actions for an interface into the kEventParamAccessibleActionNames
2195 event parameter.
2196*/
2197#ifndef QT_MAC_USE_COCOA
2198static OSStatus getAllActionNames(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
2199{
2200 Q_UNUSED(next_ref);
2201
2202 CFMutableArrayRef actions = 0;
2203 GetEventParameter(event, kEventParamAccessibleActionNames, typeCFMutableArrayRef, 0,
2204 sizeof(actions), 0, &actions);
2205
2206 // Add supported predefined actions.
2207 const QList<QAccessible::Action> predefinedActions = supportedPredefinedActions(interface);
2208 for (int i = 0; i < predefinedActions.count(); ++i) {
2209 const QCFString action = translateAction(predefinedActions.at(i));
2210 if (action != QCFString())
2211 qt_mac_append_cf_uniq(actions, action);
2212 }
2213
2214 // Add user actions
2215 const int actionCount = interface.userActionCount();
2216 for (int i = 0; i < actionCount; ++i) {
2217 const QString actionName = interface.actionText(i, QAccessible::Name);
2218 qt_mac_append_cf_uniq(actions, QCFString::toCFStringRef(actionName));
2219 }
2220
2221 return noErr;
2222}
2223#endif
2224
2225/*
2226 Handles the perforNamedAction event.
2227*/
2228#ifndef QT_MAC_USE_COCOA
2229static OSStatus performNamedAction(EventHandlerCallRef next_ref, EventRef event, const QAInterface& interface)
2230{
2231 Q_UNUSED(next_ref);
2232
2233 CFStringRef act;
2234 GetEventParameter(event, kEventParamAccessibleActionName, typeCFStringRef, 0,
2235 sizeof(act), 0, &act);
2236
2237 const QAccessible::Action action = translateAction(act);
2238
2239 // Perform built-in action
2240 if (action != QAccessible::DefaultAction) {
2241 interface.doAction(action, QVariantList());
2242 return noErr;
2243 }
2244
2245 // Search for user-defined actions and perform it if found.
2246 const int actCount = interface.userActionCount();
2247 const QString qAct = QCFString::toQString(act);
2248 for(int i = 0; i < actCount; i++) {
2249 if(interface.actionText(i, QAccessible::Name) == qAct) {
2250 interface.doAction(i, QVariantList());
2251 break;
2252 }
2253 }
2254 return noErr;
2255}
2256
2257static OSStatus setNamedAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
2258{
2259 Q_UNUSED(next_ref);
2260 Q_UNUSED(event);
2261
2262 CFStringRef var;
2263 GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0,
2264 sizeof(var), 0, &var);
2265 if(CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) {
2266 CFTypeRef val;
2267 if(GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, 0,
2268 sizeof(val), 0, &val) == noErr) {
2269 if(CFGetTypeID(val) == CFBooleanGetTypeID() &&
2270 CFEqual(static_cast<CFBooleanRef>(val), kCFBooleanTrue)) {
2271 interface.doAction(QAccessible::SetFocus);
2272 }
2273 }
2274 } else {
2275 bool found = false;
2276 for(int r = 0; text_bindings[r][0].qt != -1; r++) {
2277 if(interface.role() == (QAccessible::Role)text_bindings[r][0].qt) {
2278 for(int a = 1; text_bindings[r][a].qt != -1; a++) {
2279 if(CFStringCompare(var, CFStringRef(text_bindings[r][a].mac), 0) == kCFCompareEqualTo) {
2280 if(!text_bindings[r][a].settable) {
2281 } else {
2282 CFTypeRef val;
2283 if(GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, 0,
2284 sizeof(val), 0, &val) == noErr) {
2285 if(CFGetTypeID(val) == CFStringGetTypeID())
2286 interface.setText((QAccessible::Text)text_bindings[r][a].qt,
2287 QCFString::toQString(static_cast<CFStringRef>(val)));
2288
2289 }
2290 }
2291 found = true;
2292 break;
2293 }
2294 }
2295 break;
2296 }
2297 }
2298 }
2299 return noErr;
2300}
2301
2302/*
2303 This is the main accessibility event handler.
2304*/
2305static OSStatus accessibilityEventHandler(EventHandlerCallRef next_ref, EventRef event, void *data)
2306{
2307 Q_UNUSED(data)
2308
2309 // Return if this event is not a AccessibleGetNamedAttribute event.
2310 const UInt32 eclass = GetEventClass(event);
2311 if (eclass != kEventClassAccessibility)
2312 return eventNotHandledErr;
2313
2314 // Get the AXUIElementRef and QAInterface pointer
2315 AXUIElementRef element = 0;
2316 GetEventParameter(event, kEventParamAccessibleObject, typeCFTypeRef, 0, sizeof(element), 0, &element);
2317 QAInterface interface = accessibleHierarchyManager()->lookup(element);
2318 if (interface.isValid() == false)
2319 return eventNotHandledErr;
2320
2321 const UInt32 ekind = GetEventKind(event);
2322 OSStatus status = noErr;
2323 switch (ekind) {
2324 case kEventAccessibleGetAllAttributeNames:
2325 status = getAllAttributeNames(event, interface, next_ref);
2326 break;
2327 case kEventAccessibleGetNamedAttribute:
2328 status = getNamedAttribute(next_ref, event, interface);
2329 break;
2330 case kEventAccessibleIsNamedAttributeSettable:
2331 status = isNamedAttributeSettable(event, interface);
2332 break;
2333 case kEventAccessibleGetChildAtPoint:
2334 status = getChildAtPoint(next_ref, event, interface);
2335 break;
2336 case kEventAccessibleGetAllActionNames:
2337 status = getAllActionNames(next_ref, event, interface);
2338 break;
2339 case kEventAccessibleGetFocusedChild:
2340 status = CallNextEventHandler(next_ref, event);
2341 break;
2342 case kEventAccessibleSetNamedAttribute:
2343 status = setNamedAttribute(next_ref, event, interface);
2344 break;
2345 case kEventAccessiblePerformNamedAction:
2346 status = performNamedAction(next_ref, event, interface);
2347 break;
2348 default:
2349 status = CallNextEventHandler(next_ref, event);
2350 break;
2351 };
2352 return status;
2353}
2354#endif
2355
2356void QAccessible::initialize()
2357{
2358#ifndef QT_MAC_USE_COCOA
2359 registerQtAccessibilityHIObjectSubclass();
2360 installApplicationEventhandler();
2361#endif
2362}
2363
2364// Sets thre root object for the application
2365void QAccessible::setRootObject(QObject *object)
2366{
2367 // Call installed root object handler if we have one
2368 if (rootObjectHandler) {
2369 rootObjectHandler(object);
2370 return;
2371 }
2372
2373 rootObject = object;
2374}
2375
2376void QAccessible::cleanup()
2377{
2378 accessibleHierarchyManager()->reset();
2379#ifndef QT_MAC_USE_COCOA
2380 removeEventhandler(applicationEventHandlerUPP);
2381 removeEventhandler(objectCreateEventHandlerUPP);
2382 removeEventhandler(accessibilityEventHandlerUPP);
2383#endif
2384}
2385
2386void QAccessible::updateAccessibility(QObject *object, int child, Event reason)
2387{
2388 // Call installed update handler if we have one.
2389 if (updateHandler) {
2390 updateHandler(object, child, reason);
2391 return;
2392 }
2393
2394#ifndef QT_MAC_USE_COCOA
2395 // Return if the mac accessibility is not enabled.
2396 if(!AXAPIEnabled())
2397 return;
2398
2399 // Work around crash, disable accessiblity for focus frames.
2400 if (qstrcmp(object->metaObject()->className(), "QFocusFrame") == 0)
2401 return;
2402
2403// qDebug() << "updateAccessibility" << object << child << hex << reason;
2404
2405 if (reason == ObjectShow) {
2406 QAInterface interface = QAInterface(QAccessible::queryAccessibleInterface(object), child);
2407 accessibleHierarchyManager()->registerInterface(interface);
2408 }
2409
2410 const QAElement element = accessibleHierarchyManager()->lookup(object, child);
2411 if (element.isValid() == false)
2412 return;
2413
2414
2415 CFStringRef notification = 0;
2416 if(object && object->isWidgetType() && reason == ObjectCreated) {
2417 notification = CFStringRef(QAXWindowCreatedNotification);
2418 } else if(reason == ValueChanged) {
2419 notification = CFStringRef(QAXValueChangedNotification);
2420 } else if(reason == MenuStart) {
2421 notification = CFStringRef(QAXMenuOpenedNotification);
2422 } else if(reason == MenuEnd) {
2423 notification = CFStringRef(QAXMenuClosedNotification);
2424 } else if(reason == LocationChanged) {
2425 notification = CFStringRef(QAXWindowMovedNotification);
2426 } else if(reason == ObjectShow || reason == ObjectHide ) {
2427 // When a widget is deleted we get a ObjectHide before the destroyed(QObject *)
2428 // signal is emitted (which makes sense). However, at this point we are in the
2429 // middle of the QWidget destructor which means that we have to be careful when
2430 // using the widget pointer. Since we can't control what the accessibilty interfaces
2431 // does when navigate() is called below we ignore the hide update in this case.
2432 // (the widget will be deleted soon anyway.)
2433 extern QWidgetPrivate * qt_widget_private(QWidget *);
2434 if (QWidget *widget = qobject_cast<QWidget*>(object)) {
2435 if (qt_widget_private(widget)->data.in_destructor)
2436 return;
2437
2438 // Check widget parent as well, special case for preventing crash
2439 // when the viewport() of an abstract scroll area is hidden when
2440 // the QWidget destructor hides all its children.
2441 QWidget *parentWidget = widget->parentWidget();
2442 if (parentWidget && qt_widget_private(parentWidget)->data.in_destructor)
2443 return;
2444 }
2445
2446 // There is no equivalent Mac notification for ObjectShow/Hide, so we call HIObjectSetAccessibilityIgnored
2447 // and isItIntersting which will mark the HIObject accociated with the element as ignored if the
2448 // QAccessible::Invisible state bit is set.
2449 QAInterface interface = accessibleHierarchyManager()->lookup(element);
2450 if (interface.isValid()) {
2451 HIObjectSetAccessibilityIgnored(element.object(), !isItInteresting(interface));
2452 }
2453
2454 // If the interface manages its own children, also check if we should ignore those.
2455 if (isItemView(interface) == false && managesChildren(interface)) {
2456 for (int i = 1; i <= interface.childCount(); ++i) {
2457 QAInterface childInterface = interface.navigate(QAccessible::Child, i);
2458 if (childInterface.isValid() && childInterface.isHIView() == false) {
2459 const QAElement element = accessibleHierarchyManager()->lookup(childInterface);
2460 if (element.isValid()) {
2461 HIObjectSetAccessibilityIgnored(element.object(), !isItInteresting(childInterface));
2462 }
2463 }
2464 }
2465 }
2466
2467 } else if(reason == Focus) {
2468 if(object && object->isWidgetType()) {
2469 QWidget *w = static_cast<QWidget*>(object);
2470 if(w->isWindow())
2471 notification = CFStringRef(QAXFocusedWindowChangedNotification);
2472 else
2473 notification = CFStringRef(QAXFocusedUIElementChangedNotification);
2474 }
2475 }
2476
2477 if (!notification)
2478 return;
2479
2480 AXNotificationHIObjectNotify(notification, element.object(), element.id());
2481#endif
2482}
2483
2484QT_END_NAMESPACE
2485
2486#endif // QT_NO_ACCESSIBILITY
Note: See TracBrowser for help on using the repository browser.