bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / osx / a11ytextattributeswrapper.mm
blob8a8afe9a154b8834b4e538d1dfca9cd8a3b5182c
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  */
21 #include <osx/salinst.h>
22 #include <quartz/utils.h>
23 #include <quartz/salgdi.h>
25 #include "a11ytextattributeswrapper.h"
27 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
28 #include <com/sun/star/awt/FontUnderline.hpp>
29 #include <com/sun/star/awt/FontWeight.hpp>
30 #include <com/sun/star/awt/FontStrikeout.hpp>
31 #include <com/sun/star/lang/IllegalArgumentException.hpp>
32 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
33 #include <com/sun/star/text/TextMarkupType.hpp>
34 #include <com/sun/star/style/ParagraphAdjust.hpp>
36 namespace css_awt = ::com::sun::star::awt;
37 using namespace ::com::sun::star::accessibility;
38 using namespace ::com::sun::star::beans;
39 using namespace ::com::sun::star::lang;
40 using namespace ::com::sun::star::uno;
42 // cannot use NSFontDescriptor as it has no notion of explicit NSUn{bold,italic}FontMask
43 @interface AquaA11yFontDescriptor : NSObject
45     NSString *_name;
46     NSFontTraitMask _traits;
47     CGFloat _size;
49 -(void)setName:(NSString*)name;
50 -(void)setBold:(NSFontTraitMask)bold;
51 -(void)setItalic:(NSFontTraitMask)italic;
52 -(void)setSize:(CGFloat)size;
53 -(NSFont*)font;
54 @end
56 @implementation AquaA11yFontDescriptor
57 - (id)init
59     if((self = [super init]))
60     {
61         _name = nil;
62         _traits = 0;
63         _size = 0.0;
64     }
65     return self;
68 - (id)initWithDescriptor:(AquaA11yFontDescriptor*)descriptor {
69     if((self = [super init]))
70     {
71         _name = [descriptor->_name retain];
72         _traits = descriptor->_traits;
73         _size = descriptor->_size;
74     }
75     return self;
78 - (void)dealloc {
79     [_name release];
80     [super dealloc];
83 -(void)setName:(NSString*)name {
84     if (_name != name) {
85         [name retain];
86         [_name release];
87         _name = name;
88     }
91 -(void)setBold:(NSFontTraitMask)bold {
92     _traits &= ~(NSBoldFontMask | NSUnboldFontMask);
93     _traits |= bold & (NSBoldFontMask | NSUnboldFontMask);
96 -(void)setItalic:(NSFontTraitMask)italic {
97     _traits &= ~(NSItalicFontMask | NSUnitalicFontMask);
98     _traits |= italic & (NSItalicFontMask | NSUnitalicFontMask);
101 -(void)setSize:(CGFloat)size { _size = size; }
103 -(NSFont*)font {
104     return [[NSFontManager sharedFontManager] fontWithFamily:_name traits:_traits weight:0 size:_size];
106 @end
108 @implementation AquaA11yTextAttributesWrapper : NSObject
110 +(int)convertUnderlineStyle:(PropertyValue)property {
111     int underlineStyle = NSUnderlineStyleNone;
112     sal_Int16 value = 0;
113     property.Value >>= value;
114     if ( value != ::css_awt::FontUnderline::NONE
115       && value != ::css_awt::FontUnderline::DONTKNOW) {
116         underlineStyle = NSUnderlineStyleSingle;
117     }
118     return underlineStyle;
121 +(int)convertBoldStyle:(PropertyValue)property {
122     int boldStyle = NSUnboldFontMask;
123     float value = 0;
124     property.Value >>= value;
125     if ( value == ::css_awt::FontWeight::SEMIBOLD
126       || value == ::css_awt::FontWeight::BOLD
127       || value == ::css_awt::FontWeight::ULTRABOLD
128       || value == ::css_awt::FontWeight::BLACK ) {
129         boldStyle = NSBoldFontMask;
130     }
131     return boldStyle;
134 +(int)convertItalicStyle:(PropertyValue)property {
135     int italicStyle = NSUnitalicFontMask;
136     ::css_awt::FontSlant value = property.Value.get< ::css_awt::FontSlant>();
137     if ( value == ::css_awt::FontSlant_ITALIC ) {
138         italicStyle = NSItalicFontMask;
139     }
140     return italicStyle;
143 +(BOOL)isStrikethrough:(PropertyValue)property {
144     bool strikethrough = false;
145     sal_Int16 value = 0;
146     property.Value >>= value;
147     if ( value != ::css_awt::FontStrikeout::NONE
148       && value != ::css_awt::FontStrikeout::DONTKNOW ) {
149         strikethrough = true;
150     }
151     return strikethrough;
154 +(BOOL)convertBoolean:(PropertyValue)property {
155     bool myBoolean = false;
156     bool value = false;
157     property.Value >>= value;
158     if ( value ) {
159         myBoolean = true;
160     }
161     return myBoolean;
164 +(NSNumber *)convertShort:(PropertyValue)property {
165     sal_Int16 value = 0;
166     property.Value >>= value;
167     return [ NSNumber numberWithShort: value ];
170 +(void)addColor:(Color)nColor forAttribute:(NSString *)attribute andRange:(NSRange)range toString:(NSMutableAttributedString *)string {
171     if( nColor == COL_TRANSPARENT )
172         return;
173     const RGBAColor aRGBAColor( nColor);
174     CGColorRef aColorRef = CGColorCreate ( CGColorSpaceCreateWithName ( kCGColorSpaceGenericRGB ), aRGBAColor.AsArray() );
175     [ string addAttribute: attribute value: reinterpret_cast<id>(aColorRef) range: range ];
176     CGColorRelease( aColorRef );
179 +(void)addFont:(NSFont *)font toString:(NSMutableAttributedString *)string forRange:(NSRange)range {
180     if ( font != nil ) {
181         NSDictionary * fontDictionary = [ NSDictionary dictionaryWithObjectsAndKeys:
182             [ font fontName ], NSAccessibilityFontNameKey,
183             [ font familyName ], NSAccessibilityFontFamilyKey,
184             [ font displayName ], NSAccessibilityVisibleNameKey,
185             [ NSNumber numberWithFloat: [ font pointSize ] ], NSAccessibilityFontSizeKey,
186             nil
187         ];
188         [ string addAttribute: NSAccessibilityFontTextAttribute
189                 value: fontDictionary
190                 range: range
191         ];
192     }
195 +(void)applyAttributesFrom:(Sequence < PropertyValue > const &)attributes toString:(NSMutableAttributedString *)string forRange:(NSRange)range fontDescriptor:(AquaA11yFontDescriptor*)fontDescriptor {
196     NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
197     // vars
198     sal_Int32 underlineColor = 0;
199     bool underlineHasColor = false;
200     // add attributes to string
201     for ( const PropertyValue& property : attributes ) {
202         // TODO: NSAccessibilityMisspelledTextAttribute, NSAccessibilityAttachmentTextAttribute, NSAccessibilityLinkTextAttribute
203         // NSAccessibilityStrikethroughColorTextAttribute is unsupported by UNP-API
204         if ( property.Value.hasValue() ) {
205             if ( property.Name == "CharUnderline" ) {
206                 int style = [ AquaA11yTextAttributesWrapper convertUnderlineStyle: property ];
207                 if ( style != NSUnderlineStyleNone ) {
208                     [ string addAttribute: NSAccessibilityUnderlineTextAttribute value: [ NSNumber numberWithInt: style ] range: range ];
209                 }
210             } else if ( property.Name == "CharFontName" ) {
211                 OUString fontname;
212                 property.Value >>= fontname;
213                 [fontDescriptor setName:CreateNSString(fontname)];
214             } else if ( property.Name == "CharWeight" ) {
215                 [fontDescriptor setBold:[AquaA11yTextAttributesWrapper convertBoldStyle:property]];
216             } else if ( property.Name == "CharPosture" ) {
217                 [fontDescriptor setItalic:[AquaA11yTextAttributesWrapper convertItalicStyle:property]];
218             } else if ( property.Name == "CharHeight" ) {
219                 float size;
220                 property.Value >>= size;
221                 [fontDescriptor setSize:size];
222             } else if ( property.Name == "CharStrikeout" ) {
223                 if ( [ AquaA11yTextAttributesWrapper isStrikethrough: property ] ) {
224                     [ string addAttribute: NSAccessibilityStrikethroughTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ];
225                 }
226             } else if ( property.Name == "CharShadowed" ) {
227                 if ( [ AquaA11yTextAttributesWrapper convertBoolean: property ] ) {
228                     [ string addAttribute: NSAccessibilityShadowTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ];
229                 }
230             } else if ( property.Name == "CharUnderlineColor" ) {
231                 property.Value >>= underlineColor;
232             } else if ( property.Name == "CharUnderlineHasColor" ) {
233                 underlineHasColor = [ AquaA11yTextAttributesWrapper convertBoolean: property ];
234             } else if ( property.Name == "CharColor" ) {
235                 [ AquaA11yTextAttributesWrapper addColor: Color(ColorTransparency, property.Value.get<sal_Int32>()) forAttribute: NSAccessibilityForegroundColorTextAttribute andRange: range toString: string ];
236             } else if ( property.Name == "CharBackColor" ) {
237                 [ AquaA11yTextAttributesWrapper addColor: Color(ColorTransparency, property.Value.get<sal_Int32>()) forAttribute: NSAccessibilityBackgroundColorTextAttribute andRange: range toString: string ];
238             } else if ( property.Name == "CharEscapement" ) {
239                 // values < zero mean subscript
240                 // values > zero mean superscript
241                 // this is true for both NSAccessibility-API and UNO-API
242                 NSNumber * number = [ AquaA11yTextAttributesWrapper convertShort: property ];
243                 if ( [ number shortValue ] != 0 ) {
244                     [ string addAttribute: NSAccessibilitySuperscriptTextAttribute value: number range: range ];
245                 }
246             } else if ( property.Name == "ParaAdjust" ) {
247                 sal_Int32 alignment;
248                 property.Value >>= alignment;
249                 NSNumber *textAlignment = nil;
250                 switch(static_cast<css::style::ParagraphAdjust>(alignment)) {
251                     case css::style::ParagraphAdjust_RIGHT:
252                         textAlignment = [NSNumber numberWithInteger:NSTextAlignmentRight];
253                         break;
254                     case css::style::ParagraphAdjust_CENTER:
255                         textAlignment = [NSNumber numberWithInteger:NSTextAlignmentCenter];
256                         break;
257                     case css::style::ParagraphAdjust_BLOCK:
258                         textAlignment = [NSNumber numberWithInteger:NSTextAlignmentJustified];
259                         break;
260                     case css::style::ParagraphAdjust_LEFT:
261                     default:
262                         textAlignment = [NSNumber numberWithInteger:NSTextAlignmentLeft];
263                         break;
264                 }
265                 NSDictionary *paragraphStyle = [NSDictionary dictionaryWithObjectsAndKeys:textAlignment, @"AXTextAlignment", textAlignment, @"AXVisualTextAlignment", nil];
266                 [string addAttribute:@"AXParagraphStyle" value:paragraphStyle range:range];
267             }
268         }
269     }
270     // add underline information
271     if ( underlineHasColor ) {
272         [ AquaA11yTextAttributesWrapper addColor: Color(ColorTransparency, underlineColor) forAttribute: NSAccessibilityUnderlineColorTextAttribute andRange: range toString: string ];
273     }
274     // add font information
275     NSFont * font = [fontDescriptor font];
276     [AquaA11yTextAttributesWrapper addFont:font toString:string forRange:range];
277     [ pool release ];
280 +(void)addMarkup:(XAccessibleTextMarkup*)markup withType:(sal_Int32)type toString:(NSMutableAttributedString*)string inRange:(NSRange)range {
281     const sal_Int32 markupCount = markup->getTextMarkupCount(type);
282     for (sal_Int32 markupIndex = 0; markupIndex < markupCount; ++markupIndex) {
283         TextSegment markupSegment = markup->getTextMarkup(markupIndex, type);
284         NSRange markupRange = NSMakeRange(markupSegment.SegmentStart, markupSegment.SegmentEnd - markupSegment.SegmentStart);
285         markupRange = NSIntersectionRange(range, markupRange);
286         if (markupRange.length > 0) {
287             markupRange.location -= range.location;
288             switch(type) {
289                 case css::text::TextMarkupType::SPELLCHECK: {
290                     [string addAttribute:NSAccessibilityMisspelledTextAttribute value:[NSNumber numberWithBool:YES] range:markupRange];
291                     [string addAttribute:@"AXMarkedMisspelled" value:[NSNumber numberWithBool:YES] range:markupRange];
292                     break;
293                 }
294             }
295         }
296     }
299 +(void)addMarkup:(XAccessibleTextMarkup*)markup toString:(NSMutableAttributedString*)string inRange:(NSRange)range {
300     [AquaA11yTextAttributesWrapper addMarkup:markup withType:css::text::TextMarkupType::SPELLCHECK toString:string inRange:range];
303 // tdf#148453 Fix crash by turning off optimization for Objective-C selector
304 // The default attributes sequence sometimes crashes when it is released but
305 // only when compiler optimization is enabled, so disable optimization for the
306 // +[AquaA11yTextAttributesWrapper createAttributedStringForElement] selector.
307 +(NSMutableAttributedString *)createAttributedStringForElement:(AquaA11yWrapper *)wrapper inOrigRange:(id)origRange __attribute__((optnone)) {
308     static const Sequence < OUString > emptySequence;
309     // vars
310     NSMutableAttributedString * string = nil;
311     int loc = [ origRange rangeValue ].location;
312     int len = [ origRange rangeValue ].length;
313     int endIndex = loc + len;
314     int currentIndex = loc;
315     try {
316         NSString * myString = CreateNSString ( [ wrapper accessibleText ] -> getText() ); // TODO: dirty fix for i87817
317         string = [ [ NSMutableAttributedString alloc ] initWithString: CreateNSString ( [ wrapper accessibleText ] -> getTextRange ( loc, loc + len ) ) ];
318         [ string autorelease ];
319         if ( [ wrapper accessibleTextAttributes ] && [myString characterAtIndex:0] != 57361) { // TODO: dirty fix for i87817
320             [ string beginEditing ];
321             // add default attributes for whole string
322             Sequence < PropertyValue > defaultAttributes = [ wrapper accessibleTextAttributes ] -> getDefaultAttributes ( emptySequence );
323             AquaA11yFontDescriptor *defaultFontDescriptor = [[AquaA11yFontDescriptor alloc] init];
324             [ AquaA11yTextAttributesWrapper applyAttributesFrom: defaultAttributes toString: string forRange: NSMakeRange ( 0, len ) fontDescriptor: defaultFontDescriptor ];
325             // add attributes for attribute run(s)
326             while ( currentIndex < endIndex ) {
327                 TextSegment textSegment = [ wrapper accessibleText ] -> getTextAtIndex ( currentIndex, AccessibleTextType::ATTRIBUTE_RUN );
328                 int endOfRange = endIndex > textSegment.SegmentEnd ? textSegment.SegmentEnd : endIndex;
329                 NSRange rangeForAttributeRun = NSMakeRange ( currentIndex - loc , endOfRange - currentIndex );
330                 // add run attributes
331                 Sequence < PropertyValue > attributes = [ wrapper accessibleTextAttributes ] -> getRunAttributes ( currentIndex, emptySequence );
332                 AquaA11yFontDescriptor *fontDescriptor = [[AquaA11yFontDescriptor alloc] initWithDescriptor:defaultFontDescriptor];
333                 [ AquaA11yTextAttributesWrapper applyAttributesFrom: attributes toString: string forRange: rangeForAttributeRun fontDescriptor: fontDescriptor ];
334                 [fontDescriptor release];
335                 currentIndex = textSegment.SegmentEnd;
336             }
337             [defaultFontDescriptor release];
338             if ([wrapper accessibleTextMarkup])
339                 [AquaA11yTextAttributesWrapper addMarkup:[wrapper accessibleTextMarkup] toString:string inRange:[origRange rangeValue]];
340             [ string endEditing ];
341         }
342     } catch ( IllegalArgumentException & ) {
343         // empty
344     } catch ( IndexOutOfBoundsException & ) {
345         // empty
346     } catch ( RuntimeException& ) {
347         // at least don't crash
348     }
349     return string;
352 @end
354 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */