358 lines
8.2 KiB
C++
358 lines
8.2 KiB
C++
#include "shellprv.h"
|
|
#include "idltree.h"
|
|
|
|
BOOL CIDLData::Init(IDLDATAF flags, INT_PTR data)
|
|
{
|
|
_flags = flags;
|
|
_data = data;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT CIDLData::GetData(IDLDATAF flags, INT_PTR *pdata)
|
|
{
|
|
if (flags & _flags)
|
|
{
|
|
// we have a match
|
|
*pdata = _data;
|
|
return S_OK;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
BOOL CIDLNode::Init(LPCITEMIDLIST pidl, CIDLNode *pinParent)
|
|
{
|
|
_pidl = ILCloneFirst(pidl);
|
|
_pinParent = pinParent;
|
|
return _pidl != NULL;
|
|
}
|
|
|
|
CIDLNode::~CIDLNode()
|
|
{
|
|
ILFree(_pidl);
|
|
|
|
if (_psf)
|
|
_psf->Release();
|
|
}
|
|
|
|
BOOL CIDLNode::_InitSF()
|
|
{
|
|
// TODO MAYBE LATER - add per thread cacheing instead.
|
|
// this way we can insure nonviolation of apartments
|
|
if (!_psf)
|
|
{
|
|
if (_pinParent)
|
|
_pinParent->_BindToFolder(_pidl, &_psf);
|
|
else
|
|
SHGetDesktopFolder(&_psf);
|
|
|
|
_cUsage++;
|
|
}
|
|
|
|
return (_psf != NULL);
|
|
}
|
|
|
|
HRESULT CIDLNode::_BindToFolder(LPCITEMIDLIST pidl, IShellFolder **ppsf)
|
|
{
|
|
if (_InitSF())
|
|
{
|
|
_cUsage++;
|
|
return _psf->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, ppsf));
|
|
}
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
BOOL CIDLNode::_IsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
int iRet = ShortFromResult(IShellFolder_CompareIDs(_psf, SHCIDS_CANONICALONLY, pidl1, pidl2));
|
|
return (iRet == 0);
|
|
}
|
|
|
|
CLinkedNode<CIDLNode> *CIDLNode::_GetKid(LPCITEMIDLIST pidl)
|
|
{
|
|
CLinkedWalk<CIDLNode> lw(&_listKids);
|
|
// WARNING - need to avoid a real allocation - ZekeL - 27-SEP-2000
|
|
// when we are just doing a seek
|
|
// it creates weird state problems
|
|
LPITEMIDLIST pidlStack = (LPITEMIDLIST)alloca(pidl->mkid.cb + sizeof(pidl->mkid.cb));
|
|
memcpy(pidlStack, pidl, pidl->mkid.cb);
|
|
(_ILNext(pidlStack))->mkid.cb = 0;
|
|
|
|
while (lw.Step())
|
|
{
|
|
if (_IsEqual(lw.That()->_pidl, pidlStack))
|
|
{
|
|
return lw.Node();
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#define IsValidIDLNODE(pin) IS_VALID_WRITE_BUFFER(pin, BYTE, SIZEOF(CIDLNode))
|
|
|
|
#define _IsEmptyNode(pin) (!(pin)->_pinKids && !(pin)->_pidDatas)
|
|
|
|
void CIDLNode::_FreshenKids(void)
|
|
{
|
|
CLinkedWalk<CIDLNode> lw(&_listKids);
|
|
LONG cMostUsage = 0;
|
|
|
|
while (lw.Step())
|
|
{
|
|
CIDLNode *pin = lw.That();
|
|
LONG cUsage = pin->_cUsage;
|
|
pin->_cUsage = 0;
|
|
|
|
ASSERT(IsValidIDLNODE(pin));
|
|
pin->_FreshenKids();
|
|
ASSERT(IsValidIDLNODE(pin));
|
|
|
|
if (!cUsage && pin->_IsEmpty())
|
|
{
|
|
lw.Delete();
|
|
}
|
|
if (cUsage > cMostUsage && !lw.IsFirst())
|
|
{
|
|
// simple sorting algorithm
|
|
// we just want most used at the top
|
|
// move it from its current spot
|
|
// to the beginning of the list
|
|
CLinkedNode<CIDLNode> *p = lw.Remove();
|
|
_listKids.Insert(p);
|
|
}
|
|
|
|
cMostUsage = max(cUsage, cMostUsage);
|
|
}
|
|
}
|
|
|
|
HRESULT CIDLNode::GetNode(BOOL fCreate, LPCITEMIDLIST pidlChild, CIDLNode **ppin, IDLDATAF *pflagsFound)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
if (ILIsEmpty(pidlChild))
|
|
{
|
|
// this is just a request for self
|
|
*ppin = this;
|
|
if (pflagsFound)
|
|
*pflagsFound = IDLDATAF_MATCH_RECURSIVE;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
// search through kids looking for this child
|
|
*ppin = NULL;
|
|
CLinkedNode<CIDLNode> *pKid = _GetKid(pidlChild);
|
|
|
|
if (!pKid && fCreate)
|
|
{
|
|
// we need to allocations during fCreate
|
|
// so that memory failures dont affect Remove
|
|
if (_InitSF())
|
|
{
|
|
// we dont have it, and they want it anyway
|
|
pKid = new CLinkedNode<CIDLNode>;
|
|
|
|
// we give our pidl ref away to avoid allocations
|
|
if (pKid)
|
|
{
|
|
if (pKid->that.Init(pidlChild, this))
|
|
_listKids.Insert(pKid);
|
|
else
|
|
{
|
|
delete pKid;
|
|
pKid = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// let the child take care of setting
|
|
if (pKid)
|
|
{
|
|
pKid->that._cUsage++;
|
|
pidlChild = _ILNext(pidlChild);
|
|
hr = pKid->that.GetNode(fCreate, pidlChild, ppin, pflagsFound);
|
|
}
|
|
|
|
if (FAILED(hr) && !fCreate && pflagsFound)
|
|
{
|
|
// just return this as second best
|
|
*ppin = this;
|
|
ASSERT(!ILIsEmpty(pidlChild));
|
|
|
|
if (ILIsEmpty(_ILNext(pidlChild)))
|
|
*pflagsFound = IDLDATAF_MATCH_RECURSIVE & ~IDLDATAF_MATCH_EXACT;
|
|
else
|
|
*pflagsFound = IDLDATAF_MATCH_RECURSIVE & ~IDLDATAF_MATCH_IMMEDIATE;
|
|
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CIDLNode::IDList(LPITEMIDLIST *ppidl)
|
|
{
|
|
CIDLNode *pin = this;
|
|
*ppidl = NULL;
|
|
while (pin && pin->_pidl)
|
|
{
|
|
*ppidl = ILAppendID(*ppidl, &pin->_pidl->mkid, FALSE);
|
|
pin = pin->_pinParent;
|
|
}
|
|
|
|
return *ppidl ? S_OK : E_FAIL;
|
|
}
|
|
|
|
HRESULT CIDLNode::_AddData(IDLDATAF flags, INT_PTR data)
|
|
{
|
|
// assuming unique/no collisions of Datas
|
|
CLinkedNode<CIDLData> *p = new CLinkedNode<CIDLData>;
|
|
|
|
if (p)
|
|
{
|
|
p->that.Init(flags, data);
|
|
_listDatas.Insert(p);
|
|
}
|
|
|
|
return p ? S_OK : E_FAIL;
|
|
}
|
|
|
|
HRESULT CIDLNode::_RemoveData(INT_PTR data)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CLinkedWalk<CIDLData> lw(&_listDatas);
|
|
|
|
while (lw.Step())
|
|
{
|
|
if (lw.That()->_data == data)
|
|
{
|
|
lw.Delete();
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CIDLTree::Create(CIDLTree **pptree)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
*pptree = new CIDLTree();
|
|
if (*pptree)
|
|
{
|
|
hr = SHILClone(&c_idlDesktop, &((*pptree)->_pidl));
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
delete *pptree;
|
|
*pptree = NULL;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CIDLTree::AddData(IDLDATAF flags, LPCITEMIDLIST pidlIndex, INT_PTR data)
|
|
{
|
|
CIDLNode *pin;
|
|
if (SUCCEEDED(GetNode(TRUE, pidlIndex, &pin)))
|
|
{
|
|
return pin->_AddData(flags, data);
|
|
}
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT CIDLTree::RemoveData(LPCITEMIDLIST pidlIndex, INT_PTR data)
|
|
{
|
|
CIDLNode *pin;
|
|
if (SUCCEEDED(GetNode(FALSE, pidlIndex, &pin)))
|
|
{
|
|
return pin->_RemoveData(data);
|
|
}
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
CIDLNode *CIDLTree::_MatchNode(LPCITEMIDLIST pidlMatch, IDLMATCHF *pflags)
|
|
{
|
|
CIDLNode *pin;
|
|
IDLMATCHF flagsFound;
|
|
HRESULT hr = GetNode(FALSE, pidlMatch, &pin, &flagsFound);
|
|
|
|
if (SUCCEEDED(hr) && (flagsFound & (*pflags)))
|
|
{
|
|
*pflags &= flagsFound;
|
|
}
|
|
else
|
|
pin = NULL;
|
|
|
|
return pin;
|
|
}
|
|
|
|
HRESULT CIDLTree::MatchOne(IDLMATCHF flags, LPCITEMIDLIST pidlMatch, INT_PTR *pdata, LPITEMIDLIST *ppidl)
|
|
{
|
|
CIDLNode *pin = _MatchNode(pidlMatch, &flags);
|
|
|
|
if (pin)
|
|
{
|
|
CIDLMatchMany mm(flags, pin);
|
|
|
|
return mm.Next(pdata, ppidl);
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CIDLTree::MatchMany(IDLMATCHF flags, LPCITEMIDLIST pidlMatch, CIDLMatchMany **ppmatch)
|
|
{
|
|
CIDLNode *pin = _MatchNode(pidlMatch, &flags);
|
|
if (pin)
|
|
{
|
|
*ppmatch = new CIDLMatchMany(flags, pin);
|
|
|
|
return *ppmatch ? S_OK : E_FAIL;
|
|
}
|
|
|
|
*ppmatch = NULL;
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CIDLTree::Freshen(void)
|
|
{
|
|
_FreshenKids();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CIDLMatchMany::Next(INT_PTR *pdata, LPITEMIDLIST *ppidl)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
while (_pin && (_flags & IDLDATAF_MATCH_RECURSIVE))
|
|
{
|
|
if (_lw.Step())
|
|
{
|
|
hr = _lw.That()->GetData(_flags, pdata);
|
|
if (SUCCEEDED(hr) && ppidl)
|
|
{
|
|
hr = _pin->IDList(ppidl);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
_pin = _pin->_pinParent;
|
|
if (_pin)
|
|
{
|
|
_lw.Init(&_pin->_listDatas);
|
|
// adjust the flags as you go up the parent chain.
|
|
if (_flags & IDLDATAF_MATCH_EXACT)
|
|
_flags &= ~IDLDATAF_MATCH_EXACT;
|
|
else if (_flags & IDLDATAF_MATCH_IMMEDIATE)
|
|
_flags &= ~IDLDATAF_MATCH_IMMEDIATE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|