1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: aqua11ywrapper.mm,v $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 // MARKER(update_precomp.py): autogen include statement, do not remove
33 #include "precompiled_vcl.hxx"
36 #include "aqua11ywrapper.h"
37 #include "aqua11yactionwrapper.h"
38 #include "aqua11ycomponentwrapper.h"
39 #include "aqua11ylistener.hxx"
40 #include "aqua11yselectionwrapper.h"
41 #include "aqua11ytablewrapper.h"
42 #include "aqua11ytextwrapper.h"
43 #include "aqua11yvaluewrapper.h"
44 #include "aqua11yfactory.h"
45 #include "aqua11yfocuslistener.hxx"
46 #include "aqua11yfocustracker.hxx"
47 #include "aqua11yrolehelper.h"
48 #include <com/sun/star/accessibility/AccessibleRole.hpp>
49 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
50 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
51 #include <com/sun/star/awt/Size.hpp>
52 #include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
53 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
54 #include <com/sun/star/lang/DisposedException.hpp>
56 using namespace ::com::sun::star::accessibility;
57 using namespace ::com::sun::star::awt;
58 using namespace ::com::sun::star::lang;
59 using namespace ::com::sun::star::uno;
61 @interface SalFrameWindow : NSWindow
64 -(Reference<XAccessibleContext>)accessibleContext;
67 static MacOSBOOL isPopupMenuOpen = NO;
69 @implementation AquaA11yWrapper : NSView
72 #pragma mark Init and dealloc
74 -(id)initWithAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext {
75 self = [ super init ];
77 [ self setDefaults: rxAccessibleContext ];
82 -(void) setDefaults: (Reference < XAccessibleContext >) rxAccessibleContext {
83 mDefaultFontsize = 0.0;
84 mpDefaultFontname = nil;
85 mpReferenceWrapper = new ReferenceWrapper;
86 mActsAsRadioGroup = NO;
87 mpReferenceWrapper -> rAccessibleContext = rxAccessibleContext;
89 // Querying all supported interfaces
91 // XAccessibleComponent
92 mpReferenceWrapper -> rAccessibleComponent = Reference < XAccessibleComponent > ( rxAccessibleContext, UNO_QUERY );
93 // XAccessibleExtendedComponent
94 mpReferenceWrapper -> rAccessibleExtendedComponent = Reference < XAccessibleExtendedComponent > ( rxAccessibleContext, UNO_QUERY );
95 // XAccessibleSelection
96 mpReferenceWrapper -> rAccessibleSelection = Reference< XAccessibleSelection > ( rxAccessibleContext, UNO_QUERY );
98 mpReferenceWrapper -> rAccessibleTable = Reference < XAccessibleTable > ( rxAccessibleContext, UNO_QUERY );
100 mpReferenceWrapper -> rAccessibleText = Reference < XAccessibleText > ( rxAccessibleContext, UNO_QUERY );
101 // XAccessibleEditableText
102 mpReferenceWrapper -> rAccessibleEditableText = Reference < XAccessibleEditableText > ( rxAccessibleContext, UNO_QUERY );
104 mpReferenceWrapper -> rAccessibleValue = Reference < XAccessibleValue > ( rxAccessibleContext, UNO_QUERY );
106 mpReferenceWrapper -> rAccessibleAction = Reference < XAccessibleAction > ( rxAccessibleContext, UNO_QUERY );
107 // XAccessibleTextAttributes
108 mpReferenceWrapper -> rAccessibleTextAttributes = Reference < XAccessibleTextAttributes > ( rxAccessibleContext, UNO_QUERY );
109 // XAccessibleMultiLineText
110 mpReferenceWrapper -> rAccessibleMultiLineText = Reference < XAccessibleMultiLineText > ( rxAccessibleContext, UNO_QUERY );
111 // XAccessibleEventBroadcaster
113 /* #i102033# NSAccessibility does not seemt to know an equivalent for transient children.
114 That means we need to cache this, else e.g. tree list boxes are not accessible (moreover
115 it crashes by notifying dead objects - which would seemt o be another bug)
118 Unfortunately this can increase memory consumption drastically until the non transient parent
119 is destroyed an finally all the transients are released.
121 if ( ! rxAccessibleContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::TRANSIENT ) )
124 Reference< XAccessibleEventBroadcaster > xBroadcaster(rxAccessibleContext, UNO_QUERY);
125 if( xBroadcaster.is() ) {
127 * We intentionally do not hold a reference to the event listener in the wrapper object,
128 * but let the listener control the life cycle of the wrapper instead ..
130 xBroadcaster->addEventListener( new AquaA11yEventListener( self, rxAccessibleContext -> getAccessibleRole() ) );
134 if ( rxAccessibleContext -> getAccessibleRole() == AccessibleRole::TABLE_CELL ) {
137 } catch ( const Exception ) {
142 if ( mpReferenceWrapper != nil ) {
143 delete mpReferenceWrapper;
145 if ( mpDefaultFontname != nil ) {
146 [ mpDefaultFontname release ];
152 #pragma mark Utility Section
154 // generates selectors for attribute name AXAttributeNameHere
155 // (getter without parameter) attributeNameHereAttribute
156 // (getter with parameter) attributeNameHereAttributeForParameter:
157 // (setter) setAttributeNameHereAttributeForElement:to:
158 -(SEL)selectorForAttribute:(NSString *)attribute asGetter:(MacOSBOOL)asGetter withGetterParameter:(MacOSBOOL)withGetterParameter {
160 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
162 // step 1: create method name from attribute name
163 NSMutableString * methodName = [ NSMutableString string ];
165 [ methodName appendString: @"set" ];
167 NSString * firstChar = [ attribute substringWithRange: NSMakeRange ( 2, 1 ) ]; // drop leading "AX" and get first char
169 [ methodName appendString: [ firstChar lowercaseString ] ];
171 [ methodName appendString: firstChar ];
173 [ methodName appendString: [ attribute substringFromIndex: 3 ] ]; // append rest of attribute name
174 // append rest of method name
175 [ methodName appendString: @"Attribute" ];
177 [ methodName appendString: @"ForElement:to:" ];
178 } else if ( asGetter && withGetterParameter ) {
179 [ methodName appendString: @"ForParameter:" ];
181 // step 2: create selector
182 selector = NSSelectorFromString ( methodName );
183 } @catch ( id exception ) {
190 -(Reference < XAccessible >)getFirstRadioButtonInGroup {
191 Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
192 if( rxAccessibleRelationSet.is() )
194 AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF );
195 if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() )
196 return Reference < XAccessible > ( relationMemberOf.TargetSet[0], UNO_QUERY );
198 return Reference < XAccessible > ();
201 -(MacOSBOOL)isFirstRadioButtonInGroup {
202 Reference < XAccessible > rFirstMateAccessible = [ self getFirstRadioButtonInGroup ];
203 if ( rFirstMateAccessible.is() && rFirstMateAccessible -> getAccessibleContext().get() == [ self accessibleContext ] ) {
210 #pragma mark Attribute Value Getters
211 // ( called via Reflection by accessibilityAttributeValue )
214 Radiobutton grouping is done differently in NSAccessibility and the UNO-API. In UNO related radio buttons share an entry in their
215 RelationSet. In NSAccessibility the relationship is axpressed through the hierarchy. A AXRadioGroup contains two or more AXRadioButton
216 objects. Since this group is not available in the UNO hierarchy, an extra wrapper is used for it. This wrapper shares almost all
217 attributes with the first radio button of the group, except for the role, subrole, role description, parent and children attributes.
218 So in this five methods there is a special treatment for radio buttons and groups.
222 if ( mActsAsRadioGroup ) {
223 return NSAccessibilityRadioGroupRole;
225 return [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ];
229 -(id)subroleAttribute {
230 if ( mActsAsRadioGroup ) {
233 NSString * subRole = [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ];
234 if ( ! [ subRole isEqualToString: @"" ] ) {
238 return [ super accessibilityAttributeValue: NSAccessibilitySubroleAttribute ];
243 -(id)titleAttribute {
244 return CreateNSString ( [ self accessibleContext ] -> getAccessibleName() );
247 -(id)descriptionAttribute {
248 if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) {
249 return [ self titleAttribute ];
250 } else if ( [ self accessibleExtendedComponent ] != nil ) {
251 return [ AquaA11yComponentWrapper descriptionAttributeForElement: self ];
253 return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() );
257 -(id)enabledAttribute {
258 if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) {
259 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::ENABLED ) ];
265 -(id)focusedAttribute {
266 if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) {
268 Reference < XAccessible > rxParent = [ self accessibleContext ] -> getAccessibleParent();
269 if ( rxParent.is() ) {
270 Reference < XAccessibleContext > rxContext = rxParent -> getAccessibleContext();
271 if ( rxContext.is() && rxContext -> getAccessibleStateSet().is() ) {
272 isFocused = [ NSNumber numberWithBool: rxContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ];
276 } else if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) {
277 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ];
283 -(id)parentAttribute {
284 if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON && ! mActsAsRadioGroup ) {
285 Reference < XAccessible > rxAccessible = [ self getFirstRadioButtonInGroup ];
286 if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) {
287 Reference < XAccessibleContext > rxAccessibleContext = rxAccessible -> getAccessibleContext();
288 id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: YES asRadioGroup: YES ];
289 [ parent_wrapper autorelease ];
290 return NSAccessibilityUnignoredAncestor( parent_wrapper );
295 Reference< XAccessible > xParent( [ self accessibleContext ] -> getAccessibleParent() );
296 if ( xParent.is() ) {
297 Reference< XAccessibleContext > xContext( xParent -> getAccessibleContext() );
298 if ( xContext.is() ) {
299 id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: xContext ];
300 [ parent_wrapper autorelease ];
301 return NSAccessibilityUnignoredAncestor( parent_wrapper );
304 } catch (const Exception&) {
311 -(id)childrenAttribute {
312 if ( mActsAsRadioGroup ) {
313 NSMutableArray * children = [ [ NSMutableArray alloc ] init ];
314 Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
315 AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF );
316 if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() ) {
317 for ( int index = 0; index < relationMemberOf.TargetSet.getLength(); index++ ) {
318 Reference < XAccessible > rMateAccessible = Reference < XAccessible > ( relationMemberOf.TargetSet[index], UNO_QUERY );
319 if ( rMateAccessible.is() ) {
320 Reference< XAccessibleContext > rMateAccessibleContext( rMateAccessible -> getAccessibleContext() );
321 if ( rMateAccessibleContext.is() ) {
322 id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rMateAccessibleContext ];
323 [ children addObject: wrapper ];
330 } else if ( [ self accessibleTable ] != nil ) {
331 return [ AquaA11yTableWrapper childrenAttributeForElement: self ];
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;
378 while ( [ aWrapper accessibleContext ] -> getAccessibleParent().is() ) {
379 aWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ aWrapper accessibleContext ] -> getAccessibleParent() -> getAccessibleContext() ];
380 [ aWrapper autorelease ];
382 // get associated NSWindow
383 NSView * theView = [ aWrapper viewElementForParent ];
387 -(id)topLevelUIElementAttribute {
388 return [ self windowAttribute ];
392 if ( [ self accessibleComponent ] != nil ) {
393 return [ AquaA11yComponentWrapper sizeAttributeForElement: self ];
399 -(id)positionAttribute {
400 if ( [ self accessibleComponent ] != nil ) {
401 return [ AquaA11yComponentWrapper positionAttributeForElement: self ];
408 return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() );
411 -(id)roleDescriptionAttribute {
412 if ( mActsAsRadioGroup ) {
413 return [ AquaA11yRoleHelper getRoleDescriptionFrom: NSAccessibilityRadioGroupRole with: @"" ];
414 } else if( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) {
415 // FIXME: VO should read this because of hierarchy, this is just a workaround
416 // get parent and its children
417 AquaA11yWrapper * parent = [ self parentAttribute ];
418 NSArray * children = [ parent childrenAttribute ];
419 // find index of self
421 NSEnumerator * enumerator = [ children objectEnumerator ];
422 AquaA11yWrapper * child = nil;
423 while ( ( child = [ enumerator nextObject ] ) ) {
424 if ( self == child ) {
430 NSNumber * nIndex = [ NSNumber numberWithInt: index ];
431 NSNumber * nGroupsize = [ NSNumber numberWithInt: [ children count ] ];
432 NSMutableString * value = [ [ NSMutableString alloc ] init ];
433 [ value appendString: @"radio button " ];
434 [ value appendString: [ nIndex stringValue ] ];
435 [ value appendString: @" of " ];
436 [ value appendString: [ nGroupsize stringValue ] ];
437 // clean up and return string
439 [ nGroupsize release ];
440 [ children release ];
443 return [ AquaA11yRoleHelper getRoleDescriptionFrom:
444 [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ]
445 with: [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ] ];
449 -(id)valueAttribute {
450 if ( [ [ self roleAttribute ] isEqualToString: NSAccessibilityMenuItemRole ] ) {
452 } else if ( [ self accessibleText ] != nil ) {
453 return [ AquaA11yTextWrapper valueAttributeForElement: self ];
454 } else if ( [ self accessibleValue ] != nil ) {
455 return [ AquaA11yValueWrapper valueAttributeForElement: self ];
461 -(id)minValueAttribute {
462 if ( [ self accessibleValue ] != nil ) {
463 return [ AquaA11yValueWrapper minValueAttributeForElement: self ];
469 -(id)maxValueAttribute {
470 if ( [ self accessibleValue ] != nil ) {
471 return [ AquaA11yValueWrapper maxValueAttributeForElement: self ];
477 -(id)contentsAttribute {
478 return [ self childrenAttribute ];
481 -(id)selectedChildrenAttribute {
482 return [ AquaA11ySelectionWrapper selectedChildrenAttributeForElement: self ];
485 -(id)numberOfCharactersAttribute {
486 if ( [ self accessibleText ] != nil ) {
487 return [ AquaA11yTextWrapper numberOfCharactersAttributeForElement: self ];
493 -(id)selectedTextAttribute {
494 if ( [ self accessibleText ] != nil ) {
495 return [ AquaA11yTextWrapper selectedTextAttributeForElement: self ];
501 -(id)selectedTextRangeAttribute {
502 if ( [ self accessibleText ] != nil ) {
503 return [ AquaA11yTextWrapper selectedTextRangeAttributeForElement: self ];
509 -(id)visibleCharacterRangeAttribute {
510 if ( [ self accessibleText ] != nil ) {
511 return [ AquaA11yTextWrapper visibleCharacterRangeAttributeForElement: self ];
518 return self; // TODO ???
521 -(id)sharedTextUIElementsAttribute {
522 if ( [ self accessibleText ] != nil ) {
523 return [ AquaA11yTextWrapper sharedTextUIElementsAttributeForElement: self ];
529 -(id)sharedCharacterRangeAttribute {
530 if ( [ self accessibleText ] != nil ) {
531 return [ AquaA11yTextWrapper sharedCharacterRangeAttributeForElement: self ];
537 -(id)expandedAttribute {
538 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::EXPANDED ) ];
541 -(id)selectedAttribute {
542 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::SELECTED ) ];
545 -(id)stringForRangeAttributeForParameter:(id)range {
546 if ( [ self accessibleText ] != nil ) {
547 return [ AquaA11yTextWrapper stringForRangeAttributeForElement: self forParameter: range ];
553 -(id)attributedStringForRangeAttributeForParameter:(id)range {
554 if ( [ self accessibleText ] != nil ) {
555 return [ AquaA11yTextWrapper attributedStringForRangeAttributeForElement: self forParameter: range ];
561 -(id)rangeForIndexAttributeForParameter:(id)index {
562 if ( [ self accessibleText ] != nil ) {
563 return [ AquaA11yTextWrapper rangeForIndexAttributeForElement: self forParameter: index ];
569 -(id)rangeForPositionAttributeForParameter:(id)point {
570 if ( [ self accessibleText ] != nil ) {
571 return [ AquaA11yTextWrapper rangeForPositionAttributeForElement: self forParameter: point ];
577 -(id)boundsForRangeAttributeForParameter:(id)range {
578 if ( [ self accessibleText ] != nil ) {
579 return [ AquaA11yTextWrapper boundsForRangeAttributeForElement: self forParameter: range ];
585 -(id)styleRangeForIndexAttributeForParameter:(id)index {
586 if ( [ self accessibleText ] != nil ) {
587 return [ AquaA11yTextWrapper styleRangeForIndexAttributeForElement: self forParameter: index ];
593 -(id)rTFForRangeAttributeForParameter:(id)range {
594 if ( [ self accessibleText ] != nil ) {
595 return [ AquaA11yTextWrapper rTFForRangeAttributeForElement: self forParameter: range ];
601 -(id)orientationAttribute {
602 NSString * orientation = nil;
603 Reference < XAccessibleStateSet > stateSet = [ self accessibleContext ] -> getAccessibleStateSet();
604 if ( stateSet -> contains ( AccessibleStateType::HORIZONTAL ) ) {
605 orientation = NSAccessibilityHorizontalOrientationValue;
606 } else if ( stateSet -> contains ( AccessibleStateType::VERTICAL ) ) {
607 orientation = NSAccessibilityVerticalOrientationValue;
612 -(id)titleUIElementAttribute {
613 if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) {
614 NSString * title = [ self titleAttribute ];
615 id titleElement = nil;
616 if ( [ title length ] == 0 ) {
617 AccessibleRelation relationLabeledBy = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABELED_BY );
618 if ( relationLabeledBy.RelationType == AccessibleRelationType::LABELED_BY && relationLabeledBy.TargetSet.hasElements() ) {
619 Reference < XAccessible > rxAccessible ( relationLabeledBy.TargetSet[0], UNO_QUERY );
620 titleElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ];
623 if ( title != nil ) {
632 -(id)servesAsTitleForUIElementsAttribute {
633 if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) {
634 id titleForElement = nil;
635 AccessibleRelation relationLabelFor = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABEL_FOR );
636 if ( relationLabelFor.RelationType == AccessibleRelationType::LABEL_FOR && relationLabelFor.TargetSet.hasElements() ) {
637 Reference < XAccessible > rxAccessible ( relationLabelFor.TargetSet[0], UNO_QUERY );
638 titleForElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ];
640 return titleForElement;
646 -(id)lineForIndexAttributeForParameter:(id)index {
647 if ( [ self accessibleMultiLineText ] != nil ) {
648 return [ AquaA11yTextWrapper lineForIndexAttributeForElement: self forParameter: index ];
654 -(id)rangeForLineAttributeForParameter:(id)line {
655 if ( [ self accessibleMultiLineText ] != nil ) {
656 return [ AquaA11yTextWrapper rangeForLineAttributeForElement: self forParameter: line ];
663 #pragma mark Accessibility Protocol
665 -(id)accessibilityAttributeValue:(NSString *)attribute {
666 // #i90575# guard NSAccessibility protocol against unwanted access
667 if ( isPopupMenuOpen ) {
671 // if we are no longer in the wrapper repository, we have been disposed
672 AquaA11yWrapper * theWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ self accessibleContext ] createIfNotExists: NO ];
673 if ( theWrapper != nil || mIsTableCell ) {
675 SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: NO ];
676 if ( [ self respondsToSelector: methodSelector ] ) {
677 value = [ self performSelector: methodSelector ];
679 } catch ( const DisposedException & e ) {
680 mIsTableCell = NO; // just to be sure
681 [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ];
683 } catch ( const Exception & e ) {
687 if ( theWrapper != nil ) {
688 [ theWrapper release ]; // the above called method calls retain on the returned Wrapper
693 -(MacOSBOOL)accessibilityIsIgnored {
694 // #i90575# guard NSAccessibility protocol against unwanted access
695 if ( isPopupMenuOpen ) {
698 MacOSBOOL ignored = NO;
699 sal_Int16 nRole = [ self accessibleContext ] -> getAccessibleRole();
701 case AccessibleRole::PANEL:
702 case AccessibleRole::FRAME:
703 case AccessibleRole::ROOT_PANE:
704 case AccessibleRole::SEPARATOR:
705 case AccessibleRole::FILLER:
706 case AccessibleRole::DIALOG:
710 ignored = ! ( [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::VISIBLE ) );
713 return ignored; // TODO: to be completed
716 -(NSArray *)accessibilityAttributeNames {
717 // #i90575# guard NSAccessibility protocol against unwanted access
718 if ( isPopupMenuOpen ) {
721 NSString * nativeSubrole = nil;
722 NSString * title = nil;
723 NSMutableArray * attributeNames = nil;
725 // Default Attributes
726 attributeNames = [ NSMutableArray arrayWithObjects:
727 NSAccessibilityRoleAttribute,
728 NSAccessibilityDescriptionAttribute,
729 NSAccessibilityParentAttribute,
730 NSAccessibilityWindowAttribute,
731 NSAccessibilityHelpAttribute,
732 NSAccessibilityTopLevelUIElementAttribute,
733 NSAccessibilityRoleDescriptionAttribute,
735 nativeSubrole = (NSString *) [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ];
736 title = (NSString *) [ self titleAttribute ];
737 Reference < XAccessibleRelationSet > rxRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
738 // Special Attributes depending on attribute values
739 if ( nativeSubrole != nil && ! [ nativeSubrole isEqualToString: @"" ] ) {
740 [ attributeNames addObject: NSAccessibilitySubroleAttribute ];
744 if ( [ self accessibleContext ] -> getAccessibleChildCount() > 0 ) {
745 [ attributeNames addObject: NSAccessibilityChildrenAttribute ];
748 catch( DisposedException& ) {}
749 catch( RuntimeException& ) {}
751 if ( title != nil && ! [ title isEqualToString: @"" ] ) {
752 [ attributeNames addObject: NSAccessibilityTitleAttribute ];
754 if ( [ title length ] == 0 && rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABELED_BY ) ) {
755 [ attributeNames addObject: NSAccessibilityTitleUIElementAttribute ];
757 if ( rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABEL_FOR ) ) {
758 [ attributeNames addObject: NSAccessibilityServesAsTitleForUIElementsAttribute ];
760 // Special Attributes depending on interface
761 if ( [ self accessibleText ] != nil ) {
762 [ AquaA11yTextWrapper addAttributeNamesTo: attributeNames ];
764 if ( [ self accessibleComponent ] != nil ) {
765 [ AquaA11yComponentWrapper addAttributeNamesTo: attributeNames ];
767 if ( [ self accessibleSelection ] != nil ) {
768 [ AquaA11ySelectionWrapper addAttributeNamesTo: attributeNames ];
770 if ( [ self accessibleValue ] != nil ) {
771 [ AquaA11yValueWrapper addAttributeNamesTo: attributeNames ];
773 [ nativeSubrole release ];
775 return attributeNames;
776 } catch ( DisposedException & e ) { // Object is no longer available
777 if ( nativeSubrole != nil ) {
778 [ nativeSubrole release ];
780 if ( title != nil ) {
783 if ( attributeNames != nil ) {
784 [ attributeNames release ];
786 [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ];
787 return [ [ NSArray alloc ] init ];
791 -(MacOSBOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
792 MacOSBOOL isSettable = NO;
793 if ( [ self accessibleText ] != nil ) {
794 isSettable = [ AquaA11yTextWrapper isAttributeSettable: attribute forElement: self ];
796 if ( ! isSettable && [ self accessibleComponent ] != nil ) {
797 isSettable = [ AquaA11yComponentWrapper isAttributeSettable: attribute forElement: self ];
799 if ( ! isSettable && [ self accessibleSelection ] != nil ) {
800 isSettable = [ AquaA11ySelectionWrapper isAttributeSettable: attribute forElement: self ];
802 if ( ! isSettable && [ self accessibleValue ] != nil ) {
803 isSettable = [ AquaA11yValueWrapper isAttributeSettable: attribute forElement: self ];
805 return isSettable; // TODO: to be completed
808 -(NSArray *)accessibilityParameterizedAttributeNames {
809 NSMutableArray * attributeNames = [ [ NSMutableArray alloc ] init ];
810 // Special Attributes depending on interface
811 if ( [ self accessibleText ] != nil ) {
812 [ AquaA11yTextWrapper addParameterizedAttributeNamesTo: attributeNames ];
814 return attributeNames; // TODO: to be completed
817 -(id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter {
818 SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: YES ];
819 if ( [ self respondsToSelector: methodSelector ] ) {
820 return [ self performSelector: methodSelector withObject: parameter ];
822 return nil; // TODO: to be completed
825 -(MacOSBOOL)accessibilitySetOverrideValue:(id)value forAttribute:(NSString *)attribute {
829 -(void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
830 SEL methodSelector = [ self selectorForAttribute: attribute asGetter: NO withGetterParameter: NO ];
831 if ( [ AquaA11yComponentWrapper respondsToSelector: methodSelector ] ) {
832 [ AquaA11yComponentWrapper performSelector: methodSelector withObject: self withObject: value ];
834 if ( [ AquaA11yTextWrapper respondsToSelector: methodSelector ] ) {
835 [ AquaA11yTextWrapper performSelector: methodSelector withObject: self withObject: value ];
837 if ( [ AquaA11ySelectionWrapper respondsToSelector: methodSelector ] ) {
838 [ AquaA11ySelectionWrapper performSelector: methodSelector withObject: self withObject: value ];
840 if ( [ AquaA11yValueWrapper respondsToSelector: methodSelector ] ) {
841 [ AquaA11yValueWrapper performSelector: methodSelector withObject: self withObject: value ];
845 -(id)accessibilityFocusedUIElement {
846 // #i90575# guard NSAccessibility protocol against unwanted access
847 if ( isPopupMenuOpen ) {
851 // as this seems to be the first API call on a newly created SalFrameView object,
852 // make sure self gets registered in the repository ..
853 [ self accessibleContext ];
855 AquaA11yWrapper * focusedUIElement = AquaA11yFocusListener::get()->getFocusedUIElement();
856 // AquaA11yWrapper * ancestor = focusedUIElement;
858 // Make sure the focused object is a descendant of self
860 // if( self == ancestor )
861 return focusedUIElement;
863 // ancestor = [ ancestor accessibilityAttributeValue: NSAccessibilityParentAttribute ];
864 // } while( nil != ancestor );
869 // TODO: hard-coded like the role descriptions. is there a better way?
870 -(NSString *)accessibilityActionDescription:(NSString *)action {
871 if ( [ action isEqualToString: NSAccessibilityConfirmAction ] ) {
873 } else if ( [ action isEqualToString: NSAccessibilityDecrementAction ] ) {
875 } else if ( [ action isEqualToString: NSAccessibilityDeleteAction ] ) {
877 } else if ( [ action isEqualToString: NSAccessibilityIncrementAction ] ) {
879 } else if ( [ action isEqualToString: NSAccessibilityPickAction ] ) {
881 } else if ( [ action isEqualToString: NSAccessibilityPressAction ] ) {
883 } else if ( [ action isEqualToString: NSAccessibilityCancelAction ] ) {
885 } else if ( [ action isEqualToString: NSAccessibilityRaiseAction ] ) {
887 } else if ( [ action isEqualToString: NSAccessibilityShowMenuAction ] ) {
890 return [ NSString string ];
894 -(AquaA11yWrapper *)actionResponder {
895 AquaA11yWrapper * wrapper = nil;
896 // get some information
897 NSString * role = (NSString *) [ self accessibilityAttributeValue: NSAccessibilityRoleAttribute ];
898 id enabledAttr = [ self enabledAttribute ];
899 MacOSBOOL enabled = [ enabledAttr boolValue ];
900 NSView * parent = (NSView *) [ self accessibilityAttributeValue: NSAccessibilityParentAttribute ];
901 AquaA11yWrapper * parentAsWrapper = nil;
902 if ( [ parent isKindOfClass: [ AquaA11yWrapper class ] ] ) {
903 parentAsWrapper = (AquaA11yWrapper *) parent;
905 NSString * parentRole = (NSString *) [ parent accessibilityAttributeValue: NSAccessibilityRoleAttribute ];
906 // if we are a textarea inside a combobox, then the combobox is the action responder
908 && [ role isEqualToString: NSAccessibilityTextAreaRole ]
909 && [ parentRole isEqualToString: NSAccessibilityComboBoxRole ]
910 && parentAsWrapper != nil ) {
911 wrapper = parentAsWrapper;
912 } else if ( enabled && [ self accessibleAction ] != nil ) {
915 [ parentRole release ];
916 [ enabledAttr release ];
921 -(void)accessibilityPerformAction:(NSString *)action {
922 AquaA11yWrapper * actionResponder = [ self actionResponder ];
923 if ( actionResponder != nil ) {
924 [ AquaA11yActionWrapper doAction: action ofElement: actionResponder ];
928 -(NSArray *)accessibilityActionNames {
929 NSArray * actionNames = nil;
930 AquaA11yWrapper * actionResponder = [ self actionResponder ];
931 if ( actionResponder != nil ) {
932 actionNames = [ AquaA11yActionWrapper actionNamesForElement: actionResponder ];
934 actionNames = [ [ NSArray alloc ] init ];
940 #pragma mark Hit Test
942 -(MacOSBOOL)isViewElement:(NSObject *)viewElement hitByPoint:(NSPoint)point {
944 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
945 NSValue * position = [ viewElement accessibilityAttributeValue: NSAccessibilityPositionAttribute ];
946 NSValue * size = [ viewElement accessibilityAttributeValue: NSAccessibilitySizeAttribute ];
947 if ( position != nil && size != nil ) {
948 float minX = [ position pointValue ].x;
949 float minY = [ position pointValue ].y;
950 float maxX = minX + [ size sizeValue ].width;
951 float maxY = minY + [ size sizeValue ].height;
952 if ( minX < point.x && maxX > point.x && minY < point.y && maxY > point.y ) {
960 Reference < XAccessibleContext > hitTestRunner ( Point point, Reference < XAccessibleContext > rxAccessibleContext ) {
961 Reference < XAccessibleContext > hitChild;
962 Reference < XAccessibleContext > emptyReference;
964 Reference < XAccessibleComponent > rxAccessibleComponent ( rxAccessibleContext, UNO_QUERY );
965 if ( rxAccessibleComponent.is() ) {
966 Point location = rxAccessibleComponent -> getLocationOnScreen();
967 Point hitPoint ( point.X - location.X , point.Y - location.Y);
968 Reference < XAccessible > rxAccessible = rxAccessibleComponent -> getAccessibleAtPoint ( hitPoint );
969 if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) {
970 if ( rxAccessible -> getAccessibleContext() -> getAccessibleChildCount() > 0 ) {
971 hitChild = hitTestRunner ( point, rxAccessible -> getAccessibleContext() );
972 if ( ! hitChild.is() ) {
973 hitChild = rxAccessible -> getAccessibleContext();
976 hitChild = rxAccessible -> getAccessibleContext();
980 if ( !hitChild.is() && rxAccessibleContext -> getAccessibleChildCount() > 0 ) { // special treatment for e.g. comboboxes
981 for ( int i = 0; i < rxAccessibleContext -> getAccessibleChildCount(); i++ ) {
982 Reference < XAccessible > rxAccessibleChild = rxAccessibleContext -> getAccessibleChild ( i );
983 if ( rxAccessibleChild.is() && rxAccessibleChild -> getAccessibleContext().is() && rxAccessibleChild -> getAccessibleContext() -> getAccessibleRole() != AccessibleRole::LIST ) {
984 Reference < XAccessibleContext > myHitChild = hitTestRunner ( point, rxAccessibleChild -> getAccessibleContext() );
985 if ( myHitChild.is() ) {
986 hitChild = myHitChild;
992 } catch ( RuntimeException ) {
993 return emptyReference;
998 -(id)accessibilityHitTest:(NSPoint)point {
999 static id wrapper = nil;
1000 if ( nil != wrapper ) {
1001 [ wrapper release ];
1004 Reference < XAccessibleContext > hitChild;
1005 NSRect screenRect = [ [ NSScreen mainScreen ] frame ];
1006 Point hitPoint ( static_cast<long>(point.x) , static_cast<long>(screenRect.size.height - point.y) );
1007 // check child windows first
1008 NSWindow * window = (NSWindow *) [ self accessibilityAttributeValue: NSAccessibilityWindowAttribute ];
1009 NSArray * childWindows = [ window childWindows ];
1010 if ( [ childWindows count ] > 0 ) {
1011 NSWindow * element = nil;
1012 NSEnumerator * enumerator = [ childWindows objectEnumerator ];
1013 while ( ( element = [ enumerator nextObject ] ) && hitChild == nil ) {
1014 if ( [ element isKindOfClass: [ SalFrameWindow class ] ] && [ self isViewElement: element hitByPoint: point ] ) {
1015 // we have a child window that is hit
1016 Reference < XAccessibleRelationSet > relationSet = [ ( ( SalFrameWindow * ) element ) accessibleContext ] -> getAccessibleRelationSet();
1017 if ( relationSet.is() && relationSet -> containsRelation ( AccessibleRelationType::SUB_WINDOW_OF )) {
1018 // we have a valid relation to the parent element
1019 AccessibleRelation relation = relationSet -> getRelationByType ( AccessibleRelationType::SUB_WINDOW_OF );
1020 for ( int i = 0; i < relation.TargetSet.getLength() && !hitChild.is(); i++ ) {
1021 Reference < XAccessible > rxAccessible ( relation.TargetSet [ i ], UNO_QUERY );
1022 if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) {
1023 // hit test for children of parent
1024 hitChild = hitTestRunner ( hitPoint, rxAccessible -> getAccessibleContext() );
1031 // nothing hit yet, so check ourself
1032 if ( ! hitChild.is() ) {
1033 if ( mpReferenceWrapper == nil ) {
1034 [ self setDefaults: [ self accessibleContext ] ];
1036 hitChild = hitTestRunner ( hitPoint, mpReferenceWrapper -> rAccessibleContext );
1038 if ( hitChild.is() ) {
1039 wrapper = [ AquaA11yFactory wrapperForAccessibleContext: hitChild ];
1041 if ( wrapper != nil ) {
1042 [ wrapper retain ]; // TODO: retain only when transient ?
1048 #pragma mark Access Methods
1050 -(XAccessibleAction *)accessibleAction {
1051 return mpReferenceWrapper -> rAccessibleAction.get();
1054 -(XAccessibleContext *)accessibleContext {
1055 return mpReferenceWrapper -> rAccessibleContext.get();
1058 -(XAccessibleComponent *)accessibleComponent {
1059 return mpReferenceWrapper -> rAccessibleComponent.get();
1062 -(XAccessibleExtendedComponent *)accessibleExtendedComponent {
1063 return mpReferenceWrapper -> rAccessibleExtendedComponent.get();
1066 -(XAccessibleSelection *)accessibleSelection {
1067 return mpReferenceWrapper -> rAccessibleSelection.get();
1070 -(XAccessibleTable *)accessibleTable {
1071 return mpReferenceWrapper -> rAccessibleTable.get();
1074 -(XAccessibleText *)accessibleText {
1075 return mpReferenceWrapper -> rAccessibleText.get();
1078 -(XAccessibleEditableText *)accessibleEditableText {
1079 return mpReferenceWrapper -> rAccessibleEditableText.get();
1082 -(XAccessibleValue *)accessibleValue {
1083 return mpReferenceWrapper -> rAccessibleValue.get();
1086 -(XAccessibleTextAttributes *)accessibleTextAttributes {
1087 return mpReferenceWrapper -> rAccessibleTextAttributes.get();
1090 -(XAccessibleMultiLineText *)accessibleMultiLineText {
1091 return mpReferenceWrapper -> rAccessibleMultiLineText.get();
1094 -(NSView *)viewElementForParent {
1098 // These four are for AXTextAreas only. They are needed, because bold and italic
1099 // attributes have to be bound to a font on the Mac. Our UNO-API instead handles
1100 // and reports them independently. When they occur we bundle them to a font with
1101 // this information here to create a according NSFont.
1102 -(void)setDefaultFontname:(NSString *)fontname {
1103 if ( mpDefaultFontname != nil ) {
1104 [ mpDefaultFontname release ];
1106 mpDefaultFontname = fontname;
1109 -(NSString *)defaultFontname {
1110 return mpDefaultFontname;
1113 -(void)setDefaultFontsize:(float)fontsize {
1114 mDefaultFontsize = fontsize;
1117 -(float)defaultFontsize {
1118 return mDefaultFontsize;
1121 -(void)setActsAsRadioGroup:(MacOSBOOL)actsAsRadioGroup {
1122 mActsAsRadioGroup = actsAsRadioGroup;
1125 -(MacOSBOOL)actsAsRadioGroup {
1126 return mActsAsRadioGroup;
1129 +(void)setPopupMenuOpen:(MacOSBOOL)popupMenuOpen {
1130 isPopupMenuOpen = popupMenuOpen;