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
33#include "aqua11ytextwrapper.h"
34#include "aqua11ytextattributeswrapper.h"
35#include "aqua11yutil.h"
36
37#include <com/sun/star/accessibility/AccessibleTextType.hpp>
38#include <com/sun/star/awt/Rectangle.hpp>
39
40using namespace ::com::sun::star::accessibility;
41using namespace ::com::sun::star::awt;
42using namespace ::com::sun::star::lang;
43using namespace ::com::sun::star::uno;
44using namespace ::rtl;
45
46// Wrapper for XAccessibleText, XAccessibleEditableText and XAccessibleMultiLineText
47
48@implementation AquaA11yTextWrapper : NSObject
49
50+(id)valueAttributeForElement:(AquaA11yWrapper *)wrapper {
51    return CreateNSString ( [ wrapper accessibleText ] -> getText() );
52}
53
54+(void)setValueAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value
55{
56    // TODO
57    (void)wrapper;
58    (void)value;
59}
60
61+(id)numberOfCharactersAttributeForElement:(AquaA11yWrapper *)wrapper {
62    return [ NSNumber numberWithLong: [ wrapper accessibleText ] -> getCharacterCount() ];
63}
64
65+(id)selectedTextAttributeForElement:(AquaA11yWrapper *)wrapper {
66    return CreateNSString ( [ wrapper accessibleText ] -> getSelectedText() );
67}
68
69+(void)setSelectedTextAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value {
70    if ( [ wrapper accessibleEditableText ] != nil ) {
71        NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
72        OUString newText = GetOUString ( (NSString *) value );
73        NSRange selectedTextRange = [ [ AquaA11yTextWrapper selectedTextRangeAttributeForElement: wrapper ] rangeValue ];
74        try {
75            [ wrapper accessibleEditableText ] -> replaceText ( selectedTextRange.location, selectedTextRange.location + selectedTextRange.length, newText );
76        } catch ( const Exception & e ) {
77            // empty
78        }
79        [ pool release ];
80    }
81}
82
83+(id)selectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper {
84    sal_Int32 start = [ wrapper accessibleText ] -> getSelectionStart();
85    sal_Int32 end = [ wrapper accessibleText ] -> getSelectionEnd();
86    if ( start != end ) {
87        return [ NSValue valueWithRange: NSMakeRange ( start, end - start ) ]; // true selection
88    } else {
89        long caretPos = [ wrapper accessibleText ] -> getCaretPosition();
90        if ( caretPos < 0 || caretPos > [ wrapper accessibleText ] -> getCharacterCount() ) {
91            return nil;
92        }
93        return [ NSValue valueWithRange: NSMakeRange ( caretPos, 0 ) ]; // insertion point
94    }
95}
96
97+(void)setSelectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value {
98    NSRange range = [ value rangeValue ];
99    try {
100        [ wrapper accessibleText ] -> setSelection ( range.location, range.location + range.length );
101    } catch ( const Exception & e ) {
102        // empty
103    }
104}
105
106+(id)visibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper {
107    // the OOo a11y API returns only the visible portion...
108    return [ NSValue valueWithRange: NSMakeRange ( 0, [ wrapper accessibleText ] -> getCharacterCount() ) ];
109}
110
111+(void)setVisibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value
112{
113    // do nothing
114    (void)wrapper;
115    (void)value;
116}
117
118+(id)sharedTextUIElementsAttributeForElement:(AquaA11yWrapper *)wrapper
119{
120    (void)wrapper;
121    return [ [ NSArray alloc ] init ]; // unsupported
122}
123
124+(id)sharedCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper
125{
126    (void)wrapper;
127    return [ NSValue valueWithRange: NSMakeRange ( 0, 0 ) ]; // unsupported
128}
129
130+(void)addAttributeNamesTo:(NSMutableArray *)attributeNames {
131    [ attributeNames addObjectsFromArray: [ AquaA11yTextWrapper specialAttributeNames ] ];
132}
133
134+(NSArray *)specialAttributeNames {
135    return [ NSArray arrayWithObjects:
136            NSAccessibilityValueAttribute,
137            NSAccessibilityNumberOfCharactersAttribute,
138            NSAccessibilitySelectedTextAttribute,
139            NSAccessibilitySelectedTextRangeAttribute,
140            NSAccessibilityVisibleCharacterRangeAttribute,
141            NSAccessibilitySharedTextUIElementsAttribute,
142            NSAccessibilitySharedCharacterRangeAttribute,
143            nil ];
144}
145
146+(void)addParameterizedAttributeNamesTo:(NSMutableArray *)attributeNames {
147    [ attributeNames addObjectsFromArray: [ AquaA11yTextWrapper specialParameterizedAttributeNames ] ];
148}
149
150+(NSArray *)specialParameterizedAttributeNames {
151    return [ NSArray arrayWithObjects:
152            NSAccessibilityStringForRangeParameterizedAttribute,
153            NSAccessibilityAttributedStringForRangeParameterizedAttribute,
154            NSAccessibilityRangeForIndexParameterizedAttribute,
155            NSAccessibilityRangeForPositionParameterizedAttribute,
156            NSAccessibilityBoundsForRangeParameterizedAttribute,
157            NSAccessibilityStyleRangeForIndexParameterizedAttribute,
158            NSAccessibilityRTFForRangeParameterizedAttribute,
159            NSAccessibilityLineForIndexParameterizedAttribute,
160            NSAccessibilityRangeForLineParameterizedAttribute,
161            nil ];
162}
163
164+(id)lineForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index {
165    NSNumber * lineNumber = nil;
166    try {
167        sal_Int32 line = [ wrapper accessibleMultiLineText ] -> getLineNumberAtIndex ( (sal_Int32) [ index intValue ] );
168        lineNumber = [ NSNumber numberWithInt: line ];
169    } catch ( IndexOutOfBoundsException & e ) {
170        // empty
171    }
172    return lineNumber;
173}
174
175+(id)rangeForLineAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)line {
176    NSValue * range = nil;
177    try {
178        TextSegment textSegment = [ wrapper accessibleMultiLineText ] -> getTextAtLineNumber ( [ line intValue ] );
179        range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ];
180    } catch ( IndexOutOfBoundsException & e ) {
181        // empty
182    }
183    return range;
184}
185
186+(id)stringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
187    int loc = [ range rangeValue ].location;
188    int len = [ range rangeValue ].length;
189    NSMutableString * textRange = [ [ NSMutableString alloc ] init ];
190    try {
191        [ textRange appendString: CreateNSString ( [ wrapper accessibleText ] -> getTextRange ( loc, loc + len ) ) ];
192    } catch ( IndexOutOfBoundsException & e ) {
193        // empty
194    }
195    return textRange;
196}
197
198+(id)attributedStringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range {
199    return [ AquaA11yTextAttributesWrapper createAttributedStringForElement: wrapper inOrigRange: range ];
200}
201
202+(id)rangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index {
203    NSValue * range = nil;
204    try {
205        TextSegment textSegment = [ wrapper accessibleText ] -> getTextBeforeIndex ( [ index intValue ], AccessibleTextType::GLYPH );
206        range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ];
207    } catch ( IndexOutOfBoundsException & e ) {
208        // empty
209    } catch ( IllegalArgumentException & e ) {
210        // empty
211    }
212    return range;
213}
214
215+(id)rangeForPositionAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)point {
216    NSValue * value = nil;
217    sal_Int32 index = [ wrapper accessibleText ] -> getIndexAtPoint ( [ AquaA11yUtil nsPointToVclPoint: point ] );
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