Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / osx / a11ytextattributeswrapper.mm
blob3ac368ea17c80dab97267c54887f0fa23a496789
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/text/TextMarkupType.hpp>
32 #include <com/sun/star/style/ParagraphAdjust.hpp>
34 namespace css_awt = ::com::sun::star::awt;
35 using namespace ::com::sun::star::accessibility;
36 using namespace ::com::sun::star::beans;
37 using namespace ::com::sun::star::lang;
38 using namespace ::com::sun::star::uno;
40 // cannot use NSFontDescriptor as it has no notion of explicit NSUn{bold,italic}FontMask
41 @interface AquaA11yFontDescriptor : NSObject
43     NSString *_name;
44     NSFontTraitMask _traits;
45     CGFloat _size;
47 -(void)setName:(NSString*)name;
48 -(void)setBold:(NSFontTraitMask)bold;
49 -(void)setItalic:(NSFontTraitMask)italic;
50 -(void)setSize:(CGFloat)size;
51 -(NSFont*)font;
52 @end
54 @implementation AquaA11yFontDescriptor
55 - (id)init
57     if((self = [super init]))
58     {
59         _name = nil;
60         _traits = 0;
61         _size = 0.0;
62     }
63     return self;
66 - (id)initWithDescriptor:(AquaA11yFontDescriptor*)descriptor {
67     if((self = [super init]))
68     {
69         _name = [descriptor->_name retain];
70         _traits = descriptor->_traits;
71         _size = descriptor->_size;
72     }
73     return self;
76 - (void)dealloc {
77     [_name release];
78     [super dealloc];
81 -(void)setName:(NSString*)name {
82     if (_name != name) {
83         [name retain];
84         [_name release];
85         _name = name;
86     }
89 -(void)setBold:(NSFontTraitMask)bold {
90     _traits &= ~(NSBoldFontMask | NSUnboldFontMask);
91     _traits |= bold & (NSBoldFontMask | NSUnboldFontMask);
94 -(void)setItalic:(NSFontTraitMask)italic {
95     _traits &= ~(NSItalicFontMask | NSUnitalicFontMask);
96     _traits |= italic & (NSItalicFontMask | NSUnitalicFontMask);
99 -(void)setSize:(CGFloat)size { _size = size; }
101 -(NSFont*)font {
102     return [[NSFontManager sharedFontManager] fontWithFamily:_name traits:_traits weight:0 size:_size];
104 @end
106 @implementation AquaA11yTextAttributesWrapper : NSObject
108 +(int)convertUnderlineStyle:(PropertyValue)property {
109 #if MACOSX_SDK_VERSION >= 1090
110     int underlineStyle = NSUnderlineStyleNone;
111 #else
112     int underlineStyle = NSNoUnderlineStyle;
113 #endif
114     sal_Int16 value = 0;
115     property.Value >>= value;
116     if ( value != ::css_awt::FontUnderline::NONE
117       && value != ::css_awt::FontUnderline::DONTKNOW) {
118 #if MACOSX_SDK_VERSION >= 1090
119         underlineStyle = NSUnderlineStyleSingle;
120 #else
121         underlineStyle = NSSingleUnderlineStyle;
122 #endif
123     }
124     return underlineStyle;
127 +(int)convertBoldStyle:(PropertyValue)property {
128     int boldStyle = NSUnboldFontMask;
129     float value = 0;
130     property.Value >>= value;
131     if ( value == ::css_awt::FontWeight::SEMIBOLD
132       || value == ::css_awt::FontWeight::BOLD
133       || value == ::css_awt::FontWeight::ULTRABOLD
134       || value == ::css_awt::FontWeight::BLACK ) {
135         boldStyle = NSBoldFontMask;
136     }
137     return boldStyle;
140 +(int)convertItalicStyle:(PropertyValue)property {
141     int italicStyle = NSUnitalicFontMask;
142     sal_Int16 value = property.Value.get< ::css_awt::FontSlant>();
143     if ( value == ::css_awt::FontSlant_ITALIC ) {
144         italicStyle = NSItalicFontMask;
145     }
146     return italicStyle;
149 +(BOOL)isStrikethrough:(PropertyValue)property {
150     BOOL strikethrough = NO;
151     sal_Int16 value = 0;
152     property.Value >>= value;
153     if ( value != ::css_awt::FontStrikeout::NONE
154       && value != ::css_awt::FontStrikeout::DONTKNOW ) {
155         strikethrough = YES;
156     }
157     return strikethrough;
160 +(BOOL)convertBoolean:(PropertyValue)property {
161     BOOL myBoolean = NO;
162     bool value = false;
163     property.Value >>= value;
164     if ( value ) {
165         myBoolean = YES;
166     }
167     return myBoolean;
170 +(NSNumber *)convertShort:(PropertyValue)property {
171     sal_Int16 value = 0;
172     property.Value >>= value;
173     return [ NSNumber numberWithShort: value ];
176 +(void)addColor:(SalColor)nSalColor forAttribute:(NSString *)attribute andRange:(NSRange)range toString:(NSMutableAttributedString *)string {
177     if( nSalColor == COL_TRANSPARENT )
178         return;
179     const RGBAColor aRGBAColor( nSalColor);
180     CGColorRef aColorRef = CGColorCreate ( CGColorSpaceCreateWithName ( kCGColorSpaceGenericRGB ), aRGBAColor.AsArray() );
181     [ string addAttribute: attribute value: reinterpret_cast<id>(aColorRef) range: range ];
182     CGColorRelease( aColorRef );
185 +(void)addFont:(NSFont *)font toString:(NSMutableAttributedString *)string forRange:(NSRange)range {
186     if ( font != nil ) {
187         NSDictionary * fontDictionary = [ NSDictionary dictionaryWithObjectsAndKeys:
188             [ font fontName ], NSAccessibilityFontNameKey,
189             [ font familyName ], NSAccessibilityFontFamilyKey,
190             [ font displayName ], NSAccessibilityVisibleNameKey,
191             [ NSNumber numberWithFloat: [ font pointSize ] ], NSAccessibilityFontSizeKey,
192             nil
193         ];
194         [ string addAttribute: NSAccessibilityFontTextAttribute
195                 value: fontDictionary
196                 range: range
197         ];
198     }
201 +(void)applyAttributesFrom:(Sequence < PropertyValue >)attributes toString:(NSMutableAttributedString *)string forRange:(NSRange)range fontDescriptor:(AquaA11yFontDescriptor*)fontDescriptor {
202     NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
203     // constants
204     static const OUString attrUnderline("CharUnderline");
205     static const OUString attrBold("CharWeight");
206     static const OUString attrFontname("CharFontName");
207     static const OUString attrItalic("CharPosture");
208     static const OUString attrHeight("CharHeight");
209     static const OUString attrStrikethrough("CharStrikeout");
210     static const OUString attrShadow("CharShadowed");
211     static const OUString attrUnderlineColor("CharUnderlineColor");
212     static const OUString attrUnderlineHasColor("CharUnderlineHasColor");
213     static const OUString attrForegroundColor("CharColor");
214     static const OUString attrBackgroundColor("CharBackColor");
215     static const OUString attrSuperscript("CharEscapement");
216     static const OUString attrTextAlignment("ParaAdjust");
217     // vars
218     sal_Int32 underlineColor = 0;
219     BOOL underlineHasColor = NO;
220     // add attributes to string
221     for ( int attrIndex = 0; attrIndex < attributes.getLength(); attrIndex++ ) {
222         PropertyValue property = attributes [ attrIndex ];
223         // TODO: NSAccessibilityMisspelledTextAttribute, NSAccessibilityAttachmentTextAttribute, NSAccessibilityLinkTextAttribute
224         // NSAccessibilityStrikethroughColorTextAttribute is unsupported by UNP-API
225         if ( property.Value.hasValue() ) {
226             if ( property.Name.equals ( attrUnderline ) ) {
227                 int style = [ AquaA11yTextAttributesWrapper convertUnderlineStyle: property ];
228 #if MACOSX_SDK_VERSION >= 1090
229                 if ( style != NSUnderlineStyleNone ) {
230 #else
231                 if ( style != NSNoUnderlineStyle ) {
232 #endif
233                     [ string addAttribute: NSAccessibilityUnderlineTextAttribute value: [ NSNumber numberWithInt: style ] range: range ];
234                 }
235             } else if ( property.Name.equals ( attrFontname ) ) {
236                 OUString fontname;
237                 property.Value >>= fontname;
238                 [fontDescriptor setName:CreateNSString(fontname)];
239             } else if ( property.Name.equals ( attrBold ) ) {
240                 [fontDescriptor setBold:[AquaA11yTextAttributesWrapper convertBoldStyle:property]];
241             } else if ( property.Name.equals ( attrItalic ) ) {
242                 [fontDescriptor setItalic:[AquaA11yTextAttributesWrapper convertItalicStyle:property]];
243             } else if ( property.Name.equals ( attrHeight ) ) {
244                 float size;
245                 property.Value >>= size;
246                 [fontDescriptor setSize:size];
247             } else if ( property.Name.equals ( attrStrikethrough ) ) {
248                 if ( [ AquaA11yTextAttributesWrapper isStrikethrough: property ] ) {
249                     [ string addAttribute: NSAccessibilityStrikethroughTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ];
250                 }
251             } else if ( property.Name.equals ( attrShadow ) ) {
252                 if ( [ AquaA11yTextAttributesWrapper convertBoolean: property ] ) {
253                     [ string addAttribute: NSAccessibilityShadowTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ];
254                 }
255             } else if ( property.Name.equals ( attrUnderlineColor ) ) {
256                 property.Value >>= underlineColor;
257             } else if ( property.Name.equals ( attrUnderlineHasColor ) ) {
258                 underlineHasColor = [ AquaA11yTextAttributesWrapper convertBoolean: property ];
259             } else if ( property.Name.equals ( attrForegroundColor ) ) {
260                 [ AquaA11yTextAttributesWrapper addColor: property.Value.get<sal_Int32>() forAttribute: NSAccessibilityForegroundColorTextAttribute andRange: range toString: string ];
261             } else if ( property.Name.equals ( attrBackgroundColor ) ) {
262                 [ AquaA11yTextAttributesWrapper addColor: property.Value.get<sal_Int32>() forAttribute: NSAccessibilityBackgroundColorTextAttribute andRange: range toString: string ];
263             } else if ( property.Name.equals ( attrSuperscript ) ) {
264                 // values < zero mean subscript
265                 // values > zero mean superscript
266                 // this is true for both NSAccessibility-API and UNO-API
267                 NSNumber * number = [ AquaA11yTextAttributesWrapper convertShort: property ];
268                 if ( [ number shortValue ] != 0 ) {
269                     [ string addAttribute: NSAccessibilitySuperscriptTextAttribute value: number range: range ];
270                 }
271             } else if ( property.Name.equals ( attrTextAlignment ) ) {
272                 sal_Int32 alignment;
273                 property.Value >>= alignment;
274                 NSNumber *textAlignment = nil;
275                 switch(alignment) {
276                     case ::com::sun::star::style::ParagraphAdjust_RIGHT : textAlignment = [NSNumber numberWithInteger:NSRightTextAlignment]    ; break;
277                     case ::com::sun::star::style::ParagraphAdjust_CENTER: textAlignment = [NSNumber numberWithInteger:NSCenterTextAlignment]   ; break;
278                     case ::com::sun::star::style::ParagraphAdjust_BLOCK : textAlignment = [NSNumber numberWithInteger:NSJustifiedTextAlignment]; break;
279                     case ::com::sun::star::style::ParagraphAdjust_LEFT  :
280                     default                                             : textAlignment = [NSNumber numberWithInteger:NSLeftTextAlignment]     ; break;
281                 }
282                 NSDictionary *paragraphStyle = [NSDictionary dictionaryWithObjectsAndKeys:textAlignment, @"AXTextAlignment", textAlignment, @"AXVisualTextAlignment", nil];
283                 [string addAttribute:@"AXParagraphStyle" value:paragraphStyle range:range];
284             }
285         }
286     }
287     // add underline information
288     if ( underlineHasColor ) {
289         [ AquaA11yTextAttributesWrapper addColor: underlineColor forAttribute: NSAccessibilityUnderlineColorTextAttribute andRange: range toString: string ];
290     }
291     // add font information
292     NSFont * font = [fontDescriptor font];
293     [AquaA11yTextAttributesWrapper addFont:font toString:string forRange:range];
294     [ pool release ];
297 +(void)addMarkup:(XAccessibleTextMarkup*)markup withType:(long)type toString:(NSMutableAttributedString*)string inRange:(NSRange)range {
298     const long markupCount = markup->getTextMarkupCount(type);
299     for (long markupIndex = 0; markupIndex < markupCount; ++markupIndex) {
300         TextSegment markupSegment = markup->getTextMarkup(markupIndex, type);
301         NSRange markupRange = NSMakeRange(markupSegment.SegmentStart, markupSegment.SegmentEnd - markupSegment.SegmentStart);
302         markupRange = NSIntersectionRange(range, markupRange);
303         if (markupRange.length > 0) {
304             markupRange.location -= range.location;
305             switch(type) {
306                 case ::com::sun::star::text::TextMarkupType::SPELLCHECK: {
307                     [string addAttribute:NSAccessibilityMisspelledTextAttribute value:[NSNumber numberWithBool:YES] range:markupRange];
308                     [string addAttribute:@"AXMarkedMisspelled" value:[NSNumber numberWithBool:YES] range:markupRange];
309                     break;
310                 }
311             }
312         }
313     }
316 +(void)addMarkup:(XAccessibleTextMarkup*)markup toString:(NSMutableAttributedString*)string inRange:(NSRange)range {
317     [AquaA11yTextAttributesWrapper addMarkup:markup withType:(::com::sun::star::text::TextMarkupType::SPELLCHECK) toString:string inRange:range];
320 +(NSMutableAttributedString *)createAttributedStringForElement:(AquaA11yWrapper *)wrapper inOrigRange:(id)origRange {
321     static const Sequence < OUString > emptySequence;
322     // vars
323     NSMutableAttributedString * string = nil;
324     int loc = [ origRange rangeValue ].location;
325     int len = [ origRange rangeValue ].length;
326     int endIndex = loc + len;
327     int currentIndex = loc;
328     try {
329         NSString * myString = CreateNSString ( [ wrapper accessibleText ] -> getText() ); // TODO: dirty fix for i87817
330         string = [ [ NSMutableAttributedString alloc ] initWithString: CreateNSString ( [ wrapper accessibleText ] -> getTextRange ( loc, loc + len ) ) ];
331         if ( [ wrapper accessibleTextAttributes ] && [myString characterAtIndex:0] != 57361) { // TODO: dirty fix for i87817
332             [ string beginEditing ];
333             // add default attributes for whole string
334             Sequence < PropertyValue > defaultAttributes = [ wrapper accessibleTextAttributes ] -> getDefaultAttributes ( emptySequence );
335             AquaA11yFontDescriptor *defaultFontDescriptor = [[AquaA11yFontDescriptor alloc] init];
336             [ AquaA11yTextAttributesWrapper applyAttributesFrom: defaultAttributes toString: string forRange: NSMakeRange ( 0, len ) fontDescriptor: defaultFontDescriptor ];
337             // add attributes for attribute run(s)
338             while ( currentIndex < endIndex ) {
339                 TextSegment textSegment = [ wrapper accessibleText ] -> getTextAtIndex ( currentIndex, AccessibleTextType::ATTRIBUTE_RUN );
340                 int endOfRange = endIndex > textSegment.SegmentEnd ? textSegment.SegmentEnd : endIndex;
341                 NSRange rangeForAttributeRun = NSMakeRange ( currentIndex - loc , endOfRange - currentIndex );
342                 // add run attributes
343                 Sequence < PropertyValue > attributes = [ wrapper accessibleTextAttributes ] -> getRunAttributes ( currentIndex, emptySequence );
344                 AquaA11yFontDescriptor *fontDescriptor = [[AquaA11yFontDescriptor alloc] initWithDescriptor:defaultFontDescriptor];
345                 [ AquaA11yTextAttributesWrapper applyAttributesFrom: attributes toString: string forRange: rangeForAttributeRun fontDescriptor: fontDescriptor ];
346                 [fontDescriptor release];
347                 currentIndex = textSegment.SegmentEnd;
348             }
349             [defaultFontDescriptor release];
350             if ([wrapper accessibleTextMarkup])
351                 [AquaA11yTextAttributesWrapper addMarkup:[wrapper accessibleTextMarkup] toString:string inRange:[origRange rangeValue]];
352             [ string endEditing ];
353         }
354     } catch ( IllegalArgumentException & e ) {
355         // empty
356     } catch ( IndexOutOfBoundsException & e ) {
357         // empty
358     } catch ( RuntimeException& ) {
359         // at least don't crash
360     }
361     return string;
364 @end
366 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */