1/**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements.  See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership.  The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License.  You may obtain a copy of the License at
10 *
11 *   http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied.  See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24// MARKER(update_precomp.py): autogen include statement, do not remove
25#include "precompiled_vcl.hxx"
26
27#include "aqua/salinst.h"
28#include "aqua/aqua11yfactory.h"
29#include "aqua/aqua11yfocustracker.hxx"
30
31#include "aqua11yfocuslistener.hxx"
32#include "aqua11yrolehelper.h"
33#include "aqua11ywrapperbutton.h"
34#include "aqua11ywrapperstatictext.h"
35#include "aqua11ywrappertextarea.h"
36#include "aqua11ywrappercheckbox.h"
37#include "aqua11ywrappercombobox.h"
38#include "aqua11ywrappergroup.h"
39#include "aqua11ywrapperlist.h"
40#include "aqua11ywrapperradiobutton.h"
41#include "aqua11ywrapperradiogroup.h"
42#include "aqua11ywrapperrow.h"
43#include "aqua11ywrapperscrollarea.h"
44#include "aqua11ywrapperscrollbar.h"
45#include "aqua11ywrappersplitter.h"
46#include "aqua11ywrappertabgroup.h"
47#include "aqua11ywrappertoolbar.h"
48#include "aqua11ytablewrapper.h"
49
50#include <com/sun/star/accessibility/AccessibleStateType.hpp>
51
52using namespace ::com::sun::star::accessibility;
53using namespace ::com::sun::star::uno;
54
55static bool enabled = false;
56
57@implementation AquaA11yFactory : NSObject
58
59#pragma mark -
60#pragma mark Wrapper Repository
61
62+(NSMutableDictionary *)allWrapper {
63    static NSMutableDictionary * mdAllWrapper = nil;
64    if ( mdAllWrapper == nil ) {
65        mdAllWrapper = [ [ [ NSMutableDictionary alloc ] init ] retain ];
66        // initialize keyboard focus tracker
67        rtl::Reference< AquaA11yFocusListener > listener( AquaA11yFocusListener::get() );
68        AquaA11yFocusTracker::get().setFocusListener(listener.get());
69        enabled = true;
70    }
71    return mdAllWrapper;
72}
73
74+(NSValue *)keyForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext {
75    return [ NSValue valueWithPointer: rxAccessibleContext.get() ];
76}
77
78+(NSValue *)keyForAccessibleContextAsRadioGroup: (Reference < XAccessibleContext >) rxAccessibleContext {
79    return [ NSValue valueWithPointer: ( rxAccessibleContext.get() + 2 ) ];
80}
81
82+(AquaA11yWrapper *)wrapperForAccessible: (Reference < XAccessible >) rxAccessible {
83    if ( rxAccessible.is() ) {
84        Reference< XAccessibleContext > xAccessibleContext = rxAccessible->getAccessibleContext();
85        if( xAccessibleContext.is() ) {
86            return [ AquaA11yFactory wrapperForAccessibleContext: xAccessibleContext ];
87        }
88    }
89    return nil;
90}
91
92+(AquaA11yWrapper *)wrapperForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext {
93    return [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: YES asRadioGroup: NO ];
94}
95
96+(AquaA11yWrapper *)wrapperForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext createIfNotExists:(BOOL) bCreate {
97    return [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: bCreate asRadioGroup: NO ];
98}
99
100+(AquaA11yWrapper *)wrapperForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext createIfNotExists:(BOOL) bCreate asRadioGroup:(BOOL) asRadioGroup{
101    NSMutableDictionary * dAllWrapper = [ AquaA11yFactory allWrapper ];
102    NSValue * nKey = nil;
103    if ( asRadioGroup ) {
104        nKey = [ AquaA11yFactory keyForAccessibleContextAsRadioGroup: rxAccessibleContext ];
105    } else {
106        nKey = [ AquaA11yFactory keyForAccessibleContext: rxAccessibleContext ];
107    }
108    AquaA11yWrapper * aWrapper = (AquaA11yWrapper *) [ dAllWrapper objectForKey: nKey ];
109    if ( aWrapper != nil ) {
110        [ aWrapper retain ];
111    } else if ( bCreate ) {
112        NSString * nativeRole = [ AquaA11yRoleHelper getNativeRoleFrom: rxAccessibleContext.get() ];
113        // TODO: reflection
114        if ( [ nativeRole isEqualToString: NSAccessibilityButtonRole ] ) {
115            aWrapper = [ [ AquaA11yWrapperButton alloc ] initWithAccessibleContext: rxAccessibleContext ];
116        } else if ( [ nativeRole isEqualToString: NSAccessibilityTextAreaRole ] ) {
117            aWrapper = [ [ AquaA11yWrapperTextArea alloc ] initWithAccessibleContext: rxAccessibleContext ];
118        } else if ( [ nativeRole isEqualToString: NSAccessibilityStaticTextRole ] ) {
119            aWrapper = [ [ AquaA11yWrapperStaticText alloc ] initWithAccessibleContext: rxAccessibleContext ];
120        } else if ( [ nativeRole isEqualToString: NSAccessibilityComboBoxRole ] ) {
121            aWrapper = [ [ AquaA11yWrapperComboBox alloc ] initWithAccessibleContext: rxAccessibleContext ];
122        } else if ( [ nativeRole isEqualToString: NSAccessibilityGroupRole ] ) {
123            aWrapper = [ [ AquaA11yWrapperGroup alloc ] initWithAccessibleContext: rxAccessibleContext ];
124        } else if ( [ nativeRole isEqualToString: NSAccessibilityToolbarRole ] ) {
125            aWrapper = [ [ AquaA11yWrapperToolbar alloc ] initWithAccessibleContext: rxAccessibleContext ];
126        } else if ( [ nativeRole isEqualToString: NSAccessibilityScrollAreaRole ] ) {
127            aWrapper = [ [ AquaA11yWrapperScrollArea alloc ] initWithAccessibleContext: rxAccessibleContext ];
128        } else if ( [ nativeRole isEqualToString: NSAccessibilityTabGroupRole ] ) {
129            aWrapper = [ [ AquaA11yWrapperTabGroup alloc ] initWithAccessibleContext: rxAccessibleContext ];
130        } else if ( [ nativeRole isEqualToString: NSAccessibilityScrollBarRole ] ) {
131            aWrapper = [ [ AquaA11yWrapperScrollBar alloc ] initWithAccessibleContext: rxAccessibleContext ];
132        } else if ( [ nativeRole isEqualToString: NSAccessibilityCheckBoxRole ] ) {
133            aWrapper = [ [ AquaA11yWrapperCheckBox alloc ] initWithAccessibleContext: rxAccessibleContext ];
134        } else if ( [ nativeRole isEqualToString: NSAccessibilityRadioGroupRole ] ) {
135            aWrapper = [ [ AquaA11yWrapperRadioGroup alloc ] initWithAccessibleContext: rxAccessibleContext ];
136        } else if ( [ nativeRole isEqualToString: NSAccessibilityRadioButtonRole ] ) {
137            aWrapper = [ [ AquaA11yWrapperRadioButton alloc ] initWithAccessibleContext: rxAccessibleContext ];
138        } else if ( [ nativeRole isEqualToString: NSAccessibilityRowRole ] ) {
139            aWrapper = [ [ AquaA11yWrapperRow alloc ] initWithAccessibleContext: rxAccessibleContext ];
140        } else if ( [ nativeRole isEqualToString: NSAccessibilityListRole ] ) {
141            aWrapper = [ [ AquaA11yWrapperList alloc ] initWithAccessibleContext: rxAccessibleContext ];
142        } else if ( [ nativeRole isEqualToString: NSAccessibilitySplitterRole ] ) {
143            aWrapper = [ [ AquaA11yWrapperSplitter alloc ] initWithAccessibleContext: rxAccessibleContext ];
144        } else if ( [ nativeRole isEqualToString: NSAccessibilityTableRole ] ) {
145            aWrapper = [ [ AquaA11yTableWrapper alloc ] initWithAccessibleContext: rxAccessibleContext ];
146        } else {
147            aWrapper = [ [ AquaA11yWrapper alloc ] initWithAccessibleContext: rxAccessibleContext ];
148        }
149        [ nativeRole release ];
150        [ aWrapper setActsAsRadioGroup: asRadioGroup ];
151        #if 0
152        /* #i102033# NSAccessibility does not seemt to know an equivalent for transient children.
153           That means we need to cache this, else e.g. tree list boxes are not accessible (moreover
154           it crashes by notifying dead objects - which would seemt o be another bug)
155
156           FIXME:
157           Unfortunately this can increase memory consumption drastically until the non transient parent
158           is destroyed an finally all the transients are released.
159        */
160        if ( ! rxAccessibleContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::TRANSIENT ) )
161        #endif
162        {
163            [ dAllWrapper setObject: aWrapper forKey: nKey ];
164        }
165    }
166    return aWrapper;
167}
168
169+(void)insertIntoWrapperRepository: (NSView *) viewElement forAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext {
170    NSMutableDictionary * dAllWrapper = [ AquaA11yFactory allWrapper ];
171    [ dAllWrapper setObject: viewElement forKey: [ AquaA11yFactory keyForAccessibleContext: rxAccessibleContext ] ];
172}
173
174+(void)removeFromWrapperRepositoryFor: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) rxAccessibleContext {
175    // TODO: when RADIO_BUTTON search for associated RadioGroup-wrapper and delete that as well
176    AquaA11yWrapper * theWrapper = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: NO ];
177    if ( theWrapper != nil ) {
178        [ [ AquaA11yFactory allWrapper ] removeObjectForKey: [ AquaA11yFactory keyForAccessibleContext: rxAccessibleContext ] ];
179        [ theWrapper release ];
180    }
181}
182
183+(void)registerView: (NSView *) theView {
184    if ( enabled && [ theView isKindOfClass: [ AquaA11yWrapper class ] ] ) {
185        // insertIntoWrapperRepository gets called from SalFrameView itself to bootstrap the bridge initially
186        [ (AquaA11yWrapper *) theView accessibleContext ];
187    }
188}
189
190+(void)revokeView: (NSView *) theView {
191    if ( enabled && [ theView isKindOfClass: [ AquaA11yWrapper class ] ] ) {
192        [ AquaA11yFactory removeFromWrapperRepositoryFor: [ (AquaA11yWrapper *) theView accessibleContext ] ];
193    }
194}
195
196@end
197