| /* | |
| * 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 { | |
| IEnumString IEnumString_iface; | |
| IACList IACList_iface; | |
| IObjMgr IObjMgr_iface; | |
| LONG refCount; | |
| INT nObjs; | |
| INT currObj; | |
| struct ACLMultiSublist *objs; | |
| } ACLMulti; | |
| static inline ACLMulti *impl_from_IEnumString(IEnumString *iface) | |
| { | |
| return CONTAINING_RECORD(iface, ACLMulti, IEnumString_iface); | |
| } | |
| static inline ACLMulti *impl_from_IACList(IACList *iface) | |
| { | |
| return CONTAINING_RECORD(iface, ACLMulti, IACList_iface); | |
| } | |
| static inline ACLMulti *impl_from_IObjMgr(IObjMgr *iface) | |
| { | |
| return CONTAINING_RECORD(iface, ACLMulti, IObjMgr_iface); | |
| } | |
| 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 = impl_from_IEnumString(iface); | |
| *ppvOut = NULL; | |
| if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumString)) | |
| { | |
| *ppvOut = &This->IEnumString_iface; | |
| } | |
| else if (IsEqualIID(iid, &IID_IACList)) | |
| { | |
| *ppvOut = &This->IACList_iface; | |
| } | |
| else if (IsEqualIID(iid, &IID_IObjMgr)) | |
| { | |
| *ppvOut = &This->IObjMgr_iface; | |
| } | |
| if (*ppvOut) | |
| { | |
| IEnumString_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 = impl_from_IEnumString(iface); | |
| return InterlockedIncrement(&This->refCount); | |
| } | |
| static ULONG WINAPI ACLMulti_Release(IEnumString *iface) | |
| { | |
| ACLMulti *This = impl_from_IEnumString(iface); | |
| ULONG ret; | |
| ret = InterlockedDecrement(&This->refCount); | |
| if (ret == 0) | |
| ACLMulti_Destructor(This); | |
| return ret; | |
| } | |
| static HRESULT WINAPI ACLMulti_Next(IEnumString *iface, ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) | |
| { | |
| ACLMulti *This = impl_from_IEnumString(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 = impl_from_IEnumString(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 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 IEnumString_QueryInterface(&This->IEnumString_iface, iid, ppvOut); | |
| } | |
| static ULONG WINAPI ACLMulti_IObjMgr_AddRef(IObjMgr *iface) | |
| { | |
| ACLMulti *This = impl_from_IObjMgr(iface); | |
| return IEnumString_AddRef(&This->IEnumString_iface); | |
| } | |
| static ULONG WINAPI ACLMulti_IObjMgr_Release(IObjMgr *iface) | |
| { | |
| ACLMulti *This = impl_from_IObjMgr(iface); | |
| return IEnumString_Release(&This->IEnumString_iface); | |
| } | |
| 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 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 IEnumString_QueryInterface(&This->IEnumString_iface, iid, ppvOut); | |
| } | |
| static ULONG WINAPI ACLMulti_IACList_AddRef(IACList *iface) | |
| { | |
| ACLMulti *This = impl_from_IACList(iface); | |
| return IEnumString_AddRef(&This->IEnumString_iface); | |
| } | |
| static ULONG WINAPI ACLMulti_IACList_Release(IACList *iface) | |
| { | |
| ACLMulti *This = impl_from_IACList(iface); | |
| return IEnumString_Release(&This->IEnumString_iface); | |
| } | |
| 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 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->IEnumString_iface.lpVtbl = &ACLMultiVtbl; | |
| This->IACList_iface.lpVtbl = &ACLMulti_ACListVtbl; | |
| This->IObjMgr_iface.lpVtbl = &ACLMulti_ObjMgrVtbl; | |
| This->refCount = 1; | |
| TRACE("returning %p\n", This); | |
| *ppOut = (IUnknown *)&This->IEnumString_iface; | |
| BROWSEUI_refCount++; | |
| return S_OK; | |
| } |