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
112 if ( ! rxAccessibleContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::TRANSIENT ) ) {
113 Reference< XAccessibleEventBroadcaster > xBroadcaster(rxAccessibleContext, UNO_QUERY);
114 if( xBroadcaster.is() ) {
116 * We intentionally do not hold a reference to the event listener in the wrapper object,
117 * but let the listener control the life cycle of the wrapper instead ..
119 xBroadcaster->addEventListener( new AquaA11yEventListener( self, rxAccessibleContext -> getAccessibleRole() ) );
123 if ( rxAccessibleContext -> getAccessibleRole() == AccessibleRole::TABLE_CELL ) {
126 } catch ( const Exception ) {
131 if ( mpReferenceWrapper != nil ) {
132 delete mpReferenceWrapper;
134 if ( mpDefaultFontname != nil ) {
135 [ mpDefaultFontname release ];
141 #pragma mark Utility Section
143 // generates selectors for attribute name AXAttributeNameHere
144 // (getter without parameter) attributeNameHereAttribute
145 // (getter with parameter) attributeNameHereAttributeForParameter:
146 // (setter) setAttributeNameHereAttributeForElement:to:
147 -(SEL)selectorForAttribute:(NSString *)attribute asGetter:(MacOSBOOL)asGetter withGetterParameter:(MacOSBOOL)withGetterParameter {
149 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
151 // step 1: create method name from attribute name
152 NSMutableString * methodName = [ NSMutableString string ];
154 [ methodName appendString: @"set" ];
156 NSString * firstChar = [ attribute substringWithRange: NSMakeRange ( 2, 1 ) ]; // drop leading "AX" and get first char
158 [ methodName appendString: [ firstChar lowercaseString ] ];
160 [ methodName appendString: firstChar ];
162 [ methodName appendString: [ attribute substringFromIndex: 3 ] ]; // append rest of attribute name
163 // append rest of method name
164 [ methodName appendString: @"Attribute" ];
166 [ methodName appendString: @"ForElement:to:" ];
167 } else if ( asGetter && withGetterParameter ) {
168 [ methodName appendString: @"ForParameter:" ];
170 // step 2: create selector
171 selector = NSSelectorFromString ( methodName );
172 } @catch ( id exception ) {
179 -(Reference < XAccessible >)getFirstRadioButtonInGroup {
180 Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
181 if( rxAccessibleRelationSet.is() )
183 AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF );
184 if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() )
185 return Reference < XAccessible > ( relationMemberOf.TargetSet[0], UNO_QUERY );
187 return Reference < XAccessible > ();
190 -(MacOSBOOL)isFirstRadioButtonInGroup {
191 Reference < XAccessible > rFirstMateAccessible = [ self getFirstRadioButtonInGroup ];
192 if ( rFirstMateAccessible.is() && rFirstMateAccessible -> getAccessibleContext().get() == [ self accessibleContext ] ) {
199 #pragma mark Attribute Value Getters
200 // ( called via Reflection by accessibilityAttributeValue )
203 Radiobutton grouping is done differently in NSAccessibility and the UNO-API. In UNO related radio buttons share an entry in their
204 RelationSet. In NSAccessibility the relationship is axpressed through the hierarchy. A AXRadioGroup contains two or more AXRadioButton
205 objects. Since this group is not available in the UNO hierarchy, an extra wrapper is used for it. This wrapper shares almost all
206 attributes with the first radio button of the group, except for the role, subrole, role description, parent and children attributes.
207 So in this five methods there is a special treatment for radio buttons and groups.
211 if ( mActsAsRadioGroup ) {
212 return NSAccessibilityRadioGroupRole;
214 return [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ];
218 -(id)subroleAttribute {
219 if ( mActsAsRadioGroup ) {
222 NSString * subRole = [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ];
223 if ( ! [ subRole isEqualToString: @"" ] ) {
227 return [ super accessibilityAttributeValue: NSAccessibilitySubroleAttribute ];
232 -(id)titleAttribute {
233 return CreateNSString ( [ self accessibleContext ] -> getAccessibleName() );
236 -(id)descriptionAttribute {
237 if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) {
238 return [ self titleAttribute ];
239 } else if ( [ self accessibleExtendedComponent ] != nil ) {
240 return [ AquaA11yComponentWrapper descriptionAttributeForElement: self ];
242 return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() );
246 -(id)enabledAttribute {
247 if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) {
248 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::ENABLED ) ];
254 -(id)focusedAttribute {
255 if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) {
257 Reference < XAccessible > rxParent = [ self accessibleContext ] -> getAccessibleParent();
258 if ( rxParent.is() ) {
259 Reference < XAccessibleContext > rxContext = rxParent -> getAccessibleContext();
260 if ( rxContext.is() && rxContext -> getAccessibleStateSet().is() ) {
261 isFocused = [ NSNumber numberWithBool: rxContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ];
265 } else if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) {
266 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ];
272 -(id)parentAttribute {
273 if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON && ! mActsAsRadioGroup ) {
274 Reference < XAccessible > rxAccessible = [ self getFirstRadioButtonInGroup ];
275 if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) {
276 Reference < XAccessibleContext > rxAccessibleContext = rxAccessible -> getAccessibleContext();
277 id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: YES asRadioGroup: YES ];
278 [ parent_wrapper autorelease ];
279 return NSAccessibilityUnignoredAncestor( parent_wrapper );
284 Reference< XAccessible > xParent( [ self accessibleContext ] -> getAccessibleParent() );
285 if ( xParent.is() ) {
286 Reference< XAccessibleContext > xContext( xParent -> getAccessibleContext() );
287 if ( xContext.is() ) {
288 id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: xContext ];
289 [ parent_wrapper autorelease ];
290 return NSAccessibilityUnignoredAncestor( parent_wrapper );
293 } catch (const Exception&) {
300 -(id)childrenAttribute {
301 if ( mActsAsRadioGroup ) {
302 NSMutableArray * children = [ [ NSMutableArray alloc ] init ];
303 Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
304 AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF );
305 if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() ) {
306 for ( int index = 0; index < relationMemberOf.TargetSet.getLength(); index++ ) {
307 Reference < XAccessible > rMateAccessible = Reference < XAccessible > ( relationMemberOf.TargetSet[index], UNO_QUERY );
308 if ( rMateAccessible.is() ) {
309 Reference< XAccessibleContext > rMateAccessibleContext( rMateAccessible -> getAccessibleContext() );
310 if ( rMateAccessibleContext.is() ) {
311 id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rMateAccessibleContext ];
312 [ children addObject: wrapper ];
319 } else if ( [ self accessibleTable ] != nil ) {
320 return [ AquaA11yTableWrapper childrenAttributeForElement: self ];
323 NSMutableArray * children = [ [ NSMutableArray alloc ] init ];
324 Reference< XAccessibleContext > xContext( [ self accessibleContext ] );
326 sal_Int32 cnt = xContext -> getAccessibleChildCount();
327 for ( sal_Int32 i = 0; i < cnt; i++ ) {
328 Reference< XAccessible > xChild( xContext -> getAccessibleChild( i ) );
330 Reference< XAccessibleContext > xChildContext( xChild -> getAccessibleContext() );
331 // the menubar is already accessible (including Apple- and Application-Menu) through NSApplication => omit it here
332 if ( xChildContext.is() && AccessibleRole::MENU_BAR != xChildContext -> getAccessibleRole() ) {
333 id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: xChildContext ];
334 [ children addObject: wrapper ];
340 // if not already acting as RadioGroup now is the time to replace RadioButtons with RadioGroups and remove RadioButtons
341 if ( ! mActsAsRadioGroup ) {
342 NSEnumerator * enumerator = [ children objectEnumerator ];
343 AquaA11yWrapper * element;
344 while ( ( element = ( (AquaA11yWrapper *) [ enumerator nextObject ] ) ) ) {
345 if ( [ element accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) {
346 if ( [ element isFirstRadioButtonInGroup ] ) {
347 id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ element accessibleContext ] createIfNotExists: YES asRadioGroup: YES ];
348 [ children replaceObjectAtIndex: [ children indexOfObjectIdenticalTo: element ] withObject: wrapper ];
350 [ children removeObject: element ];
355 [ children autorelease ];
356 return NSAccessibilityUnignoredChildren( children );
357 } catch (const Exception &e) {
364 -(id)windowAttribute {
365 // go upstairs until reaching the broken connection
366 AquaA11yWrapper * aWrapper = self;
367 while ( [ aWrapper accessibleContext ] -> getAccessibleParent().is() ) {
368 aWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ aWrapper accessibleContext ] -> getAccessibleParent() -> getAccessibleContext() ];
369 [ aWrapper autorelease ];
371 // get associated NSWindow
372 NSView * theView = [ aWrapper viewElementForParent ];
376 -(id)topLevelUIElementAttribute {
377 return [ self windowAttribute ];
381 if ( [ self accessibleComponent ] != nil ) {
382 return [ AquaA11yComponentWrapper sizeAttributeForElement: self ];
388 -(id)positionAttribute {
389 if ( [ self accessibleComponent ] != nil ) {
390 return [ AquaA11yComponentWrapper positionAttributeForElement: self ];
397 return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() );
400 -(id)roleDescriptionAttribute {
401 if ( mActsAsRadioGroup ) {
402 return [ AquaA11yRoleHelper getRoleDescriptionFrom: NSAccessibilityRadioGroupRole with: @"" ];
403 } else if( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) {
404 // FIXME: VO should read this because of hierarchy, this is just a workaround
405 // get parent and its children
406 AquaA11yWrapper * parent = [ self parentAttribute ];
407 NSArray * children = [ parent childrenAttribute ];
408 // find index of self
410 NSEnumerator * enumerator = [ children objectEnumerator ];
411 AquaA11yWrapper * child = nil;
412 while ( ( child = [ enumerator nextObject ] ) ) {
413 if ( self == child ) {
419 NSNumber * nIndex = [ NSNumber numberWithInt: index ];
420 NSNumber * nGroupsize = [ NSNumber numberWithInt: [ children count ] ];
421 NSMutableString * value = [ [ NSMutableString alloc ] init ];
422 [ value appendString: @"radio button " ];
423 [ value appendString: [ nIndex stringValue ] ];
424 [ value appendString: @" of " ];
425 [ value appendString: [ nGroupsize stringValue ] ];
426 // clean up and return string
428 [ nGroupsize release ];
429 [ children release ];
432 return [ AquaA11yRoleHelper getRoleDescriptionFrom:
433 [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ]
434 with: [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ] ];
438 -(id)valueAttribute {
439 if ( [ [ self roleAttribute ] isEqualToString: NSAccessibilityMenuItemRole ] ) {
441 } else if ( [ self accessibleText ] != nil ) {
442 return [ AquaA11yTextWrapper valueAttributeForElement: self ];
443 } else if ( [ self accessibleValue ] != nil ) {
444 return [ AquaA11yValueWrapper valueAttributeForElement: self ];
450 -(id)minValueAttribute {
451 if ( [ self accessibleValue ] != nil ) {
452 return [ AquaA11yValueWrapper minValueAttributeForElement: self ];
458 -(id)maxValueAttribute {
459 if ( [ self accessibleValue ] != nil ) {
460 return [ AquaA11yValueWrapper maxValueAttributeForElement: self ];
466 -(id)contentsAttribute {
467 return [ self childrenAttribute ];
470 -(id)selectedChildrenAttribute {
471 return [ AquaA11ySelectionWrapper selectedChildrenAttributeForElement: self ];
474 -(id)numberOfCharactersAttribute {
475 if ( [ self accessibleText ] != nil ) {
476 return [ AquaA11yTextWrapper numberOfCharactersAttributeForElement: self ];
482 -(id)selectedTextAttribute {
483 if ( [ self accessibleText ] != nil ) {
484 return [ AquaA11yTextWrapper selectedTextAttributeForElement: self ];
490 -(id)selectedTextRangeAttribute {
491 if ( [ self accessibleText ] != nil ) {
492 return [ AquaA11yTextWrapper selectedTextRangeAttributeForElement: self ];
498 -(id)visibleCharacterRangeAttribute {
499 if ( [ self accessibleText ] != nil ) {
500 return [ AquaA11yTextWrapper visibleCharacterRangeAttributeForElement: self ];
507 return self; // TODO ???
510 -(id)sharedTextUIElementsAttribute {
511 if ( [ self accessibleText ] != nil ) {
512 return [ AquaA11yTextWrapper sharedTextUIElementsAttributeForElement: self ];
518 -(id)sharedCharacterRangeAttribute {
519 if ( [ self accessibleText ] != nil ) {
520 return [ AquaA11yTextWrapper sharedCharacterRangeAttributeForElement: self ];
526 -(id)expandedAttribute {
527 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::EXPANDED ) ];
530 -(id)selectedAttribute {
531 return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::SELECTED ) ];
534 -(id)stringForRangeAttributeForParameter:(id)range {
535 if ( [ self accessibleText ] != nil ) {
536 return [ AquaA11yTextWrapper stringForRangeAttributeForElement: self forParameter: range ];
542 -(id)attributedStringForRangeAttributeForParameter:(id)range {
543 if ( [ self accessibleText ] != nil ) {
544 return [ AquaA11yTextWrapper attributedStringForRangeAttributeForElement: self forParameter: range ];
550 -(id)rangeForIndexAttributeForParameter:(id)index {
551 if ( [ self accessibleText ] != nil ) {
552 return [ AquaA11yTextWrapper rangeForIndexAttributeForElement: self forParameter: index ];
558 -(id)rangeForPositionAttributeForParameter:(id)point {
559 if ( [ self accessibleText ] != nil ) {
560 return [ AquaA11yTextWrapper rangeForPositionAttributeForElement: self forParameter: point ];
566 -(id)boundsForRangeAttributeForParameter:(id)range {
567 if ( [ self accessibleText ] != nil ) {
568 return [ AquaA11yTextWrapper boundsForRangeAttributeForElement: self forParameter: range ];
574 -(id)styleRangeForIndexAttributeForParameter:(id)index {
575 if ( [ self accessibleText ] != nil ) {
576 return [ AquaA11yTextWrapper styleRangeForIndexAttributeForElement: self forParameter: index ];
582 -(id)rTFForRangeAttributeForParameter:(id)range {
583 if ( [ self accessibleText ] != nil ) {
584 return [ AquaA11yTextWrapper rTFForRangeAttributeForElement: self forParameter: range ];
590 -(id)orientationAttribute {
591 NSString * orientation = nil;
592 Reference < XAccessibleStateSet > stateSet = [ self accessibleContext ] -> getAccessibleStateSet();
593 if ( stateSet -> contains ( AccessibleStateType::HORIZONTAL ) ) {
594 orientation = NSAccessibilityHorizontalOrientationValue;
595 } else if ( stateSet -> contains ( AccessibleStateType::VERTICAL ) ) {
596 orientation = NSAccessibilityVerticalOrientationValue;
601 -(id)titleUIElementAttribute {
602 if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) {
603 NSString * title = [ self titleAttribute ];
604 id titleElement = nil;
605 if ( [ title length ] == 0 ) {
606 AccessibleRelation relationLabeledBy = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABELED_BY );
607 if ( relationLabeledBy.RelationType == AccessibleRelationType::LABELED_BY && relationLabeledBy.TargetSet.hasElements() ) {
608 Reference < XAccessible > rxAccessible ( relationLabeledBy.TargetSet[0], UNO_QUERY );
609 titleElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ];
612 if ( title != nil ) {
621 -(id)servesAsTitleForUIElementsAttribute {
622 if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) {
623 id titleForElement = nil;
624 AccessibleRelation relationLabelFor = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABEL_FOR );
625 if ( relationLabelFor.RelationType == AccessibleRelationType::LABEL_FOR && relationLabelFor.TargetSet.hasElements() ) {
626 Reference < XAccessible > rxAccessible ( relationLabelFor.TargetSet[0], UNO_QUERY );
627 titleForElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ];
629 return titleForElement;
635 -(id)lineForIndexAttributeForParameter:(id)index {
636 if ( [ self accessibleMultiLineText ] != nil ) {
637 return [ AquaA11yTextWrapper lineForIndexAttributeForElement: self forParameter: index ];
643 -(id)rangeForLineAttributeForParameter:(id)line {
644 if ( [ self accessibleMultiLineText ] != nil ) {
645 return [ AquaA11yTextWrapper rangeForLineAttributeForElement: self forParameter: line ];
652 #pragma mark Accessibility Protocol
654 -(id)accessibilityAttributeValue:(NSString *)attribute {
655 // #i90575# guard NSAccessibility protocol against unwanted access
656 if ( isPopupMenuOpen ) {
660 // if we are no longer in the wrapper repository, we have been disposed
661 AquaA11yWrapper * theWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ self accessibleContext ] createIfNotExists: NO ];
662 if ( theWrapper != nil || mIsTableCell ) {
664 SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: NO ];
665 if ( [ self respondsToSelector: methodSelector ] ) {
666 value = [ self performSelector: methodSelector ];
668 } catch ( const DisposedException & e ) {
669 mIsTableCell = NO; // just to be sure
670 [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ];
672 } catch ( const Exception & e ) {
676 if ( theWrapper != nil ) {
677 [ theWrapper release ]; // the above called method calls retain on the returned Wrapper
682 -(MacOSBOOL)accessibilityIsIgnored {
683 // #i90575# guard NSAccessibility protocol against unwanted access
684 if ( isPopupMenuOpen ) {
687 MacOSBOOL ignored = NO;
688 sal_Int16 nRole = [ self accessibleContext ] -> getAccessibleRole();
690 case AccessibleRole::PANEL:
691 case AccessibleRole::FRAME:
692 case AccessibleRole::ROOT_PANE:
693 case AccessibleRole::SEPARATOR:
694 case AccessibleRole::FILLER:
695 case AccessibleRole::DIALOG:
699 ignored = ! ( [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::VISIBLE ) );
702 return ignored; // TODO: to be completed
705 -(NSArray *)accessibilityAttributeNames {
706 // #i90575# guard NSAccessibility protocol against unwanted access
707 if ( isPopupMenuOpen ) {
710 NSString * nativeSubrole = nil;
711 NSString * title = nil;
712 NSMutableArray * attributeNames = nil;
714 // Default Attributes
715 attributeNames = [ NSMutableArray arrayWithObjects:
716 NSAccessibilityRoleAttribute,
717 NSAccessibilityDescriptionAttribute,
718 NSAccessibilityParentAttribute,
719 NSAccessibilityWindowAttribute,
720 NSAccessibilityHelpAttribute,
721 NSAccessibilityTopLevelUIElementAttribute,
722 NSAccessibilityRoleDescriptionAttribute,
724 nativeSubrole = (NSString *) [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ];
725 title = (NSString *) [ self titleAttribute ];
726 Reference < XAccessibleRelationSet > rxRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
727 // Special Attributes depending on attribute values
728 if ( nativeSubrole != nil && ! [ nativeSubrole isEqualToString: @"" ] ) {
729 [ attributeNames addObject: NSAccessibilitySubroleAttribute ];
733 if ( [ self accessibleContext ] -> getAccessibleChildCount() > 0 ) {
734 [ attributeNames addObject: NSAccessibilityChildrenAttribute ];
737 catch( DisposedException& ) {}
738 catch( RuntimeException& ) {}
740 if ( title != nil && ! [ title isEqualToString: @"" ] ) {
741 [ attributeNames addObject: NSAccessibilityTitleAttribute ];
743 if ( [ title length ] == 0 && rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABELED_BY ) ) {
744 [ attributeNames addObject: NSAccessibilityTitleUIElementAttribute ];
746 if ( rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABEL_FOR ) ) {
747 [ attributeNames addObject: NSAccessibilityServesAsTitleForUIElementsAttribute ];
749 // Special Attributes depending on interface
750 if ( [ self accessibleText ] != nil ) {
751 [ AquaA11yTextWrapper addAttributeNamesTo: attributeNames ];
753 if ( [ self accessibleComponent ] != nil ) {
754 [ AquaA11yComponentWrapper addAttributeNamesTo: attributeNames ];
756 if ( [ self accessibleSelection ] != nil ) {
757 [ AquaA11ySelectionWrapper addAttributeNamesTo: attributeNames ];
759 if ( [ self accessibleValue ] != nil ) {
760 [ AquaA11yValueWrapper addAttributeNamesTo: attributeNames ];
762 [ nativeSubrole release ];
764 return attributeNames;
765 } catch ( DisposedException & e ) { // Object is no longer available
766 if ( nativeSubrole != nil ) {
767 [ nativeSubrole release ];
769 if ( title != nil ) {
772 if ( attributeNames != nil ) {
773 [ attributeNames release ];
775 [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ];
776 return [ [ NSArray alloc ] init ];
780 -(MacOSBOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
781 MacOSBOOL isSettable = NO;
782 if ( [ self accessibleText ] != nil ) {
783 isSettable = [ AquaA11yTextWrapper isAttributeSettable: attribute forElement: self ];
785 if ( ! isSettable && [ self accessibleComponent ] != nil ) {
786 isSettable = [ AquaA11yComponentWrapper isAttributeSettable: attribute forElement: self ];
788 if ( ! isSettable && [ self accessibleSelection ] != nil ) {
789 isSettable = [ AquaA11ySelectionWrapper isAttributeSettable: attribute forElement: self ];
791 if ( ! isSettable && [ self accessibleValue ] != nil ) {
792 isSettable = [ AquaA11yValueWrapper isAttributeSettable: attribute forElement: self ];
794 return isSettable; // TODO: to be completed
797 -(NSArray *)accessibilityParameterizedAttributeNames {
798 NSMutableArray * attributeNames = [ [ NSMutableArray alloc ] init ];
799 // Special Attributes depending on interface
800 if ( [ self accessibleText ] != nil ) {
801 [ AquaA11yTextWrapper addParameterizedAttributeNamesTo: attributeNames ];
803 return attributeNames; // TODO: to be completed
806 -(id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter {
807 SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: YES ];
808 if ( [ self respondsToSelector: methodSelector ] ) {
809 return [ self performSelector: methodSelector withObject: parameter ];
811 return nil; // TODO: to be completed
814 -(MacOSBOOL)accessibilitySetOverrideValue:(id)value forAttribute:(NSString *)attribute {
818 -(void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
819 SEL methodSelector = [ self selectorForAttribute: attribute asGetter: NO withGetterParameter: NO ];
820 if ( [ AquaA11yComponentWrapper respondsToSelector: methodSelector ] ) {
821 [ AquaA11yComponentWrapper performSelector: methodSelector withObject: self withObject: value ];
823 if ( [ AquaA11yTextWrapper respondsToSelector: methodSelector ] ) {
824 [ AquaA11yTextWrapper performSelector: methodSelector withObject: self withObject: value ];
826 if ( [ AquaA11ySelectionWrapper respondsToSelector: methodSelector ] ) {
827 [ AquaA11ySelectionWrapper performSelector: methodSelector withObject: self withObject: value ];
829 if ( [ AquaA11yValueWrapper respondsToSelector: methodSelector ] ) {
830 [ AquaA11yValueWrapper performSelector: methodSelector withObject: self withObject: value ];
834 -(id)accessibilityFocusedUIElement {
835 // #i90575# guard NSAccessibility protocol against unwanted access
836 if ( isPopupMenuOpen ) {
840 // as this seems to be the first API call on a newly created SalFrameView object,
841 // make sure self gets registered in the repository ..
842 [ self accessibleContext ];
844 AquaA11yWrapper * focusedUIElement = AquaA11yFocusListener::get()->getFocusedUIElement();
845 // AquaA11yWrapper * ancestor = focusedUIElement;
847 // Make sure the focused object is a descendant of self
849 // if( self == ancestor )
850 return focusedUIElement;
852 // ancestor = [ ancestor accessibilityAttributeValue: NSAccessibilityParentAttribute ];
853 // } while( nil != ancestor );
858 // TODO: hard-coded like the role descriptions. is there a better way?
859 -(NSString *)accessibilityActionDescription:(NSString *)action {
860 if ( [ action isEqualToString: NSAccessibilityConfirmAction ] ) {
862 } else if ( [ action isEqualToString: NSAccessibilityDecrementAction ] ) {
864 } else if ( [ action isEqualToString: NSAccessibilityDeleteAction ] ) {
866 } else if ( [ action isEqualToString: NSAccessibilityIncrementAction ] ) {
868 } else if ( [ action isEqualToString: NSAccessibilityPickAction ] ) {
870 } else if ( [ action isEqualToString: NSAccessibilityPressAction ] ) {
872 } else if ( [ action isEqualToString: NSAccessibilityCancelAction ] ) {
874 } else if ( [ action isEqualToString: NSAccessibilityRaiseAction ] ) {
876 } else if ( [ action isEqualToString: NSAccessibilityShowMenuAction ] ) {
879 return [ NSString string ];
883 -(AquaA11yWrapper *)actionResponder {
884 AquaA11yWrapper * wrapper = nil;
885 // get some information
886 NSString * role = (NSString *) [ self accessibilityAttributeValue: NSAccessibilityRoleAttribute ];
887 id enabledAttr = [ self enabledAttribute ];
888 MacOSBOOL enabled = [ enabledAttr boolValue ];
889 NSView * parent = (NSView *) [ self accessibilityAttributeValue: NSAccessibilityParentAttribute ];
890 AquaA11yWrapper * parentAsWrapper = nil;
891 if ( [ parent isKindOfClass: [ AquaA11yWrapper class ] ] ) {
892 parentAsWrapper = (AquaA11yWrapper *) parent;
894 NSString * parentRole = (NSString *) [ parent accessibilityAttributeValue: NSAccessibilityRoleAttribute ];
895 // if we are a textarea inside a combobox, then the combobox is the action responder
897 && [ role isEqualToString: NSAccessibilityTextAreaRole ]
898 && [ parentRole isEqualToString: NSAccessibilityComboBoxRole ]
899 && parentAsWrapper != nil ) {
900 wrapper = parentAsWrapper;
901 } else if ( enabled && [ self accessibleAction ] != nil ) {
904 [ parentRole release ];
905 [ enabledAttr release ];
910 -(void)accessibilityPerformAction:(NSString *)action {
911 AquaA11yWrapper * actionResponder = [ self actionResponder ];
912 if ( actionResponder != nil ) {
913 [ AquaA11yActionWrapper doAction: action ofElement: actionResponder ];
917 -(NSArray *)accessibilityActionNames {
918 NSArray * actionNames = nil;
919 AquaA11yWrapper * actionResponder = [ self actionResponder ];
920 if ( actionResponder != nil ) {
921 actionNames = [ AquaA11yActionWrapper actionNamesForElement: actionResponder ];
923 actionNames = [ [ NSArray alloc ] init ];
929 #pragma mark Hit Test
931 -(MacOSBOOL)isViewElement:(NSObject *)viewElement hitByPoint:(NSPoint)point {
933 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
934 NSValue * position = [ viewElement accessibilityAttributeValue: NSAccessibilityPositionAttribute ];
935 NSValue * size = [ viewElement accessibilityAttributeValue: NSAccessibilitySizeAttribute ];
936 if ( position != nil && size != nil ) {
937 float minX = [ position pointValue ].x;
938 float minY = [ position pointValue ].y;
939 float maxX = minX + [ size sizeValue ].width;
940 float maxY = minY + [ size sizeValue ].height;
941 if ( minX < point.x && maxX > point.x && minY < point.y && maxY > point.y ) {
949 Reference < XAccessibleContext > hitTestRunner ( Point point, Reference < XAccessibleContext > rxAccessibleContext ) {
950 Reference < XAccessibleContext > hitChild;
951 Reference < XAccessibleContext > emptyReference;
953 Reference < XAccessibleComponent > rxAccessibleComponent ( rxAccessibleContext, UNO_QUERY );
954 if ( rxAccessibleComponent.is() ) {
955 Point location = rxAccessibleComponent -> getLocationOnScreen();
956 Point hitPoint ( point.X - location.X , point.Y - location.Y);
957 Reference < XAccessible > rxAccessible = rxAccessibleComponent -> getAccessibleAtPoint ( hitPoint );
958 if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) {
959 if ( rxAccessible -> getAccessibleContext() -> getAccessibleChildCount() > 0 ) {
960 hitChild = hitTestRunner ( point, rxAccessible -> getAccessibleContext() );
961 if ( ! hitChild.is() ) {
962 hitChild = rxAccessible -> getAccessibleContext();
965 hitChild = rxAccessible -> getAccessibleContext();
969 if ( !hitChild.is() && rxAccessibleContext -> getAccessibleChildCount() > 0 ) { // special treatment for e.g. comboboxes
970 for ( int i = 0; i < rxAccessibleContext -> getAccessibleChildCount(); i++ ) {
971 Reference < XAccessible > rxAccessibleChild = rxAccessibleContext -> getAccessibleChild ( i );
972 if ( rxAccessibleChild.is() && rxAccessibleChild -> getAccessibleContext().is() && rxAccessibleChild -> getAccessibleContext() -> getAccessibleRole() != AccessibleRole::LIST ) {
973 Reference < XAccessibleContext > myHitChild = hitTestRunner ( point, rxAccessibleChild -> getAccessibleContext() );
974 if ( myHitChild.is() ) {
975 hitChild = myHitChild;
981 } catch ( RuntimeException ) {
982 return emptyReference;
987 -(id)accessibilityHitTest:(NSPoint)point {
988 static id wrapper = nil;
989 if ( nil != wrapper ) {
993 Reference < XAccessibleContext > hitChild;
994 NSRect screenRect = [ [ NSScreen mainScreen ] frame ];
995 Point hitPoint ( static_cast<long>(point.x) , static_cast<long>(screenRect.size.height - point.y) );
996 // check child windows first
997 NSWindow * window = (NSWindow *) [ self accessibilityAttributeValue: NSAccessibilityWindowAttribute ];
998 NSArray * childWindows = [ window childWindows ];
999 if ( [ childWindows count ] > 0 ) {
1000 NSWindow * element = nil;
1001 NSEnumerator * enumerator = [ childWindows objectEnumerator ];
1002 while ( ( element = [ enumerator nextObject ] ) && hitChild == nil ) {
1003 if ( [ element isKindOfClass: [ SalFrameWindow class ] ] && [ self isViewElement: element hitByPoint: point ] ) {
1004 // we have a child window that is hit
1005 Reference < XAccessibleRelationSet > relationSet = [ ( ( SalFrameWindow * ) element ) accessibleContext ] -> getAccessibleRelationSet();
1006 if ( relationSet.is() && relationSet -> containsRelation ( AccessibleRelationType::SUB_WINDOW_OF )) {
1007 // we have a valid relation to the parent element
1008 AccessibleRelation relation = relationSet -> getRelationByType ( AccessibleRelationType::SUB_WINDOW_OF );
1009 for ( int i = 0; i < relation.TargetSet.getLength() && !hitChild.is(); i++ ) {
1010 Reference < XAccessible > rxAccessible ( relation.TargetSet [ i ], UNO_QUERY );
1011 if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) {
1012 // hit test for children of parent
1013 hitChild = hitTestRunner ( hitPoint, rxAccessible -> getAccessibleContext() );
1020 // nothing hit yet, so check ourself
1021 if ( ! hitChild.is() ) {
1022 if ( mpReferenceWrapper == nil ) {
1023 [ self setDefaults: [ self accessibleContext ] ];
1025 hitChild = hitTestRunner ( hitPoint, mpReferenceWrapper -> rAccessibleContext );
1027 if ( hitChild.is() ) {
1028 wrapper = [ AquaA11yFactory wrapperForAccessibleContext: hitChild ];
1030 if ( wrapper != nil ) {
1031 [ wrapper retain ]; // TODO: retain only when transient ?
1037 #pragma mark Access Methods
1039 -(XAccessibleAction *)accessibleAction {
1040 return mpReferenceWrapper -> rAccessibleAction.get();
1043 -(XAccessibleContext *)accessibleContext {
1044 return mpReferenceWrapper -> rAccessibleContext.get();
1047 -(XAccessibleComponent *)accessibleComponent {
1048 return mpReferenceWrapper -> rAccessibleComponent.get();
1051 -(XAccessibleExtendedComponent *)accessibleExtendedComponent {
1052 return mpReferenceWrapper -> rAccessibleExtendedComponent.get();
1055 -(XAccessibleSelection *)accessibleSelection {
1056 return mpReferenceWrapper -> rAccessibleSelection.get();
1059 -(XAccessibleTable *)accessibleTable {
1060 return mpReferenceWrapper -> rAccessibleTable.get();
1063 -(XAccessibleText *)accessibleText {
1064 return mpReferenceWrapper -> rAccessibleText.get();
1067 -(XAccessibleEditableText *)accessibleEditableText {
1068 return mpReferenceWrapper -> rAccessibleEditableText.get();
1071 -(XAccessibleValue *)accessibleValue {
1072 return mpReferenceWrapper -> rAccessibleValue.get();
1075 -(XAccessibleTextAttributes *)accessibleTextAttributes {
1076 return mpReferenceWrapper -> rAccessibleTextAttributes.get();
1079 -(XAccessibleMultiLineText *)accessibleMultiLineText {
1080 return mpReferenceWrapper -> rAccessibleMultiLineText.get();
1083 -(NSView *)viewElementForParent {
1087 // These four are for AXTextAreas only. They are needed, because bold and italic
1088 // attributes have to be bound to a font on the Mac. Our UNO-API instead handles
1089 // and reports them independently. When they occur we bundle them to a font with
1090 // this information here to create a according NSFont.
1091 -(void)setDefaultFontname:(NSString *)fontname {
1092 if ( mpDefaultFontname != nil ) {
1093 [ mpDefaultFontname release ];
1095 mpDefaultFontname = fontname;
1098 -(NSString *)defaultFontname {
1099 return mpDefaultFontname;
1102 -(void)setDefaultFontsize:(float)fontsize {
1103 mDefaultFontsize = fontsize;
1106 -(float)defaultFontsize {
1107 return mDefaultFontsize;
1110 -(void)setActsAsRadioGroup:(MacOSBOOL)actsAsRadioGroup {
1111 mActsAsRadioGroup = actsAsRadioGroup;
1114 -(MacOSBOOL)actsAsRadioGroup {
1115 return mActsAsRadioGroup;
1118 +(void)setPopupMenuOpen:(MacOSBOOL)popupMenuOpen {
1119 isPopupMenuOpen = popupMenuOpen;