| /* |
| * Multisource AutoComplete list |
| * |
| * Copyright 2007 Mikolaj Zalewski |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| |
| #include <stdarg.h> |
| |
| #define COBJMACROS |
| |
| #include "wine/debug.h" |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winreg.h" |
| #include "winuser.h" |
| #include "shlwapi.h" |
| #include "winerror.h" |
| #include "objbase.h" |
| |
| #include "shlguid.h" |
| #include "shlobj.h" |
| |
| #include "wine/unicode.h" |
| |
| #include "browseui.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(browseui); |
| |
| struct ACLMultiSublist { |
| IUnknown *punk; |
| IEnumString *pEnum; |
| IACList *pACL; |
| }; |
| |
| typedef struct tagACLMulti { |
| const IEnumStringVtbl *vtbl; |
| const IACListVtbl *aclVtbl; |
| const IObjMgrVtbl *objmgrVtbl; |
| LONG refCount; |
| INT nObjs; |
| INT currObj; |
| struct ACLMultiSublist *objs; |
| } ACLMulti; |
| |
| static inline ACLMulti *impl_from_IACList(IACList *iface) |
| { |
| return (ACLMulti *)((char *)iface - FIELD_OFFSET(ACLMulti, aclVtbl)); |
| } |
| |
| static inline ACLMulti *impl_from_IObjMgr(IObjMgr *iface) |
| { |
| return (ACLMulti *)((char *)iface - FIELD_OFFSET(ACLMulti, objmgrVtbl)); |
| } |
| |
| static void release_obj(struct ACLMultiSublist *obj) |
| { |
| IUnknown_Release(obj->punk); |
| if (obj->pEnum) |
| IEnumString_Release(obj->pEnum); |
| if (obj->pACL) |
| IACList_Release(obj->pACL); |
| } |
| |
| static void ACLMulti_Destructor(ACLMulti *This) |
| { |
| int i; |
| TRACE("destroying %p\n", This); |
| for (i = 0; i < This->nObjs; i++) |
| release_obj(&This->objs[i]); |
| heap_free(This->objs); |
| heap_free(This); |
| BROWSEUI_refCount--; |
| } |
| |
| static HRESULT WINAPI ACLMulti_QueryInterface(IEnumString *iface, REFIID iid, LPVOID *ppvOut) |
| { |
| ACLMulti *This = (ACLMulti *)iface; |
| *ppvOut = NULL; |
| |
| if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumString)) |
| { |
| *ppvOut = This; |
| } |
| else if (IsEqualIID(iid, &IID_IACList)) |
| { |
| *ppvOut = &This->aclVtbl; |
| } |
| else if (IsEqualIID(iid, &IID_IObjMgr)) |
| { |
| *ppvOut = &This->objmgrVtbl; |
| } |
| |
| if (*ppvOut) |
| { |
| IUnknown_AddRef(iface); |
| return S_OK; |
| } |
| |
| WARN("unsupported interface: %s\n", debugstr_guid(iid)); |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI ACLMulti_AddRef(IEnumString *iface) |
| { |
| ACLMulti *This = (ACLMulti *)iface; |
| return InterlockedIncrement(&This->refCount); |
| } |
| |
| static ULONG WINAPI ACLMulti_Release(IEnumString *iface) |
| { |
| ACLMulti *This = (ACLMulti *)iface; |
| ULONG ret; |
| |
| ret = InterlockedDecrement(&This->refCount); |
| if (ret == 0) |
| ACLMulti_Destructor(This); |
| return ret; |
| } |
| |
| static HRESULT WINAPI ACLMulti_Append(IObjMgr *iface, IUnknown *obj) |
| { |
| ACLMulti *This = impl_from_IObjMgr(iface); |
| |
| TRACE("(%p, %p)\n", This, obj); |
| if (obj == NULL) |
| return E_FAIL; |
| |
| This->objs = heap_realloc(This->objs, sizeof(This->objs[0]) * (This->nObjs+1)); |
| This->objs[This->nObjs].punk = obj; |
| IUnknown_AddRef(obj); |
| if (FAILED(IUnknown_QueryInterface(obj, &IID_IEnumString, (LPVOID *)&This->objs[This->nObjs].pEnum))) |
| This->objs[This->nObjs].pEnum = NULL; |
| if (FAILED(IUnknown_QueryInterface(obj, &IID_IACList, (LPVOID *)&This->objs[This->nObjs].pACL))) |
| This->objs[This->nObjs].pACL = NULL; |
| This->nObjs++; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ACLMulti_Remove(IObjMgr *iface, IUnknown *obj) |
| { |
| ACLMulti *This = impl_from_IObjMgr(iface); |
| int i; |
| |
| TRACE("(%p, %p)\n", This, obj); |
| for (i = 0; i < This->nObjs; i++) |
| if (This->objs[i].punk == obj) |
| { |
| release_obj(&This->objs[i]); |
| memmove(&This->objs[i], &This->objs[i+1], (This->nObjs-i-1)*sizeof(struct ACLMultiSublist)); |
| This->nObjs--; |
| This->objs = heap_realloc(This->objs, sizeof(This->objs[0]) * This->nObjs); |
| return S_OK; |
| } |
| |
| return E_FAIL; |
| } |
| |
| static HRESULT WINAPI ACLMulti_Next(IEnumString *iface, ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) |
| { |
| ACLMulti *This = (ACLMulti *)iface; |
| |
| TRACE("(%p, %d, %p, %p)\n", iface, celt, rgelt, pceltFetched); |
| while (This->currObj < This->nObjs) |
| { |
| if (This->objs[This->currObj].pEnum) |
| { |
| /* native browseui 6.0 also returns only one element */ |
| HRESULT ret = IEnumString_Next(This->objs[This->currObj].pEnum, 1, rgelt, pceltFetched); |
| if (ret != S_FALSE) |
| return ret; |
| } |
| This->currObj++; |
| } |
| |
| if (pceltFetched) |
| *pceltFetched = 0; |
| *rgelt = NULL; |
| return S_FALSE; |
| } |
| |
| static HRESULT WINAPI ACLMulti_Reset(IEnumString *iface) |
| { |
| ACLMulti *This = (ACLMulti *)iface; |
| int i; |
| |
| This->currObj = 0; |
| for (i = 0; i < This->nObjs; i++) |
| { |
| if (This->objs[i].pEnum) |
| IEnumString_Reset(This->objs[i].pEnum); |
| } |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI ACLMulti_Skip(IEnumString *iface, ULONG celt) |
| { |
| /* native browseui 6.0 returns this: */ |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI ACLMulti_Clone(IEnumString *iface, IEnumString **ppOut) |
| { |
| *ppOut = NULL; |
| /* native browseui 6.0 returns this: */ |
| return E_OUTOFMEMORY; |
| } |
| |
| static HRESULT WINAPI ACLMulti_Expand(IACList *iface, LPCWSTR wstr) |
| { |
| ACLMulti *This = impl_from_IACList(iface); |
| HRESULT res = S_OK; |
| int i; |
| |
| for (i = 0; i < This->nObjs; i++) |
| { |
| if (!This->objs[i].pACL) |
| continue; |
| res = IACList_Expand(This->objs[i].pACL, wstr); |
| /* Vista behaviour - XP would break out of the loop if res == S_OK (usually calling Expand only once) */ |
| } |
| return res; |
| } |
| |
| static const IEnumStringVtbl ACLMultiVtbl = |
| { |
| ACLMulti_QueryInterface, |
| ACLMulti_AddRef, |
| ACLMulti_Release, |
| |
| ACLMulti_Next, |
| ACLMulti_Skip, |
| ACLMulti_Reset, |
| ACLMulti_Clone |
| }; |
| |
| static HRESULT WINAPI ACLMulti_IObjMgr_QueryInterface(IObjMgr *iface, REFIID iid, LPVOID *ppvOut) |
| { |
| ACLMulti *This = impl_from_IObjMgr(iface); |
| return ACLMulti_QueryInterface((IEnumString *)This, iid, ppvOut); |
| } |
| |
| static ULONG WINAPI ACLMulti_IObjMgr_AddRef(IObjMgr *iface) |
| { |
| ACLMulti *This = impl_from_IObjMgr(iface); |
| return ACLMulti_AddRef((IEnumString *)This); |
| } |
| |
| static ULONG WINAPI ACLMulti_IObjMgr_Release(IObjMgr *iface) |
| { |
| ACLMulti *This = impl_from_IObjMgr(iface); |
| return ACLMulti_Release((IEnumString *)This); |
| } |
| |
| static const IObjMgrVtbl ACLMulti_ObjMgrVtbl = |
| { |
| ACLMulti_IObjMgr_QueryInterface, |
| ACLMulti_IObjMgr_AddRef, |
| ACLMulti_IObjMgr_Release, |
| |
| ACLMulti_Append, |
| ACLMulti_Remove |
| }; |
| |
| static HRESULT WINAPI ACLMulti_IACList_QueryInterface(IACList *iface, REFIID iid, LPVOID *ppvOut) |
| { |
| ACLMulti *This = impl_from_IACList(iface); |
| return ACLMulti_QueryInterface((IEnumString *)This, iid, ppvOut); |
| } |
| |
| static ULONG WINAPI ACLMulti_IACList_AddRef(IACList *iface) |
| { |
| ACLMulti *This = impl_from_IACList(iface); |
| return ACLMulti_AddRef((IEnumString *)This); |
| } |
| |
| static ULONG WINAPI ACLMulti_IACList_Release(IACList *iface) |
| { |
| ACLMulti *This = impl_from_IACList(iface); |
| return ACLMulti_Release((IEnumString *)This); |
| } |
| |
| static const IACListVtbl ACLMulti_ACListVtbl = |
| { |
| ACLMulti_IACList_QueryInterface, |
| ACLMulti_IACList_AddRef, |
| ACLMulti_IACList_Release, |
| |
| ACLMulti_Expand |
| }; |
| |
| HRESULT ACLMulti_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) |
| { |
| ACLMulti *This; |
| if (pUnkOuter) |
| return CLASS_E_NOAGGREGATION; |
| |
| This = heap_alloc_zero(sizeof(ACLMulti)); |
| if (This == NULL) |
| return E_OUTOFMEMORY; |
| |
| This->vtbl = &ACLMultiVtbl; |
| This->aclVtbl = &ACLMulti_ACListVtbl; |
| This->objmgrVtbl = &ACLMulti_ObjMgrVtbl; |
| This->refCount = 1; |
| |
| TRACE("returning %p\n", This); |
| *ppOut = (IUnknown *)This; |
| BROWSEUI_refCount++; |
| return S_OK; |
| } |