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
29#include "aqua11ytextwrapper.h"
30#include "aqua11ytextattributeswrapper.h"
31#include "aqua11yutil.h"
32
33#include <com/sun/star/accessibility/AccessibleTextType.hpp>
34#include <com/sun/star/awt/Rectangle.hpp>
35
36using namespace ::com::sun::star::accessibility;
37using namespace ::com::sun::star::awt;
38using namespace ::com::sun::star::lang;
39using namespace ::com::sun::star::uno;
40using namespace ::rtl;
41
42// Wrapper for XAccessibleText, XAccessibleEditableText and XAccessibleMultiLineText
43
44@implementation AquaA11yTextWrapper : NSObject
45
46+(id)valueAttributeForElement:(AquaA11yWrapper *)wrapper {
47    return CreateNSString ( [ wrapper accessibleText ] -> getText() );
48}
49
50+(void)setValueAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value
51{
52    // TODO
53    (void)wrapper;
54    (void)value;
55}
56
57+(id)numberOfCharactersAttributeForElement:(AquaA11yWrapper *)wrapper {
58    return [ NSNumber numberWithLong: [ wrapper accessibleText ] -> getCharacterCount() ];
59}
60
61+(id)selectedTextAttributeForElement:(AquaA11yWrapper *)wrapper {
62    return CreateNSString ( [ wrapper accessibleText ] -> getSelectedText() );
63}
64
65+(void)setSelectedTextAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value {
66    if ( [ wrapper accessibleEditableText ] != nil ) {
67        NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
68        OUString newText = GetOUString ( (NSString *) value );
69        NSRange selectedTextRange = [ [ AquaA11yTextWrapper selectedTextRangeAttributeForElement: wrapper ] rangeValue ];
70        try {
71            [ wrapper accessibleEditableText ] -> replaceText ( selectedTextRange.location, selectedTextRange.location + selectedTextRange.length, newText );
72        } catch ( const Exception & e ) {
73            // empty
74        }
75        [ pool release ];
76    }
77}
78
79+(id)selectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper {
80    sal_Int32 start = [ wrapper accessibleText ] -> getSelectionStart();
81    sal_Int32 end = [ wrapper accessibleText ] -> getSelectionEnd();
82    if ( start != end ) {
83        return [ NSValue valueWithRange: NSMakeRange ( start, end - start ) ]; // true selection
84    } else {
85        long caretPos = [ wrapper accessibleText ] -> getCaretPosition();
86        if ( caretPos < 0 || caretPos > [ wrapper accessibleText ] -> getCharacterCount() ) {
87            return nil;
88        }
89        return [ NSValue valueWithRange: NSMakeRange ( caretPos, 0 ) ]; // insertion point
90    }
91}
92
93+(void)setSelectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value {
94    NSRange range = [ value rangeValue ];
95    try {
96        [ wrapper accessibleText ] -> setSelection ( range.location, range.location + range.length );
97    } catch ( const Exception & e ) {
98        // empty
99    }
100}
101
102+(id)visibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper {
103    // the OOo a11y API returns only the visible portion...
104    return [ NSValue valueWithRange: NSMakeRange ( 0, [ wrapper accessibleText ] -> getCharacterCount() ) ];
105}
106
107+(void)setVisibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value
108{
109    // do nothing
110    (void)wrapper;
111    (void)value;
112}
113
114+(id)sharedTextUIElementsAttributeForElement:(AquaA11yWrapper *)wrapper
115{
116    (void)wrapper;
117    return [ [ NSArray alloc ] init ]; // unsupported
118}
119
120+(id)sharedCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper
121{
122    (void)wrapper;
123    return [ NSValue valueWithRange: NSMakeRange ( 0, 0 ) ]; // unsupported
124}
125
126+(void)addAttributeNamesTo:(NSMutableArray *)attributeNames {
127    [ attributeNames addObjectsFromArray: [ AquaA11yTextWrapper specialAttributeNames ] ];
128}
129
130+(NSArray *)specialAttributeNames {
131    return [ NSArray arrayWithObjects:
132            NSAccessibilityValueAttribute,
133            NSAccessibilityNumberOfCharactersAttribute,
134            NSAccessibilitySelectedTextAttribute,
135            NSAccessibilitySelectedTextRangeAttribute,
136            NSAccessibilityVisibleCharacterRangeAttribute,
137            NSAccessibilitySharedTextUIElementsAttribute,
138            NSAccessibilitySharedCharacterRangeAttribute,
139            nil ];
140}
141
142+(void)addParameterizedAttributeNamesTo:(NSMutableArray *)attributeNames {
143    [ attributeNames addObjectsFromArray: [ AquaA11yTextWrapper specialParameterizedAttributeNames ] ];
144}
145
146+(NSArray *)specialParameterizedAttributeNames {
147    return [ NSArray arrayWithObjects:
148            NSAccessibilityStringForRangeParameterizedAttribute,
149            NSAccessibilityAttributedStringForRangeParameterizedAttribute,
150            NSAccessibilityRangeForIndexParameterizedAttribute,
151            NSAccessibilityRangeForPositionParameterizedAttribute,
152            NSAccessibilityBoundsForRangeParameterizedAttribute,
153            NSAccessibilityStyleRangeForIndexParameterizedAttribute,
154            NSAccessibilityRTFForRangeParameterizedAttribute,
155            NSAccessibilityLineForIndexParameterizedAttribute,
156            NSAccessibilityRangeForLineParameterizedAttribute,
157            nil ];
158}
159
160+(id)lineForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index {
161    NSNumber * lineNumber = nil;
162    try {
163        sal_Int32 line = [ wrapper accessibleMultiLineText ] -> getLineNumberAtIndex ( (sal_Int32) [ index intValue ] );
164        lineNumber = [ NSNumber numberWithInt: line ];
165    } catch ( IndexOutOfBoundsException & e ) {
166        // empty
167    }
168    return lineNumber;
169}
170
171+(id)rangeForLineAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)line {
172    NSValue * range = nil;
173    try {
174        TextSegment textSegment = [ wrapper accessibleMultiLineText ] -> getTextAtLineNumber ( [ line intValue ] );
175        range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ];
176    } catch ( IndexOutOfBoundsException & e ) {
177        // empty
178    }
179    return range;
180}
181
182+(id)stringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
183    int loc = [ range rangeValue ].location;
184    int len = [ range rangeValue ].length;
185    NSMutableString * textRange = [ [ NSMutableString alloc ] init ];
186    try {
187        [ textRange appendString: CreateNSString ( [ wrapper accessibleText ] -> getTextRange ( loc, loc + len ) ) ];
188    } catch ( IndexOutOfBoundsException & e ) {
189        // empty
190    }
191    return textRange;
192}
193
194+(id)attributedStringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
195    return [ AquaA11yTextAttributesWrapper createAttributedStringForElement: wrapper inOrigRange: range ];
196}
197
198+(id)rangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index {
199    NSValue * range = nil;
200    try {
201        TextSegment textSegment = [ wrapper accessibleText ] -> getTextBeforeIndex ( [ index intValue ], AccessibleTextType::GLYPH );
202        range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ];
203    } catch ( IndexOutOfBoundsException & e ) {
204        // empty
205    } catch ( IllegalArgumentException & e ) {
206        // empty
207    }
208    return range;
209}
210
211+(id)rangeForPositionAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)point {
212    NSValue * value = nil;
213    Point aPoint( [ AquaA11yUtil nsPointToVclPoint: point ]);
214    const Point screenPos = [ wrapper accessibleComponent ] -> getLocationOnScreen();
215    aPoint.X -= screenPos.X;
216    aPoint.Y -= screenPos.Y;
217    sal_Int32 index = [ wrapper accessibleText ] -> getIndexAtPoint( aPoint );
218    if ( index > -1 ) {
219        value = [ AquaA11yTextWrapper rangeForIndexAttributeForElement: wrapper forParameter: [ NSNumber numberWithLong: index ] ];
220    }
221    return value;
222}
223
224+(id)boundsForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
225    NSValue * rect = nil;
226    try {
227        // TODO: this is ugly!!!
228        // the UNP-API can only return the bounds for a single character, not for a range
229        int loc = [ range rangeValue ].location;
230        int len = [ range rangeValue ].length;
231        int minx = 0x7fffffff, miny = 0x7fffffff, maxx = 0, maxy = 0;
232        for ( int i = 0; i < len; i++ ) {
233            Rectangle vclRect = [ wrapper accessibleText ] -> getCharacterBounds ( loc + i );
234            if ( vclRect.X < minx ) {
235                minx = vclRect.X;
236            }
237            if ( vclRect.Y < miny ) {
238                miny = vclRect.Y;
239            }
240            if ( vclRect.Width + vclRect.X > maxx ) {
241                maxx = vclRect.Width + vclRect.X;
242            }
243            if ( vclRect.Height + vclRect.Y > maxy ) {
244                maxy = vclRect.Height + vclRect.Y;
245            }
246        }
247        if ( [ wrapper accessibleComponent ] != nil ) {
248            // get location on screen (must be added since get CharacterBounds returns values relative to parent)
249            Point screenPos = [ wrapper accessibleComponent ] -> getLocationOnScreen();
250            Point pos ( minx + screenPos.X, miny + screenPos.Y );
251            Point size ( maxx - minx, maxy - miny );
252            NSValue * nsPos = [ AquaA11yUtil vclPointToNSPoint: pos ];
253            rect = [ NSValue valueWithRect: NSMakeRect ( [ nsPos pointValue ].x, [ nsPos pointValue ].y - size.Y, size.X, size.Y ) ];
254            //printf("Range: %s --- Rect: %s\n", [ NSStringFromRange ( [ range rangeValue ] ) UTF8String ], [ NSStringFromRect ( [ rect rectValue ] ) UTF8String ]);
255        }
256    } catch ( IndexOutOfBoundsException & e ) {
257        // empty
258    }
259    return rect;
260}
261
262+(id)styleRangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index {
263    NSValue * range = nil;
264    try {
265        TextSegment textSegment = [ wrapper accessibleText ] -> getTextAtIndex ( [ index intValue ], AccessibleTextType::ATTRIBUTE_RUN );
266        range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ];
267    } catch ( IndexOutOfBoundsException & e ) {
268        // empty
269    } catch ( IllegalArgumentException & e ) {
270        // empty
271    }
272    return range;
273}
274
275+(id)rTFForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
276    NSData * rtfData = nil;
277    NSAttributedString * attrString = (NSAttributedString *) [ AquaA11yTextWrapper attributedStringForRangeAttributeForElement: wrapper forParameter: range ];
278    if ( attrString != nil ) {
279        @try {
280            rtfData = [ attrString RTFFromRange: [ range rangeValue ] documentAttributes: nil ];
281        } @catch ( NSException * e) {
282            // emtpy
283        }
284    }
285    return rtfData;
286}
287
288+(BOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper {
289    BOOL isSettable = NO;
290    if ( [ attribute isEqualToString: NSAccessibilityValueAttribute ]
291      || [ attribute isEqualToString: NSAccessibilitySelectedTextAttribute ]
292      || [ attribute isEqualToString: NSAccessibilitySelectedTextRangeAttribute ]
293      || [ attribute isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute ] ) {
294        if ( ! [ [ wrapper accessibilityAttributeValue: NSAccessibilityRoleAttribute ] isEqualToString: NSAccessibilityStaticTextRole ] ) {
295            isSettable = YES;
296        }
297    }
298    return isSettable;
299}
300
301@end
302