Merge branch 'master' of git://scm.dev.nokia.troll.no/qt/qtbase-staging
* 'master' of git://scm.dev.nokia.troll.no/qt/qtbase-staging: Avoided calling updateAccessibility() from updateMicroFocus if the item was hidden Notify a11y framework of FocusChanges for QGraphicsObject Call updateAccessibility on the QGraphicsObject in updateMicroFocus Fix updateAccessibility for QGraphicsObjects Make accessibility work on Windows with alien
This commit is contained in:
commit
03fd149345
@ -47,6 +47,11 @@
|
||||
#include "qt_windows.h"
|
||||
#include "qwidget.h"
|
||||
#include "qsettings.h"
|
||||
#include <QtCore/qmap.h>
|
||||
#include <QtCore/qpair.h>
|
||||
#include <QtGui/qgraphicsitem.h>
|
||||
#include <QtGui/qgraphicsscene.h>
|
||||
#include <QtGui/qgraphicsview.h>
|
||||
|
||||
#include <winuser.h>
|
||||
#if !defined(WINABLEAPI)
|
||||
@ -148,6 +153,106 @@ static const char *roleString(QAccessible::Role role)
|
||||
return roles[int(role)];
|
||||
}
|
||||
|
||||
static const char *eventString(QAccessible::Event ev)
|
||||
{
|
||||
static const char *events[] = {
|
||||
"null", // 0
|
||||
"SoundPlayed" /*= 0x0001*/,
|
||||
"Alert" /*= 0x0002*/,
|
||||
"ForegroundChanged" /*= 0x0003*/,
|
||||
"MenuStart" /*= 0x0004*/,
|
||||
"MenuEnd" /*= 0x0005*/,
|
||||
"PopupMenuStart" /*= 0x0006*/,
|
||||
"PopupMenuEnd" /*= 0x0007*/,
|
||||
"ContextHelpStart" /*= 0x000C*/, // 8
|
||||
"ContextHelpEnd" /*= 0x000D*/,
|
||||
"DragDropStart" /*= 0x000E*/,
|
||||
"DragDropEnd" /*= 0x000F*/,
|
||||
"DialogStart" /*= 0x0010*/,
|
||||
"DialogEnd" /*= 0x0011*/,
|
||||
"ScrollingStart" /*= 0x0012*/,
|
||||
"ScrollingEnd" /*= 0x0013*/,
|
||||
"MenuCommand" /*= 0x0018*/, // 16
|
||||
|
||||
// Values from IAccessible2
|
||||
"ActionChanged" /*= 0x0101*/, // 17
|
||||
"ActiveDescendantChanged",
|
||||
"AttributeChanged",
|
||||
"DocumentContentChanged",
|
||||
"DocumentLoadComplete",
|
||||
"DocumentLoadStopped",
|
||||
"DocumentReload",
|
||||
"HyperlinkEndIndexChanged",
|
||||
"HyperlinkNumberOfAnchorsChanged",
|
||||
"HyperlinkSelectedLinkChanged",
|
||||
"HypertextLinkActivated",
|
||||
"HypertextLinkSelected",
|
||||
"HyperlinkStartIndexChanged",
|
||||
"HypertextChanged",
|
||||
"HypertextNLinksChanged",
|
||||
"ObjectAttributeChanged",
|
||||
"PageChanged",
|
||||
"SectionChanged",
|
||||
"TableCaptionChanged",
|
||||
"TableColumnDescriptionChanged",
|
||||
"TableColumnHeaderChanged",
|
||||
"TableModelChanged",
|
||||
"TableRowDescriptionChanged",
|
||||
"TableRowHeaderChanged",
|
||||
"TableSummaryChanged",
|
||||
"TextAttributeChanged",
|
||||
"TextCaretMoved",
|
||||
// TextChanged, deprecated, use TextUpdated
|
||||
//TextColumnChanged = TextCaretMoved + 2,
|
||||
"TextInserted",
|
||||
"TextRemoved",
|
||||
"TextUpdated",
|
||||
"TextSelectionChanged",
|
||||
"VisibleDataChanged", /*= 0x0101+32*/
|
||||
"ObjectCreated" /*= 0x8000*/, // 49
|
||||
"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*/
|
||||
};
|
||||
int e = int(ev);
|
||||
if (e <= 0x80c0) {
|
||||
const int last = sizeof(events)/sizeof(char*) - 1;
|
||||
|
||||
if (e <= 0x07)
|
||||
return events[e];
|
||||
else if (e <= 0x13)
|
||||
return events[e - 0x0c + 8];
|
||||
else if (e == 0x18)
|
||||
return events[16];
|
||||
else if (e <= 0x0101 + 32)
|
||||
return events[e - 0x101 + 17];
|
||||
else if (e <= 0x800f)
|
||||
return events[e - 0x8000 + 49];
|
||||
else if (e == 0x80a0)
|
||||
return events[last - 2];
|
||||
else if (e == 0x80b0)
|
||||
return events[last - 1];
|
||||
else if (e == 0x80c0)
|
||||
return events[last];
|
||||
}
|
||||
return "unknown";
|
||||
};
|
||||
|
||||
void showDebug(const char* funcName, const QAccessibleInterface *iface)
|
||||
{
|
||||
qDebug() << "Role:" << roleString(iface->role(0))
|
||||
@ -159,6 +264,12 @@ void showDebug(const char* funcName, const QAccessibleInterface *iface)
|
||||
# define showDebug(f, iface)
|
||||
#endif
|
||||
|
||||
// This stuff is used for widgets/items with no window handle:
|
||||
typedef QMap<int, QPair<QObject*,int> > NotifyMap;
|
||||
Q_GLOBAL_STATIC(NotifyMap, qAccessibleRecentSentEvents)
|
||||
static int eventNum = 0;
|
||||
|
||||
|
||||
void QAccessible::initialize()
|
||||
{
|
||||
|
||||
@ -251,18 +362,35 @@ void QAccessible::updateAccessibility(QObject *o, int who, Event reason)
|
||||
// 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;
|
||||
QObject *p = o;
|
||||
do {
|
||||
if (p->isWidgetType()) {
|
||||
w = static_cast<QWidget*>(p);
|
||||
if (w->internalWinId())
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (QGraphicsObject *gfxObj = qobject_cast<QGraphicsObject*>(p)) {
|
||||
QGraphicsItem *parentItem = gfxObj->parentItem();
|
||||
if (parentItem) {
|
||||
p = parentItem->toGraphicsObject();
|
||||
} else {
|
||||
QGraphicsView *view = 0;
|
||||
if (QGraphicsScene *scene = gfxObj->scene()) {
|
||||
QWidget *fw = QApplication::focusWidget();
|
||||
const QList<QGraphicsView*> views = scene->views();
|
||||
for (int i = 0 ; i < views.count() && view != fw; ++i) {
|
||||
view = views.at(i);
|
||||
}
|
||||
}
|
||||
p = view;
|
||||
}
|
||||
} else {
|
||||
p = p->parent();
|
||||
}
|
||||
|
||||
} while (p);
|
||||
|
||||
//qDebug() << "updateAccessibility(), hwnd:" << w << ", object:" << o << "," << eventString(reason);
|
||||
if (!w) {
|
||||
if (reason != QAccessible::ContextHelpStart &&
|
||||
reason != QAccessible::ContextHelpEnd)
|
||||
@ -282,12 +410,81 @@ void QAccessible::updateAccessibility(QObject *o, int who, Event reason)
|
||||
}
|
||||
}
|
||||
|
||||
WId wid = w->internalWinId();
|
||||
if (reason != MenuCommand) { // MenuCommand is faked
|
||||
ptrNotifyWinEvent(reason, w->winId(), OBJID_CLIENT, who);
|
||||
if (w != o) {
|
||||
// See comment "SENDING EVENTS TO OBJECTS WITH NO WINDOW HANDLE"
|
||||
eventNum %= 50; //[0..49]
|
||||
int eventId = - eventNum - 1;
|
||||
|
||||
qAccessibleRecentSentEvents()->insert(eventId, qMakePair(o,who));
|
||||
ptrNotifyWinEvent(reason, wid, OBJID_CLIENT, eventId );
|
||||
|
||||
++eventNum;
|
||||
} else {
|
||||
ptrNotifyWinEvent(reason, wid, OBJID_CLIENT, who);
|
||||
}
|
||||
}
|
||||
#endif // Q_WS_WINCE
|
||||
}
|
||||
|
||||
/* == SENDING EVENTS TO OBJECTS WITH NO WINDOW HANDLE ==
|
||||
|
||||
If the user requested to send the event to a widget with no window,
|
||||
we need to send an event to an object with no hwnd.
|
||||
The way we do that is to send it to the *first* ancestor widget
|
||||
with a window.
|
||||
Then we'll need a way of identifying the child:
|
||||
We'll just keep a list of the most recent events that we have sent,
|
||||
where each entry in the list is identified by a negative value
|
||||
between [-50,-1]. This negative value we will pass on to
|
||||
NotifyWinEvent() as the child id. When the negative value have
|
||||
reached -50, it will wrap around to -1. This seems to be enough
|
||||
|
||||
Now, when the client receives that event, he will first call
|
||||
AccessibleObjectFromEvent() where dwChildID is the special
|
||||
negative value. AccessibleObjectFromEvent does two steps:
|
||||
1. It will first sent a WM_GETOBJECT to the server, asking
|
||||
for the IAccessible interface for the HWND.
|
||||
2. With the IAccessible interface it got hold of it will call
|
||||
acc_getChild where the child id argument is the special
|
||||
negative identifier. In our reimplementation of get_accChild
|
||||
we check for this if the child id is negative. If it is, then
|
||||
we'll look up in our table for the entry that is associated
|
||||
with that value.
|
||||
The entry will then contain a pointer to the QObject /QWidget
|
||||
that we can use to call queryAccessibleInterface() on.
|
||||
|
||||
|
||||
The following figure shows how the interaction between server and
|
||||
client is in the case when the server is sending an event.
|
||||
|
||||
SERVER (Qt) | CLIENT |
|
||||
--------------------------------------------+---------------------------------------+
|
||||
|
|
||||
acc->updateAccessibility(obj, childIndex) |
|
||||
|
|
||||
recentEvents()->insert(- 1 - eventNum, |
|
||||
qMakePair(obj, childIndex) |
|
||||
NotifyWinEvent(hwnd, childId) => |
|
||||
| AccessibleObjectFromEvent(event, hwnd, OBJID_CLIENT, childId )
|
||||
| will do:
|
||||
<=== 1. send WM_GETOBJECT(hwnd, OBJID_CLIENT)
|
||||
widget ~= hwnd
|
||||
iface = queryAccessibleInteface(widget)
|
||||
(create IAccessible interface wrapper for
|
||||
iface)
|
||||
return iface ===> IAccessible* iface; (for hwnd)
|
||||
|
|
||||
<=== call iface->get_accChild(childId)
|
||||
get_accChild() { |
|
||||
if (varChildID.lVal < 0) {
|
||||
QPair ref = recentEvents().value(varChildID.lVal);
|
||||
[...]
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
void QAccessible::setRootObject(QObject *o)
|
||||
{
|
||||
if (rootObjectHandler) {
|
||||
@ -418,15 +615,18 @@ public:
|
||||
delete accessible;
|
||||
}
|
||||
|
||||
/* IUnknown */
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *);
|
||||
ULONG STDMETHODCALLTYPE AddRef();
|
||||
ULONG STDMETHODCALLTYPE Release();
|
||||
|
||||
/* IDispatch */
|
||||
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 *);
|
||||
|
||||
/* IAccessible */
|
||||
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);
|
||||
@ -451,6 +651,7 @@ public:
|
||||
HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT *pvarID);
|
||||
HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT *pvarChildren);
|
||||
|
||||
/* IOleWindow */
|
||||
HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd);
|
||||
HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);
|
||||
|
||||
@ -896,9 +1097,30 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, I
|
||||
if (varChildID.vt == VT_EMPTY)
|
||||
return E_INVALIDARG;
|
||||
|
||||
|
||||
int childIndex = varChildID.lVal;
|
||||
QAccessibleInterface *acc = 0;
|
||||
RelationFlag rel = varChildID.lVal ? Child : Self;
|
||||
accessible->navigate(rel, varChildID.lVal, &acc);
|
||||
|
||||
if (childIndex < 0) {
|
||||
const int entry = childIndex;
|
||||
QPair<QObject*, int> ref = qAccessibleRecentSentEvents()->value(entry);
|
||||
if (ref.first) {
|
||||
acc = queryAccessibleInterface(ref.first);
|
||||
if (acc && ref.second) {
|
||||
if (ref.second) {
|
||||
QAccessibleInterface *res;
|
||||
int index = acc->navigate(Child, ref.second, &res);
|
||||
delete acc;
|
||||
if (index == -1)
|
||||
return E_INVALIDARG;
|
||||
acc = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RelationFlag rel = childIndex ? Child : Self;
|
||||
accessible->navigate(rel, childIndex, &acc);
|
||||
}
|
||||
|
||||
if (acc) {
|
||||
QWindowsAccessible* wacc = new QWindowsAccessible(acc);
|
||||
@ -1203,7 +1425,7 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetWindow(HWND *phwnd)
|
||||
if (!o || !o->isWidgetType())
|
||||
return E_FAIL;
|
||||
|
||||
*phwnd = static_cast<QWidget*>(o)->winId();
|
||||
*phwnd = static_cast<QWidget*>(o)->effectiveWinId();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@ -7395,15 +7395,19 @@ void QGraphicsItem::updateMicroFocus()
|
||||
if (QWidget *fw = QApplication::focusWidget()) {
|
||||
if (scene()) {
|
||||
for (int i = 0 ; i < scene()->views().count() ; ++i) {
|
||||
if (scene()->views().at(i) == fw)
|
||||
if (QInputContext *inputContext = fw->inputContext())
|
||||
if (scene()->views().at(i) == fw) {
|
||||
if (QInputContext *inputContext = fw->inputContext()) {
|
||||
inputContext->update();
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
// ##### is this correct
|
||||
if (toGraphicsObject())
|
||||
QAccessible::updateAccessibility(toGraphicsObject(), 0, QAccessible::StateChanged);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
// ##### is this correct
|
||||
QAccessible::updateAccessibility(fw, 0, QAccessible::StateChanged);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -245,6 +245,10 @@
|
||||
#include <QtGui/qtransform.h>
|
||||
#include <QtGui/qinputcontext.h>
|
||||
#include <QtGui/qgraphicseffect.h>
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
# include <QtGui/qaccessible.h>
|
||||
#endif
|
||||
|
||||
#include <private/qapplication_p.h>
|
||||
#include <private/qobject_p.h>
|
||||
#ifdef Q_WS_X11
|
||||
@ -837,6 +841,14 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item,
|
||||
if (item)
|
||||
focusItem = item;
|
||||
updateInputMethodSensitivityInViews();
|
||||
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
if (focusItem) {
|
||||
if (QGraphicsObject *focusObj = focusItem->toGraphicsObject()) {
|
||||
QAccessible::updateAccessibility(focusObj, 0, QAccessible::Focus);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (item) {
|
||||
QFocusEvent event(QEvent::FocusIn, focusReason);
|
||||
sendEvent(item, &event);
|
||||
|
@ -11342,8 +11342,10 @@ void QWidget::updateMicroFocus()
|
||||
}
|
||||
#endif
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
// ##### is this correct
|
||||
QAccessible::updateAccessibility(this, 0, QAccessible::StateChanged);
|
||||
if (isVisible()) {
|
||||
// ##### is this correct
|
||||
QAccessible::updateAccessibility(this, 0, QAccessible::StateChanged);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user