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