diff options
Diffstat (limited to 'src/gui')
1494 files changed, 787399 insertions, 0 deletions
diff --git a/src/gui/QtGui.dynlist b/src/gui/QtGui.dynlist new file mode 100644 index 0000000000..ea47f598a7 --- /dev/null +++ b/src/gui/QtGui.dynlist @@ -0,0 +1,8 @@ +{ + extern "C++" { + "qt_x11ft_convert_pattern(_FcPattern*, QByteArray*, int*, bool*)"; + "QApplication::x11ClientMessage(QWidget*, _XEvent*, bool)"; + "QApplication::notify(QObject*, QEvent*)"; + "QApplication::qwsEventFilter(QWSEvent*)"; + }; +}; diff --git a/src/gui/accessible/accessible.pri b/src/gui/accessible/accessible.pri new file mode 100644 index 0000000000..31362ffded --- /dev/null +++ b/src/gui/accessible/accessible.pri @@ -0,0 +1,25 @@ +# Qt accessibility module + +contains(QT_CONFIG, accessibility) { + HEADERS += accessible/qaccessible.h \ + accessible/qaccessible2.h \ + accessible/qaccessibleobject.h \ + accessible/qaccessiblewidget.h \ + accessible/qaccessibleplugin.h + SOURCES += accessible/qaccessible.cpp \ + accessible/qaccessible2.cpp \ + accessible/qaccessibleobject.cpp \ + accessible/qaccessiblewidget.cpp \ + accessible/qaccessibleplugin.cpp + + mac:!embedded:!qpa { + HEADERS += accessible/qaccessible_mac_p.h + OBJECTIVE_SOURCES += accessible/qaccessible_mac.mm \ + accessible/qaccessible_mac_cocoa.mm + } else:win32 { + SOURCES += accessible/qaccessible_win.cpp + } else { + HEADERS += accessible/qaccessiblebridge.h + SOURCES += accessible/qaccessible_unix.cpp accessible/qaccessiblebridge.cpp + } +} diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp new file mode 100644 index 0000000000..abe68f8108 --- /dev/null +++ b/src/gui/accessible/qaccessible.cpp @@ -0,0 +1,1094 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessible.h" + +#ifndef QT_NO_ACCESSIBILITY + +#include "qaccessibleplugin.h" +#include "qaccessiblewidget.h" +#include "qapplication.h" +#include "qhash.h" +#include "qmetaobject.h" +#include "qmutex.h" +#include <private/qfactoryloader_p.h> + +#include "qwidget.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QAccessible + \brief The QAccessible class provides enums and static functions + relating to accessibility. + + \ingroup accessibility + + + Accessible applications can be used by people who are not able to + use applications by conventional means. + + The functions in this class are used for communication between + accessible applications (also called AT Servers) and + accessibility tools (AT Clients), such as screen readers and + braille displays. Clients and servers communicate in the following way: + + \list + \o \e{AT Servers} notify the clients about events through calls to the + updateAccessibility() function. + + \o \e{AT Clients} request information about the objects in the server. + The QAccessibleInterface class is the core interface, and encapsulates + this information in a pure virtual API. Implementations of the interface + are provided by Qt through the queryAccessibleInterface() API. + \endlist + + The communication between servers and clients is initialized by + the setRootObject() function. Function pointers can be installed + to replace or extend the default behavior of the static functions + in QAccessible. + + Qt supports Microsoft Active Accessibility (MSAA), Mac OS X + Accessibility, and the Unix/X11 AT-SPI standard. Other backends + can be supported using QAccessibleBridge. + + In addition to QAccessible's static functions, Qt offers one + generic interface, QAccessibleInterface, that can be used to wrap + all widgets and objects (e.g., QPushButton). This single + interface provides all the metadata necessary for the assistive + technologies. Qt provides implementations of this interface for + its built-in widgets as plugins. + + When you develop custom widgets, you can create custom subclasses + of QAccessibleInterface and distribute them as plugins (using + QAccessiblePlugin) or compile them into the application. + Likewise, Qt's predefined accessibility support can be built as + plugin (the default) or directly into the Qt library. The main + advantage of using plugins is that the accessibility classes are + only loaded into memory if they are actually used; they don't + slow down the common case where no assistive technology is being + used. + + Qt also includes two convenience classes, QAccessibleObject and + QAccessibleWidget, that inherit from QAccessibleInterface and + provide the lowest common denominator of metadata (e.g., widget + geometry, window title, basic help text). You can use them as + base classes when wrapping your custom QObject or QWidget + subclasses. + + \sa QAccessibleInterface +*/ + +/*! + \enum QAccessible::Action + + This enum describes the possible types of action that can occur. + + \value DefaultAction + \value Press + \value SetFocus + \value Increase + \value Decrease + \value Accept + \value Cancel + \value Select + \value ClearSelection + \value RemoveSelection + \value ExtendSelection + \value AddToSelection + + \value FirstStandardAction + \value LastStandardAction +*/ + +/*! + \enum QAccessible::Method + + This enum describes the possible types of methods that can be + invoked on an accessible object. + + \value ListSupportedMethods + \value SetCursorPosition + \value GetCursorPosition + + \omitvalue ForegroundColor + \omitvalue BackgroundColor + + \sa QAccessibleInterface::invokeMethod() +*/ + +/*! + \fn QSet<Method> QAccessibleInterface::supportedMethods() + \since 4.3 + + Returns a QSet of \l{QAccessible::}{Method}s that are supported by this + accessible interface. + + \sa QAccessible::Method invokeMethod() +*/ + +/*! + \enum QAccessible::StateFlag + + This enum type defines bit flags that can be combined to indicate + the state of an accessible object. The values are: + + \value Animated The object's appearance changes frequently. + \value Busy The object cannot accept input at the moment. + \value Checked The object's check box is checked. + \value Collapsed The object is collapsed, e.g. a closed listview item, or an iconified window. + \value DefaultButton The object represents the default button in a dialog. + \value Expanded The object is expandable, and currently the children are visible. + \value ExtSelectable The object supports extended selection. + \value Focusable The object can receive focus. Only objects in the active window can receive focus. + \value Focused The object has keyboard focus. + \value HasPopup The object opens a popup. + \value HotTracked The object's appearance is sensitive to the mouse cursor position. + \value Invisible The object is not visible to the user. + \value Linked The object is linked to another object, e.g. a hyperlink. + \value Marqueed The object displays scrolling contents, e.g. a log view. + \value Mixed The state of the object is not determined, e.g. a tri-state check box that is neither checked nor unchecked. + \value Modal The object blocks input from other objects. + \value Movable The object can be moved. + \value MultiSelectable The object supports multiple selected items. + \value Normal The normal state. + \value Offscreen The object is clipped by the visible area. Objects that are off screen are also invisible. + \value Pressed The object is pressed. + \value Protected The object is password protected, e.g. a line edit for entering a Password. + \value ReadOnly The object can usually be edited, but is explicitly set to read-only. + \value Selectable The object is selectable. + \value Selected The object is selected. + \value SelfVoicing The object describes itself through speech or sound. + \value Sizeable The object can be resized, e.g. top-level windows. + \value Traversed The object is linked and has been visited. + \value Unavailable The object is unavailable to the user, e.g. a disabled widget. + \omitvalue Moveable + \omitvalue HasInvokeExtension + + Implementations of QAccessibleInterface::state() return a combination + of these flags. +*/ + +/*! + \enum QAccessible::Event + + This enum type defines accessible event types. + + \value AcceleratorChanged + \value Alert A system alert (e.g., a message from a QMessageBox) + \value ContextHelpEnd Context help (QWhatsThis) for an object is finished. + \value ContextHelpStart Context help (QWhatsThis) for an object is initiated. + \value DefaultActionChanged The default QAccessible::Action for the accessible object changed + \value DescriptionChanged The objects QAccessible::Description changed. + \value DialogEnd A dialog (QDialog) is been hidden + \value DialogStart A dialog (QDialog) has been set visible. + \value DragDropEnd A Drag & Drop operation is about to finished. + \value DragDropStart A Drag & Drop operation is about to be initiated. + \value Focus An object has gained keyboard focus. + \value ForegroundChanged A window has been activated (i.e., a new window has gained focus on the desktop) + \value HelpChanged The QAccessible::Help text property of an object has changed + \value LocationChanged An objects location on the screen changed + \value MenuCommand A menu item is triggered. + \value MenuEnd A menu has been closed (Qt uses PopupMenuEnd for all menus) + \value MenuStart A menu has been opened on the menubar (Qt uses PopupMenuStart for all menus) + \value NameChanged The QAccessible::Name property of an object has changed + \value ObjectCreated A new object is created. + \value ObjectDestroyed An object is deleted. + \value ObjectHide An object is hidden (i.e., with QWidget::hide()). Any children the object that is hidden has do not send this event. + It is not send when an object is hidden as it is being obcured by others. + \value ObjectReorder A layout or item view has added, removed, or moved an object (Qt does not use this event). + \value ObjectShow An object is displayed (i.e., with QWidget::show()). + \value ParentChanged An objects parent object changed. + \value PopupMenuEnd A popup menu has closed. + \value PopupMenuStart A popupmenu has opened. + \value ScrollingEnd A scrollbar scroll operation has ended (the mouse has released the slider handle) + \value ScrollingStart A scrollbar scroll operation is about to start (i.e., the mouse has pressed on the slider handle) + \value Selection The selection has changed in a menu or item view. + \value SelectionAdd An item has been added to the selection in an item view. + \value SelectionRemove An item has been removed from an item view selection. + \value SelectionWithin Several changes to a selection has occurred in an item view. + \value SoundPlayed A sound has been played by an object + \value StateChanged The QAccessible::State of an object has changed. + \value ValueChanged The QAccessible::Value of an object has changed. +*/ + +/*! + \enum QAccessible::Role + + This enum defines the role of an accessible object. The roles are: + + \value AlertMessage An object that is used to alert the user. + \value Animation An object that displays an animation. + \value Application The application's main window. + \value Assistant An object that provids interactive help. + \value Border An object that represents a border. + \value ButtonDropDown A button that drops down a list of items. + \value ButtonDropGrid A button that drops down a grid. + \value ButtonMenu A button that drops down a menu. + \value Canvas An object that displays graphics that the user can interact with. + \value Caret An object that represents the system caret (text cursor). + \value Cell A cell in a table. + \value Chart An object that displays a graphical representation of data. + \value CheckBox An object that represents an option that can be checked or unchecked. Some options provide a "mixed" state, e.g. neither checked nor unchecked. + \value Client The client area in a window. + \value Clock A clock displaying time. + \value Column A column of cells, usually within a table. + \value ColumnHeader A header for a column of data. + \value ComboBox A list of choices that the user can select from. + \value Cursor An object that represents the mouse cursor. + \value Dial An object that represents a dial or knob. + \value Dialog A dialog box. + \value Document A document window, usually in an MDI environment. + \value EditableText Editable text + \value Equation An object that represents a mathematical equation. + \value Graphic A graphic or picture, e.g. an icon. + \value Grip A grip that the user can drag to change the size of widgets. + \value Grouping An object that represents a logical grouping of other objects. + \value HelpBalloon An object that displays help in a separate, short lived window. + \value HotkeyField A hotkey field that allows the user to enter a key sequence. + \value Indicator An indicator that represents a current value or item. + \value LayeredPane An object that can contain layered children, e.g. in a stack. + \value Link A link to something else. + \value List A list of items, from which the user can select one or more items. + \value ListItem An item in a list of items. + \value MenuBar A menu bar from which menus are opened by the user. + \value MenuItem An item in a menu or menu bar. + \value NoRole The object has no role. This usually indicates an invalid object. + \value PageTab A page tab that the user can select to switch to a different page in a dialog. + \value PageTabList A list of page tabs. + \value Pane A generic container. + \value PopupMenu A menu which lists options that the user can select to perform an action. + \value ProgressBar The object displays the progress of an operation in progress. + \value PropertyPage A property page where the user can change options and settings. + \value PushButton A button. + \value RadioButton An object that represents an option that is mutually exclusive with other options. + \value Row A row of cells, usually within a table. + \value RowHeader A header for a row of data. + \value ScrollBar A scroll bar, which allows the user to scroll the visible area. + \value Separator A separator that divides space into logical areas. + \value Slider A slider that allows the user to select a value within a given range. + \value Sound An object that represents a sound. + \value SpinBox A spin box widget that allows the user to enter a value within a given range. + \value Splitter A splitter distributing available space between its child widgets. + \value StaticText Static text, such as labels for other widgets. + \value StatusBar A status bar. + \value Table A table representing data in a grid of rows and columns. + \value TitleBar The title bar caption of a window. + \value ToolBar A tool bar, which groups widgets that the user accesses frequently. + \value ToolTip A tool tip which provides information about other objects. + \value Tree A list of items in a tree structure. + \value TreeItem An item in a tree structure. + \value UserRole The first value to be used for user defined roles. + \value Whitespace Blank space between other objects. + \value Window A top level window. +*/ + +/*! + \enum QAccessible::RelationFlag + + This enum type defines bit flags that can be combined to indicate + the relationship between two accessible objects. + + \value Unrelated The objects are unrelated. + \value Self The objects are the same. + \value Ancestor The first object is a parent of the second object. + \value Child The first object is a direct child of the second object. + \value Descendent The first object is an indirect child of the second object. + \value Sibling The objects are siblings. + + \value Up The first object is above the second object. + \value Down The first object is below the second object. + \value Left The first object is left of the second object. + \value Right The first object is right of the second object. + \value Covers The first object covers the second object. + \value Covered The first object is covered by the second object. + + \value FocusChild The first object is the second object's focus child. + \value Label The first object is the label of the second object. + \value Labelled The first object is labelled by the second object. + \value Controller The first object controls the second object. + \value Controlled The first object is controlled by the second object. + + \omitvalue HierarchyMask + \omitvalue GeometryMask + \omitvalue LogicalMask + + Implementations of relationTo() return a combination of these flags. + Some values are mutually exclusive. + + Implementations of navigate() can accept only one distinct value. +*/ + +/*! + \enum QAccessible::Text + + This enum specifies string information that an accessible object + returns. + + \value Name The name of the object. This can be used both + as an identifier or a short description by + accessible clients. + \value Description A short text describing the object. + \value Value The value of the object. + \value Help A longer text giving information about how to use the object. + \value Accelerator The keyboard shortcut that executes the object's default action. + \value UserText The first value to be used for user defined text. +*/ + +/*! + \fn QAccessibleInterface::~QAccessibleInterface() + + Destroys the object. +*/ + +/*! + \fn void QAccessible::initialize() + \internal +*/ + +/*! + \fn void QAccessible::cleanup() + \internal +*/ + +#ifndef QT_NO_LIBRARY +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QAccessibleFactoryInterface_iid, QLatin1String("/accessible"))) +#endif + +Q_GLOBAL_STATIC(QList<QAccessible::InterfaceFactory>, qAccessibleFactories) + +QAccessible::UpdateHandler QAccessible::updateHandler = 0; +QAccessible::RootObjectHandler QAccessible::rootObjectHandler = 0; + +static bool accessibility_active = false; +static bool cleanupAdded = false; +static void qAccessibleCleanup() +{ + qAccessibleFactories()->clear(); +} + +/*! + \typedef QAccessible::InterfaceFactory + + This is a typedef for a pointer to a function with the following + signature: + + \snippet doc/src/snippets/code/src_gui_accessible_qaccessible.cpp 1 + + The function receives a QString and a QObject pointer, where the + QString is the key identifying the interface. The QObject is used + to pass on to the QAccessibleInterface so that it can hold a reference + to it. + + If the key and the QObject does not have a corresponding + QAccessibleInterface, a null-pointer will be returned. + + Installed factories are called by queryAccessibilityInterface() until + one provides an interface. +*/ + +/*! + \typedef QAccessible::UpdateHandler + + \internal + + A function pointer type. Use a function with this prototype to install + your own update function. + + The function is called by updateAccessibility(). +*/ + +/*! + \typedef QAccessible::RootObjectHandler + + \internal + + A function pointer type. Use a function with this prototype to install + your own root object handler. + + The function is called by setRootObject(). +*/ + +/*! + Installs the InterfaceFactory \a factory. The last factory added + is the first one used by queryAccessibleInterface(). +*/ +void QAccessible::installFactory(InterfaceFactory factory) +{ + if (!factory) + return; + + if (!cleanupAdded) { + qAddPostRoutine(qAccessibleCleanup); + cleanupAdded = true; + } + if (qAccessibleFactories()->contains(factory)) + return; + qAccessibleFactories()->append(factory); +} + +/*! + Removes \a factory from the list of installed InterfaceFactories. +*/ +void QAccessible::removeFactory(InterfaceFactory factory) +{ + qAccessibleFactories()->removeAll(factory); +} + +/*! + \internal + + Installs the given \a handler as the function to be used by + updateAccessibility(), and returns the previously installed + handler. +*/ +QAccessible::UpdateHandler QAccessible::installUpdateHandler(UpdateHandler handler) +{ + UpdateHandler old = updateHandler; + updateHandler = handler; + return old; +} + +/*! + Installs the given \a handler as the function to be used by setRootObject(), + and returns the previously installed handler. +*/ +QAccessible::RootObjectHandler QAccessible::installRootObjectHandler(RootObjectHandler handler) +{ + RootObjectHandler old = rootObjectHandler; + rootObjectHandler = handler; + return old; +} + +/*! + If a QAccessibleInterface implementation exists for the given \a object, + this function returns a pointer to the implementation; otherwise it + returns 0. + + The function calls all installed factory functions (from most + recently installed to least recently installed) until one is found + that provides an interface for the class of \a object. If no + factory can provide an accessibility implementation for the class + the function loads installed accessibility plugins, and tests if + any of the plugins can provide the implementation. + + If no implementation for the object's class is available, the + function tries to find an implementation for the object's parent + class, using the above strategy. + + \warning The caller is responsible for deleting the returned + interface after use. +*/ +QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object) +{ + accessibility_active = true; + QAccessibleInterface *iface = 0; + if (!object) + return 0; + + QEvent e(QEvent::AccessibilityPrepare); + QApplication::sendEvent(object, &e); + + const QMetaObject *mo = object->metaObject(); + while (mo) { + const QLatin1String cn(mo->className()); + for (int i = qAccessibleFactories()->count(); i > 0; --i) { + InterfaceFactory factory = qAccessibleFactories()->at(i - 1); + iface = factory(cn, object); + if (iface) + return iface; + } +#ifndef QT_NO_LIBRARY + QAccessibleFactoryInterface *factory = qobject_cast<QAccessibleFactoryInterface*>(loader()->instance(cn)); + if (factory) { + iface = factory->create(cn, object); + if (iface) + return iface; + } +#endif + mo = mo->superClass(); + } + + QWidget *widget = qobject_cast<QWidget*>(object); + if (widget) + return new QAccessibleWidget(widget); + else if (object == qApp) + return new QAccessibleApplication(); + + return 0; +} + +/*! + Returns true if an accessibility implementation has been requested + during the runtime of the application; otherwise returns false. + + Use this function to prevent potentially expensive notifications via + updateAccessibility(). +*/ +bool QAccessible::isActive() +{ + return accessibility_active; +} + +/*! + \fn void QAccessible::setRootObject(QObject *object) + + Sets the root accessible object of this application to \a object. + All other accessible objects in the application can be reached by the + client using object navigation. + + You should never need to call this function. Qt sets the QApplication + object as the root object immediately before the event loop is entered + in QApplication::exec(). + + Use QAccessible::installRootObjectHandler() to redirect the function + call to a customized handler function. + + \sa queryAccessibleInterface() +*/ + +/*! + \fn void QAccessible::updateAccessibility(QObject *object, int child, Event reason) + + Notifies accessibility clients about a change in \a object's + accessibility information. + + \a reason specifies the cause of the change, for example, + \c ValueChange when the position of a slider has been changed. \a + child is the (1-based) index of the child element that has changed. + When \a child is 0, the object itself has changed. + + Call this function whenever the state of your accessible object or + one of its sub-elements has been changed either programmatically + (e.g. by calling QLabel::setText()) or by user interaction. + + If there are no accessibility tools listening to this event, the + performance penalty for calling this function is small, but if determining + the parameters of the call is expensive you can test isActive() to + avoid unnecessary computations. +*/ + + +/*! + \class QAccessibleInterface + \brief The QAccessibleInterface class defines an interface that exposes information + about accessible objects. + + \ingroup accessibility + + Accessibility tools (also called AT Clients), such as screen readers + or braille displays, require high-level information about + accessible objects in an application. Accessible objects provide + specialized input and output methods, making it possible for users + to use accessibility tools with enabled applications (AT Servers). + + Every element that the user needs to interact with or react to is + an accessible object, and should provide this information. These + are mainly visual objects, such as widgets and widget elements, but + can also be content, such as sounds. + + The AT client uses three basic concepts to acquire information + about any accessible object in an application: + \list + \i \e Properties The client can read information about + accessible objects. In some cases the client can also modify these + properties; such as text in a line edit. + \i \e Actions The client can invoke actions like pressing a button + or . + \i \e{Relationships and Navigation} The client can traverse from one + accessible object to another, using the relationships between objects. + \endlist + + The QAccessibleInterface defines the API for these three concepts. + + \section1 Relationships and Navigation + + The functions childCount() and indexOfChild() return the number of + children of an accessible object and the index a child object has + in its parent. The childAt() function returns the index of a child + at a given position. + + The relationTo() function provides information about how two + different objects relate to each other, and navigate() allows + traversing from one object to another object with a given + relationship. + + \section1 Properties + + The central property of an accessible objects is what role() it + has. Different objects can have the same role, e.g. both the "Add + line" element in a scroll bar and the \c OK button in a dialog have + the same role, "button". The role implies what kind of + interaction the user can perform with the user interface element. + + An object's state() property is a combination of different state + flags and can describe both how the object's state differs from a + "normal" state, e.g. it might be unavailable, and also how it + behaves, e.g. it might be selectable. + + The text() property provides textual information about the object. + An object usually has a name, but can provide extended information + such as a description, help text, or information about any + keyboard accelerators it provides. Some objects allow changing the + text() property through the setText() function, but this + information is in most cases read-only. + + The rect() property provides information about the geometry of an + accessible object. This information is usually only available for + visual objects. + + \section1 Actions and Selection + + To enable the user to interact with an accessible object the + object must expose information about the actions that it can + perform. userActionCount() returns the number of actions supported by + an accessible object, and actionText() returns textual information + about those actions. doAction() invokes an action. + + Objects that support selections can define actions to change the selection. + + \section2 Objects and children + + A QAccessibleInterface provides information about the accessible + object, and can also provide information for the children of that + object if those children don't provide a QAccessibleInterface + implementation themselves. This is practical if the object has + many similar children (e.g. items in a list view), or if the + children are an integral part of the object itself, for example, the + different sections in a scroll bar. + + If an accessible object provides information about its children + through one QAccessibleInterface, the children are referenced + using indexes. The index is 1-based for the children, i.e. 0 + refers to the object itself, 1 to the first child, 2 to the second + child, and so on. + + All functions in QAccessibleInterface that take a child index + relate to the object itself if the index is 0, or to the child + specified. If a child provides its own interface implementation + (which can be retrieved through navigation) asking the parent for + information about that child will usually not succeed. + + \sa QAccessible +*/ + +/*! + \fn bool QAccessibleInterface::isValid() const + + Returns true if all the data necessary to use this interface + implementation is valid (e.g. all pointers are non-null); + otherwise returns false. + + \sa object() +*/ + +/*! + \fn QObject *QAccessibleInterface::object() const + + Returns a pointer to the QObject this interface implementation provides + information for. + + \sa isValid() +*/ + +/*! + \fn int QAccessibleInterface::childCount() const + + Returns the number of children that belong to this object. A child + can provide accessibility information on its own (e.g. a child + widget), or be a sub-element of this accessible object. + + All objects provide this information. + + \sa indexOfChild() +*/ + +/*! + \fn int QAccessibleInterface::indexOfChild(const QAccessibleInterface *child) const + + Returns the 1-based index of the object \a child in this object's + children list, or -1 if \a child is not a child of this object. 0 + is not a possible return value. + + All objects provide this information about their children. + + \sa childCount() +*/ + +/*! + \fn QAccessible::Relation QAccessibleInterface::relationTo(int child, +const QAccessibleInterface *other, int otherChild) const + + Returns the relationship between this object's \a child and the \a + other object's \a otherChild. If \a child is 0 the object's own relation + is returned. + + The returned value indicates the relation of the called object to + the \a other object, e.g. if this object is a child of \a other + the return value will be \c Child. + + The return value is a combination of the bit flags in the + QAccessible::Relation enumeration. + + All objects provide this information. + + \sa indexOfChild(), navigate() +*/ + +/*! + \fn int QAccessibleInterface::childAt(int x, int y) const + + Returns the 1-based index of the child that contains the screen + coordinates (\a x, \a y). This function returns 0 if the point is + positioned on the object itself. If the tested point is outside + the boundaries of the object this function returns -1. + + This function is only relyable for visible objects (invisible + object might not be laid out correctly). + + All visual objects provide this information. + + \sa rect() +*/ + +/*! + \fn int QAccessibleInterface::navigate(RelationFlag relation, int entry, QAccessibleInterface +**target) const + + Navigates from this object to an object that has a relationship + \a relation to this object, and returns the respective object in + \a target. It is the caller's responsibility to delete *\a target + after use. + + If an object is found, \a target is set to point to the object, and + the index of the child of \a target is returned. The return value + is 0 if \a target itself is the requested object. \a target is set + to null if this object is the target object (i.e. the requested + object is a handled by this object). + + If no object is found \a target is set to null, and the return + value is -1. + + The \a entry parameter has two different meanings: + \list + \i \e{Hierarchical and Logical relationships} -- if multiple objects with + the requested relationship exist \a entry specifies which one to + return. \a entry is 1-based, e.g. use 1 to get the first (and + possibly only) object with the requested relationship. + + The following code demonstrates how to use this function to + navigate to the first child of an object: + + \snippet doc/src/snippets/code/src_gui_accessible_qaccessible.cpp 0 + + \i \e{Geometric relationships} -- the index of the child from + which to start navigating in the specified direction. \a entry + can be 0 to navigate to a sibling of this object, or non-null to + navigate within contained children that don't provide their own + accessible information. + \endlist + + Note that the \c Descendent value for \a relation is not supported. + + All objects support navigation. + + \sa relationTo(), childCount() +*/ + +/*! + \fn QString QAccessibleInterface::text(Text t, int child) const + + Returns the value of the text property \a t of the object, or of + the object's child if \a child is not 0. + + The \l Name is a string used by clients to identify, find, or + announce an accessible object for the user. All objects must have + a name that is unique within their container. The name can be + used differently by clients, so the name should both give a + short description of the object and be unique. + + An accessible object's \l Description provides textual information + about an object's visual appearance. The description is primarily + used to provide greater context for vision-impaired users, but is + also used for context searching or other applications. Not all + objects have a description. An "OK" button would not need a + description, but a tool button that shows a picture of a smiley + would. + + The \l Value of an accessible object represents visual information + contained by the object, e.g. the text in a line edit. Usually, + the value can be modified by the user. Not all objects have a + value, e.g. static text labels don't, and some objects have a + state that already is the value, e.g. toggle buttons. + + The \l Help text provides information about the function and + usage of an accessible object. Not all objects provide this + information. + + The \l Accelerator is a keyboard shortcut that activates the + object's default action. A keyboard shortcut is the underlined + character in the text of a menu, menu item or widget, and is + either the character itself, or a combination of this character + and a modifier key like Alt, Ctrl or Shift. Command controls like + tool buttons also have shortcut keys and usually display them in + their tooltip. + + All objects provide a string for \l Name. + + \sa role(), state() +*/ + +/*! + \fn void QAccessibleInterface::setText(Text t, int child, const QString &text) + + Sets the text property \a t of the object, or of the object's + child if \a child is not 0, to \a text. + + Note that the text properties of most objects are read-only. + + \sa text() +*/ + +/*! + \fn QRect QAccessibleInterface::rect(int child) const + + Returns the geometry of the object, or of the object's child if \a child + is not 0. The geometry is in screen coordinates. + + This function is only reliable for visible objects (invisible + objects might not be laid out correctly). + + All visual objects provide this information. + + \sa childAt() +*/ + +/*! + \fn QAccessible::Role QAccessibleInterface::role(int child) const + + Returns the role of the object, or of the object's child if \a child + is not 0. The role of an object is usually static. + + All accessible objects have a role. + + \sa text(), state() +*/ + +/*! + \fn QAccessible::State QAccessibleInterface::state(int child) const + + Returns the current state of the object, or of the object's child if + \a child is not 0. The returned value is a combination of the flags in + the QAccessible::StateFlag enumeration. + + All accessible objects have a state. + + \sa text(), role() +*/ + +/*! + \fn int QAccessibleInterface::userActionCount(int child) const + + Returns the number of custom actions of the object, or of the + object's child if \a child is not 0. + + The \c Action type enumerates predefined actions: these + are not included in the returned value. + + \sa actionText(), doAction() +*/ + +/*! + \fn QString QAccessibleInterface::actionText(int action, Text t, int child) const + + Returns the text property \a t of the action \a action supported by + the object, or of the object's child if \a child is not 0. + + \sa text(), userActionCount() +*/ + +/*! + \fn bool QAccessibleInterface::doAction(int action, int child, const QVariantList ¶ms) + + Asks the object, or the object's \a child if \a child is not 0, to + execute \a action using the parameters, \a params. Returns true if + the action could be executed; otherwise returns false. + + \a action can be a predefined or a custom action. + + \sa userActionCount(), actionText() +*/ + +/*! + \fn QColor QAccessibleInterface::backgroundColor() + \internal +*/ + +/*! + \fn QAccessibleEditableTextInterface *QAccessibleInterface::editableTextInterface() + \internal +*/ + +/*! + \fn QColor QAccessibleInterface::foregroundColor() + \internal +*/ + +/*! + \fn QAccessibleTextInterface *QAccessibleInterface::textInterface() + \internal +*/ + +/*! + \fn QAccessibleValueInterface *QAccessibleInterface::valueInterface() + \internal +*/ + +/*! + \fn QAccessibleTableInterface *QAccessibleInterface::tableInterface() + \internal +*/ + +/*! + \fn QAccessibleActionInterface *QAccessibleInterface::actionInterface() + \internal +*/ + +/*! + \fn QAccessibleImageInterface *QAccessibleInterface::imageInterface() + \internal +*/ + +/*! + \class QAccessibleEvent + \brief The QAccessibleEvent class is used to query addition + accessibility information about complex widgets. + + The event can be of type QEvent::AccessibilityDescription or + QEvent::AccessibilityHelp. + + Some QAccessibleInterface implementations send QAccessibleEvents + to the widget they wrap to obtain the description or help text of + a widget or of its children. The widget can answer by calling + setValue() with the requested information. + + The default QWidget::event() implementation simply sets the text + to be the widget's \l{QWidget::toolTip}{tooltip} (for \l + AccessibilityDescription event) or its + \l{QWidget::whatsThis}{"What's This?" text} (for \l + AccessibilityHelp event). + + \ingroup accessibility + \ingroup events +*/ + +/*! + \fn QAccessibleEvent::QAccessibleEvent(Type type, int child) + + Constructs an accessibility event of the given \a type, which + must be QEvent::AccessibilityDescription or + QEvent::AccessibilityHelp. + + \a child is the (1-based) index of the child to which the request + applies. If \a child is 0, the request is for the widget itself. + + \sa child() +*/ + +/*! + \fn int QAccessibleEvent::child() const + + Returns the (1-based) index of the child to which the request + applies. If the child is 0, the request is for the widget itself. +*/ + +/*! + \fn QString QAccessibleEvent::value() const + + Returns the text set using setValue(). + + \sa setValue() +*/ + +/*! + \fn void QAccessibleEvent::setValue(const QString &text) + + Set the description or help text for the given child() to \a + text, thereby answering the request. + + \sa value() +*/ + +/*! + \since 4.2 + + Invokes a \a method on \a child with the given parameters \a params + and returns the result of the operation as QVariant. + + Note that the type of the returned QVariant depends on the action. + + Returns an invalid QVariant if the object doesn't support the action. +*/ +QVariant QAccessibleInterface::invokeMethod(Method method, int child, const QVariantList ¶ms) +{ + if (!(state(0) & HasInvokeExtension)) + return QVariant(); + + return static_cast<QAccessibleInterfaceEx *>(this)->invokeMethodEx(method, child, params); +} + +QVariant QAccessibleInterfaceEx::virtual_hook(const QVariant &) +{ + return QVariant(); +} + +/*! \internal */ +QAccessible2Interface *QAccessibleInterface::cast_helper(QAccessible2::InterfaceType t) +{ + if (state(0) & HasInvokeExtension) + return static_cast<QAccessibleInterfaceEx *>(this)->interface_cast(t); + return 0; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/accessible/qaccessible.h b/src/gui/accessible/qaccessible.h new file mode 100644 index 0000000000..c71417352b --- /dev/null +++ b/src/gui/accessible/qaccessible.h @@ -0,0 +1,428 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QACCESSIBLE_H +#define QACCESSIBLE_H + +#include <QtCore/qglobal.h> +#include <QtCore/qobject.h> +#include <QtCore/qrect.h> +#include <QtCore/qset.h> +#include <QtCore/qvector.h> +#include <QtCore/qvariant.h> +#include <QtGui/qcolor.h> +#include <QtGui/qevent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_ACCESSIBILITY + +class QAccessibleInterface; + +class Q_GUI_EXPORT QAccessible +{ +public: + enum Event { + SoundPlayed = 0x0001, + Alert = 0x0002, + ForegroundChanged = 0x0003, + MenuStart = 0x0004, + MenuEnd = 0x0005, + PopupMenuStart = 0x0006, + PopupMenuEnd = 0x0007, + ContextHelpStart = 0x000C, + ContextHelpEnd = 0x000D, + DragDropStart = 0x000E, + DragDropEnd = 0x000F, + DialogStart = 0x0010, + DialogEnd = 0x0011, + ScrollingStart = 0x0012, + ScrollingEnd = 0x0013, + + MenuCommand = 0x0018, + + ObjectCreated = 0x8000, + ObjectDestroyed = 0x8001, + ObjectShow = 0x8002, + ObjectHide = 0x8003, + ObjectReorder = 0x8004, + Focus = 0x8005, + Selection = 0x8006, + SelectionAdd = 0x8007, + SelectionRemove = 0x8008, + SelectionWithin = 0x8009, + StateChanged = 0x800A, + LocationChanged = 0x800B, + NameChanged = 0x800C, + DescriptionChanged = 0x800D, + ValueChanged = 0x800E, + ParentChanged = 0x800F, + HelpChanged = 0x80A0, + DefaultActionChanged = 0x80B0, + AcceleratorChanged = 0x80C0 + }; + + enum StateFlag { + Normal = 0x00000000, + Unavailable = 0x00000001, + Selected = 0x00000002, + Focused = 0x00000004, + Pressed = 0x00000008, + Checked = 0x00000010, + Mixed = 0x00000020, + ReadOnly = 0x00000040, + HotTracked = 0x00000080, + DefaultButton = 0x00000100, + Expanded = 0x00000200, + Collapsed = 0x00000400, + Busy = 0x00000800, + // Floating = 0x00001000, + Marqueed = 0x00002000, + Animated = 0x00004000, + Invisible = 0x00008000, + Offscreen = 0x00010000, + Sizeable = 0x00020000, + Movable = 0x00040000, +#ifdef QT3_SUPPORT + Moveable = Movable, +#endif + SelfVoicing = 0x00080000, + Focusable = 0x00100000, + Selectable = 0x00200000, + Linked = 0x00400000, + Traversed = 0x00800000, + MultiSelectable = 0x01000000, + ExtSelectable = 0x02000000, + //AlertLow = 0x04000000, + //AlertMedium = 0x08000000, + //AlertHigh = 0x10000000, /* reused for HasInvokeExtension */ + Protected = 0x20000000, + HasPopup = 0x40000000, + Modal = 0x80000000, + + HasInvokeExtension = 0x10000000 // internal + }; + Q_DECLARE_FLAGS(State, StateFlag) + + enum Role { + NoRole = 0x00000000, + TitleBar = 0x00000001, + MenuBar = 0x00000002, + ScrollBar = 0x00000003, + Grip = 0x00000004, + Sound = 0x00000005, + Cursor = 0x00000006, + Caret = 0x00000007, + AlertMessage = 0x00000008, + Window = 0x00000009, + Client = 0x0000000A, + PopupMenu = 0x0000000B, + MenuItem = 0x0000000C, + ToolTip = 0x0000000D, + Application = 0x0000000E, + Document = 0x0000000F, + Pane = 0x00000010, + Chart = 0x00000011, + Dialog = 0x00000012, + Border = 0x00000013, + Grouping = 0x00000014, + Separator = 0x00000015, + ToolBar = 0x00000016, + StatusBar = 0x00000017, + Table = 0x00000018, + ColumnHeader = 0x00000019, + RowHeader = 0x0000001A, + Column = 0x0000001B, + Row = 0x0000001C, + Cell = 0x0000001D, + Link = 0x0000001E, + HelpBalloon = 0x0000001F, + Assistant = 0x00000020, + List = 0x00000021, + ListItem = 0x00000022, + Tree = 0x00000023, + TreeItem = 0x00000024, + PageTab = 0x00000025, + PropertyPage = 0x00000026, + Indicator = 0x00000027, + Graphic = 0x00000028, + StaticText = 0x00000029, + EditableText = 0x0000002A, // Editable, selectable, etc. + PushButton = 0x0000002B, + CheckBox = 0x0000002C, + RadioButton = 0x0000002D, + ComboBox = 0x0000002E, + // DropList = 0x0000002F, + ProgressBar = 0x00000030, + Dial = 0x00000031, + HotkeyField = 0x00000032, + Slider = 0x00000033, + SpinBox = 0x00000034, + Canvas = 0x00000035, + Animation = 0x00000036, + Equation = 0x00000037, + ButtonDropDown = 0x00000038, + ButtonMenu = 0x00000039, + ButtonDropGrid = 0x0000003A, + Whitespace = 0x0000003B, + PageTabList = 0x0000003C, + Clock = 0x0000003D, + Splitter = 0x0000003E, + // Additional Qt roles where enum value does not map directly to MSAA: + LayeredPane = 0x0000003F, + UserRole = 0x0000ffff + }; + + enum Text { + Name = 0, + Description, + Value, + Help, + Accelerator, + UserText = 0x0000ffff + }; + + enum RelationFlag { + Unrelated = 0x00000000, + Self = 0x00000001, + Ancestor = 0x00000002, + Child = 0x00000004, + Descendent = 0x00000008, + Sibling = 0x00000010, + HierarchyMask = 0x000000ff, + + Up = 0x00000100, + Down = 0x00000200, + Left = 0x00000400, + Right = 0x00000800, + Covers = 0x00001000, + Covered = 0x00002000, + GeometryMask = 0x0000ff00, + + FocusChild = 0x00010000, + Label = 0x00020000, + Labelled = 0x00040000, + Controller = 0x00080000, + Controlled = 0x00100000, + LogicalMask = 0x00ff0000 + }; + Q_DECLARE_FLAGS(Relation, RelationFlag) + + enum Action { + DefaultAction = 0, + Press = -1, + FirstStandardAction = Press, + SetFocus = -2, + Increase = -3, + Decrease = -4, + Accept = -5, + Cancel = -6, + Select = -7, + ClearSelection = -8, + RemoveSelection = -9, + ExtendSelection = -10, + AddToSelection = -11, + LastStandardAction = AddToSelection + }; + + enum Method { + ListSupportedMethods = 0, + SetCursorPosition = 1, + GetCursorPosition = 2, + ForegroundColor = 3, + BackgroundColor = 4 + }; + + typedef QAccessibleInterface*(*InterfaceFactory)(const QString &key, QObject*); + typedef void(*UpdateHandler)(QObject*, int who, Event reason); + typedef void(*RootObjectHandler)(QObject*); + + static void installFactory(InterfaceFactory); + static void removeFactory(InterfaceFactory); + static UpdateHandler installUpdateHandler(UpdateHandler); + static RootObjectHandler installRootObjectHandler(RootObjectHandler); + + static QAccessibleInterface *queryAccessibleInterface(QObject *); + static void updateAccessibility(QObject *, int who, Event reason); + static bool isActive(); + static void setRootObject(QObject*); + + static void initialize(); + static void cleanup(); + +private: + static UpdateHandler updateHandler; + static RootObjectHandler rootObjectHandler; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QAccessible::State) +Q_DECLARE_OPERATORS_FOR_FLAGS(QAccessible::Relation) +QT_END_NAMESPACE +Q_DECLARE_METATYPE(QSet<QAccessible::Method>) +QT_BEGIN_NAMESPACE + +namespace QAccessible2 +{ + enum InterfaceType + { + TextInterface, + EditableTextInterface, + ValueInterface, + TableInterface, + ActionInterface, + ImageInterface + }; +} + +class QAccessible2Interface; +class QAccessibleTextInterface; +class QAccessibleEditableTextInterface; +class QAccessibleValueInterface; +class QAccessibleTableInterface; +class QAccessibleActionInterface; +class QAccessibleImageInterface; + +class Q_GUI_EXPORT QAccessibleInterface : public QAccessible +{ +public: + virtual ~QAccessibleInterface() {} + // check for valid pointers + virtual bool isValid() const = 0; + virtual QObject *object() const = 0; + + // hierarchy + virtual int childCount() const = 0; + virtual int indexOfChild(const QAccessibleInterface *) const = 0; + + // relations + virtual Relation relationTo(int child, const QAccessibleInterface *other, + int otherChild) const = 0; + virtual int childAt(int x, int y) const = 0; + + // navigation + virtual int navigate(RelationFlag relation, int index, QAccessibleInterface **iface) const = 0; + + // properties and state + virtual QString text(Text t, int child) const = 0; + virtual void setText(Text t, int child, const QString &text) = 0; + virtual QRect rect(int child) const = 0; + virtual Role role(int child) const = 0; + virtual State state(int child) const = 0; + + // action + virtual int userActionCount(int child) const = 0; + virtual QString actionText(int action, Text t, int child) const = 0; + virtual bool doAction(int action, int child, const QVariantList ¶ms = QVariantList()) = 0; + + QVariant invokeMethod(Method method, int child = 0, + const QVariantList ¶ms = QVariantList()); + + inline QSet<Method> supportedMethods() + { return qvariant_cast<QSet<Method> >(invokeMethod(ListSupportedMethods)); } + + inline QColor foregroundColor() + { return qvariant_cast<QColor>(invokeMethod(ForegroundColor)); } + + inline QColor backgroundColor() + { return qvariant_cast<QColor>(invokeMethod(BackgroundColor)); } + + inline QAccessibleTextInterface *textInterface() + { return reinterpret_cast<QAccessibleTextInterface *>(cast_helper(QAccessible2::TextInterface)); } + + inline QAccessibleEditableTextInterface *editableTextInterface() + { return reinterpret_cast<QAccessibleEditableTextInterface *>(cast_helper(QAccessible2::EditableTextInterface)); } + + inline QAccessibleValueInterface *valueInterface() + { return reinterpret_cast<QAccessibleValueInterface *>(cast_helper(QAccessible2::ValueInterface)); } + + inline QAccessibleTableInterface *tableInterface() + { return reinterpret_cast<QAccessibleTableInterface *>(cast_helper(QAccessible2::TableInterface)); } + + inline QAccessibleActionInterface *actionInterface() + { return reinterpret_cast<QAccessibleActionInterface *>(cast_helper(QAccessible2::ActionInterface)); } + + inline QAccessibleImageInterface *imageInterface() + { return reinterpret_cast<QAccessibleImageInterface *>(cast_helper(QAccessible2::ImageInterface)); } + +private: + QAccessible2Interface *cast_helper(QAccessible2::InterfaceType); +}; + +class Q_GUI_EXPORT QAccessibleInterfaceEx: public QAccessibleInterface +{ +public: + virtual QVariant invokeMethodEx(Method method, int child, const QVariantList ¶ms) = 0; + virtual QVariant virtual_hook(const QVariant &data); + virtual QAccessible2Interface *interface_cast(QAccessible2::InterfaceType) + { return 0; } +}; + + +class Q_GUI_EXPORT QAccessibleEvent : public QEvent +{ +public: + inline QAccessibleEvent(Type type, int child); + inline int child() const { return c; } + inline QString value() const { return val; } + inline void setValue(const QString &aText) { val = aText; } + +private: + int c; + QString val; +}; + +inline QAccessibleEvent::QAccessibleEvent(Type atype, int achild) + : QEvent(atype), c(achild) {} + +#define QAccessibleInterface_iid "com.trolltech.Qt.QAccessibleInterface" +Q_DECLARE_INTERFACE(QAccessibleInterface, QAccessibleInterface_iid) + +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QACCESSIBLE_H diff --git a/src/gui/accessible/qaccessible2.cpp b/src/gui/accessible/qaccessible2.cpp new file mode 100644 index 0000000000..36187f5315 --- /dev/null +++ b/src/gui/accessible/qaccessible2.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessible2.h" +#include "qapplication.h" +#include "qclipboard.h" + +#ifndef QT_NO_ACCESSIBILITY + +QT_BEGIN_NAMESPACE + +/*! + \namespace QAccessible2 + \ingroup accessibility + \internal + \preliminary + + \brief The QAccessible2 namespace defines constants relating to + IAccessible2-based interfaces + + \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink +*/ + +/*! + \class QAccessibleTextInterface + + \ingroup accessibility + \internal + \preliminary + + \brief The QAccessibleTextInterface class implements support for + the IAccessibleText interface. + + \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink +*/ + +/*! + \class QAccessibleEditableTextInterface + \ingroup accessibility + \internal + \preliminary + + \brief The QAccessibleEditableTextInterface class implements support for + the IAccessibleEditableText interface. + + \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink +*/ + +/*! + \class QAccessibleSimpleEditableTextInterface + \ingroup accessibility + \internal + \preliminary + + \brief The QAccessibleSimpleEditableTextInterface class is a convenience class for + text-based widgets. + + \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink +*/ + +/*! + \class QAccessibleValueInterface + \ingroup accessibility + \internal + \preliminary + + \brief The QAccessibleValueInterface class implements support for + the IAccessibleValue interface. + + \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink +*/ + +/*! + \class QAccessibleActionInterface + \ingroup accessibility + \internal + \preliminary + + \brief The QAccessibleActionInterface class implements support for + the IAccessibleAction interface. + + \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink +*/ + +/*! + \class QAccessibleImageInterface + \ingroup accessibility + \internal + \preliminary + + \brief The QAccessibleImageInterface class implements support for + the IAccessibleImage interface. + + \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink +*/ + +QAccessibleSimpleEditableTextInterface::QAccessibleSimpleEditableTextInterface( + QAccessibleInterface *accessibleInterface) + : iface(accessibleInterface) +{ + Q_ASSERT(iface); +} + +#ifndef QT_NO_CLIPBOARD +static QString textForRange(QAccessibleInterface *iface, int startOffset, int endOffset) +{ + return iface->text(QAccessible::Value, 0).mid(startOffset, endOffset - startOffset); +} +#endif + +void QAccessibleSimpleEditableTextInterface::copyText(int startOffset, int endOffset) +{ +#ifdef QT_NO_CLIPBOARD + Q_UNUSED(startOffset); + Q_UNUSED(endOffset); +#else + QApplication::clipboard()->setText(textForRange(iface, startOffset, endOffset)); +#endif +} + +void QAccessibleSimpleEditableTextInterface::deleteText(int startOffset, int endOffset) +{ + QString txt = iface->text(QAccessible::Value, 0); + txt.remove(startOffset, endOffset - startOffset); + iface->setText(QAccessible::Value, 0, txt); +} + +void QAccessibleSimpleEditableTextInterface::insertText(int offset, const QString &text) +{ + QString txt = iface->text(QAccessible::Value, 0); + txt.insert(offset, text); + iface->setText(QAccessible::Value, 0, txt); +} + +void QAccessibleSimpleEditableTextInterface::cutText(int startOffset, int endOffset) +{ +#ifdef QT_NO_CLIPBOARD + Q_UNUSED(startOffset); + Q_UNUSED(endOffset); +#else + QString sub = textForRange(iface, startOffset, endOffset); + deleteText(startOffset, endOffset); + QApplication::clipboard()->setText(sub); +#endif +} + +void QAccessibleSimpleEditableTextInterface::pasteText(int offset) +{ +#ifdef QT_NO_CLIPBOARD + Q_UNUSED(offset); +#else + QString txt = iface->text(QAccessible::Value, 0); + txt.insert(offset, QApplication::clipboard()->text()); + iface->setText(QAccessible::Value, 0, txt); +#endif +} + +void QAccessibleSimpleEditableTextInterface::replaceText(int startOffset, int endOffset, const QString &text) +{ + QString txt = iface->text(QAccessible::Value, 0); + txt.replace(startOffset, endOffset - startOffset, text); + iface->setText(QAccessible::Value, 0, txt); +} + +QT_END_NAMESPACE + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/gui/accessible/qaccessible2.h b/src/gui/accessible/qaccessible2.h new file mode 100644 index 0000000000..65ab7166c6 --- /dev/null +++ b/src/gui/accessible/qaccessible2.h @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QACCESSIBLE2_H +#define QACCESSIBLE2_H + +#include <QtGui/qaccessible.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_ACCESSIBILITY + +namespace QAccessible2 +{ + enum CoordinateType + { + RelativeToScreen = 0, + RelativeToParent = 1 + }; + + enum BoundaryType { + CharBoundary, + WordBoundary, + SentenceBoundary, + ParagraphBoundary, + LineBoundary, + NoBoundary + }; +} + +class Q_GUI_EXPORT QAccessible2Interface +{ +public: + virtual ~QAccessible2Interface() {} +}; + +// catch-all functions. If an accessible class doesn't implement interface T, return 0 +inline QAccessible2Interface *qAccessibleValueCastHelper() { return 0; } +inline QAccessible2Interface *qAccessibleTextCastHelper() { return 0; } +inline QAccessible2Interface *qAccessibleEditableTextCastHelper() { return 0; } +inline QAccessible2Interface *qAccessibleTableCastHelper() { return 0; } +inline QAccessible2Interface *qAccessibleActionCastHelper() { return 0; } +inline QAccessible2Interface *qAccessibleImageCastHelper() { return 0; } + +#define Q_ACCESSIBLE_OBJECT \ + public: \ + QAccessible2Interface *interface_cast(QAccessible2::InterfaceType t) \ + { \ + switch (t) { \ + case QAccessible2::TextInterface: \ + return qAccessibleTextCastHelper(); \ + case QAccessible2::EditableTextInterface: \ + return qAccessibleEditableTextCastHelper(); \ + case QAccessible2::ValueInterface: \ + return qAccessibleValueCastHelper(); \ + case QAccessible2::TableInterface: \ + return qAccessibleTableCastHelper(); \ + case QAccessible2::ActionInterface: \ + return qAccessibleActionCastHelper(); \ + case QAccessible2::ImageInterface: \ + return qAccessibleImageCastHelper(); \ + } \ + return 0; \ + } \ + private: + +class Q_GUI_EXPORT QAccessibleTextInterface: public QAccessible2Interface +{ +public: + inline QAccessible2Interface *qAccessibleTextCastHelper() { return this; } + + virtual ~QAccessibleTextInterface() {} + + virtual void addSelection(int startOffset, int endOffset) = 0; + virtual QString attributes(int offset, int *startOffset, int *endOffset) = 0; + virtual int cursorPosition() = 0; + virtual QRect characterRect(int offset, QAccessible2::CoordinateType coordType) = 0; + virtual int selectionCount() = 0; + virtual int offsetAtPoint(const QPoint &point, QAccessible2::CoordinateType coordType) = 0; + virtual void selection(int selectionIndex, int *startOffset, int *endOffset) = 0; + virtual QString text(int startOffset, int endOffset) = 0; + virtual QString textBeforeOffset (int offset, QAccessible2::BoundaryType boundaryType, + int *startOffset, int *endOffset) = 0; + virtual QString textAfterOffset(int offset, QAccessible2::BoundaryType boundaryType, + int *startOffset, int *endOffset) = 0; + virtual QString textAtOffset(int offset, QAccessible2::BoundaryType boundaryType, + int *startOffset, int *endOffset) = 0; + virtual void removeSelection(int selectionIndex) = 0; + virtual void setCursorPosition(int position) = 0; + virtual void setSelection(int selectionIndex, int startOffset, int endOffset) = 0; + virtual int characterCount() = 0; + virtual void scrollToSubstring(int startIndex, int endIndex) = 0; +}; + +class Q_GUI_EXPORT QAccessibleEditableTextInterface: public QAccessible2Interface +{ +public: + inline QAccessible2Interface *qAccessibleEditableTextCastHelper() { return this; } + + virtual ~QAccessibleEditableTextInterface() {} + + virtual void copyText(int startOffset, int endOffset) = 0; + virtual void deleteText(int startOffset, int endOffset) = 0; + virtual void insertText(int offset, const QString &text) = 0; + virtual void cutText(int startOffset, int endOffset) = 0; + virtual void pasteText(int offset) = 0; + virtual void replaceText(int startOffset, int endOffset, const QString &text) = 0; + virtual void setAttributes(int startOffset, int endOffset, const QString &attributes) = 0; +}; + +class Q_GUI_EXPORT QAccessibleSimpleEditableTextInterface: public QAccessibleEditableTextInterface +{ +public: + QAccessibleSimpleEditableTextInterface(QAccessibleInterface *accessibleInterface); + + void copyText(int startOffset, int endOffset); + void deleteText(int startOffset, int endOffset); + void insertText(int offset, const QString &text); + void cutText(int startOffset, int endOffset); + void pasteText(int offset); + void replaceText(int startOffset, int endOffset, const QString &text); + inline void setAttributes(int, int, const QString &) {} + +private: + QAccessibleInterface *iface; +}; + +class Q_GUI_EXPORT QAccessibleValueInterface: public QAccessible2Interface +{ +public: + inline QAccessible2Interface *qAccessibleValueCastHelper() { return this; } + + virtual ~QAccessibleValueInterface() {} + + virtual QVariant currentValue() = 0; + virtual void setCurrentValue(const QVariant &value) = 0; + virtual QVariant maximumValue() = 0; + virtual QVariant minimumValue() = 0; +}; + +class Q_GUI_EXPORT QAccessibleTableInterface: public QAccessible2Interface +{ +public: + inline QAccessible2Interface *qAccessibleTableCastHelper() { return this; } + + virtual QAccessibleInterface *accessibleAt(int row, int column) = 0; + virtual QAccessibleInterface *caption() = 0; + virtual int childIndex(int rowIndex, int columnIndex) = 0; + virtual QString columnDescription(int column) = 0; + virtual int columnSpan(int row, int column) = 0; + virtual QAccessibleInterface *columnHeader() = 0; + virtual int columnIndex(int childIndex) = 0; + virtual int columnCount() = 0; + virtual int rowCount() = 0; + virtual int selectedColumnCount() = 0; + virtual int selectedRowCount() = 0; + virtual QString rowDescription(int row) = 0; + virtual int rowSpan(int row, int column) = 0; + virtual QAccessibleInterface *rowHeader() = 0; + virtual int rowIndex(int childIndex) = 0; + virtual int selectedRows(int maxRows, QList<int> *rows) = 0; + virtual int selectedColumns(int maxColumns, QList<int> *columns) = 0; + virtual QAccessibleInterface *summary() = 0; + virtual bool isColumnSelected(int column) = 0; + virtual bool isRowSelected(int row) = 0; + virtual bool isSelected(int row, int column) = 0; + virtual void selectRow(int row) = 0; + virtual void selectColumn(int column) = 0; + virtual void unselectRow(int row) = 0; + virtual void unselectColumn(int column) = 0; + virtual void cellAtIndex(int index, int *row, int *column, int *rowSpan, + int *columnSpan, bool *isSelected) = 0; +}; + +class Q_GUI_EXPORT QAccessibleActionInterface : public QAccessible2Interface +{ +public: + inline QAccessible2Interface *qAccessibleActionCastHelper() { return this; } + + virtual int actionCount() = 0; + virtual void doAction(int actionIndex) = 0; + virtual QString description(int actionIndex) = 0; + virtual QString name(int actionIndex) = 0; + virtual QString localizedName(int actionIndex) = 0; + virtual QStringList keyBindings(int actionIndex) = 0; +}; + +class Q_GUI_EXPORT QAccessibleImageInterface : public QAccessible2Interface +{ +public: + inline QAccessible2Interface *qAccessibleImageCastHelper() { return this; } + + virtual QString imageDescription() = 0; + virtual QSize imageSize() = 0; + virtual QRect imagePosition(QAccessible2::CoordinateType coordType) = 0; +}; + +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/accessible/qaccessible_mac.mm b/src/gui/accessible/qaccessible_mac.mm new file mode 100644 index 0000000000..d01c1c9733 --- /dev/null +++ b/src/gui/accessible/qaccessible_mac.mm @@ -0,0 +1,2469 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessible.h" + +#ifndef QT_NO_ACCESSIBILITY +#include "qaccessible_mac_p.h" +#include "qhash.h" +#include "qset.h" +#include "qpointer.h" +#include "qapplication.h" +#include "qmainwindow.h" +#include "qtextdocument.h" +#include "qdebug.h" +#include "qabstractslider.h" +#include "qsplitter.h" +#include "qtabwidget.h" +#include "qlistview.h" +#include "qtableview.h" +#include "qdockwidget.h" + +#include <private/qt_mac_p.h> +#include <private/qwidget_p.h> +#include <CoreFoundation/CoreFoundation.h> + +QT_BEGIN_NAMESPACE + +/* + Set up platform defines. There is a one-to-one correspondence between the + Carbon and Cocoa roles and attributes, but the prefix and type changes. +*/ +#ifdef QT_MAC_USE_COCOA +typedef NSString * const QAXRoleType; +#define QAXApplicationRole NSAccessibilityApplicationRole +#define QAXButtonRole NSAccessibilityButtonRole +#define QAXCancelAction NSAccessibilityCancelAction +#define QAXCheckBoxRole NSAccessibilityCheckBoxRole +#define QAXChildrenAttribute NSAccessibilityChildrenAttribute +#define QAXCloseButtonAttribute NSAccessibilityCloseButtonAttribute +#define QAXCloseButtonAttribute NSAccessibilityCloseButtonAttribute +#define QAXColumnRole NSAccessibilityColumnRole +#define QAXConfirmAction NSAccessibilityConfirmAction +#define QAXContentsAttribute NSAccessibilityContentsAttribute +#define QAXDecrementAction NSAccessibilityDecrementAction +#define QAXDecrementArrowSubrole NSAccessibilityDecrementArrowSubrole +#define QAXDecrementPageSubrole NSAccessibilityDecrementPageSubrole +#define QAXDescriptionAttribute NSAccessibilityDescriptionAttribute +#define QAXEnabledAttribute NSAccessibilityEnabledAttribute +#define QAXExpandedAttribute NSAccessibilityExpandedAttribute +#define QAXFocusedAttribute NSAccessibilityFocusedAttribute +#define QAXFocusedUIElementChangedNotification NSAccessibilityFocusedUIElementChangedNotification +#define QAXFocusedWindowChangedNotification NSAccessibilityFocusedWindowChangedNotification +#define QAXGroupRole NSAccessibilityGroupRole +#define QAXGrowAreaAttribute NSAccessibilityGrowAreaAttribute +#define QAXGrowAreaRole NSAccessibilityGrowAreaRole +#define QAXHelpAttribute NSAccessibilityHelpAttribute +#define QAXHorizontalOrientationValue NSAccessibilityHorizontalOrientationValue +#define QAXHorizontalScrollBarAttribute NSAccessibilityHorizontalScrollBarAttribute +#define QAXIncrementAction NSAccessibilityIncrementAction +#define QAXIncrementArrowSubrole NSAccessibilityIncrementArrowSubrole +#define QAXIncrementPageSubrole NSAccessibilityIncrementPageSubrole +#define QAXIncrementorRole NSAccessibilityIncrementorRole +#define QAXLinkedUIElementsAttribute NSAccessibilityLinkedUIElementsAttribute +#define QAXListRole NSAccessibilityListRole +#define QAXMainAttribute NSAccessibilityMainAttribute +#define QAXMaxValueAttribute NSAccessibilityMaxValueAttribute +#define QAXMenuBarRole NSAccessibilityMenuBarRole +#define QAXMenuButtonRole NSAccessibilityMenuButtonRole +#define QAXMenuClosedNotification NSAccessibilityMenuClosedNotification +#define QAXMenuItemRole NSAccessibilityMenuItemRole +#define QAXMenuOpenedNotification NSAccessibilityMenuOpenedNotification +#define QAXMenuRole NSAccessibilityMenuRole +#define QAXMinValueAttribute NSAccessibilityMinValueAttribute +#define QAXMinimizeButtonAttribute NSAccessibilityMinimizeButtonAttribute +#define QAXMinimizedAttribute NSAccessibilityMinimizedAttribute +#define QAXNextContentsAttribute NSAccessibilityNextContentsAttribute +#define QAXOrientationAttribute NSAccessibilityOrientationAttribute +#define QAXParentAttribute NSAccessibilityParentAttribute +#define QAXPickAction NSAccessibilityPickAction +#define QAXPopUpButtonRole NSAccessibilityPopUpButtonRole +#define QAXPositionAttribute NSAccessibilityPositionAttribute +#define QAXPressAction NSAccessibilityPressAction +#define QAXPreviousContentsAttribute NSAccessibilityPreviousContentsAttribute +#define QAXProgressIndicatorRole NSAccessibilityProgressIndicatorRole +#define QAXRadioButtonRole NSAccessibilityRadioButtonRole +#define QAXRoleAttribute NSAccessibilityRoleAttribute +#define QAXRoleDescriptionAttribute NSAccessibilityRoleDescriptionAttribute +#define QAXRowRole NSAccessibilityRowRole +#define QAXRowsAttribute NSAccessibilityRowsAttribute +#define QAXScrollAreaRole NSAccessibilityScrollAreaRole +#define QAXScrollBarRole NSAccessibilityScrollBarRole +#define QAXSelectedAttribute NSAccessibilitySelectedAttribute +#define QAXSelectedChildrenAttribute NSAccessibilitySelectedChildrenAttribute +#define QAXSelectedRowsAttribute NSAccessibilitySelectedRowsAttribute +#define QAXSizeAttribute NSAccessibilitySizeAttribute +#define QAXSliderRole NSAccessibilitySliderRole +#define QAXSplitGroupRole NSAccessibilitySplitGroupRole +#define QAXSplitterRole NSAccessibilitySplitterRole +#define QAXSplittersAttribute NSAccessibilitySplittersAttribute +#define QAXStaticTextRole NSAccessibilityStaticTextRole +#define QAXSubroleAttribute NSAccessibilitySubroleAttribute +#define QAXSubroleAttribute NSAccessibilitySubroleAttribute +#define QAXTabGroupRole NSAccessibilityTabGroupRole +#define QAXTableRole NSAccessibilityTableRole +#define QAXTabsAttribute NSAccessibilityTabsAttribute +#define QAXTextFieldRole NSAccessibilityTextFieldRole +#define QAXTitleAttribute NSAccessibilityTitleAttribute +#define QAXTitleUIElementAttribute NSAccessibilityTitleUIElementAttribute +#define QAXToolbarButtonAttribute NSAccessibilityToolbarButtonAttribute +#define QAXToolbarRole NSAccessibilityToolbarRole +#define QAXTopLevelUIElementAttribute NSAccessibilityTopLevelUIElementAttribute +#define QAXUnknownRole NSAccessibilityUnknownRole +#define QAXValueAttribute NSAccessibilityValueAttribute +#define QAXValueChangedNotification NSAccessibilityValueChangedNotification +#define QAXValueIndicatorRole NSAccessibilityValueIndicatorRole +#define QAXVerticalOrientationValue NSAccessibilityVerticalOrientationValue +#define QAXVerticalScrollBarAttribute NSAccessibilityVerticalScrollBarAttribute +#define QAXVisibleRowsAttribute NSAccessibilityVisibleRowsAttribute +#define QAXWindowAttribute NSAccessibilityWindowAttribute +#define QAXWindowCreatedNotification NSAccessibilityWindowCreatedNotification +#define QAXWindowMovedNotification NSAccessibilityWindowMovedNotification +#define QAXWindowRole NSAccessibilityWindowRole +#define QAXZoomButtonAttribute NSAccessibilityZoomButtonAttribute +#else +typedef CFStringRef const QAXRoleType; +#define QAXApplicationRole kAXApplicationRole +#define QAXButtonRole kAXButtonRole +#define QAXCancelAction kAXCancelAction +#define QAXCheckBoxRole kAXCheckBoxRole +#define QAXChildrenAttribute kAXChildrenAttribute +#define QAXCloseButtonAttribute kAXCloseButtonAttribute +#define QAXColumnRole kAXColumnRole +#define QAXConfirmAction kAXConfirmAction +#define QAXContentsAttribute kAXContentsAttribute +#define QAXDecrementAction kAXDecrementAction +#define QAXDecrementArrowSubrole kAXDecrementArrowSubrole +#define QAXDecrementPageSubrole kAXDecrementPageSubrole +#define QAXDescriptionAttribute kAXDescriptionAttribute +#define QAXEnabledAttribute kAXEnabledAttribute +#define QAXExpandedAttribute kAXExpandedAttribute +#define QAXFocusedAttribute kAXFocusedAttribute +#define QAXFocusedUIElementChangedNotification kAXFocusedUIElementChangedNotification +#define QAXFocusedWindowChangedNotification kAXFocusedWindowChangedNotification +#define QAXGroupRole kAXGroupRole +#define QAXGrowAreaAttribute kAXGrowAreaAttribute +#define QAXGrowAreaRole kAXGrowAreaRole +#define QAXHelpAttribute kAXHelpAttribute +#define QAXHorizontalOrientationValue kAXHorizontalOrientationValue +#define QAXHorizontalScrollBarAttribute kAXHorizontalScrollBarAttribute +#define QAXIncrementAction kAXIncrementAction +#define QAXIncrementArrowSubrole kAXIncrementArrowSubrole +#define QAXIncrementPageSubrole kAXIncrementPageSubrole +#define QAXIncrementorRole kAXIncrementorRole +#define QAXLinkedUIElementsAttribute kAXLinkedUIElementsAttribute +#define QAXListRole kAXListRole +#define QAXMainAttribute kAXMainAttribute +#define QAXMaxValueAttribute kAXMaxValueAttribute +#define QAXMenuBarRole kAXMenuBarRole +#define QAXMenuButtonRole kAXMenuButtonRole +#define QAXMenuClosedNotification kAXMenuClosedNotification +#define QAXMenuItemRole kAXMenuItemRole +#define QAXMenuOpenedNotification kAXMenuOpenedNotification +#define QAXMenuRole kAXMenuRole +#define QAXMinValueAttribute kAXMinValueAttribute +#define QAXMinimizeButtonAttribute kAXMinimizeButtonAttribute +#define QAXMinimizedAttribute kAXMinimizedAttribute +#define QAXNextContentsAttribute kAXNextContentsAttribute +#define QAXOrientationAttribute kAXOrientationAttribute +#define QAXParentAttribute kAXParentAttribute +#define QAXPickAction kAXPickAction +#define QAXPopUpButtonRole kAXPopUpButtonRole +#define QAXPositionAttribute kAXPositionAttribute +#define QAXPressAction kAXPressAction +#define QAXPreviousContentsAttribute kAXPreviousContentsAttribute +#define QAXProgressIndicatorRole kAXProgressIndicatorRole +#define QAXRadioButtonRole kAXRadioButtonRole +#define QAXRoleAttribute kAXRoleAttribute +#define QAXRoleDescriptionAttribute kAXRoleDescriptionAttribute +#define QAXRowRole kAXRowRole +#define QAXRowsAttribute kAXRowsAttribute +#define QAXScrollAreaRole kAXScrollAreaRole +#define QAXScrollBarRole kAXScrollBarRole +#define QAXSelectedAttribute kAXSelectedAttribute +#define QAXSelectedChildrenAttribute kAXSelectedChildrenAttribute +#define QAXSelectedRowsAttribute kAXSelectedRowsAttribute +#define QAXSizeAttribute kAXSizeAttribute +#define QAXSliderRole kAXSliderRole +#define QAXSplitGroupRole kAXSplitGroupRole +#define QAXSplitterRole kAXSplitterRole +#define QAXSplittersAttribute kAXSplittersAttribute +#define QAXStaticTextRole kAXStaticTextRole +#define QAXSubroleAttribute kAXSubroleAttribute +#define QAXTabGroupRole kAXTabGroupRole +#define QAXTableRole kAXTableRole +#define QAXTabsAttribute kAXTabsAttribute +#define QAXTextFieldRole kAXTextFieldRole +#define QAXTitleAttribute kAXTitleAttribute +#define QAXTitleUIElementAttribute kAXTitleUIElementAttribute +#define QAXToolbarButtonAttribute kAXToolbarButtonAttribute +#define QAXToolbarRole kAXToolbarRole +#define QAXTopLevelUIElementAttribute kAXTopLevelUIElementAttribute +#define QAXUnknownRole kAXUnknownRole +#define QAXValueAttribute kAXValueAttribute +#define QAXValueChangedNotification kAXValueChangedNotification +#define QAXValueIndicatorRole kAXValueIndicatorRole +#define QAXVerticalOrientationValue kAXVerticalOrientationValue +#define QAXVerticalScrollBarAttribute kAXVerticalScrollBarAttribute +#define QAXVisibleRowsAttribute kAXVisibleRowsAttribute +#define QAXWindowAttribute kAXWindowAttribute +#define QAXWindowCreatedNotification kAXWindowCreatedNotification +#define QAXWindowMovedNotification kAXWindowMovedNotification +#define QAXWindowRole kAXWindowRole +#define QAXZoomButtonAttribute kAXZoomButtonAttribute +#endif + + +/***************************************************************************** + Externals + *****************************************************************************/ +extern bool qt_mac_is_macsheet(const QWidget *w); //qwidget_mac.cpp +extern bool qt_mac_is_macdrawer(const QWidget *w); //qwidget_mac.cpp + +/***************************************************************************** + QAccessible Bindings + *****************************************************************************/ +//hardcoded bindings between control info and (known) QWidgets +struct QAccessibleTextBinding { + int qt; + QAXRoleType mac; + bool settable; +} text_bindings[][10] = { + { { QAccessible::MenuItem, QAXMenuItemRole, false }, + { -1, 0, false } + }, + { { QAccessible::MenuBar, QAXMenuBarRole, false }, + { -1, 0, false } + }, + { { QAccessible::ScrollBar, QAXScrollBarRole, false }, + { -1, 0, false } + }, + { { QAccessible::Grip, QAXGrowAreaRole, false }, + { -1, 0, false } + }, + { { QAccessible::Window, QAXWindowRole, false }, + { -1, 0, false } + }, + { { QAccessible::Dialog, QAXWindowRole, false }, + { -1, 0, false } + }, + { { QAccessible::AlertMessage, QAXWindowRole, false }, + { -1, 0, false } + }, + { { QAccessible::ToolTip, QAXWindowRole, false }, + { -1, 0, false } + }, + { { QAccessible::HelpBalloon, QAXWindowRole, false }, + { -1, 0, false } + }, + { { QAccessible::PopupMenu, QAXMenuRole, false }, + { -1, 0, false } + }, + { { QAccessible::Application, QAXApplicationRole, false }, + { -1, 0, false } + }, + { { QAccessible::Pane, QAXGroupRole, false }, + { -1, 0, false } + }, + { { QAccessible::Grouping, QAXGroupRole, false }, + { -1, 0, false } + }, + { { QAccessible::Separator, QAXSplitterRole, false }, + { -1, 0, false } + }, + { { QAccessible::ToolBar, QAXToolbarRole, false }, + { -1, 0, false } + }, + { { QAccessible::PageTab, QAXRadioButtonRole, false }, + { -1, 0, false } + }, + { { QAccessible::ButtonMenu, QAXMenuButtonRole, false }, + { -1, 0, false } + }, + { { QAccessible::ButtonDropDown, QAXPopUpButtonRole, false }, + { -1, 0, false } + }, + { { QAccessible::SpinBox, QAXIncrementorRole, false }, + { -1, 0, false } + }, + { { QAccessible::Slider, QAXSliderRole, false }, + { -1, 0, false } + }, + { { QAccessible::ProgressBar, QAXProgressIndicatorRole, false }, + { -1, 0, false } + }, + { { QAccessible::ComboBox, QAXPopUpButtonRole, false }, + { -1, 0, false } + }, + { { QAccessible::RadioButton, QAXRadioButtonRole, false }, + { -1, 0, false } + }, + { { QAccessible::CheckBox, QAXCheckBoxRole, false }, + { -1, 0, false } + }, + { { QAccessible::StaticText, QAXStaticTextRole, false }, + { QAccessible::Name, QAXValueAttribute, false }, + { -1, 0, false } + }, + { { QAccessible::Table, QAXTableRole, false }, + { -1, 0, false } + }, + { { QAccessible::StatusBar, QAXStaticTextRole, false }, + { -1, 0, false } + }, + { { QAccessible::Column, QAXColumnRole, false }, + { -1, 0, false } + }, + { { QAccessible::ColumnHeader, QAXColumnRole, false }, + { -1, 0, false } + }, + { { QAccessible::Row, QAXRowRole, false }, + { -1, 0, false } + }, + { { QAccessible::RowHeader, QAXRowRole, false }, + { -1, 0, false } + }, + { { QAccessible::Cell, QAXTextFieldRole, false }, + { -1, 0, false } + }, + { { QAccessible::PushButton, QAXButtonRole, false }, + { -1, 0, false } + }, + { { QAccessible::EditableText, QAXTextFieldRole, true }, + { -1, 0, false } + }, + { { QAccessible::Link, QAXTextFieldRole, false }, + { -1, 0, false } + }, + { { QAccessible::Indicator, QAXValueIndicatorRole, false }, + { -1, 0, false } + }, + { { QAccessible::Splitter, QAXSplitGroupRole, false }, + { -1, 0, false } + }, + { { QAccessible::List, QAXListRole, false }, + { -1, 0, false } + }, + { { QAccessible::ListItem, QAXStaticTextRole, false }, + { -1, 0, false } + }, + { { QAccessible::Cell, QAXStaticTextRole, false }, + { -1, 0, false } + }, + { { -1, 0, false } } +}; + +class QAInterface; +static CFStringRef macRole(const QAInterface &interface); + +QDebug operator<<(QDebug debug, const QAInterface &interface) +{ + if (interface.isValid() == false) + debug << "invalid interface"; + else + debug << interface.object() << "id" << interface.id() << "role" << hex << interface.role(); + return debug; +} + +// The root of the Qt accessible hiearchy. +static QObject *rootObject = 0; + + +bool QAInterface::operator==(const QAInterface &other) const +{ + if (isValid() == false || other.isValid() == false) + return (isValid() && other.isValid()); + + // walk up the parent chain, comparing child indexes, until we reach + // an interface that has a QObject. + QAInterface currentThis = *this; + QAInterface currentOther = other; + + while (currentThis.object() == 0) { + if (currentOther.object() != 0) + return false; + + // fail if the child indexes in the two hirearchies don't match. + if (currentThis.parent().indexOfChild(currentThis) != + currentOther.parent().indexOfChild(currentOther)) + return false; + + currentThis = currentThis.parent(); + currentOther = currentOther.parent(); + } + + return (currentThis.object() == currentOther.object() && currentThis.id() == currentOther.id()); +} + +bool QAInterface::operator!=(const QAInterface &other) const +{ + return !operator==(other); +} + +uint qHash(const QAInterface &item) +{ + if (item.isValid()) + return qHash(item.object()) + qHash(item.id()); + else + return qHash(item.cachedObject()) + qHash(item.id()); +} + +QAInterface QAInterface::navigate(RelationFlag relation, int entry) const +{ + if (!checkValid()) + return QAInterface(); + + // On a QAccessibleInterface that handles its own children we can short-circut + // the navigation if this QAInterface refers to one of the children: + if (child != 0) { + // The Ancestor interface will always be the same QAccessibleInterface with + // a child value of 0. + if (relation == QAccessible::Ancestor) + return QAInterface(*this, 0); + + // The child hiearchy is only one level deep, so navigating to a child + // of a child is not possible. + if (relation == QAccessible::Child) { + return QAInterface(); + } + } + QAccessibleInterface *child_iface = 0; + + const int status = base.interface->navigate(relation, entry, &child_iface); + + if (status == -1) + return QAInterface(); // not found; + + // Check if target is a child of this interface. + if (!child_iface) { + return QAInterface(*this, status); + } else { + // Target is child_iface or a child of that (status decides). + return QAInterface(child_iface, status); + } +} + +QAElement::QAElement() +:elementRef(0) +{} + +QAElement::QAElement(AXUIElementRef elementRef) +:elementRef(elementRef) +{ + if (elementRef != 0) { + CFRetain(elementRef); + CFRetain(object()); + } +} + +QAElement::QAElement(const QAElement &element) +:elementRef(element.elementRef) +{ + if (elementRef != 0) { + CFRetain(elementRef); + CFRetain(object()); + } +} + +QAElement::QAElement(HIObjectRef object, int child) +{ +#ifndef QT_MAC_USE_COCOA + if (object == 0) { + elementRef = 0; // Create invalid QAElement. + } else { + elementRef = AXUIElementCreateWithHIObjectAndIdentifier(object, child); + CFRetain(object); + } +#else + Q_UNUSED(object); + Q_UNUSED(child); +#endif +} + +QAElement::~QAElement() +{ + if (elementRef != 0) { + CFRelease(object()); + CFRelease(elementRef); + } +} + +void QAElement::operator=(const QAElement &other) +{ + if (*this == other) + return; + + if (elementRef != 0) { + CFRelease(object()); + CFRelease(elementRef); + } + + elementRef = other.elementRef; + + if (elementRef != 0) { + CFRetain(elementRef); + CFRetain(object()); + } +} + +bool QAElement::operator==(const QAElement &other) const +{ + if (elementRef == 0 || other.elementRef == 0) + return (elementRef == other.elementRef); + + return CFEqual(elementRef, other.elementRef); +} + +uint qHash(QAElement element) +{ + return qHash(element.object()) + qHash(element.id()); +} + +#ifndef QT_MAC_USE_COCOA +static QInterfaceFactory *createFactory(const QAInterface &interface); +#endif +Q_GLOBAL_STATIC(QAccessibleHierarchyManager, accessibleHierarchyManager); + +/* + Reomves all accessibility info accosiated with the sender object. +*/ +void QAccessibleHierarchyManager::objectDestroyed(QObject *object) +{ + HIObjectRef hiObject = qobjectHiobjectHash.value(object); + delete qobjectElementHash.value(object); + qobjectElementHash.remove(object); + hiobjectInterfaceHash.remove(hiObject); +} + +/* + Removes all stored items. +*/ +void QAccessibleHierarchyManager::reset() +{ + qDeleteAll(qobjectElementHash); + qobjectElementHash.clear(); + hiobjectInterfaceHash.clear(); + qobjectHiobjectHash.clear(); +} + +QAccessibleHierarchyManager *QAccessibleHierarchyManager::instance() +{ + return accessibleHierarchyManager(); +} + +#ifndef QT_MAC_USE_COCOA +static bool isItemView(const QAInterface &interface) +{ + QObject *object = interface.object(); + return (interface.role() == QAccessible::List || interface.role() == QAccessible::Table + || (object && qobject_cast<QAbstractItemView *>(interface.object())) + || (object && object->objectName() == QLatin1String("qt_scrollarea_viewport") + && qobject_cast<QAbstractItemView *>(object->parent()))); +} +#endif + +static bool isTabWidget(const QAInterface &interface) +{ + if (QObject *object = interface.object()) + return (object->inherits("QTabWidget") && interface.id() == 0); + return false; +} + +static bool isStandaloneTabBar(const QAInterface &interface) +{ + QObject *object = interface.object(); + if (interface.role() == QAccessible::PageTabList && object) + return (qobject_cast<QTabWidget *>(object->parent()) == 0); + + return false; +} + +static bool isEmbeddedTabBar(const QAInterface &interface) +{ + QObject *object = interface.object(); + if (interface.role() == QAccessible::PageTabList && object) + return (qobject_cast<QTabWidget *>(object->parent())); + + return false; +} + +/* + Decides if a QAInterface is interesting from an accessibility users point of view. +*/ +bool isItInteresting(const QAInterface &interface) +{ + // Mac accessibility does not have an attribute that corresponds to the Invisible/Offscreen + // state, so we disable the interface here. + const QAccessible::State state = interface.state(); + if (state & QAccessible::Invisible || + state & QAccessible::Offscreen ) + return false; + + const QAccessible::Role role = interface.role(); + + if (QObject * const object = interface.object()) { + const QString className = QLatin1String(object->metaObject()->className()); + + // VoiceOver focusing on tool tips can be confusing. The contents of the + // tool tip is avalible through the description attribute anyway, so + // we disable accessibility for tool tips. + if (className == QLatin1String("QTipLabel")) + return false; + + // Hide TabBars that has a QTabWidget parent (the tab widget handles the accessibility) + if (isEmbeddedTabBar(interface)) + return false; + + // Hide docked dockwidgets. ### causes infinitie loop in the apple accessibility code. + /* if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(object)) { + if (dockWidget->isFloating() == false) + return false; + } + */ + } + + // Client is a generic role returned by plain QWidgets or other + // widgets that does not have separate QAccessible interface, such + // as the TabWidget. Return false unless macRole gives the interface + // a special role. + if (role == QAccessible::Client && macRole(interface) == CFStringRef(QAXUnknownRole)) + return false; + + // Some roles are not interesting: + if (role == QAccessible::Border || // QFrame + role == QAccessible::Application || // We use the system-provided application element. + role == QAccessible::MenuItem) // The system also provides the menu items. + return false; + + // It is probably better to access the toolbar buttons directly than having + // to navigate through the toolbar. + if (role == QAccessible::ToolBar) + return false; + + return true; +} + +QAElement QAccessibleHierarchyManager::registerInterface(QObject *object, int child) +{ +#ifndef QT_MAC_USE_COCOA + return registerInterface(QAInterface(QAccessible::queryAccessibleInterface(object), child)); +#else + Q_UNUSED(object); + Q_UNUSED(child); + return QAElement(); +#endif +} + +/* + Creates a QAXUIelement that corresponds to the given QAInterface. +*/ +QAElement QAccessibleHierarchyManager::registerInterface(const QAInterface &interface) +{ +#ifndef QT_MAC_USE_COCOA + if (interface.isValid() == false) + return QAElement(); + QAInterface objectInterface = interface.objectInterface(); + + QObject * qobject = objectInterface.object(); + HIObjectRef hiobject = objectInterface.hiObject(); + if (qobject == 0 || hiobject == 0) + return QAElement(); + + if (qobjectElementHash.contains(qobject) == false) { + registerInterface(qobject, hiobject, createFactory(interface)); + HIObjectSetAccessibilityIgnored(hiobject, !isItInteresting(interface)); + } + + return QAElement(hiobject, interface.id()); +#else + Q_UNUSED(interface); + return QAElement(); +#endif +} + +#ifndef QT_MAC_USE_COCOA +#include "qaccessible_mac_carbon.cpp" +#endif + +void QAccessibleHierarchyManager::registerInterface(QObject * qobject, HIObjectRef hiobject, QInterfaceFactory *interfaceFactory) +{ +#ifndef QT_MAC_USE_COCOA + if (qobjectElementHash.contains(qobject) == false) { + qobjectElementHash.insert(qobject, interfaceFactory); + qobjectHiobjectHash.insert(qobject, hiobject); + connect(qobject, SIGNAL(destroyed(QObject *)), SLOT(objectDestroyed(QObject *))); + } + + if (hiobjectInterfaceHash.contains(hiobject) == false) { + hiobjectInterfaceHash.insert(hiobject, interfaceFactory); + installAcessibilityEventHandler(hiobject); + } +#else + Q_UNUSED(qobject); + Q_UNUSED(hiobject); + Q_UNUSED(interfaceFactory); +#endif +} + +void QAccessibleHierarchyManager::registerChildren(const QAInterface &interface) +{ + QObject * const object = interface.object(); + if (object == 0) + return; + + QInterfaceFactory *interfaceFactory = qobjectElementHash.value(object); + + if (interfaceFactory == 0) + return; + + interfaceFactory->registerChildren(); +} + +QAInterface QAccessibleHierarchyManager::lookup(const AXUIElementRef &element) +{ + if (element == 0) + return QAInterface(); +#ifndef QT_MAC_USE_COCOA + HIObjectRef hiObject = AXUIElementGetHIObject(element); + + QInterfaceFactory *factory = hiobjectInterfaceHash.value(hiObject); + if (factory == 0) { + return QAInterface(); + } + + UInt64 id; + AXUIElementGetIdentifier(element, &id); + return factory->interface(id); +#else + return QAInterface(); +#endif +} + +QAInterface QAccessibleHierarchyManager::lookup(const QAElement &element) +{ + return lookup(element.element()); +} + +QAElement QAccessibleHierarchyManager::lookup(const QAInterface &interface) +{ + if (interface.isValid() == false) + return QAElement(); + + QInterfaceFactory *factory = qobjectElementHash.value(interface.objectInterface().object()); + if (factory == 0) + return QAElement(); + + return factory->element(interface); +} + +QAElement QAccessibleHierarchyManager::lookup(QObject * const object, int id) +{ + QInterfaceFactory *factory = qobjectElementHash.value(object); + if (factory == 0) + return QAElement(); + + return factory->element(id); +} + +/* + Standard interface mapping, return the stored interface + or HIObjectRef, and there is an one-to-one mapping between + the identifier and child. +*/ +class QStandardInterfaceFactory : public QInterfaceFactory +{ +public: + QStandardInterfaceFactory(const QAInterface &interface) + : m_interface(interface), object(interface.hiObject()) + { + CFRetain(object); + } + + ~QStandardInterfaceFactory() + { + CFRelease(object); + } + + + QAInterface interface(UInt64 identifier) + { + const int child = identifier; + return QAInterface(m_interface, child); + } + + QAElement element(int id) + { + return QAElement(object, id); + } + + QAElement element(const QAInterface &interface) + { + if (interface.object() == 0) + return QAElement(); + return QAElement(object, interface.id()); + } + + void registerChildren() + { + const int childCount = m_interface.childCount(); + for (int i = 1; i <= childCount; ++i) { + accessibleHierarchyManager()->registerInterface(m_interface.navigate(QAccessible::Child, i)); + } + } + +private: + QAInterface m_interface; + HIObjectRef object; +}; + +/* + Interface mapping where that creates one HIObject for each interface child. +*/ +class QMultipleHIObjectFactory : public QInterfaceFactory +{ +public: + QMultipleHIObjectFactory(const QAInterface &interface) + : m_interface(interface) + { } + + ~QMultipleHIObjectFactory() + { + foreach (HIObjectRef object, objects) { + CFRelease(object); + } + } + + QAInterface interface(UInt64 identifier) + { + const int child = identifier; + return QAInterface(m_interface, child); + } + + QAElement element(int child) + { + if (child == 0) + return QAElement(m_interface.hiObject(), 0); + + if (child > objects.count()) + return QAElement(); + + return QAElement(objects.at(child - 1), child); + } + + void registerChildren() + { +#ifndef QT_MAC_USE_COCOA + const int childCount = m_interface.childCount(); + for (int i = 1; i <= childCount; ++i) { + HIObjectRef hiobject; + HIObjectCreate(kObjectQtAccessibility, 0, &hiobject); + objects.append(hiobject); + accessibleHierarchyManager()->registerInterface(m_interface.object(), hiobject, this); + HIObjectSetAccessibilityIgnored(hiobject, !isItInteresting(m_interface.navigate(QAccessible::Child, i))); + } +#endif + } + +private: + QAInterface m_interface; + QList<HIObjectRef> objects; +}; + +class QItemViewInterfaceFactory : public QInterfaceFactory +{ +public: + QItemViewInterfaceFactory(const QAInterface &interface) + : m_interface(interface), object(interface.hiObject()) + { + CFRetain(object); + columnCount = 0; + if (QTableView * tableView = qobject_cast<QTableView *>(interface.parent().object())) { + if (tableView->model()) + columnCount = tableView->model()->columnCount(); + if (tableView->verticalHeader()) + ++columnCount; + } + } + + ~QItemViewInterfaceFactory() + { + CFRelease(object); + } + + QAInterface interface(UInt64 identifier) + { + if (identifier == 0) + return m_interface; + + if (m_interface.role() == QAccessible::List) + return m_interface.childAt(identifier); + + if (m_interface.role() == QAccessible::Table) { + const int index = identifier; + if (index == 0) + return m_interface; // return the item view interface. + + const int rowIndex = (index - 1) / (columnCount + 1); + const int cellIndex = (index - 1) % (columnCount + 1); +/* + qDebug() << "index" << index; + qDebug() << "rowIndex" << rowIndex; + qDebug() << "cellIndex" << cellIndex; +*/ + const QAInterface rowInterface = m_interface.childAt(rowIndex + 1); + + if ((cellIndex) == 0) // Is it a row? + return rowInterface; + else { + return rowInterface.childAt(cellIndex); + } + } + + return QAInterface(); + } + + QAElement element(int id) + { + if (id != 0) { + return QAElement(); + } + return QAElement(object, 0); + } + + QAElement element(const QAInterface &interface) + { + if (interface.object() && interface.object() == m_interface.object()) { + return QAElement(object, 0); + } else if (m_interface.role() == QAccessible::List) { + if (interface.parent().object() && interface.parent().object() == m_interface.object()) + return QAElement(object, m_interface.indexOfChild(interface)); + } else if (m_interface.role() == QAccessible::Table) { + QAInterface currentInterface = interface; + int index = 0; + + while (currentInterface.isValid() && currentInterface.object() == 0) { + const QAInterface parentInterface = currentInterface.parent(); +/* + qDebug() << "current index" << index; + qDebug() << "current interface" << interface; + + qDebug() << "parent interface" << parentInterface; + qDebug() << "grandparent interface" << parentInterface.parent(); + qDebug() << "childCount" << interface.childCount(); + qDebug() << "index of child" << parentInterface.indexOfChild(currentInterface); +*/ + index += ((parentInterface.indexOfChild(currentInterface) - 1) * (currentInterface.childCount() + 1)) + 1; + currentInterface = parentInterface; +// qDebug() << "new current interface" << currentInterface; + } + if (currentInterface.object() == m_interface.object()) + return QAElement(object, index); + + + } + return QAElement(); + } + + void registerChildren() + { + // Item view child interfraces don't have their own qobjects, so there is nothing to register here. + } + +private: + QAInterface m_interface; + HIObjectRef object; + int columnCount; // for table views; +}; + +#ifndef QT_MAC_USE_COCOA +static bool managesChildren(const QAInterface &interface) +{ + return (interface.childCount() > 0 && interface.childAt(1).id() > 0); +} + +static QInterfaceFactory *createFactory(const QAInterface &interface) +{ + if (isItemView(interface)) { + return new QItemViewInterfaceFactory(interface); + } if (managesChildren(interface)) { + return new QMultipleHIObjectFactory(interface); + } + + return new QStandardInterfaceFactory(interface); +} +#endif + +QList<QAElement> lookup(const QList<QAInterface> &interfaces) +{ + QList<QAElement> elements; + foreach (const QAInterface &interface, interfaces) + if (interface.isValid()) { + const QAElement element = accessibleHierarchyManager()->lookup(interface); + if (element.isValid()) + elements.append(element); + } + return elements; +} + +// Debug output helpers: +/* +static QString nameForEventKind(UInt32 kind) +{ + switch(kind) { + case kEventAccessibleGetChildAtPoint: return QString("GetChildAtPoint"); break; + case kEventAccessibleGetAllAttributeNames: return QString("GetAllAttributeNames"); break; + case kEventAccessibleGetNamedAttribute: return QString("GetNamedAttribute"); break; + case kEventAccessibleSetNamedAttribute: return QString("SetNamedAttribute"); break; + case kEventAccessibleGetAllActionNames: return QString("GetAllActionNames"); break; + case kEventAccessibleGetFocusedChild: return QString("GetFocusedChild"); break; + default: + return QString("Unknown accessibility event type: %1").arg(kind); + break; + }; +} +*/ +#ifndef QT_MAC_USE_COCOA +static bool qt_mac_append_cf_uniq(CFMutableArrayRef array, CFTypeRef value) +{ + if (value == 0) + return false; + + CFRange range; + range.location = 0; + range.length = CFArrayGetCount(array); + if(!CFArrayContainsValue(array, range, value)) { + CFArrayAppendValue(array, value); + return true; + } + return false; +} + +static OSStatus setAttributeValue(EventRef event, const QList<QAElement> &elements) +{ + CFMutableArrayRef array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); + foreach (const QAElement &element, elements) { + if (element.isValid()) + CFArrayAppendValue(array, element.element()); + } + + const OSStatus err = SetEventParameter(event, kEventParamAccessibleAttributeValue, + typeCFTypeRef, sizeof(array), &array); + CFRelease(array); + return err; +} +#endif //QT_MAC_USE_COCOA + +/* + Gets the AccessibleObject parameter from an event. +*/ +static inline AXUIElementRef getAccessibleObjectParameter(EventRef event) +{ + AXUIElementRef element; + GetEventParameter(event, kEventParamAccessibleObject, typeCFTypeRef, 0, + sizeof(element), 0, &element); + return element; +} + +/* + The application event handler makes sure that all top-level qt windows are registered + before any accessibility events are handeled. +*/ +#ifndef QT_MAC_USE_COCOA +static OSStatus applicationEventHandler(EventHandlerCallRef next_ref, EventRef event, void *) +{ + QAInterface rootInterface(QAccessible::queryAccessibleInterface(rootObject ? rootObject : qApp), 0); + accessibleHierarchyManager()->registerChildren(rootInterface); + + return CallNextEventHandler(next_ref, event); +} + +/* + Returns the value for element by combining the QAccessibility::Checked and + QAccessibility::Mixed flags into an int value that the Mac accessibilty + system understands. This works for check boxes, radio buttons, and the like. + The return values are: + 0: unchecked + 1: checked + 2: undecided +*/ +static int buttonValue(QAInterface element) +{ + const QAccessible::State state = element.state(); + if (state & QAccessible::Mixed) + return 2; + else if(state & QAccessible::Checked) + return 1; + else + return 0; +} + +static QString getValue(const QAInterface &interface) +{ + const QAccessible::Role role = interface.role(); + if (role == QAccessible::RadioButton || role == QAccessible::CheckBox) + return QString::number(buttonValue(interface)); + else + return interface.text(QAccessible::Value); +} +#endif //QT_MAC_USE_COCOA + +/* + Translates a QAccessible::Role into a mac accessibility role. +*/ +static CFStringRef macRole(const QAInterface &interface) +{ + const QAccessible::Role qtRole = interface.role(); + +// qDebug() << "role for" << interface.object() << "interface role" << hex << qtRole; + + // Qt accessibility: QAccessible::Splitter contains QAccessible::Grip. + // Mac accessibility: AXSplitGroup contains AXSplitter. + if (qtRole == QAccessible::Grip) { + const QAInterface parent = interface.parent(); + if (parent.isValid() && parent.role() == QAccessible::Splitter) + return CFStringRef(QAXSplitterRole); + } + + // Tab widgets and standalone tab bars get the kAXTabGroupRole. Accessibility + // for tab bars emebedded in a tab widget is handled by the tab widget. + if (isTabWidget(interface) || isStandaloneTabBar(interface)) + return kAXTabGroupRole; + + if (QObject *object = interface.object()) { + // ### The interface for an abstract scroll area returns the generic "Client" + // role, so we have to to an extra detect on the QObject here. + if (object->inherits("QAbstractScrollArea") && interface.id() == 0) + return CFStringRef(QAXScrollAreaRole); + + if (object->inherits("QDockWidget")) + return CFStringRef(QAXUnknownRole); + } + + int i = 0; + int testRole = text_bindings[i][0].qt; + while (testRole != -1) { + if (testRole == qtRole) + return CFStringRef(text_bindings[i][0].mac); + ++i; + testRole = text_bindings[i][0].qt; + } + +// qDebug() << "got unknown role!" << interface << interface.parent(); + + return CFStringRef(QAXUnknownRole); +} + +/* + Translates a QAccessible::Role and an attribute name into a QAccessible::Text, taking into + account execptions listed in text_bindings. +*/ +#ifndef QT_MAC_USE_COCOA +static int textForRoleAndAttribute(QAccessible::Role role, CFStringRef attribute) +{ + // Search for exception, return it if found. + int testRole = text_bindings[0][0].qt; + int i = 0; + while (testRole != -1) { + if (testRole == role) { + int j = 1; + int qtRole = text_bindings[i][j].qt; + CFStringRef testAttribute = CFStringRef(text_bindings[i][j].mac); + while (qtRole != -1) { + if (CFStringCompare(attribute, testAttribute, 0) == kCFCompareEqualTo) { + return (QAccessible::Text)qtRole; + } + ++j; + testAttribute = CFStringRef(text_bindings[i][j].mac); /// ### custom compare + qtRole = text_bindings[i][j].qt; /// ### custom compare + } + break; + } + ++i; + testRole = text_bindings[i][0].qt; + } + + // Return default mappping + if (CFStringCompare(attribute, CFStringRef(QAXTitleAttribute), 0) == kCFCompareEqualTo) + return QAccessible::Name; + else if (CFStringCompare(attribute, CFStringRef(QAXValueAttribute), 0) == kCFCompareEqualTo) + return QAccessible::Value; + else if (CFStringCompare(attribute, CFStringRef(QAXHelpAttribute), 0) == kCFCompareEqualTo) + return QAccessible::Help; + else if (CFStringCompare(attribute, CFStringRef(QAXDescriptionAttribute), 0) == kCFCompareEqualTo) + return QAccessible::Description; + else + return -1; +} + +/* + Returns the subrole string constant for the interface if it has one, + else returns an empty string. +*/ +static QCFString subrole(const QAInterface &interface) +{ + const QAInterface parent = interface.parent(); + if (parent.isValid() == false) + return QCFString(); + + if (parent.role() == QAccessible::ScrollBar) { + QCFString subrole; + switch(interface.id()) { + case 1: subrole = CFStringRef(QAXDecrementArrowSubrole); break; + case 2: subrole = CFStringRef(QAXDecrementPageSubrole); break; + case 4: subrole = CFStringRef(QAXIncrementPageSubrole); break; + case 5: subrole = CFStringRef(QAXIncrementArrowSubrole); break; + default: + break; + } + return subrole; + } + return QCFString(); +} + +// Gets the scroll bar orientation by asking the QAbstractSlider object directly. +static Qt::Orientation scrollBarOrientation(const QAInterface &scrollBar) +{ + QObject *const object = scrollBar.object(); + if (QAbstractSlider * const sliderObject = qobject_cast<QAbstractSlider * const>(object)) + return sliderObject->orientation(); + + return Qt::Vertical; // D'oh! The interface wasn't a scroll bar. +} + +static QAInterface scrollAreaGetScrollBarInterface(const QAInterface &scrollArea, Qt::Orientation orientation) +{ + if (macRole(scrollArea) != CFStringRef(CFStringRef(QAXScrollAreaRole))) + return QAInterface(); + + // Child 1 is the contents widget, 2 and 3 are the scroll bar containers wich contains possible scroll bars. + for (int i = 2; i <= 3; ++i) { + QAInterface scrollBarContainer = scrollArea.childAt(i); + for (int i = 1; i <= scrollBarContainer.childCount(); ++i) { + QAInterface scrollBar = scrollBarContainer.childAt(i); + if (scrollBar.isValid() && + scrollBar.role() == QAccessible::ScrollBar && + scrollBarOrientation(scrollBar) == orientation) + return scrollBar; + } + } + + return QAInterface(); +} + +static bool scrollAreaHasScrollBar(const QAInterface &scrollArea, Qt::Orientation orientation) +{ + return scrollAreaGetScrollBarInterface(scrollArea, orientation).isValid(); +} + +static QAElement scrollAreaGetScrollBar(const QAInterface &scrollArea, Qt::Orientation orientation) +{ + return accessibleHierarchyManager()->lookup(scrollAreaGetScrollBarInterface(scrollArea, orientation)); +} + +static QAElement scrollAreaGetContents(const QAInterface &scrollArea) +{ + // Child 1 is the contents widget, + return accessibleHierarchyManager()->lookup(scrollArea.navigate(QAccessible::Child, 1)); +} + +static QAElement tabWidgetGetContents(const QAInterface &interface) +{ + // A kAXTabGroup has a kAXContents attribute, which consists of the + // ui elements for the current tab page. Get the current tab page + // from the QStackedWidget, where the current visible page can + // be found at index 1. + QAInterface stackedWidget = interface.childAt(1); + accessibleHierarchyManager()->registerChildren(stackedWidget); + QAInterface tabPageInterface = stackedWidget.childAt(1); + return accessibleHierarchyManager()->lookup(tabPageInterface); +} + +static QList<QAElement> tabBarGetTabs(const QAInterface &interface) +{ + // Get the tabs by searching for children with the "PageTab" role. + // This filters out the left/right navigation buttons. + accessibleHierarchyManager()->registerChildren(interface); + QList<QAElement> tabs; + const int numChildren = interface.childCount(); + for (int i = 1; i < numChildren + 1; ++i) { + QAInterface child = interface.navigate(QAccessible::Child, i); + if (child.isValid() && child.role() == QAccessible::PageTab) { + tabs.append(accessibleHierarchyManager()->lookup(child)); + } + } + return tabs; +} + +static QList<QAElement> tabWidgetGetTabs(const QAInterface &interface) +{ + // Each QTabWidget has two children, a QStackedWidget and a QTabBar. + // Get the tabs from the QTabBar. + return tabBarGetTabs(interface.childAt(2)); +} + +static QList<QAElement> tabWidgetGetChildren(const QAInterface &interface) +{ + // The children for a kAXTabGroup should consist of the tabs and the + // contents of the current open tab page. + QList<QAElement> children = tabWidgetGetTabs(interface); + children += tabWidgetGetContents(interface); + return children; +} +#endif //QT_MAC_USE_COCOA + +/* + Returns the label (buddy) interface for interface, or 0 if it has none. +*/ +/* +static QAInterface findLabel(const QAInterface &interface) +{ + return interface.navigate(QAccessible::Label, 1); +} +*/ +/* + Returns a list of interfaces this interface labels, or an empty list if it doesn't label any. +*/ +/* +static QList<QAInterface> findLabelled(const QAInterface &interface) +{ + QList<QAInterface> interfaceList; + + int count = 1; + const QAInterface labelled = interface.navigate(QAccessible::Labelled, count); + while (labelled.isValid()) { + interfaceList.append(labelled); + ++count; + } + return interfaceList; +} +*/ +/* + Tests if the given QAInterface has data for a mac attribute. +*/ +#ifndef QT_MAC_USE_COCOA +static bool supportsAttribute(CFStringRef attribute, const QAInterface &interface) +{ + const int text = textForRoleAndAttribute(interface.role(), attribute); + + // Special case: Static texts don't have a title. + if (interface.role() == QAccessible::StaticText && attribute == CFStringRef(QAXTitleAttribute)) + return false; + + // Return true if we the attribute matched a QAccessible::Role and we get text for that role from the interface. + if (text != -1) { + if (text == QAccessible::Value) // Special case for Value, see getValue() + return !getValue(interface).isEmpty(); + else + return !interface.text((QAccessible::Text)text).isEmpty(); + } + + if (CFStringCompare(attribute, CFStringRef(QAXChildrenAttribute), 0) == kCFCompareEqualTo) { + if (interface.childCount() > 0) + return true; + } + + if (CFStringCompare(attribute, CFStringRef(QAXSubroleAttribute), 0) == kCFCompareEqualTo) { + return (subrole(interface) != QCFString()); + } + + return false; +} + +static void appendIfSupported(CFMutableArrayRef array, CFStringRef attribute, const QAInterface &interface) +{ + if (supportsAttribute(attribute, interface)) + qt_mac_append_cf_uniq(array, attribute); +} + +/* + Returns the names of the attributes the give QAInterface supports. +*/ +static OSStatus getAllAttributeNames(EventRef event, const QAInterface &interface, EventHandlerCallRef next_ref) +{ + // Call system event handler. + OSStatus err = CallNextEventHandler(next_ref, event); + if(err != noErr && err != eventNotHandledErr) + return err; + CFMutableArrayRef attrs = 0; + GetEventParameter(event, kEventParamAccessibleAttributeNames, typeCFMutableArrayRef, 0, + sizeof(attrs), 0, &attrs); + + if (!attrs) + return eventNotHandledErr; + + // Append attribute names that are always supported. + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXPositionAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSizeAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXRoleAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXEnabledAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXWindowAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXTopLevelUIElementAttribute)); + + // Append these names if the QInterafceItem returns any data for them. + appendIfSupported(attrs, CFStringRef(QAXTitleAttribute), interface); + appendIfSupported(attrs, CFStringRef(QAXValueAttribute), interface); + appendIfSupported(attrs, CFStringRef(QAXDescriptionAttribute), interface); + appendIfSupported(attrs, CFStringRef(QAXLinkedUIElementsAttribute), interface); + appendIfSupported(attrs, CFStringRef(QAXHelpAttribute), interface); + appendIfSupported(attrs, CFStringRef(QAXTitleUIElementAttribute), interface); + appendIfSupported(attrs, CFStringRef(QAXChildrenAttribute), interface); + appendIfSupported(attrs, CFStringRef(QAXSubroleAttribute), interface); + + // Append attribute names based on the interaface role. + switch (interface.role()) { + case QAccessible::Window: + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMainAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinimizedAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXCloseButtonAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXZoomButtonAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinimizeButtonAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXToolbarButtonAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXGrowAreaAttribute)); + break; + case QAccessible::RadioButton: + case QAccessible::CheckBox: + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinValueAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMaxValueAttribute)); + break; + case QAccessible::ScrollBar: + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXOrientationAttribute)); + break; + case QAccessible::Splitter: + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSplittersAttribute)); + break; + case QAccessible::Table: + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXRowsAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXVisibleRowsAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSelectedRowsAttribute)); + break; + default: + break; + } + + // Append attribute names based on the mac accessibility role. + const QCFString mac_role = macRole(interface); + if (mac_role == CFStringRef(QAXSplitterRole)) { + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXPreviousContentsAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXNextContentsAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXOrientationAttribute)); + } else if (mac_role == CFStringRef(QAXScrollAreaRole)) { + if (scrollAreaHasScrollBar(interface, Qt::Horizontal)) + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXHorizontalScrollBarAttribute)); + if (scrollAreaHasScrollBar(interface, Qt::Vertical)) + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXVerticalScrollBarAttribute)); + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXContentsAttribute)); + } else if (mac_role == CFStringRef(QAXTabGroupRole)) { + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXTabsAttribute)); + // Only tab widgets can have the contents attribute, there is no way of getting + // the contents from a QTabBar. + if (isTabWidget(interface)) + qt_mac_append_cf_uniq(attrs, CFStringRef(QAXContentsAttribute)); + } + + return noErr; +} + +static void handleStringAttribute(EventRef event, QAccessible::Text text, const QAInterface &interface) +{ + QString str = interface.text(text); + if (str.isEmpty()) + return; + + // Remove any html markup from the text string, or VoiceOver will read the html tags. + static QTextDocument document; + document.setHtml(str); + str = document.toPlainText(); + + CFStringRef cfstr = QCFString::toCFStringRef(str); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, sizeof(cfstr), &cfstr); +} + +/* + Handles the parent attribute for a interface. + There are basically three cases here: + 1. interface is a HIView and has only HIView children. + 2. interface is a HIView but has children that is not a HIView + 3. interface is not a HIView. +*/ +static OSStatus handleChildrenAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) +{ + // Add the children for this interface to the global QAccessibelHierachyManager. + accessibleHierarchyManager()->registerChildren(interface); + + if (isTabWidget(interface)) { + QList<QAElement> children = tabWidgetGetChildren(interface); + const int childCount = children.count(); + + CFMutableArrayRef array = 0; + array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); + for (int i = 0; i < childCount; ++i) { + qt_mac_append_cf_uniq(array, children.at(i).element()); + } + + OSStatus err; + err = SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFArrayRef, sizeof(array), &array); + if (err != noErr) + qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__); + + return noErr; + } + + const QList<QAElement> children = lookup(interface.children()); + const int childCount = children.count(); + + OSStatus err = eventNotHandledErr; + if (interface.isHIView()) + err = CallNextEventHandler(next_ref, event); + + CFMutableArrayRef array = 0; + int arraySize = 0; + if (err == noErr) { + CFTypeRef obj = 0; + err = GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, NULL , sizeof(obj), NULL, &obj); + if (err == noErr && obj != 0) { + array = (CFMutableArrayRef)obj; + arraySize = CFArrayGetCount(array); + } + } + + if (array) { + CFArrayRemoveAllValues(array); + for (int i = 0; i < childCount; ++i) { + qt_mac_append_cf_uniq(array, children.at(i).element()); + } + } else { + array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); + for (int i = 0; i < childCount; ++i) { + qt_mac_append_cf_uniq(array, children.at(i).element()); + } + + err = SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFArrayRef, sizeof(array), &array); + if (err != noErr) + qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__); + } + + return noErr; +} + +/* + +*/ +static OSStatus handleParentAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) +{ + OSStatus err = eventNotHandledErr; + if (interface.isHIView()) { + err = CallNextEventHandler(next_ref, event); + } + if (err == noErr) + return err; + + const QAInterface parentInterface = interface.navigate(QAccessible::Ancestor, 1); + const QAElement parentElement = accessibleHierarchyManager()->lookup(parentInterface); + + if (parentElement.isValid() == false) + return eventNotHandledErr; + + AXUIElementRef elementRef = parentElement.element(); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(elementRef), &elementRef); + return noErr; +} +#endif + +struct IsWindowTest +{ + static inline bool test(const QAInterface &interface) + { + return (interface.role() == QAccessible::Window); + } +}; + +struct IsWindowAndNotDrawerOrSheetTest +{ + static inline bool test(const QAInterface &interface) + { + QWidget * const widget = qobject_cast<QWidget*>(interface.object()); + return (interface.role() == QAccessible::Window && + widget && widget->isWindow() && + !qt_mac_is_macdrawer(widget) && + !qt_mac_is_macsheet(widget)); + } +}; + +/* + Navigates up the iterfaces ancestor hierachy until a QAccessibleInterface that + passes the Test is found. If we reach a interface that is a HIView we stop the + search and call AXUIElementCopyAttributeValue. +*/ +template <typename TestType> +OSStatus navigateAncestors(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface, CFStringRef attribute) +{ + if (interface.isHIView()) + return CallNextEventHandler(next_ref, event); + + QAInterface current = interface; + QAElement element; + while (current.isValid()) { + if (TestType::test(interface)) { + element = accessibleHierarchyManager()->lookup(current); + break; + } + + // If we reach an InterfaceItem that is a HiView we can hand of the search to + // the system event handler. This is the common case. + if (current.isHIView()) { + CFTypeRef value = 0; + const QAElement currentElement = accessibleHierarchyManager()->lookup(current); + AXError err = AXUIElementCopyAttributeValue(currentElement.element(), attribute, &value); + AXUIElementRef newElement = (AXUIElementRef)value; + + if (err == noErr) + element = QAElement(newElement); + + if (newElement != 0) + CFRelease(newElement); + break; + } + + QAInterface next = current.parent(); + if (next.isValid() == false) + break; + if (next == current) + break; + current = next; + } + + if (element.isValid() == false) + return eventNotHandledErr; + + + AXUIElementRef elementRef = element.element(); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, + sizeof(elementRef), &elementRef); + return noErr; +} + +/* + Returns the top-level window for an interface, which is the closest ancestor interface that + has the Window role, but is not a sheet or a drawer. +*/ +#ifndef QT_MAC_USE_COCOA +static OSStatus handleWindowAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) +{ + return navigateAncestors<IsWindowAndNotDrawerOrSheetTest>(next_ref, event, interface, CFStringRef(QAXWindowAttribute)); +} + +/* + Returns the top-level window for an interface, which is the closest ancestor interface that + has the Window role. (Can also be a sheet or a drawer) +*/ +static OSStatus handleTopLevelUIElementAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) +{ + return navigateAncestors<IsWindowTest>(next_ref, event, interface, CFStringRef(QAXTopLevelUIElementAttribute)); +} + +/* + Returns the tab buttons for an interface. +*/ +static OSStatus handleTabsAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) +{ + Q_UNUSED(next_ref); + if (isTabWidget(interface)) + return setAttributeValue(event, tabWidgetGetTabs(interface)); + else + return setAttributeValue(event, tabBarGetTabs(interface)); +} + +static OSStatus handlePositionAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface) +{ + QPoint qpoint(interface.rect().topLeft()); + HIPoint point; + point.x = qpoint.x(); + point.y = qpoint.y(); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(point), &point); + return noErr; +} + +static OSStatus handleSizeAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface) +{ + QSize qSize(interface.rect().size()); + HISize size; + size.width = qSize.width(); + size.height = qSize.height(); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeHISize, sizeof(size), &size); + return noErr; +} + +static OSStatus handleSubroleAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface) +{ + const QCFString role = subrole(interface); + CFStringRef rolestr = (CFStringRef)role; + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(rolestr), &rolestr); + return noErr; +} + +static OSStatus handleOrientationAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) +{ + QObject *const object = interface.object(); + Qt::Orientation orientation; + if (interface.role() == QAccessible::ScrollBar) { + orientation = scrollBarOrientation(interface); + } else if (QSplitterHandle * const splitter = qobject_cast<QSplitterHandle * const>(object)) { + // Qt reports the layout orientation, but we want the splitter handle orientation. + orientation = (splitter->orientation() == Qt::Horizontal) ? Qt::Vertical : Qt::Horizontal; + } else { + return CallNextEventHandler(next_ref, event); + } + const CFStringRef orientationString = (orientation == Qt::Vertical) + ? CFStringRef(QAXVerticalOrientationValue) : CFStringRef(QAXHorizontalOrientationValue); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, sizeof(orientationString), &orientationString); + return noErr; +} + +/* + Figures out the next or previous contents for a splitter. +*/ +static OSStatus handleSplitterContentsAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface, QCFString nextOrPrev) +{ + if (interface.isValid() == false || interface.role() != QAccessible::Grip) + return eventNotHandledErr; + + const QAInterface parent = interface.parent(); + if (parent.isValid() == false) + return CallNextEventHandler(next_ref, event); + + if (parent.role() != QAccessible::Splitter) + return CallNextEventHandler(next_ref, event); + + const QSplitter * const splitter = qobject_cast<const QSplitter * const>(parent.object()); + if (splitter == 0) + return CallNextEventHandler(next_ref, event); + + QWidget * const splitterHandle = qobject_cast<QWidget * const>(interface.object()); + const int splitterHandleIndex = splitter->indexOf(splitterHandle); + const int widgetIndex = (nextOrPrev == QCFString(CFStringRef(QAXPreviousContentsAttribute))) ? splitterHandleIndex - 1 : splitterHandleIndex; + const QAElement contentsElement = accessibleHierarchyManager()->lookup(splitter->widget(widgetIndex), 0); + return setAttributeValue(event, QList<QAElement>() << contentsElement); +} + +/* + Creates a list of all splitter handles the splitter contains. +*/ +static OSStatus handleSplittersAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) +{ + const QSplitter * const splitter = qobject_cast<const QSplitter * const>(interface.object()); + if (splitter == 0) + return CallNextEventHandler(next_ref, event); + + accessibleHierarchyManager()->registerChildren(interface); + + QList<QAElement> handles; + const int visibleSplitterCount = splitter->count() -1; // skip first handle, it's always invisible. + for (int i = 0; i < visibleSplitterCount; ++i) + handles.append(accessibleHierarchyManager()->lookup(splitter->handle(i + 1), 0)); + + return setAttributeValue(event, handles); +} + +// This handler gets the scroll bars for a scroll area +static OSStatus handleScrollBarAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &scrollArea, Qt::Orientation orientation) +{ + QAElement scrollBar = scrollAreaGetScrollBar(scrollArea, orientation); + if (scrollBar.isValid() == false) + return CallNextEventHandler(next_ref, event); + + AXUIElementRef elementRef = scrollBar.element(); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(elementRef), &elementRef); + return noErr; +} + +// This handler gets the contents for a scroll area or tab widget. +static OSStatus handleContentsAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) +{ + const QCFString mac_role = macRole(interface); + + QAElement contents; + + if (mac_role == kAXTabGroupRole) { + contents = tabWidgetGetContents(interface); + } else { + contents = scrollAreaGetContents(interface); + if (contents.isValid() == false) + return CallNextEventHandler(next_ref, event); + } + + return setAttributeValue(event, QList<QAElement>() << contents); +} + +static OSStatus handleRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView) +{ + QList<QAElement> rows = lookup(tableView.children()); + + // kill the first row which is the horizontal header. + rows.removeAt(0); + + return setAttributeValue(event, rows); +} + +static OSStatus handleVisibleRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView) +{ + QList<QAElement> visibleRows; + + QList<QAInterface> rows = tableView.children(); + // kill the first row which is the horizontal header. + rows.removeAt(0); + + foreach (const QAInterface &interface, rows) + if ((interface.state() & QAccessible::Invisible) == false) + visibleRows.append(accessibleHierarchyManager()->lookup(interface)); + + return setAttributeValue(event, visibleRows); +} + +static OSStatus handleSelectedRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView) +{ + QList<QAElement> selectedRows; + foreach (const QAInterface &interface, tableView.children()) + if ((interface.state() & QAccessible::Selected)) + selectedRows.append(accessibleHierarchyManager()->lookup(interface)); + + return setAttributeValue(event, selectedRows); +} + +static OSStatus getNamedAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) +{ + CFStringRef var; + GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0, + sizeof(var), 0, &var); + + if (CFStringCompare(var, CFStringRef(QAXChildrenAttribute), 0) == kCFCompareEqualTo) { + return handleChildrenAttribute(next_ref, event, interface); + } else if(CFStringCompare(var, CFStringRef(QAXTopLevelUIElementAttribute), 0) == kCFCompareEqualTo) { + return handleTopLevelUIElementAttribute(next_ref, event, interface); + } else if(CFStringCompare(var, CFStringRef(QAXWindowAttribute), 0) == kCFCompareEqualTo) { + return handleWindowAttribute(next_ref, event, interface); + } else if(CFStringCompare(var, CFStringRef(QAXParentAttribute), 0) == kCFCompareEqualTo) { + return handleParentAttribute(next_ref, event, interface); + } else if (CFStringCompare(var, CFStringRef(QAXPositionAttribute), 0) == kCFCompareEqualTo) { + return handlePositionAttribute(next_ref, event, interface); + } else if (CFStringCompare(var, CFStringRef(QAXSizeAttribute), 0) == kCFCompareEqualTo) { + return handleSizeAttribute(next_ref, event, interface); + } else if (CFStringCompare(var, CFStringRef(QAXRoleAttribute), 0) == kCFCompareEqualTo) { + CFStringRef role = macRole(interface); +// ### +// QWidget * const widget = qobject_cast<QWidget *>(interface.object()); +// if (role == CFStringRef(QAXUnknownRole) && widget && widget->isWindow()) +// role = CFStringRef(QAXWindowRole); + + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, + sizeof(role), &role); + + } else if (CFStringCompare(var, CFStringRef(QAXEnabledAttribute), 0) == kCFCompareEqualTo) { + Boolean val = !((interface.state() & QAccessible::Unavailable)) + && !((interface.state() & QAccessible::Invisible)); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, + sizeof(val), &val); + } else if (CFStringCompare(var, CFStringRef(QAXExpandedAttribute), 0) == kCFCompareEqualTo) { + Boolean val = (interface.state() & QAccessible::Expanded); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, + sizeof(val), &val); + } else if (CFStringCompare(var, CFStringRef(QAXSelectedAttribute), 0) == kCFCompareEqualTo) { + Boolean val = (interface.state() & QAccessible::Selection); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, + sizeof(val), &val); + } else if (CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) { + Boolean val = (interface.state() & QAccessible::Focus); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, + sizeof(val), &val); + } else if (CFStringCompare(var, CFStringRef(QAXSelectedChildrenAttribute), 0) == kCFCompareEqualTo) { + const int cc = interface.childCount(); + QList<QAElement> selected; + for (int i = 1; i <= cc; ++i) { + const QAInterface child_iface = interface.navigate(QAccessible::Child, i); + if (child_iface.isValid() && child_iface.state() & QAccessible::Selected) + selected.append(accessibleHierarchyManager()->lookup(child_iface)); + } + + return setAttributeValue(event, selected); + + } else if (CFStringCompare(var, CFStringRef(QAXCloseButtonAttribute), 0) == kCFCompareEqualTo) { + if(interface.object() && interface.object()->isWidgetType()) { + Boolean val = true; //do we want to add a WState for this? + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, + sizeof(val), &val); + } + } else if (CFStringCompare(var, CFStringRef(QAXZoomButtonAttribute), 0) == kCFCompareEqualTo) { + if(interface.object() && interface.object()->isWidgetType()) { + QWidget *widget = (QWidget*)interface.object(); + Boolean val = (widget->windowFlags() & Qt::WindowMaximizeButtonHint); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, + sizeof(val), &val); + } + } else if (CFStringCompare(var, CFStringRef(QAXMinimizeButtonAttribute), 0) == kCFCompareEqualTo) { + if(interface.object() && interface.object()->isWidgetType()) { + QWidget *widget = (QWidget*)interface.object(); + Boolean val = (widget->windowFlags() & Qt::WindowMinimizeButtonHint); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, + sizeof(val), &val); + } + } else if (CFStringCompare(var, CFStringRef(QAXToolbarButtonAttribute), 0) == kCFCompareEqualTo) { + if(interface.object() && interface.object()->isWidgetType()) { + QWidget *widget = (QWidget*)interface.object(); + Boolean val = qobject_cast<QMainWindow *>(widget) != 0; + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, + sizeof(val), &val); + } + } else if (CFStringCompare(var, CFStringRef(QAXGrowAreaAttribute), 0) == kCFCompareEqualTo) { + if(interface.object() && interface.object()->isWidgetType()) { + Boolean val = true; //do we want to add a WState for this? + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, + sizeof(val), &val); + } + } else if (CFStringCompare(var, CFStringRef(QAXMinimizedAttribute), 0) == kCFCompareEqualTo) { + if (interface.object() && interface.object()->isWidgetType()) { + QWidget *widget = (QWidget*)interface.object(); + Boolean val = (widget->windowState() & Qt::WindowMinimized); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean, + sizeof(val), &val); + } + } else if (CFStringCompare(var, CFStringRef(QAXSubroleAttribute), 0) == kCFCompareEqualTo) { + return handleSubroleAttribute(next_ref, event, interface); + } else if (CFStringCompare(var, CFStringRef(QAXRoleDescriptionAttribute), 0) == kCFCompareEqualTo) { +#if !defined(QT_MAC_USE_COCOA) + if (HICopyAccessibilityRoleDescription) { + const CFStringRef roleDescription = HICopyAccessibilityRoleDescription(macRole(interface), 0); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, + sizeof(roleDescription), &roleDescription); + } else +#endif + { + // Just use Qt::Description on 10.3 + handleStringAttribute(event, QAccessible::Description, interface); + } + } else if (CFStringCompare(var, CFStringRef(QAXTitleAttribute), 0) == kCFCompareEqualTo) { + const QAccessible::Role role = interface.role(); + const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); + handleStringAttribute(event, text, interface); + } else if (CFStringCompare(var, CFStringRef(QAXValueAttribute), 0) == kCFCompareEqualTo) { + const QAccessible::Role role = interface.role(); + const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); + if (role == QAccessible::CheckBox || role == QAccessible::RadioButton) { + int value = buttonValue(interface); + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value); + } else { + handleStringAttribute(event, text, interface); + } + } else if (CFStringCompare(var, CFStringRef(QAXDescriptionAttribute), 0) == kCFCompareEqualTo) { + const QAccessible::Role role = interface.role(); + const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); + handleStringAttribute(event, text, interface); + } else if (CFStringCompare(var, CFStringRef(QAXLinkedUIElementsAttribute), 0) == kCFCompareEqualTo) { + return CallNextEventHandler(next_ref, event); + } else if (CFStringCompare(var, CFStringRef(QAXHelpAttribute), 0) == kCFCompareEqualTo) { + const QAccessible::Role role = interface.role(); + const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var); + handleStringAttribute(event, text, interface); + } else if (CFStringCompare(var, kAXTitleUIElementAttribute, 0) == kCFCompareEqualTo) { + return CallNextEventHandler(next_ref, event); + } else if (CFStringCompare(var, CFStringRef(QAXTabsAttribute), 0) == kCFCompareEqualTo) { + return handleTabsAttribute(next_ref, event, interface); + } else if (CFStringCompare(var, CFStringRef(QAXMinValueAttribute), 0) == kCFCompareEqualTo) { + // tabs we first go to the tab bar which is child #2. + QAInterface tabBarInterface = interface.childAt(2); + return handleTabsAttribute(next_ref, event, tabBarInterface); + } else if (CFStringCompare(var, CFStringRef(QAXMinValueAttribute), 0) == kCFCompareEqualTo) { + if (interface.role() == QAccessible::RadioButton || interface.role() == QAccessible::CheckBox) { + uint value = 0; + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value); + } else { + return CallNextEventHandler(next_ref, event); + } + } else if (CFStringCompare(var, CFStringRef(QAXMaxValueAttribute), 0) == kCFCompareEqualTo) { + if (interface.role() == QAccessible::RadioButton || interface.role() == QAccessible::CheckBox) { + uint value = 2; + SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value); + } else { + return CallNextEventHandler(next_ref, event); + } + } else if (CFStringCompare(var, CFStringRef(QAXOrientationAttribute), 0) == kCFCompareEqualTo) { + return handleOrientationAttribute(next_ref, event, interface); + } else if (CFStringCompare(var, CFStringRef(QAXPreviousContentsAttribute), 0) == kCFCompareEqualTo) { + return handleSplitterContentsAttribute(next_ref, event, interface, CFStringRef(QAXPreviousContentsAttribute)); + } else if (CFStringCompare(var, CFStringRef(QAXNextContentsAttribute), 0) == kCFCompareEqualTo) { + return handleSplitterContentsAttribute(next_ref, event, interface, CFStringRef(QAXNextContentsAttribute)); + } else if (CFStringCompare(var, CFStringRef(QAXSplittersAttribute), 0) == kCFCompareEqualTo) { + return handleSplittersAttribute(next_ref, event, interface); + } else if (CFStringCompare(var, CFStringRef(QAXHorizontalScrollBarAttribute), 0) == kCFCompareEqualTo) { + return handleScrollBarAttribute(next_ref, event, interface, Qt::Horizontal); + } else if (CFStringCompare(var, CFStringRef(QAXVerticalScrollBarAttribute), 0) == kCFCompareEqualTo) { + return handleScrollBarAttribute(next_ref, event, interface, Qt::Vertical); + } else if (CFStringCompare(var, CFStringRef(QAXContentsAttribute), 0) == kCFCompareEqualTo) { + return handleContentsAttribute(next_ref, event, interface); + } else if (CFStringCompare(var, CFStringRef(QAXRowsAttribute), 0) == kCFCompareEqualTo) { + return handleRowsAttribute(next_ref, event, interface); + } else if (CFStringCompare(var, CFStringRef(QAXVisibleRowsAttribute), 0) == kCFCompareEqualTo) { + return handleVisibleRowsAttribute(next_ref, event, interface); + } else if (CFStringCompare(var, CFStringRef(QAXSelectedRowsAttribute), 0) == kCFCompareEqualTo) { + return handleSelectedRowsAttribute(next_ref, event, interface); + } else { + return CallNextEventHandler(next_ref, event); + } + return noErr; +} + +static OSStatus isNamedAttributeSettable(EventRef event, const QAInterface &interface) +{ + CFStringRef var; + GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0, + sizeof(var), 0, &var); + Boolean settable = false; + if (CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) { + settable = true; + } else { + for (int r = 0; text_bindings[r][0].qt != -1; r++) { + if(interface.role() == (QAccessible::Role)text_bindings[r][0].qt) { + for (int a = 1; text_bindings[r][a].qt != -1; a++) { + if (CFStringCompare(var, CFStringRef(text_bindings[r][a].mac), 0) == kCFCompareEqualTo) { + settable = text_bindings[r][a].settable; + break; + } + } + } + } + } + SetEventParameter(event, kEventParamAccessibleAttributeSettable, typeBoolean, + sizeof(settable), &settable); + return noErr; +} + +static OSStatus getChildAtPoint(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface) +{ + Q_UNUSED(next_ref); + if (interface.isValid() == false) + return eventNotHandledErr; + + // Add the children for this interface to the global QAccessibelHierachyManager. + accessibleHierarchyManager()->registerChildren(interface); + + Point where; + GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(where), 0, &where); + const QAInterface childInterface = interface.childAt(where.h, where.v); + + if (childInterface.isValid() == false || childInterface == interface) + return eventNotHandledErr; + + const QAElement element = accessibleHierarchyManager()->lookup(childInterface); + if (element.isValid() == false) + return eventNotHandledErr; + + AXUIElementRef elementRef = element.element(); + CFRetain(elementRef); + SetEventParameter(event, kEventParamAccessibleChild, typeCFTypeRef, + sizeof(elementRef), &elementRef); + + return noErr; +} + +/* + Returns a list of actions the given interface supports. + Currently implemented by getting the interface role and deciding based on that. +*/ +static QList<QAccessible::Action> supportedPredefinedActions(const QAInterface &interface) +{ + QList<QAccessible::Action> actions; + switch (interface.role()) { + default: + // Most things can be pressed. + actions.append(QAccessible::Press); + break; + } + + return actions; +} + +/* + Translates a predefined QAccessible::Action to a Mac action constant. + Returns an empty string if the Qt Action has no mac equivalent. +*/ +static QCFString translateAction(const QAccessible::Action action) +{ + switch (action) { + case QAccessible::Press: + return CFStringRef(QAXPressAction); + break; + case QAccessible::Increase: + return CFStringRef(QAXIncrementAction); + break; + case QAccessible::Decrease: + return CFStringRef(QAXDecrementAction); + break; + case QAccessible::Accept: + return CFStringRef(QAXConfirmAction); + break; + case QAccessible::Select: + return CFStringRef(QAXPickAction); + break; + case QAccessible::Cancel: + return CFStringRef(QAXCancelAction); + break; + default: + return QCFString(); + break; + } +} + +/* + Translates between a Mac action constant and a QAccessible::Action. + Returns QAccessible::Default action if there is no Qt predefined equivalent. +*/ +static QAccessible::Action translateAction(const CFStringRef actionName) +{ + if(CFStringCompare(actionName, CFStringRef(QAXPressAction), 0) == kCFCompareEqualTo) { + return QAccessible::Press; + } else if(CFStringCompare(actionName, CFStringRef(QAXIncrementAction), 0) == kCFCompareEqualTo) { + return QAccessible::Increase; + } else if(CFStringCompare(actionName, CFStringRef(QAXDecrementAction), 0) == kCFCompareEqualTo) { + return QAccessible::Decrease; + } else if(CFStringCompare(actionName, CFStringRef(QAXConfirmAction), 0) == kCFCompareEqualTo) { + return QAccessible::Accept; + } else if(CFStringCompare(actionName, CFStringRef(QAXPickAction), 0) == kCFCompareEqualTo) { + return QAccessible::Select; + } else if(CFStringCompare(actionName, CFStringRef(QAXCancelAction), 0) == kCFCompareEqualTo) { + return QAccessible::Cancel; + } else { + return QAccessible::DefaultAction; + } +} +#endif // QT_MAC_USE_COCOA + +/* + Copies the translated names all supported actions for an interface into the kEventParamAccessibleActionNames + event parameter. +*/ +#ifndef QT_MAC_USE_COCOA +static OSStatus getAllActionNames(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) +{ + Q_UNUSED(next_ref); + + CFMutableArrayRef actions = 0; + GetEventParameter(event, kEventParamAccessibleActionNames, typeCFMutableArrayRef, 0, + sizeof(actions), 0, &actions); + + // Add supported predefined actions. + const QList<QAccessible::Action> predefinedActions = supportedPredefinedActions(interface); + for (int i = 0; i < predefinedActions.count(); ++i) { + const QCFString action = translateAction(predefinedActions.at(i)); + if (action != QCFString()) + qt_mac_append_cf_uniq(actions, action); + } + + // Add user actions + const int actionCount = interface.userActionCount(); + for (int i = 0; i < actionCount; ++i) { + const QString actionName = interface.actionText(i, QAccessible::Name); + qt_mac_append_cf_uniq(actions, QCFString::toCFStringRef(actionName)); + } + + return noErr; +} +#endif + +/* + Handles the perforNamedAction event. +*/ +#ifndef QT_MAC_USE_COCOA +static OSStatus performNamedAction(EventHandlerCallRef next_ref, EventRef event, const QAInterface& interface) +{ + Q_UNUSED(next_ref); + + CFStringRef act; + GetEventParameter(event, kEventParamAccessibleActionName, typeCFStringRef, 0, + sizeof(act), 0, &act); + + const QAccessible::Action action = translateAction(act); + + // Perform built-in action + if (action != QAccessible::DefaultAction) { + interface.doAction(action, QVariantList()); + return noErr; + } + + // Search for user-defined actions and perform it if found. + const int actCount = interface.userActionCount(); + const QString qAct = QCFString::toQString(act); + for(int i = 0; i < actCount; i++) { + if(interface.actionText(i, QAccessible::Name) == qAct) { + interface.doAction(i, QVariantList()); + break; + } + } + return noErr; +} + +static OSStatus setNamedAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface) +{ + Q_UNUSED(next_ref); + Q_UNUSED(event); + + CFStringRef var; + GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0, + sizeof(var), 0, &var); + if(CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) { + CFTypeRef val; + if(GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, 0, + sizeof(val), 0, &val) == noErr) { + if(CFGetTypeID(val) == CFBooleanGetTypeID() && + CFEqual(static_cast<CFBooleanRef>(val), kCFBooleanTrue)) { + interface.doAction(QAccessible::SetFocus); + } + } + } else { + bool found = false; + for(int r = 0; text_bindings[r][0].qt != -1; r++) { + if(interface.role() == (QAccessible::Role)text_bindings[r][0].qt) { + for(int a = 1; text_bindings[r][a].qt != -1; a++) { + if(CFStringCompare(var, CFStringRef(text_bindings[r][a].mac), 0) == kCFCompareEqualTo) { + if(!text_bindings[r][a].settable) { + } else { + CFTypeRef val; + if(GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, 0, + sizeof(val), 0, &val) == noErr) { + if(CFGetTypeID(val) == CFStringGetTypeID()) + interface.setText((QAccessible::Text)text_bindings[r][a].qt, + QCFString::toQString(static_cast<CFStringRef>(val))); + + } + } + found = true; + break; + } + } + break; + } + } + } + return noErr; +} + +/* + This is the main accessibility event handler. +*/ +static OSStatus accessibilityEventHandler(EventHandlerCallRef next_ref, EventRef event, void *data) +{ + Q_UNUSED(data) + + // Return if this event is not a AccessibleGetNamedAttribute event. + const UInt32 eclass = GetEventClass(event); + if (eclass != kEventClassAccessibility) + return eventNotHandledErr; + + // Get the AXUIElementRef and QAInterface pointer + AXUIElementRef element = 0; + GetEventParameter(event, kEventParamAccessibleObject, typeCFTypeRef, 0, sizeof(element), 0, &element); + QAInterface interface = accessibleHierarchyManager()->lookup(element); + if (interface.isValid() == false) + return eventNotHandledErr; + + const UInt32 ekind = GetEventKind(event); + OSStatus status = noErr; + switch (ekind) { + case kEventAccessibleGetAllAttributeNames: + status = getAllAttributeNames(event, interface, next_ref); + break; + case kEventAccessibleGetNamedAttribute: + status = getNamedAttribute(next_ref, event, interface); + break; + case kEventAccessibleIsNamedAttributeSettable: + status = isNamedAttributeSettable(event, interface); + break; + case kEventAccessibleGetChildAtPoint: + status = getChildAtPoint(next_ref, event, interface); + break; + case kEventAccessibleGetAllActionNames: + status = getAllActionNames(next_ref, event, interface); + break; + case kEventAccessibleGetFocusedChild: + status = CallNextEventHandler(next_ref, event); + break; + case kEventAccessibleSetNamedAttribute: + status = setNamedAttribute(next_ref, event, interface); + break; + case kEventAccessiblePerformNamedAction: + status = performNamedAction(next_ref, event, interface); + break; + default: + status = CallNextEventHandler(next_ref, event); + break; + }; + return status; +} +#endif + +void QAccessible::initialize() +{ +#ifndef QT_MAC_USE_COCOA + registerQtAccessibilityHIObjectSubclass(); + installApplicationEventhandler(); +#endif +} + +// Sets thre root object for the application +void QAccessible::setRootObject(QObject *object) +{ + // Call installed root object handler if we have one + if (rootObjectHandler) { + rootObjectHandler(object); + return; + } + + rootObject = object; +} + +void QAccessible::cleanup() +{ + accessibleHierarchyManager()->reset(); +#ifndef QT_MAC_USE_COCOA + removeEventhandler(applicationEventHandlerUPP); + removeEventhandler(objectCreateEventHandlerUPP); + removeEventhandler(accessibilityEventHandlerUPP); +#endif +} + +void QAccessible::updateAccessibility(QObject *object, int child, Event reason) +{ + // Call installed update handler if we have one. + if (updateHandler) { + updateHandler(object, child, reason); + return; + } + +#ifndef QT_MAC_USE_COCOA + // Return if the mac accessibility is not enabled. + if(!AXAPIEnabled()) + return; + + // Work around crash, disable accessiblity for focus frames. + if (qstrcmp(object->metaObject()->className(), "QFocusFrame") == 0) + return; + +// qDebug() << "updateAccessibility" << object << child << hex << reason; + + if (reason == ObjectShow) { + QAInterface interface = QAInterface(QAccessible::queryAccessibleInterface(object), child); + accessibleHierarchyManager()->registerInterface(interface); + } + + const QAElement element = accessibleHierarchyManager()->lookup(object, child); + if (element.isValid() == false) + return; + + + CFStringRef notification = 0; + if(object && object->isWidgetType() && reason == ObjectCreated) { + notification = CFStringRef(QAXWindowCreatedNotification); + } else if(reason == ValueChanged) { + notification = CFStringRef(QAXValueChangedNotification); + } else if(reason == MenuStart) { + notification = CFStringRef(QAXMenuOpenedNotification); + } else if(reason == MenuEnd) { + notification = CFStringRef(QAXMenuClosedNotification); + } else if(reason == LocationChanged) { + notification = CFStringRef(QAXWindowMovedNotification); + } else if(reason == ObjectShow || reason == ObjectHide ) { + // When a widget is deleted we get a ObjectHide before the destroyed(QObject *) + // signal is emitted (which makes sense). However, at this point we are in the + // middle of the QWidget destructor which means that we have to be careful when + // using the widget pointer. Since we can't control what the accessibilty interfaces + // does when navigate() is called below we ignore the hide update in this case. + // (the widget will be deleted soon anyway.) + extern QWidgetPrivate * qt_widget_private(QWidget *); + if (QWidget *widget = qobject_cast<QWidget*>(object)) { + if (qt_widget_private(widget)->data.in_destructor) + return; + + // Check widget parent as well, special case for preventing crash + // when the viewport() of an abstract scroll area is hidden when + // the QWidget destructor hides all its children. + QWidget *parentWidget = widget->parentWidget(); + if (parentWidget && qt_widget_private(parentWidget)->data.in_destructor) + return; + } + + // There is no equivalent Mac notification for ObjectShow/Hide, so we call HIObjectSetAccessibilityIgnored + // and isItIntersting which will mark the HIObject accociated with the element as ignored if the + // QAccessible::Invisible state bit is set. + QAInterface interface = accessibleHierarchyManager()->lookup(element); + if (interface.isValid()) { + HIObjectSetAccessibilityIgnored(element.object(), !isItInteresting(interface)); + } + + // If the interface manages its own children, also check if we should ignore those. + if (isItemView(interface) == false && managesChildren(interface)) { + for (int i = 1; i <= interface.childCount(); ++i) { + QAInterface childInterface = interface.navigate(QAccessible::Child, i); + if (childInterface.isValid() && childInterface.isHIView() == false) { + const QAElement element = accessibleHierarchyManager()->lookup(childInterface); + if (element.isValid()) { + HIObjectSetAccessibilityIgnored(element.object(), !isItInteresting(childInterface)); + } + } + } + } + + } else if(reason == Focus) { + if(object && object->isWidgetType()) { + QWidget *w = static_cast<QWidget*>(object); + if(w->isWindow()) + notification = CFStringRef(QAXFocusedWindowChangedNotification); + else + notification = CFStringRef(QAXFocusedUIElementChangedNotification); + } + } + + if (!notification) + return; + + AXNotificationHIObjectNotify(notification, element.object(), element.id()); +#endif +} + +QT_END_NAMESPACE + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/gui/accessible/qaccessible_mac_carbon.cpp b/src/gui/accessible/qaccessible_mac_carbon.cpp new file mode 100644 index 0000000000..2e8a7628e8 --- /dev/null +++ b/src/gui/accessible/qaccessible_mac_carbon.cpp @@ -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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +static OSStatus applicationEventHandler(EventHandlerCallRef next_ref, EventRef event, void *data); +static EventHandlerUPP applicationEventHandlerUPP = 0; +static EventTypeSpec application_events[] = { + { kEventClassAccessibility, kEventAccessibleGetChildAtPoint }, + { kEventClassAccessibility, kEventAccessibleGetNamedAttribute } +}; + +static CFStringRef kObjectQtAccessibility = CFSTR("com.trolltech.qt.accessibility"); +static EventHandlerUPP objectCreateEventHandlerUPP = 0; +static EventTypeSpec objectCreateEvents[] = { + { kEventClassHIObject, kEventHIObjectConstruct }, + { kEventClassHIObject, kEventHIObjectInitialize }, + { kEventClassHIObject, kEventHIObjectDestruct }, + { kEventClassHIObject, kEventHIObjectPrintDebugInfo } +}; + +static OSStatus accessibilityEventHandler(EventHandlerCallRef next_ref, EventRef event, void *data); +static EventHandlerUPP accessibilityEventHandlerUPP = 0; +static EventTypeSpec accessibilityEvents[] = { + { kEventClassAccessibility, kEventAccessibleGetChildAtPoint }, + { kEventClassAccessibility, kEventAccessibleGetFocusedChild }, + { kEventClassAccessibility, kEventAccessibleGetAllAttributeNames }, + { kEventClassAccessibility, kEventAccessibleGetNamedAttribute }, + { kEventClassAccessibility, kEventAccessibleSetNamedAttribute }, + { kEventClassAccessibility, kEventAccessibleIsNamedAttributeSettable }, + { kEventClassAccessibility, kEventAccessibleGetAllActionNames }, + { kEventClassAccessibility, kEventAccessiblePerformNamedAction }, + { kEventClassAccessibility, kEventAccessibleGetNamedActionDescription } +}; + +static void installAcessibilityEventHandler(HIObjectRef hiObject) +{ + if (!accessibilityEventHandlerUPP) + accessibilityEventHandlerUPP = NewEventHandlerUPP(accessibilityEventHandler); + + InstallHIObjectEventHandler(hiObject, accessibilityEventHandlerUPP, + GetEventTypeCount(accessibilityEvents), + accessibilityEvents, 0, 0); +} + +static OSStatus objectCreateEventHandler(EventHandlerCallRef next_ref, EventRef event, void *data) +{ + Q_UNUSED(data) + Q_UNUSED(event) + Q_UNUSED(next_ref) + return noErr; +} + +static void registerQtAccessibilityHIObjectSubclass() +{ + if (!objectCreateEventHandlerUPP) + objectCreateEventHandlerUPP = NewEventHandlerUPP(objectCreateEventHandler); + OSStatus err = HIObjectRegisterSubclass(kObjectQtAccessibility, 0, 0, objectCreateEventHandlerUPP, + GetEventTypeCount(objectCreateEvents), objectCreateEvents, 0, 0); + if (err && err != hiObjectClassExistsErr) + qWarning("qaccessible_mac internal error: Could not register accessibility HIObject subclass"); +} + +static void installApplicationEventhandler() +{ + if (!applicationEventHandlerUPP) + applicationEventHandlerUPP = NewEventHandlerUPP(applicationEventHandler); + + OSStatus err = InstallApplicationEventHandler(applicationEventHandlerUPP, + GetEventTypeCount(application_events), application_events, + 0, 0); + + if (err && err != eventHandlerAlreadyInstalledErr) + qWarning("qaccessible_mac internal error: Could not install application accessibility event handler"); +} + +static void removeEventhandler(EventHandlerUPP eventHandler) +{ + if (eventHandler) { + DisposeEventHandlerUPP(eventHandler); + eventHandler = 0; + } +} diff --git a/src/gui/accessible/qaccessible_mac_cocoa.mm b/src/gui/accessible/qaccessible_mac_cocoa.mm new file mode 100644 index 0000000000..60b967ab39 --- /dev/null +++ b/src/gui/accessible/qaccessible_mac_cocoa.mm @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qaccessible.h" +#include "qaccessible_mac_p.h" +#include "qdebug.h" +#include "qtabwidget.h" + +#include <private/qt_mac_p.h> +#include <private/qcocoaview_mac_p.h> +#include <private/qwidget_p.h> + + +#ifndef QT_NO_ACCESSIBILITY + +#ifdef QT_MAC_USE_COCOA + +QT_BEGIN_NAMESPACE + +//#define MAC_ACCESSIBILTY_DEVELOPER_MODE + +#ifndef QT_NO_DEBUG_STREAM +#ifdef MAC_ACCESSIBILTY_DEVELOPER_MODE +#define MAC_ACCESSIBILTY_DEBUG QT_PREPEND_NAMESPACE(qDebug) +#else +#define MAC_ACCESSIBILTY_DEBUG if (0) QT_PREPEND_NAMESPACE(qDebug) +#endif +#else +#define MAC_ACCESSIBILTY_DEBUG if (0) QT_PREPEND_NAMESPACE(QNoDebug) +#endif + +typedef QMap<QAccessible::Role, NSString *> QMacAccessibiltyRoleMap; +Q_GLOBAL_STATIC(QMacAccessibiltyRoleMap, qMacAccessibiltyRoleMap); + +static QAInterface interfaceForView(QT_MANGLE_NAMESPACE(QCocoaView) *view) +{ + return QAInterface(QAccessible::queryAccessibleInterface([view qt_qwidget])); +} + +/* + Set up mappings from Qt accessibilty roles to Mac accessibilty roles. +*/ +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; +} + +/* + Returns a Mac accessibility role for the given interface, or + NSAccessibilityUnknownRole if no role mapping is found. +*/ +static NSString *macRoleForInterface(QAInterface interface) +{ + const QAccessible::Role qtRole = interface.role(); + 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; +} + +/* + Is the interface a QTabBar embedded in a QTabWidget? + (as opposed to a stand-alone tab bar) +*/ +static bool isEmbeddedTabBar(const QAInterface &interface) +{ + QObject *object = interface.object(); + if (interface.role() == QAccessible::PageTabList && object) + return (qobject_cast<QTabWidget *>(object->parent())); + + return false; +} + +static bool isInterfaceIgnored(QAInterface interface) +{ + // Mac accessibility does not have an attribute that corresponds to the + // Invisible/Offscreen state. Use the ignore facility to disable them. + const QAccessible::State state = interface.state(); + if (state & QAccessible::Invisible || + state & QAccessible::Offscreen ) + return false; + + // Hide QTabBars that has a QTabWidget parent (the QTabWidget handles the accessibility) + if (isEmbeddedTabBar(interface)) + return false; + + if (QObject * const object = interface.object()) { + const QString className = QLatin1String(object->metaObject()->className()); + + // Prevent VoiceOver from focusing on tool tips by ignoring those + // interfaces. Shifting VoiceOver focus to the tool tip is confusing + // and the contents of the tool tip is avalible through the description + // attribute anyway. + if (className == QLatin1String("QTipLabel")) + return false; + } + + // Hide interfaces with an unknown role. When developing it's often useful to disable + // this check to see all interfaces in the hierarchy. +#ifndef MAC_ACCESSIBILTY_DEVELOPER_MODE + return [macRoleForInterface(interface) isEqualToString: NSAccessibilityUnknownRole]; +#else + return NO; +#endif +} + +QT_END_NAMESPACE + +@implementation QT_MANGLE_NAMESPACE(QCocoaView) (Accessibility) + +- (BOOL)accessibilityIsIgnored +{ + QT_PREPEND_NAMESPACE(QAInterface) interface = QT_PREPEND_NAMESPACE(interfaceForView)(self); + return isInterfaceIgnored(interface); +} + +- (NSArray *)accessibilityAttributeNames +{ + QT_PREPEND_NAMESPACE(QAInterface) interface = QT_PREPEND_NAMESPACE(interfaceForView)(self); + + static NSArray *attributes = nil; + if (attributes == nil) { + attributes = [super accessibilityAttributeNames]; + + } + return attributes; +} + +- (id)accessibilityAttributeValue:(NSString *)attribute +{ + MAC_ACCESSIBILTY_DEBUG() << "accessibilityAttributeValue" << self << + QT_PREPEND_NAMESPACE(QCFString)::toQString(reinterpret_cast<CFStringRef>(attribute)); + + QT_PREPEND_NAMESPACE(QAInterface) interface = QT_PREPEND_NAMESPACE(interfaceForView)(self); + + // Switch on the attribute name and call the appropriate handler function. + // Pass the call on to the NSView class for attributes we don't handle. + if ([attribute isEqualToString:@"AXRole"]) { + return macRoleForInterface(interface); + } else { + return [super accessibilityAttributeValue:attribute]; + } +} + +@end + +#endif // QT_MAC_USE_COCOA + +#endif // QT_NO_ACCESSIBILITY + diff --git a/src/gui/accessible/qaccessible_mac_p.h b/src/gui/accessible/qaccessible_mac_p.h new file mode 100644 index 0000000000..9acb5d3aef --- /dev/null +++ b/src/gui/accessible/qaccessible_mac_p.h @@ -0,0 +1,479 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QACCESSIBLE_MAC_P_H +#define QACCESSIBLE_MAC_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +// + +#include <qglobal.h> +#include <private/qt_mac_p.h> +#include <qaccessible.h> +#include <qwidget.h> +#include <qdebug.h> + +//#define Q_ACCESSIBLE_MAC_DEBUG + +QT_BEGIN_NAMESPACE + +/* + QAccessibleInterfaceWrapper wraps QAccessibleInterface and adds + a ref count. QAccessibleInterfaceWrapper is a "by-value" class. +*/ +class Q_AUTOTEST_EXPORT QAccessibleInterfaceWrapper +{ +public: + QAccessibleInterfaceWrapper() + : interface(0), childrenIsRegistered(new bool(false)), refCount(new int(1)) { } + + QAccessibleInterfaceWrapper(QAccessibleInterface *interface) + :interface(interface), childrenIsRegistered(new bool(false)), refCount(new int(1)) { } + + ~QAccessibleInterfaceWrapper() + { + if (--(*refCount) == 0) { + delete interface; + delete refCount; + delete childrenIsRegistered; + } + } + + QAccessibleInterfaceWrapper(const QAccessibleInterfaceWrapper &other) + :interface(other.interface), childrenIsRegistered(other.childrenIsRegistered), refCount(other.refCount) + { + ++(*refCount); + } + + void operator=(const QAccessibleInterfaceWrapper &other) + { + if (other.interface == interface) + return; + + if (--(*refCount) == 0) { + delete interface; + delete refCount; + delete childrenIsRegistered; + } + + interface = other.interface; + childrenIsRegistered = other.childrenIsRegistered; + refCount = other.refCount; + ++(*refCount); + } + + QAccessibleInterface *interface; + bool *childrenIsRegistered; +private: + int *refCount; +}; + +/* + QAInterface represents one accessiblity item. It hides the fact that + one QAccessibleInterface may represent more than one item, and it also + automates the memory management for QAccessibleInterfaces using the + QAccessibleInterfaceWrapper wrapper class. + + It has the same API as QAccessibleInterface, minus the child parameter + in the functions. +*/ +class Q_AUTOTEST_EXPORT QAInterface : public QAccessible +{ +public: + QAInterface() + : base(QAccessibleInterfaceWrapper()) + { } + + QAInterface(QAccessibleInterface *interface, int child = 0) + { + if (interface == 0 || child > interface->childCount()) { + base = QAccessibleInterfaceWrapper(); + } else { + base = QAccessibleInterfaceWrapper(interface); + m_cachedObject = interface->object(); + this->child = child; + } + } + + QAInterface(QAccessibleInterfaceWrapper wrapper, int child = 0) + :base(wrapper), m_cachedObject(wrapper.interface->object()), child(child) + { } + + QAInterface(const QAInterface &other, int child) + { + if (other.isValid() == false || child > other.childCount()) { + base = QAccessibleInterfaceWrapper(); + } else { + base = other.base; + m_cachedObject = other.m_cachedObject; + this->child = child; + } + } + + bool operator==(const QAInterface &other) const; + bool operator!=(const QAInterface &other) const; + + inline QString actionText (int action, Text text) const + { return base.interface->actionText(action, text, child); } + + QAInterface childAt(int x, int y) const + { + if (!checkValid()) + return QAInterface(); + + const int foundChild = base.interface->childAt(x, y); + + if (foundChild == -1) + return QAInterface(); + + if (child == 0) + return navigate(QAccessible::Child, foundChild); + + if (foundChild == child) + return *this; + return QAInterface(); + } + + int indexOfChild(const QAInterface &child) const + { + if (!checkValid()) + return -1; + + if (*this != child.parent()) + return -1; + + if (object() == child.object()) + return child.id(); + + return base.interface->indexOfChild(child.base.interface); + } + + inline int childCount() const + { + if (!checkValid()) + return 0; + + if (child != 0) + return 0; + return base.interface->childCount(); + } + + QList<QAInterface> children() const + { + if (!checkValid()) + return QList<QAInterface>(); + + QList<QAInterface> children; + for (int i = 1; i <= childCount(); ++i) { + children.append(navigate(QAccessible::Child, i)); + } + return children; + } + + QAInterface childAt(int index) const + { + return navigate(QAccessible::Child, index); + } + + inline void doAction(int action, const QVariantList ¶ms = QVariantList()) const + { + if (!checkValid()) + return; + + base.interface->doAction(action, child, params); + } + + QAInterface navigate(RelationFlag relation, int entry) const; + + inline QObject * object() const + { + if (!checkValid()) + return 0; + + return base.interface->object(); + } + + QAInterface objectInterface() const + { + if (!checkValid()) + return QAInterface(); + + QObject *obj = object(); + QAInterface current = *this; + while (obj == 0) + { + QAInterface parent = current.parent(); + if (parent.isValid() == false) + break; + obj = parent.object(); + current = parent; + } + return current; + } + + inline HIObjectRef hiObject() const + { + if (!checkValid()) + return 0; + QWidget * const widget = qobject_cast<QWidget * const>(object()); + if (widget) + return (HIObjectRef)widget->winId(); + else + return 0; + } + + inline QObject * cachedObject() const + { + if (!checkValid()) + return 0; + return m_cachedObject; + } + + inline QRect rect() const + { + if (!checkValid()) + return QRect(); + return base.interface->rect(child); + } + + inline Role role() const + { + if (!checkValid()) + return QAccessible::NoRole; + return base.interface->role(child); + } + + inline void setText(Text t, const QString &text) const + { + if (!checkValid()) + return; + base.interface->setText(t, child, text); + } + + inline State state() const + { + if (!checkValid()) + return 0; + return base.interface->state(child); + } + + inline QString text (Text text) const + { + if (!checkValid()) + return QString(); + return base.interface->text(text, child); + } + + inline QString value() const + { return text(QAccessible::Value); } + + inline QString name() const + { return text(QAccessible::Name); } + + inline int userActionCount() const + { + if (!checkValid()) + return 0; + return base.interface->userActionCount(child); + } + + inline QString className() const + { + if (!checkValid()) + return QString(); + return QLatin1String(base.interface->object()->metaObject()->className()); + } + + inline bool isHIView() const + { return (child == 0 && object() != 0); } + + inline int id() const + { return child; } + + inline bool isValid() const + { + return (base.interface != 0 && base.interface->isValid()); + } + + QAInterface parent() const + { return navigate(QAccessible::Ancestor, 1); } + + QAccessibleInterfaceWrapper interfaceWrapper() const + { return base; } + +protected: + bool checkValid() const + { + const bool valid = isValid(); +#ifdef Q_ACCESSIBLE_MAC_DEBUG + if (!valid) + qFatal("QAccessible_mac: tried to use invalid interface."); +#endif + return valid; + } + + QAccessibleInterfaceWrapper base; + QObject *m_cachedObject; + int child; +}; + +Q_AUTOTEST_EXPORT QDebug operator<<(QDebug debug, const QAInterface &interface); + +/* + QAElement is a thin wrapper around an AXUIElementRef that automates + the ref-counting. +*/ +class Q_AUTOTEST_EXPORT QAElement +{ +public: + QAElement(); + explicit QAElement(AXUIElementRef elementRef); + QAElement(const QAElement &element); + QAElement(HIObjectRef, int child); + ~QAElement(); + + inline HIObjectRef object() const + { +#ifndef Q_WS_MAC64 + return AXUIElementGetHIObject(elementRef); +#else + return 0; +#endif + } + + inline int id() const + { + UInt64 theId; +#ifndef QT_MAC_USE_COCOA + AXUIElementGetIdentifier(elementRef, &theId); +#else + theId = 0; +#endif + return theId; + } + + inline AXUIElementRef element() const + { + return elementRef; + } + + inline bool isValid() const + { + return (elementRef != 0); + } + + void operator=(const QAElement &other); + bool operator==(const QAElement &other) const; +private: + AXUIElementRef elementRef; +}; + + +class QInterfaceFactory +{ +public: + virtual QAInterface interface(UInt64 identifier) = 0; + virtual QAElement element(int id) = 0; + virtual QAElement element(const QAInterface &interface) + { + return element(interface.id()); + } + virtual void registerChildren() = 0; + virtual ~QInterfaceFactory() {} +}; + +/* + QAccessibleHierarchyManager bridges the Mac and Qt accessibility hierarchies. + There is a one-to-one relationship between QAElements on the Mac side + and QAInterfaces on the Qt side, and this class provides lookup functions + that translates between these to items. + + The identity of a QAInterface is determined by its QAccessibleInterface and + child identifier, and the identity of a QAElement is determined by its + HIObjectRef and identifier. + + QAccessibleHierarchyManager receives QObject::destroyed() signals and deletes + the accessibility objects for destroyed objects. +*/ +class Q_AUTOTEST_EXPORT QAccessibleHierarchyManager : public QObject +{ +Q_OBJECT +public: + ~QAccessibleHierarchyManager() { reset(); } + static QAccessibleHierarchyManager *instance(); + void reset(); + + QAElement registerInterface(QObject *object, int child); + QAElement registerInterface(const QAInterface &interface); + void registerInterface(QObject *object, HIObjectRef hiobject, QInterfaceFactory *interfaceFactory); + + void registerChildren(const QAInterface &interface); + + QAInterface lookup(const AXUIElementRef &element); + QAInterface lookup(const QAElement &element); + QAElement lookup(const QAInterface &interface); + QAElement lookup(QObject * const object, int id); +private slots: + void objectDestroyed(QObject *); +private: + typedef QHash<QObject *, QInterfaceFactory *> QObjectElementHash; + typedef QHash<HIObjectRef, QInterfaceFactory *> HIObjectInterfaceHash; + typedef QHash<QObject *, HIObjectRef> QObjectHIObjectHash; + + QObjectElementHash qobjectElementHash; + HIObjectInterfaceHash hiobjectInterfaceHash; + QObjectHIObjectHash qobjectHiobjectHash; +}; + +Q_AUTOTEST_EXPORT bool isItInteresting(const QAInterface &interface); + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/accessible/qaccessible_unix.cpp b/src/gui/accessible/qaccessible_unix.cpp new file mode 100644 index 0000000000..f04d075543 --- /dev/null +++ b/src/gui/accessible/qaccessible_unix.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessible.h" +#include "qaccessiblebridge.h" + +#ifndef QT_NO_ACCESSIBILITY + +#include "qcoreapplication.h" +#include "qmutex.h" +#include "qvector.h" +#include "private/qfactoryloader_p.h" + +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_LIBRARY +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QAccessibleBridgeFactoryInterface_iid, QLatin1String("/accessiblebridge"))) +#endif +Q_GLOBAL_STATIC(QVector<QAccessibleBridge *>, bridges) +static bool isInit = false; + +void QAccessible::initialize() +{ + if (isInit) + return; + isInit = true; + + if (qgetenv("QT_ACCESSIBILITY") != "1") + return; +#ifndef QT_NO_LIBRARY + const QStringList l = loader()->keys(); + for (int i = 0; i < l.count(); ++i) { + if (QAccessibleBridgeFactoryInterface *factory = + qobject_cast<QAccessibleBridgeFactoryInterface*>(loader()->instance(l.at(i)))) { + QAccessibleBridge * bridge = factory->create(l.at(i)); + if (bridge) + bridges()->append(bridge); + } + } +#endif +} + +void QAccessible::cleanup() +{ + qDeleteAll(*bridges()); +} + +void QAccessible::updateAccessibility(QObject *o, int who, Event reason) +{ + Q_ASSERT(o); + + if (updateHandler) { + updateHandler(o, who, reason); + return; + } + + initialize(); + if (bridges()->isEmpty()) + return; + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(o); + if (!iface) + return; + + for (int i = 0; i < bridges()->count(); ++i) + bridges()->at(i)->notifyAccessibilityUpdate(reason, iface, who); + delete iface; +} + +void QAccessible::setRootObject(QObject *o) +{ + if (rootObjectHandler) { + rootObjectHandler(o); + return; + } + + initialize(); + if (bridges()->isEmpty()) + return; + + if (!o) + return; + + for (int i = 0; i < bridges()->count(); ++i) { + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(o); + bridges()->at(i)->setRootObject(iface); + } +} + +QT_END_NAMESPACE + +#endif // QT_NO_ACCESSIBILITY + diff --git a/src/gui/accessible/qaccessible_win.cpp b/src/gui/accessible/qaccessible_win.cpp new file mode 100644 index 0000000000..caa21043d8 --- /dev/null +++ b/src/gui/accessible/qaccessible_win.cpp @@ -0,0 +1,1217 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qaccessible.h" +#ifndef QT_NO_ACCESSIBILITY + +#include "qapplication.h" +#include <private/qsystemlibrary_p.h> +#include "qmessagebox.h" // ### dependency +#include "qt_windows.h" +#include "qwidget.h" +#include "qsettings.h" + +#include <winuser.h> +#if !defined(WINABLEAPI) +# if defined(Q_WS_WINCE) +# include <bldver.h> +# endif +# include <winable.h> +#endif + +#include <oleacc.h> +#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU) +#include <comdef.h> +#endif + +#ifdef Q_WS_WINCE +#include "qguifunctions_wince.h" +#endif + +QT_BEGIN_NAMESPACE + +//#define DEBUG_SHOW_ATCLIENT_COMMANDS +#ifdef DEBUG_SHOW_ATCLIENT_COMMANDS +QT_BEGIN_INCLUDE_NAMESPACE +#include <qdebug.h> +QT_END_INCLUDE_NAMESPACE + +static const char *roleString(QAccessible::Role role) +{ + static const char *roles[] = { + "NoRole" /* = 0x00000000 */, + "TitleBar" /* = 0x00000001 */, + "MenuBar" /* = 0x00000002 */, + "ScrollBar" /* = 0x00000003 */, + "Grip" /* = 0x00000004 */, + "Sound" /* = 0x00000005 */, + "Cursor" /* = 0x00000006 */, + "Caret" /* = 0x00000007 */, + "AlertMessage" /* = 0x00000008 */, + "Window" /* = 0x00000009 */, + "Client" /* = 0x0000000A */, + "PopupMenu" /* = 0x0000000B */, + "MenuItem" /* = 0x0000000C */, + "ToolTip" /* = 0x0000000D */, + "Application" /* = 0x0000000E */, + "Document" /* = 0x0000000F */, + "Pane" /* = 0x00000010 */, + "Chart" /* = 0x00000011 */, + "Dialog" /* = 0x00000012 */, + "Border" /* = 0x00000013 */, + "Grouping" /* = 0x00000014 */, + "Separator" /* = 0x00000015 */, + "ToolBar" /* = 0x00000016 */, + "StatusBar" /* = 0x00000017 */, + "Table" /* = 0x00000018 */, + "ColumnHeader" /* = 0x00000019 */, + "RowHeader" /* = 0x0000001A */, + "Column" /* = 0x0000001B */, + "Row" /* = 0x0000001C */, + "Cell" /* = 0x0000001D */, + "Link" /* = 0x0000001E */, + "HelpBalloon" /* = 0x0000001F */, + "Assistant" /* = 0x00000020 */, + "List" /* = 0x00000021 */, + "ListItem" /* = 0x00000022 */, + "Tree" /* = 0x00000023 */, + "TreeItem" /* = 0x00000024 */, + "PageTab" /* = 0x00000025 */, + "PropertyPage" /* = 0x00000026 */, + "Indicator" /* = 0x00000027 */, + "Graphic" /* = 0x00000028 */, + "StaticText" /* = 0x00000029 */, + "EditableText" /* = 0x0000002A */, // Editable, selectable, etc. + "PushButton" /* = 0x0000002B */, + "CheckBox" /* = 0x0000002C */, + "RadioButton" /* = 0x0000002D */, + "ComboBox" /* = 0x0000002E */, + "DropList" /* = 0x0000002F */, // commented out + "ProgressBar" /* = 0x00000030 */, + "Dial" /* = 0x00000031 */, + "HotkeyField" /* = 0x00000032 */, + "Slider" /* = 0x00000033 */, + "SpinBox" /* = 0x00000034 */, + "Canvas" /* = 0x00000035 */, + "Animation" /* = 0x00000036 */, + "Equation" /* = 0x00000037 */, + "ButtonDropDown" /* = 0x00000038 */, + "ButtonMenu" /* = 0x00000039 */, + "ButtonDropGrid" /* = 0x0000003A */, + "Whitespace" /* = 0x0000003B */, + "PageTabList" /* = 0x0000003C */, + "Clock" /* = 0x0000003D */, + "Splitter" /* = 0x0000003E */, + "LayeredPane" /* = 0x0000003F */, + "UserRole" /* = 0x0000ffff*/ + }; + + if (role >=0x40) + role = QAccessible::UserRole; + return roles[int(role)]; +} + +void showDebug(const char* funcName, const QAccessibleInterface *iface) +{ + qDebug() << "Role:" << roleString(iface->role(0)) + << "Name:" << iface->text(QAccessible::Name, 0) + << "State:" << QString::number(int(iface->state(0)), 16) + << QLatin1String(funcName); +} +#else +# define showDebug(f, iface) +#endif + +void QAccessible::initialize() +{ + +} +void QAccessible::cleanup() +{ + +} + +void QAccessible::updateAccessibility(QObject *o, int who, Event reason) +{ + Q_ASSERT(o); + + if (updateHandler) { + updateHandler(o, who, reason); + return; + } + + QString soundName; + switch (reason) { + case PopupMenuStart: + soundName = QLatin1String("MenuPopup"); + break; + + case MenuCommand: + soundName = QLatin1String("MenuCommand"); + break; + + case Alert: + { +#ifndef QT_NO_MESSAGEBOX + QMessageBox *mb = qobject_cast<QMessageBox*>(o); + if (mb) { + switch (mb->icon()) { + case QMessageBox::Warning: + soundName = QLatin1String("SystemExclamation"); + break; + case QMessageBox::Critical: + soundName = QLatin1String("SystemHand"); + break; + case QMessageBox::Information: + soundName = QLatin1String("SystemAsterisk"); + break; + default: + break; + } + } else +#endif // QT_NO_MESSAGEBOX + { + soundName = QLatin1String("SystemAsterisk"); + } + + } + break; + default: + break; + } + + if (soundName.size()) { +#ifndef QT_NO_SETTINGS + QSettings settings(QLatin1String("HKEY_CURRENT_USER\\AppEvents\\Schemes\\Apps\\.Default\\") + soundName, + QSettings::NativeFormat); + QString file = settings.value(QLatin1String(".Current/.")).toString(); +#else + QString file; +#endif + if (!file.isEmpty()) { + PlaySound(reinterpret_cast<const wchar_t *>(soundName.utf16()), 0, SND_ALIAS | SND_ASYNC | SND_NODEFAULT | SND_NOWAIT); + } + } + + if (!isActive()) + return; + + typedef void (WINAPI *PtrNotifyWinEvent)(DWORD, HWND, LONG, LONG); + +#if defined(Q_WS_WINCE) // ### TODO: check for NotifyWinEvent in CE 6.0 + // There is no user32.lib nor NotifyWinEvent for CE + return; +#else + static PtrNotifyWinEvent ptrNotifyWinEvent = 0; + static bool resolvedNWE = false; + if (!resolvedNWE) { + resolvedNWE = true; + ptrNotifyWinEvent = (PtrNotifyWinEvent)QSystemLibrary::resolve(QLatin1String("user32"), "NotifyWinEvent"); + } + if (!ptrNotifyWinEvent) + return; + + // An event has to be associated with a window, + // so find the first parent that is a widget. + QWidget *w = 0; + if (o->isWidgetType()) { + w = (QWidget*)o; + } else { + QObject *p = o; + while ((p = p->parent()) != 0) { + if (p->isWidgetType()) { + w = (QWidget*)p; + break; + } + } + } + + if (!w) { + if (reason != QAccessible::ContextHelpStart && + reason != QAccessible::ContextHelpEnd) + w = QApplication::focusWidget(); + if (!w) { + w = QApplication::activeWindow(); + + if (!w) + return; + +// ### Fixme +// if (!w) { +// w = qApp->mainWidget(); +// if (!w) +// return; +// } + } + } + + if (reason != MenuCommand) { // MenuCommand is faked + ptrNotifyWinEvent(reason, w->winId(), OBJID_CLIENT, who); + } +#endif // Q_WS_WINCE +} + +void QAccessible::setRootObject(QObject *o) +{ + if (rootObjectHandler) { + rootObjectHandler(o); + } +} + +class QWindowsEnumerate : public IEnumVARIANT +{ +public: + QWindowsEnumerate(const QVector<int> &a) + : ref(0), current(0),array(a) + { + } + + virtual ~QWindowsEnumerate() {} + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE Clone(IEnumVARIANT **ppEnum); + HRESULT STDMETHODCALLTYPE Next(unsigned long celt, VARIANT FAR* rgVar, unsigned long FAR* pCeltFetched); + HRESULT STDMETHODCALLTYPE Reset(); + HRESULT STDMETHODCALLTYPE Skip(unsigned long celt); + +private: + ULONG ref; + ULONG current; + QVector<int> array; +}; + +HRESULT STDMETHODCALLTYPE QWindowsEnumerate::QueryInterface(REFIID id, LPVOID *iface) +{ + *iface = 0; + if (id == IID_IUnknown) + *iface = (IUnknown*)this; + else if (id == IID_IEnumVARIANT) + *iface = (IEnumVARIANT*)this; + + if (*iface) { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE QWindowsEnumerate::AddRef() +{ + return ++ref; +} + +ULONG STDMETHODCALLTYPE QWindowsEnumerate::Release() +{ + if (!--ref) { + delete this; + return 0; + } + return ref; +} + +HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Clone(IEnumVARIANT **ppEnum) +{ + QWindowsEnumerate *penum = 0; + *ppEnum = 0; + + penum = new QWindowsEnumerate(array); + if (!penum) + return E_OUTOFMEMORY; + penum->current = current; + penum->array = array; + penum->AddRef(); + *ppEnum = penum; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Next(unsigned long celt, VARIANT FAR* rgVar, unsigned long FAR* pCeltFetched) +{ + if (pCeltFetched) + *pCeltFetched = 0; + + ULONG l; + for (l = 0; l < celt; l++) { + VariantInit(&rgVar[l]); + if ((current+1) > (ULONG)array.size()) { + *pCeltFetched = l; + return S_FALSE; + } + + rgVar[l].vt = VT_I4; + rgVar[l].lVal = array[(int)current]; + ++current; + } + *pCeltFetched = l; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Reset() +{ + current = 0; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Skip(unsigned long celt) +{ + current += celt; + if (current > (ULONG)array.size()) { + current = array.size(); + return S_FALSE; + } + return S_OK; +} + +/* +*/ +class QWindowsAccessible : public IAccessible, IOleWindow, QAccessible +{ +public: + QWindowsAccessible(QAccessibleInterface *a) + : ref(0), accessible(a) + { + } + + virtual ~QWindowsAccessible() + { + delete accessible; + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int *); + HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int, unsigned long, ITypeInfo **); + HRESULT STDMETHODCALLTYPE GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *); + HRESULT STDMETHODCALLTYPE Invoke(long, const _GUID &, unsigned long, unsigned short, tagDISPPARAMS *, tagVARIANT *, tagEXCEPINFO *, unsigned int *); + + HRESULT STDMETHODCALLTYPE accHitTest(long xLeft, long yTop, VARIANT *pvarID); + HRESULT STDMETHODCALLTYPE accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID); + HRESULT STDMETHODCALLTYPE accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd); + HRESULT STDMETHODCALLTYPE get_accChild(VARIANT varChildID, IDispatch** ppdispChild); + HRESULT STDMETHODCALLTYPE get_accChildCount(long* pcountChildren); + HRESULT STDMETHODCALLTYPE get_accParent(IDispatch** ppdispParent); + + HRESULT STDMETHODCALLTYPE accDoDefaultAction(VARIANT varID); + HRESULT STDMETHODCALLTYPE get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction); + HRESULT STDMETHODCALLTYPE get_accDescription(VARIANT varID, BSTR* pszDescription); + HRESULT STDMETHODCALLTYPE get_accHelp(VARIANT varID, BSTR *pszHelp); + HRESULT STDMETHODCALLTYPE get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic); + HRESULT STDMETHODCALLTYPE get_accKeyboardShortcut(VARIANT varID, BSTR *pszKeyboardShortcut); + HRESULT STDMETHODCALLTYPE get_accName(VARIANT varID, BSTR* pszName); + HRESULT STDMETHODCALLTYPE put_accName(VARIANT varChild, BSTR szName); + HRESULT STDMETHODCALLTYPE get_accRole(VARIANT varID, VARIANT *pvarRole); + HRESULT STDMETHODCALLTYPE get_accState(VARIANT varID, VARIANT *pvarState); + HRESULT STDMETHODCALLTYPE get_accValue(VARIANT varID, BSTR* pszValue); + HRESULT STDMETHODCALLTYPE put_accValue(VARIANT varChild, BSTR szValue); + + HRESULT STDMETHODCALLTYPE accSelect(long flagsSelect, VARIANT varID); + HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT *pvarID); + HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT *pvarChildren); + + HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd); + HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode); + +private: + ULONG ref; + QAccessibleInterface *accessible; +}; + +static inline BSTR QStringToBSTR(const QString &str) +{ + BSTR bstrVal; + + int wlen = str.length()+1; + bstrVal = SysAllocStringByteLen(0, wlen*2); + memcpy(bstrVal, str.unicode(), sizeof(QChar)*(wlen)); + bstrVal[wlen] = 0; + + return bstrVal; +} + +/* +*/ +IAccessible *qt_createWindowsAccessible(QAccessibleInterface *access) +{ + QWindowsAccessible *acc = new QWindowsAccessible(access); + IAccessible *iface; + acc->QueryInterface(IID_IAccessible, (void**)&iface); + + return iface; +} + +/* + IUnknown +*/ +HRESULT STDMETHODCALLTYPE QWindowsAccessible::QueryInterface(REFIID id, LPVOID *iface) +{ + *iface = 0; + if (id == IID_IUnknown) + *iface = (IUnknown*)(IDispatch*)this; + else if (id == IID_IDispatch) + *iface = (IDispatch*)this; + else if (id == IID_IAccessible) + *iface = (IAccessible*)this; + else if (id == IID_IOleWindow) + *iface = (IOleWindow*)this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; +} + +ULONG STDMETHODCALLTYPE QWindowsAccessible::AddRef() +{ + return ++ref; +} + +ULONG STDMETHODCALLTYPE QWindowsAccessible::Release() +{ + if (!--ref) { + delete this; + return 0; + } + return ref; +} + +/* + IDispatch +*/ + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetTypeInfoCount(unsigned int * pctinfo) +{ + // We don't use a type library + *pctinfo = 0; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetTypeInfo(unsigned int, unsigned long, ITypeInfo **pptinfo) +{ + // We don't use a type library + *pptinfo = 0; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetIDsOfNames(const _GUID &, wchar_t **rgszNames, unsigned int, unsigned long, long *rgdispid) +{ +#if !defined(Q_CC_BOR) && !defined(Q_CC_GNU) + // PROPERTIES: Hierarchical + if (_bstr_t(rgszNames[0]) == _bstr_t(L"accParent")) + rgdispid[0] = DISPID_ACC_PARENT; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accChildCount")) + rgdispid[0] = DISPID_ACC_CHILDCOUNT; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accChild")) + rgdispid[0] = DISPID_ACC_CHILD; + + // PROPERTIES: Descriptional + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accName(")) + rgdispid[0] = DISPID_ACC_NAME; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accValue")) + rgdispid[0] = DISPID_ACC_VALUE; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accDescription")) + rgdispid[0] = DISPID_ACC_DESCRIPTION; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accRole")) + rgdispid[0] = DISPID_ACC_ROLE; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accState")) + rgdispid[0] = DISPID_ACC_STATE; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accHelp")) + rgdispid[0] = DISPID_ACC_HELP; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accHelpTopic")) + rgdispid[0] = DISPID_ACC_HELPTOPIC; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accKeyboardShortcut")) + rgdispid[0] = DISPID_ACC_KEYBOARDSHORTCUT; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accFocus")) + rgdispid[0] = DISPID_ACC_FOCUS; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accSelection")) + rgdispid[0] = DISPID_ACC_SELECTION; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accDefaultAction")) + rgdispid[0] = DISPID_ACC_DEFAULTACTION; + + // METHODS + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accSelect")) + rgdispid[0] = DISPID_ACC_SELECT; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accLocation")) + rgdispid[0] = DISPID_ACC_LOCATION; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accNavigate")) + rgdispid[0] = DISPID_ACC_NAVIGATE; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accHitTest")) + rgdispid[0] = DISPID_ACC_HITTEST; + else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accDoDefaultAction")) + rgdispid[0] = DISPID_ACC_DODEFAULTACTION; + else + return DISP_E_UNKNOWNINTERFACE; + + return S_OK; +#else + Q_UNUSED(rgszNames); + Q_UNUSED(rgdispid); + + return DISP_E_MEMBERNOTFOUND; +#endif +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::Invoke(long dispIdMember, const _GUID &, unsigned long, unsigned short wFlags, tagDISPPARAMS *pDispParams, tagVARIANT *pVarResult, tagEXCEPINFO *, unsigned int *) +{ + HRESULT hr = DISP_E_MEMBERNOTFOUND; + + switch(dispIdMember) + { + case DISPID_ACC_PARENT: + if (wFlags == DISPATCH_PROPERTYGET) { + if (!pVarResult) + return E_INVALIDARG; + hr = get_accParent(&pVarResult->pdispVal); + } else { + hr = DISP_E_MEMBERNOTFOUND; + } + break; + + case DISPID_ACC_CHILDCOUNT: + if (wFlags == DISPATCH_PROPERTYGET) { + if (!pVarResult) + return E_INVALIDARG; + hr = get_accChildCount(&pVarResult->lVal); + } else { + hr = DISP_E_MEMBERNOTFOUND; + } + break; + + case DISPID_ACC_CHILD: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accChild(pDispParams->rgvarg[0], &pVarResult->pdispVal); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_NAME: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accName(pDispParams->rgvarg[0], &pVarResult->bstrVal); + else if (wFlags == DISPATCH_PROPERTYPUT) + hr = put_accName(pDispParams->rgvarg[0], pVarResult->bstrVal); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_VALUE: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accValue(pDispParams->rgvarg[0], &pVarResult->bstrVal); + else if (wFlags == DISPATCH_PROPERTYPUT) + hr = put_accValue(pDispParams->rgvarg[0], pVarResult->bstrVal); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_DESCRIPTION: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accDescription(pDispParams->rgvarg[0], &pVarResult->bstrVal); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_ROLE: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accRole(pDispParams->rgvarg[0], pVarResult); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_STATE: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accState(pDispParams->rgvarg[0], pVarResult); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_HELP: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accHelp(pDispParams->rgvarg[0], &pVarResult->bstrVal); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_HELPTOPIC: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accHelpTopic(&pDispParams->rgvarg[2].bstrVal, pDispParams->rgvarg[1], &pDispParams->rgvarg[0].lVal); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_KEYBOARDSHORTCUT: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accKeyboardShortcut(pDispParams->rgvarg[0], &pVarResult->bstrVal); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_FOCUS: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accFocus(pVarResult); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_SELECTION: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accSelection(pVarResult); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_DEFAULTACTION: + if (wFlags == DISPATCH_PROPERTYGET) + hr = get_accDefaultAction(pDispParams->rgvarg[0], &pVarResult->bstrVal); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_SELECT: + if (wFlags == DISPATCH_METHOD) + hr = accSelect(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0]); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_LOCATION: + if (wFlags == DISPATCH_METHOD) + hr = accLocation(&pDispParams->rgvarg[4].lVal, &pDispParams->rgvarg[3].lVal, &pDispParams->rgvarg[2].lVal, &pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0]); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_NAVIGATE: + if (wFlags == DISPATCH_METHOD) + hr = accNavigate(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0], pVarResult); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_HITTEST: + if (wFlags == DISPATCH_METHOD) + hr = accHitTest(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0].lVal, pVarResult); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_ACC_DODEFAULTACTION: + if (wFlags == DISPATCH_METHOD) + hr = accDoDefaultAction(pDispParams->rgvarg[0]); + else + hr = DISP_E_MEMBERNOTFOUND; + break; + + default: + hr = DISP_E_MEMBERNOTFOUND; + break; + } + + if (!SUCCEEDED(hr)) { + return hr; + } + return hr; +} + +/* + IAccessible +*/ +HRESULT STDMETHODCALLTYPE QWindowsAccessible::accHitTest(long xLeft, long yTop, VARIANT *pvarID) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + int control = accessible->childAt(xLeft, yTop); + if (control == -1) { + (*pvarID).vt = VT_EMPTY; + return S_FALSE; + } + QAccessibleInterface *acc = 0; + if (control) + accessible->navigate(Child, control, &acc); + if (!acc) { + (*pvarID).vt = VT_I4; + (*pvarID).lVal = control; + return S_OK; + } + + QWindowsAccessible* wacc = new QWindowsAccessible(acc); + IDispatch *iface = 0; + wacc->QueryInterface(IID_IDispatch, (void**)&iface); + if (iface) { + (*pvarID).vt = VT_DISPATCH; + (*pvarID).pdispVal = iface; + return S_OK; + } else { + delete wacc; + } + + (*pvarID).vt = VT_EMPTY; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + QRect rect = accessible->rect(varID.lVal); + if (rect.isValid()) { + *pxLeft = rect.x(); + *pyTop = rect.y(); + *pcxWidth = rect.width(); + *pcyHeight = rect.height(); + } else { + *pxLeft = 0; + *pyTop = 0; + *pcxWidth = 0; + *pcyHeight = 0; + } + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + QAccessibleInterface *acc = 0; + int control = -1; + switch(navDir) { + case NAVDIR_FIRSTCHILD: + control = accessible->navigate(Child, 1, &acc); + break; + case NAVDIR_LASTCHILD: + control = accessible->navigate(Child, accessible->childCount(), &acc); + break; + case NAVDIR_NEXT: + case NAVDIR_PREVIOUS: + if (!varStart.lVal){ + QAccessibleInterface *parent = 0; + accessible->navigate(Ancestor, 1, &parent); + if (parent) { + int index = parent->indexOfChild(accessible); + index += (navDir == NAVDIR_NEXT) ? 1 : -1; + if (index > 0 && index <= parent->childCount()) + control = parent->navigate(Child, index, &acc); + delete parent; + } + } else { + int index = varStart.lVal; + index += (navDir == NAVDIR_NEXT) ? 1 : -1; + if (index > 0 && index <= accessible->childCount()) + control = accessible->navigate(Child, index, &acc); + } + break; + case NAVDIR_UP: + control = accessible->navigate(Up, varStart.lVal, &acc); + break; + case NAVDIR_DOWN: + control = accessible->navigate(Down, varStart.lVal, &acc); + break; + case NAVDIR_LEFT: + control = accessible->navigate(Left, varStart.lVal, &acc); + break; + case NAVDIR_RIGHT: + control = accessible->navigate(Right, varStart.lVal, &acc); + break; + default: + break; + } + if (control == -1) { + (*pvarEnd).vt = VT_EMPTY; + return S_FALSE; + } + if (!acc) { + (*pvarEnd).vt = VT_I4; + (*pvarEnd).lVal = control; + return S_OK; + } + + QWindowsAccessible* wacc = new QWindowsAccessible(acc); + + IDispatch *iface = 0; + wacc->QueryInterface(IID_IDispatch, (void**)&iface); + if (iface) { + (*pvarEnd).vt = VT_DISPATCH; + (*pvarEnd).pdispVal = iface; + return S_OK; + } else { + delete wacc; + } + + (*pvarEnd).vt = VT_EMPTY; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, IDispatch** ppdispChild) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (varChildID.vt == VT_EMPTY) + return E_INVALIDARG; + + QAccessibleInterface *acc = 0; + RelationFlag rel = varChildID.lVal ? Child : Self; + accessible->navigate(rel, varChildID.lVal, &acc); + + if (acc) { + QWindowsAccessible* wacc = new QWindowsAccessible(acc); + wacc->QueryInterface(IID_IDispatch, (void**)ppdispChild); + return S_OK; + } + + *ppdispChild = 0; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChildCount(long* pcountChildren) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + *pcountChildren = accessible->childCount(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accParent(IDispatch** ppdispParent) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + QAccessibleInterface *acc = 0; + accessible->navigate(Ancestor, 1, &acc); + if (acc) { + QWindowsAccessible* wacc = new QWindowsAccessible(acc); + wacc->QueryInterface(IID_IDispatch, (void**)ppdispParent); + + if (*ppdispParent) + return S_OK; + } + + *ppdispParent = 0; + return S_FALSE; +} + +/* + Properties and methods +*/ +HRESULT STDMETHODCALLTYPE QWindowsAccessible::accDoDefaultAction(VARIANT varID) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + return accessible->doAction(DefaultAction, varID.lVal, QVariantList()) ? S_OK : S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + QString def = accessible->actionText(DefaultAction, Name, varID.lVal); + if (def.isEmpty()) { + *pszDefaultAction = 0; + return S_FALSE; + } + + *pszDefaultAction = QStringToBSTR(def); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDescription(VARIANT varID, BSTR* pszDescription) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + QString descr = accessible->text(Description, varID.lVal); + if (descr.size()) { + *pszDescription = QStringToBSTR(descr); + return S_OK; + } + + *pszDescription = 0; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accHelp(VARIANT varID, BSTR *pszHelp) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + QString help = accessible->text(Help, varID.lVal); + if (help.size()) { + *pszHelp = QStringToBSTR(help); + return S_OK; + } + + *pszHelp = 0; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accHelpTopic(BSTR *, VARIANT, long *) +{ + return DISP_E_MEMBERNOTFOUND; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accKeyboardShortcut(VARIANT varID, BSTR *pszKeyboardShortcut) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + QString sc = accessible->text(Accelerator, varID.lVal); + if (sc.size()) { + *pszKeyboardShortcut = QStringToBSTR(sc); + return S_OK; + } + + *pszKeyboardShortcut = 0; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accName(VARIANT varID, BSTR* pszName) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + QString n = accessible->text(Name, varID.lVal); + if (n.size()) { + *pszName = QStringToBSTR(n); + return S_OK; + } + + *pszName = 0; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::put_accName(VARIANT, BSTR) +{ + showDebug(__FUNCTION__, accessible); + return DISP_E_MEMBERNOTFOUND; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accRole(VARIANT varID, VARIANT *pvarRole) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + Role role = accessible->role(varID.lVal); + if (role != NoRole) { + if (role == LayeredPane) + role = QAccessible::Pane; + (*pvarRole).vt = VT_I4; + (*pvarRole).lVal = role; + } else { + (*pvarRole).vt = VT_EMPTY; + } + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accState(VARIANT varID, VARIANT *pvarState) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + (*pvarState).vt = VT_I4; + (*pvarState).lVal = accessible->state(varID.lVal); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accValue(VARIANT varID, BSTR* pszValue) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + QString value = accessible->text(Value, varID.lVal); + if (!value.isNull()) { + *pszValue = QStringToBSTR(value); + return S_OK; + } + + *pszValue = 0; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::put_accValue(VARIANT, BSTR) +{ + showDebug(__FUNCTION__, accessible); + return DISP_E_MEMBERNOTFOUND; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::accSelect(long flagsSelect, VARIANT varID) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + bool res = false; + + if (flagsSelect & SELFLAG_TAKEFOCUS) + res = accessible->doAction(SetFocus, varID.lVal, QVariantList()); + if (flagsSelect & SELFLAG_TAKESELECTION) { + accessible->doAction(ClearSelection, 0, QVariantList()); + res = accessible->doAction(AddToSelection, varID.lVal, QVariantList()); + } + if (flagsSelect & SELFLAG_EXTENDSELECTION) + res = accessible->doAction(ExtendSelection, varID.lVal, QVariantList()); + if (flagsSelect & SELFLAG_ADDSELECTION) + res = accessible->doAction(AddToSelection, varID.lVal, QVariantList()); + if (flagsSelect & SELFLAG_REMOVESELECTION) + res = accessible->doAction(RemoveSelection, varID.lVal, QVariantList()); + + return res ? S_OK : S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accFocus(VARIANT *pvarID) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + QAccessibleInterface *acc = 0; + int control = accessible->navigate(FocusChild, 1, &acc); + if (control == -1) { + (*pvarID).vt = VT_EMPTY; + return S_FALSE; + } + if (!acc || control == 0) { + (*pvarID).vt = VT_I4; + (*pvarID).lVal = control ? control : CHILDID_SELF; + return S_OK; + } + + QWindowsAccessible* wacc = new QWindowsAccessible(acc); + IDispatch *iface = 0; + wacc->QueryInterface(IID_IDispatch, (void**)&iface); + if (iface) { + (*pvarID).vt = VT_DISPATCH; + (*pvarID).pdispVal = iface; + return S_OK; + } else { + delete wacc; + } + + (*pvarID).vt = VT_EMPTY; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accSelection(VARIANT *pvarChildren) +{ + showDebug(__FUNCTION__, accessible); + if (!accessible->isValid()) + return E_FAIL; + + int cc = accessible->childCount(); + QVector<int> sel(cc); + int selIndex = 0; + for (int i = 1; i <= cc; ++i) { + QAccessibleInterface *child = 0; + int i2 = accessible->navigate(Child, i, &child); + bool isSelected = false; + if (child) { + isSelected = child->state(0) & Selected; + delete child; + child = 0; + } else { + isSelected = accessible->state(i2) & Selected; + } + if (isSelected) + sel[selIndex++] = i; + } + sel.resize(selIndex); + if (sel.isEmpty()) { + (*pvarChildren).vt = VT_EMPTY; + return S_FALSE; + } + if (sel.size() == 1) { + (*pvarChildren).vt = VT_I4; + (*pvarChildren).lVal = sel[0]; + return S_OK; + } + IEnumVARIANT *iface = new QWindowsEnumerate(sel); + IUnknown *uiface; + iface->QueryInterface(IID_IUnknown, (void**)&uiface); + (*pvarChildren).vt = VT_UNKNOWN; + (*pvarChildren).punkVal = uiface; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetWindow(HWND *phwnd) +{ + *phwnd = 0; + if (!accessible->isValid()) + return E_UNEXPECTED; + + QObject *o = accessible->object(); + if (!o || !o->isWidgetType()) + return E_FAIL; + + *phwnd = static_cast<QWidget*>(o)->winId(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsAccessible::ContextSensitiveHelp(BOOL) +{ + return S_OK; +} + +QT_END_NAMESPACE + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/gui/accessible/qaccessiblebridge.cpp b/src/gui/accessible/qaccessiblebridge.cpp new file mode 100644 index 0000000000..e151bb6752 --- /dev/null +++ b/src/gui/accessible/qaccessiblebridge.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessiblebridge.h" + +#ifndef QT_NO_ACCESSIBILITY + +QT_BEGIN_NAMESPACE + +/*! + \class QAccessibleBridge + \brief The QAccessibleBridge class is the base class for + accessibility back-ends. + + \ingroup accessibility + + Qt supports Microsoft Active Accessibility (MSAA), Mac OS X + Accessibility, and the Unix/X11 AT-SPI standard. By subclassing + QAccessibleBridge, you can support other backends than the + predefined ones. + + Currently, custom bridges are only supported on Unix. We might + add support for them on other platforms as well if there is + enough demand. + + \sa QAccessible, QAccessibleBridgePlugin +*/ + +/*! + \fn QAccessibleBridge::~QAccessibleBridge() + + Destroys the accessibility bridge object. +*/ + +/*! + \fn void QAccessibleBridge::setRootObject(QAccessibleInterface *object) + + This function is called by Qt at application startup to set the + root accessible object of the application to \a object. All other + accessible objects in the application can be reached by the + client using object navigation. +*/ + +/*! + \fn void QAccessibleBridge::notifyAccessibilityUpdate(int reason, QAccessibleInterface *interface, int child) + + This function is called by Qt to notify the bridge about a change + in the accessibility information for object wrapped by the given + \a interface. + + \a reason specifies the cause of the change. It can take values + of type QAccessible::Event. + + \a child is the (1-based) index of the child element that has + changed. When \a child is 0, the object itself has changed. + + \sa QAccessible::updateAccessibility() +*/ + +/*! + \class QAccessibleBridgePlugin + \brief The QAccessibleBridgePlugin class provides an abstract + base for accessibility bridge plugins. + + \ingroup plugins + \ingroup accessibility + + Writing an accessibility bridge plugin is achieved by subclassing + this base class, reimplementing the pure virtual functions keys() + and create(), and exporting the class with the + Q_EXPORT_PLUGIN2() macro. + + \sa QAccessibleBridge, QAccessiblePlugin, {How to Create Qt Plugins} +*/ + +/*! + Constructs an accessibility bridge plugin with the given \a + parent. This is invoked automatically by the Q_EXPORT_PLUGIN2() + macro. +*/ +QAccessibleBridgePlugin::QAccessibleBridgePlugin(QObject *parent) + : QObject(parent) +{ + +} + +/*! + Destroys the accessibility bridge plugin. + + You never have to call this explicitly. Qt destroys a plugin + automatically when it is no longer used. +*/ +QAccessibleBridgePlugin::~QAccessibleBridgePlugin() +{ + +} + +/*! + \fn QStringList QAccessibleBridgePlugin::keys() const + + Returns the list of keys this plugins supports. + + These keys must be the names of the bridges that this + plugin provides. + + \sa create() +*/ + +/*! + \fn QAccessibleBridge *QAccessibleBridgePlugin::create(const QString &key) + + Creates and returns the QAccessibleBridge object corresponding to + the given \a key. Keys are case sensitive. + + \sa keys() +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/gui/accessible/qaccessiblebridge.h b/src/gui/accessible/qaccessiblebridge.h new file mode 100644 index 0000000000..d5e35df33f --- /dev/null +++ b/src/gui/accessible/qaccessiblebridge.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QACCESSIBLEBRIDGE_H +#define QACCESSIBLEBRIDGE_H + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_ACCESSIBILITY + +class QAccessibleInterface; + +class QAccessibleBridge +{ +public: + virtual ~QAccessibleBridge() {} + virtual void setRootObject(QAccessibleInterface *) = 0; + virtual void notifyAccessibilityUpdate(int, QAccessibleInterface*, int) = 0; +}; + +struct Q_GUI_EXPORT QAccessibleBridgeFactoryInterface : public QFactoryInterface +{ + virtual QAccessibleBridge *create(const QString& name) = 0; +}; + +#define QAccessibleBridgeFactoryInterface_iid "com.trolltech.Qt.QAccessibleBridgeFactoryInterface" +Q_DECLARE_INTERFACE(QAccessibleBridgeFactoryInterface, QAccessibleBridgeFactoryInterface_iid) + +class Q_GUI_EXPORT QAccessibleBridgePlugin : public QObject, public QAccessibleBridgeFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QAccessibleBridgeFactoryInterface:QFactoryInterface) +public: + explicit QAccessibleBridgePlugin(QObject *parent = 0); + ~QAccessibleBridgePlugin(); + + virtual QStringList keys() const = 0; + virtual QAccessibleBridge *create(const QString &key) = 0; +}; + +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QACCESSIBLEBRIDGE_H diff --git a/src/gui/accessible/qaccessibleobject.cpp b/src/gui/accessible/qaccessibleobject.cpp new file mode 100644 index 0000000000..1d2d1da806 --- /dev/null +++ b/src/gui/accessible/qaccessibleobject.cpp @@ -0,0 +1,410 @@ +/**************************************************************************** +** +** 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 QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessibleobject.h" + +#ifndef QT_NO_ACCESSIBILITY + +#include "qapplication.h" +#include "qwidget.h" +#include "qpointer.h" +#include "qmetaobject.h" +#include "qvarlengtharray.h" + +QT_BEGIN_NAMESPACE + +class QAccessibleObjectPrivate +{ +public: + QPointer<QObject> object; + + QList<QByteArray> actionList() const; +}; + +QList<QByteArray> QAccessibleObjectPrivate::actionList() const +{ + QList<QByteArray> actionList; + + if (!object) + return actionList; + + const QMetaObject *mo = object->metaObject(); + Q_ASSERT(mo); + + QByteArray defaultAction = QMetaObject::normalizedSignature( + mo->classInfo(mo->indexOfClassInfo("DefaultSlot")).value()); + + for (int i = 0; i < mo->methodCount(); ++i) { + const QMetaMethod member = mo->method(i); + if (member.methodType() != QMetaMethod::Slot && member.access() != QMetaMethod::Public) + continue; + + if (!qstrcmp(member.tag(), "QACCESSIBLE_SLOT")) { + if (member.signature() == defaultAction) + actionList.prepend(defaultAction); + else + actionList << member.signature(); + } + } + + return actionList; +} + +/*! + \class QAccessibleObject + \brief The QAccessibleObject class implements parts of the + QAccessibleInterface for QObjects. + + \ingroup accessibility + + This class is mainly provided for convenience. All subclasses of + the QAccessibleInterface that provide implementations of non-widget objects + should use this class as their base class. + + \sa QAccessible, QAccessibleWidget +*/ + +/*! + Creates a QAccessibleObject for \a object. +*/ +QAccessibleObject::QAccessibleObject(QObject *object) +{ + d = new QAccessibleObjectPrivate; + d->object = object; +} + +/*! + Destroys the QAccessibleObject. + + This only happens when a call to release() decrements the internal + reference counter to zero. +*/ +QAccessibleObject::~QAccessibleObject() +{ + delete d; +} + +/*! + \reimp +*/ +QObject *QAccessibleObject::object() const +{ +#ifndef QT_NO_DEBUG + if (!d->object) + qWarning("QAccessibleInterface is invalid. Crash pending..."); +#endif + return d->object; +} + +/*! + \reimp +*/ +bool QAccessibleObject::isValid() const +{ + return !d->object.isNull(); +} + +/*! \reimp */ +QRect QAccessibleObject::rect(int) const +{ + return QRect(); +} + +/*! \reimp */ +void QAccessibleObject::setText(Text, int, const QString &) +{ +} + +/*! \reimp */ +int QAccessibleObject::userActionCount(int) const +{ + return 0; +} + +/*! \reimp */ +bool QAccessibleObject::doAction(int, int, const QVariantList &) +{ + return false; +} + +static const char * const action_text[][5] = +{ + // Name, Description, Value, Help, Accelerator + { "Press", "", "", "", "Space" }, + { "SetFocus", "Passes focus to this widget", "", "", "" }, + { "Increase", "", "", "", "" }, + { "Decrease", "", "", "", "" }, + { "Accept", "", "", "", "" }, + { "Cancel", "", "", "", "" }, + { "Select", "", "", "", "" }, + { "ClearSelection", "", "", "", "" }, + { "RemoveSelection", "", "", "", "" }, + { "ExtendSelection", "", "", "", "" }, + { "AddToSelection", "", "", "", "" } +}; + +/*! \reimp */ +QString QAccessibleObject::actionText(int action, Text t, int child) const +{ + if (child || action > FirstStandardAction || action < LastStandardAction || t > Accelerator) + return QString(); |