view dwt/accessible/Accessible.d @ 31:92c102dd64a3

Added all widgets modules as dummy. Most modules of accessible are equal to the linux version, except Accessible.
author Frank Benoit <benoit@tionex.de>
date Mon, 28 Jan 2008 04:47:28 +0100
parents
children
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2000, 2007 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 * Port to the D programming language:
 *     Frank Benoit <benoit@tionex.de>
 *******************************************************************************/
module dwt.accessibility.Accessible;

//PORTING_TYPE
public class Accessible {
}

/++
import dwt.DWT;
import dwt.DWTException;
import dwt.internal.ole.win32.COM;
import dwt.internal.ole.win32.COMObject;
import dwt.internal.ole.win32.GUID;
import dwt.internal.ole.win32.IAccessible;
import dwt.internal.ole.win32.IEnumVARIANT;
import dwt.internal.ole.win32.VARIANT;
import dwt.internal.win32.OS;
import dwt.internal.win32.TVITEM;
import dwt.ole.win32.OLE;
import dwt.widgets.Control;
import dwt.widgets.Table;
import dwt.widgets.TableItem;
import dwt.widgets.Tree;
import dwt.widgets.TreeItem;
import dwt.widgets.Widget;

/**
 * Instances of this class provide a bridge between application
 * code and assistive technology clients. Many platforms provide
 * default accessible behavior for most widgets, and this class
 * allows that default behavior to be overridden. Applications
 * can get the default Accessible object for a control by sending
 * it <code>getAccessible</code>, and then add an accessible listener
 * to override simple items like the name and help string, or they
 * can add an accessible control listener to override complex items.
 * As a rule of thumb, an application would only want to use the
 * accessible control listener to implement accessibility for a
 * custom control.
 *
 * @see Control#getAccessible
 * @see AccessibleListener
 * @see AccessibleEvent
 * @see AccessibleControlListener
 * @see AccessibleControlEvent
 *
 * @since 2.0
 */
public class Accessible {
    int refCount = 0, enumIndex = 0;
    COMObject objIAccessible, objIEnumVARIANT;
    IAccessible iaccessible;
    Vector accessibleListeners = new Vector();
    Vector accessibleControlListeners = new Vector();
    Vector textListeners = new Vector ();
    Object[] variants;
    Control control;

    Accessible(Control control) {
        this.control = control;
        int /*long*/[] ppvObject = new int /*long*/[1];
        /* CreateStdAccessibleObject([in] hwnd, [in] idObject, [in] riidInterface, [out] ppvObject).
         * AddRef has already been called on ppvObject by the callee and must be released by the caller.
         */
        int result = (int)/*64*/COM.CreateStdAccessibleObject(control.handle, COM.OBJID_CLIENT, COM.IIDIAccessible, ppvObject);
        /* The object needs to be checked, because if the CreateStdAccessibleObject()
         * symbol is not found, the return value is S_OK.
         */
        if (ppvObject[0] is 0) return;
        if (result !is COM.S_OK) OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result);
        iaccessible = new IAccessible(ppvObject[0]);

        objIAccessible = new COMObject(new int[] {2,0,0,1,3,5,8,1,1,2,2,2,2,2,2,2,3,2,1,1,2,2,5,3,3,1,2,2}) {
            public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);}
            public int /*long*/ method1(int /*long*/[] args) {return AddRef();}
            public int /*long*/ method2(int /*long*/[] args) {return Release();}
            // method3 GetTypeInfoCount - not implemented
            // method4 GetTypeInfo - not implemented
            // method5 GetIDsOfNames - not implemented
            // method6 Invoke - not implemented
            public int /*long*/ method7(int /*long*/[] args) {return get_accParent(args[0]);}
            public int /*long*/ method8(int /*long*/[] args) {return get_accChildCount(args[0]);}
            public int /*long*/ method9(int /*long*/[] args) {return get_accChild(args[0], args[1]);}
            public int /*long*/ method10(int /*long*/[] args) {return get_accName(args[0], args[1]);}
            public int /*long*/ method11(int /*long*/[] args) {return get_accValue(args[0], args[1]);}
            public int /*long*/ method12(int /*long*/[] args) {return get_accDescription(args[0], args[1]);}
            public int /*long*/ method13(int /*long*/[] args) {return get_accRole(args[0], args[1]);}
            public int /*long*/ method14(int /*long*/[] args) {return get_accState(args[0], args[1]);}
            public int /*long*/ method15(int /*long*/[] args) {return get_accHelp(args[0], args[1]);}
            public int /*long*/ method16(int /*long*/[] args) {return get_accHelpTopic(args[0], args[1], args[2]);}
            public int /*long*/ method17(int /*long*/[] args) {return get_accKeyboardShortcut(args[0], args[1]);}
            public int /*long*/ method18(int /*long*/[] args) {return get_accFocus(args[0]);}
            public int /*long*/ method19(int /*long*/[] args) {return get_accSelection(args[0]);}
            public int /*long*/ method20(int /*long*/[] args) {return get_accDefaultAction(args[0], args[1]);}
            public int /*long*/ method21(int /*long*/[] args) {return accSelect((int)/*64*/args[0], args[1]);}
            public int /*long*/ method22(int /*long*/[] args) {return accLocation(args[0], args[1], args[2], args[3], args[4]);}
            public int /*long*/ method23(int /*long*/[] args) {return accNavigate((int)/*64*/args[0], args[1], args[2]);}
            public int /*long*/ method24(int /*long*/[] args) {return accHitTest((int)/*64*/args[0], (int)/*64*/args[1], args[2]);}
            public int /*long*/ method25(int /*long*/[] args) {return accDoDefaultAction(args[0]);}
            public int /*long*/ method26(int /*long*/[] args) {return put_accName(args[0], args[1]);}
            public int /*long*/ method27(int /*long*/[] args) {return put_accValue(args[0], args[1]);}
        };

        int /*long*/ ppVtable = objIAccessible.ppVtable;
        int /*long*/[] pVtable = new int /*long*/[1];
        COM.MoveMemory(pVtable, ppVtable, OS.PTR_SIZEOF);
        int /*long*/[] funcs = new int /*long*/[28];
        COM.MoveMemory(funcs, pVtable[0], OS.PTR_SIZEOF * funcs.length);
        funcs[9] = COM.get_accChild_CALLBACK(funcs[9]);
        funcs[10] = COM.get_accName_CALLBACK(funcs[10]);
        funcs[11] = COM.get_accValue_CALLBACK(funcs[11]);
        funcs[12] = COM.get_accDescription_CALLBACK(funcs[12]);
        funcs[13] = COM.get_accRole_CALLBACK(funcs[13]);
        funcs[14] = COM.get_accState_CALLBACK(funcs[14]);
        funcs[15] = COM.get_accHelp_CALLBACK(funcs[15]);
        funcs[16] = COM.get_accHelpTopic_CALLBACK(funcs[16]);
        funcs[17] = COM.get_accKeyboardShortcut_CALLBACK(funcs[17]);
        funcs[20] = COM.get_accDefaultAction_CALLBACK(funcs[20]);
        funcs[21] = COM.accSelect_CALLBACK(funcs[21]);
        funcs[22] = COM.accLocation_CALLBACK(funcs[22]);
        funcs[23] = COM.accNavigate_CALLBACK(funcs[23]);
        funcs[25] = COM.accDoDefaultAction_CALLBACK(funcs[25]);
        funcs[26] = COM.put_accName_CALLBACK(funcs[26]);
        funcs[27] = COM.put_accValue_CALLBACK(funcs[27]);
        COM.MoveMemory(pVtable[0], funcs, OS.PTR_SIZEOF * funcs.length);

        objIEnumVARIANT = new COMObject(new int[] {2,0,0,3,1,0,1}) {
            public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);}
            public int /*long*/ method1(int /*long*/[] args) {return AddRef();}
            public int /*long*/ method2(int /*long*/[] args) {return Release();}
            public int /*long*/ method3(int /*long*/[] args) {return Next((int)args[0], args[1], args[2]);}
            public int /*long*/ method4(int /*long*/[] args) {return Skip((int)args[0]);}
            public int /*long*/ method5(int /*long*/[] args) {return Reset();}
            public int /*long*/ method6(int /*long*/[] args) {return Clone(args[0]);}
        };
        AddRef();
    }

    /**
     * Invokes platform specific functionality to allocate a new accessible object.
     * <p>
     * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
     * API for <code>Accessible</code>. It is marked public only so that it
     * can be shared within the packages provided by DWT. It is not
     * available on all platforms, and should never be called from
     * application code.
     * </p>
     *
     * @param control the control to get the accessible object for
     * @return the platform specific accessible object
     */
    public static Accessible internal_new_Accessible(Control control) {
        return new Accessible(control);
    }

    /**
     * Adds the listener to the collection of listeners who will
     * be notified when an accessible client asks for certain strings,
     * such as name, description, help, or keyboard shortcut. The
     * listener is notified by sending it one of the messages defined
     * in the <code>AccessibleListener</code> interface.
     *
     * @param listener the listener that should be notified when the receiver
     * is asked for a name, description, help, or keyboard shortcut string
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
     * </ul>
     *
     * @see AccessibleListener
     * @see #removeAccessibleListener
     */
    public void addAccessibleListener(AccessibleListener listener) {
        checkWidget();
        if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
        accessibleListeners.addElement(listener);
    }

    /**
     * Adds the listener to the collection of listeners who will
     * be notified when an accessible client asks for custom control
     * specific information. The listener is notified by sending it
     * one of the messages defined in the <code>AccessibleControlListener</code>
     * interface.
     *
     * @param listener the listener that should be notified when the receiver
     * is asked for custom control specific information
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
     * </ul>
     *
     * @see AccessibleControlListener
     * @see #removeAccessibleControlListener
     */
    public void addAccessibleControlListener(AccessibleControlListener listener) {
        checkWidget();
        if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
        accessibleControlListeners.addElement(listener);
    }

    /**
     * Adds the listener to the collection of listeners who will
     * be notified when an accessible client asks for custom text control
     * specific information. The listener is notified by sending it
     * one of the messages defined in the <code>AccessibleTextListener</code>
     * interface.
     *
     * @param listener the listener that should be notified when the receiver
     * is asked for custom text control specific information
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
     * </ul>
     *
     * @see AccessibleTextListener
     * @see #removeAccessibleTextListener
     *
     * @since 3.0
     */
    public void addAccessibleTextListener (AccessibleTextListener listener) {
        checkWidget ();
        if (listener is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
        textListeners.addElement (listener);
    }

    /**
     * Returns the control for this Accessible object.
     *
     * @return the receiver's control
     * @since 3.0
     */
    public Control getControl() {
        return control;
    }

    /**
     * Invokes platform specific functionality to dispose an accessible object.
     * <p>
     * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
     * API for <code>Accessible</code>. It is marked public only so that it
     * can be shared within the packages provided by DWT. It is not
     * available on all platforms, and should never be called from
     * application code.
     * </p>
     */
    public void internal_dispose_Accessible() {
        if (iaccessible !is null) {
            iaccessible.Release();
        }
        iaccessible = null;
        Release();
    }

    /**
     * Invokes platform specific functionality to handle a window message.
     * <p>
     * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
     * API for <code>Accessible</code>. It is marked public only so that it
     * can be shared within the packages provided by DWT. It is not
     * available on all platforms, and should never be called from
     * application code.
     * </p>
     */
    public int /*long*/ internal_WM_GETOBJECT (int /*long*/ wParam, int /*long*/ lParam) {
        if (objIAccessible is null) return 0;
        if ((int)/*64*/lParam is COM.OBJID_CLIENT) {
            /* LresultFromObject([in] riid, [in] wParam, [in] pAcc)
             * The argument pAcc is owned by the caller so reference count does not
             * need to be incremented.
             */
            return COM.LresultFromObject(COM.IIDIAccessible, wParam, objIAccessible.getAddress());
        }
        return 0;
    }

    /**
     * Removes the listener from the collection of listeners who will
     * be notified when an accessible client asks for certain strings,
     * such as name, description, help, or keyboard shortcut.
     *
     * @param listener the listener that should no longer be notified when the receiver
     * is asked for a name, description, help, or keyboard shortcut string
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
     * </ul>
     *
     * @see AccessibleListener
     * @see #addAccessibleListener
     */
    public void removeAccessibleListener(AccessibleListener listener) {
        checkWidget();
        if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
        accessibleListeners.removeElement(listener);
    }

    /**
     * Removes the listener from the collection of listeners who will
     * be notified when an accessible client asks for custom control
     * specific information.
     *
     * @param listener the listener that should no longer be notified when the receiver
     * is asked for custom control specific information
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
     * </ul>
     *
     * @see AccessibleControlListener
     * @see #addAccessibleControlListener
     */
    public void removeAccessibleControlListener(AccessibleControlListener listener) {
        checkWidget();
        if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
        accessibleControlListeners.removeElement(listener);
    }

    /**
     * Removes the listener from the collection of listeners who will
     * be notified when an accessible client asks for custom text control
     * specific information.
     *
     * @param listener the listener that should no longer be notified when the receiver
     * is asked for custom text control specific information
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
     * </ul>
     *
     * @see AccessibleTextListener
     * @see #addAccessibleTextListener
     *
     * @since 3.0
     */
    public void removeAccessibleTextListener (AccessibleTextListener listener) {
        checkWidget ();
        if (listener is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
        textListeners.removeElement (listener);
    }

    /**
     * Sends a message to accessible clients that the child selection
     * within a custom container control has changed.
     *
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
     * </ul>
     *
     * @since 3.0
     */
    public void selectionChanged () {
        checkWidget();
        COM.NotifyWinEvent (COM.EVENT_OBJECT_SELECTIONWITHIN, control.handle, COM.OBJID_CLIENT, COM.CHILDID_SELF);
    }

    /**
     * Sends a message to accessible clients indicating that the focus
     * has changed within a custom control.
     *
     * @param childID an identifier specifying a child of the control
     *
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
     * </ul>
     */
    public void setFocus(int childID) {
        checkWidget();
        COM.NotifyWinEvent (COM.EVENT_OBJECT_FOCUS, control.handle, COM.OBJID_CLIENT, childIDToOs(childID));
    }

    /**
     * Sends a message to accessible clients that the text
     * caret has moved within a custom control.
     *
     * @param index the new caret index within the control
     *
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
     * </ul>
     *
     * @since 3.0
     */
    public void textCaretMoved (int index) {
        checkWidget();
        COM.NotifyWinEvent (COM.EVENT_OBJECT_LOCATIONCHANGE, control.handle, COM.OBJID_CARET, COM.CHILDID_SELF);
    }

    /**
     * Sends a message to accessible clients that the text
     * within a custom control has changed.
     *
     * @param type the type of change, one of <code>ACC.NOTIFY_TEXT_INSERT</code>
     * or <code>ACC.NOTIFY_TEXT_DELETE</code>
     * @param startIndex the text index within the control where the insertion or deletion begins
     * @param length the non-negative length in characters of the insertion or deletion
     *
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
     * </ul>
     *
     * @see ACC#TEXT_INSERT
     * @see ACC#TEXT_DELETE
     *
     * @since 3.0
     */
    public void textChanged (int type, int startIndex, int length) {
        checkWidget();
        COM.NotifyWinEvent (COM.EVENT_OBJECT_VALUECHANGE, control.handle, COM.OBJID_CLIENT, COM.CHILDID_SELF);
    }

    /**
     * Sends a message to accessible clients that the text
     * selection has changed within a custom control.
     *
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
     * </ul>
     *
     * @since 3.0
     */
    public void textSelectionChanged () {
        checkWidget();
        // not an MSAA event
    }

    /* QueryInterface([in] iid, [out] ppvObject)
     * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject
     * must be incremented before returning.  Caller is responsible for releasing ppvObject.
     */
    int QueryInterface(int /*long*/ iid, int /*long*/ ppvObject) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        GUID guid = new GUID();
        COM.MoveMemory(guid, iid, GUID.sizeof);

        if (COM.IsEqualGUID(guid, COM.IIDIUnknown)) {
            COM.MoveMemory(ppvObject, new int /*long*/[] { objIAccessible.getAddress()}, OS.PTR_SIZEOF);
            AddRef();
            return COM.S_OK;
        }

        if (COM.IsEqualGUID(guid, COM.IIDIDispatch)) {
            COM.MoveMemory(ppvObject, new int /*long*/[] { objIAccessible.getAddress()}, OS.PTR_SIZEOF);
            AddRef();
            return COM.S_OK;
        }

        if (COM.IsEqualGUID(guid, COM.IIDIAccessible)) {
            COM.MoveMemory(ppvObject, new int /*long*/[] { objIAccessible.getAddress()}, OS.PTR_SIZEOF);
            AddRef();
            return COM.S_OK;
        }

        if (COM.IsEqualGUID(guid, COM.IIDIEnumVARIANT)) {
            COM.MoveMemory(ppvObject, new int /*long*/[] { objIEnumVARIANT.getAddress()}, OS.PTR_SIZEOF);
            AddRef();
            enumIndex = 0;
            return COM.S_OK;
        }

        int /*long*/[] ppv = new int /*long*/[1];
        int result = iaccessible.QueryInterface(guid, ppv);
        COM.MoveMemory(ppvObject, ppv, OS.PTR_SIZEOF);
        return result;
    }

    int AddRef() {
        refCount++;
        return refCount;
    }

    int Release() {
        refCount--;

        if (refCount is 0) {
            if (objIAccessible !is null)
                objIAccessible.dispose();
            objIAccessible = null;

            if (objIEnumVARIANT !is null)
                objIEnumVARIANT.dispose();
            objIEnumVARIANT = null;
        }
        return refCount;
    }

    int accDoDefaultAction(int /*long*/ variant) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        // Currently, we don't let the application override this. Forward to the proxy.
        int code = iaccessible.accDoDefaultAction(variant);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        return code;
    }

    int accHitTest(int xLeft, int yTop, int /*long*/ pvarChild) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        if (accessibleControlListeners.size() is 0) {
            return iaccessible.accHitTest(xLeft, yTop, pvarChild);
        }

        AccessibleControlEvent event = new AccessibleControlEvent(this);
        event.childID = ACC.CHILDID_NONE;
        event.x = xLeft;
        event.y = yTop;
        for (int i = 0; i < accessibleControlListeners.size(); i++) {
            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
            listener.getChildAtPoint(event);
        }
        int childID = event.childID;
        if (childID is ACC.CHILDID_NONE) {
            return iaccessible.accHitTest(xLeft, yTop, pvarChild);
        }
        COM.MoveMemory(pvarChild, new short[] { COM.VT_I4 }, 2);
        COM.MoveMemory(pvarChild + 8, new int[] { childIDToOs(childID) }, 4);
        return COM.S_OK;
    }

    int accLocation(int /*long*/ pxLeft, int /*long*/ pyTop, int /*long*/ pcxWidth, int /*long*/ pcyHeight, int /*long*/ variant) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        VARIANT v = new VARIANT();
        COM.MoveMemory(v, variant, VARIANT.sizeof);
        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;

        /* Get the default location from the OS. */
        int osLeft = 0, osTop = 0, osWidth = 0, osHeight = 0;
        int code = iaccessible.accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, variant);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        if (accessibleControlListeners.size() is 0) return code;
        if (code is COM.S_OK) {
            int[] pLeft = new int[1], pTop = new int[1], pWidth = new int[1], pHeight = new int[1];
            COM.MoveMemory(pLeft, pxLeft, 4);
            COM.MoveMemory(pTop, pyTop, 4);
            COM.MoveMemory(pWidth, pcxWidth, 4);
            COM.MoveMemory(pHeight, pcyHeight, 4);
            osLeft = pLeft[0]; osTop = pTop[0]; osWidth = pWidth[0]; osHeight = pHeight[0];
        }

        AccessibleControlEvent event = new AccessibleControlEvent(this);
        event.childID = osToChildID((int)/*64*/v.lVal);
        event.x = osLeft;
        event.y = osTop;
        event.width = osWidth;
        event.height = osHeight;
        for (int i = 0; i < accessibleControlListeners.size(); i++) {
            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
            listener.getLocation(event);
        }
        OS.MoveMemory(pxLeft, new int[] { event.x }, 4);
        OS.MoveMemory(pyTop, new int[] { event.y }, 4);
        OS.MoveMemory(pcxWidth, new int[] { event.width }, 4);
        OS.MoveMemory(pcyHeight, new int[] { event.height }, 4);
        return COM.S_OK;
    }

    int accNavigate(int navDir, int /*long*/ variant, int /*long*/ pvarEndUpAt) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        // Currently, we don't let the application override this. Forward to the proxy.
        int code = iaccessible.accNavigate(navDir, variant, pvarEndUpAt);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        return code;
    }

    int accSelect(int flagsSelect, int /*long*/ variant) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        // Currently, we don't let the application override this. Forward to the proxy.
        int code = iaccessible.accSelect(flagsSelect, variant);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        return code;
    }

    /* get_accChild([in] varChild, [out] ppdispChild)
     * Ownership of ppdispChild transfers from callee to caller so reference count on ppdispChild
     * must be incremented before returning.  The caller is responsible for releasing ppdispChild.
     */
    int get_accChild(int /*long*/ variant, int /*long*/ ppdispChild) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        VARIANT v = new VARIANT();
        COM.MoveMemory(v, variant, VARIANT.sizeof);
        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
        if (accessibleControlListeners.size() is 0) {
            int code = iaccessible.get_accChild(variant, ppdispChild);
            if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
            return code;
        }

        AccessibleControlEvent event = new AccessibleControlEvent(this);
        event.childID = osToChildID((int)/*64*/v.lVal);
        for (int i = 0; i < accessibleControlListeners.size(); i++) {
            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
            listener.getChild(event);
        }
        Accessible accessible = event.accessible;
        if (accessible !is null) {
            accessible.AddRef();
            COM.MoveMemory(ppdispChild, new int /*long*/[] { accessible.objIAccessible.getAddress() }, OS.PTR_SIZEOF);
            return COM.S_OK;
        }
        return COM.S_FALSE;
    }

    int get_accChildCount(int /*long*/ pcountChildren) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;

        /* Get the default child count from the OS. */
        int osChildCount = 0;
        int code = iaccessible.get_accChildCount(pcountChildren);
        if (accessibleControlListeners.size() is 0) return code;
        if (code is COM.S_OK) {
            int[] pChildCount = new int[1];
            COM.MoveMemory(pChildCount, pcountChildren, 4);
            osChildCount = pChildCount[0];
        }

        AccessibleControlEvent event = new AccessibleControlEvent(this);
        event.childID = ACC.CHILDID_SELF;
        event.detail = osChildCount;
        for (int i = 0; i < accessibleControlListeners.size(); i++) {
            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
            listener.getChildCount(event);
        }

        COM.MoveMemory(pcountChildren, new int[] { event.detail }, 4);
        return COM.S_OK;
    }

    int get_accDefaultAction(int /*long*/ variant, int /*long*/ pszDefaultAction) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        VARIANT v = new VARIANT();
        COM.MoveMemory(v, variant, VARIANT.sizeof);
        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;

        /* Get the default defaultAction from the OS. */
        String osDefaultAction = null;
        int code = iaccessible.get_accDefaultAction(variant, pszDefaultAction);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        if (accessibleControlListeners.size() is 0) return code;
        if (code is COM.S_OK) {
            int /*long*/[] pDefaultAction = new int /*long*/[1];
            COM.MoveMemory(pDefaultAction, pszDefaultAction, OS.PTR_SIZEOF);
            int size = COM.SysStringByteLen(pDefaultAction[0]);
            if (size > 0) {
                char[] buffer = new char[(size + 1) /2];
                COM.MoveMemory(buffer, pDefaultAction[0], size);
                osDefaultAction = new String(buffer);
            }
        }

        AccessibleControlEvent event = new AccessibleControlEvent(this);
        event.childID = osToChildID((int)/*64*/v.lVal);
        event.result = osDefaultAction;
        for (int i = 0; i < accessibleControlListeners.size(); i++) {
            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
            listener.getDefaultAction(event);
        }
        if (event.result is null) return code;
        char[] data = (event.result + "\0").toCharArray();
        int /*long*/ ptr = COM.SysAllocString(data);
        COM.MoveMemory(pszDefaultAction, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
        return COM.S_OK;
    }

    int get_accDescription(int /*long*/ variant, int /*long*/ pszDescription) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        VARIANT v = new VARIANT();
        COM.MoveMemory(v, variant, VARIANT.sizeof);
        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;

        /* Get the default description from the OS. */
        String osDescription = null;
        int code = iaccessible.get_accDescription(variant, pszDescription);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        // TEMPORARY CODE - process tree even if there are no apps listening
        if (accessibleListeners.size() is 0 && !(control instanceof Tree)) return code;
        if (code is COM.S_OK) {
            int /*long*/[] pDescription = new int /*long*/[1];
            COM.MoveMemory(pDescription, pszDescription, OS.PTR_SIZEOF);
            int size = COM.SysStringByteLen(pDescription[0]);
            if (size > 0) {
                char[] buffer = new char[(size + 1) /2];
                COM.MoveMemory(buffer, pDescription[0], size);
                osDescription = new String(buffer);
            }
        }

        AccessibleEvent event = new AccessibleEvent(this);
        event.childID = osToChildID((int)/*64*/v.lVal);
        event.result = osDescription;

        // TEMPORARY CODE
        /* Currently our tree columns are emulated using custom draw,
         * so we need to create the description using the tree column
         * header text and tree item text. */
        if (v.lVal !is COM.CHILDID_SELF) {
            if (control instanceof Tree) {
                Tree tree = (Tree) control;
                int columnCount = tree.getColumnCount ();
                if (columnCount > 1) {
                    int /*long*/ hwnd = control.handle, hItem = 0;
                    if (OS.COMCTL32_MAJOR >= 6) {
                        hItem = OS.SendMessage (hwnd, OS.TVM_MAPACCIDTOHTREEITEM, v.lVal, 0);
                    } else {
                        hItem = v.lVal;
                    }
                    Widget widget = tree.getDisplay ().findWidget (hwnd, hItem);
                    event.result = "";
                    if (widget !is null && widget instanceof TreeItem) {
                        TreeItem item = (TreeItem) widget;
                        for (int i = 1; i < columnCount; i++) {
                            event.result += tree.getColumn(i).getText() + ": " + item.getText(i);
                            if (i + 1 < columnCount) event.result += ", ";
                        }
                    }
                }
            }
        }
        for (int i = 0; i < accessibleListeners.size(); i++) {
            AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
            listener.getDescription(event);
        }
        if (event.result is null) return code;
        char[] data = (event.result + "\0").toCharArray();
        int /*long*/ ptr = COM.SysAllocString(data);
        COM.MoveMemory(pszDescription, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
        return COM.S_OK;
    }

    /* get_accFocus([out] int pvarChild)
     * Ownership of pvarChild transfers from callee to caller so reference count on pvarChild
     * must be incremented before returning.  The caller is responsible for releasing pvarChild.
     */
    int get_accFocus(int /*long*/ pvarChild) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;

        /* Get the default focus child from the OS. */
        int osChild = ACC.CHILDID_NONE;
        int code = iaccessible.get_accFocus(pvarChild);
        if (accessibleControlListeners.size() is 0) return code;
        if (code is COM.S_OK) {
            short[] pvt = new short[1];
            COM.MoveMemory(pvt, pvarChild, 2);
            if (pvt[0] is COM.VT_I4) {
                int[] pChild = new int[1];
                COM.MoveMemory(pChild, pvarChild + 8, 4);
                osChild = osToChildID(pChild[0]);
            }
        }

        AccessibleControlEvent event = new AccessibleControlEvent(this);
        event.childID = osChild;
        for (int i = 0; i < accessibleControlListeners.size(); i++) {
            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
            listener.getFocus(event);
        }
        Accessible accessible = event.accessible;
        if (accessible !is null) {
            accessible.AddRef();
            COM.MoveMemory(pvarChild, new short[] { COM.VT_DISPATCH }, 2);
            COM.MoveMemory(pvarChild + 8, new int /*long*/[] { accessible.objIAccessible.getAddress() }, OS.PTR_SIZEOF);
            return COM.S_OK;
        }
        int childID = event.childID;
        if (childID is ACC.CHILDID_NONE) {
            COM.MoveMemory(pvarChild, new short[] { COM.VT_EMPTY }, 2);
            return COM.S_FALSE;
        }
        if (childID is ACC.CHILDID_SELF) {
            AddRef();
            COM.MoveMemory(pvarChild, new short[] { COM.VT_DISPATCH }, 2);
            COM.MoveMemory(pvarChild + 8, new int /*long*/[] { objIAccessible.getAddress() }, OS.PTR_SIZEOF);
            return COM.S_OK;
        }
        COM.MoveMemory(pvarChild, new short[] { COM.VT_I4 }, 2);
        COM.MoveMemory(pvarChild + 8, new int[] { childIDToOs(childID) }, 4);
        return COM.S_OK;
    }

    int get_accHelp(int /*long*/ variant, int /*long*/ pszHelp) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        VARIANT v = new VARIANT();
        COM.MoveMemory(v, variant, VARIANT.sizeof);
        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;

        /* Get the default help string from the OS. */
        String osHelp = null;
        int code = iaccessible.get_accHelp(variant, pszHelp);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        if (accessibleListeners.size() is 0) return code;
        if (code is COM.S_OK) {
            int /*long*/[] pHelp = new int /*long*/[1];
            COM.MoveMemory(pHelp, pszHelp, OS.PTR_SIZEOF);
            int size = COM.SysStringByteLen(pHelp[0]);
            if (size > 0) {
                char[] buffer = new char[(size + 1) /2];
                COM.MoveMemory(buffer, pHelp[0], size);
                osHelp = new String(buffer);
            }
        }

        AccessibleEvent event = new AccessibleEvent(this);
        event.childID = osToChildID((int)/*64*/v.lVal);
        event.result = osHelp;
        for (int i = 0; i < accessibleListeners.size(); i++) {
            AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
            listener.getHelp(event);
        }
        if (event.result is null) return code;
        char[] data = (event.result + "\0").toCharArray();
        int /*long*/ ptr = COM.SysAllocString(data);
        COM.MoveMemory(pszHelp, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
        return COM.S_OK;
    }

    int get_accHelpTopic(int /*long*/ pszHelpFile, int /*long*/ variant, int /*long*/ pidTopic) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        // Currently, we don't let the application override this. Forward to the proxy.
        int code = iaccessible.get_accHelpTopic(pszHelpFile, variant, pidTopic);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        return code;
    }

    int get_accKeyboardShortcut(int /*long*/ variant, int /*long*/ pszKeyboardShortcut) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        VARIANT v = new VARIANT();
        COM.MoveMemory(v, variant, VARIANT.sizeof);
        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;

        /* Get the default keyboard shortcut from the OS. */
        String osKeyboardShortcut = null;
        int code = iaccessible.get_accKeyboardShortcut(variant, pszKeyboardShortcut);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        if (accessibleListeners.size() is 0) return code;
        if (code is COM.S_OK) {
            int /*long*/[] pKeyboardShortcut = new int /*long*/[1];
            COM.MoveMemory(pKeyboardShortcut, pszKeyboardShortcut, OS.PTR_SIZEOF);
            int size = COM.SysStringByteLen(pKeyboardShortcut[0]);
            if (size > 0) {
                char[] buffer = new char[(size + 1) /2];
                COM.MoveMemory(buffer, pKeyboardShortcut[0], size);
                osKeyboardShortcut = new String(buffer);
            }
        }

        AccessibleEvent event = new AccessibleEvent(this);
        event.childID = osToChildID((int)/*64*/v.lVal);
        event.result = osKeyboardShortcut;
        for (int i = 0; i < accessibleListeners.size(); i++) {
            AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
            listener.getKeyboardShortcut(event);
        }
        if (event.result is null) return code;
        char[] data = (event.result + "\0").toCharArray();
        int /*long*/ ptr = COM.SysAllocString(data);
        COM.MoveMemory(pszKeyboardShortcut, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
        return COM.S_OK;
    }

    int get_accName(int /*long*/ variant, int /*long*/ pszName) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        VARIANT v = new VARIANT();
        COM.MoveMemory(v, variant, VARIANT.sizeof);
        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;

        /* Get the default name from the OS. */
        String osName = null;
        int code = iaccessible.get_accName(variant, pszName);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        if (accessibleListeners.size() is 0) return code;
        if (code is COM.S_OK) {
            int /*long*/[] pName = new int /*long*/[1];
            COM.MoveMemory(pName, pszName, OS.PTR_SIZEOF);
            int size = COM.SysStringByteLen(pName[0]);
            if (size > 0) {
                char[] buffer = new char[(size + 1) /2];
                COM.MoveMemory(buffer, pName[0], size);
                osName = new String(buffer);
            }
        }

        AccessibleEvent event = new AccessibleEvent(this);
        event.childID = osToChildID((int)/*64*/v.lVal);
        event.result = osName;
        for (int i = 0; i < accessibleListeners.size(); i++) {
            AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
            listener.getName(event);
        }
        if (event.result is null) return code;
        char[] data = (event.result + "\0").toCharArray();
        int /*long*/ ptr = COM.SysAllocString(data);
        COM.MoveMemory(pszName, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
        return COM.S_OK;
    }

    /* get_accParent([out] ppdispParent)
     * Ownership of ppdispParent transfers from callee to caller so reference count on ppdispParent
     * must be incremented before returning.  The caller is responsible for releasing ppdispParent.
     */
    int get_accParent(int /*long*/ ppdispParent) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        // Currently, we don't let the application override this. Forward to the proxy.
        return iaccessible.get_accParent(ppdispParent);
    }

    int get_accRole(int /*long*/ variant, int /*long*/ pvarRole) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        VARIANT v = new VARIANT();
        COM.MoveMemory(v, variant, VARIANT.sizeof);
        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;

        /* Get the default role from the OS. */
        int osRole = COM.ROLE_SYSTEM_CLIENT;
        int code = iaccessible.get_accRole(variant, pvarRole);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        // TEMPORARY CODE - process tree and table even if there are no apps listening
        if (accessibleControlListeners.size() is 0 && !(control instanceof Tree || control instanceof Table)) return code;
        if (code is COM.S_OK) {
            short[] pvt = new short[1];
            COM.MoveMemory(pvt, pvarRole, 2);
            if (pvt[0] is COM.VT_I4) {
                int[] pRole = new int[1];
                COM.MoveMemory(pRole, pvarRole + 8, 4);
                osRole = pRole[0];
            }
        }

        AccessibleControlEvent event = new AccessibleControlEvent(this);
        event.childID = osToChildID((int)/*64*/v.lVal);
        event.detail = osToRole(osRole);
        // TEMPORARY CODE
        /* Currently our checkbox table and tree are emulated using state mask
         * images, so we need to specify 'checkbox' role for the items. */
        if (v.lVal !is COM.CHILDID_SELF) {
            if (control instanceof Tree || control instanceof Table) {
                if ((control.getStyle() & DWT.CHECK) !is 0) event.detail = ACC.ROLE_CHECKBUTTON;
            }
        }
        for (int i = 0; i < accessibleControlListeners.size(); i++) {
            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
            listener.getRole(event);
        }
        int role = roleToOs(event.detail);
        COM.MoveMemory(pvarRole, new short[] { COM.VT_I4 }, 2);
        COM.MoveMemory(pvarRole + 8, new int[] { role }, 4);
        return COM.S_OK;
    }

    /* get_accSelection([out] pvarChildren)
     * Ownership of pvarChildren transfers from callee to caller so reference count on pvarChildren
     * must be incremented before returning.  The caller is responsible for releasing pvarChildren.
     */
    int get_accSelection(int /*long*/ pvarChildren) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;

        /* Get the default selection from the OS. */
        int osChild = ACC.CHILDID_NONE;
        int code = iaccessible.get_accSelection(pvarChildren);
        if (accessibleControlListeners.size() is 0) return code;
        if (code is COM.S_OK) {
            short[] pvt = new short[1];
            COM.MoveMemory(pvt, pvarChildren, 2);
            if (pvt[0] is COM.VT_I4) {
                int[] pChild = new int[1];
                COM.MoveMemory(pChild, pvarChildren + 8, 4);
                osChild = osToChildID(pChild[0]);
            } else if (pvt[0] is COM.VT_UNKNOWN) {
                osChild = ACC.CHILDID_MULTIPLE;
                /* Should get IEnumVARIANT from punkVal field, and enumerate children... */
            }
        }

        AccessibleControlEvent event = new AccessibleControlEvent(this);
        event.childID = osChild;
        for (int i = 0; i < accessibleControlListeners.size(); i++) {
            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
            listener.getSelection(event);
        }
        Accessible accessible = event.accessible;
        if (accessible !is null) {
            accessible.AddRef();
            COM.MoveMemory(pvarChildren, new short[] { COM.VT_DISPATCH }, 2);
            COM.MoveMemory(pvarChildren + 8, new int /*long*/[] { accessible.objIAccessible.getAddress() }, OS.PTR_SIZEOF);
            return COM.S_OK;
        }
        int childID = event.childID;
        if (childID is ACC.CHILDID_NONE) {
            COM.MoveMemory(pvarChildren, new short[] { COM.VT_EMPTY }, 2);
            return COM.S_FALSE;
        }
        if (childID is ACC.CHILDID_MULTIPLE) {
            AddRef();
            COM.MoveMemory(pvarChildren, new short[] { COM.VT_UNKNOWN }, 2);
            COM.MoveMemory(pvarChildren + 8, new int /*long*/[] { objIAccessible.getAddress() }, OS.PTR_SIZEOF);
            return COM.S_OK;
        }
        if (childID is ACC.CHILDID_SELF) {
            AddRef();
            COM.MoveMemory(pvarChildren, new short[] { COM.VT_DISPATCH }, 2);
            COM.MoveMemory(pvarChildren + 8, new int /*long*/[] { objIAccessible.getAddress() }, OS.PTR_SIZEOF);
            return COM.S_OK;
        }
        COM.MoveMemory(pvarChildren, new short[] { COM.VT_I4 }, 2);
        COM.MoveMemory(pvarChildren + 8, new int[] { childIDToOs(childID) }, 4);
        return COM.S_OK;
    }

    int get_accState(int /*long*/ variant, int /*long*/ pvarState) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        VARIANT v = new VARIANT();
        COM.MoveMemory(v, variant, VARIANT.sizeof);
        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;

        /* Get the default state from the OS. */
        int osState = 0;
        int code = iaccessible.get_accState(variant, pvarState);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        // TEMPORARY CODE - process tree and table even if there are no apps listening
        if (accessibleControlListeners.size() is 0 && !(control instanceof Tree || control instanceof Table)) return code;
        if (code is COM.S_OK) {
            short[] pvt = new short[1];
            COM.MoveMemory(pvt, pvarState, 2);
            if (pvt[0] is COM.VT_I4) {
                int[] pState = new int[1];
                COM.MoveMemory(pState, pvarState + 8, 4);
                osState = pState[0];
            }
        }

        AccessibleControlEvent event = new AccessibleControlEvent(this);
        event.childID = osToChildID((int)/*64*/v.lVal);
        event.detail = osToState(osState);
        // TEMPORARY CODE
        /* Currently our checkbox table and tree are emulated using state mask
         * images, so we need to determine if the item state is 'checked'. */
        if (v.lVal !is COM.CHILDID_SELF) {
            if (control instanceof Tree && (control.getStyle() & DWT.CHECK) !is 0) {
                int /*long*/ hwnd = control.handle;
                TVITEM tvItem = new TVITEM ();
                tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
                tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
                if (OS.COMCTL32_MAJOR >= 6) {
                    tvItem.hItem = OS.SendMessage (hwnd, OS.TVM_MAPACCIDTOHTREEITEM, v.lVal, 0);
                } else {
                    tvItem.hItem = v.lVal;
                }
                int /*long*/ result = OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
                bool checked = (result !is 0) && (((tvItem.state >> 12) & 1) is 0);
                if (checked) event.detail |= ACC.STATE_CHECKED;
            } else if (control instanceof Table && (control.getStyle() & DWT.CHECK) !is 0) {
                Table table = (Table) control;
                TableItem item = table.getItem(event.childID);
                if (item !is null) {
                    if (item.getChecked()) event.detail |= ACC.STATE_CHECKED;
                }
            }
        }
        for (int i = 0; i < accessibleControlListeners.size(); i++) {
            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
            listener.getState(event);
        }
        int state = stateToOs(event.detail);
        COM.MoveMemory(pvarState, new short[] { COM.VT_I4 }, 2);
        COM.MoveMemory(pvarState + 8, new int[] { state }, 4);
        return COM.S_OK;
    }

    int get_accValue(int /*long*/ variant, int /*long*/ pszValue) {
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        VARIANT v = new VARIANT();
        COM.MoveMemory(v, variant, VARIANT.sizeof);
        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;

        /* Get the default value string from the OS. */
        String osValue = null;
        int code = iaccessible.get_accValue(variant, pszValue);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        if (accessibleControlListeners.size() is 0) return code;
        if (code is COM.S_OK) {
            int /*long*/[] pValue = new int /*long*/[1];
            COM.MoveMemory(pValue, pszValue, OS.PTR_SIZEOF);
            int size = COM.SysStringByteLen(pValue[0]);
            if (size > 0) {
                char[] buffer = new char[(size + 1) /2];
                COM.MoveMemory(buffer, pValue[0], size);
                osValue = new String(buffer);
            }
        }

        AccessibleControlEvent event = new AccessibleControlEvent(this);
        event.childID = osToChildID((int)/*64*/v.lVal);
        event.result = osValue;
        for (int i = 0; i < accessibleControlListeners.size(); i++) {
            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
            listener.getValue(event);
        }
        if (event.result is null) return code;
        char[] data = (event.result + "\0").toCharArray();
        int /*long*/ ptr = COM.SysAllocString(data);
        COM.MoveMemory(pszValue, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
        return COM.S_OK;
    }

    int put_accName(int /*long*/ variant, int /*long*/ szName) {
        // MSAA: this method is no longer supported
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        // We don't implement this. Forward to the proxy.
        int code = iaccessible.put_accName(variant, szName);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        return code;
    }

    int put_accValue(int /*long*/ variant, int /*long*/ szValue) {
        // MSAA: this method is typically only used for edit controls
        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
        // We don't implement this. Forward to the proxy.
        int code = iaccessible.put_accValue(variant, szValue);
        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
        return code;
    }

    /* IEnumVARIANT methods: Next, Skip, Reset, Clone */
    /* Retrieve the next celt items in the enumeration sequence.
     * If there are fewer than the requested number of elements left
     * in the sequence, retrieve the remaining elements.
     * The number of elements actually retrieved is returned in pceltFetched
     * (unless the caller passed in NULL for that parameter).
     */

     /* Next([in] celt, [out] rgvar, [in, out] pceltFetched)
     * Ownership of rgvar transfers from callee to caller so reference count on rgvar
     * must be incremented before returning.  The caller is responsible for releasing rgvar.
     */
    int Next(int celt, int /*long*/ rgvar, int /*long*/ pceltFetched) {
        /* If there are no listeners, query the proxy for
         * its IEnumVariant, and get the Next items from it.
         */
        if (accessibleControlListeners.size() is 0) {
            int /*long*/[] ppvObject = new int /*long*/[1];
            int code = iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
            if (code !is COM.S_OK) return code;
            IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
            int[] celtFetched = new int[1];
            code = ienumvariant.Next(celt, rgvar, celtFetched);
            ienumvariant.Release();
            COM.MoveMemory(pceltFetched, celtFetched, 4);
            return code;
        }

        if (rgvar is 0) return COM.E_INVALIDARG;
        if (pceltFetched is 0 && celt !is 1) return COM.E_INVALIDARG;
        if (enumIndex is 0) {
            AccessibleControlEvent event = new AccessibleControlEvent(this);
            event.childID = ACC.CHILDID_SELF;
            for (int i = 0; i < accessibleControlListeners.size(); i++) {
                AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
                listener.getChildren(event);
            }
            variants = event.children;
        }
        Object[] nextItems = null;
        if (variants !is null && celt >= 1) {
            int endIndex = enumIndex + celt - 1;
            if (endIndex > (variants.length - 1)) endIndex = variants.length - 1;
            if (enumIndex <= endIndex) {
                nextItems = new Object[endIndex - enumIndex + 1];
                for (int i = 0; i < nextItems.length; i++) {
                    Object child = variants[enumIndex];
                    if (child instanceof Integer) {
                        nextItems[i] = new Integer(childIDToOs(((Integer)child).intValue()));
                    } else {
                        nextItems[i] = child;
                    }
                    enumIndex++;
                }
            }
        }
        if (nextItems !is null) {
            for (int i = 0; i < nextItems.length; i++) {
                Object nextItem = nextItems[i];
                if (nextItem instanceof Integer) {
                    int item = ((Integer) nextItem).intValue();
                    COM.MoveMemory(rgvar + i * 16, new short[] { COM.VT_I4 }, 2);
                    COM.MoveMemory(rgvar + i * 16 + 8, new int[] { item }, 4);
                } else {
                    Accessible accessible = (Accessible) nextItem;
                    accessible.AddRef();
                    COM.MoveMemory(rgvar + i * 16, new short[] { COM.VT_DISPATCH }, 2);
                    COM.MoveMemory(rgvar + i * 16 + 8, new int /*long*/[] { accessible.objIAccessible.getAddress() }, OS.PTR_SIZEOF);
                }
            }
            if (pceltFetched !is 0)
                COM.MoveMemory(pceltFetched, new int[] {nextItems.length}, 4);
            if (nextItems.length is celt) return COM.S_OK;
        } else {
            if (pceltFetched !is 0)
                COM.MoveMemory(pceltFetched, new int[] {0}, 4);
        }
        return COM.S_FALSE;
    }

    /* Skip over the specified number of elements in the enumeration sequence. */
    int Skip(int celt) {
        /* If there are no listeners, query the proxy
         * for its IEnumVariant, and tell it to Skip.
         */
        if (accessibleControlListeners.size() is 0) {
            int /*long*/[] ppvObject = new int /*long*/[1];
            int code = iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
            if (code !is COM.S_OK) return code;
            IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
            code = ienumvariant.Skip(celt);
            ienumvariant.Release();
            return code;
        }

        if (celt < 1 ) return COM.E_INVALIDARG;
        enumIndex += celt;
        if (enumIndex > (variants.length - 1)) {
            enumIndex = variants.length - 1;
            return COM.S_FALSE;
        }
        return COM.S_OK;
    }

    /* Reset the enumeration sequence to the beginning. */
    int Reset() {
        /* If there are no listeners, query the proxy
         * for its IEnumVariant, and tell it to Reset.
         */
        if (accessibleControlListeners.size() is 0) {
            int /*long*/[] ppvObject = new int /*long*/[1];
            int code = (int)/*64*/iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
            if (code !is COM.S_OK) return code;
            IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
            code = ienumvariant.Reset();
            ienumvariant.Release();
            return code;
        }

        enumIndex = 0;
        return COM.S_OK;
    }

     /* Clone([out] ppEnum)
     * Ownership of ppEnum transfers from callee to caller so reference count on ppEnum
     * must be incremented before returning.  The caller is responsible for releasing ppEnum.
     */
    int Clone(int /*long*/ ppEnum) {
        /* If there are no listeners, query the proxy for
         * its IEnumVariant, and get the Clone from it.
         */
        if (accessibleControlListeners.size() is 0) {
            int /*long*/[] ppvObject = new int /*long*/[1];
            int code = iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
            if (code !is COM.S_OK) return code;
            IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
            int[] pEnum = new int[1];
            code = ienumvariant.Clone(pEnum);
            ienumvariant.Release();
            COM.MoveMemory(ppEnum, pEnum, 4);
            return code;
        }

        if (ppEnum is 0) return COM.E_INVALIDARG;
        COM.MoveMemory(ppEnum, new int /*long*/[] { objIEnumVARIANT.getAddress()}, OS.PTR_SIZEOF);
        AddRef();
        return COM.S_OK;
    }

    int childIDToOs(int childID) {
        if (childID is ACC.CHILDID_SELF) return COM.CHILDID_SELF;
        /*
        * Feature of Windows:
        * In Windows XP, tree item ids are 1-based indices. Previous versions
        * of Windows use the tree item handle for the accessible child ID.
        * For backward compatibility, we still take a handle childID for tree
        * items on XP. All other childIDs are 1-based indices.
        */
        if (!(control instanceof Tree)) return childID + 1;
        if (OS.COMCTL32_MAJOR < 6) return childID;
        return (int)/*64*/OS.SendMessage (control.handle, OS.TVM_MAPHTREEITEMTOACCID, childID, 0);
    }

    int osToChildID(int osChildID) {
        if (osChildID is COM.CHILDID_SELF) return ACC.CHILDID_SELF;
        /*
        * Feature of Windows:
        * In Windows XP, tree item ids are 1-based indices. Previous versions
        * of Windows use the tree item handle for the accessible child ID.
        * For backward compatibility, we still take a handle childID for tree
        * items on XP. All other childIDs are 1-based indices.
        */
        if (!(control instanceof Tree)) return osChildID - 1;
        if (OS.COMCTL32_MAJOR < 6) return osChildID;
        return (int)/*64*/OS.SendMessage (control.handle, OS.TVM_MAPACCIDTOHTREEITEM, osChildID, 0);
    }

    int stateToOs(int state) {
        int osState = 0;
        if ((state & ACC.STATE_SELECTED) !is 0) osState |= COM.STATE_SYSTEM_SELECTED;
        if ((state & ACC.STATE_SELECTABLE) !is 0) osState |= COM.STATE_SYSTEM_SELECTABLE;
        if ((state & ACC.STATE_MULTISELECTABLE) !is 0) osState |= COM.STATE_SYSTEM_MULTISELECTABLE;
        if ((state & ACC.STATE_FOCUSED) !is 0) osState |= COM.STATE_SYSTEM_FOCUSED;
        if ((state & ACC.STATE_FOCUSABLE) !is 0) osState |= COM.STATE_SYSTEM_FOCUSABLE;
        if ((state & ACC.STATE_PRESSED) !is 0) osState |= COM.STATE_SYSTEM_PRESSED;
        if ((state & ACC.STATE_CHECKED) !is 0) osState |= COM.STATE_SYSTEM_CHECKED;
        if ((state & ACC.STATE_EXPANDED) !is 0) osState |= COM.STATE_SYSTEM_EXPANDED;
        if ((state & ACC.STATE_COLLAPSED) !is 0) osState |= COM.STATE_SYSTEM_COLLAPSED;
        if ((state & ACC.STATE_HOTTRACKED) !is 0) osState |= COM.STATE_SYSTEM_HOTTRACKED;
        if ((state & ACC.STATE_BUSY) !is 0) osState |= COM.STATE_SYSTEM_BUSY;
        if ((state & ACC.STATE_READONLY) !is 0) osState |= COM.STATE_SYSTEM_READONLY;
        if ((state & ACC.STATE_INVISIBLE) !is 0) osState |= COM.STATE_SYSTEM_INVISIBLE;
        if ((state & ACC.STATE_OFFSCREEN) !is 0) osState |= COM.STATE_SYSTEM_OFFSCREEN;
        if ((state & ACC.STATE_SIZEABLE) !is 0) osState |= COM.STATE_SYSTEM_SIZEABLE;
        if ((state & ACC.STATE_LINKED) !is 0) osState |= COM.STATE_SYSTEM_LINKED;
        return osState;
    }

    int osToState(int osState) {
        int state = ACC.STATE_NORMAL;
        if ((osState & COM.STATE_SYSTEM_SELECTED) !is 0) state |= ACC.STATE_SELECTED;
        if ((osState & COM.STATE_SYSTEM_SELECTABLE) !is 0) state |= ACC.STATE_SELECTABLE;
        if ((osState & COM.STATE_SYSTEM_MULTISELECTABLE) !is 0) state |= ACC.STATE_MULTISELECTABLE;
        if ((osState & COM.STATE_SYSTEM_FOCUSED) !is 0) state |= ACC.STATE_FOCUSED;
        if ((osState & COM.STATE_SYSTEM_FOCUSABLE) !is 0) state |= ACC.STATE_FOCUSABLE;
        if ((osState & COM.STATE_SYSTEM_PRESSED) !is 0) state |= ACC.STATE_PRESSED;
        if ((osState & COM.STATE_SYSTEM_CHECKED) !is 0) state |= ACC.STATE_CHECKED;
        if ((osState & COM.STATE_SYSTEM_EXPANDED) !is 0) state |= ACC.STATE_EXPANDED;
        if ((osState & COM.STATE_SYSTEM_COLLAPSED) !is 0) state |= ACC.STATE_COLLAPSED;
        if ((osState & COM.STATE_SYSTEM_HOTTRACKED) !is 0) state |= ACC.STATE_HOTTRACKED;
        if ((osState & COM.STATE_SYSTEM_BUSY) !is 0) state |= ACC.STATE_BUSY;
        if ((osState & COM.STATE_SYSTEM_READONLY) !is 0) state |= ACC.STATE_READONLY;
        if ((osState & COM.STATE_SYSTEM_INVISIBLE) !is 0) state |= ACC.STATE_INVISIBLE;
        if ((osState & COM.STATE_SYSTEM_OFFSCREEN) !is 0) state |= ACC.STATE_OFFSCREEN;
        if ((osState & COM.STATE_SYSTEM_SIZEABLE) !is 0) state |= ACC.STATE_SIZEABLE;
        if ((osState & COM.STATE_SYSTEM_LINKED) !is 0) state |= ACC.STATE_LINKED;
        return state;
    }

    int roleToOs(int role) {
        switch (role) {
            case ACC.ROLE_CLIENT_AREA: return COM.ROLE_SYSTEM_CLIENT;
            case ACC.ROLE_WINDOW: return COM.ROLE_SYSTEM_WINDOW;
            case ACC.ROLE_MENUBAR: return COM.ROLE_SYSTEM_MENUBAR;
            case ACC.ROLE_MENU: return COM.ROLE_SYSTEM_MENUPOPUP;
            case ACC.ROLE_MENUITEM: return COM.ROLE_SYSTEM_MENUITEM;
            case ACC.ROLE_SEPARATOR: return COM.ROLE_SYSTEM_SEPARATOR;
            case ACC.ROLE_TOOLTIP: return COM.ROLE_SYSTEM_TOOLTIP;
            case ACC.ROLE_SCROLLBAR: return COM.ROLE_SYSTEM_SCROLLBAR;
            case ACC.ROLE_DIALOG: return COM.ROLE_SYSTEM_DIALOG;
            case ACC.ROLE_LABEL: return COM.ROLE_SYSTEM_STATICTEXT;
            case ACC.ROLE_PUSHBUTTON: return COM.ROLE_SYSTEM_PUSHBUTTON;
            case ACC.ROLE_CHECKBUTTON: return COM.ROLE_SYSTEM_CHECKBUTTON;
            case ACC.ROLE_RADIOBUTTON: return COM.ROLE_SYSTEM_RADIOBUTTON;
            case ACC.ROLE_COMBOBOX: return COM.ROLE_SYSTEM_COMBOBOX;
            case ACC.ROLE_TEXT: return COM.ROLE_SYSTEM_TEXT;
            case ACC.ROLE_TOOLBAR: return COM.ROLE_SYSTEM_TOOLBAR;
            case ACC.ROLE_LIST: return COM.ROLE_SYSTEM_LIST;
            case ACC.ROLE_LISTITEM: return COM.ROLE_SYSTEM_LISTITEM;
            case ACC.ROLE_TABLE: return COM.ROLE_SYSTEM_TABLE;
            case ACC.ROLE_TABLECELL: return COM.ROLE_SYSTEM_CELL;
            case ACC.ROLE_TABLECOLUMNHEADER: return COM.ROLE_SYSTEM_COLUMNHEADER;
            case ACC.ROLE_TABLEROWHEADER: return COM.ROLE_SYSTEM_ROWHEADER;
            case ACC.ROLE_TREE: return COM.ROLE_SYSTEM_OUTLINE;
            case ACC.ROLE_TREEITEM: return COM.ROLE_SYSTEM_OUTLINEITEM;
            case ACC.ROLE_TABFOLDER: return COM.ROLE_SYSTEM_PAGETABLIST;
            case ACC.ROLE_TABITEM: return COM.ROLE_SYSTEM_PAGETAB;
            case ACC.ROLE_PROGRESSBAR: return COM.ROLE_SYSTEM_PROGRESSBAR;
            case ACC.ROLE_SLIDER: return COM.ROLE_SYSTEM_SLIDER;
            case ACC.ROLE_LINK: return COM.ROLE_SYSTEM_LINK;
        }
        return COM.ROLE_SYSTEM_CLIENT;
    }

    int osToRole(int osRole) {
        switch (osRole) {
            case COM.ROLE_SYSTEM_CLIENT: return ACC.ROLE_CLIENT_AREA;
            case COM.ROLE_SYSTEM_WINDOW: return ACC.ROLE_WINDOW;
            case COM.ROLE_SYSTEM_MENUBAR: return ACC.ROLE_MENUBAR;
            case COM.ROLE_SYSTEM_MENUPOPUP: return ACC.ROLE_MENU;
            case COM.ROLE_SYSTEM_MENUITEM: return ACC.ROLE_MENUITEM;
            case COM.ROLE_SYSTEM_SEPARATOR: return ACC.ROLE_SEPARATOR;
            case COM.ROLE_SYSTEM_TOOLTIP: return ACC.ROLE_TOOLTIP;
            case COM.ROLE_SYSTEM_SCROLLBAR: return ACC.ROLE_SCROLLBAR;
            case COM.ROLE_SYSTEM_DIALOG: return ACC.ROLE_DIALOG;
            case COM.ROLE_SYSTEM_STATICTEXT: return ACC.ROLE_LABEL;
            case COM.ROLE_SYSTEM_PUSHBUTTON: return ACC.ROLE_PUSHBUTTON;
            case COM.ROLE_SYSTEM_CHECKBUTTON: return ACC.ROLE_CHECKBUTTON;
            case COM.ROLE_SYSTEM_RADIOBUTTON: return ACC.ROLE_RADIOBUTTON;
            case COM.ROLE_SYSTEM_COMBOBOX: return ACC.ROLE_COMBOBOX;
            case COM.ROLE_SYSTEM_TEXT: return ACC.ROLE_TEXT;
            case COM.ROLE_SYSTEM_TOOLBAR: return ACC.ROLE_TOOLBAR;
            case COM.ROLE_SYSTEM_LIST: return ACC.ROLE_LIST;
            case COM.ROLE_SYSTEM_LISTITEM: return ACC.ROLE_LISTITEM;
            case COM.ROLE_SYSTEM_TABLE: return ACC.ROLE_TABLE;
            case COM.ROLE_SYSTEM_CELL: return ACC.ROLE_TABLECELL;
            case COM.ROLE_SYSTEM_COLUMNHEADER: return ACC.ROLE_TABLECOLUMNHEADER;
            case COM.ROLE_SYSTEM_ROWHEADER: return ACC.ROLE_TABLEROWHEADER;
            case COM.ROLE_SYSTEM_OUTLINE: return ACC.ROLE_TREE;
            case COM.ROLE_SYSTEM_OUTLINEITEM: return ACC.ROLE_TREEITEM;
            case COM.ROLE_SYSTEM_PAGETABLIST: return ACC.ROLE_TABFOLDER;
            case COM.ROLE_SYSTEM_PAGETAB: return ACC.ROLE_TABITEM;
            case COM.ROLE_SYSTEM_PROGRESSBAR: return ACC.ROLE_PROGRESSBAR;
            case COM.ROLE_SYSTEM_SLIDER: return ACC.ROLE_SLIDER;
            case COM.ROLE_SYSTEM_LINK: return ACC.ROLE_LINK;
        }
        return ACC.ROLE_CLIENT_AREA;
    }

    /* checkWidget was copied from Widget, and rewritten to work in this package */
    void checkWidget () {
        if (!isValidThread ()) DWT.error (DWT.ERROR_THREAD_INVALID_ACCESS);
        if (control.isDisposed ()) DWT.error (DWT.ERROR_WIDGET_DISPOSED);
    }

    /* isValidThread was copied from Widget, and rewritten to work in this package */
    bool isValidThread () {
        return control.getDisplay ().getThread () is Thread.currentThread ();
    }
}
++/