summaryrefslogtreecommitdiffstats
path: root/vcl/osx/a11ywrapper.mm
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /vcl/osx/a11ywrapper.mm
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/osx/a11ywrapper.mm')
-rw-r--r--vcl/osx/a11ywrapper.mm1616
1 files changed, 1616 insertions, 0 deletions
diff --git a/vcl/osx/a11ywrapper.mm b/vcl/osx/a11ywrapper.mm
new file mode 100644
index 0000000000..df1d3690df
--- /dev/null
+++ b/vcl/osx/a11ywrapper.mm
@@ -0,0 +1,1616 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <osx/salinst.h>
+#include <osx/saldata.hxx>
+
+#include <osx/a11ywrapper.h>
+#include <osx/a11ylistener.hxx>
+#include <osx/a11yfactory.h>
+#include <osx/a11yfocustracker.hxx>
+
+#include <quartz/salgdi.h>
+#include <quartz/utils.h>
+
+#include "a11yfocuslistener.hxx"
+#include "a11yactionwrapper.h"
+#include "a11ycomponentwrapper.h"
+#include "a11yselectionwrapper.h"
+#include "a11ytablewrapper.h"
+#include "a11ytextwrapper.h"
+#include "a11yvaluewrapper.h"
+#include "a11yrolehelper.h"
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+@interface SalFrameWindow : NSWindow
+{
+}
+-(Reference<XAccessibleContext>)accessibleContext;
+@end
+
+static bool isPopupMenuOpen = false;
+
+static std::ostream &operator<<(std::ostream &s, NSObject *obj) {
+ return s << [[obj description] UTF8String];
+}
+
+@implementation AquaA11yWrapper
+
+#pragma mark -
+#pragma mark Init and dealloc
+
+-(id)init {
+ return [ super init ];
+}
+
+-(id)initWithAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext {
+ self = [ super init ];
+ if ( self ) {
+ [ self setDefaults: rxAccessibleContext ];
+ }
+ return self;
+}
+
+-(void) setDefaults: (Reference < XAccessibleContext >) rxAccessibleContext {
+ mActsAsRadioGroup = NO;
+ maReferenceWrapper.rAccessibleContext = rxAccessibleContext;
+ mIsTableCell = NO;
+ // Querying all supported interfaces
+ try {
+ // XAccessibleComponent
+ maReferenceWrapper.rAccessibleComponent.set( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleExtendedComponent
+ maReferenceWrapper.rAccessibleExtendedComponent.set( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleSelection
+ maReferenceWrapper.rAccessibleSelection.set( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleTable
+ maReferenceWrapper.rAccessibleTable.set( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleText
+ maReferenceWrapper.rAccessibleText.set( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleEditableText
+ maReferenceWrapper.rAccessibleEditableText.set( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleValue
+ maReferenceWrapper.rAccessibleValue.set( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleAction
+ maReferenceWrapper.rAccessibleAction.set( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleTextAttributes
+ maReferenceWrapper.rAccessibleTextAttributes.set( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleMultiLineText
+ maReferenceWrapper.rAccessibleMultiLineText.set( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleTextMarkup
+ maReferenceWrapper.rAccessibleTextMarkup.set( rxAccessibleContext, UNO_QUERY );
+ // XAccessibleEventBroadcaster
+ #if 0
+ /* #i102033# NSAccessibility does not seemt to know an equivalent for transient children.
+ That means we need to cache this, else e.g. tree list boxes are not accessible (moreover
+ it crashes by notifying dead objects - which would seemt o be another bug)
+
+ FIXME:
+ Unfortunately this can increase memory consumption drastically until the non transient parent
+ is destroyed and finally all the transients are released.
+ */
+ if ( ! ( rxAccessibleContext -> getAccessibleStateSet() & AccessibleStateType::TRANSIENT ) )
+ #endif
+ {
+ Reference< XAccessibleEventBroadcaster > xBroadcaster(rxAccessibleContext, UNO_QUERY);
+ if( xBroadcaster.is() ) {
+ /*
+ * We intentionally do not hold a reference to the event listener in the wrapper object,
+ * but let the listener control the life cycle of the wrapper instead ..
+ */
+ xBroadcaster->addAccessibleEventListener( new AquaA11yEventListener( self, rxAccessibleContext -> getAccessibleRole() ) );
+ }
+ }
+ // TABLE_CELL
+ if ( rxAccessibleContext -> getAccessibleRole() == AccessibleRole::TABLE_CELL ) {
+ mIsTableCell = YES;
+ }
+ } catch ( const Exception ) {
+ }
+}
+
+#pragma mark -
+#pragma mark Utility Section
+
+// generates selectors for attribute name AXAttributeNameHere
+// (getter without parameter) attributeNameHereAttribute
+// (getter with parameter) attributeNameHereAttributeForParameter:
+// (setter) setAttributeNameHereAttributeForElement:to:
+-(SEL)selectorForAttribute:(NSString *)attribute asGetter:(BOOL)asGetter withGetterParameter:(BOOL)withGetterParameter {
+ SEL selector = static_cast<SEL>(nil);
+ NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
+ @try {
+ // step 1: create method name from attribute name
+ NSMutableString * methodName = [ NSMutableString string ];
+ if ( ! asGetter ) {
+ [ methodName appendString: @"set" ];
+ }
+ NSRange const aRange = { 2, 1 };
+ NSString * firstChar = [ attribute substringWithRange: aRange ]; // drop leading "AX" and get first char
+ if ( asGetter ) {
+ [ methodName appendString: [ firstChar lowercaseString ] ];
+ } else {
+ [ methodName appendString: firstChar ];
+ }
+ [ methodName appendString: [ attribute substringFromIndex: 3 ] ]; // append rest of attribute name
+ // append rest of method name
+ [ methodName appendString: @"Attribute" ];
+ if ( ! asGetter ) {
+ [ methodName appendString: @"ForElement:to:" ];
+ } else if ( asGetter && withGetterParameter ) {
+ [ methodName appendString: @"ForParameter:" ];
+ }
+ // step 2: create selector
+ selector = NSSelectorFromString ( methodName );
+ } @catch ( id ) {
+ selector = static_cast<SEL>(nil);
+ }
+ [ pool release ];
+ return selector;
+}
+
+-(Reference < XAccessible >)getFirstRadioButtonInGroup {
+ Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
+ if( rxAccessibleRelationSet.is() )
+ {
+ 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 > ();
+}
+
+-(BOOL)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 expressed through the hierarchy. An 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 {
+ if ( mActsAsRadioGroup ) {
+ return NSAccessibilityRadioGroupRole;
+ }
+ else {
+ return [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ];
+ }
+}
+
+-(id)subroleAttribute {
+ if ( mActsAsRadioGroup ) {
+ return @"";
+ } else {
+ NSString * subRole = [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ];
+ if ( ! [ subRole isEqualToString: @"" ] ) {
+ return subRole;
+ } else {
+ [ subRole release ];
+ SAL_WNODEPRECATED_DECLARATIONS_PUSH
+ //TODO: 10.10 accessibilityAttributeValue:
+ return [ super accessibilityAttributeValue: NSAccessibilitySubroleAttribute ];
+ SAL_WNODEPRECATED_DECLARATIONS_POP
+ }
+ }
+}
+
+-(id)titleAttribute {
+ return CreateNSString ( [ self accessibleContext ] -> getAccessibleName() );
+}
+
+-(id)descriptionAttribute {
+ if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) {
+ return [ self titleAttribute ];
+ } else if ( [ self accessibleExtendedComponent ] ) {
+ return [ AquaA11yComponentWrapper descriptionAttributeForElement: self ];
+ } else {
+ return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() );
+ }
+}
+
+-(id)enabledAttribute {
+ sal_Int64 nStateSet = [ self accessibleContext ] -> getAccessibleStateSet();
+ if ( nStateSet ) {
+ return [ NSNumber numberWithBool: ( (nStateSet & AccessibleStateType::ENABLED) != 0 ) ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)focusedAttribute {
+ 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() ) {
+ sal_Int64 nStateSet = rxContext -> getAccessibleStateSet();
+ if ( nStateSet ) {
+ isFocused = [ NSNumber numberWithBool: ( ( nStateSet & AccessibleStateType::FOCUSED ) != 0 ) ];
+ }
+ }
+ }
+ return isFocused;
+ } else if ( [ self accessibleContext ] -> getAccessibleStateSet() ) {
+ sal_Int64 nStateSet = [ self accessibleContext ] -> getAccessibleStateSet();
+ return [ NSNumber numberWithBool: ( ( nStateSet & AccessibleStateType::FOCUSED ) != 0 ) ];
+ } else {
+ return nil;
+ }
+}
+
+-(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() ) {
+ Reference< XAccessibleContext > xContext( xParent -> getAccessibleContext() );
+ if ( xContext.is() ) {
+ id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: xContext ];
+ [ parent_wrapper autorelease ];
+ return NSAccessibilityUnignoredAncestor( parent_wrapper );
+ }
+ }
+ } catch (const Exception&) {
+ }
+
+ OSL_ASSERT( false );
+ return nil;
+}
+
+-(id)childrenAttribute {
+ if ( mActsAsRadioGroup ) {
+ NSMutableArray * children = [ [ NSMutableArray alloc ] init ];
+ Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
+ AccessibleRelation const relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF );
+ if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() ) {
+ for ( const auto& i : relationMemberOf.TargetSet ) {
+ Reference < XAccessible > rMateAccessible( i, 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 ] )
+ {
+ AquaA11yTableWrapper* pTable = [self isKindOfClass: [AquaA11yTableWrapper class]] ? static_cast<AquaA11yTableWrapper*>(self) : nil;
+ return [ AquaA11yTableWrapper childrenAttributeForElement: pTable ];
+ } else {
+ try {
+ NSMutableArray * children = [ [ NSMutableArray alloc ] init ];
+ Reference< XAccessibleContext > xContext( [ self accessibleContext ] );
+
+ try {
+ sal_Int64 cnt = xContext -> getAccessibleChildCount();
+ for ( sal_Int64 i = 0; i < cnt; i++ ) {
+ Reference< XAccessible > xChild( xContext -> getAccessibleChild( i ) );
+ if( xChild.is() ) {
+ Reference< XAccessibleContext > xChildContext( xChild -> getAccessibleContext() );
+ // the menubar is already accessible (including Apple- and Application-Menu) through NSApplication => omit it here
+ if ( xChildContext.is() && AccessibleRole::MENU_BAR != xChildContext -> getAccessibleRole() ) {
+ id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: xChildContext ];
+ [ children addObject: wrapper ];
+ [ wrapper release ];
+ }
+ }
+ }
+ }
+ catch (const IndexOutOfBoundsException&)
+ {
+ SAL_WARN("vcl", "Accessible object has invalid index in parent");
+ }
+
+ // 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 = static_cast<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 &) {
+ // TODO: Log
+ return nil;
+ }
+ }
+}
+
+-(id)windowAttribute {
+ // go upstairs until reaching the broken connection
+ AquaA11yWrapper * aWrapper = self;
+ int loops = 0;
+ while ( [ aWrapper accessibleContext ] -> getAccessibleParent().is() ) {
+ AquaA11yWrapper *aTentativeParentWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ aWrapper accessibleContext ] -> getAccessibleParent() -> getAccessibleContext() ];
+ // Quick-and-dirty fix for infinite loop after fixing crash in
+ // fdo#47275
+ if ( aTentativeParentWrapper == aWrapper )
+ break;
+ // Even dirtier fix for infinite loop in fdo#55156
+ if ( loops++ == 100 )
+ break;
+ aWrapper = aTentativeParentWrapper;
+ [ aWrapper autorelease ];
+ }
+ // get associated NSWindow
+ NSWindow* theWindow = [ aWrapper windowForParent ];
+ return theWindow;
+}
+
+-(id)topLevelUIElementAttribute {
+ return [ self windowAttribute ];
+}
+
+-(id)sizeAttribute {
+ if ( [ self accessibleComponent ] ) {
+ return [ AquaA11yComponentWrapper sizeAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)positionAttribute {
+ if ( [ self accessibleComponent ] ) {
+ return [ AquaA11yComponentWrapper positionAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)helpAttribute {
+ return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() );
+}
+
+-(id)roleDescriptionAttribute {
+ 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 roleAttribute ] isEqualToString: NSAccessibilityMenuItemRole ] ) {
+ return nil;
+ } else if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper valueAttributeForElement: self ];
+ } else if ( [ self accessibleValue ] ) {
+ return [ AquaA11yValueWrapper valueAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)minValueAttribute {
+ if ( [ self accessibleValue ] ) {
+ return [ AquaA11yValueWrapper minValueAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)maxValueAttribute {
+ if ( [ self accessibleValue ] ) {
+ return [ AquaA11yValueWrapper maxValueAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)contentsAttribute {
+ return [ self childrenAttribute ];
+}
+
+-(id)selectedChildrenAttribute {
+ return [ AquaA11ySelectionWrapper selectedChildrenAttributeForElement: self ];
+}
+
+-(id)numberOfCharactersAttribute {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper numberOfCharactersAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)selectedTextAttribute {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper selectedTextAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)selectedTextRangeAttribute {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper selectedTextRangeAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)visibleCharacterRangeAttribute {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper visibleCharacterRangeAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)tabsAttribute {
+ return self; // TODO ???
+}
+
+-(id)sharedTextUIElementsAttribute {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper sharedTextUIElementsAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)sharedCharacterRangeAttribute {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper sharedCharacterRangeAttributeForElement: self ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)expandedAttribute {
+ sal_Int64 nStateSet = [ self accessibleContext ] -> getAccessibleStateSet();
+ return [ NSNumber numberWithBool: ( ( nStateSet & AccessibleStateType::EXPANDED ) != 0 ) ];
+}
+
+-(id)selectedAttribute {
+ sal_Int64 nStateSet = [ self accessibleContext ] -> getAccessibleStateSet();
+ return [ NSNumber numberWithBool: ( ( nStateSet & AccessibleStateType::SELECTED ) != 0 ) ];
+}
+
+-(id)stringForRangeAttributeForParameter:(id)range {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper stringForRangeAttributeForElement: self forParameter: range ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)attributedStringForRangeAttributeForParameter:(id)range {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper attributedStringForRangeAttributeForElement: self forParameter: range ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)rangeForIndexAttributeForParameter:(id)index {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper rangeForIndexAttributeForElement: self forParameter: index ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)rangeForPositionAttributeForParameter:(id)point {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper rangeForPositionAttributeForElement: self forParameter: point ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)boundsForRangeAttributeForParameter:(id)range {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper boundsForRangeAttributeForElement: self forParameter: range ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)styleRangeForIndexAttributeForParameter:(id)index {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper styleRangeForIndexAttributeForElement: self forParameter: index ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)rTFForRangeAttributeForParameter:(id)range {
+ if ( [ self accessibleText ] ) {
+ return [ AquaA11yTextWrapper rTFForRangeAttributeForElement: self forParameter: range ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)orientationAttribute {
+ NSString * orientation = nil;
+ sal_Int64 stateSet = [ self accessibleContext ] -> getAccessibleStateSet();
+ if ( stateSet & AccessibleStateType::HORIZONTAL ) {
+ orientation = NSAccessibilityHorizontalOrientationValue;
+ } else if ( stateSet & AccessibleStateType::VERTICAL ) {
+ orientation = NSAccessibilityVerticalOrientationValue;
+ }
+ return orientation;
+}
+
+-(id)titleUIElementAttribute {
+ if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) {
+ NSString * title = [ self titleAttribute ];
+ id titleElement = nil;
+ if ( [ title length ] == 0 ) {
+ AccessibleRelation relationLabeledBy = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABELED_BY );
+ if ( relationLabeledBy.RelationType == AccessibleRelationType::LABELED_BY && relationLabeledBy.TargetSet.hasElements() ) {
+ Reference < XAccessible > rxAccessible ( relationLabeledBy.TargetSet[0], UNO_QUERY );
+ titleElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ];
+ }
+ }
+ if ( title ) {
+ [ title release ];
+ }
+ return titleElement;
+ } else {
+ return nil;
+ }
+}
+
+-(id)servesAsTitleForUIElementsAttribute {
+ if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) {
+ id titleForElement = nil;
+ AccessibleRelation relationLabelFor = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABEL_FOR );
+ if ( relationLabelFor.RelationType == AccessibleRelationType::LABEL_FOR && relationLabelFor.TargetSet.hasElements() ) {
+ Reference < XAccessible > rxAccessible ( relationLabelFor.TargetSet[0], UNO_QUERY );
+ titleForElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ];
+ }
+ return titleForElement;
+ } else {
+ return nil;
+ }
+}
+
+-(id)lineForIndexAttributeForParameter:(id)index {
+ if ( [ self accessibleMultiLineText ] ) {
+ return [ AquaA11yTextWrapper lineForIndexAttributeForElement: self forParameter: index ];
+ } else {
+ return nil;
+ }
+}
+
+-(id)rangeForLineAttributeForParameter:(id)line {
+ if ( [ self accessibleMultiLineText ] ) {
+ return [ AquaA11yTextWrapper rangeForLineAttributeForElement: self forParameter: line ];
+ } else {
+ return nil;
+ }
+}
+
+#pragma mark -
+#pragma mark Accessibility Protocol
+
+-(id)accessibilityAttributeValue:(NSString *)attribute {
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeValue:" << attribute << "]");
+ // #i90575# guard NSAccessibility protocol against unwanted access
+ if ( isPopupMenuOpen ) {
+ return nil;
+ }
+
+ id value = nil;
+ // if we are no longer in the wrapper repository, we have been disposed
+ AquaA11yWrapper * theWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ self accessibleContext ] createIfNotExists: NO ];
+ if ( theWrapper || mIsTableCell ) {
+ try {
+ SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: NO ];
+ if ( [ self respondsToSelector: methodSelector ] ) {
+ value = [ self performSelector: methodSelector ];
+ }
+ } catch ( const DisposedException & ) {
+ mIsTableCell = NO; // just to be sure
+ [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ];
+ return nil;
+ } catch ( const Exception & ) {
+ // empty
+ }
+ }
+ if ( theWrapper ) {
+ [ theWrapper release ]; // the above called method calls retain on the returned Wrapper
+ }
+ return value;
+}
+
+-(BOOL)accessibilityIsIgnored {
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityIsIgnored]");
+ // #i90575# guard NSAccessibility protocol against unwanted access
+ if ( isPopupMenuOpen ) {
+ return NO;
+ }
+ bool ignored = false;
+ try {
+ sal_Int16 nRole = [ self accessibleContext ] -> getAccessibleRole();
+ switch ( nRole ) {
+ //case AccessibleRole::PANEL:
+ case AccessibleRole::FRAME:
+ case AccessibleRole::ROOT_PANE:
+ case AccessibleRole::SEPARATOR:
+ case AccessibleRole::FILLER:
+ case AccessibleRole::DIALOG:
+ ignored = true;
+ break;
+ default:
+ ignored = ! ( [ self accessibleContext ] -> getAccessibleStateSet() & AccessibleStateType::VISIBLE );
+ break;
+ }
+ } catch ( DisposedException& ) {
+ ignored = true;
+ } catch ( RuntimeException& ) {
+ ignored = true;
+ }
+
+ return ignored; // TODO: to be completed
+}
+
+-(NSArray *)accessibilityAttributeNames {
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeNames]");
+ // #i90575# guard NSAccessibility protocol against unwanted access
+ if ( isPopupMenuOpen ) {
+ return nil;
+ }
+ NSString * nativeSubrole = nil;
+ NSString * title = nil;
+ NSMutableArray * attributeNames = nil;
+ sal_Int64 nAccessibleChildren = 0;
+ try {
+ // Default Attributes
+ attributeNames = [ NSMutableArray arrayWithObjects:
+ NSAccessibilityRoleAttribute,
+ NSAccessibilityDescriptionAttribute,
+ NSAccessibilityParentAttribute,
+ NSAccessibilityWindowAttribute,
+ NSAccessibilityHelpAttribute,
+ NSAccessibilityTopLevelUIElementAttribute,
+ NSAccessibilityRoleDescriptionAttribute,
+ nil ];
+ nativeSubrole = static_cast<NSString *>([ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ]);
+ title = static_cast<NSString *>([ self titleAttribute ]);
+ Reference < XAccessibleRelationSet > rxRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet();
+ // Special Attributes depending on attribute values
+ if ( nativeSubrole && ! [ nativeSubrole isEqualToString: @"" ] ) {
+ [ attributeNames addObject: NSAccessibilitySubroleAttribute ];
+ }
+ try
+ {
+ nAccessibleChildren = [ self accessibleContext ] -> getAccessibleChildCount();
+ if ( nAccessibleChildren > 0 ) {
+ [ attributeNames addObject: NSAccessibilityChildrenAttribute ];
+ }
+ }
+ catch( DisposedException& ) {}
+ catch( RuntimeException& ) {}
+
+ if ( title && ! [ title isEqualToString: @"" ] ) {
+ [ attributeNames addObject: NSAccessibilityTitleAttribute ];
+ }
+ if ( [ title length ] == 0 && rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABELED_BY ) ) {
+ [ attributeNames addObject: NSAccessibilityTitleUIElementAttribute ];
+ }
+ if ( rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABEL_FOR ) ) {
+ [ attributeNames addObject: NSAccessibilityServesAsTitleForUIElementsAttribute ];
+ }
+ // Special Attributes depending on interface
+ if( [self accessibleContext ] -> getAccessibleRole() == AccessibleRole::TABLE )
+ [AquaA11yTableWrapper addAttributeNamesTo: attributeNames object: self];
+
+ if ( [ self accessibleText ] ) {
+ [ AquaA11yTextWrapper addAttributeNamesTo: attributeNames ];
+ }
+ if ( [ self accessibleComponent ] ) {
+ [ AquaA11yComponentWrapper addAttributeNamesTo: attributeNames ];
+ }
+ if ( [ self accessibleSelection ] ) {
+ [ AquaA11ySelectionWrapper addAttributeNamesTo: attributeNames ];
+ }
+ if ( [ self accessibleValue ] ) {
+ [ AquaA11yValueWrapper addAttributeNamesTo: attributeNames ];
+ }
+ if ( nativeSubrole ) {
+ [ nativeSubrole release ];
+ }
+ if ( title ) {
+ [ title release ];
+ }
+ // Related: tdf#153374 Don't release autoreleased attributeNames
+ return attributeNames;
+ } catch ( DisposedException & ) { // Object is no longer available
+ if ( nativeSubrole ) {
+ [ nativeSubrole release ];
+ }
+ if ( title ) {
+ [ title release ];
+ }
+ // Related: tdf#153374 Don't release autoreleased attributeNames
+ // Also, return an autoreleased empty array instead of a retained array.
+ [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ];
+ return [ NSArray array ];
+ }
+}
+
+-(BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeIsSettable:" << attribute << "]");
+ bool isSettable = false;
+ if ( [ self accessibleText ] ) {
+ isSettable = [ AquaA11yTextWrapper isAttributeSettable: attribute forElement: self ];
+ }
+ if ( ! isSettable && [ self accessibleComponent ] ) {
+ isSettable = [ AquaA11yComponentWrapper isAttributeSettable: attribute forElement: self ];
+ }
+ if ( ! isSettable && [ self accessibleSelection ] ) {
+ isSettable = [ AquaA11ySelectionWrapper isAttributeSettable: attribute forElement: self ];
+ }
+ if ( ! isSettable && [ self accessibleValue ] ) {
+ isSettable = [ AquaA11yValueWrapper isAttributeSettable: attribute forElement: self ];
+ }
+ return isSettable; // TODO: to be completed
+}
+
+-(NSArray *)accessibilityParameterizedAttributeNames {
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityParameterizedAttributeNames]");
+ NSMutableArray * attributeNames = [ NSMutableArray array ];
+ // Special Attributes depending on interface
+ if ( [ self accessibleText ] ) {
+ [ AquaA11yTextWrapper addParameterizedAttributeNamesTo: attributeNames ];
+ }
+ return attributeNames; // TODO: to be completed
+}
+
+-(id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter {
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityAttributeValue:" << attribute << " forParameter:" << (static_cast<NSObject*>(parameter)) << "]");
+ SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: YES ];
+ if ( [ self respondsToSelector: methodSelector ] ) {
+ try {
+ return [ self performSelector: methodSelector withObject: parameter ];
+ } catch ( const DisposedException & ) {
+ mIsTableCell = NO; // just to be sure
+ [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ];
+ return nil;
+ } catch ( const Exception & ) {
+ // empty
+ }
+ }
+ return nil; // TODO: to be completed
+}
+
+-(BOOL)accessibilitySetOverrideValue:(id)value forAttribute:(NSString *)attribute
+{
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilitySetOverrideValue:" << (static_cast<NSObject*>(value)) << " forAttribute:" << attribute << "]");
+ return NO; // TODO
+}
+
+-(void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilitySetValue:" << (static_cast<NSObject*>(value)) << " forAttribute:" << attribute << "]");
+ SEL methodSelector = [ self selectorForAttribute: attribute asGetter: NO withGetterParameter: NO ];
+ if ( [ AquaA11yComponentWrapper respondsToSelector: methodSelector ] ) {
+ [ AquaA11yComponentWrapper performSelector: methodSelector withObject: self withObject: value ];
+ }
+ if ( [ AquaA11yTextWrapper respondsToSelector: methodSelector ] ) {
+ [ AquaA11yTextWrapper performSelector: methodSelector withObject: self withObject: value ];
+ }
+ if ( [ AquaA11ySelectionWrapper respondsToSelector: methodSelector ] ) {
+ [ AquaA11ySelectionWrapper performSelector: methodSelector withObject: self withObject: value ];
+ }
+ if ( [ AquaA11yValueWrapper respondsToSelector: methodSelector ] ) {
+ [ AquaA11yValueWrapper performSelector: methodSelector withObject: self withObject: value ];
+ }
+}
+
+-(id)accessibilityFocusedUIElement {
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("vcl.a11y", "[" << self << " 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 ..
+ [ self accessibleContext ];
+
+ AquaA11yWrapper * focusedUIElement = AquaA11yFocusListener::get()->getFocusedUIElement();
+// AquaA11yWrapper * ancestor = focusedUIElement;
+
+ // Make sure the focused object is a descendant of self
+// do {
+// if( self == ancestor )
+ return focusedUIElement;
+
+// ancestor = [ ancestor accessibilityAttributeValue: NSAccessibilityParentAttribute ];
+// } while( nil != ancestor );
+
+ return self;
+}
+
+-(NSString *)accessibilityActionDescription:(NSString *)action {
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityActionDescription:" << action << "]");
+ return NSAccessibilityActionDescription(action);
+}
+
+-(AquaA11yWrapper *)actionResponder {
+ AquaA11yWrapper * wrapper = nil;
+ // get some information
+ NSString * role = static_cast<NSString *>([ self accessibilityAttributeValue: NSAccessibilityRoleAttribute ]);
+ id enabledAttr = [ self enabledAttribute ];
+ bool enabled = [ enabledAttr boolValue ];
+ NSView * parent = static_cast<NSView *>([ self accessibilityAttributeValue: NSAccessibilityParentAttribute ]);
+ AquaA11yWrapper * parentAsWrapper = nil;
+ if ( [ parent isKindOfClass: [ AquaA11yWrapper class ] ] ) {
+ parentAsWrapper = static_cast<AquaA11yWrapper *>(parent);
+ }
+ SAL_WNODEPRECATED_DECLARATIONS_PUSH
+ //TODO: 10.10 accessibilityAttributeValue:
+ NSString * parentRole = static_cast<NSString *>([ parent accessibilityAttributeValue: NSAccessibilityRoleAttribute ]);
+ SAL_WNODEPRECATED_DECLARATIONS_POP
+ // if we are a textarea inside a combobox, then the combobox is the action responder
+ if ( enabled
+ && [ role isEqualToString: NSAccessibilityTextAreaRole ]
+ && [ parentRole isEqualToString: NSAccessibilityComboBoxRole ]
+ && parentAsWrapper ) {
+ wrapper = parentAsWrapper;
+ } else if ( enabled && [ self accessibleAction ] ) {
+ wrapper = self ;
+ }
+ [ parentRole release ];
+ [ enabledAttr release ];
+ [ role release ];
+ return wrapper;
+}
+
+-(void)accessibilityPerformAction:(NSString *)action {
+ [ self performAction: action ];
+}
+
+-(BOOL)performAction:(NSString *)action {
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityPerformAction:" << action << "]");
+ AquaA11yWrapper * actionResponder = [ self actionResponder ];
+ if ( actionResponder ) {
+ [ AquaA11yActionWrapper doAction: action ofElement: actionResponder ];
+ return YES;
+ }
+ return NO;
+}
+
+-(NSArray *)accessibilityActionNames {
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityActionNames]");
+ NSArray * actionNames = nil;
+ AquaA11yWrapper * actionResponder = [ self actionResponder ];
+ if ( actionResponder ) {
+ actionNames = [ AquaA11yActionWrapper actionNamesForElement: actionResponder ];
+ } else {
+ actionNames = [ [ NSArray alloc ] init ];
+ }
+ return actionNames;
+}
+
+#pragma mark -
+#pragma mark Hit Test
+
+-(BOOL)isViewElement:(NSObject *)viewElement hitByPoint:(NSPoint)point {
+ bool hit = false;
+ NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
+ SAL_WNODEPRECATED_DECLARATIONS_PUSH
+ //TODO: 10.10 accessibilityAttributeValue:
+ NSValue * position = [ viewElement accessibilityAttributeValue: NSAccessibilityPositionAttribute ];
+ NSValue * size = [ viewElement accessibilityAttributeValue: NSAccessibilitySizeAttribute ];
+ SAL_WNODEPRECATED_DECLARATIONS_POP
+ if ( position && size ) {
+ float minX = [ position pointValue ].x;
+ float minY = [ position pointValue ].y;
+ float maxX = minX + [ size sizeValue ].width;
+ float maxY = minY + [ size sizeValue ].height;
+ if ( minX < point.x && maxX > point.x && minY < point.y && maxY > point.y ) {
+ hit = true;
+ }
+ }
+ [ pool release ];
+ return hit;
+}
+
+static Reference < XAccessibleContext > hitTestRunner ( css::awt::Point point,
+ Reference < XAccessibleContext > const & rxAccessibleContext ) {
+ Reference < XAccessibleContext > hitChild;
+ Reference < XAccessibleContext > emptyReference;
+ try {
+ Reference < XAccessibleComponent > rxAccessibleComponent ( rxAccessibleContext, UNO_QUERY );
+ if ( rxAccessibleComponent.is() ) {
+ css::awt::Point location = rxAccessibleComponent -> getLocationOnScreen();
+ css::awt::Point hitPoint ( point.X - location.X , point.Y - location.Y);
+ Reference < XAccessible > rxAccessible = rxAccessibleComponent -> getAccessibleAtPoint ( hitPoint );
+ if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() &&
+ rxAccessible -> getAccessibleContext() -> getAccessibleChildCount() == 0 ) {
+ hitChild = rxAccessible -> getAccessibleContext();
+ }
+ }
+
+ // iterate the hierarchy looking doing recursive hit testing.
+ // apparently necessary as a special treatment for e.g. comboboxes
+ if ( !hitChild.is() ) {
+ bool bSafeToIterate = true;
+ sal_Int64 nCount = rxAccessibleContext -> getAccessibleChildCount();
+
+ if (nCount < 0 || nCount > SAL_MAX_UINT16 /* slow enough for anyone */)
+ bSafeToIterate = false;
+ else { // manages descendants is an horror from the a11y standards guys.
+ sal_Int64 nStateSet = rxAccessibleContext -> getAccessibleStateSet();
+ if ( nStateSet & AccessibleStateType::MANAGES_DESCENDANTS )
+ bSafeToIterate = false;
+ }
+
+ if( bSafeToIterate ) {
+ try {
+ for ( sal_Int64 i = 0; i < rxAccessibleContext -> getAccessibleChildCount(); i++ ) {
+ Reference < XAccessible > rxAccessibleChild = rxAccessibleContext -> getAccessibleChild ( i );
+ if ( rxAccessibleChild.is() && rxAccessibleChild -> getAccessibleContext().is() && rxAccessibleChild -> getAccessibleContext() -> getAccessibleRole() != AccessibleRole::LIST ) {
+ Reference < XAccessibleContext > myHitChild = hitTestRunner ( point, rxAccessibleChild -> getAccessibleContext() );
+ if ( myHitChild.is() ) {
+ hitChild = myHitChild;
+ break;
+ }
+ }
+ }
+ }
+ catch (const IndexOutOfBoundsException&)
+ {
+ SAL_WARN("vcl", "Accessible object has invalid index in parent");
+ }
+ }
+ }
+ } catch ( RuntimeException ) {
+ return emptyReference;
+ }
+ return hitChild;
+}
+
+-(id)accessibilityHitTest:(NSPoint)point {
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("vcl.a11y", "[" << self << " accessibilityHitTest:" << point << "]");
+ static id wrapper = nil;
+ if ( nil != wrapper ) {
+ [ wrapper release ];
+ wrapper = nil;
+ }
+ Reference < XAccessibleContext > hitChild;
+ NSRect screenRect = [ [ NSScreen mainScreen ] frame ];
+ css::awt::Point hitPoint ( static_cast<sal_Int32>(point.x) , static_cast<sal_Int32>(screenRect.size.height - point.y) );
+ // check child windows first
+ NSWindow * window = static_cast<NSWindow *>([ self accessibilityAttributeValue: NSAccessibilityWindowAttribute ]);
+ NSArray * childWindows = [ window childWindows ];
+ if ( [ childWindows count ] > 0 ) {
+ NSWindow * element = nil;
+ NSEnumerator * enumerator = [ childWindows objectEnumerator ];
+ while ( ( element = [ enumerator nextObject ] ) && !hitChild.is() ) {
+ if ( [ element isKindOfClass: [ SalFrameWindow class ] ] && [ self isViewElement: element hitByPoint: point ] ) {
+ // we have a child window that is hit
+ Reference < XAccessibleRelationSet > relationSet = [ static_cast<SalFrameWindow *>(element) accessibleContext ] -> getAccessibleRelationSet();
+ if ( relationSet.is() && relationSet -> containsRelation ( AccessibleRelationType::SUB_WINDOW_OF )) {
+ // we have a valid relation to the parent element
+ AccessibleRelation const relation = relationSet -> getRelationByType ( AccessibleRelationType::SUB_WINDOW_OF );
+ for ( const auto & i : relation.TargetSet ) {
+ Reference < XAccessible > rxAccessible ( i, UNO_QUERY );
+ if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) {
+ // hit test for children of parent
+ hitChild = hitTestRunner ( hitPoint, rxAccessible -> getAccessibleContext() );
+ if (hitChild.is())
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ // nothing hit yet, so check ourself
+ if ( ! hitChild.is() ) {
+ if ( !maReferenceWrapper.rAccessibleContext ) {
+ [ self setDefaults: [ self accessibleContext ] ];
+ }
+ hitChild = hitTestRunner ( hitPoint, maReferenceWrapper.rAccessibleContext );
+ }
+ if ( hitChild.is() ) {
+ wrapper = [ AquaA11yFactory wrapperForAccessibleContext: hitChild ];
+ }
+ if ( wrapper ) {
+ [ wrapper retain ]; // TODO: retain only when transient ?
+ }
+ return wrapper;
+}
+
+#pragma mark -
+#pragma mark Access Methods
+
+-(XAccessibleAction *)accessibleAction {
+ return maReferenceWrapper.rAccessibleAction.get();
+}
+
+-(XAccessibleContext *)accessibleContext {
+ return maReferenceWrapper.rAccessibleContext.get();
+}
+
+-(XAccessibleComponent *)accessibleComponent {
+ return maReferenceWrapper.rAccessibleComponent.get();
+}
+
+-(XAccessibleExtendedComponent *)accessibleExtendedComponent {
+ return maReferenceWrapper.rAccessibleExtendedComponent.get();
+}
+
+-(XAccessibleSelection *)accessibleSelection {
+ return maReferenceWrapper.rAccessibleSelection.get();
+}
+
+-(XAccessibleTable *)accessibleTable {
+ return maReferenceWrapper.rAccessibleTable.get();
+}
+
+-(XAccessibleText *)accessibleText {
+ return maReferenceWrapper.rAccessibleText.get();
+}
+
+-(XAccessibleEditableText *)accessibleEditableText {
+ return maReferenceWrapper.rAccessibleEditableText.get();
+}
+
+-(XAccessibleValue *)accessibleValue {
+ return maReferenceWrapper.rAccessibleValue.get();
+}
+
+-(XAccessibleTextAttributes *)accessibleTextAttributes {
+ return maReferenceWrapper.rAccessibleTextAttributes.get();
+}
+
+-(XAccessibleMultiLineText *)accessibleMultiLineText {
+ return maReferenceWrapper.rAccessibleMultiLineText.get();
+}
+
+-(XAccessibleTextMarkup *)accessibleTextMarkup {
+ return maReferenceWrapper.rAccessibleTextMarkup.get();
+}
+
+-(NSWindow*)windowForParent {
+ return nil;
+}
+
+-(void)setActsAsRadioGroup:(BOOL)actsAsRadioGroup {
+ mActsAsRadioGroup = actsAsRadioGroup;
+}
+
+-(BOOL)actsAsRadioGroup {
+ return mActsAsRadioGroup;
+}
+
++(void)setPopupMenuOpen:(BOOL)popupMenuOpen {
+ isPopupMenuOpen = popupMenuOpen;
+}
+
+// NSAccessibility selectors
+
+- (BOOL)isAccessibilityElement
+{
+ return ! [ self accessibilityIsIgnored ];
+}
+
+- (BOOL)isAccessibilityFocused
+{
+ NSNumber *pNumber = [ self accessibilityAttributeValue: NSAccessibilityFocusedAttribute ];
+ if ( pNumber )
+ return [ pNumber boolValue ];
+ else
+ return NO;
+}
+
+- (id)accessibilityTopLevelUIElement
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityTopLevelUIElementAttribute ];
+}
+
+- (id)accessibilityValue
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityValueAttribute ];
+}
+
+- (NSArray *)accessibilityVisibleChildren
+{
+ return [ self accessibilityChildren ];
+}
+
+- (NSAccessibilitySubrole)accessibilitySubrole
+{
+ return [ self accessibilityAttributeValue: NSAccessibilitySubroleAttribute ];
+}
+
+- (NSString *)accessibilityTitle
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityTitleAttribute ];
+}
+
+- (id)accessibilityTitleUIElement
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityTitleUIElementAttribute ];
+}
+
+- (NSAccessibilityOrientation)accessibilityOrientation
+{
+ NSNumber *pNumber = [ self accessibilityAttributeValue: NSAccessibilityOrientationAttribute ];
+ if ( pNumber )
+ return NSAccessibilityOrientation([ pNumber integerValue ]);
+ else
+ return NSAccessibilityOrientationUnknown;
+}
+
+- (id)accessibilityParent
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityParentAttribute ];
+}
+
+- (NSAccessibilityRole)accessibilityRole
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityRoleAttribute ];
+}
+
+- (NSString *)accessibilityRoleDescription
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityRoleDescriptionAttribute ];
+}
+
+- (BOOL)isAccessibilitySelected
+{
+ NSNumber *pNumber = [ self accessibilityAttributeValue: NSAccessibilitySelectedAttribute ];
+ if ( pNumber )
+ return [ pNumber boolValue ];
+ else
+ return NO;
+}
+
+- (NSArray *)accessibilitySelectedChildren
+{
+ return [ self accessibilityAttributeValue: NSAccessibilitySelectedChildrenAttribute ];
+}
+
+- (NSArray *)accessibilityServesAsTitleForUIElements
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityServesAsTitleForUIElementsAttribute ];
+}
+
+- (id)accessibilityMinValue
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityMinValueAttribute ];
+}
+
+- (id)accessibilityMaxValue
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityMaxValueAttribute ];
+}
+
+- (id)accessibilityWindow
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityWindowAttribute ];
+}
+
+- (NSString *)accessibilityHelp
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityHelpAttribute ];
+}
+
+- (BOOL)isAccessibilityExpanded
+{
+ NSNumber *pNumber = [ self accessibilityAttributeValue: NSAccessibilityExpandedAttribute ];
+ if ( pNumber )
+ return [ pNumber boolValue ];
+ else
+ return NO;
+}
+
+- (BOOL)isAccessibilityEnabled
+{
+ NSNumber *pNumber = [ self accessibilityAttributeValue: NSAccessibilityEnabledAttribute ];
+ if ( pNumber )
+ return [ pNumber boolValue ];
+ else
+ return NO;
+}
+
+- (NSArray *)accessibilityChildren
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityChildrenAttribute ];
+}
+
+- (NSArray <id<NSAccessibilityElement>> *)accessibilityChildrenInNavigationOrder
+{
+ return [ self accessibilityChildren ];
+}
+
+- (NSArray *)accessibilityContents
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityContentsAttribute ];
+}
+
+- (NSString *)accessibilityLabel
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityDescriptionAttribute ];
+}
+
+- (id)accessibilityApplicationFocusedUIElement
+{
+ return [ self accessibilityFocusedUIElement ];
+}
+
+- (BOOL)isAccessibilityDisclosed
+{
+ NSNumber *pNumber = [ self accessibilityAttributeValue: NSAccessibilityDisclosingAttribute ];
+ if ( pNumber )
+ return [ pNumber boolValue ];
+ else
+ return NO;
+}
+
+- (id)accessibilityHorizontalScrollBar
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityHorizontalScrollBarAttribute ];
+}
+
+- (id)accessibilityVerticalScrollBar
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityVerticalScrollBarAttribute ];
+}
+
+- (NSArray *)accessibilityTabs
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityTabsAttribute ];
+}
+
+- (NSArray *)accessibilityColumns
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityColumnsAttribute ];
+}
+
+- (NSArray *)accessibilityRows
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityRowsAttribute ];
+}
+
+- (NSRange)accessibilitySharedCharacterRange
+{
+ NSValue *pValue = [ self accessibilityAttributeValue: NSAccessibilitySharedCharacterRangeAttribute ];
+ if ( pValue )
+ return [ pValue rangeValue ];
+ else
+ return NSMakeRange( NSNotFound, 0 );
+}
+
+- (NSArray *)accessibilitySharedTextUIElements
+{
+ return [ self accessibilityAttributeValue: NSAccessibilitySharedTextUIElementsAttribute ];
+}
+
+- (NSRange)accessibilityVisibleCharacterRange
+{
+ NSValue *pValue = [ self accessibilityAttributeValue: NSAccessibilityVisibleCharacterRangeAttribute ];
+ if ( pValue )
+ return [ pValue rangeValue ];
+ else
+ return NSMakeRange( NSNotFound, 0 );
+}
+
+- (NSInteger)accessibilityNumberOfCharacters
+{
+ NSNumber *pNumber = [ self accessibilityAttributeValue: NSAccessibilityNumberOfCharactersAttribute ];
+ if ( pNumber )
+ return [ pNumber integerValue ];
+ else
+ return 0;
+}
+
+- (NSString *)accessibilitySelectedText
+{
+ return [ self accessibilityAttributeValue: NSAccessibilitySelectedTextAttribute ];
+}
+
+- (NSRange)accessibilitySelectedTextRange
+{
+ NSValue *pValue = [ self accessibilityAttributeValue: NSAccessibilitySelectedTextRangeAttribute ];
+ if ( pValue )
+ return [ pValue rangeValue ];
+ else
+ return NSMakeRange( NSNotFound, 0 );
+}
+
+- (NSAttributedString *)accessibilityAttributedStringForRange:(NSRange)aRange
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityAttributedStringForRangeParameterizedAttribute forParameter: [ NSValue valueWithRange: aRange ] ];
+}
+
+- (NSRange)accessibilityRangeForLine:(NSInteger)nLine
+{
+ NSValue *pValue = [ self accessibilityAttributeValue: NSAccessibilityRangeForLineParameterizedAttribute forParameter: [NSNumber numberWithInteger: nLine ] ];
+ if ( pValue )
+ return [ pValue rangeValue ];
+ else
+ return NSMakeRange( NSNotFound, 0 );
+}
+
+- (NSString *)accessibilityStringForRange:(NSRange)aRange
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityStringForRangeParameterizedAttribute forParameter: [ NSValue valueWithRange: aRange ] ];
+}
+
+- (NSRange)accessibilityRangeForPosition:(NSPoint)aPoint
+{
+ NSValue *pValue = [ self accessibilityAttributeValue: NSAccessibilityRangeForPositionParameterizedAttribute forParameter: [ NSValue valueWithPoint: aPoint ] ];
+ if ( pValue )
+ return [ pValue rangeValue ];
+ else
+ return NSMakeRange( NSNotFound, 0 );
+}
+
+- (NSRange)accessibilityRangeForIndex:(NSInteger)nIndex
+{
+ NSValue *pValue = [ self accessibilityAttributeValue: NSAccessibilityRangeForIndexParameterizedAttribute forParameter: [ NSNumber numberWithInteger: nIndex ] ];
+ if ( pValue )
+ return [ pValue rangeValue ];
+ else
+ return NSMakeRange( NSNotFound, 0 );
+}
+
+- (NSRect)accessibilityFrameForRange:(NSRange)aRange
+{
+ NSValue *pValue = [ self accessibilityAttributeValue: NSAccessibilityBoundsForRangeParameterizedAttribute forParameter: [ NSValue valueWithRange: aRange ] ];
+ if ( pValue )
+ return [ pValue rectValue ];
+ else
+ return NSZeroRect;
+}
+
+- (NSData *)accessibilityRTFForRange:(NSRange)aRange
+{
+ return [ self accessibilityAttributeValue: NSAccessibilityRTFForRangeParameterizedAttribute forParameter: [ NSValue valueWithRange: aRange ] ];
+}
+
+- (NSRange)accessibilityStyleRangeForIndex:(NSInteger)nIndex
+{
+ NSValue *pValue = [ self accessibilityAttributeValue: NSAccessibilityStyleRangeForIndexParameterizedAttribute forParameter: [ NSNumber numberWithInteger: nIndex ] ];
+ if ( pValue )
+ return [ pValue rangeValue ];
+ else
+ return NSMakeRange( NSNotFound, 0 );
+}
+
+- (NSInteger)accessibilityLineForIndex:(NSInteger)nIndex
+{
+ NSNumber *pNumber = [ self accessibilityAttributeValue: NSAccessibilityLineForIndexParameterizedAttribute forParameter: [ NSNumber numberWithInteger: nIndex ] ];
+ if ( pNumber )
+ return [ pNumber integerValue ];
+ else
+ return 0;
+}
+
+- (BOOL)accessibilityPerformDecrement
+{
+ return [ self performAction: NSAccessibilityDecrementAction ];
+}
+
+- (BOOL)accessibilityPerformPick
+{
+ return [ self performAction: NSAccessibilityPickAction ];
+}
+
+- (BOOL)accessibilityPerformShowMenu
+{
+ return [ self performAction: NSAccessibilityShowMenuAction ];
+}
+
+- (BOOL)accessibilityPerformPress
+{
+ return [ self performAction: NSAccessibilityPressAction ];
+}
+
+- (BOOL)accessibilityPerformIncrement
+{
+ return [ self performAction: NSAccessibilityIncrementAction ];
+}
+
+// NSAccessibilityElement selectors
+
+- (NSRect)accessibilityFrame
+{
+ // Related: tdf#148453 Acquire solar mutex during native accessibility calls
+ SolarMutexGuard aGuard;
+
+ try {
+ XAccessibleComponent *pAccessibleComponent = [ self accessibleComponent ];
+ if ( pAccessibleComponent ) {
+ com::sun::star::awt::Point location = pAccessibleComponent->getLocationOnScreen();
+ com::sun::star::awt::Size size = pAccessibleComponent->getSize();
+ NSRect screenRect = sal::aqua::getTotalScreenBounds();
+ NSRect frame = NSMakeRect( float(location.X), float( screenRect.size.height - size.Height - location.Y ), float(size.Width), float(size.Height) );
+ return frame;
+ }
+ } catch ( DisposedException& ) {
+ } catch ( RuntimeException& ) {
+ }
+
+ return NSZeroRect;
+}
+
+- (BOOL)accessibilityNotifiesWhenDestroyed
+{
+ return YES;
+}
+
+- (BOOL)isAccessibilitySelectorAllowed:(SEL)aSelector
+{
+ if ( ! aSelector )
+ return NO;
+
+ // don't explicitly report (non-)expanded state when not expandable
+ if (aSelector == @selector(isAccessibilityExpanded))
+ {
+ const sal_Int64 nStateSet = [ self accessibleContext ] -> getAccessibleStateSet();
+ if (!( nStateSet & AccessibleStateType::EXPANDABLE))
+ return false;
+ }
+
+ if ( [ self respondsToSelector: aSelector ] ) {
+ // Ignore actions if action is not supported
+ NSAccessibilityActionName pActionName = [ AquaA11yActionWrapper actionNameForSelector: aSelector ];
+ if ( pActionName ) {
+ NSArray *pActionNames = [ self accessibilityActionNames ];
+ if ( ! pActionNames || ! [ pActionNames containsObject: pActionName ] )
+ return NO;
+ } else {
+ // Ignore "setAccessibility" selectors if attribute is not settable
+ static NSString *pSetPrefix = @"setAccessibility";
+ NSString *pSelName = NSStringFromSelector( aSelector );
+ if ( pSelName && [ pSelName hasPrefix: pSetPrefix ] && [ pSelName hasSuffix: @":" ] ) {
+ NSAccessibilityAttributeName pAttrName = [ pSelName substringToIndex: [ pSelName length ] - 1 ];
+ if ( pAttrName && [ pAttrName length ] > [ pSetPrefix length ] ) {
+ pAttrName = [ pAttrName substringFromIndex: [ pSetPrefix length ] ];
+ if ( pAttrName && [ pAttrName length ] ) {
+ pAttrName = [ @"AX" stringByAppendingString: pAttrName ];
+ if ( pAttrName && [ pAttrName length ] && ! [ self accessibilityIsAttributeSettable: pAttrName ] )
+ return NO;
+ }
+ }
+ }
+ }
+ }
+
+ return [ super isAccessibilitySelectorAllowed: aSelector ];
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */