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