[Kde-accessibility] understanding the whole picture

Volker Hilsheimer vohi@trolltech.com
Thu, 28 Nov 2002 14:43:00 +0100


This is a multi-part message in MIME format.

------=_NextPart_000_0192_01C296EC.72F8C730
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: 7bit

> > So, we should put that code somewhere (still don't know where) but
my main
> > question is that we'll need a AtkObject *QWidget::getAccessible()
function
> > (or AtkObject *QObject::getAccessible() function), right ?
>
> If you look closely at the AtkImplementor interface you will see that
> any GObject type/instance can implement accessibility; in such cases
you
> will need to create appropriate AtkObjectFactories for the type.
>
> > That stats that QWidget or QObject should understand about AtkObject
(as
> > GtkWidget does), right ? well, that's not going to happen :\
>
> No, this is not a necessary conclusion.  What you need to do is
subclass
> AtkObject (create a KatObject or something that derives from
AtkObject),
> and then provide new implementations of all the virtual methods that
> need to be changed for KDE, for instance those that assume that the
> backing objects are GObjects).  Then you need to provide
implementations
> for some of the basic ATK utility methods (for instance as is done in
> gailutil.c for GAIL-based accessibility) for getting the "root
> accessible" of an application/process.  Your widgets don't need to
know
> anything about this, but they need to provide enough information for
you
> to implement all the appropriate Atk methods on their behalf.
>
> Note that ATK has a number of queryable interfaces and UI
> objects/instances should implement only those interfaces that make
sense
> for them; The ATK docs contain definitions of what the various
> interfaces are intended to do.
>
> > Should we then, get a QAccessibleObject object and make katk 'cast'
it to an
> > AtkObject ?
>
> No, that's not likely to work (at least not with anything like a true
> 'cast').  And it's not necessary for you to have a special
> QAccessibleObject type at all.
>
> best regards,
>
>
> -Bill
>

QAccessibleInterface and friends are already a wrapper around
QObject/QWidget, and neither QObject nor QWidget know anything about it.
ONLY the factory inside QAccessible::queryAccessibleObject() knows about
which factory can create a QAccessibleInterface implementation for which
class. The QAccessibleInterface implementation inside the plugin of
course knows everything about the QObject/QWidget.

Another class, QWindowsAccessible, implements the MSAA interface
IAccessible (which makes it a COM object), and has a
QAccessibleInterface as a member. The COM RPC server then calls the
IAccessible API that QWindowsAccessible implements, and
QWindowsAccessible delegates the calls to it's QAccessibleInterface
member. Since the QAccessibleInterface had been designed with
IAccessible in mind it's fairly trivial to do that. I've attached the
qaccessible_win.cpp file so you can get an idea about what's going on.
It's not documented, but should be easy enough to understand. The global
function qt_createWindowsAccessible is the one that QApplication calls
when it receives a WM_GETOBJECT message (for completeness, here's the
respective part of the window procedure:

     case WM_GETOBJECT:
  {
      // Ignoring all requests while starting up
      if ( qApp->startingUp() || !qApp->loopLevel() || lParam !=
OBJID_CLIENT ) {
   result = FALSE;
   break;
      }

      typedef LRESULT (WINAPI *PtrLresultFromObject)(REFIID, WPARAM,
LPUNKNOWN );
      static PtrLresultFromObject ptrLresultFromObject = 0;
      static bool oleaccChecked = FALSE;

      if ( !oleaccChecked ) {
   oleaccChecked = TRUE;
   ptrLresultFromObject = (PtrLresultFromObject)QLibrary::resolve(
"oleacc.dll", "LresultFromObject" );
      }
      if ( ptrLresultFromObject ) {
   QAccessibleInterface *acc = 0;
   QAccessible::queryAccessibleInterface( widget, &acc );
   if ( !acc ) {
       result = FALSE;
       break;
   }

   QCustomEvent e( QEvent::Accessibility, acc );
   QApplication::sendEvent( widget, &e );

   // and get an instance of the IAccessibile implementation
   IAccessible *iface = qt_createWindowsAccessible( acc );
   acc->release();
   LRESULT res = ptrLresultFromObject( IID_IAccessible, wParam, iface );
// ref == 2
   iface->Release(); // the client will release the object again, and
then it will destroy itself

   if ( res > 0 )
       RETURN(res);
      }
  }
  result = FALSE;
  break;


LresultFromObject is some magic function that associates a unsigned long
with a IAccessible* or something, so that the ulong return value of the
SendMessage() call is usable for the accessibility client, or at least
that's what I guess).

A UNIX counterpart for QWindowsAccessible, and of above message handler
is what is missing.

The central questions for us (Trolltech) are

- what is missing from our interfaces?
All we implement right now with our accessibility objects is the
QAccessibleInterface class - obviously, we need more interfaces to
provide a reasonable mapping between our objects and ATK or AT-SPI.
Which interfaces are those? Since we have no know how about the
requirements we will have to have a thorough look at ATK, and work with
the feedback we receive from here.

- how does the accessibility infrastructure communicate with the
"server" application?
How to implement the RPC to get and call the QAccessibleInterface
implementations? How to implement the notification? DCOP seems like the
natural way to go here, or at least like a smallest common denominator
that can easily be bridged to other RPC technologies.

- How to bridge QAccessibleInterface (and similar interfaces extending
the API, e.g. QAccessibleRelationInterface or whatnot) to "something"
the RPC infrastructure can use?

Once we know all that we can probably provide an implementation of
qaccessible_unix.cpp. For KDE, it is probably the easiest to work
directly on QAccessibleInterface (or a thin wrapper around it that DCOP
can work on), but that won't handle other platforms without extra work
(but then it will be hardly possible for Qt applications to be
accessible through all infrastructures on all platforms).

--
Volker


------=_NextPart_000_0192_01C296EC.72F8C730
Content-Type: application/octet-stream;
	name="qaccessible_win.cpp"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="qaccessible_win.cpp"

/************************************************************************=
****
** $Id$
**
** Implementation of QAccessible class for Win32
**
** Copyright (C) 2000-2001 Trolltech AS.  All rights reserved.
**
** This file is part of the kernel module of the Qt GUI Toolkit.
**
** Licensees holding valid Qt Enterprise Edition or Qt Professional =
Edition
** licenses for Windows may use this file in accordance with the Qt =
Commercial
** License Agreement provided with the Software.
**
** This file is not available for use under any other license without
** express written permission from the copyright holder.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING =
THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR =
PURPOSE.
**
** See http://www.trolltech.com/pricing.html or email =
sales@trolltech.com for
**   information about Qt Commercial License Agreements.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

#include "qaccessible.h"

#if defined(QT_ACCESSIBILITY_SUPPORT)

#include "qapplication.h"
#include "qt_windows.h"
#include "qwidget.h"
#include "qobjectlist.h"
#include "qmessagebox.h" // ### dependency
#include "qlibrary.h"

#include <winable.h>
#include <oleacc.h>
#ifndef Q_CC_BOR
#include <comdef.h>
#endif

void QAccessible::updateAccessibility( QObject *o, int who, Event reason =
)
{
    Q_ASSERT(o);

    QString soundName;
    switch ( reason ) {
    case PopupMenuStart:
	soundName =3D "MenuPopup";
	break;

    case MenuCommand:
	soundName =3D "MenuCommand";
	break;

    case Alert:
	if ( o->inherits( "QMessageBox" ) ) {
	    QMessageBox *mb =3D (QMessageBox*)o;
	    switch ( mb->icon() ) {
	    case QMessageBox::Warning:
		soundName =3D "SystemExclamation";
		break;
	    case QMessageBox::Critical:
		soundName =3D "SystemHand";
		break;
	    case QMessageBox::Information:
		soundName =3D "SystemAsterisk";
		break;
	    default:
		break;
	    }
	} else {
	    soundName =3D "SystemAsterisk";
	}
	break;

    default:
	break;
    }

    if ( !!soundName ) {
#if defined(UNICODE)
	if ( qWinVersion() & Qt::WV_NT_based )
	    PlaySoundW( (TCHAR*)qt_winTchar( soundName, TRUE ), NULL, SND_ALIAS =
| SND_ASYNC | SND_NODEFAULT | SND_NOWAIT );
	else
#endif
	    PlaySoundA( soundName.local8Bit(), NULL, SND_ALIAS | SND_ASYNC | =
SND_NODEFAULT | SND_NOWAIT  );
    }

    typedef void (WINAPI *PtrNotifyWinEvent)(DWORD, HWND, LONG, LONG );
   =20
    static PtrNotifyWinEvent ptrNotifyWinEvent =3D 0;
    static bool resolvedNWE =3D FALSE;
    if ( !resolvedNWE ) {
	resolvedNWE =3D TRUE;
	ptrNotifyWinEvent =3D (PtrNotifyWinEvent)QLibrary::resolve( "user32", =
"NotifyWinEvent" );
    }
    if ( !ptrNotifyWinEvent )
	return;

    // An event has to be associated with a window,=20
    // so find the first parent that is a widget.
    QWidget *w =3D 0;
    if ( o->isWidgetType() ) {
	w =3D (QWidget*)o;
    } else {
	QObject *p =3D o;
	while ( ( p =3D p->parent() ) !=3D 0 ) {
	    if ( p->isWidgetType() ) {
		w =3D (QWidget*)p;
		break;
	    }
	}
    }

    if ( !w ) {
	if ( reason !=3D QAccessible::ContextHelpStart &&=20
	     reason !=3D QAccessible::ContextHelpEnd )
	    w =3D qApp->focusWidget();
	if ( !w ) {
	    w =3D qApp->activeWindow();
	    if ( !w ) {
		w =3D qApp->mainWidget();
		if ( !w )
		    return;
	    }
	}
    }

    if ( reason !=3D MenuCommand ) // MenuCommand is faked
	ptrNotifyWinEvent( reason, w->winId(), OBJID_CLIENT, who );
}

class QWindowsEnumerate : public IEnumVARIANT
{
public:
    QWindowsEnumerate( const QMemArray<int> &a )
	: ref( 0 ), current( 0 ),array( a )
    {
    }

    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;
    QMemArray<int> array;
};

HRESULT STDMETHODCALLTYPE QWindowsEnumerate::QueryInterface( REFIID id, =
LPVOID *iface )
{
    *iface =3D 0;
    if ( id =3D=3D IID_IUnknown )
	*iface =3D (IUnknown*)this;
    else if ( id =3D=3D IID_IEnumVARIANT )
	*iface =3D (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 =3D NULL;
    *ppEnum =3D NULL;

    penum =3D new QWindowsEnumerate( array );
    if ( !penum )
	return E_OUTOFMEMORY;
    penum->current =3D current;
    penum->array =3D array;
    penum->AddRef();
    *ppEnum =3D penum;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Next( unsigned long  celt, =
VARIANT FAR*  rgVar, unsigned long FAR*  pCeltFetched )
{
    if ( pCeltFetched !=3D NULL )
	*pCeltFetched =3D 0;

    ULONG l;
    for ( l =3D 0; l < celt; l++ ) {
	VariantInit( &rgVar[l] );
	if ( (current+1) > array.size() ) {
	    *pCeltFetched =3D l;
	    return S_FALSE;
	}

	rgVar[l].vt =3D VT_I4;
	rgVar[l].lVal =3D array[(int)current];
	++current;
    }
    *pCeltFetched =3D l;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Reset()
{
    current =3D 0;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Skip( unsigned long celt )
{
    current +=3D celt;
    if ( current > array.size() ) {
	current =3D array.size();
	return S_FALSE;
    }
    return S_OK;
}

/*
*/
class QWindowsAccessible : public IAccessible, QAccessible
{
public:
    QWindowsAccessible( QAccessibleInterface *a )
	: ref( 0 ), accessible( a )
    {
	accessible->addRef();
    }

    ~QWindowsAccessible()
    {
	accessible->release();
    }

    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 );

private:
    ULONG ref;
    QAccessibleInterface *accessible;
};

static inline BSTR QStringToBSTR( const QString &str )
{
    BSTR bstrVal;

    int wlen =3D str.length();
    bstrVal =3D SysAllocStringByteLen( 0, wlen*2 );
    memcpy( bstrVal, str.unicode(), sizeof(QChar)*(wlen) );
    bstrVal[wlen] =3D 0;

    return bstrVal;
}

/*
*/
IAccessible *qt_createWindowsAccessible( QAccessibleInterface *access )
{
    QWindowsAccessible *acc =3D new QWindowsAccessible( access );
    IAccessible *iface;
    acc->QueryInterface( IID_IAccessible, (void**)&iface );

    return iface;
}

/*
  IUnknown
*/
HRESULT STDMETHODCALLTYPE QWindowsAccessible::QueryInterface( REFIID id, =
LPVOID *iface )
{
    *iface =3D 0;
    if ( id =3D=3D IID_IUnknown )
	*iface =3D (IUnknown*)this;
    else if ( id =3D=3D IID_IDispatch )
	*iface =3D (IDispatch*)this;
    else if ( id =3D=3D IID_IAccessible )
	*iface =3D (IAccessible*)this;

    if ( *iface ) {
	AddRef();
	return S_OK;
    }

    return E_NOINTERFACE;
}

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 =3D 0;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetTypeInfo( unsigned int, =
unsigned long, ITypeInfo **pptinfo )
{
    // We don't use a type library
    *pptinfo =3D NULL;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetIDsOfNames( const _GUID =
&, wchar_t **rgszNames, unsigned int, unsigned long, long *rgdispid )
{
#ifndef Q_CC_BOR
    // PROPERTIES:  Hierarchical
    if ( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accParent") )=20
	rgdispid[0] =3D DISPID_ACC_PARENT;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accChildCount") )=20
	rgdispid[0] =3D DISPID_ACC_CHILDCOUNT;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accChild") )=20
	rgdispid[0] =3D DISPID_ACC_CHILD;

    // PROPERTIES:  Descriptional
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accName(") )=20
	rgdispid[0] =3D DISPID_ACC_NAME;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accValue") )=20
	rgdispid[0] =3D DISPID_ACC_VALUE;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accDescription") )=20
	rgdispid[0] =3D DISPID_ACC_DESCRIPTION;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accRole") )=20
	rgdispid[0] =3D DISPID_ACC_ROLE;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accState") )=20
	rgdispid[0] =3D DISPID_ACC_STATE;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accHelp") )=20
	rgdispid[0] =3D DISPID_ACC_HELP;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accHelpTopic") )=20
	rgdispid[0] =3D DISPID_ACC_HELPTOPIC;
    else if( _bstr_t(rgszNames[0]) =3D=3D =
_bstr_t(L"accKeyboardShortcut") )=20
	rgdispid[0] =3D DISPID_ACC_KEYBOARDSHORTCUT;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accFocus") )=20
	rgdispid[0] =3D DISPID_ACC_FOCUS;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accSelection") )=20
	rgdispid[0] =3D DISPID_ACC_SELECTION;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accDefaultAction") ) =

	rgdispid[0] =3D DISPID_ACC_DEFAULTACTION;

    // METHODS
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accSelect") )=20
	rgdispid[0] =3D DISPID_ACC_SELECT;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accLocation") )=20
	rgdispid[0] =3D DISPID_ACC_LOCATION;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accNavigate") )=20
	rgdispid[0] =3D DISPID_ACC_NAVIGATE;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accHitTest") )=20
	rgdispid[0] =3D DISPID_ACC_HITTEST;
    else if( _bstr_t(rgszNames[0]) =3D=3D _bstr_t(L"accDoDefaultAction") =
)=20
	rgdispid[0] =3D DISPID_ACC_DODEFAULTACTION;
    else=20
	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 * )
{=20
    HRESULT hr =3D DISP_E_MEMBERNOTFOUND;

    switch( dispIdMember )
    {
	case DISPID_ACC_PARENT:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET ) {
		if ( pVarResult =3D=3D NULL )
		    return E_INVALIDARG;
		hr =3D get_accParent( &pVarResult->pdispVal );
	    } else {
		hr =3D DISP_E_MEMBERNOTFOUND;
	    }
	    break;

	case DISPID_ACC_CHILDCOUNT:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET ) {
		if ( pVarResult =3D=3D NULL )
		    return E_INVALIDARG;
		hr =3D get_accChildCount( &pVarResult->lVal );
	    } else {
		hr =3D DISP_E_MEMBERNOTFOUND;
	    }
	    break;
=09
	case DISPID_ACC_CHILD:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accChild( pDispParams->rgvarg[0], &pVarResult->pdispVal );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_NAME:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accName( pDispParams->rgvarg[0], &pVarResult->bstrVal );
    	    else if ( wFlags =3D=3D DISPATCH_PROPERTYPUT )
		hr =3D put_accName( pDispParams->rgvarg[0], pVarResult->bstrVal );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_VALUE:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accValue( pDispParams->rgvarg[0], &pVarResult->bstrVal );
	    else if ( wFlags =3D=3D DISPATCH_PROPERTYPUT )
		hr =3D put_accValue( pDispParams->rgvarg[0], pVarResult->bstrVal );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_DESCRIPTION:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accDescription( pDispParams->rgvarg[0], =
&pVarResult->bstrVal );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_ROLE:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accRole( pDispParams->rgvarg[0], pVarResult );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_STATE:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accState( pDispParams->rgvarg[0], pVarResult );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_HELP:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accHelp( pDispParams->rgvarg[0], &pVarResult->bstrVal );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_HELPTOPIC:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accHelpTopic( &pDispParams->rgvarg[2].bstrVal, =
pDispParams->rgvarg[1], &pDispParams->rgvarg[0].lVal );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_KEYBOARDSHORTCUT:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accKeyboardShortcut( pDispParams->rgvarg[0], =
&pVarResult->bstrVal );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_FOCUS:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accFocus( pVarResult );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_SELECTION:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accSelection( pVarResult );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_DEFAULTACTION:
	    if ( wFlags =3D=3D DISPATCH_PROPERTYGET )
		hr =3D get_accDefaultAction( pDispParams->rgvarg[0], =
&pVarResult->bstrVal );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_SELECT:
	    if ( wFlags =3D=3D DISPATCH_METHOD )
		hr =3D accSelect( pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0] =
);
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_LOCATION:
	    if ( wFlags =3D=3D DISPATCH_METHOD )
		hr =3D accLocation( &pDispParams->rgvarg[4].lVal, =
&pDispParams->rgvarg[3].lVal, &pDispParams->rgvarg[2].lVal, =
&pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0] );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_NAVIGATE:
	    if ( wFlags =3D=3D DISPATCH_METHOD )
		hr =3D accNavigate( pDispParams->rgvarg[1].lVal, =
pDispParams->rgvarg[0], pVarResult );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_HITTEST:
	    if ( wFlags =3D=3D DISPATCH_METHOD )
		hr =3D accHitTest( pDispParams->rgvarg[1].lVal, =
pDispParams->rgvarg[0].lVal, pVarResult );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	case DISPID_ACC_DODEFAULTACTION:
	    if ( wFlags =3D=3D DISPATCH_METHOD )
		hr =3D accDoDefaultAction( pDispParams->rgvarg[0] );
	    else
		hr =3D DISP_E_MEMBERNOTFOUND;
	    break;

	default:
	    hr =3D DISP_E_MEMBERNOTFOUND;
	    break;
    }=20
   =20
    return hr;
}

/*=20
  IAccessible
*/
HRESULT STDMETHODCALLTYPE QWindowsAccessible::accHitTest( long xLeft, =
long yTop, VARIANT *pvarID )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    int control =3D accessible->controlAt( xLeft, yTop );
    if ( control =3D=3D -1 ) {
	(*pvarID).vt =3D VT_EMPTY;
	return S_FALSE;
    }
    QAccessibleInterface *acc =3D 0;
    if ( control )
	accessible->queryChild( control, &acc );
    if ( !acc ) {
	(*pvarID).vt =3D VT_I4;
	(*pvarID).lVal =3D control;
	return S_OK;
    }

    QWindowsAccessible* wacc =3D new QWindowsAccessible( acc );
    acc->release();
    IDispatch *iface =3D 0;
    wacc->QueryInterface( IID_IDispatch, (void**)&iface );
    if ( iface ) {
	(*pvarID).vt =3D VT_DISPATCH;
	(*pvarID).pdispVal =3D iface;
	return S_OK;
    } else {
	delete wacc;
    }

    (*pvarID).vt =3D VT_EMPTY;
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::accLocation( long *pxLeft, =
long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    QRect rect =3D accessible->rect( varID.lVal );
    if ( rect.isValid() ) {
	*pxLeft =3D rect.x();
	*pyTop =3D rect.y();
	*pcxWidth =3D rect.width();
	*pcyHeight =3D rect.height();
    } else {
	*pxLeft =3D 0;
	*pyTop =3D 0;
	*pcxWidth =3D 0;
	*pcyHeight =3D 0;
    }
    return S_OK;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::accNavigate( long navDir, =
VARIANT varStart, VARIANT *pvarEnd )
{
    if ( !accessible->isValid() )
	return E_FAIL;

    int control =3D accessible->navigate( (NavDirection)navDir, =
varStart.lVal );
    if ( control =3D=3D -1 ) {
	(*pvarEnd).vt =3D VT_EMPTY;
	return S_FALSE;
    }
    QAccessibleInterface *acc =3D 0;
    if ( control ) {
	if ( varStart.lVal || navDir =3D=3D NavFirstChild || navDir =3D=3D =
NavLastChild || navDir =3D=3D NavFocusChild ) {
	    accessible->queryChild( control, &acc );
	} else {
	    QAccessibleInterface *parent =3D 0;
	    accessible->queryParent( &parent );
	    if ( parent ) {
		parent->queryChild( control, &acc );
		parent->release();
	    }
	}
    } else {
	acc =3D accessible;
	acc->addRef();
    }
    if ( !acc ) {
	(*pvarEnd).vt =3D VT_I4;
	(*pvarEnd).lVal =3D control;
	return S_OK;
    }
   =20
    QWindowsAccessible* wacc =3D new QWindowsAccessible( acc );
    acc->release();
    IDispatch *iface =3D 0;
    wacc->QueryInterface( IID_IDispatch, (void**)&iface );
    if ( iface ) {
	(*pvarEnd).vt =3D VT_DISPATCH;
	(*pvarEnd).pdispVal =3D iface;
	return S_OK;
    } else {
	delete wacc;
    }

    (*pvarEnd).vt =3D VT_EMPTY;
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild( VARIANT =
varChildID, IDispatch** ppdispChild )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    if ( varChildID.vt =3D=3D VT_EMPTY )
	return E_INVALIDARG;

    QAccessibleInterface *acc =3D 0;
    if ( !varChildID.lVal ) {
	acc =3D accessible;
	acc->addRef();
    } else {
	accessible->queryChild( varChildID.lVal, &acc );
    }

    if ( acc ) {
	QWindowsAccessible* wacc =3D new QWindowsAccessible( acc );
	acc->release();
	wacc->QueryInterface( IID_IDispatch, (void**)ppdispChild );
	return S_OK;
    }

    *ppdispChild =3D NULL;
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChildCount( long* =
pcountChildren )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    *pcountChildren =3D accessible->childCount();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accParent( IDispatch** =
ppdispParent )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    QAccessibleInterface *acc =3D 0;
    accessible->queryParent( &acc );
    if ( acc ) {
	QWindowsAccessible* wacc =3D new QWindowsAccessible( acc );
	acc->release();
	wacc->QueryInterface( IID_IDispatch, (void**)ppdispParent );

	if ( *ppdispParent )
	    return S_OK;
    }

    *ppdispParent =3D NULL;
    return S_FALSE;
}

/*
  Properties and methods
*/
HRESULT STDMETHODCALLTYPE QWindowsAccessible::accDoDefaultAction( =
VARIANT varID )
{
    if ( !accessible->isValid() )
	return E_FAIL;

    if ( accessible->doDefaultAction( varID.lVal ) )
	return S_OK;

    return DISP_E_MEMBERNOTFOUND;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDefaultAction( =
VARIANT varID, BSTR* pszDefaultAction )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    QString def =3D accessible->text( DefaultAction, varID.lVal );
    if ( !!def ) {
	*pszDefaultAction =3D QStringToBSTR( def );
	return S_OK;
    }

    *pszDefaultAction =3D NULL;
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDescription( =
VARIANT varID, BSTR* pszDescription )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    QString descr =3D accessible->text( Description, varID.lVal );
    if ( !!descr ) {
	*pszDescription =3D QStringToBSTR( descr );
	return S_OK;
    }

    *pszDescription =3D NULL;
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accHelp( VARIANT =
varID, BSTR *pszHelp )
{
    if ( !accessible->isValid() )
	return E_FAIL;

    QString help =3D accessible->text( Help, varID.lVal );
    if ( !!help ) {
	*pszHelp =3D QStringToBSTR( help );
	return S_OK;
    }
   =20
    *pszHelp =3D NULL;
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accHelpTopic( BSTR *, =
VARIANT, long * )
{=20
    return DISP_E_MEMBERNOTFOUND;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accKeyboardShortcut( =
VARIANT varID, BSTR *pszKeyboardShortcut )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    QString sc =3D accessible->text( Accelerator, varID.lVal );
    if ( !!sc ) {
	*pszKeyboardShortcut =3D QStringToBSTR( sc );
	return S_OK;
    }

    *pszKeyboardShortcut =3D NULL;
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accName( VARIANT =
varID, BSTR* pszName )
{
    if ( !accessible->isValid() )
	return E_FAIL;

    QString n =3D accessible->text( Name, varID.lVal );
    if ( !!n ) {
	*pszName =3D QStringToBSTR( n );
	return S_OK;
    }

    *pszName =3D NULL;
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::put_accName( VARIANT, BSTR =
)
{=20
    return DISP_E_MEMBERNOTFOUND;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accRole( VARIANT =
varID, VARIANT *pvarRole )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    Role role =3D accessible->role( varID.lVal );
    if ( role !=3D NoRole ) {
	(*pvarRole).vt =3D VT_I4;
	(*pvarRole).lVal =3D role;=09
    } else {
	(*pvarRole).vt =3D VT_EMPTY;
    }
    return S_OK;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accState( VARIANT =
varID, VARIANT *pvarState )
{
    if ( !accessible->isValid() )
	return E_FAIL;

    (*pvarState).vt =3D VT_I4;
    (*pvarState).lVal =3D accessible->state( varID.lVal );
    return S_OK;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accValue( VARIANT =
varID, BSTR* pszValue )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    QString value =3D accessible->text( Value, varID.lVal );
    if ( !value.isNull() ) {
	*pszValue =3D QStringToBSTR( value );
	return S_OK;
    }

    *pszValue =3D NULL;
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::put_accValue( VARIANT, =
BSTR )
{=20
    return DISP_E_MEMBERNOTFOUND;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::accSelect( long =
flagsSelect, VARIANT varID )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    bool res =3D FALSE;
    if ( flagsSelect & SELFLAG_TAKEFOCUS )
	res =3D accessible->setFocus( varID.lVal );
    if ( flagsSelect & SELFLAG_TAKESELECTION ) {
	accessible->clearSelection();
	res =3D accessible->setSelected( varID.lVal, TRUE, FALSE );
    }
    if ( flagsSelect & SELFLAG_EXTENDSELECTION )
	res =3D accessible->setSelected( varID.lVal, TRUE, TRUE );
    if ( flagsSelect & SELFLAG_ADDSELECTION )
	res =3D accessible->setSelected( varID.lVal, TRUE, FALSE );
    if ( flagsSelect & SELFLAG_REMOVESELECTION )
	res =3D accessible->setSelected( varID.lVal, FALSE, FALSE );

    return res ? S_OK : S_FALSE;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accFocus( VARIANT =
*pvarID )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    int control =3D accessible->navigate( NavFocusChild, 0 );
    if ( control =3D=3D -1 ) {
	(*pvarID).vt =3D VT_EMPTY;
	return S_FALSE;
    }
    QAccessibleInterface *acc =3D 0;
    if ( control )
	accessible->queryChild( control, &acc );
    if ( !acc ) {
	(*pvarID).vt =3D VT_I4;
	(*pvarID).lVal =3D control;
	return S_OK;
    }
   =20
    QWindowsAccessible* wacc =3D new QWindowsAccessible( acc );
    acc->release();
    IDispatch *iface =3D 0;
    wacc->QueryInterface( IID_IDispatch, (void**)&iface );
    if ( iface ) {
	(*pvarID).vt =3D VT_DISPATCH;
	(*pvarID).pdispVal =3D iface;
	return S_OK;
    } else {
	delete wacc;
    }

    (*pvarID).vt =3D VT_EMPTY;
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accSelection( VARIANT =
*pvarChildren )
{=20
    if ( !accessible->isValid() )
	return E_FAIL;

    QMemArray<int> sel =3D accessible->selection();
    if ( sel.isEmpty() ) {
	(*pvarChildren).vt =3D VT_EMPTY;
	return S_FALSE;
    }
    if ( sel.size() =3D=3D 1 ) {
	(*pvarChildren).vt =3D VT_I4;
	(*pvarChildren).lVal =3D sel[0];
	return S_OK;
    }
    IEnumVARIANT *iface =3D new QWindowsEnumerate( sel );
    IUnknown *uiface;
    iface->QueryInterface( IID_IUnknown, (void**)&uiface );
    (*pvarChildren).vt =3D VT_UNKNOWN;
    (*pvarChildren).punkVal =3D uiface;
   =20
    return S_OK;
}

#endif

------=_NextPart_000_0192_01C296EC.72F8C730--