Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 21 ++++++++++++++++++++- dlls/oleacc/main.c | 2 +- dlls/oleacc/oleacc_private.h | 1 + 3 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 93f6b590f78..5f702d0135b 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; }
@@ -668,8 +669,26 @@ 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;
hres = IAccessible_QueryInterface(&client->IAccessible_iface, iid, obj); IAccessible_Release(&client->IAccessible_iface); return hres; } + +HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj) +{ + IAccessible *acc; + Client *client; + HRESULT hres; + + hres = create_client_object(hwnd, &IID_IAccessible, (void **)&acc); + if (FAILED(hres)) return hres; + + client = impl_from_Client(acc); + client->role = ROLE_SYSTEM_TEXT; + + hres = IAccessible_QueryInterface(acc, iid, obj); + IAccessible_Release(acc); + return hres; +} 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 | 69 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 14 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 5f702d0135b..043d98d93cc 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -25,6 +25,10 @@
WINE_DEFAULT_DEBUG_CHANNEL(oleacc);
+typedef struct { + HRESULT (*get_state)(IAccessible *, VARIANT, VARIANT *); +} WinClassVtbl; + typedef struct { IAccessible IAccessible_iface; IOleWindow IOleWindow_iface; @@ -35,6 +39,8 @@ typedef struct { HWND hwnd; HWND enum_pos; INT role; + + WinClassVtbl vtbl; } Client;
static inline Client* impl_from_Client(IAccessible *iface) @@ -225,33 +231,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 void default_client_get_state(IAccessible *iface, VARIANT *var_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; - return E_INVALIDARG; - } - - V_VT(pvarState) = VT_I4; - V_I4(pvarState) = 0; + V_VT(var_state) = VT_I4; + V_I4(var_state) = 0;
style = GetWindowLongW(This->hwnd, GWL_STYLE); if(style & WS_DISABLED) - V_I4(pvarState) |= STATE_SYSTEM_UNAVAILABLE; + V_I4(var_state) |= STATE_SYSTEM_UNAVAILABLE; else if(IsWindow(This->hwnd)) - V_I4(pvarState) |= STATE_SYSTEM_FOCUSABLE; + V_I4(var_state) |= STATE_SYSTEM_FOCUSABLE;
info.cbSize = sizeof(info); if(GetGUIThreadInfo(0, &info) && info.hwndFocus == This->hwnd) - V_I4(pvarState) |= STATE_SYSTEM_FOCUSED; + V_I4(var_state) |= STATE_SYSTEM_FOCUSED; if(!(style & WS_VISIBLE)) - V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE; + V_I4(var_state) |= STATE_SYSTEM_INVISIBLE; +} + +static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) +{ + Client *This = impl_from_Client(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pvarState); + + if (This->vtbl.get_state) + return This->vtbl.get_state(iface, varID, pvarState); + + if(convert_child_id(&varID) != CHILDID_SELF) { + V_VT(pvarState) = VT_EMPTY; + return E_INVALIDARG; + } + + default_client_get_state(iface, pvarState); + return S_OK; }
@@ -676,6 +693,29 @@ HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) return hres; }
+static HRESULT edit_get_state(IAccessible *iface, VARIANT var_id, VARIANT *out_state) +{ + Client *This = impl_from_Client(iface); + LONG style; + + TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&var_id), out_state); + + if(convert_child_id(&var_id) != CHILDID_SELF) { + V_VT(out_state) = VT_EMPTY; + return E_INVALIDARG; + } + + default_client_get_state(iface, out_state); + + style = GetWindowLongW(This->hwnd, GWL_STYLE); + if(style & ES_READONLY) + V_I4(out_state) |= STATE_SYSTEM_READONLY; + if(style & ES_PASSWORD) + V_I4(out_state) |= STATE_SYSTEM_PROTECTED; + + return S_OK; +} + HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj) { IAccessible *acc; @@ -687,6 +727,7 @@ HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj)
client = impl_from_Client(acc); client->role = ROLE_SYSTEM_TEXT; + client->vtbl.get_state = &edit_get_state;
hres = IAccessible_QueryInterface(acc, iid, obj); IAccessible_Release(acc);
Hi Connor,
On 9/22/21 6:20 PM, Connor McAdams wrote:
+typedef struct {
- HRESULT (*get_state)(IAccessible *, VARIANT, VARIANT *);
+} WinClassVtbl;
I think it's better to pass Client* as first argument. Please also avoid camel-case in new code.
-static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) +static void default_client_get_state(IAccessible *iface, VARIANT *var_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;
return E_INVALIDARG;
- }
- V_VT(pvarState) = VT_I4;
- V_I4(pvarState) = 0;
V_VT(var_state) = VT_I4;
V_I4(var_state) = 0;
style = GetWindowLongW(This->hwnd, GWL_STYLE); if(style & WS_DISABLED)
V_I4(pvarState) |= STATE_SYSTEM_UNAVAILABLE;
V_I4(var_state) |= STATE_SYSTEM_UNAVAILABLE; else if(IsWindow(This->hwnd))
V_I4(pvarState) |= STATE_SYSTEM_FOCUSABLE;
V_I4(var_state) |= STATE_SYSTEM_FOCUSABLE; info.cbSize = sizeof(info); if(GetGUIThreadInfo(0, &info) && info.hwndFocus == This->hwnd)
V_I4(pvarState) |= STATE_SYSTEM_FOCUSED;
V_I4(var_state) |= STATE_SYSTEM_FOCUSED; if(!(style & WS_VISIBLE))
V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE;
V_I4(var_state) |= STATE_SYSTEM_INVISIBLE;
+}
+static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) +{
- Client *This = impl_from_Client(iface);
- TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pvarState);
- if (This->vtbl.get_state)
return This->vtbl.get_state(iface, varID, pvarState);
- if(convert_child_id(&varID) != CHILDID_SELF) {
V_VT(pvarState) = VT_EMPTY;
return E_INVALIDARG;
- }
- default_client_get_state(iface, pvarState);
}return S_OK;
Isn't it enough to call vtbl.get_state near the end of the function? I'm asking about something along these lines (I've also changed get_state header): static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) { ... info.cbSize = sizeof(info); if(GetGUIThreadInfo(0, &info) && info.hwndFocus == This->hwnd) V_I4(pvarState) |= STATE_SYSTEM_FOCUSED; if(!(style & WS_VISIBLE)) V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE;
if(This->vtbl.get_state) return This->vtbl.get_state(This, &V_I4(pvarState)) return S_OK; }
Thanks, Piotr
On Wed, Sep 22, 2021 at 07:19:44PM +0200, Piotr Caban wrote:
Hi Connor,
On 9/22/21 6:20 PM, Connor McAdams wrote:
+typedef struct {
- HRESULT (*get_state)(IAccessible *, VARIANT, VARIANT *);
+} WinClassVtbl;
I think it's better to pass Client* as first argument. Please also avoid camel-case in new code.
-static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) +static void default_client_get_state(IAccessible *iface, VARIANT *var_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;
return E_INVALIDARG;
- }
- V_VT(pvarState) = VT_I4;
- V_I4(pvarState) = 0;
- V_VT(var_state) = VT_I4;
- V_I4(var_state) = 0; style = GetWindowLongW(This->hwnd, GWL_STYLE); if(style & WS_DISABLED)
V_I4(pvarState) |= STATE_SYSTEM_UNAVAILABLE;
V_I4(var_state) |= STATE_SYSTEM_UNAVAILABLE; else if(IsWindow(This->hwnd))
V_I4(pvarState) |= STATE_SYSTEM_FOCUSABLE;
V_I4(var_state) |= STATE_SYSTEM_FOCUSABLE; info.cbSize = sizeof(info); if(GetGUIThreadInfo(0, &info) && info.hwndFocus == This->hwnd)
V_I4(pvarState) |= STATE_SYSTEM_FOCUSED;
V_I4(var_state) |= STATE_SYSTEM_FOCUSED; if(!(style & WS_VISIBLE))
V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE;
V_I4(var_state) |= STATE_SYSTEM_INVISIBLE;
+}
+static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) +{
- Client *This = impl_from_Client(iface);
- TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pvarState);
- if (This->vtbl.get_state)
return This->vtbl.get_state(iface, varID, pvarState);
- if(convert_child_id(&varID) != CHILDID_SELF) {
V_VT(pvarState) = VT_EMPTY;
return E_INVALIDARG;
- }
- default_client_get_state(iface, pvarState);
}return S_OK;
Isn't it enough to call vtbl.get_state near the end of the function? I'm asking about something along these lines (I've also changed get_state header): static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) { ... info.cbSize = sizeof(info); if(GetGUIThreadInfo(0, &info) && info.hwndFocus == This->hwnd) V_I4(pvarState) |= STATE_SYSTEM_FOCUSED; if(!(style & WS_VISIBLE)) V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE;
if(This->vtbl.get_state) return This->vtbl.get_state(This, &V_I4(pvarState)) return S_OK;
}
Yeah, I see what you're saying. That makes more sense. I guess in my mind, I was thinking of the functions in the vtbl as more of an "override" instead of an extension.
Thanks, Piotr
Thanks for the review!
On Wed, Sep 22, 2021 at 07:19:44PM +0200, Piotr Caban wrote:
Hi Connor,
On 9/22/21 6:20 PM, Connor McAdams wrote:
+typedef struct {
- HRESULT (*get_state)(IAccessible *, VARIANT, VARIANT *);
+} WinClassVtbl;
I think it's better to pass Client* as first argument. Please also avoid camel-case in new code.
-static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) +static void default_client_get_state(IAccessible *iface, VARIANT *var_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;
return E_INVALIDARG;
- }
- V_VT(pvarState) = VT_I4;
- V_I4(pvarState) = 0;
- V_VT(var_state) = VT_I4;
- V_I4(var_state) = 0; style = GetWindowLongW(This->hwnd, GWL_STYLE); if(style & WS_DISABLED)
V_I4(pvarState) |= STATE_SYSTEM_UNAVAILABLE;
V_I4(var_state) |= STATE_SYSTEM_UNAVAILABLE; else if(IsWindow(This->hwnd))
V_I4(pvarState) |= STATE_SYSTEM_FOCUSABLE;
V_I4(var_state) |= STATE_SYSTEM_FOCUSABLE; info.cbSize = sizeof(info); if(GetGUIThreadInfo(0, &info) && info.hwndFocus == This->hwnd)
V_I4(pvarState) |= STATE_SYSTEM_FOCUSED;
V_I4(var_state) |= STATE_SYSTEM_FOCUSED; if(!(style & WS_VISIBLE))
V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE;
V_I4(var_state) |= STATE_SYSTEM_INVISIBLE;
+}
+static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) +{
- Client *This = impl_from_Client(iface);
- TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pvarState);
- if (This->vtbl.get_state)
return This->vtbl.get_state(iface, varID, pvarState);
- if(convert_child_id(&varID) != CHILDID_SELF) {
V_VT(pvarState) = VT_EMPTY;
return E_INVALIDARG;
- }
- default_client_get_state(iface, pvarState);
}return S_OK;
Isn't it enough to call vtbl.get_state near the end of the function? I'm asking about something along these lines (I've also changed get_state header): static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) { ... info.cbSize = sizeof(info); if(GetGUIThreadInfo(0, &info) && info.hwndFocus == This->hwnd) V_I4(pvarState) |= STATE_SYSTEM_FOCUSED; if(!(style & WS_VISIBLE)) V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE;
if(This->vtbl.get_state) return This->vtbl.get_state(This, &V_I4(pvarState)) return S_OK;
}
Oh, problem with this: Not all classes are restricted to having a varID of CHILDID_SELF for their get_accState implementation. I.e, listbox controls have individual state values for each item in the list. Not having the vtbl function called from the start would break this.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 88 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 11 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 043d98d93cc..ef6967752b4 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"
@@ -27,6 +29,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(oleacc);
typedef struct { HRESULT (*get_state)(IAccessible *, VARIANT, VARIANT *); + HRESULT (*get_name)(IAccessible *, VARIANT, BSTR *); } WinClassVtbl;
typedef struct { @@ -43,6 +46,25 @@ typedef struct { WinClassVtbl vtbl; } Client;
+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); @@ -166,10 +188,13 @@ 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);
+ if (This->vtbl.get_name) + return This->vtbl.get_name(iface, varID, pszName); + *pszName = NULL; if(convert_child_id(&varID) != CHILDID_SELF || !IsWindow(This->hwnd)) return E_INVALIDARG; @@ -178,16 +203,7 @@ static HRESULT WINAPI Client_get_accName(IAccessible *iface, VARIANT varID, BSTR 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) @@ -693,6 +709,55 @@ HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj) return hres; }
+/* + * 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(IAccessible *iface, VARIANT var_id, BSTR *out_name) +{ + Client *This = impl_from_Client(iface); + WCHAR name[1024]; + HWND label; + UINT len; + + TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&var_id), out_name); + + *out_name = NULL; + if(convert_child_id(&var_id) != CHILDID_SELF || !IsWindow(This->hwnd)) + return E_INVALIDARG; + + label = acc_edit_find_label(This->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); +} + static HRESULT edit_get_state(IAccessible *iface, VARIANT var_id, VARIANT *out_state) { Client *This = impl_from_Client(iface); @@ -728,6 +793,7 @@ HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj) client = impl_from_Client(acc); client->role = ROLE_SYSTEM_TEXT; client->vtbl.get_state = &edit_get_state; + client->vtbl.get_name = &edit_get_name;
hres = IAccessible_QueryInterface(acc, iid, obj); IAccessible_Release(acc);
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 63 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 13 deletions(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index ef6967752b4..22b8c225dd9 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -30,6 +30,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(oleacc); typedef struct { HRESULT (*get_state)(IAccessible *, VARIANT, VARIANT *); HRESULT (*get_name)(IAccessible *, VARIANT, BSTR *); + HRESULT (*get_kbd_shortcut)(IAccessible *, VARIANT, BSTR *); } WinClassVtbl;
typedef struct { @@ -65,6 +66,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); @@ -313,28 +334,20 @@ 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);
+ if (This->vtbl.get_kbd_shortcut) + return This->vtbl.get_kbd_shortcut(iface, varID, pszKeyboardShortcut); + *pszKeyboardShortcut = NULL; 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; - - *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, pszKeyboardShortcut); }
static HRESULT WINAPI Client_get_accFocus(IAccessible *iface, VARIANT *focus) @@ -781,6 +794,29 @@ static HRESULT edit_get_state(IAccessible *iface, VARIANT var_id, VARIANT *out_s return S_OK; }
+static HRESULT edit_get_kbd_shortcut(IAccessible *iface, VARIANT var_id, + BSTR *out_kbd_shortcut) +{ + Client *This = impl_from_Client(iface); + WCHAR name[1024]; + HWND label; + UINT len; + + TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&var_id), out_kbd_shortcut); + + *out_kbd_shortcut = NULL; + if(convert_child_id(&var_id) != CHILDID_SELF || !IsWindow(This->hwnd)) + return E_INVALIDARG; + + label = acc_edit_find_label(This->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); +} + HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj) { IAccessible *acc; @@ -794,6 +830,7 @@ HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj) client->role = ROLE_SYSTEM_TEXT; client->vtbl.get_state = &edit_get_state; client->vtbl.get_name = &edit_get_name; + client->vtbl.get_kbd_shortcut = &edit_get_kbd_shortcut;
hres = IAccessible_QueryInterface(acc, iid, obj); IAccessible_Release(acc);
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 22b8c225dd9..1cfbc7cd8cc 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -31,6 +31,7 @@ typedef struct { HRESULT (*get_state)(IAccessible *, VARIANT, VARIANT *); HRESULT (*get_name)(IAccessible *, VARIANT, BSTR *); HRESULT (*get_kbd_shortcut)(IAccessible *, VARIANT, BSTR *); + HRESULT (*get_value)(IAccessible *, VARIANT, BSTR *); } WinClassVtbl;
typedef struct { @@ -233,6 +234,9 @@ static HRESULT WINAPI Client_get_accValue(IAccessible *iface, VARIANT varID, BST
TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszValue);
+ if (This->vtbl.get_value) + return This->vtbl.get_value(iface, varID, pszValue); + *pszValue = NULL; if(convert_child_id(&varID) != CHILDID_SELF) return E_INVALIDARG; @@ -817,6 +821,33 @@ static HRESULT edit_get_kbd_shortcut(IAccessible *iface, VARIANT var_id, return acc_client_get_kbd_shortcut_str(name, len, out_kbd_shortcut); }
+static HRESULT edit_get_value(IAccessible *iface, VARIANT var_id, BSTR *out_value) +{ + Client *This = impl_from_Client(iface); + WCHAR *buf; + UINT len; + + TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&var_id), out_value); + + *out_value = NULL; + if(convert_child_id(&var_id) != CHILDID_SELF || !IsWindow(This->hwnd)) + return E_INVALIDARG; + + if (GetWindowLongW(This->hwnd, GWL_STYLE) & ES_PASSWORD) + return E_ACCESSDENIED; + + len = SendMessageW(This->hwnd, WM_GETTEXTLENGTH, 0, 0); + buf = heap_alloc_zero((len + 1) * sizeof(*buf)); + if (!buf) + return E_OUTOFMEMORY; + + SendMessageW(This->hwnd, WM_GETTEXT, len + 1, (LPARAM)buf); + *out_value = SysAllocString(buf); + heap_free(buf); + + return S_OK; +} + HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj) { IAccessible *acc; @@ -831,6 +862,7 @@ HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj) client->vtbl.get_state = &edit_get_state; client->vtbl.get_name = &edit_get_name; client->vtbl.get_kbd_shortcut = &edit_get_kbd_shortcut; + client->vtbl.get_value = &edit_get_value;
hres = IAccessible_QueryInterface(acc, iid, obj); IAccessible_Release(acc);
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/client.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 1cfbc7cd8cc..38a3111ac04 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -32,6 +32,7 @@ typedef struct { HRESULT (*get_name)(IAccessible *, VARIANT, BSTR *); HRESULT (*get_kbd_shortcut)(IAccessible *, VARIANT, BSTR *); HRESULT (*get_value)(IAccessible *, VARIANT, BSTR *); + HRESULT (*put_value)(IAccessible *, VARIANT, BSTR); } WinClassVtbl;
typedef struct { @@ -496,7 +497,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); + FIXME("(%p)->(%s %s)\n", This, debugstr_variant(&varID), debugstr_w(pszValue)); + + if (This->vtbl.put_value) + return This->vtbl.put_value(iface, varID, pszValue); + return E_NOTIMPL; }
@@ -848,6 +854,20 @@ static HRESULT edit_get_value(IAccessible *iface, VARIANT var_id, BSTR *out_valu return S_OK; }
+static HRESULT edit_put_value(IAccessible *iface, VARIANT var_id, BSTR value) +{ + Client *This = impl_from_Client(iface); + + TRACE("(%p)->(%s %s)\n", This, debugstr_variant(&var_id), debugstr_w(value)); + + if(convert_child_id(&var_id) != CHILDID_SELF || !IsWindow(This->hwnd)) + return E_INVALIDARG; + + SendMessageW(This->hwnd, WM_SETTEXT, 0, (LPARAM)value); + + return S_OK; +} + HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj) { IAccessible *acc; @@ -863,6 +883,7 @@ HRESULT create_edit_client_object(HWND hwnd, const IID *iid, void **obj) client->vtbl.get_name = &edit_get_name; client->vtbl.get_kbd_shortcut = &edit_get_kbd_shortcut; client->vtbl.get_value = &edit_get_value; + client->vtbl.put_value = &edit_put_value;
hres = IAccessible_QueryInterface(acc, iid, obj); IAccessible_Release(acc);
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();