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    sal_Int32 index = [ wrapper accessibleText ] -> getIndexAtPoint ( [ AquaA11yUtil nsPointToVclPoint: point ] );
214    if ( index > -1 ) {
215        value = [ AquaA11yTextWrapper rangeForIndexAttributeForElement: wrapper forParameter: [ NSNumber numberWithLong: index ] ];
216    }
217    return value;
218}
219
220+(id)boundsForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
221    NSValue * rect = nil;
222    try {
223        // TODO: this is ugly!!!
224        // the UNP-API can only return the bounds for a single character, not for a range
225        int loc = [ range rangeValue ].location;
226        int len = [ range rangeValue ].length;
227        int minx = 0x7fffffff, miny = 0x7fffffff, maxx = 0, maxy = 0;
228        for ( int i = 0; i < len; i++ ) {
229            Rectangle vclRect = [ wrapper accessibleText ] -> getCharacterBounds ( loc + i );
230            if ( vclRect.X < minx ) {
231                minx = vclRect.X;
232            }
233            if ( vclRect.Y < miny ) {
234                miny = vclRect.Y;
235            }
236            if ( vclRect.Width + vclRect.X > maxx ) {
237                maxx = vclRect.Width + vclRect.X;
238            }
239            if ( vclRect.Height + vclRect.Y > maxy ) {
240                maxy = vclRect.Height + vclRect.Y;
241            }
242        }
243        if ( [ wrapper accessibleComponent ] != nil ) {
244            // get location on screen (must be added since get CharacterBounds returns values relative to parent)
245            Point screenPos = [ wrapper accessibleComponent ] -> getLocationOnScreen();
246            Point pos ( minx + screenPos.X, miny + screenPos.Y );
247            Point size ( maxx - minx, maxy - miny );
248            NSValue * nsPos = [ AquaA11yUtil vclPointToNSPoint: pos ];
249            rect = [ NSValue valueWithRect: NSMakeRect ( [ nsPos pointValue ].x, [ nsPos pointValue ].y - size.Y, size.X, size.Y ) ];
250            //printf("Range: %s --- Rect: %s\n", [ NSStringFromRange ( [ range rangeValue ] ) UTF8String ], [ NSStringFromRect ( [ rect rectValue ] ) UTF8String ]);
251        }
252    } catch ( IndexOutOfBoundsException & e ) {
253        // empty
254    }
255    return rect;
256}
257
258+(id)styleRangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index {
259    NSValue * range = nil;
260    try {
261        TextSegment textSegment = [ wrapper accessibleText ] -> getTextAtIndex ( [ index intValue ], AccessibleTextType::ATTRIBUTE_RUN );
262        range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ];
263    } catch ( IndexOutOfBoundsException & e ) {
264        // empty
265    } catch ( IllegalArgumentException & e ) {
266        // empty
267    }
268    return range;
269}
270
271+(id)rTFForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
272    NSData * rtfData = nil;
273    NSAttributedString * attrString = (NSAttributedString *) [ AquaA11yTextWrapper attributedStringForRangeAttributeForElement: wrapper forParameter: range ];
274    if ( attrString != nil ) {
275        @try {
276            rtfData = [ attrString RTFFromRange: [ range rangeValue ] documentAttributes: nil ];
277        } @catch ( NSException * e) {
278            // emtpy
279        }
280    }
281    return rtfData;
282}
283
284+(BOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper {
285    BOOL isSettable = NO;
286    if ( [ attribute isEqualToString: NSAccessibilityValueAttribute ]
287      || [ attribute isEqualToString: NSAccessibilitySelectedTextAttribute ]
288      || [ attribute isEqualToString: NSAccessibilitySelectedTextRangeAttribute ]
289      || [ attribute isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute ] ) {
290        if ( ! [ [ wrapper accessibilityAttributeValue: NSAccessibilityRoleAttribute ] isEqualToString: NSAccessibilityStaticTextRole ] ) {
291            isSettable = YES;
292        }
293    }
294    return isSettable;
295}
296
297@end
298