Cocoa: Add initial accessibility implementation.
authorMorten Sorvig <morten.sorvig@nokia.com>
Tue, 11 Oct 2011 09:04:59 +0000 (11:04 +0200)
committerQt by Nokia <qt-info@nokia.com>
Wed, 19 Oct 2011 01:04:40 +0000 (03:04 +0200)
See qcocoaaccessiblity.h for details.

For now only the first level of the hierarchy is
made accessible.

Also add tools/accessibilityinspector which is an
utility for inspecting and debugging the Qt
accessibility tree.

Change-Id: Iff520bec26b3761feb0c2e00471feb379daaa735
Reviewed-by: Morten Johan Sørvig <morten.sorvig@nokia.com>
20 files changed:
src/plugins/platforms/cocoa/cocoa.pro
src/plugins/platforms/cocoa/qcocoaaccessibility.h [new file with mode: 0644]
src/plugins/platforms/cocoa/qcocoaaccessibility.mm [new file with mode: 0644]
src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h [new file with mode: 0644]
src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm [new file with mode: 0644]
src/plugins/platforms/cocoa/qnsview.h
src/plugins/platforms/cocoa/qnsview.mm
src/plugins/platforms/cocoa/qnsviewaccessibility.mm [new file with mode: 0644]
tools/accessibilityinspector/accessibilityinspector.cpp [new file with mode: 0644]
tools/accessibilityinspector/accessibilityinspector.h [new file with mode: 0644]
tools/accessibilityinspector/accessibilityinspector.pri [new file with mode: 0644]
tools/accessibilityinspector/accessibilityinspector.pro [new file with mode: 0644]
tools/accessibilityinspector/accessibilityscenemanager.cpp [new file with mode: 0644]
tools/accessibilityinspector/accessibilityscenemanager.h [new file with mode: 0644]
tools/accessibilityinspector/main.cpp [new file with mode: 0644]
tools/accessibilityinspector/optionswidget.cpp [new file with mode: 0644]
tools/accessibilityinspector/optionswidget.h [new file with mode: 0644]
tools/accessibilityinspector/screenreader.cpp [new file with mode: 0644]
tools/accessibilityinspector/screenreader.h [new file with mode: 0644]
tools/accessibilityinspector/screenreader_mac.mm [new file with mode: 0644]

index 40d3323..80149ae 100644 (file)
@@ -7,6 +7,7 @@ OBJECTIVE_SOURCES += main.mm \
     qcocoabackingstore.mm \
     qcocoawindow.mm \
     qnsview.mm \
+    qnsviewaccessibility.mm \
     qcocoaautoreleasepool.mm \
     qnswindowdelegate.mm \
     qcocoaglcontext.mm \
@@ -19,6 +20,8 @@ OBJECTIVE_SOURCES += main.mm \
     qmenu_mac.mm \
     qcocoahelpers.mm \
     qmultitouch_mac.mm \
+    qcocoaaccessibilityelement.mm \
+    qcocoaaccessibility.mm \
 
 HEADERS += qcocoaintegration.h \
     qcocoabackingstore.h \
@@ -36,6 +39,8 @@ HEADERS += qcocoaintegration.h \
     qmenu_mac.h \
     qcocoahelpers.h \
     qmultitouch_mac_p.h \
+    qcocoaaccessibilityelement.h \
+    qcocoaaccessibility.h \
 
 RESOURCES += qcocoaresources.qrc
 
@@ -47,3 +52,7 @@ QT += core-private gui-private widgets-private platformsupport-private
 CONFIG += qpa/basicunixfontdatabase
 target.path += $$[QT_INSTALL_PLUGINS]/platforms
 INSTALLS += target
+
+# Acccessibility debug support
+# DEFINES += QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
+# include ($$PWD/../../../../tools/accessibilityinspector/accessibilityinspector.pri)
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.h b/src/plugins/platforms/cocoa/qcocoaaccessibility.h
new file mode 100644 (file)
index 0000000..7f4a840
--- /dev/null
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QCOCOAACCESIBILITY_H
+#define QCOCOAACCESIBILITY_H
+
+#include <Cocoa/Cocoa.h>
+
+#include <QtGui>
+
+/*
+    Qt Cocoa Accessibility Overview
+
+    Cocoa accessibility is implemented in the following files:
+
+    - qnsviewaccessibility            : Root accessibility implementation for QNSView
+    - qcocoaaccessibilityelement      : Cocoa accessibility protocol wrapper for QAccessibleInterface
+    - qcocoaaccessibility (this file) : Conversion and helper functions.
+
+    The accessibility implementation wraps QAccessibleInterfaces in QCocoaAccessibleElements, which
+    implements the cocoa accessibility protocol. The root QAccessibleInterface (the one returned
+    by QWindow::accessibleRoot), is anchored to the QNSView in qnsviewaccessibility.mm.
+
+    Cocoa explores the accessibility tree by walking the tree using the parent/child
+    relationships or hit testing. When this happens we create QCocoaAccessibleElements on
+    demand.
+*/
+
+NSString *macRole(QAccessible::Role);
+
+#endif
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
new file mode 100644 (file)
index 0000000..a2ce743
--- /dev/null
@@ -0,0 +1,113 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the plugins of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+#include "qcocoaaccessibility.h"
+
+typedef QMap<QAccessible::Role, NSString *> QMacAccessibiltyRoleMap;
+Q_GLOBAL_STATIC(QMacAccessibiltyRoleMap, qMacAccessibiltyRoleMap);
+
+static void populateRoleMap()
+{
+    QMacAccessibiltyRoleMap &roleMap = *qMacAccessibiltyRoleMap();
+    roleMap[QAccessible::MenuItem] = NSAccessibilityMenuItemRole;
+    roleMap[QAccessible::MenuBar] = NSAccessibilityMenuBarRole;
+    roleMap[QAccessible::ScrollBar] = NSAccessibilityScrollBarRole;
+    roleMap[QAccessible::Grip] = NSAccessibilityGrowAreaRole;
+    roleMap[QAccessible::Window] = NSAccessibilityWindowRole;
+    roleMap[QAccessible::Dialog] = NSAccessibilityWindowRole;
+    roleMap[QAccessible::AlertMessage] = NSAccessibilityWindowRole;
+    roleMap[QAccessible::ToolTip] = NSAccessibilityWindowRole;
+    roleMap[QAccessible::HelpBalloon] = NSAccessibilityWindowRole;
+    roleMap[QAccessible::PopupMenu] = NSAccessibilityMenuRole;
+    roleMap[QAccessible::Application] = NSAccessibilityApplicationRole;
+    roleMap[QAccessible::Pane] = NSAccessibilityGroupRole;
+    roleMap[QAccessible::Grouping] = NSAccessibilityGroupRole;
+    roleMap[QAccessible::Separator] = NSAccessibilitySplitterRole;
+    roleMap[QAccessible::ToolBar] = NSAccessibilityToolbarRole;
+    roleMap[QAccessible::PageTab] = NSAccessibilityRadioButtonRole;
+    roleMap[QAccessible::ButtonMenu] = NSAccessibilityMenuButtonRole;
+    roleMap[QAccessible::ButtonDropDown] = NSAccessibilityPopUpButtonRole;
+    roleMap[QAccessible::SpinBox] = NSAccessibilityIncrementorRole;
+    roleMap[QAccessible::Slider] = NSAccessibilitySliderRole;
+    roleMap[QAccessible::ProgressBar] = NSAccessibilityProgressIndicatorRole;
+    roleMap[QAccessible::ComboBox] = NSAccessibilityPopUpButtonRole;
+    roleMap[QAccessible::RadioButton] = NSAccessibilityRadioButtonRole;
+    roleMap[QAccessible::CheckBox] = NSAccessibilityCheckBoxRole;
+    roleMap[QAccessible::StaticText] = NSAccessibilityStaticTextRole;
+    roleMap[QAccessible::Table] = NSAccessibilityTableRole;
+    roleMap[QAccessible::StatusBar] = NSAccessibilityStaticTextRole;
+    roleMap[QAccessible::Column] = NSAccessibilityColumnRole;
+    roleMap[QAccessible::ColumnHeader] = NSAccessibilityColumnRole;
+    roleMap[QAccessible::Row] = NSAccessibilityRowRole;
+    roleMap[QAccessible::RowHeader] = NSAccessibilityRowRole;
+    roleMap[QAccessible::Cell] = NSAccessibilityTextFieldRole;
+    roleMap[QAccessible::PushButton] = NSAccessibilityButtonRole;
+    roleMap[QAccessible::EditableText] = NSAccessibilityTextFieldRole;
+    roleMap[QAccessible::Link] = NSAccessibilityTextFieldRole;
+    roleMap[QAccessible::Indicator] = NSAccessibilityValueIndicatorRole;
+    roleMap[QAccessible::Splitter] = NSAccessibilitySplitGroupRole;
+    roleMap[QAccessible::List] = NSAccessibilityListRole;
+    roleMap[QAccessible::ListItem] = NSAccessibilityStaticTextRole;
+    roleMap[QAccessible::Cell] = NSAccessibilityStaticTextRole;
+    roleMap[QAccessible::Client] = NSAccessibilityGroupRole;
+}
+
+/*
+    Returns a Mac accessibility role for the given interface, or
+    NSAccessibilityUnknownRole if no role mapping is found.
+*/
+NSString *macRole(QAccessible::Role qtRole)
+{
+    QMacAccessibiltyRoleMap &roleMap = *qMacAccessibiltyRoleMap();
+
+    if (roleMap.isEmpty())
+        populateRoleMap();
+
+    // MAC_ACCESSIBILTY_DEBUG() << "role for" << interface.object() << "interface role" << hex << qtRole;
+
+    if (roleMap.contains(qtRole)) {
+       // MAC_ACCESSIBILTY_DEBUG() << "return" <<  roleMap[qtRole];
+        return roleMap[qtRole];
+    }
+
+    // MAC_ACCESSIBILTY_DEBUG() << "return NSAccessibilityUnknownRole";
+    return NSAccessibilityUnknownRole;
+}
+
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h
new file mode 100644 (file)
index 0000000..76509f9
--- /dev/null
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QCOCOAACCESIBILITYELEMENT_H
+#define QCOCOAACCESIBILITYELEMENT_H
+
+
+#import <Cocoa/Cocoa.h>
+#import <AppKit/NSAccessibility.h>
+
+@class QCocoaAccessibleElement;
+
+@interface QCocoaAccessibleElement : NSObject {
+    NSUInteger index;
+    NSString *role;
+    NSObject * parent;
+    void *accessibleInterface;
+
+}
+
+- (id)initWithIndex:(int)aIndex parent:(id)aParent accessibleInterface:(void *)anQAccessibleInterface;
++ (QCocoaAccessibleElement *)elementWithIndex:(int)aIndex parent:(id)aParent accessibleInterface:(void *)anQAccessibleInterface;
+- (NSUInteger)index;
+
+@end
+
+#endif
+
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
new file mode 100644 (file)
index 0000000..4ce4130
--- /dev/null
@@ -0,0 +1,197 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the plugins of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+#include "qcocoaaccessibilityelement.h"
+#include "qcocoaaccessibility.h"
+#include "qcocoahelpers.h"
+
+#include <QAccessible>
+#include "QAccessibleActionInterface"
+
+#import <AppKit/NSAccessibility.h>
+
+static QAccessibleInterface *acast(void *ptr)
+{
+    return reinterpret_cast<QAccessibleInterface *>(ptr);
+}
+
+@implementation QCocoaAccessibleElement
+
+- (id)initWithIndex:(int)aIndex parent:(id)aParent accessibleInterface:(void *)anQAccessibleInterface
+{
+    self = [super init];
+    if (self) {
+        index = aIndex;
+        accessibleInterface = anQAccessibleInterface;
+        role = macRole(acast(accessibleInterface)->role());
+        parent = aParent;
+
+    }
+
+    return self;
+}
+
++ (QCocoaAccessibleElement *)elementWithIndex:(int)aIndex parent:(id)aParent accessibleInterface:(void *)anQAccessibleInterface
+{
+    return [[[self alloc] initWithIndex:aIndex parent:aParent accessibleInterface:anQAccessibleInterface] autorelease];
+}
+
+- (void)dealloc {
+    [super dealloc];
+}
+
+- (BOOL)isEqual:(id)object {
+    if ([object isKindOfClass:[QCocoaAccessibleElement class]]) {
+        QCocoaAccessibleElement *other = object;
+        return (index == other->index) && [role isEqualToString:other->role] && [parent isEqual:other->parent];
+    } else
+        return NO;
+}
+
+- (NSUInteger)hash {
+    return [parent hash] + index;
+}
+
+- (NSUInteger)index {
+    return index;
+}
+
+//
+// accessibility protocol
+//
+
+// attributes
+
+- (NSArray *)accessibilityAttributeNames {
+    static NSArray *attributes = nil;
+    if (attributes == nil) {
+        attributes = [[NSArray alloc] initWithObjects:
+        NSAccessibilityRoleAttribute,
+        NSAccessibilityRoleDescriptionAttribute,
+        NSAccessibilityFocusedAttribute,
+        NSAccessibilityParentAttribute,
+        NSAccessibilityWindowAttribute,
+        NSAccessibilityTopLevelUIElementAttribute,
+        NSAccessibilityPositionAttribute,
+        NSAccessibilitySizeAttribute,
+        NSAccessibilityDescriptionAttribute,
+        nil];
+    }
+    return attributes;
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute {
+    if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
+        return role;
+    } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
+        return NSAccessibilityRoleDescription(role, nil);
+    } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
+        // Just check if the app thinks we're focused.
+        id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute];
+        return [NSNumber numberWithBool:[focusedElement isEqual:self]];
+    } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
+        return NSAccessibilityUnignoredAncestor(parent);
+    } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
+        // We're in the same window as our parent.
+        return [parent accessibilityAttributeValue:NSAccessibilityWindowAttribute];
+    } else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
+        // We're in the same top level element as our parent.
+        return [parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
+    } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
+        QPoint qtPosition = acast(accessibleInterface)->rect().topLeft();
+        QSize qtSize = acast(accessibleInterface)->rect().size();
+        return [NSValue valueWithPoint: NSMakePoint(qtPosition.x(), qt_mac_flipYCoordinate(qtPosition.y() + qtSize.height()))];
+    } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
+        QSize qtSize = acast(accessibleInterface)->rect().size();
+        return [NSValue valueWithSize: NSMakeSize(qtSize.width(), qtSize.height())];
+    } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
+        return qt_mac_QStringToNSString(acast(accessibleInterface)->text(QAccessible::Name));
+    }
+
+    return nil;
+}
+
+- (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
+    if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
+        return NO; // YES to handle keyboard input
+    } else {
+        return NO;
+    }
+}
+
+- (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
+    Q_UNUSED(value);
+    if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
+
+    }
+}
+
+// actions
+
+- (NSArray *)accessibilityActionNames {
+    return [NSArray arrayWithObject:NSAccessibilityPressAction];
+}
+
+- (NSString *)accessibilityActionDescription:(NSString *)action {
+    return NSAccessibilityActionDescription(action);
+}
+
+- (void)accessibilityPerformAction:(NSString *)action {
+    Q_UNUSED(action);
+    if (acast(accessibleInterface)->actionInterface())
+        acast(accessibleInterface)->actionInterface()->doAction(0);
+}
+
+// misc
+
+- (BOOL)accessibilityIsIgnored {
+    return NO;
+}
+
+- (id)accessibilityHitTest:(NSPoint)point {
+    Q_UNUSED(point);
+    return NSAccessibilityUnignoredAncestor(self);
+}
+
+- (id)accessibilityFocusedUIElement {
+    return NSAccessibilityUnignoredAncestor(self);
+}
+
+@end
index 0b96928..eddc1aa 100644 (file)
 #include <Cocoa/Cocoa.h>
 
 #include <QtGui/QImage>
+#include <QtGui/QAccessible>
 
 @interface QNSView : NSView {
     CGImageRef m_cgImage;
     QWindow *m_window;
     Qt::MouseButtons m_buttons;
+    QAccessibleInterface *m_accessibleRoot;
 }
 
 - (id)init;
index 67bc61c..664b876 100644 (file)
 #include <QtGui/QWindowSystemInterface>
 #include <QtCore/QDebug>
 
+#ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
+#include <accessibilityinspector.h>
+#endif
+
 @interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
   - (CGFloat)deviceDeltaX;
   - (CGFloat)deviceDeltaY;
@@ -58,7 +62,7 @@
 
 - (id) init
 {
-    self = [super init];
+    self = [super initWithFrame : NSMakeRect(0,0, 300,300)];
     if (self) {
         m_cgImage = 0;
         m_window = 0;
     return self;
 }
 
-- (id)initWithQWindow:(QWindow *)widget {
+- (id)initWithQWindow:(QWindow *)window {
     self = [self init];
-    if (self) {
-        m_window = widget;
+    if (!self)
+        return 0;
+
+    m_window = window;
+    m_accessibleRoot = 0;
+
+#ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
+    // prevent rift in space-time continuum, disable
+    // accessibility for the accessibility inspector's windows.
+    static bool skipAccessibilityForInspectorWindows = false;
+    if (!skipAccessibilityForInspectorWindows) {
+
+        m_accessibleRoot = window->accessibleRoot();
+
+        AccessibilityInspector *inspector = new AccessibilityInspector(window);
+        skipAccessibilityForInspectorWindows = true;
+        inspector->inspectWindow(window);
+        skipAccessibilityForInspectorWindows = false;
     }
+#else
+    m_accessibleRoot = window->accessibleRoot();
+#endif
+
     return self;
 }
 
diff --git a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm
new file mode 100644 (file)
index 0000000..327bace
--- /dev/null
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <Carbon/Carbon.h>
+
+#include "qnsview.h"
+#include "qcocoahelpers.h"
+#include "qcocoaaccessibility.h"
+#include "qcocoaaccessibilityelement.h"
+
+#include "QAccessibleActionInterface"
+#include <QtCore/QDebug>
+
+#import <AppKit/NSAccessibility.h>
+
+@implementation QNSView (QNSViewAccessibility)
+
+- (BOOL)accessibilityIsIgnored {
+    return NO;
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute {
+    if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
+        if (m_accessibleRoot)
+            return macRole(m_accessibleRoot->role());
+        return NSAccessibilityUnknownRole;
+    } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
+        return NSAccessibilityRoleDescriptionForUIElement(self);
+    } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
+        if (!m_accessibleRoot)
+            return [super accessibilityAttributeValue:attribute];
+
+        // Create QCocoaAccessibleElements for each child if the
+        // root accessible interface.
+        int numKids = m_accessibleRoot->childCount();
+        NSMutableArray *kids = [NSMutableArray arrayWithCapacity:numKids];
+        for (int i = 0; i < numKids; ++i) {
+            [kids addObject:[QCocoaAccessibleElement elementWithIndex:i parent:self accessibleInterface:(void*)m_accessibleRoot->child(i)]];
+        }
+
+        return NSAccessibilityUnignoredChildren(kids);
+    } else {
+        return [super accessibilityAttributeValue:attribute];
+    }
+}
+
+- (id)accessibilityHitTest:(NSPoint)point {
+    NSPoint windowPoint = [[self window] convertScreenToBase:point];
+    NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
+
+    int index = -1;
+    if (m_accessibleRoot) {
+        index = m_accessibleRoot->childAt(point.x, qt_mac_flipYCoordinate(point.y));
+
+        // qDebug() << "root rect" << m_accessibleRoot->rect();
+        // qDebug() << "hit screen" << point.x << qt_mac_flipYCoordinate(point.y)  << index;
+        // if (index > 0) {
+        //      qDebug() << "child name" << m_accessibleRoot->child(index - 1)->text(QAccessible::Name);
+        //      qDebug() << "child rect" << m_accessibleRoot->child(index - 1)->rect();
+        // }
+    }
+
+    // hit outside
+    if (index == -1) {
+        return [super accessibilityHitTest:point];
+    }
+
+    // hit the NSView / top-level window
+    if (index == 0) {
+        QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement elementWithIndex:index parent:self accessibleInterface:(void*)m_accessibleRoot];
+        return [accessibleElement accessibilityHitTest:point];
+    }
+
+    // hit a child, forward to child accessible interface.
+    QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement elementWithIndex:index - 1 parent:self accessibleInterface:(void*)m_accessibleRoot->child(index -1)];
+    return [accessibleElement accessibilityHitTest:point];
+}
+
+@end
diff --git a/tools/accessibilityinspector/accessibilityinspector.cpp b/tools/accessibilityinspector/accessibilityinspector.cpp
new file mode 100644 (file)
index 0000000..4f65f2b
--- /dev/null
@@ -0,0 +1,189 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the tools applications of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#include "accessibilityinspector.h"
+
+#include "screenreader.h"
+#include "optionswidget.h"
+#include "accessibilityscenemanager.h"
+
+#include <QtDeclarative/QtDeclarative>
+
+void MouseInterceptingGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+    emit mousePressed(event->scenePos().toPoint());
+    QGraphicsScene::mousePressEvent(event);
+}
+
+void MouseInterceptingGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+    emit mouseDobleClicked();
+    QGraphicsScene::mouseDoubleClickEvent(event);
+}
+
+AccessibilitySceneManager *sceneManager = 0;
+QAccessible::UpdateHandler previousUpdateHandler = 0;
+bool updateHandlerRecursion = false;
+void accessibilityUpdateHandler(QObject *object, int who, QAccessible::Event reason)
+{
+    if (updateHandlerRecursion)
+        return;
+
+    if (!qobject_cast<QSGItem *>(object))
+        return;
+
+    updateHandlerRecursion = true;
+
+    if (sceneManager) {
+        sceneManager->handleUpdate(object, reason);
+
+        //qDebug() << "update";
+    }
+
+    if (previousUpdateHandler) // call prev just to be sure.
+        previousUpdateHandler(object, who, reason);
+
+    updateHandlerRecursion = false;
+}
+
+AccessibilityInspector::AccessibilityInspector(QObject *parent) :
+    QObject(parent)
+{
+}
+
+AccessibilityInspector::~AccessibilityInspector()
+{
+    delete optionsWidget;
+    delete accessibilityScene;
+    delete accessibilityView;
+    delete accessibilityTreeScene;
+    delete accessibilityTreeView;
+    delete screenReader;
+}
+
+void AccessibilityInspector::inspectWindow(QWindow *window)
+{
+    qDebug() << "AccessibilityInspector::inspectWindow()" << window;
+
+    optionsWidget = new OptionsWidget();
+
+    accessibilityScene = new MouseInterceptingGraphicsScene();
+
+    accessibilityView = new QGraphicsView();
+    accessibilityView->setScene(accessibilityScene);
+    accessibilityView->resize(1024, 768);
+    accessibilityView->scale(1.3, 1.3);
+
+    accessibilityTreeScene = new QGraphicsScene();
+
+    accessibilityTreeView = new QGraphicsView();
+    accessibilityTreeView->setScene(accessibilityTreeScene);
+    accessibilityTreeView->resize(1024, 768);
+
+    sceneManager = new AccessibilitySceneManager();
+    QObject::connect(optionsWidget, SIGNAL(optionsChanged()), sceneManager, SLOT(updateAccessibilitySceneItemFlags()));
+    QObject::connect(optionsWidget, SIGNAL(refreshClicked()), sceneManager, SLOT(populateAccessibilityScene()));
+    QObject::connect(optionsWidget, SIGNAL(refreshClicked()), sceneManager, SLOT(populateAccessibilityTreeScene()));
+    QObject::connect(optionsWidget, SIGNAL(scaleChanged(int)), sceneManager, SLOT(changeScale(int)));
+
+    sceneManager->setOptionsWidget(optionsWidget);
+    sceneManager->setRootWindow(window);
+    sceneManager->setScene(accessibilityScene);
+    sceneManager->setView(accessibilityView);
+    sceneManager->setTreeScene(accessibilityTreeScene);
+    sceneManager->setTreeView(accessibilityTreeView);
+
+    screenReader = new ScreenReader;
+    QObject::connect(accessibilityScene, SIGNAL(mousePressed(QPoint)), screenReader, SLOT(touchPoint(QPoint)));
+    QObject::connect(accessibilityScene, SIGNAL(mouseDobleClicked()), screenReader, SLOT(activate()));
+    QObject::connect(screenReader, SIGNAL(selected(QObject*)), sceneManager, SLOT(setSelected(QObject*)));
+    screenReader->setRootObject(window);
+    screenReader->setOptionsWidget(optionsWidget);
+
+    previousUpdateHandler = QAccessible::installUpdateHandler(accessibilityUpdateHandler);
+
+    QTimer::singleShot(100, sceneManager, SLOT(populateAccessibilityScene()));
+    QTimer::singleShot(100, sceneManager, SLOT(populateAccessibilityTreeScene()));
+
+    QSettings settings;
+    accessibilityView->restoreGeometry(settings.value("accessiblityGeometry").toByteArray());
+    accessibilityView->setObjectName(QLatin1String("accessibilityInspectorView"));
+    accessibilityView->show();
+
+
+    accessibilityTreeView->restoreGeometry(settings.value("treeGeometry").toByteArray());
+    accessibilityTreeView->setObjectName(QLatin1String("accessibilityInspectorTreeView"));
+    accessibilityTreeView->show();
+    optionsWidget->restoreGeometry(settings.value("optionsGeometry").toByteArray());
+    optionsWidget->setObjectName(QLatin1String("accessibilityInspectorOptions"));
+    optionsWidget->show();
+}
+
+void AccessibilityInspector::saveWindowGeometry()
+{
+    QSettings settings;
+    settings.setValue("accessiblityGeometry", accessibilityView->saveGeometry());
+    settings.setValue("treeGeometry", accessibilityTreeView->saveGeometry());
+    settings.setValue("optionsGeometry", optionsWidget->saveGeometry());
+}
+
+QString translateRole(QAccessible::Role role)
+{
+    if (role == 0x2B)
+        return "PushButton";
+    if (role == 0x2C)
+        return "CheckBox";
+    if (role == 0x2D)
+        return "RadioButton";
+    if (role == 0xA)
+        return "Client";
+    if (role == 0x29)
+        return "Static Text";
+    if (role == 0x33)
+        return "Slider";
+    if (role == 0x33)
+        return "Slider";
+    if (role == 0x10)
+        return "Pane";
+
+    return QString::number(role, 16);
+}
+
diff --git a/tools/accessibilityinspector/accessibilityinspector.h b/tools/accessibilityinspector/accessibilityinspector.h
new file mode 100644 (file)
index 0000000..504fecd
--- /dev/null
@@ -0,0 +1,89 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the tools applications of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#ifndef ACCESSIBILITYINSPECTOR_H
+#define ACCESSIBILITYINSPECTOR_H
+
+#include <QObject>
+#include <qgraphicsscene.h>
+#include <QAccessible>
+
+QString translateRole(QAccessible::Role role);
+
+class OptionsWidget;
+class MouseInterceptingGraphicsScene;
+class QGraphicsView;
+class QGraphicsScene;
+class AccessibilitySceneManager;
+class ScreenReader;
+class AccessibilityInspector : public QObject
+{
+    Q_OBJECT
+public:
+    explicit AccessibilityInspector(QObject *parent = 0);
+    ~AccessibilityInspector();
+    void inspectWindow(QWindow *window);
+    void saveWindowGeometry();
+signals:
+
+public slots:
+
+private:
+    OptionsWidget *optionsWidget;
+    MouseInterceptingGraphicsScene *accessibilityScene;
+    QGraphicsView *accessibilityView;
+    QGraphicsScene *accessibilityTreeScene;
+    QGraphicsView *accessibilityTreeView;
+    ScreenReader *screenReader;
+};
+
+class MouseInterceptingGraphicsScene : public QGraphicsScene
+{
+Q_OBJECT
+protected:
+    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+    virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+signals:
+    void mousePressed(const QPoint point);
+    void mouseDobleClicked();
+};
+
+#endif // ACCESSIBILITYINSPECTOR_H
diff --git a/tools/accessibilityinspector/accessibilityinspector.pri b/tools/accessibilityinspector/accessibilityinspector.pri
new file mode 100644 (file)
index 0000000..dc1f150
--- /dev/null
@@ -0,0 +1,25 @@
+QT += declarative
+INCLUDEPATH += $$PWD
+
+# DEFINES += ACCESSIBILITYINSPECTOR_NO_UITOOLS
+# CONFIG += uitools
+
+mac {
+    # for text-to-speach
+    LIBS += -framework AppKit
+}
+
+HEADERS += \
+    $$PWD/screenreader.h \
+    $$PWD/optionswidget.h \
+    $$PWD/accessibilityscenemanager.h \
+    $$PWD/accessibilityinspector.h
+SOURCES += \
+    $$PWD/optionswidget.cpp \
+    $$PWD/accessibilityscenemanager.cpp \
+    $$PWD/screenreader.cpp \
+    $$PWD/accessibilityinspector.cpp
+
+OBJECTIVE_SOURCES += $$PWD/screenreader_mac.mm
+
+
diff --git a/tools/accessibilityinspector/accessibilityinspector.pro b/tools/accessibilityinspector/accessibilityinspector.pro
new file mode 100644 (file)
index 0000000..f2d8a3f
--- /dev/null
@@ -0,0 +1,5 @@
+include (accessibilityinspector.pri)
+
+SOURCES += main.cpp \
+
+CONFIG += console
diff --git a/tools/accessibilityinspector/accessibilityscenemanager.cpp b/tools/accessibilityinspector/accessibilityscenemanager.cpp
new file mode 100644 (file)
index 0000000..aa0b93d
--- /dev/null
@@ -0,0 +1,487 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the tools applications of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#include "accessibilityscenemanager.h"
+
+AccessibilitySceneManager::AccessibilitySceneManager()
+{
+    m_window = 0;
+    m_view = 0;
+    m_scene = 0;
+    m_rootItem = 0;
+    m_optionsWidget = 0;
+    m_selectedObject = 0;
+}
+
+void AccessibilitySceneManager::populateAccessibilityScene()
+{
+    m_scene->clear();
+    m_graphicsItems.clear();
+
+    QAccessibleInterface * rootInterface = m_window->accessibleRoot();
+    if (!rootInterface)
+        return;
+
+    populateAccessibilityScene(rootInterface, 0, m_scene);
+}
+
+void AccessibilitySceneManager::updateAccessibilitySceneItemFlags()
+{
+    qDebug() << "update";
+    foreach (QObject *object, m_graphicsItems.keys()) {
+        if (!object)
+            continue;
+        QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
+        if (!interface)
+            continue;
+        updateItemFlags(m_graphicsItems.value(object), interface);
+        delete interface;
+    }
+}
+
+void AccessibilitySceneManager::populateAccessibilityTreeScene()
+{
+    m_treeScene->clear();
+    QAccessibleInterface * rootInterface = m_window->accessibleRoot();
+    if (!rootInterface)
+        return;
+
+    populateAccessibilityTreeScene(rootInterface, 0);
+}
+
+void AccessibilitySceneManager::handleUpdate(QObject *object, QAccessible::Event reason)
+{
+    QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
+    if (!interface)
+        return;
+
+    QString name = interface->text(QAccessible::Name, 0);
+
+    if (reason == QAccessible::ObjectCreated) {
+  //      qDebug() << "ObjectCreated" << object << name;
+        populateAccessibilityScene(interface, 0, m_scene);
+    }
+
+    QGraphicsRectItem *item = m_graphicsItems.value(object);
+
+    if (!item) {
+//        qDebug() << "populateAccessibilityScene failed for" << object;
+        return;
+    }
+
+    if (reason == QAccessible::LocationChanged) {
+
+        //if (name.startsWith("List"))
+            qDebug() << "locationChange" << object << name << interface->rect(0);
+
+        updateItem(item, interface);
+        for (int i = 0; i < interface->childCount(); ++i) {
+           QAccessibleInterface *child = 0;
+           int ret = interface->navigate(QAccessible::Child, i + 1, &child);
+           if (ret == 0 && child) {
+               updateItem(m_graphicsItems.value(child->object()), child);
+               delete child;
+            }
+        }
+
+        delete interface;
+    } else if (reason == QAccessible::ObjectDestroyed) {
+//        qDebug() << "ObjectDestroyed" << object << name;
+        delete m_graphicsItems.value(object);
+        m_graphicsItems.remove(object);
+        m_animatedObjects.remove(object);
+        if (object == m_selectedObject) {
+            m_selectedObject = 0;
+        }
+    } else if (reason == QAccessible::ObjectHide) {
+//        qDebug() << "ObjectCreated Hide" << object;
+        updateItemFlags(item, interface);
+    } else if (reason == QAccessible::ObjectShow) {
+//        qDebug() << "ObjectCreated Show" << object;
+        updateItemFlags(item, interface);
+    } else if (reason == QAccessible::ScrollingStart) {
+        qDebug() << "ObjectCreated ScrollingStart" << object;
+        QAccessibleInterface *child = 0;
+        for (int i = 0; i < interface->childCount(); ++i) {
+            int ret = interface->navigate(QAccessible::Child, i + 1, &child);
+            if (ret == 0 && child) {
+                m_animatedObjects.insert(child->object());
+                delete child;
+            }
+        }
+    } else if (reason == QAccessible::ScrollingEnd) {
+        // qDebug() << "ObjectCreated ScrollingEnd" << object;
+        foreach (QObject *object, m_animatedObjects) {
+            updateItem(m_graphicsItems.value(object), interface);
+        }
+        delete interface;
+        m_animatedObjects.clear();
+
+    } else {
+        qDebug() << "other update" << object;
+    }
+}
+
+void AccessibilitySceneManager::setSelected(QObject *object)
+{
+    m_scene->update(); // scedule update
+
+    // clear existing selection
+    if (m_selectedObject) {
+        QObject *previousSelectedObject = m_selectedObject;
+        m_selectedObject = 0;
+        updateItem(previousSelectedObject);
+    }
+
+    m_selectedObject = object;
+    updateItem(object);
+
+    populateAccessibilityTreeScene();
+}
+
+void AccessibilitySceneManager::changeScale(int)
+{
+    // No QGraphicsView::setScale :(
+
+    //m_view->scale(scale / 10.0, scale / 10.0);
+    //if (m_rootItem)
+    //    m_view->ensureVisible(m_rootItem);
+}
+
+void AccessibilitySceneManager::updateItems(QObject *root)
+{
+    QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(root);
+    if (!interface)
+        return;
+    updateItem(m_graphicsItems.value(root), interface);
+
+    for (int i = 0; i < interface->childCount(); ++i) {
+        QAccessibleInterface *child = interface->child(i);
+        updateItems(child->object());
+        delete child;
+    }
+
+    delete interface;
+}
+
+void AccessibilitySceneManager::updateItem(QObject *object)
+{
+    if (!object)
+        return;
+
+    QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
+    if (!interface)
+        return;
+
+    updateItem(m_graphicsItems.value(object), interface);
+
+    delete interface;
+}
+
+void AccessibilitySceneManager::updateItem(QGraphicsRectItem *item, QAccessibleInterface *interface)
+{
+    if (!item)
+        return;
+
+    QRect rect = interface->rect(0);
+    item->setPos(rect.topLeft());
+    item->setRect(QRect(QPoint(0,0), rect.size()));
+
+    updateItemFlags(item, interface);
+}
+
+void AccessibilitySceneManager::updateItemFlags(QGraphicsRectItem *item, QAccessibleInterface *interface)
+{
+  //  qDebug() << "udpateItemFlags" << interface << interface->object();
+
+    bool shouldShow = true;
+
+    if (m_optionsWidget->hideInvisibleItems()) {
+        if (isHidden(interface)) {
+            shouldShow = false;
+        }
+    }
+
+    if (m_optionsWidget->hideOffscreenItems()) {
+        if (interface->state(0) & QAccessible::Offscreen) {
+            shouldShow = false;
+        }
+    }
+
+    if (m_optionsWidget->hidePaneItems()) {
+        if (interface->role(0) & QAccessible::Pane) {
+            shouldShow = false;
+        }
+    }
+
+    item->setVisible(shouldShow);
+
+    if (interface->object() == m_selectedObject)
+        item->setBrush(QColor(Qt::yellow));
+    else
+        item->setBrush(QColor(Qt::white));
+
+    m_view->update();
+}
+
+QGraphicsRectItem * AccessibilitySceneManager::processInterface(QAccessibleInterface * interface, int child, QGraphicsScene *scene)
+{
+    // Process this interface
+
+    QGraphicsRectItem * item = new QGraphicsRectItem();
+    scene->addItem(item);
+    if (!m_rootItem)
+        m_rootItem = item;
+
+    QString name = interface->text(QAccessibleInterface::Name, child);
+    QString description; // = interface->text(QAccessibleInterface::Description, child);
+    QString role = translateRole(interface->role(child));
+    int childCount = interface->childCount();
+
+    /* qDebug() << "name:" << name << "local pos" <<
+               interface->rect(0) << "description" << description << "childCount" << childCount;
+*/
+
+    updateItem(item, interface);
+
+    QGraphicsSimpleTextItem * textItem = new QGraphicsSimpleTextItem();
+    textItem->setParentItem(item);
+    textItem->setPos(QPoint(5, 5));
+
+    QString text;
+    text.append("Name: " + name + " ");
+    if (!description.isEmpty())
+        text.append("Description: " + description + " ");
+    text.append("Role: " + role + " ");
+    if (childCount > 0)
+        text.append("ChildCount: " + QString::number(childCount) + " ");
+    textItem->setText(text);
+
+    QFont font;
+    font.setPointSize(10);
+ //   font.setPointSize(14);
+    textItem->setFont(font);
+
+    return item;
+}
+
+void AccessibilitySceneManager::populateAccessibilityScene(QAccessibleInterface * interface, int child, QGraphicsScene *scene)
+{
+    if (!interface)
+        return;
+
+    QGraphicsRectItem *item = processInterface(interface, child, scene);
+
+    QObject *object = interface->object();
+    if (object) {
+        m_graphicsItems.insert(object, item);
+    }
+
+    // Possibly process children
+    if (child != 0)
+        return;
+
+    for (int i = 0; i < interface->childCount(); ++i) {
+        QAccessibleInterface *child = interface->child(i);
+        updateItems(child->object());
+        populateAccessibilityScene(child, 0, scene);
+        delete child;
+    }
+}
+
+AccessibilitySceneManager::TreeItem AccessibilitySceneManager::computeLevels(QAccessibleInterface * interface, int level)
+{
+    if (interface == 0)
+        return TreeItem();
+
+    TreeItem currentLevel;
+
+    int usedChildren = 0;
+    for (int i = 0; i < interface->childCount(); ++i) {
+        QAccessibleInterface *child = interface->child(i);
+        if (child != 0) {
+            ++usedChildren;
+            TreeItem childLevel = computeLevels(child, level + 1);
+            currentLevel.children.append(childLevel);
+            currentLevel.width += childLevel.width + m_treeItemHorizontalPadding;
+            delete child;
+        }
+    }
+
+    // leaf node case
+    if (usedChildren == 0) {
+        currentLevel.width = m_treeItemWidth + m_treeItemHorizontalPadding;
+    }
+
+    // capture information:
+    currentLevel.name = interface->text(QAccessible::Name, 0);
+    //currentLevel.description += interface->text(QAccessible::DebugDescription, 0);
+    currentLevel.role = translateRole(interface->role(0));
+    currentLevel.rect = interface->rect(0);
+    currentLevel.state = interface->state(0);
+    currentLevel.object = interface->object();
+
+    return currentLevel;
+}
+
+void AccessibilitySceneManager::populateAccessibilityTreeScene(QAccessibleInterface * interface, int child)
+{
+    if (!interface)
+        return;
+
+    // set some layout metrics:
+    m_treeItemWidth = 90;
+    m_treeItemHorizontalPadding = 10;
+    m_treeItemHeight = 60;
+    m_treeItemVerticalPadding = 30;
+
+    // We want to draw the accessibility hiearchy as a vertical
+    // tree, growing from the root node at the top.
+
+    // First, figure out the number of levels and the width of each level:
+    m_rootTreeItem = computeLevels(interface, 0);
+
+    // create graphics items for each tree item
+    addGraphicsItems(m_rootTreeItem, 0, 0);
+}
+
+void AccessibilitySceneManager::addGraphicsItems(AccessibilitySceneManager::TreeItem item, int row, int xPos)
+{
+    //qDebug() << "add graphics item" << row << item.name << item.role << xPos << item.width << item.children.count();
+
+    int yPos = row * (m_treeItemHeight + m_treeItemVerticalPadding);
+
+    // Process this interface
+    QGraphicsRectItem * graphicsItem = new QGraphicsRectItem();
+    graphicsItem->setPos(xPos, yPos);
+    graphicsItem->setRect(0, 0, m_treeItemWidth, m_treeItemHeight);
+    graphicsItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
+
+    if (item.object == m_selectedObject)
+        graphicsItem->setBrush(QColor(Qt::yellow));
+    else
+        graphicsItem->setBrush(QColor(Qt::white));
+
+    if (item.state & QAccessible::Invisible) {
+        QPen linePen;
+        linePen.setStyle(Qt::DashLine);
+        graphicsItem->setPen(linePen);
+    }
+
+    m_treeScene->addItem(graphicsItem);
+
+    QGraphicsTextItem * textItem = new QGraphicsTextItem();
+    textItem->setParentItem(graphicsItem);
+    textItem->setPos(QPoint(0, 0));
+
+    QFont font;
+    font.setPointSize(8);
+    textItem->setFont(font);
+
+    QString text;
+    text += item.name + "\n";
+    text += item.role + "\n";
+    text += item.description.split(" ", QString::SkipEmptyParts).join("\n") + "\n";
+    text += "P:" + QString::number(item.rect.x()) + " " + QString::number(item.rect.y()) + " ";
+    text += "S:" + QString::number(item.rect.width()) + " " + QString::number(item.rect.height()) + "\n";
+
+    textItem->setPlainText(text);
+
+    // recurse to children
+    int childIndex = 0;
+    int childCount = item.children.count();
+    int segmentSize = item.width / qMax(1, childCount);
+    int segmentCenterOffset = segmentSize / 2;
+    int segmentsStart = xPos - (item.width / 2);
+    foreach (TreeItem child, item.children) {
+        // spread the children out, covering the width, centered on xPos
+        int segmentPosition = segmentsStart + (segmentSize * childIndex) + segmentCenterOffset;
+        addGraphicsItems(child, row + 1, segmentPosition);
+        ++childIndex;
+    }
+
+    // add lines from parents to kids
+    int boxBottom = yPos + m_treeItemHeight;
+    int boxMiddleX = xPos + m_treeItemWidth / 2;
+    int yBottomMiddle = boxBottom + m_treeItemVerticalPadding / 2;
+    int boxTop = yPos;
+    int yTopMiddle = boxTop - m_treeItemVerticalPadding / 2;
+
+    if (row > 0) {
+        QGraphicsLineItem *childVerticalStem = new QGraphicsLineItem();
+        childVerticalStem->setLine(boxMiddleX, yTopMiddle, boxMiddleX, boxTop);
+        m_treeScene->addItem(childVerticalStem);
+    }
+
+    if (childCount > 0) {
+        QGraphicsLineItem *parentVerticalStem = new QGraphicsLineItem();
+        parentVerticalStem->setLine(boxMiddleX, boxBottom, boxMiddleX, yBottomMiddle);
+        m_treeScene->addItem(parentVerticalStem);
+    }
+
+    if (childCount > 1) {
+        QGraphicsLineItem *horizontalStem = new QGraphicsLineItem();
+        // match the end points with the horizontal lines
+        int lineStartX = segmentsStart + segmentCenterOffset + m_treeItemWidth / 2;
+        int lineStopX = segmentsStart + segmentSize * (childCount -1) + segmentCenterOffset + m_treeItemWidth / 2;
+        horizontalStem->setLine(lineStartX, yBottomMiddle, lineStopX , yBottomMiddle);
+        m_treeScene->addItem(horizontalStem);
+    }
+}
+
+bool AccessibilitySceneManager::isHidden(QAccessibleInterface *interface)
+{
+    QAccessibleInterface *current = interface;
+    while (current) {
+
+        if (current->state(0) & QAccessible::Invisible) {
+            return true;
+        }
+
+        QAccessibleInterface *parent = current->parent();
+
+        if (current != interface)
+            delete current;
+        current = parent;
+    }
+
+    return false;
+}
diff --git a/tools/accessibilityinspector/accessibilityscenemanager.h b/tools/accessibilityinspector/accessibilityscenemanager.h
new file mode 100644 (file)
index 0000000..108f8f2
--- /dev/null
@@ -0,0 +1,117 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the tools applications of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#ifndef ACCESSIBILITYSCENEMANAGER_H
+#define ACCESSIBILITYSCENEMANAGER_H
+
+#include <QtGui>
+
+#include "optionswidget.h"
+
+QString translateRole(QAccessible::Role role);
+class AccessibilitySceneManager : public QObject
+{
+Q_OBJECT
+public:
+    AccessibilitySceneManager();
+    void setRootWindow(QWindow * window) { m_window = window; }
+    void setView(QGraphicsView *view) { m_view = view; }
+    void setScene(QGraphicsScene *scene) { m_scene = scene; }
+    void setTreeView(QGraphicsView *treeView) { m_treeView = treeView; }
+    void setTreeScene(QGraphicsScene *treeScene) { m_treeScene = treeScene; }
+
+    void setOptionsWidget(OptionsWidget *optionsWidget) { m_optionsWidget = optionsWidget; }
+public slots:
+    void populateAccessibilityScene();
+    void updateAccessibilitySceneItemFlags();
+    void populateAccessibilityTreeScene();
+    void handleUpdate(QObject *object, QAccessible::Event reason);
+    void setSelected(QObject *object);
+
+    void changeScale(int scale);
+private:
+    void updateItems(QObject *root);
+    void updateItem(QObject *object);
+    void updateItem(QGraphicsRectItem *item, QAccessibleInterface *interface);
+    void updateItemFlags(QGraphicsRectItem *item, QAccessibleInterface *interface);
+
+    void populateAccessibilityScene(QAccessibleInterface * interface, int child, QGraphicsScene *scene);
+    QGraphicsRectItem * processInterface(QAccessibleInterface * interface, int child, QGraphicsScene *scene);
+
+    struct TreeItem;
+    TreeItem computeLevels(QAccessibleInterface * interface, int level);
+    void populateAccessibilityTreeScene(QAccessibleInterface * interface, int child);
+    void addGraphicsItems(TreeItem item, int row, int xPos);
+
+    bool isHidden(QAccessibleInterface *interface);
+
+    QWindow *m_window;
+    QGraphicsView *m_view;
+    QGraphicsScene *m_scene;
+    QGraphicsView *m_treeView;
+    QGraphicsScene *m_treeScene;
+    QGraphicsItem *m_rootItem;
+    OptionsWidget *m_optionsWidget;
+    QObject *m_selectedObject;
+
+    QHash<QObject *, QGraphicsRectItem*> m_graphicsItems;
+    QSet<QObject *> m_animatedObjects;
+
+    struct TreeItem {
+        QList<TreeItem> children;
+        int width;
+        QString name;
+        QString role;
+        QString description;
+        QRect rect;
+        QAccessible::State state;
+        QObject *object;
+        TreeItem() : width(0) {}
+    };
+
+    TreeItem m_rootTreeItem;
+    int m_treeItemWidth;
+    int m_treeItemHorizontalPadding;
+    int m_treeItemHeight;
+    int m_treeItemVerticalPadding;
+};
+
+#endif // ACCESSIBILITYSCENEMANAGER_H
diff --git a/tools/accessibilityinspector/main.cpp b/tools/accessibilityinspector/main.cpp
new file mode 100644 (file)
index 0000000..215c0a9
--- /dev/null
@@ -0,0 +1,119 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the tools applications of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#include <QtGui>
+#include <QtDeclarative/QtDeclarative>
+#include <QtUiTools/QtUiTools>
+
+#include "accessibilityinspector.h"
+
+int main(int argc, char **argv)
+{
+    QApplication app(argc, argv);
+
+    if (app.arguments().count() < 2) {
+        qDebug() << "Usage: accessebilityInspector [ ui-file | qml-file ] [Option]";
+        qDebug() << "Option:";
+#ifdef QT_ACCESSIBILITY_INSPECTOR_SCENE_GRAPH
+        qDebug() << "-qtquick1: Use QDeclarativeView instead of QSGView for rendering QML files";
+#endif
+        return 0;
+    }
+
+    QString fileName = app.arguments().at(1);
+    QString mode;
+    if (app.arguments().count() > 2) {
+        mode = app.arguments().at(2);
+    }
+
+    QWidget *window;
+
+    if (fileName.endsWith(".ui")) {
+        QUiLoader loader;
+        QFile file(fileName);
+        file.open(QFile::ReadOnly);
+        window = loader.load(&file, 0);
+    } else if (fileName.endsWith(".qml")){
+        QUrl fileUrl;
+        if (fileName.startsWith(":")) { // detect resources.
+            QString name = fileName;
+            name.remove(0, 2); // reomve ":/"
+            fileUrl.setUrl(QLatin1String("qrc:/") + name);
+        } else {
+            fileUrl = QUrl::fromLocalFile(fileName);
+        }
+
+#ifdef QT_ACCESSIBILITY_INSPECTOR_SCENE_GRAPH
+        if (mode == QLatin1String("-qtquick1"))
+#endif
+        {
+            QDeclarativeView * declarativeView = new QDeclarativeView();
+            declarativeView->setSource(fileUrl);
+            window = declarativeView;
+        }
+#ifdef QT_ACCESSIBILITY_INSPECTOR_SCENE_GRAPH
+        else {
+            QSGView * sceneGraphView = new QSGView();
+            sceneGraphView->setSource(fileUrl);
+            window = sceneGraphView;
+        }
+#endif
+    } else {
+        qDebug() << "Error: don't know what to do with" << fileName;
+    }
+
+    AccessibilityInspector *accessibilityInspector = new AccessibilityInspector();
+
+    accessibilityInspector->inspectWindow(window);
+
+    window->move(50, 50);
+    window->show();
+
+    int ret = app.exec();
+
+    accessibilityInspector->saveWindowGeometry();
+    delete accessibilityInspector;
+
+    return ret;
+
+
+}
+
diff --git a/tools/accessibilityinspector/optionswidget.cpp b/tools/accessibilityinspector/optionswidget.cpp
new file mode 100644 (file)
index 0000000..165d891
--- /dev/null
@@ -0,0 +1,43 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the tools applications of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#include "optionswidget.h"
+
diff --git a/tools/accessibilityinspector/optionswidget.h b/tools/accessibilityinspector/optionswidget.h
new file mode 100644 (file)
index 0000000..2cf8c6b
--- /dev/null
@@ -0,0 +1,117 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the tools applications of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#ifndef OPTIONSWIDGET_H
+#define OPTIONSWIDGET_H
+
+#include <QtGui>
+#include <QtWidgets>
+
+class OptionsWidget : public QWidget
+{
+Q_OBJECT
+public:
+    OptionsWidget()
+        :QWidget()
+    {
+        QVBoxLayout *m_layout = new QVBoxLayout;
+
+        m_refresh = new QPushButton(this);
+        m_refresh->setText(QLatin1String("Refresh"));
+        m_layout->addWidget(m_refresh);
+        connect(m_refresh, SIGNAL(clicked()), SIGNAL(refreshClicked()));
+
+        m_hideInvisibleItems = new QCheckBox(this);
+        m_layout->addWidget(m_hideInvisibleItems);
+        m_hideInvisibleItems->setText("Hide Invisible Items");
+        m_hideInvisibleItems->setChecked(true);
+        connect(m_hideInvisibleItems, SIGNAL(toggled(bool)), SIGNAL(optionsChanged()));
+
+        m_hideOffscreenItems = new QCheckBox(this);
+        m_layout->addWidget(m_hideOffscreenItems);
+        m_hideOffscreenItems->setText("Hide Offscreen Items");
+        m_hideOffscreenItems->setChecked(true);
+        connect(m_hideOffscreenItems, SIGNAL(toggled(bool)), SIGNAL(optionsChanged()));
+
+        m_hidePaneItems = new QCheckBox(this);
+        m_layout->addWidget(m_hidePaneItems);
+        m_hidePaneItems->setText("Hide Items with the Pane role");
+        m_hidePaneItems->setChecked(true);
+        connect(m_hidePaneItems, SIGNAL(toggled(bool)), SIGNAL(optionsChanged()));
+
+        m_enableTextToSpeach = new QCheckBox(this);
+        m_layout->addWidget(m_enableTextToSpeach);
+        m_enableTextToSpeach->setText("Enable Text To Speech");
+        m_enableTextToSpeach->setChecked(false);
+        connect(m_enableTextToSpeach, SIGNAL(toggled(bool)), SIGNAL(optionsChanged()));
+
+
+        m_scale = new QSlider(Qt::Horizontal);
+//        m_layout->addWidget(m_scale);
+        m_scale->setRange(5, 30);
+        m_scale->setValue(1);
+        connect(m_scale, SIGNAL(valueChanged(int)), SIGNAL(scaleChanged(int)));
+
+        this->setLayout(m_layout);
+    }
+
+    bool hideInvisibleItems() { return m_hideInvisibleItems->isChecked(); }
+    bool hideOffscreenItems() { return m_hideOffscreenItems->isChecked(); }
+    bool hidePaneItems() { return m_hidePaneItems->isChecked(); }
+    bool enableTextToSpeach() { return m_enableTextToSpeach->isChecked(); }
+signals:
+    void optionsChanged();
+    void refreshClicked();
+    void scaleChanged(int);
+
+private:
+    QVBoxLayout *m_layout;
+
+    QPushButton *m_refresh;
+    QCheckBox *m_hideInvisibleItems;
+    QCheckBox *m_hideOffscreenItems;
+    QCheckBox *m_hidePaneItems;
+    QCheckBox *m_enableTextToSpeach;
+    QSlider *m_scale;
+};
+
+
+#endif // OPTIONSWIDGET_H
diff --git a/tools/accessibilityinspector/screenreader.cpp b/tools/accessibilityinspector/screenreader.cpp
new file mode 100644 (file)
index 0000000..5f864f3
--- /dev/null
@@ -0,0 +1,162 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the tools applications of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#include "screenreader.h"
+#include "optionswidget.h"
+#include "accessibilityscenemanager.h"
+#include <QtGui>
+
+#ifdef Q_OS_MAC
+#include <private/qt_mac_p.h>
+#endif
+
+ScreenReader::ScreenReader(QObject *parent) :
+    QObject(parent)
+{
+    m_selectedInterface = 0;
+    m_rootInterface = 0;
+    bool activateCalled = false;
+}
+
+ScreenReader::~ScreenReader()
+{
+    delete m_selectedInterface;
+    delete m_rootInterface;
+}
+
+void ScreenReader::setRootObject(QObject *rootObject)
+{
+    m_rootInterface = QAccessible::queryAccessibleInterface(rootObject);
+}
+
+void ScreenReader::setOptionsWidget(OptionsWidget *optionsWidget)
+{
+    m_optionsWidget = optionsWidget;
+}
+
+void ScreenReader::touchPoint(const QPoint &point)
+{
+    qDebug() << "touch" << point;
+    // Wait and see if this touch is the start of a double-tap
+    // (activate will then be called and cancel the touch processing)
+    m_activateCalled = false;
+    m_currentTouchPoint = point;
+    QTimer::singleShot(200, this, SLOT(processTouchPoint()));
+}
+
+void ScreenReader::processTouchPoint()
+{
+    if (m_activateCalled) {
+        return;
+    }
+
+    if (m_rootInterface == 0) {
+        return;
+    }
+
+    QAccessibleInterface * currentInterface = m_rootInterface;
+
+    int hit = -2;
+    int guardCounter = 0;
+    const int guardMax = 40;
+    while (hit != 0) {
+        ++guardCounter;
+        if (guardCounter > guardMax) {
+            qDebug() << "touchPoint exit recursion overflow";
+            return; // outside
+        }
+
+        hit = currentInterface->childAt(m_currentTouchPoint.x(), m_currentTouchPoint.y());
+        //qDebug() << "hit" << hit;
+        if (hit == -1) {
+            return; // outside
+        } else if (hit == 0) {
+            break; // found it.
+        } else {
+            QAccessibleInterface *childInterface = 0;
+            int child = currentInterface->navigate(QAccessible::Child, hit, &childInterface);
+            if (childInterface == 0) {
+                return; // navigation error
+            }
+
+            if (currentInterface != m_rootInterface)
+                delete currentInterface;
+            currentInterface = childInterface;
+        }
+    }
+
+    m_selectedInterface = currentInterface;
+    emit selected(m_selectedInterface->object());
+    if (m_optionsWidget->enableTextToSpeach())
+        speak(m_selectedInterface->text(QAccessible::Name, 0)
+              /*+ "," + translateRole(m_selectedInterface->role(0)) */);
+
+//    qDebug() << "touchPoint exit found" << m_selectedInterface->text(QAccessible::Name, 0) << m_selectedInterface->object() << m_selectedInterface->rect(0);
+}
+
+void ScreenReader::activate()
+{
+    qDebug() << "ScreenReader::activate";
+    m_activateCalled = true;
+    if (m_selectedInterface) {
+        m_selectedInterface->doAction(QAccessible::Press, 0);
+    }
+}
+
+#ifdef Q_OS_MAC
+
+    // screenreader.mm
+
+#else
+
+void ScreenReader::speak(const QString &text, const QString &/*voice*/)
+{
+    QFile f("festivalspeachhack");
+    f.open(QIODevice::WriteOnly);
+    f.write(text.toLocal8Bit());
+    f.close();
+
+    QProcess *process = new QProcess;
+    process->start("/usr/bin/festival", QStringList() << "--tts" << "festivalspeachhack");
+}
+
+#endif
+
diff --git a/tools/accessibilityinspector/screenreader.h b/tools/accessibilityinspector/screenreader.h
new file mode 100644 (file)
index 0000000..b3ba91d
--- /dev/null
@@ -0,0 +1,84 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the tools applications of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+
+#ifndef SCREENREADER_H
+#define SCREENREADER_H
+
+#include <QObject>
+#include <QAccessible>
+#include <QAccessibleBridge>
+
+/*
+    A Simple screen reader for touch-based user interfaces.
+
+    Requires a text-to-speach backend. Currently implemented on
+    Mac OS X and using festival on unix.
+*/
+class OptionsWidget;
+class ScreenReader : public QObject
+{
+    Q_OBJECT
+public:
+    explicit ScreenReader(QObject *parent = 0);
+    ~ScreenReader();
+
+    void setRootObject(QObject *rootObject);
+    void setOptionsWidget(OptionsWidget *optionsWidget);
+public slots:
+    void touchPoint(const QPoint &point);
+    void activate();
+protected slots:
+    void processTouchPoint();
+signals:
+    void selected(QObject *object);
+
+protected:
+    void speak(const QString &text, const QString &voice = QString());
+private:
+    QAccessibleInterface *m_selectedInterface;
+    QAccessibleInterface *m_rootInterface;
+    OptionsWidget *m_optionsWidget;
+    QPoint m_currentTouchPoint;
+    bool m_activateCalled;
+};
+
+#endif // SCREENREADER_H
diff --git a/tools/accessibilityinspector/screenreader_mac.mm b/tools/accessibilityinspector/screenreader_mac.mm
new file mode 100644 (file)
index 0000000..063cecd
--- /dev/null
@@ -0,0 +1,60 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the tools applications of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** This file may be used under the terms of the GNU Lesser General Public
+ ** License version 2.1 as published by the Free Software Foundation and
+ ** appearing in the file LICENSE.LGPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU Lesser
+ ** General Public License version 2.1 requirements will be met:
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU General
+ ** Public License version 3.0 as published by the Free Software Foundation
+ ** and appearing in the file LICENSE.GPL included in the packaging of this
+ ** file. Please review the following information to ensure the GNU General
+ ** Public License version 3.0 requirements will be met:
+ ** http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#include "screenreader.h"
+#include <QtCore>
+#include <private/qt_mac_p.h>
+
+void ScreenReader::speak(const QString &text, const QString &voice)
+{
+    QString voiceBase = "com.apple.speech.synthesis.voice.";
+    if (voice.isEmpty())
+        voiceBase += "Vici";
+    else
+        voiceBase += voice;
+
+    CFStringRef cfVoice = QCFString::toCFStringRef(voiceBase);
+    NSSpeechSynthesizer *synth = [[NSSpeechSynthesizer alloc] initWithVoice:(NSString *)cfVoice];
+    CFStringRef cfText = QCFString::toCFStringRef(text);
+    [synth startSpeakingString: (NSString *)cfText];
+    CFRelease(cfText);
+    CFRelease(cfVoice);
+}