Add support for accessibility actions.
[profile/ivi/qtbase.git] / src / plugins / platforms / cocoa / qcocoaaccessibilityelement.mm
1 /****************************************************************************
2  **
3  ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4  ** All rights reserved.
5  ** Contact: Nokia Corporation (qt-info@nokia.com)
6  **
7  ** This file is part of the plugins of the Qt Toolkit.
8  **
9  ** $QT_BEGIN_LICENSE:LGPL$
10  ** GNU Lesser General Public License Usage
11  ** This file may be used under the terms of the GNU Lesser General Public
12  ** License version 2.1 as published by the Free Software Foundation and
13  ** appearing in the file LICENSE.LGPL included in the packaging of this
14  ** file. Please review the following information to ensure the GNU Lesser
15  ** General Public License version 2.1 requirements will be met:
16  ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17  **
18  ** In addition, as a special exception, Nokia gives you certain additional
19  ** rights. These rights are described in the Nokia Qt LGPL Exception
20  ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21  **
22  ** GNU General Public License Usage
23  ** Alternatively, this file may be used under the terms of the GNU General
24  ** Public License version 3.0 as published by the Free Software Foundation
25  ** and appearing in the file LICENSE.GPL included in the packaging of this
26  ** file. Please review the following information to ensure the GNU General
27  ** Public License version 3.0 requirements will be met:
28  ** http://www.gnu.org/copyleft/gpl.html.
29  **
30  ** Other Usage
31  ** Alternatively, this file may be used in accordance with the terms and
32  ** conditions contained in a signed written agreement between you and Nokia.
33  **
34  **
35  **
36  **
37  **
38  ** $QT_END_LICENSE$
39  **
40  ****************************************************************************/
41 #include "qcocoaaccessibilityelement.h"
42 #include "qcocoaaccessibility.h"
43 #include "qcocoahelpers.h"
44
45 #include <QAccessible>
46 #include "QAccessibleActionInterface"
47
48 #import <AppKit/NSAccessibility.h>
49
50 static QAccessibleInterface *acast(void *ptr)
51 {
52     return reinterpret_cast<QAccessibleInterface *>(ptr);
53 }
54
55 @implementation QCocoaAccessibleElement
56
57 - (id)initWithIndex:(int)aIndex parent:(id)aParent accessibleInterface:(void *)anQAccessibleInterface
58 {
59     self = [super init];
60     if (self) {
61         index = aIndex;
62         accessibleInterface = anQAccessibleInterface;
63         role = QCocoaAccessible::macRole(acast(accessibleInterface)->role());
64         parent = aParent;
65
66     }
67
68     return self;
69 }
70
71 + (QCocoaAccessibleElement *)elementWithIndex:(int)aIndex parent:(id)aParent accessibleInterface:(void *)anQAccessibleInterface
72 {
73     return [[[self alloc] initWithIndex:aIndex parent:aParent accessibleInterface:anQAccessibleInterface] autorelease];
74 }
75
76 - (void)dealloc {
77     [super dealloc];
78 }
79
80 - (BOOL)isEqual:(id)object {
81     if ([object isKindOfClass:[QCocoaAccessibleElement class]]) {
82         QCocoaAccessibleElement *other = object;
83         return (index == other->index) && [role isEqualToString:other->role] && [parent isEqual:other->parent];
84     } else
85         return NO;
86 }
87
88 - (NSUInteger)hash {
89     return [parent hash] + index;
90 }
91
92 - (NSUInteger)index {
93     return index;
94 }
95
96 //
97 // accessibility protocol
98 //
99
100 // attributes
101
102 - (NSArray *)accessibilityAttributeNames {
103     static NSArray *attributes = nil;
104     if (attributes == nil) {
105         attributes = [[NSArray alloc] initWithObjects:
106         NSAccessibilityRoleAttribute,
107         NSAccessibilityRoleDescriptionAttribute,
108         NSAccessibilityChildrenAttribute,
109         NSAccessibilityFocusedAttribute,
110         NSAccessibilityParentAttribute,
111         NSAccessibilityWindowAttribute,
112         NSAccessibilityTopLevelUIElementAttribute,
113         NSAccessibilityPositionAttribute,
114         NSAccessibilitySizeAttribute,
115         NSAccessibilityDescriptionAttribute,
116         nil];
117     }
118     return attributes;
119 }
120
121 - (id)accessibilityAttributeValue:(NSString *)attribute {
122     if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
123         return role;
124     } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
125         return NSAccessibilityRoleDescription(role, nil);
126     } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
127         int numKids = acast(accessibleInterface)->childCount();
128
129         NSMutableArray *kids = [NSMutableArray arrayWithCapacity:numKids];
130         for (int i = 0; i < numKids; ++i) {
131             QAccessibleInterface *childInterface = acast(accessibleInterface)->child(i);
132             [kids addObject:[QCocoaAccessibleElement elementWithIndex:i parent:self accessibleInterface:(void*)childInterface]];
133         }
134
135         return NSAccessibilityUnignoredChildren(kids);
136     } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
137         // Just check if the app thinks we're focused.
138         id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute];
139         return [NSNumber numberWithBool:[focusedElement isEqual:self]];
140     } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
141         return NSAccessibilityUnignoredAncestor(parent);
142     } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
143         // We're in the same window as our parent.
144         return [parent accessibilityAttributeValue:NSAccessibilityWindowAttribute];
145     } else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
146         // We're in the same top level element as our parent.
147         return [parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
148     } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
149         QPoint qtPosition = acast(accessibleInterface)->rect().topLeft();
150         QSize qtSize = acast(accessibleInterface)->rect().size();
151         return [NSValue valueWithPoint: NSMakePoint(qtPosition.x(), qt_mac_flipYCoordinate(qtPosition.y() + qtSize.height()))];
152     } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
153         QSize qtSize = acast(accessibleInterface)->rect().size();
154         return [NSValue valueWithSize: NSMakeSize(qtSize.width(), qtSize.height())];
155     } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
156         return qt_mac_QStringToNSString(acast(accessibleInterface)->text(QAccessible::Name));
157     }
158
159     return nil;
160 }
161
162 - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
163     if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
164         return NO; // YES to handle keyboard input
165     } else {
166         return NO;
167     }
168 }
169
170 - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
171     Q_UNUSED(value);
172     if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
173
174     }
175 }
176
177 // actions
178
179 - (NSArray *)accessibilityActionNames {
180     NSMutableArray * nsActions = [NSMutableArray new];
181
182     QAccessibleActionInterface *actionInterface = acast(accessibleInterface)->actionInterface();
183     if (actionInterface) {
184         QStringList supportedActionNames = actionInterface->actionNames();
185
186         foreach (const QString &qtAction, supportedActionNames) {
187             NSString *nsAction = QCocoaAccessible::getTranslatedAction(qtAction);
188             if (nsAction)
189                 [nsActions addObject : nsAction];
190         }
191     }
192
193     return nsActions;
194 }
195
196 - (NSString *)accessibilityActionDescription:(NSString *)action {
197     QAccessibleActionInterface *actionInterface = acast(accessibleInterface)->actionInterface();
198     if (actionInterface) {
199         QString qtAction = QCocoaAccessible::translateAction(action);
200         QString description = actionInterface->localizedActionDescription(qtAction);
201         if (!description.isEmpty())
202             return qt_mac_QStringToNSString(description);
203     }
204
205     return NSAccessibilityActionDescription(action);
206 }
207
208 - (void)accessibilityPerformAction:(NSString *)action {
209     QAccessibleActionInterface *actionInterface = acast(accessibleInterface)->actionInterface();
210     if (actionInterface) {
211         QString qtAction = QCocoaAccessible::translateAction(action);
212         actionInterface->doAction(QAccessibleActionInterface::pressAction());
213     }
214 }
215
216 // misc
217
218 - (BOOL)accessibilityIsIgnored {
219     return NO;
220 }
221
222 - (id)accessibilityHitTest:(NSPoint)point {
223
224     if (!accessibleInterface)
225         return NSAccessibilityUnignoredAncestor(self);
226     QAccessibleInterface *childInterface = acast(accessibleInterface)->childAt(point.x, qt_mac_flipYCoordinate(point.y));
227
228     // No child found, meaning we hit this element.
229     if (!childInterface) {
230         return NSAccessibilityUnignoredAncestor(self);
231     }
232
233     // hit a child, forward to child accessible interface.
234     int childIndex = acast(accessibleInterface)->indexOfChild(childInterface);
235     QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement elementWithIndex:childIndex  -1 parent:self accessibleInterface: childInterface];
236     return [accessibleElement accessibilityHitTest:point];
237 }
238
239 - (id)accessibilityFocusedUIElement {
240     return NSAccessibilityUnignoredAncestor(self);
241 }
242
243 @end