diff options
author | Oliver Bolte <obo@openoffice.org> | 2008-07-25 08:07:54 +0000 |
---|---|---|
committer | Oliver Bolte <obo@openoffice.org> | 2008-07-25 08:07:54 +0000 |
commit | 5ee6445bffd95ac9f0f0e3175ef0f2ce9a0ea68f (patch) | |
tree | 2a2afcb2a501b55679853d2d224631560f450057 /vcl/aqua/source/a11y | |
parent | ef50b51bf0e2d8ce91d89b86b05a149738da8873 (diff) |
INTEGRATION: CWS aqua11y02 (1.2.30); FILE MERGED
2008/07/22 07:52:08 fne 1.2.30.11: #i90575# fixed new found crash
2008/07/17 09:09:34 fne 1.2.30.10: #i88045# workaround
2008/06/19 12:29:51 fne 1.2.30.9: #i90525# resolved conflict with another issue
2008/06/11 09:15:56 fne 1.2.30.8: #i90575# guard NSAccessibility API against unwanted access from NSPopUp
2008/06/05 08:21:49 fne 1.2.30.7: #i85851# attributes are now supported
2008/05/27 08:55:52 fne 1.2.30.6: #i89131# fix so that VO can read selected entries while traversing through the comboboxes menu
2008/05/26 14:05:03 fne 1.2.30.5: #i89131# set focus correctly for toolbox-comboboxes
2008/05/22 08:17:06 fne 1.2.30.4: #i88045# added special treatment for role description
2008/05/21 08:33:59 fne 1.2.30.3: #i88045# support grouping of radio buttons
2008/05/19 11:16:18 fne 1.2.30.2: #i87833# when wrapper has been disposed it is unwise to access its data
2008/05/15 12:08:33 fne 1.2.30.1: #i87833# when caret pos is -1, VO tries to access invalid memory position
Diffstat (limited to 'vcl/aqua/source/a11y')
-rw-r--r-- | vcl/aqua/source/a11y/aqua11ywrapper.mm | 234 |
1 files changed, 213 insertions, 21 deletions
diff --git a/vcl/aqua/source/a11y/aqua11ywrapper.mm b/vcl/aqua/source/a11y/aqua11ywrapper.mm index 68dc0403af59..066748cc1525 100644 --- a/vcl/aqua/source/a11y/aqua11ywrapper.mm +++ b/vcl/aqua/source/a11y/aqua11ywrapper.mm @@ -8,7 +8,7 @@ * * $RCSfile: aqua11ywrapper.mm,v $ * - * $Revision: 1.2 $ + * $Revision: 1.3 $ * * This file is part of OpenOffice.org. * @@ -64,6 +64,8 @@ using namespace ::com::sun::star::uno; -(Reference<XAccessibleContext>)accessibleContext; @end +static MacOSBOOL isPopupMenuOpen = NO; + @implementation AquaA11yWrapper : NSView #pragma mark - @@ -81,7 +83,9 @@ using namespace ::com::sun::star::uno; mDefaultFontsize = 0.0; mpDefaultFontname = nil; mpReferenceWrapper = new ReferenceWrapper; + mActsAsRadioGroup = NO; mpReferenceWrapper -> rAccessibleContext = rxAccessibleContext; + mIsTableCell = NO; // Querying all supported interfaces try { // XAccessibleComponent @@ -102,6 +106,8 @@ using namespace ::com::sun::star::uno; mpReferenceWrapper -> rAccessibleAction = Reference < XAccessibleAction > ( rxAccessibleContext, UNO_QUERY ); // XAccessibleTextAttributes mpReferenceWrapper -> rAccessibleTextAttributes = Reference < XAccessibleTextAttributes > ( rxAccessibleContext, UNO_QUERY ); + // XAccessibleMultiLineText + mpReferenceWrapper -> rAccessibleMultiLineText = Reference < XAccessibleMultiLineText > ( rxAccessibleContext, UNO_QUERY ); // XAccessibleEventBroadcaster if ( ! rxAccessibleContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::TRANSIENT ) ) { Reference< XAccessibleEventBroadcaster > xBroadcaster(rxAccessibleContext, UNO_QUERY); @@ -113,6 +119,10 @@ using namespace ::com::sun::star::uno; xBroadcaster->addEventListener( new AquaA11yEventListener( self, rxAccessibleContext -> getAccessibleRole() ) ); } } + // TABLE_CELL + if ( rxAccessibleContext -> getAccessibleRole() == AccessibleRole::TABLE_CELL ) { + mIsTableCell = YES; + } } catch ( const Exception ) { } } @@ -166,21 +176,54 @@ using namespace ::com::sun::star::uno; return selector; } +-(Reference < XAccessible >)getFirstRadioButtonInGroup { + Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet(); + AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF ); + if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() ) { + return Reference < XAccessible > ( relationMemberOf.TargetSet[0], UNO_QUERY ); + } + return Reference < XAccessible > (); +} + +-(MacOSBOOL)isFirstRadioButtonInGroup { + Reference < XAccessible > rFirstMateAccessible = [ self getFirstRadioButtonInGroup ]; + if ( rFirstMateAccessible.is() && rFirstMateAccessible -> getAccessibleContext().get() == [ self accessibleContext ] ) { + return YES; + } + return NO; +} + #pragma mark - #pragma mark Attribute Value Getters // ( called via Reflection by accessibilityAttributeValue ) +/* + Radiobutton grouping is done differently in NSAccessibility and the UNO-API. In UNO related radio buttons share an entry in their + RelationSet. In NSAccessibility the relationship is axpressed through the hierarchy. A AXRadioGroup contains two or more AXRadioButton + objects. Since this group is not available in the UNO hierarchy, an extra wrapper is used for it. This wrapper shares almost all + attributes with the first radio button of the group, except for the role, subrole, role description, parent and children attributes. + So in this five methods there is a special treatment for radio buttons and groups. +*/ + -(id)roleAttribute { - return [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ]; + if ( mActsAsRadioGroup ) { + return NSAccessibilityRadioGroupRole; + } else { + return [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ]; + } } -(id)subroleAttribute { - NSString * subRole = [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ]; - if ( ! [ subRole isEqualToString: @"" ] ) { - return subRole; + if ( mActsAsRadioGroup ) { + return @""; } else { - [ subRole release ]; - return [ super accessibilityAttributeValue: NSAccessibilitySubroleAttribute ]; + NSString * subRole = [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ]; + if ( ! [ subRole isEqualToString: @"" ] ) { + return subRole; + } else { + [ subRole release ]; + return [ super accessibilityAttributeValue: NSAccessibilitySubroleAttribute ]; + } } } @@ -189,7 +232,9 @@ using namespace ::com::sun::star::uno; } -(id)descriptionAttribute { - if ( [ self accessibleExtendedComponent ] != nil ) { + if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) { + return [ self titleAttribute ]; + } else if ( [ self accessibleExtendedComponent ] != nil ) { return [ AquaA11yComponentWrapper descriptionAttributeForElement: self ]; } else { return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() ); @@ -205,7 +250,17 @@ using namespace ::com::sun::star::uno; } -(id)focusedAttribute { - if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) { + if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) { + id isFocused = nil; + Reference < XAccessible > rxParent = [ self accessibleContext ] -> getAccessibleParent(); + if ( rxParent.is() ) { + Reference < XAccessibleContext > rxContext = rxParent -> getAccessibleContext(); + if ( rxContext.is() && rxContext -> getAccessibleStateSet().is() ) { + isFocused = [ NSNumber numberWithBool: rxContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ]; + } + } + return isFocused; + } else if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) { return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ]; } else { return nil; @@ -213,6 +268,16 @@ using namespace ::com::sun::star::uno; } -(id)parentAttribute { + if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON && ! mActsAsRadioGroup ) { + Reference < XAccessible > rxAccessible = [ self getFirstRadioButtonInGroup ]; + if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) { + Reference < XAccessibleContext > rxAccessibleContext = rxAccessible -> getAccessibleContext(); + id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: YES asRadioGroup: YES ]; + [ parent_wrapper autorelease ]; + return NSAccessibilityUnignoredAncestor( parent_wrapper ); + } + return nil; + } try { Reference< XAccessible > xParent( [ self accessibleContext ] -> getAccessibleParent() ); if ( xParent.is() ) { @@ -231,7 +296,25 @@ using namespace ::com::sun::star::uno; } -(id)childrenAttribute { - if ( [ self accessibleTable ] != nil ) { + if ( mActsAsRadioGroup ) { + NSMutableArray * children = [ [ NSMutableArray alloc ] init ]; + Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet(); + AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF ); + if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() ) { + for ( int index = 0; index < relationMemberOf.TargetSet.getLength(); index++ ) { + Reference < XAccessible > rMateAccessible = Reference < XAccessible > ( relationMemberOf.TargetSet[index], UNO_QUERY ); + if ( rMateAccessible.is() ) { + Reference< XAccessibleContext > rMateAccessibleContext( rMateAccessible -> getAccessibleContext() ); + if ( rMateAccessibleContext.is() ) { + id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rMateAccessibleContext ]; + [ children addObject: wrapper ]; + [ wrapper release ]; + } + } + } + } + return children; + } else if ( [ self accessibleTable ] != nil ) { return [ AquaA11yTableWrapper childrenAttributeForElement: self ]; } else { try { @@ -252,6 +335,21 @@ using namespace ::com::sun::star::uno; } } + // if not already acting as RadioGroup now is the time to replace RadioButtons with RadioGroups and remove RadioButtons + if ( ! mActsAsRadioGroup ) { + NSEnumerator * enumerator = [ children objectEnumerator ]; + AquaA11yWrapper * element; + while ( ( element = ( (AquaA11yWrapper *) [ enumerator nextObject ] ) ) ) { + if ( [ element accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) { + if ( [ element isFirstRadioButtonInGroup ] ) { + id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ element accessibleContext ] createIfNotExists: YES asRadioGroup: YES ]; + [ children replaceObjectAtIndex: [ children indexOfObjectIdenticalTo: element ] withObject: wrapper ]; + } + [ children removeObject: element ]; + } + } + } + [ children autorelease ]; return NSAccessibilityUnignoredChildren( children ); } catch (const Exception &e) { @@ -298,13 +396,47 @@ using namespace ::com::sun::star::uno; } -(id)roleDescriptionAttribute { - return [ AquaA11yRoleHelper getRoleDescriptionFrom: - [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ] - with: [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ] ]; + if ( mActsAsRadioGroup ) { + return [ AquaA11yRoleHelper getRoleDescriptionFrom: NSAccessibilityRadioGroupRole with: @"" ]; + } else if( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) { + // FIXME: VO should read this because of hierarchy, this is just a workaround + // get parent and its children + AquaA11yWrapper * parent = [ self parentAttribute ]; + NSArray * children = [ parent childrenAttribute ]; + // find index of self + int index = 1; + NSEnumerator * enumerator = [ children objectEnumerator ]; + AquaA11yWrapper * child = nil; + while ( ( child = [ enumerator nextObject ] ) ) { + if ( self == child ) { + break; + } + index++; + } + // build string + NSNumber * nIndex = [ NSNumber numberWithInt: index ]; + NSNumber * nGroupsize = [ NSNumber numberWithInt: [ children count ] ]; + NSMutableString * value = [ [ NSMutableString alloc ] init ]; + [ value appendString: @"radio button " ]; + [ value appendString: [ nIndex stringValue ] ]; + [ value appendString: @" of " ]; + [ value appendString: [ nGroupsize stringValue ] ]; + // clean up and return string + [ nIndex release ]; + [ nGroupsize release ]; + [ children release ]; + return value; + } else { + return [ AquaA11yRoleHelper getRoleDescriptionFrom: + [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ] + with: [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ] ]; + } } -(id)valueAttribute { - if ( [ self accessibleText ] != nil ) { + if ( [ [ self roleAttribute ] isEqualToString: NSAccessibilityMenuItemRole ] ) { + return nil; + } else if ( [ self accessibleText ] != nil ) { return [ AquaA11yTextWrapper valueAttributeForElement: self ]; } else if ( [ self accessibleValue ] != nil ) { return [ AquaA11yValueWrapper valueAttributeForElement: self ]; @@ -498,23 +630,58 @@ using namespace ::com::sun::star::uno; } } +-(id)lineForIndexAttributeForParameter:(id)index { + if ( [ self accessibleMultiLineText ] != nil ) { + return [ AquaA11yTextWrapper lineForIndexAttributeForElement: self forParameter: index ]; + } else { + return nil; + } +} + +-(id)rangeForLineAttributeForParameter:(id)line { + if ( [ self accessibleMultiLineText ] != nil ) { + return [ AquaA11yTextWrapper rangeForLineAttributeForElement: self forParameter: line ]; + } else { + return nil; + } +} + #pragma mark - #pragma mark Accessibility Protocol -(id)accessibilityAttributeValue:(NSString *)attribute { + // #i90575# guard NSAccessibility protocol against unwanted access + if ( isPopupMenuOpen ) { + return nil; + } id value = nil; - try { - SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: NO ]; - if ( [ self respondsToSelector: methodSelector ] ) { - value = [ self performSelector: methodSelector ]; + // if we are no longer in the wrapper repository, we have been disposed + AquaA11yWrapper * theWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ self accessibleContext ] createIfNotExists: NO ]; + if ( theWrapper != nil || mIsTableCell ) { + try { + SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: NO ]; + if ( [ self respondsToSelector: methodSelector ] ) { + value = [ self performSelector: methodSelector ]; + } + } catch ( const DisposedException & e ) { + mIsTableCell = NO; // just to be sure + [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ]; + return nil; + } catch ( const Exception & e ) { + // empty } - } catch ( const Exception & e ) { - // empty + } + if ( theWrapper != nil ) { + [ theWrapper release ]; // the above called method calls retain on the returned Wrapper } return value; } -(MacOSBOOL)accessibilityIsIgnored { + // #i90575# guard NSAccessibility protocol against unwanted access + if ( isPopupMenuOpen ) { + return nil; + } MacOSBOOL ignored = NO; sal_Int16 nRole = [ self accessibleContext ] -> getAccessibleRole(); switch ( nRole ) { @@ -534,6 +701,10 @@ using namespace ::com::sun::star::uno; } -(NSArray *)accessibilityAttributeNames { + // #i90575# guard NSAccessibility protocol against unwanted access + if ( isPopupMenuOpen ) { + return nil; + } NSString * nativeSubrole = nil; NSString * title = nil; NSMutableArray * attributeNames = nil; @@ -593,7 +764,8 @@ using namespace ::com::sun::star::uno; if ( attributeNames != nil ) { [ attributeNames release ]; } - return nil; + [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ]; + return [ [ NSArray alloc ] init ]; } } @@ -652,6 +824,10 @@ using namespace ::com::sun::star::uno; } -(id)accessibilityFocusedUIElement { + // #i90575# guard NSAccessibility protocol against unwanted access + if ( isPopupMenuOpen ) { + return nil; + } // as this seems to be the first API call on a newly created SalFrameView object, // make sure self gets registered in the repository .. @@ -892,6 +1068,10 @@ Reference < XAccessibleContext > hitTestRunner ( Point point, Reference < XAcces return mpReferenceWrapper -> rAccessibleTextAttributes.get(); } +-(XAccessibleMultiLineText *)accessibleMultiLineText { + return mpReferenceWrapper -> rAccessibleMultiLineText.get(); +} + -(NSView *)viewElementForParent { return self; } @@ -919,4 +1099,16 @@ Reference < XAccessibleContext > hitTestRunner ( Point point, Reference < XAcces return mDefaultFontsize; } +-(void)setActsAsRadioGroup:(MacOSBOOL)actsAsRadioGroup { + mActsAsRadioGroup = actsAsRadioGroup; +} + +-(MacOSBOOL)actsAsRadioGroup { + return mActsAsRadioGroup; +} + ++(void)setPopupMenuOpen:(MacOSBOOL)popupMenuOpen { + isPopupMenuOpen = popupMenuOpen; +} + @end |