Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 17 +++++++++++++++-- dlls/oleacc/main.c | 2 +- dlls/oleacc/oleacc_private.h | 1 + 3 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 93f6b590f78..99be7d9fa37 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -34,6 +34,7 @@ typedef struct {
HWND hwnd; HWND enum_pos; + INT role; } Client;
static inline Client* impl_from_Client(IAccessible *iface) @@ -220,7 +221,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; }
@@ -650,7 +651,8 @@ static const IEnumVARIANTVtbl ClientEnumVARIANTVtbl = { Client_EnumVARIANT_Clone };
-HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) +static HRESULT create_class_object(HWND hwnd, const IID *iid, void **obj, + INT role) { Client *client; HRESULT hres; @@ -668,8 +670,19 @@ HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) client->ref = 1; client->hwnd = hwnd; client->enum_pos = 0; + client->role = role;
hres = IAccessible_QueryInterface(&client->IAccessible_iface, iid, obj); IAccessible_Release(&client->IAccessible_iface); return hres; } + +HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) +{ + return create_class_object(hwnd, iid, obj, ROLE_SYSTEM_CLIENT); +} + +HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj) +{ + return create_class_object(hwnd, iid, obj, ROLE_SYSTEM_TEXT); +} diff --git a/dlls/oleacc/main.c b/dlls/oleacc/main.c index db9f646988c..813ebc64a6e 100644 --- a/dlls/oleacc/main.c +++ b/dlls/oleacc/main.c @@ -54,7 +54,7 @@ static struct { {L"#32768", 0x10001, NULL, NULL}, /* menu */ {WC_BUTTONW, 0x10002, NULL, NULL}, {WC_STATICW, 0x10003, NULL, NULL}, - {WC_EDITW, 0x10004, NULL, NULL}, + {WC_EDITW, 0x10004, create_edit_client_object, NULL}, {WC_COMBOBOXW, 0x10005, NULL, NULL}, {L"#32770", 0x10006, NULL, NULL}, /* dialog */ {L"#32771", 0x10007, NULL, NULL}, /* winswitcher */ diff --git a/dlls/oleacc/oleacc_private.h b/dlls/oleacc/oleacc_private.h index 32561ef3d2f..144009b1f33 100644 --- a/dlls/oleacc/oleacc_private.h +++ b/dlls/oleacc/oleacc_private.h @@ -19,6 +19,7 @@ #include "oleacc_classes.h"
HRESULT create_client_object(HWND, const IID*, void**) DECLSPEC_HIDDEN; +HRESULT create_edit_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;
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 99be7d9fa37..afa2aa5babd 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -25,6 +25,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,14 @@ typedef struct { HWND hwnd; HWND enum_pos; INT role; + + const win_class_vtbl *vtbl; } Client;
+struct win_class_vtbl { + HRESULT (*get_state)(Client *, INT *); +}; + static inline Client* impl_from_Client(IAccessible *iface) { return CONTAINING_RECORD(iface, Client, IAccessible_iface); @@ -252,6 +259,10 @@ static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VAR V_I4(pvarState) |= STATE_SYSTEM_FOCUSED; if(!(style & WS_VISIBLE)) V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE; + + if (This->vtbl && This->vtbl->get_state) + return This->vtbl->get_state(This, &V_I4(pvarState)); + return S_OK; }
@@ -652,7 +663,7 @@ static const IEnumVARIANTVtbl ClientEnumVARIANTVtbl = { };
static HRESULT create_class_object(HWND hwnd, const IID *iid, void **obj, - INT role) + INT role, const win_class_vtbl *vtbl) { Client *client; HRESULT hres; @@ -671,6 +682,7 @@ static HRESULT create_class_object(HWND hwnd, const IID *iid, void **obj, client->hwnd = hwnd; client->enum_pos = 0; client->role = role; + client->vtbl = vtbl;
hres = IAccessible_QueryInterface(&client->IAccessible_iface, iid, obj); IAccessible_Release(&client->IAccessible_iface); @@ -679,10 +691,29 @@ static HRESULT create_class_object(HWND hwnd, const IID *iid, void **obj,
HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) { - return create_class_object(hwnd, iid, obj, ROLE_SYSTEM_CLIENT); + return create_class_object(hwnd, iid, obj, ROLE_SYSTEM_CLIENT, NULL); }
+static HRESULT edit_get_state(Client *client, INT *state) +{ + LONG style; + + TRACE("(%p, %p)\n", client, state); + + style = GetWindowLongW(client->hwnd, GWL_STYLE); + if(style & ES_READONLY) + *state |= STATE_SYSTEM_READONLY; + if(style & ES_PASSWORD) + *state |= STATE_SYSTEM_PROTECTED; + + return S_OK; +} + +const win_class_vtbl edit_class_vtbl = { + edit_get_state, +}; + HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj) { - return create_class_object(hwnd, iid, obj, ROLE_SYSTEM_TEXT); + return create_class_object(hwnd, iid, obj, ROLE_SYSTEM_TEXT, &edit_class_vtbl); }
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 83 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 11 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index afa2aa5babd..24dc1879142 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -20,6 +20,8 @@
#include "oleacc_private.h"
+#include "commctrl.h" + #include "wine/debug.h" #include "wine/heap.h"
@@ -42,8 +44,28 @@ typedef struct {
struct win_class_vtbl { HRESULT (*get_state)(Client *, INT *); + HRESULT (*get_name)(Client *, BSTR *); };
+static HRESULT acc_client_get_name_str(WCHAR *name, UINT len, BSTR *pszName) +{ + 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; + } + } + + *pszName = SysAllocStringLen(name, len); + return *pszName ? S_OK : E_OUTOFMEMORY; +} + static inline Client* impl_from_Client(IAccessible *iface) { return CONTAINING_RECORD(iface, Client, IAccessible_iface); @@ -167,7 +189,7 @@ static HRESULT WINAPI Client_get_accName(IAccessible *iface, VARIANT varID, BSTR { Client *This = impl_from_Client(iface); WCHAR name[1024]; - UINT i, len; + UINT len;
TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszName);
@@ -175,20 +197,14 @@ static HRESULT WINAPI Client_get_accName(IAccessible *iface, VARIANT varID, BSTR if(convert_child_id(&varID) != CHILDID_SELF || !IsWindow(This->hwnd)) return E_INVALIDARG;
+ if (This->vtbl && This->vtbl->get_name) + return This->vtbl->get_name(This, pszName); + 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, pszName); }
static HRESULT WINAPI Client_get_accValue(IAccessible *iface, VARIANT varID, BSTR *pszValue) @@ -709,8 +725,53 @@ static HRESULT edit_get_state(Client *client, INT *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 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, BSTR *out_name) +{ + WCHAR name[1024]; + HWND label; + UINT len; + + TRACE("(%p, %p)\n", client, out_name); + + 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, out_name); +} + const win_class_vtbl edit_class_vtbl = { edit_get_state, + edit_get_name, };
HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj)
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 57 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 13 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 24dc1879142..9d5da15e624 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -45,6 +45,7 @@ typedef struct { struct win_class_vtbl { HRESULT (*get_state)(Client *, INT *); HRESULT (*get_name)(Client *, BSTR *); + HRESULT (*get_kbd_shortcut)(Client *, BSTR *); };
static HRESULT acc_client_get_name_str(WCHAR *name, UINT len, BSTR *pszName) @@ -66,6 +67,26 @@ static HRESULT acc_client_get_name_str(WCHAR *name, UINT len, BSTR *pszName) return *pszName ? S_OK : E_OUTOFMEMORY; }
+static HRESULT acc_client_get_kbd_shortcut_str(WCHAR *name, UINT len, + BSTR *pszKeyboardShortcut) +{ + UINT i; + + 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; +} + static inline Client* impl_from_Client(IAccessible *iface) { return CONTAINING_RECORD(iface, Client, IAccessible_iface); @@ -307,7 +328,7 @@ static HRESULT WINAPI Client_get_accKeyboardShortcut(IAccessible *iface, { Client *This = impl_from_Client(iface); WCHAR name[1024]; - UINT i, len; + UINT len;
TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszKeyboardShortcut);
@@ -315,20 +336,12 @@ static HRESULT WINAPI Client_get_accKeyboardShortcut(IAccessible *iface, if(convert_child_id(&varID) != 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; + if (This->vtbl && This->vtbl->get_kbd_shortcut) + return This->vtbl->get_kbd_shortcut(This, pszKeyboardShortcut);
- *pszKeyboardShortcut = SysAllocString(L"Alt+!"); - if(!*pszKeyboardShortcut) - return E_OUTOFMEMORY; + len = SendMessageW(This->hwnd, WM_GETTEXT, ARRAY_SIZE(name), (LPARAM)name);
- (*pszKeyboardShortcut)[4] = name[i+1]; - return S_OK; + return acc_client_get_kbd_shortcut_str(name, len, pszKeyboardShortcut); }
static HRESULT WINAPI Client_get_accFocus(IAccessible *iface, VARIANT *focus) @@ -769,9 +782,27 @@ static HRESULT edit_get_name(Client *client, BSTR *out_name) return acc_client_get_name_str(name, len, out_name); }
+static HRESULT edit_get_kbd_shortcut(Client *client, BSTR *out_kbd_shortcut) +{ + WCHAR name[1024]; + HWND label; + UINT len; + + TRACE("(%p, %p)\n", client, out_kbd_shortcut); + + 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, out_kbd_shortcut); +} + const win_class_vtbl edit_class_vtbl = { edit_get_state, edit_get_name, + edit_get_kbd_shortcut, };
HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj)
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 9d5da15e624..a5620a4f24c 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -46,6 +46,7 @@ struct win_class_vtbl { HRESULT (*get_state)(Client *, INT *); HRESULT (*get_name)(Client *, BSTR *); HRESULT (*get_kbd_shortcut)(Client *, BSTR *); + HRESULT (*get_value)(Client *, BSTR *); };
static HRESULT acc_client_get_name_str(WCHAR *name, UINT len, BSTR *pszName) @@ -237,6 +238,10 @@ static HRESULT WINAPI Client_get_accValue(IAccessible *iface, VARIANT varID, BST *pszValue = NULL; if(convert_child_id(&varID) != CHILDID_SELF) return E_INVALIDARG; + + if (This->vtbl && This->vtbl->get_value) + return This->vtbl->get_value(This, pszValue); + return S_FALSE; }
@@ -799,10 +804,32 @@ static HRESULT edit_get_kbd_shortcut(Client *client, BSTR *out_kbd_shortcut) return acc_client_get_kbd_shortcut_str(name, len, out_kbd_shortcut); }
+static HRESULT edit_get_value(Client *client, BSTR *out_value) +{ + WCHAR *buf; + UINT len; + + TRACE("(%p, %p)\n", client, out_value); + + 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); + *out_value = SysAllocString(buf); + heap_free(buf); + + return S_OK; +} + const win_class_vtbl edit_class_vtbl = { edit_get_state, edit_get_name, edit_get_kbd_shortcut, + edit_get_value, };
HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj)
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index a5620a4f24c..3bf4a5a39fe 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -47,6 +47,7 @@ struct win_class_vtbl { HRESULT (*get_name)(Client *, BSTR *); HRESULT (*get_kbd_shortcut)(Client *, BSTR *); HRESULT (*get_value)(Client *, BSTR *); + HRESULT (*put_value)(Client *, VARIANT, BSTR); };
static HRESULT acc_client_get_name_str(WCHAR *name, UINT len, BSTR *pszName) @@ -491,7 +492,12 @@ static HRESULT WINAPI Client_put_accName(IAccessible *iface, VARIANT varID, BSTR static HRESULT WINAPI Client_put_accValue(IAccessible *iface, VARIANT varID, BSTR pszValue) { Client *This = impl_from_Client(iface); + + if (This->vtbl && This->vtbl->put_value) + return This->vtbl->put_value(This, varID, pszValue); + FIXME("(%p)->(%s %s)\n", This, debugstr_variant(&varID), debugstr_w(pszValue)); + return E_NOTIMPL; }
@@ -825,11 +831,24 @@ static HRESULT edit_get_value(Client *client, BSTR *out_value) return S_OK; }
+static HRESULT edit_put_value(Client *client, VARIANT var_id, BSTR value) +{ + TRACE("(%p, %s, %s)\n", client, debugstr_variant(&var_id), debugstr_w(value)); + + if(convert_child_id(&var_id) != CHILDID_SELF || !IsWindow(client->hwnd)) + return E_INVALIDARG; + + SendMessageW(client->hwnd, WM_SETTEXT, 0, (LPARAM)value); + + return S_OK; +} + const win_class_vtbl edit_class_vtbl = { edit_get_state, edit_get_name, edit_get_kbd_shortcut, edit_get_value, + edit_put_value, };
HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj)
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,
It looks like the code for handling class specific behavior may use some more changes.
- The list of client / window classes that needs special handling is different. I think it's better to keep it separated (attached patch 1). Please note that the list may be incomplete. - Instead of adding more arguments to create function add init function to the vtbl (patch 2). - Change get_state function so it can be used for listbox (patch 3).
What do you think about it?
Thanks, Piotr
On Thu, Sep 23, 2021 at 04:12:37PM +0200, Piotr Caban wrote:
Hi Connor,
It looks like the code for handling class specific behavior may use some more changes.
- The list of client / window classes that needs special handling is
different. I think it's better to keep it separated (attached patch 1). Please note that the list may be incomplete.
- Instead of adding more arguments to create function add init function to
the vtbl (patch 2).
- Change get_state function so it can be used for listbox (patch 3).
What do you think about it?
Thanks, Piotr
Yeah, I'm thinking this approach looks better at a glance. I'm going to try and do a little bit of testing/research on the general behavior of each window class tomorrow to try and figure out which ones behave more like the default and which ones are more quirky.
Thanks!
From e5de42525c84af47bb591734d1897b19da915692 Mon Sep 17 00:00:00 2001 From: Piotr Caban piotr@codeweavers.com Date: Thu, 23 Sep 2021 15:25:11 +0200 Subject: [PATCH 1/3] oleacc: Reorganize class specific behaviour handling. To: wine-devel wine-devel@winehq.org
dlls/oleacc/client.c | 36 ++++++++++++++++ dlls/oleacc/main.c | 82 +++++------------------------------- dlls/oleacc/oleacc_private.h | 7 +++ dlls/oleacc/window.c | 9 ++++ 4 files changed, 62 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..aa433d624cf 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,12 @@ static const IEnumVARIANTVtbl WindowEnumVARIANTVtbl = { Window_EnumVARIANT_Clone };
+static const struct win_class_data classes[] = {
- {WC_LISTBOXW, 0x10000, TRUE},
- {L"#32768", 0x10001, TRUE}, /* menu */
- {NULL}
+};
HRESULT create_window_object(HWND hwnd, const IID *iid, void **obj) { Window *window; @@ -428,6 +435,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;
-- 2.32.0
From ff6ebfe14faee0aaca86fa8e8f38eae8e996a4cd Mon Sep 17 00:00:00 2001 From: Connor McAdams cmcadams@codeweavers.com Date: Wed, 22 Sep 2021 16:12:35 -0400 Subject: [PATCH 2/3] oleacc: Add get_accRole implementation for edit client accessible object. To: wine-devel wine-devel@winehq.org
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..4a0471a2657 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, TRUE, &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;
-- 2.32.0
From f96c16d931c5c3840c40924ed2c9607af1dea3d2 Mon Sep 17 00:00:00 2001 From: Connor McAdams cmcadams@codeweavers.com Date: Wed, 22 Sep 2021 16:12:36 -0400 Subject: [PATCH 3/3] oleacc: Add get_accState function for edit client accessible object. To: wine-devel wine-devel@winehq.org
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 4a0471a2657..12058f5545a 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)
if(!(style & WS_VISIBLE))V_I4(state) |= STATE_SYSTEM_FOCUSED;
V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE;
return S_OK;V_I4(state) |= STATE_SYSTEM_INVISIBLE;
}
+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[] = {
2.32.0
Okay, went back to my notes and also did a bit more research with some of the accessibility inspector tools. Here's an excerpt from my notes, which I've confirmed to be the behavior on the various window classes:
Layout of an MSAA window object (represents an HWND): +------------------------------------------_-----+ |-0| -1 (Title) _ |_| x | |--+---------------------------------------------+ | File | Edit | Format | Help -2 | +---------------------------------------------+--+ | -3 | | | | | | | | | | | | | | | |-4| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +---------------------------------------------+--+ | -5 |-6| +---------------------------------------------+--+
-0: A "System" menubar (icon in top left corner.) -1: A title bar (also includes minimize, maximize, close) -2: An "Application" menubar (file, help, etc.) -3: A client area. Different for each window class. -4: A vertical scroll bar. -5: A horizontal scroll bar. -6: A grip between the horizontal/vertical scroll bars, for resizing
Even though not all windows have these pieces enabled in their window style, these objects are still created for each window object. The client area (3) is what changes between different window classes.
Window styles/WM_GETOBJECT OBJID's for each window part: -0: WS_SYSMENU/OBJID_SYSMENU -1: WS_CAPTION/OBJID_TITLEBAR -2: WS_SYSMENU/OBJID_MENU (also, only visible if the window is provided a menu class.) -3: WS_XXXXXXX/OBJID_CLIENT (all windows have a client area AFAICT.) -4: WS_VSCROLL/OBJID_VSCROLL -5: WS_HSCROLL/OBJID_HSCROLL -6: WS_SIZEBOX/OBJID_SIZEGRIP
https://docs.microsoft.com/en-us/windows/win32/winauto/window
All windows have a child count of 7, and each child is a full object from the above list.
I think we really only need unique behavior WRT window class in the client code. The rest of the objects are the same for all windows. So, the changes you've made make sense, and we can probably just leave the checks in the client.c file, we shouldn't need any in window.c. I'll work on following the style of the patches you've sent and send in a v3.