1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "osx/salinst.h"
22 #include "osx/saldata.hxx"
24 #include "osx/a11ywrapper.h"
25 #include "osx/a11ylistener.hxx"
26 #include "osx/a11yfactory.h"
27 #include "osx/a11yfocustracker.hxx"
29 #include "quartz/utils.h"
31 #include "a11yfocuslistener.hxx"
32 #include "a11yactionwrapper.h"
33 #include "a11ycomponentwrapper.h"
34 #include "a11yselectionwrapper.h"
35 #include "a11ytablewrapper.h"
36 #include "a11ytextwrapper.h"
37 #include "a11yvaluewrapper.h"
38 #include "a11yrolehelper.h"
40 #include <com/sun/star/accessibility/AccessibleRole.hpp>
41 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
42 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
43 #include <com/sun/star/awt/Size.hpp>
44 #include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
45 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
46 #include <com/sun/star/lang/DisposedException.hpp>
48 using namespace ::com::sun::star::accessibility;
49 using namespace ::com::sun::star::awt;
50 using namespace ::com::sun::star::lang;
51 using namespace ::com::sun::star::uno;
53 @interface SalFrameWindow : NSWindow
56 -(Reference<XAccessibleContext>)accessibleContext;
59 static BOOL isPopupMenuOpen = NO;
61 static std::ostream &operator<<(std::ostream &s, NSObject *obj) {
62 return s << [[obj description] UTF8String];
65 @implementation AquaA11yWrapper : NSView
68 #pragma mark Init and dealloc
70 -(id)initWithAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext {
71 self = [ super init ];
73 [ self setDefaults: rxAccessibleContext ];
78 -(void) setDefaults: (Reference < XAccessibleContext >) rxAccessibleContext {
79 mpReferenceWrapper = new ReferenceWrapper;
80 mActsAsRadioGroup = NO;
81 mpReferenceWrapper -> rAccessibleContext = rxAccessibleContext;
83 // Querying all supported interfaces
85 // XAccessibleComponent
86 mpReferenceWrapper -> rAccessibleComponent = Reference < XAccessibleComponent > ( rxAccessibleContext, UNO_QUERY );
87 // XAccessibleExtendedComponent
88 mpReferenceWrapper -> rAccessibleExtendedComponent = Reference < XAccessibleExtendedComponent > ( rxAccessibleContext, UNO_QUERY );
89 // XAccessibleSelection
90 mpReferenceWrapper -> rAccessibleSelection = Reference< XAccessibleSelection > ( rxAccessibleContext, UNO_QUERY );
92 mpReferenceWrapper -> rAccessibleTable = Reference < XAccessibleTable > ( rxAccessibleContext, UNO_QUERY );
94 mpReferenceWrapper -> rAccessibleText = Reference < XAccessibleText > ( rxAccessibleContext, UNO_QUERY );
95 // XAccessibleEditableText
96 mpReferenceWrapper -> rAccessibleEditableText = Reference < XAccessibleEditableText > ( rxAccessibleContext, UNO_QUERY );
98 mpReferenceWrapper -> rAccessibleValue = Reference < XAccessibleValue > ( rxAccessibleContext, UNO_QUERY );
100 mpReferenceWrapper -> rAccessibleAction = Reference < XAccessibleAction > ( rxAccessibleContext, UNO_QUERY );
101 // XAccessibleTextAttributes
102 mpReferenceWrapper -> rAccessibleTextAttributes = Reference < XAccessibleTextAttributes > ( rxAccessibleContext, UNO_QUERY );
103 // XAccessibleMultiLineText
104 mpReferenceWrapper -> rAccessibleMultiLineText = Reference < XAccessibleMultiLineText > ( rxAccessibleContext, UNO_QUERY );
105 // XAccessibleTextMarkup
106 mpReferenceWrapper -> rAccessibleTextMarkup = Reference < XAccessibleTextMarkup > ( rxAccessibleContext, UNO_QUERY );
107 // XAccessibleEventBroadcaster
109 /* #i102033# NSAccessibility does not seemt to know an equivalent for transient children.
110 That means we need to cache this, else e.g. tree list boxes are not accessible (moreover
111 it crashes by notifying dead objects - which would seemt o be another bug)
114 Unfortunately this can increase memory consumption drastically until the non transient parent
115 is destroyed an finally all the transients are released.
117 if ( ! rxAccessibleContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::TRANSIENT ) )
120 Reference< XAccessibleEventBroadcaster > xBroadcaster(rxAccessibleContext, UNO_QUERY);
121 if( xBroadcaster.is() ) {
123 * We intentionally do not hold a reference to the event listener in the wrapper object,
124 * but let the listener control the life cycle of the wrapper instead ..
126 xBroadcaster->addAccessibleEventListener( new AquaA11yEventListener( self, rxAccessibleContext -> getAccessibleRole() ) );
130 if ( rxAccessibleContext -> getAccessibleRole() == AccessibleRole::TABLE_CELL ) {
133 } catch ( const Exception ) {
138 if ( mpReferenceWrapper ) {
139 delete mpReferenceWrapper;
145 #pragma mark Utility Section
147 // generates selectors for attribute name AXAttributeNameHere
148 // (getter without parameter) attributeNameHereAttribute
149 // (getter with parameter) attributeNameHereAttributeForParameter:
150 // (setter) setAttributeNameHereAttributeForElement:to:
151 -(SEL)selectorForAttribute:(NSString *)attribute asGetter:(BOOL)asGetter withGetterParameter:(BOOL)withGetterParameter {
152 SEL selector = (SEL)nil;
153 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
155 // step 1: create method name from attribute name
156 NSMutableString * methodName = [ NSMutableString string ];
158 [ methodName appendString: @"set" ];
160 NSRange aRange = { 2, 1 };
161 NSString * firstChar = [ attribute substringWithRange: aRange ]; // drop leading "AX" and get first char
163 [ methodName appendString: [ firstChar lowercaseString ] ];
165 [ methodName appendString: firstChar ];
167 [ methodName appendString: [ attribute substringFromIndex: 3 ] ]; // append rest of attribute name
168 // append rest of method name
169 [ methodName appendString: @"Attribute" ];
171 [ methodName appendString: @"ForElement:to:" ];
172 } else if ( asGetter && withGetterParameter ) {
173 [ methodName appendString: @"ForParameter:" ];
175 // step 2: create selector
176 selector = NSSelectorFromString ( methodName );
177 } @catch ( id exception ) {
184 -(Reference < XAccessible >)getFirstRadioButtonInGroup {
185 Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
186 if( rxAccessibleRelationSet.is() )
188 AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF );
189 if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() )
190 return Reference < XAccessible > ( relationMemberOf.TargetSet[0], UNO_QUERY );
192 return Reference < XAccessible > ();
195 -(BOOL)isFirstRadioButtonInGroup {
196 Reference < XAccessible > rFirstMateAccessible = [ self getFirstRadioButtonInGroup ];
197 if ( rFirstMateAccessible.is() && rFirstMateAccessible -> getAccessibleContext().get() == [ self accessibleContext ] ) {
204 #pragma mark Attribute Value Getters
205 // ( called via Reflection by accessibilityAttributeValue )
208 Radiobutton grouping is done differently in NSAccessibility and the UNO-API. In UNO related radio buttons share an entry in their
209 RelationSet. In NSAccessibility the relationship is axpressed through the hierarchy. A AXRadioGroup contains two or more AXRadioButton
210 objects. Since this group is not available in the UNO hierarchy, an extra wrapper is used for it. This wrapper shares almost all
211 attributes with the first radio button of the group, except for the role, subrole, role description, parent and children attributes.
212 So in this five methods there is a special treatment for radio buttons and groups.
216 if ( mActsAsRadioGroup ) {
217 return NSAccessibilityRadioGroupRole;
220 return [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ];
224 -(id)subroleAttribute {
225 if ( mActsAsRadioGroup ) {
228 NSString * subRole = [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ];
229 if ( ! [ subRole isEqualToString: @"" ] ) {
233 SAL_WNODEPRECATED_DECLARATIONS_PUSH
234 //TODO: 10.10 accessibilityAttributeValue:
235 return [ super accessibilityAttributeValue: NSAccessibilitySubroleAttribute ];
236 SAL_WNODEPRECATED_DECLARATIONS_POP
241 -(id)titleAttribute {
242 return CreateNSString ( [ self accessibleContext ] -> getAccessibleName() );
245 -(id)descriptionAttribute {
246 if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) {
247 return [ self titleAttribute ];
248 } else if ( [ self accessibleExtendedComponent ] ) {
249 return [ AquaA11yComponentWrapper descriptionAttributeForElement: self ];
251 return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() );
255 -(id)enabledAttribute {
256 if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) {
257 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::ENABLED ) ];
263 -(id)focusedAttribute {
264 if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) {
266 Reference < XAccessible > rxParent = [ self accessibleContext ] -> getAccessibleParent();
267 if ( rxParent.is() ) {
268 Reference < XAccessibleContext > rxContext = rxParent -> getAccessibleContext();
269 if ( rxContext.is() && rxContext -> getAccessibleStateSet().is() ) {
270 isFocused = [ NSNumber numberWithBool: rxContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ];
274 } else if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) {
275 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ];
281 -(id)parentAttribute {
282 if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON && ! mActsAsRadioGroup ) {
283 Reference < XAccessible > rxAccessible = [ self getFirstRadioButtonInGroup ];
284 if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) {
285 Reference < XAccessibleContext > rxAccessibleContext = rxAccessible -> getAccessibleContext();
286 id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: YES asRadioGroup: YES ];
287 [ parent_wrapper autorelease ];
288 return NSAccessibilityUnignoredAncestor( parent_wrapper );
293 Reference< XAccessible > xParent( [ self accessibleContext ] -> getAccessibleParent() );
294 if ( xParent.is() ) {
295 Reference< XAccessibleContext > xContext( xParent -> getAccessibleContext() );
296 if ( xContext.is() ) {
297 id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: xContext ];
298 [ parent_wrapper autorelease ];
299 return NSAccessibilityUnignoredAncestor( parent_wrapper );
302 } catch (const Exception&) {
309 -(id)childrenAttribute {
310 if ( mActsAsRadioGroup ) {
311 NSMutableArray * children = [ [ NSMutableArray alloc ] init ];
312 Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
313 AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF );
314 if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() ) {
315 for ( int index = 0; index < relationMemberOf.TargetSet.getLength(); index++ ) {
316 Reference < XAccessible > rMateAccessible = Reference < XAccessible > ( relationMemberOf.TargetSet[index], UNO_QUERY );
317 if ( rMateAccessible.is() ) {
318 Reference< XAccessibleContext > rMateAccessibleContext( rMateAccessible -> getAccessibleContext() );
319 if ( rMateAccessibleContext.is() ) {
320 id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rMateAccessibleContext ];
321 [ children addObject: wrapper ];
328 } else if ( [ self accessibleTable ] )
330 AquaA11yTableWrapper* pTable = [self isKindOfClass: [AquaA11yTableWrapper class]] ? (AquaA11yTableWrapper*)self : nil;
331 return [ AquaA11yTableWrapper childrenAttributeForElement: pTable ];
334 NSMutableArray * children = [ [ NSMutableArray alloc ] init ];
335 Reference< XAccessibleContext > xContext( [ self accessibleContext ] );
337 sal_Int32 cnt = xContext -> getAccessibleChildCount();
338 for ( sal_Int32 i = 0; i < cnt; i++ ) {
339 Reference< XAccessible > xChild( xContext -> getAccessibleChild( i ) );
341 Reference< XAccessibleContext > xChildContext( xChild -> getAccessibleContext() );
342 // the menubar is already accessible (including Apple- and Application-Menu) through NSApplication => omit it here
343 if ( xChildContext.is() && AccessibleRole::MENU_BAR != xChildContext -> getAccessibleRole() ) {
344 id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: xChildContext ];
345 [ children addObject: wrapper ];
351 // if not already acting as RadioGroup now is the time to replace RadioButtons with RadioGroups and remove RadioButtons
352 if ( ! mActsAsRadioGroup ) {
353 NSEnumerator * enumerator = [ children objectEnumerator ];
354 AquaA11yWrapper * element;
355 while ( ( element = ( (AquaA11yWrapper *) [ enumerator nextObject ] ) ) ) {
356 if ( [ element accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) {
357 if ( [ element isFirstRadioButtonInGroup ] ) {
358 id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ element accessibleContext ] createIfNotExists: YES asRadioGroup: YES ];
359 [ children replaceObjectAtIndex: [ children indexOfObjectIdenticalTo: element ] withObject: wrapper ];
361 [ children removeObject: element ];
366 [ children autorelease ];
367 return NSAccessibilityUnignoredChildren( children );
368 } catch (const Exception &e) {
375 -(id)windowAttribute {
376 // go upstairs until reaching the broken connection
377 AquaA11yWrapper * aWrapper = self;
379 while ( [ aWrapper accessibleContext ] -> getAccessibleParent().is() ) {
380 AquaA11yWrapper *aTentativeParentWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ aWrapper accessibleContext ] -> getAccessibleParent() -> getAccessibleContext() ];
381 // Quick-and-dirty fix for infinite loop after fixing crash in
383 if ( aTentativeParentWrapper == aWrapper )
385 // Even dirtier fix for infinite loop in fdo#55156
386 if ( loops++ == 100 )
388 aWrapper = aTentativeParentWrapper;
389 [ aWrapper autorelease ];
391 // get associated NSWindow
392 NSWindow* theWindow = [ aWrapper windowForParent ];
396 -(id)topLevelUIElementAttribute {
397 return [ self windowAttribute ];
401 if ( [ self accessibleComponent ] ) {
402 return [ AquaA11yComponentWrapper sizeAttributeForElement: self ];
408 -(id)positionAttribute {
409 if ( [ self accessibleComponent ] ) {
410 return [ AquaA11yComponentWrapper positionAttributeForElement: self ];
417 return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() );
420 -(id)roleDescriptionAttribute {
421 if ( mActsAsRadioGroup ) {
422 return [ AquaA11yRoleHelper getRoleDescriptionFrom: NSAccessibilityRadioGroupRole with: @"" ];
423 } else if( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) {
424 // FIXME: VO should read this because of hierarchy, this is just a workaround
425 // get parent and its children
426 AquaA11yWrapper * parent = [ self parentAttribute ];
427 NSArray * children = [ parent childrenAttribute ];
428 // find index of self
430 NSEnumerator * enumerator = [ children objectEnumerator ];
431 AquaA11yWrapper * child = nil;
432 while ( ( child = [ enumerator nextObject ] ) ) {
433 if ( self == child ) {
439 NSNumber * nIndex = [ NSNumber numberWithInt: index ];
440 NSNumber * nGroupsize = [ NSNumber numberWithInt: [ children count ] ];
441 NSMutableString * value = [ [ NSMutableString alloc ] init ];
442 [ value appendString: @"radio button " ];
443 [ value appendString: [ nIndex stringValue ] ];
444 [ value appendString: @" of " ];
445 [ value appendString: [ nGroupsize stringValue ] ];
446 // clean up and return string
448 [ nGroupsize release ];
449 [ children release ];
452 return [ AquaA11yRoleHelper getRoleDescriptionFrom:
453 [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ]
454 with: [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ] ];
458 -(id)valueAttribute {
459 if ( [ [ self roleAttribute ] isEqualToString: NSAccessibilityMenuItemRole ] ) {
461 } else if ( [ self accessibleText ] ) {
462 return [ AquaA11yTextWrapper valueAttributeForElement: self ];
463 } else if ( [ self accessibleValue ] ) {
464 return [ AquaA11yValueWrapper valueAttributeForElement: self ];
470 -(id)minValueAttribute {
471 if ( [ self accessibleValue ] ) {
472 return [ AquaA11yValueWrapper minValueAttributeForElement: self ];
478 -(id)maxValueAttribute {
479 if ( [ self accessibleValue ] ) {
480 return [ AquaA11yValueWrapper maxValueAttributeForElement: self ];
486 -(id)contentsAttribute {
487 return [ self childrenAttribute ];
490 -(id)selectedChildrenAttribute {
491 return [ AquaA11ySelectionWrapper selectedChildrenAttributeForElement: self ];
494 -(id)numberOfCharactersAttribute {
495 if ( [ self accessibleText ] ) {
496 return [ AquaA11yTextWrapper numberOfCharactersAttributeForElement: self ];
502 -(id)selectedTextAttribute {
503 if ( [ self accessibleText ] ) {
504 return [ AquaA11yTextWrapper selectedTextAttributeForElement: self ];
510 -(id)selectedTextRangeAttribute {
511 if ( [ self accessibleText ] ) {
512 return [ AquaA11yTextWrapper selectedTextRangeAttributeForElement: self ];
518 -(id)visibleCharacterRangeAttribute {
519 if ( [ self accessibleText ] ) {
520 return [ AquaA11yTextWrapper visibleCharacterRangeAttributeForElement: self ];
527 return self; // TODO ???
530 -(id)sharedTextUIElementsAttribute {
531 if ( [ self accessibleText ] ) {
532 return [ AquaA11yTextWrapper sharedTextUIElementsAttributeForElement: self ];
538 -(id)sharedCharacterRangeAttribute {
539 if ( [ self accessibleText ] ) {
540 return [ AquaA11yTextWrapper sharedCharacterRangeAttributeForElement: self ];
546 -(id)expandedAttribute {
547 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::EXPANDED ) ];
550 -(id)selectedAttribute {
551 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::SELECTED ) ];
554 -(id)stringForRangeAttributeForParameter:(id)range {
555 if ( [ self accessibleText ] ) {
556 return [ AquaA11yTextWrapper stringForRangeAttributeForElement: self forParameter: range ];
562 -(id)attributedStringForRangeAttributeForParameter:(id)range {
563 if ( [ self accessibleText ] ) {
564 return [ AquaA11yTextWrapper attributedStringForRangeAttributeForElement: self forParameter: range ];
570 -(id)rangeForIndexAttributeForParameter:(id)index {
571 if ( [ self accessibleText ] ) {
572 return [ AquaA11yTextWrapper rangeForIndexAttributeForElement: self forParameter: index ];
578 -(id)rangeForPositionAttributeForParameter:(id)point {
579 if ( [ self accessibleText ] ) {
580 return [ AquaA11yTextWrapper rangeForPositionAttributeForElement: self forParameter: point ];
586 -(id)boundsForRangeAttributeForParameter:(id)range {
587 if ( [ self accessibleText ] ) {
588 return [ AquaA11yTextWrapper boundsForRangeAttributeForElement: self forParameter: range ];
594 -(id)styleRangeForIndexAttributeForParameter:(id)index {
595 if ( [ self accessibleText ] ) {
596 return [ AquaA11yTextWrapper styleRangeForIndexAttributeForElement: self forParameter: index ];
602 -(id)rTFForRangeAttributeForParameter:(id)range {
603 if ( [ self accessibleText ] ) {
604 return [ AquaA11yTextWrapper rTFForRangeAttributeForElement: self forParameter: range ];
610 -(id)orientationAttribute {
611 NSString * orientation = nil;
612 Reference < XAccessibleStateSet > stateSet = [ self accessibleContext ] -> getAccessibleStateSet();
613 if ( stateSet -> contains ( AccessibleStateType::HORIZONTAL ) ) {
614 orientation = NSAccessibilityHorizontalOrientationValue;
615 } else if ( stateSet -> contains ( AccessibleStateType::VERTICAL ) ) {
616 orientation = NSAccessibilityVerticalOrientationValue;
621 -(id)titleUIElementAttribute {
622 if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) {
623 NSString * title = [ self titleAttribute ];
624 id titleElement = nil;
625 if ( [ title length ] == 0 ) {
626 AccessibleRelation relationLabeledBy = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABELED_BY );
627 if ( relationLabeledBy.RelationType == AccessibleRelationType::LABELED_BY && relationLabeledBy.TargetSet.hasElements() ) {
628 Reference < XAccessible > rxAccessible ( relationLabeledBy.TargetSet[0], UNO_QUERY );
629 titleElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ];
641 -(id)servesAsTitleForUIElementsAttribute {
642 if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) {
643 id titleForElement = nil;
644 AccessibleRelation relationLabelFor = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABEL_FOR );
645 if ( relationLabelFor.RelationType == AccessibleRelationType::LABEL_FOR && relationLabelFor.TargetSet.hasElements() ) {
646 Reference < XAccessible > rxAccessible ( relationLabelFor.TargetSet[0], UNO_QUERY );
647 titleForElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ];
649 return titleForElement;
655 -(id)lineForIndexAttributeForParameter:(id)index {
656 if ( [ self accessibleMultiLineText ] ) {
657 return [ AquaA11yTextWrapper lineForIndexAttributeForElement: self forParameter: index ];
663 -(id)rangeForLineAttributeForParameter:(id)line {
664 if ( [ self accessibleMultiLineText ] ) {
665 return [ AquaA11yTextWrapper rangeForLineAttributeForElement: self forParameter: line ];
672 #pragma mark Accessibility Protocol
674 -(id)accessibilityAttributeValue:(NSString *)attribute {
675 SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeValue:" << attribute << "]");
676 // #i90575# guard NSAccessibility protocol against unwanted access
677 if ( isPopupMenuOpen ) {
682 // if we are no longer in the wrapper repository, we have been disposed
683 AquaA11yWrapper * theWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ self accessibleContext ] createIfNotExists: NO ];
684 if ( theWrapper || mIsTableCell ) {
686 SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: NO ];
687 if ( [ self respondsToSelector: methodSelector ] ) {
688 value = [ self performSelector: methodSelector ];
690 } catch ( const DisposedException & e ) {
691 mIsTableCell = NO; // just to be sure
692 [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ];
694 } catch ( const Exception & e ) {
699 [ theWrapper release ]; // the above called method calls retain on the returned Wrapper
704 -(BOOL)accessibilityIsIgnored {
705 SAL_INFO("vcl.a11y", "[" << self << " accessibilityIsIgnored]");
706 // #i90575# guard NSAccessibility protocol against unwanted access
707 if ( isPopupMenuOpen ) {
711 sal_Int16 nRole = [ self accessibleContext ] -> getAccessibleRole();
713 //case AccessibleRole::PANEL:
714 case AccessibleRole::FRAME:
715 case AccessibleRole::ROOT_PANE:
716 case AccessibleRole::SEPARATOR:
717 case AccessibleRole::FILLER:
718 case AccessibleRole::DIALOG:
722 ignored = ! ( [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::VISIBLE ) );
725 return ignored; // TODO: to be completed
728 -(NSArray *)accessibilityAttributeNames {
729 SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeNames]");
730 // #i90575# guard NSAccessibility protocol against unwanted access
731 if ( isPopupMenuOpen ) {
734 NSString * nativeSubrole = nil;
735 NSString * title = nil;
736 NSMutableArray * attributeNames = nil;
737 sal_Int32 nAccessibleChildren = 0;
739 // Default Attributes
740 attributeNames = [ NSMutableArray arrayWithObjects:
741 NSAccessibilityRoleAttribute,
742 NSAccessibilityDescriptionAttribute,
743 NSAccessibilityParentAttribute,
744 NSAccessibilityWindowAttribute,
745 NSAccessibilityHelpAttribute,
746 NSAccessibilityTopLevelUIElementAttribute,
747 NSAccessibilityRoleDescriptionAttribute,
749 nativeSubrole = (NSString *) [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ];
750 title = (NSString *) [ self titleAttribute ];
751 Reference < XAccessibleRelationSet > rxRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
752 // Special Attributes depending on attribute values
753 if ( nativeSubrole && ! [ nativeSubrole isEqualToString: @"" ] ) {
754 [ attributeNames addObject: NSAccessibilitySubroleAttribute ];
758 nAccessibleChildren = [ self accessibleContext ] -> getAccessibleChildCount();
759 if ( nAccessibleChildren > 0 ) {
760 [ attributeNames addObject: NSAccessibilityChildrenAttribute ];
763 catch( DisposedException& ) {}
764 catch( RuntimeException& ) {}
766 if ( title && ! [ title isEqualToString: @"" ] ) {
767 [ attributeNames addObject: NSAccessibilityTitleAttribute ];
769 if ( [ title length ] == 0 && rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABELED_BY ) ) {
770 [ attributeNames addObject: NSAccessibilityTitleUIElementAttribute ];
772 if ( rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABEL_FOR ) ) {
773 [ attributeNames addObject: NSAccessibilityServesAsTitleForUIElementsAttribute ];
775 // Special Attributes depending on interface
776 if( [self accessibleContext ] -> getAccessibleRole() == AccessibleRole::TABLE )
777 [AquaA11yTableWrapper addAttributeNamesTo: attributeNames object: self];
779 if ( [ self accessibleText ] ) {
780 [ AquaA11yTextWrapper addAttributeNamesTo: attributeNames ];
782 if ( [ self accessibleComponent ] ) {
783 [ AquaA11yComponentWrapper addAttributeNamesTo: attributeNames ];
785 if ( [ self accessibleSelection ] ) {
786 [ AquaA11ySelectionWrapper addAttributeNamesTo: attributeNames ];
788 if ( [ self accessibleValue ] ) {
789 [ AquaA11yValueWrapper addAttributeNamesTo: attributeNames ];
791 [ nativeSubrole release ];
793 return attributeNames;
794 } catch ( DisposedException & e ) { // Object is no longer available
795 if ( nativeSubrole ) {
796 [ nativeSubrole release ];
801 if ( attributeNames ) {
802 [ attributeNames release ];
804 [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ];
805 return [ [ NSArray alloc ] init ];
809 -(BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
810 SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeIsSettable:" << attribute << "]");
811 BOOL isSettable = NO;
812 if ( [ self accessibleText ] ) {
813 isSettable = [ AquaA11yTextWrapper isAttributeSettable: attribute forElement: self ];
815 if ( ! isSettable && [ self accessibleComponent ] ) {
816 isSettable = [ AquaA11yComponentWrapper isAttributeSettable: attribute forElement: self ];
818 if ( ! isSettable && [ self accessibleSelection ] ) {
819 isSettable = [ AquaA11ySelectionWrapper isAttributeSettable: attribute forElement: self ];
821 if ( ! isSettable && [ self accessibleValue ] ) {
822 isSettable = [ AquaA11yValueWrapper isAttributeSettable: attribute forElement: self ];
824 return isSettable; // TODO: to be completed
827 -(NSArray *)accessibilityParameterizedAttributeNames {
828 SAL_INFO("vcl.a11y", "[" << self << " accessibilityParameterizedAttributeNames]");
829 NSMutableArray * attributeNames = [ [ NSMutableArray alloc ] init ];
830 // Special Attributes depending on interface
831 if ( [ self accessibleText ] ) {
832 [ AquaA11yTextWrapper addParameterizedAttributeNamesTo: attributeNames ];
834 return attributeNames; // TODO: to be completed
837 -(id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter {
838 SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeValue:" << attribute << " forParameter:" << ((NSObject*)parameter) << "]");
839 SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: YES ];
840 if ( [ self respondsToSelector: methodSelector ] ) {
841 return [ self performSelector: methodSelector withObject: parameter ];
843 return nil; // TODO: to be completed
846 -(BOOL)accessibilitySetOverrideValue:(id)value forAttribute:(NSString *)attribute
848 SAL_INFO("vcl.a11y", "[" << self << " accessibilitySetOverrideValue:" << ((NSObject*)value) << " forAttribute:" << attribute << "]");
854 -(void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
855 SAL_INFO("vcl.a11y", "[" << self << " accessibilitySetValue:" << ((NSObject*)value) << " forAttribute:" << attribute << "]");
856 SEL methodSelector = [ self selectorForAttribute: attribute asGetter: NO withGetterParameter: NO ];
857 if ( [ AquaA11yComponentWrapper respondsToSelector: methodSelector ] ) {
858 [ AquaA11yComponentWrapper performSelector: methodSelector withObject: self withObject: value ];
860 if ( [ AquaA11yTextWrapper respondsToSelector: methodSelector ] ) {
861 [ AquaA11yTextWrapper performSelector: methodSelector withObject: self withObject: value ];
863 if ( [ AquaA11ySelectionWrapper respondsToSelector: methodSelector ] ) {
864 [ AquaA11ySelectionWrapper performSelector: methodSelector withObject: self withObject: value ];
866 if ( [ AquaA11yValueWrapper respondsToSelector: methodSelector ] ) {
867 [ AquaA11yValueWrapper performSelector: methodSelector withObject: self withObject: value ];
871 -(id)accessibilityFocusedUIElement {
872 SAL_INFO("vcl.a11y", "[" << self << " accessibilityFocusedUIElement]");
873 // #i90575# guard NSAccessibility protocol against unwanted access
874 if ( isPopupMenuOpen ) {
878 // as this seems to be the first API call on a newly created SalFrameView object,
879 // make sure self gets registered in the repository ..
880 [ self accessibleContext ];
882 AquaA11yWrapper * focusedUIElement = AquaA11yFocusListener::get()->getFocusedUIElement();
883 // AquaA11yWrapper * ancestor = focusedUIElement;
885 // Make sure the focused object is a descendant of self
887 // if( self == ancestor )
888 return focusedUIElement;
890 // ancestor = [ ancestor accessibilityAttributeValue: NSAccessibilityParentAttribute ];
891 // } while( nil != ancestor );
896 -(NSString *)accessibilityActionDescription:(NSString *)action {
897 SAL_INFO("vcl.a11y", "[" << self << " accessibilityActionDescription:" << action << "]");
898 return NSAccessibilityActionDescription(action);
901 -(AquaA11yWrapper *)actionResponder {
902 AquaA11yWrapper * wrapper = nil;
903 // get some information
904 NSString * role = (NSString *) [ self accessibilityAttributeValue: NSAccessibilityRoleAttribute ];
905 id enabledAttr = [ self enabledAttribute ];
906 BOOL enabled = [ enabledAttr boolValue ];
907 NSView * parent = (NSView *) [ self accessibilityAttributeValue: NSAccessibilityParentAttribute ];
908 AquaA11yWrapper * parentAsWrapper = nil;
909 if ( [ parent isKindOfClass: [ AquaA11yWrapper class ] ] ) {
910 parentAsWrapper = (AquaA11yWrapper *) parent;
912 SAL_WNODEPRECATED_DECLARATIONS_PUSH
913 //TODO: 10.10 accessibilityAttributeValue:
914 NSString * parentRole = (NSString *) [ parent accessibilityAttributeValue: NSAccessibilityRoleAttribute ];
915 SAL_WNODEPRECATED_DECLARATIONS_POP
916 // if we are a textarea inside a combobox, then the combobox is the action responder
918 && [ role isEqualToString: NSAccessibilityTextAreaRole ]
919 && [ parentRole isEqualToString: NSAccessibilityComboBoxRole ]
920 && parentAsWrapper ) {
921 wrapper = parentAsWrapper;
922 } else if ( enabled && [ self accessibleAction ] ) {
925 [ parentRole release ];
926 [ enabledAttr release ];
931 -(void)accessibilityPerformAction:(NSString *)action {
932 SAL_INFO("vcl.a11y", "[" << self << " accessibilityPerformAction:" << action << "]");
933 AquaA11yWrapper * actionResponder = [ self actionResponder ];
934 if ( actionResponder ) {
935 [ AquaA11yActionWrapper doAction: action ofElement: actionResponder ];
939 -(NSArray *)accessibilityActionNames {
940 SAL_INFO("vcl.a11y", "[" << self << " accessibilityActionNames]");
941 NSArray * actionNames = nil;
942 AquaA11yWrapper * actionResponder = [ self actionResponder ];
943 if ( actionResponder ) {
944 actionNames = [ AquaA11yActionWrapper actionNamesForElement: actionResponder ];
946 actionNames = [ [ NSArray alloc ] init ];
952 #pragma mark Hit Test
954 -(BOOL)isViewElement:(NSObject *)viewElement hitByPoint:(NSPoint)point {
956 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
957 SAL_WNODEPRECATED_DECLARATIONS_PUSH
958 //TODO: 10.10 accessibilityAttributeValue:
959 NSValue * position = [ viewElement accessibilityAttributeValue: NSAccessibilityPositionAttribute ];
960 NSValue * size = [ viewElement accessibilityAttributeValue: NSAccessibilitySizeAttribute ];
961 SAL_WNODEPRECATED_DECLARATIONS_POP
962 if ( position && size ) {
963 float minX = [ position pointValue ].x;
964 float minY = [ position pointValue ].y;
965 float maxX = minX + [ size sizeValue ].width;
966 float maxY = minY + [ size sizeValue ].height;
967 if ( minX < point.x && maxX > point.x && minY < point.y && maxY > point.y ) {
975 Reference < XAccessibleContext > hitTestRunner ( com::sun::star::awt::Point point,
976 Reference < XAccessibleContext > rxAccessibleContext ) {
977 Reference < XAccessibleContext > hitChild;
978 Reference < XAccessibleContext > emptyReference;
980 Reference < XAccessibleComponent > rxAccessibleComponent ( rxAccessibleContext, UNO_QUERY );
981 if ( rxAccessibleComponent.is() ) {
982 com::sun::star::awt::Point location = rxAccessibleComponent -> getLocationOnScreen();
983 com::sun::star::awt::Point hitPoint ( point.X - location.X , point.Y - location.Y);
984 Reference < XAccessible > rxAccessible = rxAccessibleComponent -> getAccessibleAtPoint ( hitPoint );
985 if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() &&
986 rxAccessible -> getAccessibleContext() -> getAccessibleChildCount() == 0 ) {
987 hitChild = rxAccessible -> getAccessibleContext();
991 // iterate the hirerachy looking doing recursive hit testing.
992 // apparently necessary as a special treatment for e.g. comboboxes
993 if ( !hitChild.is() ) {
994 bool bSafeToIterate = true;
995 sal_Int32 nCount = rxAccessibleContext -> getAccessibleChildCount();
997 if ( nCount < 0 || nCount > SAL_MAX_UINT16 /* slow enough for anyone */ )
998 bSafeToIterate = false;
999 else { // manages descendants is an horror from the a11y standards guys.
1000 Reference< XAccessibleStateSet > xStateSet;
1001 xStateSet = rxAccessibleContext -> getAccessibleStateSet();
1002 if (xStateSet.is() && xStateSet -> contains(AccessibleStateType::MANAGES_DESCENDANTS ) )
1003 bSafeToIterate = false;
1006 if( bSafeToIterate ) {
1007 for ( int i = 0; i < rxAccessibleContext -> getAccessibleChildCount(); i++ ) {
1008 Reference < XAccessible > rxAccessibleChild = rxAccessibleContext -> getAccessibleChild ( i );
1009 if ( rxAccessibleChild.is() && rxAccessibleChild -> getAccessibleContext().is() && rxAccessibleChild -> getAccessibleContext() -> getAccessibleRole() != AccessibleRole::LIST ) {
1010 Reference < XAccessibleContext > myHitChild = hitTestRunner ( point, rxAccessibleChild -> getAccessibleContext() );
1011 if ( myHitChild.is() ) {
1012 hitChild = myHitChild;
1019 } catch ( RuntimeException ) {
1020 return emptyReference;
1025 -(id)accessibilityHitTest:(NSPoint)point {
1026 SAL_INFO("vcl.a11y", "[" << self << " accessibilityHitTest:" << point << "]");
1027 static id wrapper = nil;
1028 if ( nil != wrapper ) {
1029 [ wrapper release ];
1032 Reference < XAccessibleContext > hitChild;
1033 NSRect screenRect = [ [ NSScreen mainScreen ] frame ];
1034 com::sun::star::awt::Point hitPoint ( static_cast<long>(point.x) , static_cast<long>(screenRect.size.height - point.y) );
1035 // check child windows first
1036 NSWindow * window = (NSWindow *) [ self accessibilityAttributeValue: NSAccessibilityWindowAttribute ];
1037 NSArray * childWindows = [ window childWindows ];
1038 if ( [ childWindows count ] > 0 ) {
1039 NSWindow * element = nil;
1040 NSEnumerator * enumerator = [ childWindows objectEnumerator ];
1041 while ( ( element = [ enumerator nextObject ] ) && !hitChild.is() ) {
1042 if ( [ element isKindOfClass: [ SalFrameWindow class ] ] && [ self isViewElement: element hitByPoint: point ] ) {
1043 // we have a child window that is hit
1044 Reference < XAccessibleRelationSet > relationSet = [ ( ( SalFrameWindow * ) element ) accessibleContext ] -> getAccessibleRelationSet();
1045 if ( relationSet.is() && relationSet -> containsRelation ( AccessibleRelationType::SUB_WINDOW_OF )) {
1046 // we have a valid relation to the parent element
1047 AccessibleRelation relation = relationSet -> getRelationByType ( AccessibleRelationType::SUB_WINDOW_OF );
1048 for ( int i = 0; i < relation.TargetSet.getLength() && !hitChild.is(); i++ ) {
1049 Reference < XAccessible > rxAccessible ( relation.TargetSet [ i ], UNO_QUERY );
1050 if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) {
1051 // hit test for children of parent
1052 hitChild = hitTestRunner ( hitPoint, rxAccessible -> getAccessibleContext() );
1059 // nothing hit yet, so check ourself
1060 if ( ! hitChild.is() ) {
1061 if ( !mpReferenceWrapper ) {
1062 [ self setDefaults: [ self accessibleContext ] ];
1064 hitChild = hitTestRunner ( hitPoint, mpReferenceWrapper -> rAccessibleContext );
1066 if ( hitChild.is() ) {
1067 wrapper = [ AquaA11yFactory wrapperForAccessibleContext: hitChild ];
1070 [ wrapper retain ]; // TODO: retain only when transient ?
1076 #pragma mark Access Methods
1078 -(XAccessibleAction *)accessibleAction {
1079 return mpReferenceWrapper -> rAccessibleAction.get();
1082 -(XAccessibleContext *)accessibleContext {
1083 return mpReferenceWrapper -> rAccessibleContext.get();
1086 -(XAccessibleComponent *)accessibleComponent {
1087 return mpReferenceWrapper -> rAccessibleComponent.get();
1090 -(XAccessibleExtendedComponent *)accessibleExtendedComponent {
1091 return mpReferenceWrapper -> rAccessibleExtendedComponent.get();
1094 -(XAccessibleSelection *)accessibleSelection {
1095 return mpReferenceWrapper -> rAccessibleSelection.get();
1098 -(XAccessibleTable *)accessibleTable {
1099 return mpReferenceWrapper -> rAccessibleTable.get();
1102 -(XAccessibleText *)accessibleText {
1103 return mpReferenceWrapper -> rAccessibleText.get();
1106 -(XAccessibleEditableText *)accessibleEditableText {
1107 return mpReferenceWrapper -> rAccessibleEditableText.get();
1110 -(XAccessibleValue *)accessibleValue {
1111 return mpReferenceWrapper -> rAccessibleValue.get();
1114 -(XAccessibleTextAttributes *)accessibleTextAttributes {
1115 return mpReferenceWrapper -> rAccessibleTextAttributes.get();
1118 -(XAccessibleMultiLineText *)accessibleMultiLineText {
1119 return mpReferenceWrapper -> rAccessibleMultiLineText.get();
1122 -(XAccessibleTextMarkup *)accessibleTextMarkup {
1123 return mpReferenceWrapper -> rAccessibleTextMarkup.get();
1126 -(NSWindow*)windowForParent {
1127 return [self window];
1130 // These four are for AXTextAreas only. They are needed, because bold and italic
1131 // attributes have to be bound to a font on the Mac. Our UNO-API instead handles
1132 // and reports them independently. When they occur we bundle them to a font with
1133 // this information here to create a according NSFont.
1134 -(void)setActsAsRadioGroup:(BOOL)actsAsRadioGroup {
1135 mActsAsRadioGroup = actsAsRadioGroup;
1138 -(BOOL)actsAsRadioGroup {
1139 return mActsAsRadioGroup;
1142 +(void)setPopupMenuOpen:(BOOL)popupMenuOpen {
1143 isPopupMenuOpen = popupMenuOpen;
1148 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */