From: Piotr Caban piotr@codeweavers.com
--- -v4: Restore previously deleted class check for default window object, and add dialog window class to window object class table.
dlls/oleacc/client.c | 36 ++++++++++++++++ dlls/oleacc/main.c | 82 +++++------------------------------- dlls/oleacc/oleacc_private.h | 7 +++ dlls/oleacc/window.c | 10 +++++ 4 files changed, 63 insertions(+), 72 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 93f6b590f78..333a95dc388 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -19,6 +19,7 @@ #define COBJMACROS
#include "oleacc_private.h" +#include "commctrl.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -650,6 +651,39 @@ static const IEnumVARIANTVtbl ClientEnumVARIANTVtbl = { Client_EnumVARIANT_Clone };
+static const struct win_class_data classes[] = { + {WC_LISTBOXW, 0x10000, TRUE}, + {L"#32768", 0x10001, TRUE}, /* menu */ + {WC_BUTTONW, 0x10002, TRUE}, + {WC_STATICW, 0x10003, TRUE}, + {WC_EDITW, 0x10004, TRUE}, + {WC_COMBOBOXW, 0x10005, TRUE}, + {L"#32770", 0x10006, TRUE}, /* dialog */ + {L"#32771", 0x10007, TRUE}, /* winswitcher */ + {L"MDIClient", 0x10008, TRUE}, + {L"#32769", 0x10009, TRUE}, /* desktop */ + {WC_SCROLLBARW, 0x1000a, TRUE}, + {STATUSCLASSNAMEW, 0x1000b, TRUE}, + {TOOLBARCLASSNAMEW, 0x1000c, TRUE}, + {PROGRESS_CLASSW, 0x1000d, TRUE}, + {ANIMATE_CLASSW, 0x1000e, TRUE}, + {WC_TABCONTROLW, 0x1000f, TRUE}, + {HOTKEY_CLASSW, 0x10010, TRUE}, + {WC_HEADERW, 0x10011, TRUE}, + {TRACKBAR_CLASSW, 0x10012, TRUE}, + {WC_LISTVIEWW, 0x10013, TRUE}, + {UPDOWN_CLASSW, 0x10016, TRUE}, + {TOOLTIPS_CLASSW, 0x10018, TRUE}, + {WC_TREEVIEWW, 0x10019, TRUE}, + {MONTHCAL_CLASSW, 0, TRUE}, + {DATETIMEPICK_CLASSW, 0, TRUE}, + {WC_IPADDRESSW, 0, TRUE}, + {L"RICHEDIT", 0x1001c, TRUE}, + {L"RichEdit20A", 0, TRUE}, + {L"RichEdit20W", 0, TRUE}, + {NULL} +}; + HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) { Client *client; @@ -662,6 +696,8 @@ HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) if(!client) return E_OUTOFMEMORY;
+ find_class_data(hwnd, classes); + client->IAccessible_iface.lpVtbl = &ClientVtbl; client->IOleWindow_iface.lpVtbl = &ClientOleWindowVtbl; client->IEnumVARIANT_iface.lpVtbl = &ClientEnumVARIANTVtbl; diff --git a/dlls/oleacc/main.c b/dlls/oleacc/main.c index db9f646988c..3ce616ae0c1 100644 --- a/dlls/oleacc/main.c +++ b/dlls/oleacc/main.c @@ -20,13 +20,6 @@
#define COBJMACROS
-#include <stdarg.h> -#include "windef.h" -#include "winbase.h" -#include "ole2.h" -#include "commctrl.h" -#include "rpcproxy.h" - #include "initguid.h" #include "oleacc_private.h" #include "resource.h" @@ -37,50 +30,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(oleacc);
static const WCHAR lresult_atom_prefix[] = {'w','i','n','e','_','o','l','e','a','c','c',':'};
-typedef HRESULT (*accessible_create)(HWND, const IID*, void**); - extern HRESULT WINAPI OLEACC_DllGetClassObject(REFCLSID, REFIID, void**) DECLSPEC_HIDDEN; extern BOOL WINAPI OLEACC_DllMain(HINSTANCE, DWORD, void*) DECLSPEC_HIDDEN; extern HRESULT WINAPI OLEACC_DllRegisterServer(void) DECLSPEC_HIDDEN; extern HRESULT WINAPI OLEACC_DllUnregisterServer(void) DECLSPEC_HIDDEN;
-static struct { - const WCHAR *name; - DWORD idx; - accessible_create create_client; - accessible_create create_window; -} builtin_classes[] = { - {WC_LISTBOXW, 0x10000, NULL, NULL}, - {L"#32768", 0x10001, NULL, NULL}, /* menu */ - {WC_BUTTONW, 0x10002, NULL, NULL}, - {WC_STATICW, 0x10003, NULL, NULL}, - {WC_EDITW, 0x10004, NULL, NULL}, - {WC_COMBOBOXW, 0x10005, NULL, NULL}, - {L"#32770", 0x10006, NULL, NULL}, /* dialog */ - {L"#32771", 0x10007, NULL, NULL}, /* winswitcher */ - {L"MDIClient", 0x10008, NULL, NULL}, - {L"#32769", 0x10009, NULL, NULL}, /* desktop */ - {WC_SCROLLBARW, 0x1000a, NULL, NULL}, - {STATUSCLASSNAMEW, 0x1000b, NULL, NULL}, - {TOOLBARCLASSNAMEW, 0x1000c, NULL, NULL}, - {PROGRESS_CLASSW, 0x1000d, NULL, NULL}, - {ANIMATE_CLASSW, 0x1000e, NULL, NULL}, - {WC_TABCONTROLW, 0x1000f, NULL, NULL}, - {HOTKEY_CLASSW, 0x10010, NULL, NULL}, - {WC_HEADERW, 0x10011, NULL, NULL}, - {TRACKBAR_CLASSW, 0x10012, NULL, NULL}, - {WC_LISTVIEWW, 0x10013, NULL, NULL}, - {UPDOWN_CLASSW, 0x10016, NULL, NULL}, - {TOOLTIPS_CLASSW, 0x10018, NULL, NULL}, - {WC_TREEVIEWW, 0x10019, NULL, NULL}, - {MONTHCAL_CLASSW, 0, NULL, NULL}, - {DATETIMEPICK_CLASSW, 0, NULL, NULL}, - {WC_IPADDRESSW, 0, NULL, NULL}, - {L"RICHEDIT", 0x1001c, NULL, NULL}, - {L"RichEdit20A", 0, NULL, NULL}, - {L"RichEdit20W", 0, NULL, NULL}, -}; - static HINSTANCE oleacc_handle = 0;
int convert_child_id(VARIANT *v) @@ -94,7 +48,7 @@ int convert_child_id(VARIANT *v) } }
-static accessible_create get_builtin_accessible_obj(HWND hwnd, LONG objid) +const struct win_class_data* find_class_data(HWND hwnd, const struct win_class_data *classes) { WCHAR class_name[64]; int i, idx; @@ -103,31 +57,21 @@ static accessible_create get_builtin_accessible_obj(HWND hwnd, LONG objid) return NULL; TRACE("got window class: %s\n", debugstr_w(class_name));
- for(i=0; i<ARRAY_SIZE(builtin_classes); i++) { - if(!wcsicmp(class_name, builtin_classes[i].name)) { - accessible_create ret; - - ret = (objid==OBJID_CLIENT ? - builtin_classes[i].create_client : - builtin_classes[i].create_window); - if(!ret) + for(i=0; classes[i].name; i++) { + if(!wcsicmp(class_name, classes[i].name)) { + if(classes[i].stub) FIXME("unhandled window class: %s\n", debugstr_w(class_name)); - return ret; + return &classes[i]; } }
idx = SendMessageW(hwnd, WM_GETOBJECT, 0, OBJID_QUERYCLASSNAMEIDX); if(idx) { - for(i=0; i<ARRAY_SIZE(builtin_classes); i++) { - if(idx == builtin_classes[i].idx) { - accessible_create ret; - - ret = (objid==OBJID_CLIENT ? - builtin_classes[i].create_client : - builtin_classes[i].create_window); - if(!ret) - FIXME("unhandled class name idx: %x\n", idx); - return ret; + for(i=0; classes[i].name; i++) { + if(idx == classes[i].idx) { + if(classes[i].stub) + FIXME("unhandled window class: %s\n", debugstr_w(class_name)); + return &classes[i]; } }
@@ -140,19 +84,13 @@ static accessible_create get_builtin_accessible_obj(HWND hwnd, LONG objid) HRESULT WINAPI CreateStdAccessibleObject( HWND hwnd, LONG idObject, REFIID riidInterface, void** ppvObject ) { - accessible_create create; - TRACE("%p %d %s %p\n", hwnd, idObject, debugstr_guid( riidInterface ), ppvObject );
switch(idObject) { case OBJID_CLIENT: - create = get_builtin_accessible_obj(hwnd, idObject); - if(create) return create(hwnd, riidInterface, ppvObject); return create_client_object(hwnd, riidInterface, ppvObject); case OBJID_WINDOW: - create = get_builtin_accessible_obj(hwnd, idObject); - if(create) return create(hwnd, riidInterface, ppvObject); return create_window_object(hwnd, riidInterface, ppvObject); default: FIXME("unhandled object id: %d\n", idObject); diff --git a/dlls/oleacc/oleacc_private.h b/dlls/oleacc/oleacc_private.h index 32561ef3d2f..64926b6abb7 100644 --- a/dlls/oleacc/oleacc_private.h +++ b/dlls/oleacc/oleacc_private.h @@ -18,6 +18,13 @@
#include "oleacc_classes.h"
+struct win_class_data { + const WCHAR *name; + DWORD idx; + BOOL stub; +}; +const struct win_class_data* find_class_data(HWND, const struct win_class_data*) DECLSPEC_HIDDEN; + HRESULT create_client_object(HWND, const IID*, void**) DECLSPEC_HIDDEN; HRESULT create_window_object(HWND, const IID*, void**) DECLSPEC_HIDDEN; HRESULT get_accpropservices_factory(REFIID, void**) DECLSPEC_HIDDEN; diff --git a/dlls/oleacc/window.c b/dlls/oleacc/window.c index 387ed4bc9f7..75e5d592699 100644 --- a/dlls/oleacc/window.c +++ b/dlls/oleacc/window.c @@ -19,6 +19,7 @@ #define COBJMACROS
#include "oleacc_private.h" +#include "commctrl.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -416,6 +417,13 @@ static const IEnumVARIANTVtbl WindowEnumVARIANTVtbl = { Window_EnumVARIANT_Clone };
+static const struct win_class_data classes[] = { + {WC_LISTBOXW, 0x10000, TRUE}, + {L"#32768", 0x10001, TRUE}, /* menu */ + {L"#32770", 0x10006, TRUE}, /* dialog */ + {NULL} +}; + HRESULT create_window_object(HWND hwnd, const IID *iid, void **obj) { Window *window; @@ -428,6 +436,8 @@ HRESULT create_window_object(HWND hwnd, const IID *iid, void **obj) if(!window) return E_OUTOFMEMORY;
+ find_class_data(hwnd, classes); + window->IAccessible_iface.lpVtbl = &WindowVtbl; window->IOleWindow_iface.lpVtbl = &WindowOleWindowVtbl; window->IEnumVARIANT_iface.lpVtbl = &WindowEnumVARIANTVtbl;
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 32 ++++++++++++++++++++++++++++---- dlls/oleacc/oleacc_private.h | 1 + 2 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 333a95dc388..1f24daf1a47 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -26,6 +26,7 @@
WINE_DEFAULT_DEBUG_CHANNEL(oleacc);
+typedef struct win_class_vtbl win_class_vtbl; typedef struct { IAccessible IAccessible_iface; IOleWindow IOleWindow_iface; @@ -35,8 +36,15 @@ typedef struct {
HWND hwnd; HWND enum_pos; + INT role; + + const win_class_vtbl *vtbl; } Client;
+struct win_class_vtbl { + void (*init)(Client*); +}; + static inline Client* impl_from_Client(IAccessible *iface) { return CONTAINING_RECORD(iface, Client, IAccessible_iface); @@ -221,7 +229,7 @@ static HRESULT WINAPI Client_get_accRole(IAccessible *iface, VARIANT varID, VARI }
V_VT(pvarRole) = VT_I4; - V_I4(pvarRole) = ROLE_SYSTEM_CLIENT; + V_I4(pvarRole) = This->role; return S_OK; }
@@ -651,12 +659,21 @@ static const IEnumVARIANTVtbl ClientEnumVARIANTVtbl = { Client_EnumVARIANT_Clone };
+static void edit_init(Client *client) +{ + client->role = ROLE_SYSTEM_TEXT; +} + +static const win_class_vtbl edit_vtbl = { + edit_init, +}; + static const struct win_class_data classes[] = { {WC_LISTBOXW, 0x10000, TRUE}, {L"#32768", 0x10001, TRUE}, /* menu */ {WC_BUTTONW, 0x10002, TRUE}, {WC_STATICW, 0x10003, TRUE}, - {WC_EDITW, 0x10004, TRUE}, + {WC_EDITW, 0x10004, FALSE, &edit_vtbl}, {WC_COMBOBOXW, 0x10005, TRUE}, {L"#32770", 0x10006, TRUE}, /* dialog */ {L"#32771", 0x10007, TRUE}, /* winswitcher */ @@ -686,8 +703,9 @@ static const struct win_class_data classes[] = {
HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) { + const struct win_class_data *data; Client *client; - HRESULT hres; + HRESULT hres = S_OK;
if(!IsWindow(hwnd)) return E_FAIL; @@ -696,7 +714,7 @@ HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) if(!client) return E_OUTOFMEMORY;
- find_class_data(hwnd, classes); + data = find_class_data(hwnd, classes);
client->IAccessible_iface.lpVtbl = &ClientVtbl; client->IOleWindow_iface.lpVtbl = &ClientOleWindowVtbl; @@ -704,6 +722,12 @@ HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) client->ref = 1; client->hwnd = hwnd; client->enum_pos = 0; + client->role = ROLE_SYSTEM_CLIENT; + + if(data) + client->vtbl = data->vtbl; + if(client->vtbl && client->vtbl->init) + client->vtbl->init(client);
hres = IAccessible_QueryInterface(&client->IAccessible_iface, iid, obj); IAccessible_Release(&client->IAccessible_iface); diff --git a/dlls/oleacc/oleacc_private.h b/dlls/oleacc/oleacc_private.h index 64926b6abb7..a01fc3615fe 100644 --- a/dlls/oleacc/oleacc_private.h +++ b/dlls/oleacc/oleacc_private.h @@ -22,6 +22,7 @@ struct win_class_data { const WCHAR *name; DWORD idx; BOOL stub; + const void *vtbl; }; const struct win_class_data* find_class_data(HWND, const struct win_class_data*) DECLSPEC_HIDDEN;
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 60 +++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 15 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 1f24daf1a47..2a8c58d79a4 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -18,6 +18,7 @@
#define COBJMACROS
+#include <assert.h> #include "oleacc_private.h" #include "commctrl.h"
@@ -43,6 +44,7 @@ typedef struct {
struct win_class_vtbl { void (*init)(Client*); + HRESULT (*get_state)(Client*, VARIANT, VARIANT*); };
static inline Client* impl_from_Client(IAccessible *iface) @@ -233,36 +235,44 @@ static HRESULT WINAPI Client_get_accRole(IAccessible *iface, VARIANT varID, VARI return S_OK; }
-static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) +static HRESULT client_get_state(Client *client, VARIANT id, VARIANT *state) { - Client *This = impl_from_Client(iface); GUITHREADINFO info; LONG style;
- TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pvarState); - - if(convert_child_id(&varID) != CHILDID_SELF) { - V_VT(pvarState) = VT_EMPTY; + if(convert_child_id(&id) != CHILDID_SELF) { + V_VT(state) = VT_EMPTY; return E_INVALIDARG; }
- V_VT(pvarState) = VT_I4; - V_I4(pvarState) = 0; + V_VT(state) = VT_I4; + V_I4(state) = 0;
- style = GetWindowLongW(This->hwnd, GWL_STYLE); + style = GetWindowLongW(client->hwnd, GWL_STYLE); if(style & WS_DISABLED) - V_I4(pvarState) |= STATE_SYSTEM_UNAVAILABLE; - else if(IsWindow(This->hwnd)) - V_I4(pvarState) |= STATE_SYSTEM_FOCUSABLE; + V_I4(state) |= STATE_SYSTEM_UNAVAILABLE; + else if(IsWindow(client->hwnd)) + V_I4(state) |= STATE_SYSTEM_FOCUSABLE;
info.cbSize = sizeof(info); - if(GetGUIThreadInfo(0, &info) && info.hwndFocus == This->hwnd) - V_I4(pvarState) |= STATE_SYSTEM_FOCUSED; + if(GetGUIThreadInfo(0, &info) && info.hwndFocus == client->hwnd) + V_I4(state) |= STATE_SYSTEM_FOCUSED; if(!(style & WS_VISIBLE)) - V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE; + V_I4(state) |= STATE_SYSTEM_INVISIBLE; return S_OK; }
+static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT id, VARIANT *state) +{ + Client *This = impl_from_Client(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&id), state); + + if(This->vtbl && This->vtbl->get_state) + return This->vtbl->get_state(This, id, state); + return client_get_state(This, id, state); +} + static HRESULT WINAPI Client_get_accHelp(IAccessible *iface, VARIANT varID, BSTR *pszHelp) { Client *This = impl_from_Client(iface); @@ -664,8 +674,28 @@ static void edit_init(Client *client) client->role = ROLE_SYSTEM_TEXT; }
+static HRESULT edit_get_state(Client *client, VARIANT id, VARIANT *state) +{ + HRESULT hres; + LONG style; + + hres = client_get_state(client, id, state); + if(FAILED(hres)) + return hres; + + assert(V_VT(state) == VT_I4); + + style = GetWindowLongW(client->hwnd, GWL_STYLE); + if(style & ES_READONLY) + V_I4(state) |= STATE_SYSTEM_READONLY; + if(style & ES_PASSWORD) + V_I4(state) |= STATE_SYSTEM_PROTECTED; + return S_OK; +} + static const win_class_vtbl edit_vtbl = { edit_init, + edit_get_state, };
static const struct win_class_data classes[] = {
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 92 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 18 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 2a8c58d79a4..c01f060f3a5 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -45,8 +45,28 @@ typedef struct { struct win_class_vtbl { void (*init)(Client*); HRESULT (*get_state)(Client*, VARIANT, VARIANT*); + HRESULT (*get_name)(Client*, VARIANT, BSTR*); };
+static HRESULT acc_client_get_name_str(WCHAR *name, UINT len, BSTR *name_out) +{ + UINT i; + + if(!len) + return S_FALSE; + + for(i=0; i<len; i++) { + if(name[i] == '&') { + len--; + memmove(name+i, name+i+1, (len-i)*sizeof(WCHAR)); + break; + } + } + + *name_out = SysAllocStringLen(name, len); + return *name_out ? S_OK : E_OUTOFMEMORY; +} + static inline Client* impl_from_Client(IAccessible *iface) { return CONTAINING_RECORD(iface, Client, IAccessible_iface); @@ -166,32 +186,23 @@ static HRESULT WINAPI Client_get_accChild(IAccessible *iface, return E_INVALIDARG; }
-static HRESULT WINAPI Client_get_accName(IAccessible *iface, VARIANT varID, BSTR *pszName) +static HRESULT WINAPI Client_get_accName(IAccessible *iface, VARIANT id, BSTR *name_out) { Client *This = impl_from_Client(iface); WCHAR name[1024]; - UINT i, len; + UINT len;
- TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszName); + TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&id), name_out);
- *pszName = NULL; - if(convert_child_id(&varID) != CHILDID_SELF || !IsWindow(This->hwnd)) + *name_out = NULL; + if (This->vtbl && This->vtbl->get_name) + return This->vtbl->get_name(This, id, name_out); + + if(convert_child_id(&id) != CHILDID_SELF || !IsWindow(This->hwnd)) return E_INVALIDARG;
len = SendMessageW(This->hwnd, WM_GETTEXT, ARRAY_SIZE(name), (LPARAM)name); - if(!len) - return S_FALSE; - - for(i=0; i<len; i++) { - if(name[i] == '&') { - len--; - memmove(name+i, name+i+1, (len-i)*sizeof(WCHAR)); - break; - } - } - - *pszName = SysAllocStringLen(name, len); - return *pszName ? S_OK : E_OUTOFMEMORY; + return acc_client_get_name_str(name, len, name_out); }
static HRESULT WINAPI Client_get_accValue(IAccessible *iface, VARIANT varID, BSTR *pszValue) @@ -693,9 +704,54 @@ static HRESULT edit_get_state(Client *client, VARIANT id, VARIANT *state) return S_OK; }
+/* + * Edit control objects have their name property defined by the first static + * text control preceding them in the order of window creation. If one is not + * found, the edit has no name property. In the case of the keyboard shortcut + * property, the first preceding visible static text control is used. + */ +static HWND acc_edit_find_label(HWND hwnd, BOOL visible) +{ + HWND cur; + + for(cur = hwnd; cur; cur = GetWindow(cur, GW_HWNDPREV)) { + WCHAR class_name[64]; + + if(!RealGetWindowClassW(cur, class_name, ARRAY_SIZE(class_name))) + continue; + + if(!wcsicmp(class_name, WC_STATICW)) { + if (visible && !(GetWindowLongW(cur, GWL_STYLE) & WS_VISIBLE)) + continue; + else + break; + } + } + + return cur; +} + +static HRESULT edit_get_name(Client *client, VARIANT id, BSTR *name_out) +{ + WCHAR name[1024]; + HWND label; + UINT len; + + if(convert_child_id(&id) != CHILDID_SELF || !IsWindow(client->hwnd)) + return E_INVALIDARG; + + label = acc_edit_find_label(client->hwnd, FALSE); + if(!label) + return S_FALSE; + + len = SendMessageW(label, WM_GETTEXT, ARRAY_SIZE(name), (LPARAM)name); + return acc_client_get_name_str(name, len, name_out); +} + static const win_class_vtbl edit_vtbl = { edit_init, edit_get_state, + edit_get_name, };
static const struct win_class_data classes[] = {
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 66 ++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 18 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index c01f060f3a5..ed7dd86b13b 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -46,6 +46,7 @@ struct win_class_vtbl { void (*init)(Client*); HRESULT (*get_state)(Client*, VARIANT, VARIANT*); HRESULT (*get_name)(Client*, VARIANT, BSTR*); + HRESULT (*get_kbd_shortcut)(Client*, VARIANT, BSTR*); };
static HRESULT acc_client_get_name_str(WCHAR *name, UINT len, BSTR *name_out) @@ -67,6 +68,26 @@ static HRESULT acc_client_get_name_str(WCHAR *name, UINT len, BSTR *name_out) return *name_out ? S_OK : E_OUTOFMEMORY; }
+static HRESULT acc_client_get_kbd_shortcut_str(WCHAR *name, UINT len, + BSTR *kbd_shortcut_out) +{ + UINT i; + + for(i=0; i<len; i++) { + if(name[i] == '&') + break; + } + if(i+1 >= len) + return S_FALSE; + + *kbd_shortcut_out = SysAllocString(L"Alt+!"); + if(!*kbd_shortcut_out) + return E_OUTOFMEMORY; + + (*kbd_shortcut_out)[4] = name[i+1]; + return S_OK; +} + static inline Client* impl_from_Client(IAccessible *iface) { return CONTAINING_RECORD(iface, Client, IAccessible_iface); @@ -305,32 +326,23 @@ static HRESULT WINAPI Client_get_accHelpTopic(IAccessible *iface, }
static HRESULT WINAPI Client_get_accKeyboardShortcut(IAccessible *iface, - VARIANT varID, BSTR *pszKeyboardShortcut) + VARIANT id, BSTR *kbd_shortcut_out) { Client *This = impl_from_Client(iface); WCHAR name[1024]; - UINT i, len; + UINT len;
- TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszKeyboardShortcut); + TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&id), kbd_shortcut_out);
- *pszKeyboardShortcut = NULL; - if(convert_child_id(&varID) != CHILDID_SELF) + *kbd_shortcut_out = NULL; + if (This->vtbl && This->vtbl->get_kbd_shortcut) + return This->vtbl->get_kbd_shortcut(This, id, kbd_shortcut_out); + + if(convert_child_id(&id) != CHILDID_SELF) return E_INVALIDARG;
len = SendMessageW(This->hwnd, WM_GETTEXT, ARRAY_SIZE(name), (LPARAM)name); - for(i=0; i<len; i++) { - if(name[i] == '&') - break; - } - if(i+1 >= len) - return S_FALSE; - - *pszKeyboardShortcut = SysAllocString(L"Alt+!"); - if(!*pszKeyboardShortcut) - return E_OUTOFMEMORY; - - (*pszKeyboardShortcut)[4] = name[i+1]; - return S_OK; + return acc_client_get_kbd_shortcut_str(name, len, kbd_shortcut_out); }
static HRESULT WINAPI Client_get_accFocus(IAccessible *iface, VARIANT *focus) @@ -748,10 +760,28 @@ static HRESULT edit_get_name(Client *client, VARIANT id, BSTR *name_out) return acc_client_get_name_str(name, len, name_out); }
+static HRESULT edit_get_kbd_shortcut(Client *client, VARIANT id, BSTR *kbd_shortcut_out) +{ + WCHAR name[1024]; + HWND label; + UINT len; + + if(convert_child_id(&id) != CHILDID_SELF) + return E_INVALIDARG; + + label = acc_edit_find_label(client->hwnd, TRUE); + if(!label) + return S_FALSE; + + len = SendMessageW(label, WM_GETTEXT, ARRAY_SIZE(name), (LPARAM)name); + return acc_client_get_kbd_shortcut_str(name, len, kbd_shortcut_out); +} + static const win_class_vtbl edit_vtbl = { edit_init, edit_get_state, edit_get_name, + edit_get_kbd_shortcut, };
static const struct win_class_data classes[] = {
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index ed7dd86b13b..636e7a1cfea 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -47,6 +47,7 @@ struct win_class_vtbl { HRESULT (*get_state)(Client*, VARIANT, VARIANT*); HRESULT (*get_name)(Client*, VARIANT, BSTR*); HRESULT (*get_kbd_shortcut)(Client*, VARIANT, BSTR*); + HRESULT (*get_value)(Client*, VARIANT, BSTR*); };
static HRESULT acc_client_get_name_str(WCHAR *name, UINT len, BSTR *name_out) @@ -226,14 +227,17 @@ static HRESULT WINAPI Client_get_accName(IAccessible *iface, VARIANT id, BSTR *n return acc_client_get_name_str(name, len, name_out); }
-static HRESULT WINAPI Client_get_accValue(IAccessible *iface, VARIANT varID, BSTR *pszValue) +static HRESULT WINAPI Client_get_accValue(IAccessible *iface, VARIANT id, BSTR *value_out) { Client *This = impl_from_Client(iface);
- TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszValue); + TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&id), value_out);
- *pszValue = NULL; - if(convert_child_id(&varID) != CHILDID_SELF) + *value_out = NULL; + if (This->vtbl && This->vtbl->get_value) + return This->vtbl->get_value(This, id, value_out); + + if(convert_child_id(&id) != CHILDID_SELF) return E_INVALIDARG; return S_FALSE; } @@ -777,11 +781,33 @@ static HRESULT edit_get_kbd_shortcut(Client *client, VARIANT id, BSTR *kbd_short return acc_client_get_kbd_shortcut_str(name, len, kbd_shortcut_out); }
+static HRESULT edit_get_value(Client *client, VARIANT id, BSTR *value_out) +{ + WCHAR *buf; + UINT len; + + if (convert_child_id(&id) != CHILDID_SELF) return E_INVALIDARG; + + if (GetWindowLongW(client->hwnd, GWL_STYLE) & ES_PASSWORD) + return E_ACCESSDENIED; + + len = SendMessageW(client->hwnd, WM_GETTEXTLENGTH, 0, 0); + buf = heap_alloc_zero((len + 1) * sizeof(*buf)); + if (!buf) return E_OUTOFMEMORY; + + SendMessageW(client->hwnd, WM_GETTEXT, len + 1, (LPARAM)buf); + *value_out = SysAllocString(buf); + heap_free(buf); + + return S_OK; +} + static const win_class_vtbl edit_vtbl = { edit_init, edit_get_state, edit_get_name, edit_get_kbd_shortcut, + edit_get_value, };
static const struct win_class_data classes[] = {
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 636e7a1cfea..7b9d6786bb6 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -48,6 +48,7 @@ struct win_class_vtbl { HRESULT (*get_name)(Client*, VARIANT, BSTR*); HRESULT (*get_kbd_shortcut)(Client*, VARIANT, BSTR*); HRESULT (*get_value)(Client*, VARIANT, BSTR*); + HRESULT (*put_value)(Client*, VARIANT, BSTR); };
static HRESULT acc_client_get_name_str(WCHAR *name, UINT len, BSTR *name_out) @@ -488,10 +489,15 @@ static HRESULT WINAPI Client_put_accName(IAccessible *iface, VARIANT varID, BSTR return E_NOTIMPL; }
-static HRESULT WINAPI Client_put_accValue(IAccessible *iface, VARIANT varID, BSTR pszValue) +static HRESULT WINAPI Client_put_accValue(IAccessible *iface, VARIANT id, BSTR value) { Client *This = impl_from_Client(iface); - FIXME("(%p)->(%s %s)\n", This, debugstr_variant(&varID), debugstr_w(pszValue)); + + if (This->vtbl && This->vtbl->put_value) + return This->vtbl->put_value(This, id, value); + + FIXME("(%p)->(%s %s)\n", This, debugstr_variant(&id), debugstr_w(value)); + return E_NOTIMPL; }
@@ -802,12 +808,25 @@ static HRESULT edit_get_value(Client *client, VARIANT id, BSTR *value_out) return S_OK; }
+static HRESULT edit_put_value(Client *client, VARIANT id, BSTR value) +{ + TRACE("(%p, %s, %s)\n", client, debugstr_variant(&id), debugstr_w(value)); + + if(convert_child_id(&id) != CHILDID_SELF || !IsWindow(client->hwnd)) + return E_INVALIDARG; + + SendMessageW(client->hwnd, WM_SETTEXT, 0, (LPARAM)value); + + return S_OK; +} + static const win_class_vtbl edit_vtbl = { edit_init, edit_get_state, edit_get_name, edit_get_kbd_shortcut, edit_get_value, + edit_put_value, };
static const struct win_class_data classes[] = {
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/tests/main.c | 436 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 436 insertions(+)
diff --git a/dlls/oleacc/tests/main.c b/dlls/oleacc/tests/main.c index 98da7eb3ddc..629813587b5 100644 --- a/dlls/oleacc/tests/main.c +++ b/dlls/oleacc/tests/main.c @@ -1313,6 +1313,441 @@ static void test_CAccPropServices(void) IAccPropServices_Release(acc_prop_services); }
+typedef struct +{ + INT child_id; + + const WCHAR *expected_name; + HRESULT expected_name_hr; + + const WCHAR *expected_value; + HRESULT expected_value_hr; + + const WCHAR *expected_description; + HRESULT expected_description_hr; + + const WCHAR *expected_help; + HRESULT expected_help_hr; + + const WCHAR *expected_kbd_shortcut; + HRESULT expected_kbd_shortcut_hr; + + const WCHAR *expected_default_action; + HRESULT expected_default_action_hr; + + INT expected_role; + HRESULT expected_role_hr; + + INT expected_state; + HRESULT expected_state_hr; + + LONG left_offset, top_offset, width_offset, height_offset; + HRESULT expected_location_hr; + + LONG expected_child_count; + HRESULT expected_child_count_hr; + + BOOL expected_valid_child; + HRESULT expected_child_hr; + + BOOL expected_valid_parent; + HRESULT expected_parent_hr; + + INT expected_focus_vt; + INT expected_focus_cid; + HRESULT expected_focus_hr; +} acc_expected_vals; + +#define check_acc_vals( acc, vals) \ + check_acc_vals_( (acc), (vals), __LINE__) +static void check_acc_vals_(IAccessible *acc, const acc_expected_vals *vals, + int line) +{ + LONG left, top, width, height; + LONG child_count; + VARIANT vid, var; + IDispatch *disp; + HRESULT hr; + RECT rect; + HWND hwnd; + POINT pt; + BSTR str; + + V_VT(&vid) = VT_I4; + V_I4(&vid) = vals->child_id; + hr = IAccessible_get_accName(acc, vid, &str); + ok_(__FILE__, line) (hr == vals->expected_name_hr, "get_accName returned %#x, expected %#x\n", + hr, vals->expected_name_hr); + ok_(__FILE__, line) (!lstrcmpW(str, vals->expected_name), "get_accName returned %s, expected %s\n", + wine_dbgstr_w(str), wine_dbgstr_w(vals->expected_name)); + SysFreeString(str); + + hr = IAccessible_get_accValue(acc, vid, &str); + ok_(__FILE__, line) (hr == vals->expected_value_hr, "get_accValue returned %#x, expected %#x\n", + hr, vals->expected_value_hr); + ok_(__FILE__, line) (!lstrcmpW(str, vals->expected_value), "get_accValue returned %s, expected %s\n", + wine_dbgstr_w(str), wine_dbgstr_w(vals->expected_value)); + SysFreeString(str); + + hr = IAccessible_get_accDescription(acc, vid, &str); + ok_(__FILE__, line) (hr == vals->expected_description_hr, "get_accDescription returned %#x, expected %#x\n", + hr, vals->expected_description_hr); + ok_(__FILE__, line) (!lstrcmpW(str, vals->expected_description), "get_accDescription returned %s, expected %s\n", + wine_dbgstr_w(str), wine_dbgstr_w(vals->expected_description)); + SysFreeString(str); + + hr = IAccessible_get_accHelp(acc, vid, &str); + ok_(__FILE__, line) (hr == vals->expected_help_hr, "get_accHelp returned %#x, expected %#x\n", + hr, vals->expected_help_hr); + ok_(__FILE__, line) (!lstrcmpW(str, vals->expected_help), "get_accHelp returned %s, expected %s\n", + wine_dbgstr_w(str), wine_dbgstr_w(vals->expected_help)); + SysFreeString(str); + + hr = IAccessible_get_accKeyboardShortcut(acc, vid, &str); + ok_(__FILE__, line) (hr == vals->expected_kbd_shortcut_hr, "get_accKeyboardShortcut returned %#x, expected %#x\n", + hr, vals->expected_kbd_shortcut_hr); + ok_(__FILE__, line) (!lstrcmpW(str, vals->expected_kbd_shortcut), "get_accKeyboardShortcut returned %s, expected %s\n", + wine_dbgstr_w(str), wine_dbgstr_w(vals->expected_kbd_shortcut)); + SysFreeString(str); + + hr = IAccessible_get_accDefaultAction(acc, vid, &str); + ok_(__FILE__, line) (hr == vals->expected_default_action_hr, "get_accDefaultAction returned %#x, expected %#x\n", + hr, vals->expected_default_action_hr); + ok_(__FILE__, line) (!lstrcmpW(str, vals->expected_default_action), "get_accDefaultAction returned %s, expected %s\n", + wine_dbgstr_w(str), wine_dbgstr_w(vals->expected_default_action)); + SysFreeString(str); + + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = (void*)0xdeadbeef; + hr = IAccessible_get_accRole(acc, vid, &var); + ok_(__FILE__, line) (hr == vals->expected_role_hr, "get_accRole returned %#x, expected %#x\n", + hr, vals->expected_role_hr); + ok_(__FILE__, line) (V_VT(&var) == VT_I4, "V_VT(&var) returned %d, expected %d\n", V_VT(&var), VT_I4); + ok_(__FILE__, line) (V_I4(&var) == vals->expected_role, "get_accRole returned %d, expected %d\n", + V_I4(&var), vals->expected_role); + + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = (void*)0xdeadbeef; + hr = IAccessible_get_accState(acc, vid, &var); + ok_(__FILE__, line) (hr == vals->expected_state_hr, "get_accState returned %#x, expected %#x\n", + hr, vals->expected_state_hr); + ok_(__FILE__, line) (V_VT(&var) == VT_I4, "V_VT(&var) returned %d, expected %d\n", V_VT(&var), VT_I4); + ok_(__FILE__, line) (V_I4(&var) == vals->expected_state, "get_accState returned %#x, expected %#x\n", + V_I4(&var), vals->expected_state); + + hr = WindowFromAccessibleObject(acc, &hwnd); + ok_(__FILE__, line) (hr == S_OK, "got %x\n", hr); + ok_(__FILE__, line) (GetClientRect(hwnd, &rect), "GetClientRect failed\n"); + pt.x = rect.left + vals->left_offset; + pt.y = rect.top + vals->top_offset; + MapWindowPoints(hwnd, NULL, &pt, 1); + rect.left = pt.x; + rect.top = pt.y; + pt.x = rect.right + vals->width_offset; + pt.y = rect.bottom + vals->height_offset; + MapWindowPoints(hwnd, NULL, &pt, 1); + hr = IAccessible_accLocation(acc, &left, &top, &width, &height, vid); + ok_(__FILE__, line) (hr == vals->expected_location_hr, "got %x\n", hr); + ok_(__FILE__, line) (left == rect.left, "left = %d, expected %d\n", left, rect.left); + ok_(__FILE__, line) (top == rect.top, "top = %d, expected %d\n", top, rect.top); + ok_(__FILE__, line) (width == pt.x-rect.left, "width = %d, expected %d\n", width, pt.x-rect.left); + ok_(__FILE__, line) (height == pt.y-rect.top, "height = %d, expected %d\n", height, pt.y-rect.top); + + /* + * Tests beyond this point are only applicable to full accessible objects + * and not simple elements. + */ + if (vals->child_id != CHILDID_SELF) + return; + + child_count = -1; + hr = IAccessible_get_accChildCount(acc, &child_count); + ok_(__FILE__, line) (hr == vals->expected_child_count_hr, "get_accChildCount returned %#x, expected %#x\n", + hr, vals->expected_child_count_hr); + ok_(__FILE__, line) (child_count == vals->expected_child_count, "get_accChildCount returned %d, expected %#x\n", + child_count, vals->expected_child_count); + + disp = NULL; + V_VT(&var) = VT_I4; + V_I4(&var) = 1; + hr = IAccessible_get_accChild(acc, var, &disp); + ok_(__FILE__, line) (hr == vals->expected_child_hr, "get_accChild returned %#x, expected %#x\n", + hr, vals->expected_child_hr); + if (disp) + { + ok_(__FILE__, line) (vals->expected_valid_child, "get_accChild unexpectedly returned a child\n"); + IDispatch_Release(disp); + } + else + ok_(__FILE__, line) (!vals->expected_valid_child, "get_accChild expected a valid child, none was returned\n"); + + disp = NULL; + hr = IAccessible_get_accParent(acc, &disp); + ok_(__FILE__, line) (hr == vals->expected_parent_hr, "get_accParent returned %#x, expected %#x\n", + hr, vals->expected_parent_hr); + if (disp) + { + ok_(__FILE__, line) (vals->expected_valid_parent, "get_accParent unexpectedly returned a parent\n"); + IDispatch_Release(disp); + } + else + ok_(__FILE__, line) (!vals->expected_valid_parent, "get_accParent expected a valid parent, none was returned\n"); + + V_VT(&var) = VT_EMPTY; + hr = IAccessible_get_accFocus(acc, &var); + ok_(__FILE__, line) (hr == vals->expected_focus_hr, "get_accFocus returned %#x, expected %#x\n", + hr, vals->expected_focus_hr); + ok_(__FILE__, line) (V_VT(&var) == vals->expected_focus_vt, "get_accFocus returned V_VT(&var) %d, expected %d\n", + V_VT(&var), vals->expected_focus_vt); + if (V_VT(&var) == VT_I4) + { + ok_(__FILE__, line) (V_I4(&var) == vals->expected_focus_cid, "get_accFocus returned childID %d, expected %d\n", + V_I4(&var), vals->expected_focus_cid); + } +} + +static const acc_expected_vals edit_acc_vals[] = { + /* edit0, regular edit, no label. */ + { .child_id = CHILDID_SELF, + .expected_name = NULL, + .expected_name_hr = S_FALSE, + .expected_value = L"edit0-test", + .expected_value_hr = S_OK, + .expected_description = NULL, + .expected_description_hr = S_FALSE, + .expected_help = NULL, + .expected_help_hr = S_FALSE, + .expected_kbd_shortcut = NULL, + .expected_kbd_shortcut_hr = S_FALSE, + .expected_default_action = NULL, + .expected_default_action_hr = S_FALSE, + .expected_role = ROLE_SYSTEM_TEXT, + .expected_role_hr = S_OK, + .expected_state = STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY, + .expected_state_hr = S_OK, + .left_offset = 0, + .top_offset = 0, + .width_offset = 0, + .height_offset = 0, + .expected_location_hr = S_OK, + .expected_child_count = 0, + .expected_child_count_hr = S_OK, + .expected_valid_child = FALSE, + .expected_child_hr = E_INVALIDARG, + .expected_valid_parent = TRUE, + .expected_parent_hr = S_OK, + .expected_focus_vt = VT_EMPTY, + .expected_focus_cid = 0, + .expected_focus_hr = S_OK }, + /* edit1, ES_PASSWORD edit style. */ + { .child_id = CHILDID_SELF, + .expected_name = L"label0:", + .expected_name_hr = S_OK, + .expected_value = NULL, + .expected_value_hr = E_ACCESSDENIED, + .expected_description = NULL, + .expected_description_hr = S_FALSE, + .expected_help = NULL, + .expected_help_hr = S_FALSE, + .expected_kbd_shortcut = L"Alt+l", + .expected_kbd_shortcut_hr = S_OK, + .expected_default_action = NULL, + .expected_default_action_hr = S_FALSE, + .expected_role = ROLE_SYSTEM_TEXT, + .expected_role_hr = S_OK, + .expected_state = STATE_SYSTEM_PROTECTED | STATE_SYSTEM_FOCUSABLE, + .expected_state_hr = S_OK, + .left_offset = 0, + .top_offset = 0, + .width_offset = 0, + .height_offset = 0, + .expected_location_hr = S_OK, + .expected_child_count = 0, + .expected_child_count_hr = S_OK, + .expected_valid_child = FALSE, + .expected_child_hr = E_INVALIDARG, + .expected_valid_parent = TRUE, + .expected_parent_hr = S_OK, + .expected_focus_vt = VT_EMPTY, + .expected_focus_cid = 0, + .expected_focus_hr = S_OK }, + /* edit2 */ + { .child_id = CHILDID_SELF, + .expected_name = L"label1:", + .expected_name_hr = S_OK, + .expected_value = L"edit2-test\r\ntest-edit2\n", + .expected_value_hr = S_OK, + .expected_description = NULL, + .expected_description_hr = S_FALSE, + .expected_help = NULL, + .expected_help_hr = S_FALSE, + .expected_kbd_shortcut = L"Alt+e", + .expected_kbd_shortcut_hr = S_OK, + .expected_default_action = NULL, + .expected_default_action_hr = S_FALSE, + .expected_role = ROLE_SYSTEM_TEXT, + .expected_role_hr = S_OK, + .expected_state = STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED, + .expected_state_hr = S_OK, + .left_offset = 0, + .top_offset = 0, + .width_offset = 0, + .height_offset = 0, + .expected_location_hr = S_OK, + .expected_child_count = 0, + .expected_child_count_hr = S_OK, + .expected_valid_child = FALSE, + .expected_child_hr = E_INVALIDARG, + .expected_valid_parent = TRUE, + .expected_parent_hr = S_OK, + .expected_focus_vt = VT_I4, + .expected_focus_cid = CHILDID_SELF, + .expected_focus_hr = S_OK }, + /* edit3 */ + { .child_id = CHILDID_SELF, + .expected_name = L"label1:", + .expected_name_hr = S_OK, + .expected_value = L"", + .expected_value_hr = S_OK, + .expected_description = NULL, + .expected_description_hr = S_FALSE, + .expected_help = NULL, + .expected_help_hr = S_FALSE, + .expected_kbd_shortcut = L"Alt+l", + .expected_kbd_shortcut_hr = S_OK, + .expected_default_action = NULL, + .expected_default_action_hr = S_FALSE, + .expected_role = ROLE_SYSTEM_TEXT, + .expected_role_hr = S_OK, + .expected_state = STATE_SYSTEM_FOCUSABLE, + .expected_state_hr = S_OK, + .left_offset = 0, + .top_offset = 0, + .width_offset = 0, + .height_offset = 0, + .expected_location_hr = S_OK, + /* Embedded button control HWND. */ + .expected_child_count = 1, + .expected_child_count_hr = S_OK, + .expected_valid_child = FALSE, + .expected_child_hr = E_INVALIDARG, + .expected_valid_parent = TRUE, + .expected_parent_hr = S_OK, + .expected_focus_vt = VT_DISPATCH, + .expected_focus_cid = 0, + .expected_focus_hr = S_OK }, +}; + +static void test_default_edit_accessible_object(void) +{ + HWND hwnd, label0, label1, btn0, btn1; + IAccessible *acc; + HWND edit[4]; + HRESULT hr; + VARIANT v; + BSTR str; + + /* Create a window that looks like this: + * +----------------------------------------+ + * | ___________________________________ | + * | |___________________________________| | + * | (edit[0]) | + * | ____________________________ | + * | Label0: |____________________________| | + * |(label0) (edit[1]) (password) | + * | ______ _________________ | + * | Label1: |button| |multi-line edit | | + * |(label1) (btn0) |_________________| | + * | (edit[2]) | + * | __________________________________ | + * | | | | + * | | edit with embedded button | | + * | |__________________________________| | + * | (edit[3]) (btn1) | + * +----------------------------------------+ + */ + + hwnd = CreateWindowW(L"oleacc_test", L"edit_acc_test_win", WS_OVERLAPPEDWINDOW, + 0, 0, 220, 160, NULL, NULL, NULL, NULL); + ok(!!hwnd, "CreateWindow failed\n"); + + edit[0] = CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD | ES_READONLY, 5, 5, 190, 20, + hwnd, NULL, NULL, NULL); + ok(!!edit[0], "Failed to create edit[0] hwnd\n"); + + label0 = CreateWindowW(L"STATIC", L"&label0:", WS_VISIBLE | WS_CHILD, 5, 30, 55, 20, + hwnd, NULL, NULL, NULL); + ok(!!label0, "Failed to create label0 hwnd\n"); + + edit[1] = CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD | ES_PASSWORD, + 65, 30, 130, 20, hwnd, NULL, NULL, NULL); + ok(!!edit[1], "Failed to create edit[1] hwnd\n"); + + label1 = CreateWindowW(L"STATIC", L"lab&el1:", WS_VISIBLE | WS_CHILD, 5, 55, 45, 20, + hwnd, NULL, NULL, NULL); + ok(!!label1, "Failed to create label1 hwnd\n"); + + btn0 = CreateWindowW(L"BUTTON", L"but&ton0", WS_VISIBLE | WS_CHILD, 55, 55, 45, 20, + hwnd, NULL, NULL, NULL); + ok(!!btn0, "Failed to create btn0 hwnd\n"); + + edit[2] = CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD | ES_MULTILINE, + 105, 55, 90, 40, hwnd, NULL, NULL, NULL); + ok(!!edit[2], "Failed to create edit[2] hwnd\n"); + + edit[3] = CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD, 5, 100, 190, 20, + hwnd, NULL, NULL, NULL); + ok(!!edit[3], "Failed to create edit[3] hwnd\n"); + + /* Button embedded within an edit control window. */ + btn1 = CreateWindowW(L"BUTTON", L"button1", WS_VISIBLE | WS_CHILD, 100, 5, 85, 10, + edit[3], NULL, NULL, NULL); + ok(!!btn1, "Failed to create btn1 hwnd\n"); + + hr = CreateStdAccessibleObject(edit[0], OBJID_CLIENT, &IID_IAccessible, (void**)&acc); + ok(hr == S_OK, "got %x\n", hr); + V_VT(&v) = VT_I4; + V_I4(&v) = CHILDID_SELF; + str = SysAllocString(L"edit0-test"); + hr = IAccessible_put_accValue(acc, v, str); + ok(hr == S_OK, "got %x\n", hr); + SysFreeString(str); + check_acc_vals(acc, &edit_acc_vals[0]); + IAccessible_Release(acc); + + hr = CreateStdAccessibleObject(edit[1], OBJID_CLIENT, &IID_IAccessible, (void**)&acc); + ok(hr == S_OK, "got %x\n", hr); + str = SysAllocString(L"password"); + hr = IAccessible_put_accValue(acc, v, str); + ok(hr == S_OK, "got %x\n", hr); + SysFreeString(str); + check_acc_vals(acc, &edit_acc_vals[1]); + IAccessible_Release(acc); + + SetFocus(edit[2]); + hr = CreateStdAccessibleObject(edit[2], OBJID_CLIENT, &IID_IAccessible, (void**)&acc); + str = SysAllocString(L"edit2-test\r\ntest-edit2\n"); + hr = IAccessible_put_accValue(acc, v, str); + ok(hr == S_OK, "got %x\n", hr); + SysFreeString(str); + check_acc_vals(acc, &edit_acc_vals[2]); + IAccessible_Release(acc); + + /* + * Hiding a label with a keyboard shortcut makes get_accKeyboardShortcut + * on the edit no longer return the labels shortcut, however get_accName + * still returns the labels string. + */ + ShowWindow(label1, SW_HIDE); + SetFocus(btn1); + hr = CreateStdAccessibleObject(edit[3], OBJID_CLIENT, &IID_IAccessible, (void**)&acc); + ok(hr == S_OK, "got %x\n", hr); + check_acc_vals(acc, &edit_acc_vals[3]); + IAccessible_Release(acc); + + DestroyWindow(hwnd); +} + START_TEST(main) { int argc; @@ -1351,6 +1786,7 @@ START_TEST(main) test_default_client_accessible_object(); test_AccessibleChildren(&Accessible); test_AccessibleObjectFromEvent(); + test_default_edit_accessible_object();
unregister_window_class(); CoUninitialize();
Hi Connor,
On 9/24/21 10:53 PM, Connor McAdams wrote:
+typedef struct +{
- INT child_id;
- const WCHAR *expected_name;
- HRESULT expected_name_hr;
- const WCHAR *expected_value;
- HRESULT expected_value_hr;
- const WCHAR *expected_description;
- HRESULT expected_description_hr;
- const WCHAR *expected_help;
- HRESULT expected_help_hr;
- const WCHAR *expected_kbd_shortcut;
- HRESULT expected_kbd_shortcut_hr;
- const WCHAR *expected_default_action;
- HRESULT expected_default_action_hr;
Do we really need all these *_hr fields? Looking on the tests the functions are always returning non-NULL pointer+S_OK and NULL+S_FALSE.
- LONG left_offset, top_offset, width_offset, height_offset;
I bet you're adding it for future use but currently all of these fields are always 0. It's better to add them when they are actually used. I'm not sure if hard-coding exact values will work here.
- /*
* Tests beyond this point are only applicable to full accessible objects
* and not simple elements.
*/
- if (vals->child_id != CHILDID_SELF)
return;
The code is not used at this point. It's better to add this check while adding tests with different child_id.
+static void test_default_edit_accessible_object(void) +{
- HWND hwnd, label0, label1, btn0, btn1;
- IAccessible *acc;
- HWND edit[4];
- HRESULT hr;
- VARIANT v;
- BSTR str;
- /* Create a window that looks like this:
* +----------------------------------------+
* | ___________________________________ |
* | |___________________________________| |
* | (edit[0]) |
* | ____________________________ |
* | Label0: |____________________________| |
* |(label0) (edit[1]) (password) |
* | ______ _________________ |
* | Label1: |button| |multi-line edit | |
* |(label1) (btn0) |_________________| |
* | (edit[2]) |
* | __________________________________ |
* | | | |
* | | edit with embedded button | |
* | |__________________________________| |
* | (edit[3]) (btn1) |
* +----------------------------------------+
*/
We're generally trying to avoid adding comments when code is self-explanatory. As far as I understand the positions are not important anyway so maybe it would be even better to show that in tests.
Thanks, Piotr
On Mon, Sep 27, 2021 at 04:10:39PM +0200, Piotr Caban wrote:
Hi Connor,
On 9/24/21 10:53 PM, Connor McAdams wrote:
+typedef struct +{
- INT child_id;
- const WCHAR *expected_name;
- HRESULT expected_name_hr;
- const WCHAR *expected_value;
- HRESULT expected_value_hr;
- const WCHAR *expected_description;
- HRESULT expected_description_hr;
- const WCHAR *expected_help;
- HRESULT expected_help_hr;
- const WCHAR *expected_kbd_shortcut;
- HRESULT expected_kbd_shortcut_hr;
- const WCHAR *expected_default_action;
- HRESULT expected_default_action_hr;
Do we really need all these *_hr fields? Looking on the tests the functions are always returning non-NULL pointer+S_OK and NULL+S_FALSE.
- LONG left_offset, top_offset, width_offset, height_offset;
I bet you're adding it for future use but currently all of these fields are always 0. It's better to add them when they are actually used. I'm not sure if hard-coding exact values will work here.
Yeah, I added these for future simple element tests, but they may not work. I can remove them for now and test this later when we've got a case of this.
- /*
* Tests beyond this point are only applicable to full accessible objects
* and not simple elements.
*/
- if (vals->child_id != CHILDID_SELF)
return;
The code is not used at this point. It's better to add this check while adding tests with different child_id.
+static void test_default_edit_accessible_object(void) +{
- HWND hwnd, label0, label1, btn0, btn1;
- IAccessible *acc;
- HWND edit[4];
- HRESULT hr;
- VARIANT v;
- BSTR str;
- /* Create a window that looks like this:
* +----------------------------------------+
* | ___________________________________ |
* | |___________________________________| |
* | (edit[0]) |
* | ____________________________ |
* | Label0: |____________________________| |
* |(label0) (edit[1]) (password) |
* | ______ _________________ |
* | Label1: |button| |multi-line edit | |
* |(label1) (btn0) |_________________| |
* | (edit[2]) |
* | __________________________________ |
* | | | |
* | | edit with embedded button | |
* | |__________________________________| |
* | (edit[3]) (btn1) |
* +----------------------------------------+
*/
We're generally trying to avoid adding comments when code is self-explanatory. As far as I understand the positions are not important anyway so maybe it would be even better to show that in tests.
Thanks, Piotr
In general, do you think this style of test is too complicated? I did it in hopes of making future tests for different control objects cleaner/more readable, but if really long test functions are preferable, I can do it that way. My main issue with doing long function body tests is that after awhile I find it hard to keep track of what is being tested.
Thanks for the review!
On 9/27/21 4:44 PM, Connor McAdams wrote:
In general, do you think this style of test is too complicated? I did it in hopes of making future tests for different control objects cleaner/more readable, but if really long test functions are preferable, I can do it that way. My main issue with doing long function body tests is that after awhile I find it hard to keep track of what is being tested.
I'm OK with this style of test. I'm not sure why you've got an impression that there's anything wrong with the way the tests are written. The only problem is that you're adding code that is not needed at this point. It would be better to add more fields to test structure when needed. There's nothing wrong in modifying the code when adding tests for other classes.
Thanks, Piotr
On Mon, Sep 27, 2021 at 05:37:08PM +0200, Piotr Caban wrote:
On 9/27/21 4:44 PM, Connor McAdams wrote:
In general, do you think this style of test is too complicated? I did it in hopes of making future tests for different control objects cleaner/more readable, but if really long test functions are preferable, I can do it that way. My main issue with doing long function body tests is that after awhile I find it hard to keep track of what is being tested.
I'm OK with this style of test. I'm not sure why you've got an impression that there's anything wrong with the way the tests are written. The only problem is that you're adding code that is not needed at this point. It would be better to add more fields to test structure when needed. There's nothing wrong in modifying the code when adding tests for other classes.
Thanks, Piotr
Just making sure, I feel like I have a tendency to write overcomplicated tests. :) Just wanted to make sure that wasn't the case here.
Thanks, Connor.
Hi Connor,
I've sent another version of these patches.
Changes: - added test for first patch, removed SysMonthCal32 from client list and dialog class from window list - some minor style tweaks - moved SendMessage calls to get_name and get_kbd_shortcut helpers
Thanks, Piotr
On 9/24/21 10:52 PM, Connor McAdams wrote:
From: Piotr Caban piotr@codeweavers.com
-v4: Restore previously deleted class check for default window object, and add dialog window class to window object class table.
dlls/oleacc/client.c | 36 ++++++++++++++++ dlls/oleacc/main.c | 82 +++++------------------------------- dlls/oleacc/oleacc_private.h | 7 +++ dlls/oleacc/window.c | 10 +++++ 4 files changed, 63 insertions(+), 72 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 93f6b590f78..333a95dc388 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -19,6 +19,7 @@ #define COBJMACROS
#include "oleacc_private.h" +#include "commctrl.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -650,6 +651,39 @@ static const IEnumVARIANTVtbl ClientEnumVARIANTVtbl = { Client_EnumVARIANT_Clone };
+static const struct win_class_data classes[] = {
- {WC_LISTBOXW, 0x10000, TRUE},
- {L"#32768", 0x10001, TRUE}, /* menu */
- {WC_BUTTONW, 0x10002, TRUE},
- {WC_STATICW, 0x10003, TRUE},
- {WC_EDITW, 0x10004, TRUE},
- {WC_COMBOBOXW, 0x10005, TRUE},
- {L"#32770", 0x10006, TRUE}, /* dialog */
- {L"#32771", 0x10007, TRUE}, /* winswitcher */
- {L"MDIClient", 0x10008, TRUE},
- {L"#32769", 0x10009, TRUE}, /* desktop */
- {WC_SCROLLBARW, 0x1000a, TRUE},
- {STATUSCLASSNAMEW, 0x1000b, TRUE},
- {TOOLBARCLASSNAMEW, 0x1000c, TRUE},
- {PROGRESS_CLASSW, 0x1000d, TRUE},
- {ANIMATE_CLASSW, 0x1000e, TRUE},
- {WC_TABCONTROLW, 0x1000f, TRUE},
- {HOTKEY_CLASSW, 0x10010, TRUE},
- {WC_HEADERW, 0x10011, TRUE},
- {TRACKBAR_CLASSW, 0x10012, TRUE},
- {WC_LISTVIEWW, 0x10013, TRUE},
- {UPDOWN_CLASSW, 0x10016, TRUE},
- {TOOLTIPS_CLASSW, 0x10018, TRUE},
- {WC_TREEVIEWW, 0x10019, TRUE},
- {MONTHCAL_CLASSW, 0, TRUE},
- {DATETIMEPICK_CLASSW, 0, TRUE},
- {WC_IPADDRESSW, 0, TRUE},
- {L"RICHEDIT", 0x1001c, TRUE},
- {L"RichEdit20A", 0, TRUE},
- {L"RichEdit20W", 0, TRUE},
- {NULL}
+};
- HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) { Client *client;
@@ -662,6 +696,8 @@ HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) if(!client) return E_OUTOFMEMORY;
- find_class_data(hwnd, classes);
client->IAccessible_iface.lpVtbl = &ClientVtbl; client->IOleWindow_iface.lpVtbl = &ClientOleWindowVtbl; client->IEnumVARIANT_iface.lpVtbl = &ClientEnumVARIANTVtbl;
diff --git a/dlls/oleacc/main.c b/dlls/oleacc/main.c index db9f646988c..3ce616ae0c1 100644 --- a/dlls/oleacc/main.c +++ b/dlls/oleacc/main.c @@ -20,13 +20,6 @@
#define COBJMACROS
-#include <stdarg.h> -#include "windef.h" -#include "winbase.h" -#include "ole2.h" -#include "commctrl.h" -#include "rpcproxy.h"
- #include "initguid.h" #include "oleacc_private.h" #include "resource.h"
@@ -37,50 +30,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(oleacc);
static const WCHAR lresult_atom_prefix[] = {'w','i','n','e','_','o','l','e','a','c','c',':'};
-typedef HRESULT (*accessible_create)(HWND, const IID*, void**);
- extern HRESULT WINAPI OLEACC_DllGetClassObject(REFCLSID, REFIID, void**) DECLSPEC_HIDDEN; extern BOOL WINAPI OLEACC_DllMain(HINSTANCE, DWORD, void*) DECLSPEC_HIDDEN; extern HRESULT WINAPI OLEACC_DllRegisterServer(void) DECLSPEC_HIDDEN; extern HRESULT WINAPI OLEACC_DllUnregisterServer(void) DECLSPEC_HIDDEN;
-static struct {
- const WCHAR *name;
- DWORD idx;
- accessible_create create_client;
- accessible_create create_window;
-} builtin_classes[] = {
- {WC_LISTBOXW, 0x10000, NULL, NULL},
- {L"#32768", 0x10001, NULL, NULL}, /* menu */
- {WC_BUTTONW, 0x10002, NULL, NULL},
- {WC_STATICW, 0x10003, NULL, NULL},
- {WC_EDITW, 0x10004, NULL, NULL},
- {WC_COMBOBOXW, 0x10005, NULL, NULL},
- {L"#32770", 0x10006, NULL, NULL}, /* dialog */
- {L"#32771", 0x10007, NULL, NULL}, /* winswitcher */
- {L"MDIClient", 0x10008, NULL, NULL},
- {L"#32769", 0x10009, NULL, NULL}, /* desktop */
- {WC_SCROLLBARW, 0x1000a, NULL, NULL},
- {STATUSCLASSNAMEW, 0x1000b, NULL, NULL},
- {TOOLBARCLASSNAMEW, 0x1000c, NULL, NULL},
- {PROGRESS_CLASSW, 0x1000d, NULL, NULL},
- {ANIMATE_CLASSW, 0x1000e, NULL, NULL},
- {WC_TABCONTROLW, 0x1000f, NULL, NULL},
- {HOTKEY_CLASSW, 0x10010, NULL, NULL},
- {WC_HEADERW, 0x10011, NULL, NULL},
- {TRACKBAR_CLASSW, 0x10012, NULL, NULL},
- {WC_LISTVIEWW, 0x10013, NULL, NULL},
- {UPDOWN_CLASSW, 0x10016, NULL, NULL},
- {TOOLTIPS_CLASSW, 0x10018, NULL, NULL},
- {WC_TREEVIEWW, 0x10019, NULL, NULL},
- {MONTHCAL_CLASSW, 0, NULL, NULL},
- {DATETIMEPICK_CLASSW, 0, NULL, NULL},
- {WC_IPADDRESSW, 0, NULL, NULL},
- {L"RICHEDIT", 0x1001c, NULL, NULL},
- {L"RichEdit20A", 0, NULL, NULL},
- {L"RichEdit20W", 0, NULL, NULL},
-};
static HINSTANCE oleacc_handle = 0;
int convert_child_id(VARIANT *v)
@@ -94,7 +48,7 @@ int convert_child_id(VARIANT *v) } }
-static accessible_create get_builtin_accessible_obj(HWND hwnd, LONG objid) +const struct win_class_data* find_class_data(HWND hwnd, const struct win_class_data *classes) { WCHAR class_name[64]; int i, idx; @@ -103,31 +57,21 @@ static accessible_create get_builtin_accessible_obj(HWND hwnd, LONG objid) return NULL; TRACE("got window class: %s\n", debugstr_w(class_name));
- for(i=0; i<ARRAY_SIZE(builtin_classes); i++) {
if(!wcsicmp(class_name, builtin_classes[i].name)) {
accessible_create ret;
ret = (objid==OBJID_CLIENT ?
builtin_classes[i].create_client :
builtin_classes[i].create_window);
if(!ret)
- for(i=0; classes[i].name; i++) {
if(!wcsicmp(class_name, classes[i].name)) {
if(classes[i].stub) FIXME("unhandled window class: %s\n", debugstr_w(class_name));
return ret;
return &classes[i]; } } idx = SendMessageW(hwnd, WM_GETOBJECT, 0, OBJID_QUERYCLASSNAMEIDX); if(idx) {
for(i=0; i<ARRAY_SIZE(builtin_classes); i++) {
if(idx == builtin_classes[i].idx) {
accessible_create ret;
ret = (objid==OBJID_CLIENT ?
builtin_classes[i].create_client :
builtin_classes[i].create_window);
if(!ret)
FIXME("unhandled class name idx: %x\n", idx);
return ret;
for(i=0; classes[i].name; i++) {
if(idx == classes[i].idx) {
if(classes[i].stub)
FIXME("unhandled window class: %s\n", debugstr_w(class_name));
return &classes[i]; } }
@@ -140,19 +84,13 @@ static accessible_create get_builtin_accessible_obj(HWND hwnd, LONG objid) HRESULT WINAPI CreateStdAccessibleObject( HWND hwnd, LONG idObject, REFIID riidInterface, void** ppvObject ) {
accessible_create create;
TRACE("%p %d %s %p\n", hwnd, idObject, debugstr_guid( riidInterface ), ppvObject ); switch(idObject) { case OBJID_CLIENT:
create = get_builtin_accessible_obj(hwnd, idObject);
if(create) return create(hwnd, riidInterface, ppvObject); return create_client_object(hwnd, riidInterface, ppvObject); case OBJID_WINDOW:
create = get_builtin_accessible_obj(hwnd, idObject);
if(create) return create(hwnd, riidInterface, ppvObject); return create_window_object(hwnd, riidInterface, ppvObject); default: FIXME("unhandled object id: %d\n", idObject);
diff --git a/dlls/oleacc/oleacc_private.h b/dlls/oleacc/oleacc_private.h index 32561ef3d2f..64926b6abb7 100644 --- a/dlls/oleacc/oleacc_private.h +++ b/dlls/oleacc/oleacc_private.h @@ -18,6 +18,13 @@
#include "oleacc_classes.h"
+struct win_class_data {
- const WCHAR *name;
- DWORD idx;
- BOOL stub;
+}; +const struct win_class_data* find_class_data(HWND, const struct win_class_data*) DECLSPEC_HIDDEN;
- HRESULT create_client_object(HWND, const IID*, void**) DECLSPEC_HIDDEN; HRESULT create_window_object(HWND, const IID*, void**) DECLSPEC_HIDDEN; HRESULT get_accpropservices_factory(REFIID, void**) DECLSPEC_HIDDEN;
diff --git a/dlls/oleacc/window.c b/dlls/oleacc/window.c index 387ed4bc9f7..75e5d592699 100644 --- a/dlls/oleacc/window.c +++ b/dlls/oleacc/window.c @@ -19,6 +19,7 @@ #define COBJMACROS
#include "oleacc_private.h" +#include "commctrl.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -416,6 +417,13 @@ static const IEnumVARIANTVtbl WindowEnumVARIANTVtbl = { Window_EnumVARIANT_Clone };
+static const struct win_class_data classes[] = {
- {WC_LISTBOXW, 0x10000, TRUE},
- {L"#32768", 0x10001, TRUE}, /* menu */
- {L"#32770", 0x10006, TRUE}, /* dialog */
- {NULL}
+};
- HRESULT create_window_object(HWND hwnd, const IID *iid, void **obj) { Window *window;
@@ -428,6 +436,8 @@ HRESULT create_window_object(HWND hwnd, const IID *iid, void **obj) if(!window) return E_OUTOFMEMORY;
- find_class_data(hwnd, classes);
window->IAccessible_iface.lpVtbl = &WindowVtbl; window->IOleWindow_iface.lpVtbl = &WindowOleWindowVtbl; window->IEnumVARIANT_iface.lpVtbl = &WindowEnumVARIANTVtbl;