Merge remote-tracking branch 'staging/master'

This commit is contained in:
Olivier Goffart 2011-05-10 13:15:10 +02:00
commit 48b7c870ec
5 changed files with 263 additions and 23 deletions

View File

@ -47,6 +47,11 @@
#include "qt_windows.h" #include "qt_windows.h"
#include "qwidget.h" #include "qwidget.h"
#include "qsettings.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> #include <winuser.h>
#if !defined(WINABLEAPI) #if !defined(WINABLEAPI)
@ -148,6 +153,106 @@ static const char *roleString(QAccessible::Role role)
return roles[int(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) void showDebug(const char* funcName, const QAccessibleInterface *iface)
{ {
qDebug() << "Role:" << roleString(iface->role(0)) qDebug() << "Role:" << roleString(iface->role(0))
@ -159,6 +264,12 @@ void showDebug(const char* funcName, const QAccessibleInterface *iface)
# define showDebug(f, iface) # define showDebug(f, iface)
#endif #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() 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, // An event has to be associated with a window,
// so find the first parent that is a widget. // so find the first parent that is a widget.
QWidget *w = 0; QWidget *w = 0;
if (o->isWidgetType()) { QObject *p = o;
w = (QWidget*)o; do {
} else { if (p->isWidgetType()) {
QObject *p = o; w = static_cast<QWidget*>(p);
while ((p = p->parent()) != 0) { if (w->internalWinId())
if (p->isWidgetType()) {
w = (QWidget*)p;
break; 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 (!w) {
if (reason != QAccessible::ContextHelpStart && if (reason != QAccessible::ContextHelpStart &&
reason != QAccessible::ContextHelpEnd) 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 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 #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) void QAccessible::setRootObject(QObject *o)
{ {
if (rootObjectHandler) { if (rootObjectHandler) {
@ -418,15 +615,18 @@ public:
delete accessible; delete accessible;
} }
/* IUnknown */
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *);
ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release(); ULONG STDMETHODCALLTYPE Release();
/* IDispatch */
HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int *); HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int *);
HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int, unsigned long, ITypeInfo **); HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int, unsigned long, ITypeInfo **);
HRESULT STDMETHODCALLTYPE GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *); 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 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 accHitTest(long xLeft, long yTop, VARIANT *pvarID);
HRESULT STDMETHODCALLTYPE accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID); HRESULT STDMETHODCALLTYPE accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID);
HRESULT STDMETHODCALLTYPE accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd); HRESULT STDMETHODCALLTYPE accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd);
@ -451,6 +651,7 @@ public:
HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT *pvarID); HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT *pvarID);
HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT *pvarChildren); HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT *pvarChildren);
/* IOleWindow */
HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd); HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd);
HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode); HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);
@ -896,9 +1097,30 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, I
if (varChildID.vt == VT_EMPTY) if (varChildID.vt == VT_EMPTY)
return E_INVALIDARG; return E_INVALIDARG;
int childIndex = varChildID.lVal;
QAccessibleInterface *acc = 0; 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) { if (acc) {
QWindowsAccessible* wacc = new QWindowsAccessible(acc); QWindowsAccessible* wacc = new QWindowsAccessible(acc);
@ -1203,7 +1425,7 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetWindow(HWND *phwnd)
if (!o || !o->isWidgetType()) if (!o || !o->isWidgetType())
return E_FAIL; return E_FAIL;
*phwnd = static_cast<QWidget*>(o)->winId(); *phwnd = static_cast<QWidget*>(o)->effectiveWinId();
return S_OK; return S_OK;
} }

View File

@ -7395,15 +7395,19 @@ void QGraphicsItem::updateMicroFocus()
if (QWidget *fw = QApplication::focusWidget()) { if (QWidget *fw = QApplication::focusWidget()) {
if (scene()) { if (scene()) {
for (int i = 0 ; i < scene()->views().count() ; ++i) { for (int i = 0 ; i < scene()->views().count() ; ++i) {
if (scene()->views().at(i) == fw) if (scene()->views().at(i) == fw) {
if (QInputContext *inputContext = fw->inputContext()) if (QInputContext *inputContext = fw->inputContext()) {
inputContext->update(); 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 #endif
} }

View File

@ -245,6 +245,10 @@
#include <QtGui/qtransform.h> #include <QtGui/qtransform.h>
#include <QtGui/qinputcontext.h> #include <QtGui/qinputcontext.h>
#include <QtGui/qgraphicseffect.h> #include <QtGui/qgraphicseffect.h>
#ifndef QT_NO_ACCESSIBILITY
# include <QtGui/qaccessible.h>
#endif
#include <private/qapplication_p.h> #include <private/qapplication_p.h>
#include <private/qobject_p.h> #include <private/qobject_p.h>
#ifdef Q_WS_X11 #ifdef Q_WS_X11
@ -837,6 +841,14 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item,
if (item) if (item)
focusItem = item; focusItem = item;
updateInputMethodSensitivityInViews(); updateInputMethodSensitivityInViews();
#ifndef QT_NO_ACCESSIBILITY
if (focusItem) {
if (QGraphicsObject *focusObj = focusItem->toGraphicsObject()) {
QAccessible::updateAccessibility(focusObj, 0, QAccessible::Focus);
}
}
#endif
if (item) { if (item) {
QFocusEvent event(QEvent::FocusIn, focusReason); QFocusEvent event(QEvent::FocusIn, focusReason);
sendEvent(item, &event); sendEvent(item, &event);

View File

@ -11344,8 +11344,10 @@ void QWidget::updateMicroFocus()
} }
#endif #endif
#ifndef QT_NO_ACCESSIBILITY #ifndef QT_NO_ACCESSIBILITY
// ##### is this correct if (isVisible()) {
QAccessible::updateAccessibility(this, 0, QAccessible::StateChanged); // ##### is this correct
QAccessible::updateAccessibility(this, 0, QAccessible::StateChanged);
}
#endif #endif
} }

View File

@ -593,11 +593,11 @@ QRawFont QRawFont::fromFont(const QFont &font, QFontDatabase::WritingSystem writ
// if none of them match, just pick the first one // if none of them match, just pick the first one
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
QGlyphRun glyphs = list.at(i); QGlyphRun glyphs = list.at(i);
QRawFont rawfont = glyphs.font(); QRawFont rawfont = glyphs.rawFont();
if (rawfont.familyName() == font.family()) if (rawfont.familyName() == font.family())
return rawfont; return rawfont;
} }
return list.at(0).font(); return list.at(0).rawFont();
} }
return QRawFont(); return QRawFont();
#else #else