|
|
//-------------------------------------------------------------------------//
//
// AugMisf.cpp - Augmented Merge IShellFolder class implementation.
//
//-------------------------------------------------------------------------//
#include "priv.h"
#include "augmisf.h"
#include "resource.h"
#include "mluisupp.h"
#define TF_AUGM 0x10000000
//-------------------------------------------------------------------------//
// BUGBUG: Shell allocator bullchit, inserted here because SHRealloc
// isn't imported into browseui, this module's hosting executable.
// If we get SHRealloc, the following block can be removed:
#define _EXPL_SHELL_ALLOCATOR_
#ifdef _EXPL_SHELL_ALLOCATOR_
#define SHRealloc( pv, cb ) shrealloc( pv, cb )
void* shrealloc( void* pv, size_t cb ) { IMalloc* pMalloc ; void* pvRet = NULL ; if( SUCCEEDED( SHGetMalloc( &pMalloc ) ) ) { pvRet = pMalloc->Realloc( pv, cb ) ; ATOMICRELEASE( pMalloc ) ; } return pvRet ; }
#endif _EXPL_SHELL_ALLOCATOR_
BOOL AffectAllUsers(HWND hwnd);
// id - verb mappings for IContextMenu impl
const struct { UINT id; LPCSTR pszVerb; } c_sIDVerbMap[] = { {SMIDM_DELETE, "delete"}, {SMIDM_RENAME, "rename"}, {SMIDM_PROPERTIES, "properties"}, //{SMIDM_OPEN, "open"},
//{SMIDM_EXPLORE, "explore"},
};
// augmisf context menu
class CAugMergeISFContextMenu : public IContextMenu2 { public: // *** IUnknown methods ***
STDMETHOD (QueryInterface)(REFIID, void**); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)();
// *** IContextMenu methods ***
STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici); STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax);
// *** IContextMenu2 methods ***
STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
protected: CAugMergeISFContextMenu(IShellFolder *psfCommon, LPCITEMIDLIST pidlCommon, IShellFolder *psfUser, LPCITEMIDLIST pidlUser, LPITEMIDLIST pidl, HWND hwnd, UINT * prgfInOut); ~CAugMergeISFContextMenu();
friend class CAugmentedMergeISF; friend CAugMergeISFContextMenu* CreateMergeISFContextMenu( IShellFolder *psfCommon, LPCITEMIDLIST pidlCommon, IShellFolder *psfUser, LPCITEMIDLIST pidlUser, LPITEMIDLIST pidl, HWND hwnd, UINT * prgfInOut); protected: LPITEMIDLIST _pidlItem; IShellFolder * _psfCommon; IShellFolder * _psfUser; IContextMenu * _pcmCommon; IContextMenu * _pcmUser; LPITEMIDLIST _pidlCommon; LPITEMIDLIST _pidlUser; UINT _idFirst; LONG _cRef; HWND _hwnd; };
CAugMergeISFContextMenu* CreateMergeISFContextMenu( IShellFolder *psfCommon, LPCITEMIDLIST pidlCommon, IShellFolder *psfUser, LPCITEMIDLIST pidlUser, LPITEMIDLIST pidl, HWND hwnd, UINT * prgfInOut) { CAugMergeISFContextMenu* pcm = new CAugMergeISFContextMenu(psfCommon, pidlCommon, psfUser, pidlUser, pidl, hwnd, prgfInOut); if (pcm) { if (!pcm->_pidlItem) { delete pcm; pcm = NULL; } } return pcm; }
CAugMergeISFContextMenu::CAugMergeISFContextMenu(IShellFolder *psfCommon, LPCITEMIDLIST pidlCommon, IShellFolder *psfUser, LPCITEMIDLIST pidlUser, LPITEMIDLIST pidl, HWND hwnd, UINT * prgfInOut) { _cRef = 1; HRESULT hres;
_hwnd = hwnd; _psfCommon = psfCommon; if (_psfCommon) { _psfCommon->AddRef(); hres = _psfCommon->GetUIObjectOf(hwnd, 1, (LPCITEMIDLIST*)&pidl, IID_IContextMenu, prgfInOut, (void **)&_pcmCommon);
ASSERT(SUCCEEDED(hres) || !_pcmCommon); _pidlCommon = ILClone(pidlCommon); } _psfUser = psfUser; if (_psfUser) { _psfUser->AddRef(); hres = _psfUser->GetUIObjectOf(hwnd, 1, (LPCITEMIDLIST*)&pidl, IID_IContextMenu, prgfInOut, (void **)&_pcmUser);
ASSERT(SUCCEEDED(hres) || !_pcmUser); _pidlUser = ILClone(pidlUser); } _pidlItem = ILClone(pidl); ASSERT(_psfCommon || _psfUser); }
CAugMergeISFContextMenu::~CAugMergeISFContextMenu() { ATOMICRELEASE(_psfCommon); ATOMICRELEASE(_pcmCommon); ATOMICRELEASE(_psfUser); ATOMICRELEASE(_pcmUser); ILFree(_pidlCommon); ILFree(_pidlUser); ILFree(_pidlItem); }
STDMETHODIMP CAugMergeISFContextMenu::QueryInterface(REFIID riid, LPVOID *ppvOut) { static const QITAB qit[] = { QITABENTMULTI(CAugMergeISFContextMenu, IContextMenu, IContextMenu2), QITABENT(CAugMergeISFContextMenu, IContextMenu2), { 0 }, }; return QISearch(this, qit, riid, ppvOut); }
STDMETHODIMP_(ULONG) CAugMergeISFContextMenu::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CAugMergeISFContextMenu::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
HRESULT CAugMergeISFContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { HRESULT hres = E_INVALIDARG; if (hmenu) { HMENU hmContext = LoadMenuPopup(MENU_SM_CONTEXTMENU); if (hmContext) { if (!_psfCommon || !_psfUser) { DeleteMenu(hmContext, SMIDM_OPENCOMMON, MF_BYCOMMAND); DeleteMenu(hmContext, SMIDM_EXPLORECOMMON, MF_BYCOMMAND); }
_idFirst = idCmdFirst; Shell_MergeMenus(hmenu, hmContext, -1, idCmdFirst, idCmdLast, MM_ADDSEPARATOR); DestroyMenu(hmContext);
// Make it look "Shell Like"
SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION);
hres = S_OK; } else hres = E_OUTOFMEMORY; } return hres; }
HRESULT CAugMergeISFContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici) { UINT id = -1; HRESULT hres = E_FAIL; CMINVOKECOMMANDINFO ici = *pici;
if (pici->cbSize < SIZEOF(CMINVOKECOMMANDINFO)) return E_INVALIDARG;
if (HIWORD(pici->lpVerb)) { for (int i=0; i < ARRAYSIZE(c_sIDVerbMap); i++) { if (lstrcmpiA(pici->lpVerb, c_sIDVerbMap[i].pszVerb) == 0) { id = c_sIDVerbMap[i].id; break; } } } else id = (UINT) PtrToUlong( pici->lpVerb ); // Win64: should be ok since MAKEINTRESOURCE assumed
switch (id) { case -1: hres = E_INVALIDARG; break;
case SMIDM_OPEN: case SMIDM_EXPLORE: case SMIDM_OPENCOMMON: case SMIDM_EXPLORECOMMON: { IShellFolder * psf; LPITEMIDLIST pidl;
if (id == SMIDM_OPEN || id == SMIDM_EXPLORE) { if (_psfUser) { psf = _psfUser; pidl = _pidlUser; } else { psf = _psfCommon; pidl = _pidlCommon; } } else { psf = _psfCommon; pidl = _pidlCommon; } if (psf && pidl) { SHELLEXECUTEINFO shei = {0};
shei.lpIDList = ILCombine(pidl, _pidlItem); if (shei.lpIDList) { shei.cbSize = sizeof(shei); shei.fMask = SEE_MASK_IDLIST; shei.nShow = SW_SHOWNORMAL; if (id == SMIDM_EXPLORE || id == SMIDM_EXPLORECOMMON) shei.lpVerb = TEXT("explore"); else shei.lpVerb = TEXT("open"); hres = ShellExecuteEx(&shei) ? S_OK : E_FAIL; ILFree((LPITEMIDLIST)shei.lpIDList); } } } break;
case SMIDM_PROPERTIES: { IContextMenu * pcm = _pcmUser ? _pcmUser : _pcmCommon;
if (pcm) { ici.lpVerb = "properties"; hres = pcm->InvokeCommand(&ici); } } break; case SMIDM_DELETE: ici.lpVerb = "delete"; hres = S_OK; if (_pcmUser) { hres = _pcmUser->InvokeCommand(&ici); } else if (_pcmCommon) { ici.fMask |= CMIC_MASK_FLAG_NO_UI; if (AffectAllUsers(_hwnd)) hres = _pcmCommon->InvokeCommand(&ici); else hres = E_FAIL; } break; case SMIDM_RENAME: ASSERT(0); hres = E_NOTIMPL; // sftbar picks this off
break; } return hres; }
HRESULT CAugMergeISFContextMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax) { HRESULT hres = E_NOTIMPL;
// if hiword in not null then a string is passed to us. we don't handle that case (yet?)
if (!HIWORD(idCmd) && (uType == GCS_VERBA || uType == GCS_VERBW)) { hres = E_INVALIDARG;
for (int i = 0; hres != S_OK && i < ARRAYSIZE(c_sIDVerbMap); i++) { if (c_sIDVerbMap[i].id == idCmd) { if (uType == GCS_VERBA) { hres = StringCchCopyA(pszName, cchMax, c_sIDVerbMap[i].pszVerb); } else { SHAnsiToUnicode(c_sIDVerbMap[i].pszVerb, (LPWSTR)pszName, cchMax); hres = S_OK; } } } } return hres; }
// we need IContextMenu2 although HandleMenuMsg is not impl because of the way sftbar
// works -- it caches only IContextMenu2 so if we don't have ICM2 sftbar will think
// that it does not have context menu so it will eat the messages intended for the hmenu
// that way, with context menu up, if user presses esc it will kill start menu sub menu
// not the context menu.
HRESULT CAugMergeISFContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) { return E_NOTIMPL; } //-------------------------------------------------------------------------//
// Augmented Merge Shell Folder's pidl wrapper package consists of a versioned
// header followed by n 'source namespace' pidl wrappers.
// Each individual pidl wrapper consists of a header containing a
// collection lookup index followed by the source pidl itself. The
// source pidl's mkid.cb member is used to seek the next pidl wrap in
// the package.
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
//--- Augmented Merge Shell Folder's pidl wrapper header
typedef struct tagAUGM_IDWRAP { USHORT cb ; // pidl wrap length
USHORT Reserved ; // reserved.
ULONG tag ; // AugMergeISF pidl signature
ULONG version ; // AugMergeISF pidl version
ULONG cSrcs ; // Number of source namespace objects backing this composite pidl
} AUGM_IDWRAP;
typedef UNALIGNED AUGM_IDWRAP *PAUGM_IDWRAP;
//--- Source pidl header. One or more of these records will be concatenated
// within the wrap following the wrap header.
typedef struct tagAUGM_IDSRC { UINT nID ; // source namespace index
BYTE pidl[0] ; // source pidl
} AUGM_IDSRC;
typedef UNALIGNED AUGM_IDSRC *PAUGM_IDSRC;
//-------------------------------------------------------------------------//
// Constants
//-------------------------------------------------------------------------//
#define AUGM_WRAPTAG MAKELONG( MAKEWORD('A','u'), MAKEWORD('g','M') )
#define AUGM_WRAPVERSION_1_0 MAKELONG( 1, 0 )
#define AUGM_WRAPCURRENTVERSION AUGM_WRAPVERSION_1_0
#define INVALID_NAMESPACE_INDEX ((UINT)-1)
#define CB_IDLIST_TERMINATOR sizeof(USHORT)
//-------------------------------------------------------------------------//
// Augmented Merge shell folder pidl wrap utilities
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// Resolves the wrap header from the indicated pidl.
#define AugMergeISF_GetWrap( p ) ((PAUGM_IDWRAP)(p))
//-------------------------------------------------------------------------//
// Determines whether the indicated pidl is an Augmented Merge
// shell folder pidl wrapper.
HRESULT AugMergeISF_IsWrap( IN LPCITEMIDLIST pidlTest, IN ULONG nVersion = AUGM_WRAPCURRENTVERSION ) { ASSERT(IS_VALID_PIDL( pidlTest ));
if (pidlTest) { PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pidlTest ) ;
return (pWrap->cb >= sizeof(AUGM_IDWRAP) && pWrap->tag == AUGM_WRAPTAG && pWrap->version == nVersion) ? S_OK : E_UNEXPECTED ; //BUGBUG: better error code for version mismatch?
} else { return E_INVALIDARG; } }
//-------------------------------------------------------------------------//
// Retrieves the number of source namespace pidls in the wrap.
// If the pidl was not wrapped, the return value is -1.
ULONG AugMergeISF_GetSourceCount( IN LPCITEMIDLIST pidl ) { ASSERT(SUCCEEDED(AugMergeISF_IsWrap(pidl))); return AugMergeISF_GetWrap(pidl)->cSrcs; }
//-------------------------------------------------------------------------//
// Creates an IDLIST wrapper object based on the indicated source pidl.
HRESULT AugMergeISF_CreateWrap( IN LPCITEMIDLIST pidlSrc, IN UINT nSrcID, OUT LPITEMIDLIST* ppidlWrap ) { ASSERT( ppidlWrap ) ; ASSERT( IS_VALID_PIDL( pidlSrc ) && INVALID_NAMESPACE_INDEX != nSrcID ) ;
*ppidlWrap = NULL ;
// Allocate a header and terminator
LPBYTE pBuf = NULL ; WORD cbAlloc = sizeof(AUGM_IDWRAP) + sizeof(AUGM_IDSRC) + pidlSrc->mkid.cb + // we need two terminators, one for pidlSrc and one for the wrap
// the one for pidlSrc is necessary for the ILClone to work
// because it gets confused with the nSrcID that follows the pidl
CB_IDLIST_TERMINATOR + CB_IDLIST_TERMINATOR ;
if( NULL == (pBuf = (LPBYTE)IEILCreate( cbAlloc )) ) return E_OUTOFMEMORY ;
// Initialize wrap header members
PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pBuf ) ; pWrap->cb = cbAlloc - CB_IDLIST_TERMINATOR ; pWrap->tag = AUGM_WRAPTAG ; pWrap->version = AUGM_WRAPCURRENTVERSION ;
if( pidlSrc ) { PAUGM_IDSRC pSrc = (PAUGM_IDSRC)(pBuf + sizeof(AUGM_IDWRAP)) ; pSrc->nID = nSrcID ; memcpy( pSrc->pidl, pidlSrc, pidlSrc->mkid.cb ) ; pWrap->cSrcs = 1 ; }
*ppidlWrap = (LPITEMIDLIST)pWrap ; return S_OK ; }
BOOL WrappedPidlContainsSrcID(LPCITEMIDLIST pidlWrap, UINT uSrcID) { ASSERT(SUCCEEDED(AugMergeISF_IsWrap(pidlWrap))); PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pidlWrap ) ;
if( pWrap->cSrcs > 0 ) { LPBYTE p = ((LPBYTE)pWrap) + sizeof(AUGM_IDWRAP) ; // position of first pidl header.
PAUGM_IDSRC pSrc = (PAUGM_IDSRC)p ; // offset to next pidl header, needs terminator so that ILClone below can work
UINT cbPidl= ((LPITEMIDLIST)pSrc->pidl)->mkid.cb + CB_IDLIST_TERMINATOR;
if (pSrc->nID != uSrcID && pWrap->cSrcs > 1) { pSrc = (PAUGM_IDSRC)(p + sizeof(AUGM_IDSRC) + cbPidl) ; }
if (pSrc->nID == uSrcID) return TRUE; }
return FALSE; }
HRESULT AugMergeISF_WrapRemovePidl( IN LPITEMIDLIST pidlWrap, IN UINT nSrcID, OUT LPITEMIDLIST* ppidlRet ) { ASSERT( IS_VALID_WRITE_PTR( ppidlRet, LPITEMIDLIST )) ; *ppidlRet = NULL ;
HRESULT hr = AugMergeISF_IsWrap(pidlWrap); if (SUCCEEDED(hr)) { PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pidlWrap ) ;
ASSERT(pWrap->cSrcs > 1);
LPBYTE p = ((LPBYTE)pWrap) + sizeof(AUGM_IDWRAP) ; // position of first pidl header.
PAUGM_IDSRC pSrc = (PAUGM_IDSRC)p ; // offset to next pidl header, needs terminator so that ILClone below can work
UINT cbPidl= ((LPITEMIDLIST)pSrc->pidl)->mkid.cb + CB_IDLIST_TERMINATOR;
// We want to look for the Other SrcID. So we loop while the source id we're removing is
// equal. When it's not equal, we've got the ID.
if (pSrc->nID == nSrcID) { pSrc = (PAUGM_IDSRC)(p + sizeof(AUGM_IDSRC) + cbPidl) ; }
if (pSrc->nID != nSrcID) { hr = AugMergeISF_CreateWrap((LPITEMIDLIST)pSrc->pidl, pSrc->nID, ppidlRet); ILFree(pidlWrap); } else { hr = E_FAIL; } }
return hr; }
//-------------------------------------------------------------------------//
// Adds a source pidl to the indicated pidl wrap.
HRESULT AugMergeISF_WrapAddPidl( IN LPCITEMIDLIST pidlSrc, IN UINT nSrcID, IN OUT LPITEMIDLIST* ppidlWrap ) { ASSERT (ppidlWrap && IS_VALID_PIDL( *ppidlWrap )); ASSERT (IS_VALID_PIDL( pidlSrc )); ASSERT (INVALID_NAMESPACE_INDEX != nSrcID );
HRESULT hr ; if (FAILED((hr = AugMergeISF_IsWrap(*ppidlWrap)))) return hr ;
// AHHHHHHHHHHH Rewrite this.
if (WrappedPidlContainsSrcID(*ppidlWrap, nSrcID)) { if (AugMergeISF_GetSourceCount(*ppidlWrap) > 1) { hr = AugMergeISF_WrapRemovePidl((LPITEMIDLIST)*ppidlWrap, nSrcID, ppidlWrap); } else { ILFree(*ppidlWrap); return AugMergeISF_CreateWrap(pidlSrc, nSrcID, ppidlWrap); }
if (FAILED(hr)) { return hr; } }
// Retrieve wrap header
PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)*ppidlWrap ; // Reallocate a block large enough to contain our new record.
WORD offTerm0 = pWrap->cb, // offset to end of existing wrap
offTerm1 = offTerm0 + sizeof(AUGM_IDSRC) + pidlSrc->mkid.cb, // offset to end of next record
cbRealloc= offTerm1 + 2*CB_IDLIST_TERMINATOR ; // total bytes to reallocate
LPBYTE pRealloc ; if( NULL == (pRealloc = (LPBYTE)SHRealloc( pWrap, cbRealloc )) ) return E_OUTOFMEMORY ;
// Adjust our pointers if memory moved
pWrap = (PAUGM_IDWRAP)pRealloc ;
// Initialize new record in the wrap
UNALIGNED AUGM_IDSRC* pSrc = (PAUGM_IDSRC)(pRealloc + offTerm0 ) ; pSrc->nID = nSrcID ; memcpy( pSrc->pidl, pidlSrc, pidlSrc->mkid.cb ) ;
// Terminate new record
ZeroMemory( pRealloc + offTerm1, 2*CB_IDLIST_TERMINATOR ) ;
// Update our header
pWrap->cb = cbRealloc - CB_IDLIST_TERMINATOR ; pWrap->cSrcs++ ;
*ppidlWrap = (LPITEMIDLIST)pWrap ; return S_OK ; }
//-------------------------------------------------------------------------//
// Private pidl enumeration block (GetFirst/GetNext)
typedef struct tagAUGM_IDWRAP_ENUM { ULONG cbStruct ; // structure size
PAUGM_IDWRAP pWrap ; // wrap header.
PAUGM_IDSRC pSrcNext ; // pointer to next src header
} AUGM_IDWRAP_ENUM, *PAUGM_IDWRAP_ENUM ;
//-------------------------------------------------------------------------//
// Begins enumeration of source pidls in the indicated pidl wrap.
HANDLE AugMergeISF_EnumFirstSrcPidl( IN LPCITEMIDLIST pidlWrap, OUT UINT* pnSrcID, OUT LPITEMIDLIST* ppidlRet ) { ASSERT( IS_VALID_WRITE_PTR( ppidlRet, LPITEMIDLIST ) && IS_VALID_WRITE_PTR( pnSrcID, UINT ) ) ; PAUGM_IDWRAP_ENUM pEnum = NULL ; *ppidlRet = NULL ; *pnSrcID = (UINT)-1 ;
HRESULT hr = AugMergeISF_IsWrap(pidlWrap); if(SUCCEEDED(hr)) { PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pidlWrap ) ;
if( pWrap->cSrcs > 0 ) { LPBYTE p = ((LPBYTE)pWrap) + sizeof(AUGM_IDWRAP) ; // position of first pidl header.
PAUGM_IDSRC pSrc = (PAUGM_IDSRC)p ; // offset to next pidl header, needs terminator so that ILClone below can work
UINT cbPidl= ((LPITEMIDLIST)pSrc->pidl)->mkid.cb + CB_IDLIST_TERMINATOR; if( NULL != (pEnum = new AUGM_IDWRAP_ENUM) ) { pEnum->cbStruct = sizeof(*pEnum) ; pEnum->pWrap = pWrap ; pEnum->pSrcNext = (PAUGM_IDSRC)(p + sizeof(AUGM_IDSRC) + cbPidl) ; *pnSrcID = pSrc->nID ; *ppidlRet = ILClone( (LPITEMIDLIST)pSrc->pidl ) ; if ( NULL == *ppidlRet ) { delete pEnum; pEnum = NULL; } } } } return pEnum ; }
//-------------------------------------------------------------------------//
// Continues source pidl enumeration
BOOL AugMergeISF_EnumNextSrcPidl( IN HANDLE hEnum, OUT UINT* pnSrcID, OUT LPITEMIDLIST* ppidlRet ) { PAUGM_IDWRAP_ENUM pEnum = (PAUGM_IDWRAP_ENUM)hEnum ; HRESULT hr = E_UNEXPECTED ;
ASSERT( IS_VALID_WRITE_PTR( pEnum, AUGM_IDWRAP_ENUM ) ) ; ASSERT( sizeof(*pEnum) == pEnum->cbStruct ) ; ASSERT( sizeof(*pEnum) == pEnum->cbStruct );
*ppidlRet = NULL ; *pnSrcID = (UINT)-1 ;
if (SUCCEEDED((hr = AugMergeISF_IsWrap((LPCITEMIDLIST)pEnum->pWrap)))) { if ((LPBYTE)(pEnum->pWrap) + pEnum->pWrap->cb <= (LPBYTE)pEnum->pSrcNext) hr = S_FALSE ; else { UNALIGNED AUGM_IDSRC* pualSrcNext = pEnum->pSrcNext;
*pnSrcID = pualSrcNext->nID; *ppidlRet = ILClone((LPITEMIDLIST)pualSrcNext->pidl); pEnum->pSrcNext = (PAUGM_IDSRC)( ((LPBYTE)pualSrcNext) + sizeof(AUGM_IDSRC) + ((LPITEMIDLIST)pualSrcNext->pidl)->mkid.cb + CB_IDLIST_TERMINATOR);
hr = S_OK ; return TRUE ; } } return FALSE ; }
//-------------------------------------------------------------------------//
// Terminates source pidl enumeration
void AugMergeISF_EndEnumSrcPidls( IN OUT HANDLE& hEnum ) { PAUGM_IDWRAP_ENUM pEnum = (PAUGM_IDWRAP_ENUM)hEnum ;
ASSERT( IS_VALID_WRITE_PTR( pEnum, AUGM_IDWRAP_ENUM ) && sizeof(*pEnum) == pEnum->cbStruct ); delete pEnum ; hEnum = NULL ; }
//-------------------------------------------------------------------------//
// Allocates and returns a copy of the specified source pidl
// from the wrapped pidl.
HRESULT AugMergeISF_GetSrcPidl( IN LPCITEMIDLIST pidlWrap, IN UINT nSrcID, OUT LPITEMIDLIST* ppidlRet ) { ASSERT( ppidlRet ) ; *ppidlRet = NULL ;
HANDLE hEnum ; BOOL bEnum ; UINT nSrcIDEnum ; LPITEMIDLIST pidlEnum ;
for( hEnum = AugMergeISF_EnumFirstSrcPidl( pidlWrap, &nSrcIDEnum, &pidlEnum ), bEnum = TRUE ; hEnum && bEnum ; bEnum = AugMergeISF_EnumNextSrcPidl( hEnum, &nSrcIDEnum, &pidlEnum ) ) { if( nSrcIDEnum == nSrcID ) { *ppidlRet = pidlEnum ; AugMergeISF_EndEnumSrcPidls(hEnum); return S_OK ; }
ILFree( pidlEnum ) ; } AugMergeISF_EndEnumSrcPidls( hEnum ) ;
return E_FAIL ; }
#ifdef DEBUG
BOOL IsValidWrappedPidl(LPCITEMIDLIST pidlWrap) { BOOL fValid = FALSE;
if (pidlWrap == NULL) return FALSE;
if (FAILED(AugMergeISF_IsWrap(pidlWrap))) return FALSE;
HANDLE hEnum ; UINT nSrcIDEnum ; LPITEMIDLIST pidlEnum ;
hEnum = AugMergeISF_EnumFirstSrcPidl( pidlWrap, &nSrcIDEnum, &pidlEnum ); do { fValid = IS_VALID_PIDL(pidlEnum); ILFree(pidlEnum); } while( fValid && AugMergeISF_EnumNextSrcPidl( hEnum, &nSrcIDEnum, &pidlEnum )); AugMergeISF_EndEnumSrcPidls( hEnum ) ;
return fValid; } #endif
//-------------------------------------------------------------------------//
int AugmEnumCompare(void *pv1, void *pv2, LPARAM lParam) { CAugISFEnumItem* paugmEnum1 = (CAugISFEnumItem*)pv1; CAugISFEnumItem* paugmEnum2 = (CAugISFEnumItem*)pv2; int iRet = -1;
if (paugmEnum1 && paugmEnum2) { // Are these two items of different types?
if (BOOLIFY(paugmEnum1->_rgfAttrib & SFGAO_FOLDER) ^ BOOLIFY(paugmEnum2->_rgfAttrib & SFGAO_FOLDER)) { // Yes. Then Folders sort before items.
iRet = BOOLIFY(paugmEnum1->_rgfAttrib & SFGAO_FOLDER) ? 1 : -1; } else // They are of the same type. Then compare by name
{ iRet = lstrcmpi(paugmEnum1->_pszDisplayName, paugmEnum2->_pszDisplayName); } }
return iRet; }
LPVOID AugmEnumMerge(UINT uMsg, void * pv1, void * pv2, LPARAM lParam) { void * pvRet = pv1; switch (uMsg) { case DPAMM_MERGE: { HANDLE hEnum; UINT nSrcID; LPITEMIDLIST pidl; CAugISFEnumItem* paugmeDest = (CAugISFEnumItem*)pv1; CAugISFEnumItem* paugmeSrc = (CAugISFEnumItem*)pv2;
ASSERT(paugmeDest && paugmeSrc);
hEnum = AugMergeISF_EnumFirstSrcPidl(paugmeSrc->_pidlWrap, &nSrcID, &pidl); if (hEnum) { // add pidl from src to dest
AugMergeISF_WrapAddPidl(pidl, nSrcID, &paugmeDest->_pidlWrap); // no longer need hEnum
AugMergeISF_EndEnumSrcPidls(hEnum); // this was copied to paugmeDest->_pidlWrap
ILFree(pidl); } } break; case DPAMM_INSERT: { CAugISFEnumItem* paugmNew = new CAugISFEnumItem; CAugISFEnumItem* paugmSrc = (CAugISFEnumItem*)pv1;
if (paugmNew) { paugmNew->_pidlWrap = ILClone(paugmSrc->_pidlWrap); if (paugmNew->_pidlWrap) { paugmNew->SetDisplayName(paugmSrc->_pszDisplayName); paugmNew->_rgfAttrib = paugmSrc->_rgfAttrib; } else { delete paugmNew; paugmNew = NULL; } } pvRet = paugmNew; } break; default: ASSERT(0); } return pvRet; }
typedef struct { LPTSTR pszDisplayName; BOOL fFolder; } AUGMISFSEARCHFORPIDL;
int CALLBACK AugMISFSearchForOnePidlByDisplayName(LPVOID p1, LPVOID p2, LPARAM lParam) { AUGMISFSEARCHFORPIDL* pSearchFor = (AUGMISFSEARCHFORPIDL*)p1; CAugISFEnumItem* paugmEnum = (CAugISFEnumItem*)p2;
// Are they of different types?
if (BOOLIFY(paugmEnum->_rgfAttrib & SFGAO_FOLDER) ^ pSearchFor->fFolder) { // Yes.
return pSearchFor->fFolder ? 1 : -1; } else // They are of the same type. Then compare by name
{ return StrCmpI(pSearchFor->pszDisplayName, paugmEnum->_pszDisplayName); } }
//-------------------------------------------------------------------------------------------------//
// DPA utilities
#define DPA_GETPTRCOUNT( hdpa ) ((NULL != (hdpa)) ? DPA_GetPtrCount((hdpa)) : 0)
#define DPA_GETPTR( hdpa, i, type ) ((NULL != (hdpa)) ? (type*)DPA_GetPtr((hdpa), i) : (type*)NULL)
#define DPA_DESTROY( hdpa, pfn ) { if( NULL != hdpa ) \
{ DPA_DestroyCallback( hdpa, pfn, NULL ) ; \ hdpa = NULL ; }}
//-------------------------------------------------------------------------------------------------//
// Forwards...
class CEnum ; class CChildObject ;
//-------------------------------------------------------------------------------------------------//
// Augmented Merge Shell Folder source namespace descriptor.
//
// Objects of class CNamespace are created by CAugmentedMergeISF in
// the AddNameSpace() method impl, and are maintained in the collection
// CAugmentedMergeISF::_hdpaNamespaces.
//
class CNamespace //-------------------------------------------------------------------------------------------------//
{ public: CNamespace( const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib ) ; ~CNamespace() ;
IShellFolder* ShellFolder() { return _psf ; } REFGUID Guid() { return _guid ; } ULONG Attrib() const { return _dwAttrib ; } LPITEMIDLIST GetPidl() const { return _pidl; }
void Assign( const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib ) ; void Unassign() ;
HRESULT RegisterNotify( HWND, UINT, ULONG ) ; HRESULT UnregisterNotify() ;
BOOL SetOwner( IUnknown *punk ) ; protected: IShellFolder* _psf ; // IShellFolder interface pointer
GUID _guid ; // optional GUID for specialized UI handling
LPITEMIDLIST _pidl ; // optional pidl
ULONG _dwAttrib ; // optional flags
UINT _uChangeReg ; // Shell change notify registration ID.
} ;
//-------------------------------------------------------------------------------------------------//
inline CNamespace::CNamespace( const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib ) : _psf(NULL), _pidl(NULL), _guid(GUID_NULL), _dwAttrib(0), _uChangeReg(0) { Assign( pguidUIObject, psf, pidl, dwAttrib ) ; }
//-------------------------------------------------------------------------------------------------//
inline CNamespace::~CNamespace() { UnregisterNotify() ; Unassign() ; }
//-------------------------------------------------------------------------------------------------//
// Assigns data members.
void CNamespace::Assign( const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib ) { Unassign() ; if( NULL != (_psf = psf) ) _psf->AddRef() ;
_pidl = ILClone( pidl ) ; _guid = pguidUIObject ? *pguidUIObject : GUID_NULL ; _dwAttrib = dwAttrib ;
}
//-------------------------------------------------------------------------------------------------//
// Unassigns data members.
void CNamespace::Unassign() { ATOMICRELEASE( _psf ) ; ILFree( _pidl ) ; _pidl = NULL ; _guid = GUID_NULL ; _dwAttrib = 0L ; }
//-------------------------------------------------------------------------------------------------//
// Register change notification for the namespace
HRESULT CNamespace::RegisterNotify( HWND hwnd, UINT uMsg, ULONG lEvents ) { if( 0 == _uChangeReg ) _uChangeReg = ::RegisterNotify(hwnd, uMsg, _pidl, lEvents, SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_RecursiveInterrupt, TRUE);
return 0 != _uChangeReg ? S_OK : E_FAIL ; }
//-------------------------------------------------------------------------------------------------//
// Unregister change notification for the namespace
HRESULT CNamespace::UnregisterNotify() { if( 0 != _uChangeReg ) { UINT uID = _uChangeReg;
_uChangeReg = 0; ::SHChangeNotifyDeregister(uID); } return S_OK; }
//-------------------------------------------------------------------------------------------------//
inline BOOL CNamespace::SetOwner(IUnknown *punkOwner) { if (_psf) { IUnknown_SetOwner(_psf, punkOwner); return TRUE ; } return FALSE ; }
//-------------------------------------------------------------------------//
CAugmentedMergeISF::CAugmentedMergeISF() : _cRef(1) { ASSERT(_hdpaNamespaces == NULL); ASSERT(_punkOwner == NULL); ASSERT(_pdt == NULL); DllAddRef() ; }
//-------------------------------------------------------------------------//
CAugmentedMergeISF::~CAugmentedMergeISF() { SetOwner(NULL); FreeNamespaces(); DllRelease(); }
//-------------------------------------------------------------------------//
// CAugmentedMergeISF global CreateInstance method for da class factory
//-------------------------------------------------------------------------//
STDAPI CAugmentedISF2_CreateInstance( IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi ) { // aggregation checking is handled in class factory
CAugmentedMergeISF* pObj;
if( NULL == (pObj = new CAugmentedMergeISF) ) return E_OUTOFMEMORY ;
*ppunk = SAFECAST( pObj, IAugmentedShellFolder2 * ) ; return S_OK; }
//-------------------------------------------------------------------------//
// CAugmentedMergeISF - IUnknown methods
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENTMULTI( CAugmentedMergeISF, IShellFolder, IAugmentedShellFolder ), QITABENT( CAugmentedMergeISF, IAugmentedShellFolder ), QITABENT( CAugmentedMergeISF, IAugmentedShellFolder2 ), QITABENT( CAugmentedMergeISF, IShellFolder2 ), QITABENT( CAugmentedMergeISF, IShellService ), QITABENT( CAugmentedMergeISF, ITranslateShellChangeNotify ), QITABENT( CAugmentedMergeISF, IDropTarget ), { 0 }, }; return QISearch( this, qit, riid, ppvObj ) ; }
//-------------------------------------------------------------------------//
STDMETHODIMP_(ULONG) CAugmentedMergeISF::AddRef() { return InterlockedIncrement(&_cRef); }
//-------------------------------------------------------------------------//
STDMETHODIMP_(ULONG) CAugmentedMergeISF::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
//-------------------------------------------------------------------------//
// CAugmentedMergeISF - IShellFolder methods
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList) { HRESULT hr = E_FAIL;
if (_hdpaNamespaces) { // BUGBUG (lamadio): This does not work if you have 2 enumerators. But,
// when asking for a new enumerator, we should flush the cache.
FreeObjects();
*ppenumIDList = new CEnum(this, grfFlags);
if (NULL == *ppenumIDList) return E_OUTOFMEMORY ; hr = S_OK ; } return hr; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::BindToObject( LPCITEMIDLIST pidlWrap, LPBC pbc, REFIID riid, LPVOID *ppvOut ) { ASSERT(IS_VALID_PIDL( pidlWrap ) && NULL != ppvOut);
*ppvOut = NULL ; if (SUCCEEDED(AugMergeISF_IsWrap(pidlWrap))) { PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pidlWrap ) ; ASSERT(IsValidWrappedPidl(pidlWrap)); ASSERT( pWrap ) ; ASSERT( pWrap->cSrcs > 0 ) ; // should never, never happen
HANDLE hEnum ; BOOL bEnum ; UINT nIDSrc = -1 ; DEBUG_CODE(int iNumBound = 0); LPITEMIDLIST pidlSrc ; HRESULT hr = E_UNEXPECTED ; CNamespace* pSrc = NULL ; CAugmentedMergeISF* pISF ; if (NULL == (pISF = new CAugmentedMergeISF)) return E_OUTOFMEMORY ; for (hEnum = AugMergeISF_EnumFirstSrcPidl( pidlWrap, &nIDSrc, &pidlSrc ), bEnum = TRUE ; hEnum && bEnum ; bEnum = AugMergeISF_EnumNextSrcPidl( hEnum, &nIDSrc, &pidlSrc)) { if (SUCCEEDED((hr = QueryNameSpace(nIDSrc, (PVOID*)&pSrc))) && pSrc) { IShellFolder *psf;
hr = S_FALSE; if (SUCCEEDED(pSrc->ShellFolder()->BindToObject(pidlSrc, NULL, IID_IShellFolder, (void **)&psf))) { LPCITEMIDLIST pidlParent = pSrc->GetPidl(); LPITEMIDLIST pidlFull = ILCombine(pidlParent, pidlSrc); hr = pISF->AddNameSpace(NULL, psf, pidlFull, pSrc->Attrib()); #ifdef DEBUG
if (SUCCEEDED(hr)) iNumBound++; #endif
ILFree(pidlFull); psf->Release(); } ASSERT(SUCCEEDED(hr)); } ILFree(pidlSrc); }
// If this hits, then something is terribly wrong. Either we were unable to bind to the
// ShellFolders, or the add failed. This could be caused by a bad wrapped pidl.
ASSERT(iNumBound > 0);
AugMergeISF_EndEnumSrcPidls( hEnum ) ; hr = pISF->QueryInterface(riid, ppvOut); pISF->Release(); return hr ; } return E_UNEXPECTED ; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::BindToStorage( LPCITEMIDLIST, LPBC, REFIID, void ** ) { return E_NOTIMPL ; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::CompareIDs( LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { IShellFolder *psf1 = NULL, *psf2 = NULL; LPITEMIDLIST pidlItem1 = NULL, pidlItem2 = NULL; int iRet = 0 ; HRESULT hr1, hr2, hr ;
hr1 = GetDefNamespace( pidl1, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf1, &pidlItem1 ) ; hr2 = GetDefNamespace( pidl2, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf2, &pidlItem2 ) ;
if( SUCCEEDED( hr1 ) && SUCCEEDED( hr2 ) ) { ULONG dwAttrib1 = SFGAO_FOLDER, dwAttrib2 = SFGAO_FOLDER; // Same namespace? Just forward the request.
if( psf1 == psf2 ) { hr = psf1->CompareIDs( lParam, pidlItem1, pidlItem2 ) ; ILFree( pidlItem1 ) ; ILFree( pidlItem2 ) ; return hr ; }
hr1 = psf1->GetAttributesOf( 1, (LPCITEMIDLIST*)&pidlItem1, &dwAttrib1 ) ; hr2 = psf2->GetAttributesOf( 1, (LPCITEMIDLIST*)&pidlItem2, &dwAttrib2 ) ;
if( SUCCEEDED( hr1 ) && SUCCEEDED( hr2 ) ) { // Comparison heuristics:
// (1) folders take precedence over nonfolders, (2) alphanum comparison
if( 0 != (dwAttrib1 & SFGAO_FOLDER) && 0 == (dwAttrib2 & SFGAO_FOLDER) ) iRet = -1 ; else if( 0 == (dwAttrib1 & SFGAO_FOLDER) && 0 != (dwAttrib2 & SFGAO_FOLDER) ) iRet = 1 ; else { STRRET strName1, strName2; HRESULT hres1 = E_FAIL; HRESULT hres2 = E_FAIL; TCHAR szName1[MAX_PATH], szName2[MAX_PATH]; hr1 = psf1->GetDisplayNameOf(pidlItem1, SHGDN_FORPARSING | SHGDN_INFOLDER, &strName1); hr2 = psf2->GetDisplayNameOf(pidlItem2, SHGDN_FORPARSING | SHGDN_INFOLDER, &strName2);
if (SUCCEEDED(hr1) && SUCCEEDED(hr2)) { // must call StrRetToBuf because it frees StrRet strings if allocated
hres1 = StrRetToBuf(&strName1, pidlItem1, szName1, ARRAYSIZE(szName1)); hres2 = StrRetToBuf(&strName2, pidlItem2, szName2, ARRAYSIZE(szName2)); } // if the names match we return -1 because they are different pidls with
// the same name
if (SUCCEEDED(hr1) && SUCCEEDED(hr2) && SUCCEEDED(hres1) && SUCCEEDED(hres2)) { iRet = lstrcmp(szName1, szName2); // Comparisons are by name with items of the same type.
} } } }
hr = FAILED( hr1 ) ? hr1 : FAILED( hr2 ) ? hr2 : S_OK ; if( pidlItem1 ) ILFree( pidlItem1 ) ; if( pidlItem2 ) ILFree( pidlItem2 ) ;
return MAKE_HRESULT( HRESULT_SEVERITY( hr ), HRESULT_FACILITY( hr ), iRet ) ; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::CreateViewObject( HWND hwndOwner, REFIID riid, LPVOID * ppvOut ) { HRESULT hr ; CNamespace *pSrc, *pSrc0 ; pSrc = pSrc0 = NULL ;
// TODO: Handle IDropTarget here, delegate for all others.
if (IsEqualIID(riid, IID_IDropTarget)) { hr = QueryInterface(riid, ppvOut); if (SUCCEEDED(hr)) _hwnd = hwndOwner; return hr; }
// Search for default namespace for CreateViewObj()
if( FAILED( (hr = GetDefNamespace( ASFF_DEFNAMESPACE_VIEWOBJ, (PVOID*)&pSrc, NULL, (PVOID*)&pSrc0 )) ) ) return hr ;
if( NULL == pSrc ) pSrc = pSrc0 ;
if( NULL != pSrc ) { ASSERT( pSrc->ShellFolder() ) ; hr = pSrc->ShellFolder()->CreateViewObject( hwndOwner, riid, ppvOut ) ; }
return hr ; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetAttributesOf( UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfInOut ) { IShellFolder* pISF ; LPITEMIDLIST pidlItem ; HRESULT hr ;
if( cidl > 1 ) // support 1 only.
return E_NOTIMPL ; if( !apidl ) return E_INVALIDARG ; // Forward to default namespace for item attributes
if( FAILED( (hr = GetDefNamespace( apidl[0], ASFF_DEFNAMESPACE_ATTRIB, &pISF, &pidlItem )) ) ) return hr ;
hr = pISF->GetAttributesOf( 1, (LPCITEMIDLIST*)&pidlItem, rgfInOut ) ; ILFree( pidlItem ) ; return hr ; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetUIObjectOf( HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut ) { IShellFolder* pISF ; LPITEMIDLIST pidlItem ; HRESULT hr ;
if (cidl > 1) // support 1 only.
return E_NOTIMPL ; if (!apidl) return E_INVALIDARG ;
if (IsEqualGUID(riid, IID_IContextMenu)) { hr = _GetContextMenu(hwndOwner, cidl, apidl, prgfInOut, ppvOut); } else { // Forward to default namespace for UI object
if (FAILED((hr = GetDefNamespace(apidl[0], ASFF_DEFNAMESPACE_UIOBJ, &pISF, &pidlItem)))) return hr ;
hr = pISF->GetUIObjectOf(hwndOwner, 1, (LPCITEMIDLIST*)&pidlItem, riid, prgfInOut, ppvOut); ILFree(pidlItem); } return hr ; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetDisplayNameOf( LPCITEMIDLIST pidl, DWORD grfFlags, LPSTRRET pstrName ) { IShellFolder* pISF ; LPITEMIDLIST pidlItem ; HRESULT hr ;
// Forward to default namespace for display name
if (FAILED((hr = GetDefNamespace( pidl, ASFF_DEFNAMESPACE_DISPLAYNAME, &pISF, &pidlItem)))) return hr ;
if (SUCCEEDED((hr = pISF->GetDisplayNameOf(pidlItem, grfFlags, pstrName)))) { // STRRET_OFFSET has no meaning in context of the pidl wrapper.
// We can either calculate the offset into the wrapper, or allocate
// a wide char for the name. For expedience, we'll allocate the name.
if (pstrName->uType == STRRET_OFFSET) { UINT cch = lstrlenA( STRRET_OFFPTR( pidlItem, pstrName ) ) ; LPWSTR pwszName = (LPWSTR)SHAlloc( (cch + 1) * sizeof(WCHAR));
if (NULL != pwszName) { SHAnsiToUnicode( STRRET_OFFPTR( pidlItem, pstrName ), pwszName, cch+1 ); pwszName[cch] = (WCHAR)0 ; } pstrName->pOleStr = pwszName ; pstrName->uType = STRRET_WSTR ; }
#ifdef DEBUG
// If the trace flags are set, and this is not comming from an internal query,
// Then append the location where this name came from
if (g_qwTraceFlags & TF_AUGM && _fInternalGDNO == FALSE) { if (pstrName->uType == STRRET_WSTR) { LPWSTR wszOldName = pstrName->pOleStr; UINT cch = lstrlenW(wszOldName); UINT cchWithExtra = cch + 50;
pstrName->pOleStr = (LPWSTR)SHAlloc( cchWithExtra * sizeof(WCHAR));
if (pstrName->pOleStr) { StringCchCopyW(pstrName->pOleStr, cchWithExtra, wszOldName);
if (AugMergeISF_GetSourceCount(pidl) > 1) StringCchCatW(pstrName->pOleStr, cchWithExtra, L" (Merged)"); else if (WrappedPidlContainsSrcID(pidl, 0)) StringCchCatW(pstrName->pOleStr, cchWithExtra, L" (1)"); else StringCchCatW(pstrName->pOleStr, cchWithExtra, L" (2)");
SHFree(wszOldName); } else { pstrName->pOleStr = wszOldName; } } else if (pstrName->uType == STRRET_CSTR) { if (AugMergeISF_GetSourceCount(pidl) > 1) StringCchCatA(pstrName->cStr, ARRAYSIZE(pstrName->cStr), " (Merged)"); else if (WrappedPidlContainsSrcID(pidl, 0)) StringCchCatA(pstrName->cStr, ARRAYSIZE(pstrName->cStr), " (1)"); else StringCchCatA(pstrName->cStr, ARRAYSIZE(pstrName->cStr), " (2)"); } }
#endif
}
ILFree( pidlItem ) ; return hr ; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::ParseDisplayName( HWND hwndOwner, LPBC pbcReserved, LPOLESTR pwszName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG * pdwAttrib ) { int iIndex; LPITEMIDLIST pidl;
*ppidl = NULL; // This ParseDisplayName should iterate through all our delegates until one works.
for (iIndex = NamespaceCount() - 1; iIndex >=0 ; iIndex--) { CNamespace* pSrc = Namespace(iIndex) ; if (pSrc) { if (SUCCEEDED(pSrc->ShellFolder()->ParseDisplayName(hwndOwner, pbcReserved, pwszName, pchEaten, &pidl, pdwAttrib))) { ASSERT(pidl); // Make sure a valid pidl comes out.
if (*ppidl == NULL) AugMergeISF_CreateWrap(pidl, iIndex, ppidl); else AugMergeISF_WrapAddPidl(pidl, iIndex, ppidl);
ILFree(pidl); } } }
return *ppidl? S_OK : E_FAIL; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::SetNameOf( HWND hwndOwner, LPCITEMIDLIST pidl, LPCOLESTR pwszName, DWORD uFlags, LPITEMIDLIST *ppidlOut ) { CNamespace* pnsCommon; CNamespace* pnsUser; LPITEMIDLIST pidlItem; HRESULT hres; UINT uiUser; UINT uiCommon;
hres = _GetNamespaces(pidl, &pnsCommon, &uiCommon, &pnsUser, &uiUser, &pidlItem, NULL); if (SUCCEEDED(hres)) { LPITEMIDLIST pidlNew = NULL; UINT uiNamespace = INVALID_NAMESPACE_INDEX;
if (pnsUser) { hres = pnsUser->ShellFolder()->SetNameOf(hwndOwner, pidlItem, pwszName, uFlags, &pidlNew); uiNamespace = uiUser; } else if (pnsCommon) { hres = E_FAIL;
if (AffectAllUsers(hwndOwner)) { hres = pnsCommon->ShellFolder()->SetNameOf(hwndOwner, pidlItem, pwszName, uFlags, &pidlNew); uiNamespace = uiCommon; } }
if (ppidlOut) { *ppidlOut = NULL; // wrap the pidl
if (SUCCEEDED(hres) && pidlNew) AugMergeISF_CreateWrap(pidlNew, uiNamespace, ppidlOut); } ILFree(pidlNew); ILFree(pidlItem); } return hres; }
//-------------------------------------------------------------------------//
// IAugmentedShellFolder methods
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// Adds a source namespace to the Augmented Merge shell folder object.
STDMETHODIMP CAugmentedMergeISF::AddNameSpace( const GUID * pguidObject, IShellFolder * psf, LPCITEMIDLIST pidl, DWORD dwFlags ) { ASSERT (IS_VALID_CODE_PTR(psf, IShellFolder*)); ASSERT (IS_VALID_PIDL(pidl));
// Check for duplicate via full display name
for( int i=0, max = NamespaceCount() ; i < max; i++ ) { CNamespace* pSrc = Namespace( i ) ; if (pSrc) { if (ILIsEqual(pSrc->GetPidl(), pidl)) { // Found! Reassign attributes
pSrc->Assign( pguidObject, psf, pidl, dwFlags ) ; return S_OK ; } } }
// No match; safe to append it to collection, creating DPA if necessary.
if( NULL == _hdpaNamespaces && NULL == (_hdpaNamespaces= DPA_Create( 2 )) ) return E_OUTOFMEMORY ;
CNamespace *pSrc = new CNamespace( pguidObject, psf, pidl, dwFlags ); if( NULL == pSrc ) return E_OUTOFMEMORY ; return DPA_AppendPtr( _hdpaNamespaces, pSrc ) >= 0 ? S_OK : E_FAIL; }
//-------------------------------------------------------------------------//
// Retrieves the primary namespace iid for the wrapped pidl.
STDMETHODIMP CAugmentedMergeISF::GetNameSpaceID( LPCITEMIDLIST pidl, GUID * pguidOut ) { HRESULT hr ; if (FAILED((hr = AugMergeISF_IsWrap( pidl )))) return hr ;
// BUGBUG: need to enumerate wrapped source pidls
return E_NOTIMPL ; }
//-------------------------------------------------------------------------//
// Retrieves a pointer to a source namespace descriptor associated with
// the specified lookup index.
STDMETHODIMP CAugmentedMergeISF::QueryNameSpace( ULONG nID, PVOID* ppSrc ) { if (!ppSrc) return E_INVALIDARG; *ppSrc = NULL;
LONG cSrcs;
if ((cSrcs = NamespaceCount()) <=0) return E_FAIL;
if(nID >= (ULONG)cSrcs) return E_INVALIDARG;
if (NULL == (*ppSrc = Namespace(nID))) return E_UNEXPECTED;
return S_OK; }
//-------------------------------------------------------------------------//
// Retrieves data for the namespace identified by dwID.
STDMETHODIMP CAugmentedMergeISF::QueryNameSpace( ULONG nID, GUID * pguidOut, IShellFolder ** ppsf ) { CNamespace* pSrc = NULL ; HRESULT hr = QueryNameSpace( nID, (PVOID*)&pSrc ) ;
if( pguidOut ) *pguidOut = NULL != pSrc ? pSrc->Guid() : GUID_NULL ;
if( ppsf ) { if( (*ppsf = (NULL != pSrc) ? pSrc->ShellFolder() : NULL) != NULL ) (*ppsf)->AddRef() ; }
return hr ; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::EnumNameSpace( DWORD uNameSpace, DWORD * pdwID ) { return E_NOTIMPL ; }
//-------------------------------------------------------------------------//
// IAugmentedShellFolder2 methods
//-------------------------------------------------------------------------//
//GetNameSpaceCount and GetIDListWrapCount are not used anywhere
#if 0
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetNameSpaceCount( OUT LONG* pcNamespaces ) { if( !pcNamespaces ) return E_INVALIDARG ;
*pcNamespaces = (LONG)NamespaceCount() ; return S_OK ; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetIDListWrapCount( LPCITEMIDLIST pidlWrap, OUT LONG * pcPidls) { if( NULL == pidlWrap || NULL == pcPidls ) return E_INVALIDARG ;
*pcPidls = 0 ;
HRESULT hr ; if (SUCCEEDED((hr = AugMergeISF_IsWrap(pidlWrap)))) { PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap(pidlWrap); *pcPidls = pWrap->cSrcs; hr = S_OK; } return hr; } #endif // #if 0
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::UnWrapIDList( LPCITEMIDLIST pidlWrap, LONG cPidls, IShellFolder** apsf, LPITEMIDLIST* apidlFolder, LPITEMIDLIST* apidlItems, LONG* pcFetched ) { HRESULT hr ; HANDLE hEnum ; BOOL bEnum = TRUE ; UINT nSrcID ; LPITEMIDLIST pidlItem ; LONG cFetched = 0;
if (NULL == pidlWrap || cPidls <= 0) return E_INVALIDARG ;
if (FAILED((hr = AugMergeISF_IsWrap(pidlWrap)))) return hr ;
// Enumerate pidls in wrap
for (hEnum = AugMergeISF_EnumFirstSrcPidl( pidlWrap, &nSrcID, &pidlItem); cFetched < cPidls && hEnum && bEnum ; bEnum = AugMergeISF_EnumNextSrcPidl( hEnum, &nSrcID, &pidlItem)) { // Retrieve namespace data
CNamespace* pSrc ; if (SUCCEEDED((hr = QueryNameSpace(nSrcID, (PVOID*)&pSrc)))) { if (apsf) { apsf[cFetched] = pSrc->ShellFolder() ; if (apsf[cFetched]) apsf[cFetched]->AddRef(); } if (apidlFolder) apidlFolder[cFetched] = ILClone(pSrc->GetPidl()); if (apidlItems) { apidlItems[cFetched] = pidlItem; pidlItem = NULL; // paranoia -- just making sure we, somehow, don't free this guy at the end of the for loop
} cFetched++ ; } else { ILFree( pidlItem ) ; } } ILFree(pidlItem); // AugMergeISF_EnumNextSrcPidl is called (if there are 2 wrapped pidls and we ask for only one)
// right before we exit the for loop so we have to free pidl if allocated.
if (hEnum) AugMergeISF_EndEnumSrcPidls( hEnum );
if( pcFetched ) *pcFetched = cFetched ; return cFetched == cPidls ? S_OK : S_FALSE ; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::SetOwner( IUnknown* punkOwner ) { HRESULT hr = S_OK ; int cSrcs = NamespaceCount() ;
if( cSrcs > 0 ) DPA_EnumCallback( _hdpaNamespaces, SetOwnerProc, NULL ) ;
ATOMICRELEASE( _punkOwner ) ;
if( punkOwner ) { hr = punkOwner->QueryInterface(IID_IUnknown, (LPVOID *)&_punkOwner ) ; if( cSrcs ) DPA_EnumCallback( _hdpaNamespaces, SetOwnerProc, (void *)_punkOwner); }
return hr ; }
//-------------------------------------------------------------------------//
int CAugmentedMergeISF::SetOwnerProc( LPVOID pv, LPVOID pvParam ) { CNamespace* pSrc = (CNamespace*) pv ; ASSERT( pSrc ) ;
return pSrc->SetOwner( (IUnknown*)pvParam ) ; }
//-------------------------------------------------------------------------//
// ITranslateShellChangeNotify methods
//-------------------------------------------------------------------------//
LPITEMIDLIST ILCombineBase(LPCITEMIDLIST pidlContainingBase, LPCITEMIDLIST pidlRel) { // This routine differs from ILCombine in that it takes the First pidl's base, and
// cats on the last id of the second pidl. We need this so Wrapped pidls
// end up with the same base, and we get a valid full pidl.
LPITEMIDLIST pidlRet = NULL; LPITEMIDLIST pidlBase = ILClone(pidlContainingBase); if (pidlBase) { ILRemoveLastID(pidlBase);
pidlRet = ILCombine(pidlBase, pidlRel);
ILFree(pidlBase); }
return pidlRet; }
BOOL IsFolderEvent(LONG lEvent) { return lEvent == SHCNE_MKDIR || lEvent == SHCNE_RMDIR || lEvent == SHCNE_RENAMEFOLDER; }
#ifdef DEBUG
void CAugmentedMergeISF::DumpObjects() { if (g_dwDumpFlags & TF_AUGM) { ASSERT(_hdpaObjects); int iObjectCount = DPA_GetPtrCount(_hdpaObjects); TraceMsg(TF_AUGM, "CAugMISF::DumpObjects: Number of items: %d", iObjectCount);
CNamespace* pns = (CNamespace *)DPA_FastGetPtr(_hdpaNamespaces, 0); if (pns) DebugDumpPidl(TF_AUGM, TEXT("CAugMISF::DumpObjects Namespace 1"), pns->GetPidl());
pns = (CNamespace *)DPA_FastGetPtr(_hdpaNamespaces, 1); if (pns) DebugDumpPidl(TF_AUGM, TEXT("CAugMISF::DumpObjects Namespace 2"), pns->GetPidl());
for (int i = 0; i < iObjectCount; i++) { CAugISFEnumItem* pEnumItem = (CAugISFEnumItem*)DPA_FastGetPtr(_hdpaObjects, i); TraceMsg(TF_ALWAYS, "CAugMISF::DumpObjects: %s, Folder: %s Merged: %s", pEnumItem->_pszDisplayName, BOOLIFY(pEnumItem->_rgfAttrib & SFGAO_FOLDER) ? TEXT("Yes") : TEXT("No"), (AugMergeISF_GetSourceCount(pEnumItem->_pidlWrap) > 1)? TEXT("Yes") : TEXT("No")); } } } #endif
BOOL GetRealPidlFromSimple(LPCITEMIDLIST pidlSimple, LPITEMIDLIST* ppidlReal) { // Similar to SHGetRealIDL in Function, but SHGetRealIDL does SHGDN_FORPARSING | INFOLDER.
// I need the parsing name. I can't rev SHGetRealIDL very easily, so here's this one!
TCHAR szFullName[MAX_PATH]; if (SUCCEEDED(SHGetNameAndFlags(pidlSimple, SHGDN_FORPARSING, szFullName, SIZECHARS(szFullName), NULL))) { *ppidlReal = ILCreateFromPath(szFullName); }
if (*ppidlReal == NULL) // Unable to create? Then use the simple pidl. This is because it does not exist any more
{ // For say, a Delete Notify
*ppidlReal = ILClone(pidlSimple); }
return *ppidlReal != NULL; }
//-------------------------------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::TranslateIDs( LONG *plEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, LPITEMIDLIST * ppidlOut1, LPITEMIDLIST * ppidlOut2, LONG *plEvent2, LPITEMIDLIST *ppidlOut1Event2, LPITEMIDLIST *ppidlOut2Event2) { HRESULT hres = E_FAIL;
switch (*plEvent) { case SHCNE_EXTENDED_EVENT: case SHCNE_ASSOCCHANGED: case SHCNE_UPDATEIMAGE: return S_OK;
case SHCNE_UPDATEDIR: FreeObjects(); return S_OK; }
ASSERT(ppidlOut1); ASSERT(ppidlOut2); LONG lEvent = *plEvent;
*plEvent2 = (LONG)-1; *ppidlOut1Event2 = NULL; *ppidlOut2Event2 = NULL;
*ppidlOut1 = (LPITEMIDLIST)pidl1; *ppidlOut2 = (LPITEMIDLIST)pidl2;
if (!plEvent) return E_FAIL;
// If they are already wrapped, don't wrap twice.
if ((pidl1 && SUCCEEDED(AugMergeISF_IsWrap(ILFindLastID(pidl1)))) || (pidl2 && SUCCEEDED(AugMergeISF_IsWrap(ILFindLastID(pidl2))))) { // We don't want to wrap twice.
return E_FAIL; }
if (!_hdpaNamespaces) return E_FAIL;
if (!_hdpaObjects) return E_FAIL;
CAugISFEnumItem* pEnumItem;
int iIndex; int iShellFolder1 = -1; int iShellFolder2 = -1; IShellFolder* psf1 = NULL; IShellFolder* psf2 = NULL; LPITEMIDLIST pidlReal1 = NULL; LPITEMIDLIST pidlReal2 = NULL; LPITEMIDLIST pidlRealRel1 = NULL; LPITEMIDLIST pidlRealRel2 = NULL; BOOL fFolder = IsFolderEvent(*plEvent);
// Get the information about these Simple pidls: Are they our Children? If so, what namespace?
BOOL fChild1 = IsChildIDInternal(pidl1, TRUE, &iShellFolder1); BOOL fChild2 = IsChildIDInternal(pidl2, TRUE, &iShellFolder2);
// Is either a child?
if (!(fChild1 || fChild2)) return hres;
// Ok, pidl1 is a child, can we get the Real pidl from the simple one?
if (pidl1 && !GetRealPidlFromSimple(pidl1, &pidlReal1)) goto Cleanup;
// Ok, pidl2 is a child, can we get the Real pidl from the simple one?
if (pidl2 && !GetRealPidlFromSimple(pidl2, &pidlReal2)) goto Cleanup;
// These are for code clarity later on. We deal with Relative pidls from here until the very end,
// when we combine the base of the in pidls with the outgoing wrapped pidls.
if (pidlReal1) pidlRealRel1 = ILFindLastID(pidlReal1);
if (pidlReal2) pidlRealRel2 = ILFindLastID(pidlReal2);
// Is Pidl1 in our namespaces?
if (iShellFolder1 != -1) { // Yes, lets get the non-refcounted shell folder that know's about this pidl.
CNamespace * pns = (CNamespace *)DPA_GetPtr(_hdpaNamespaces, iShellFolder1); psf1 = pns->ShellFolder(); // Non ref counted.
}
// Is Pidl2 in our namespaces?
if (iShellFolder2 != -1) { // Yes, lets get the non-refcounted shell folder that know's about this pidl.
CNamespace * pns = (CNamespace *)DPA_GetPtr(_hdpaNamespaces, iShellFolder2); psf2 = pns->ShellFolder(); // Non ref counted.
}
hres = S_OK;
DEBUG_CODE(_fInternalGDNO = TRUE);
switch(*plEvent) { case 0: // Just look up the pidls and return.
{ DWORD rgfAttrib = SFGAO_FOLDER; if (iShellFolder1 != -1) { psf1->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlRealRel1, &rgfAttrib); if (S_OK == _SearchForPidl(psf1, pidlRealRel1, BOOLIFY(rgfAttrib & SFGAO_FOLDER), &iIndex, &pEnumItem)) { *ppidlOut1 = ILCombineBase(pidlReal1, pEnumItem->_pidlWrap); if (!*ppidlOut1) hres = E_OUTOFMEMORY; } }
rgfAttrib = SFGAO_FOLDER; if (iShellFolder2 != -1 && SUCCEEDED(hres)) { psf2->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlRealRel2, &rgfAttrib); if (S_OK == _SearchForPidl(psf2, pidlRealRel2, BOOLIFY(rgfAttrib & SFGAO_FOLDER), &iIndex, &pEnumItem)) { *ppidlOut2 = ILCombineBase(pidlReal2, pEnumItem->_pidlWrap); if (!*ppidlOut2) hres = E_OUTOFMEMORY; } } }
break;
case SHCNE_CREATE: case SHCNE_MKDIR: { TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s", fFolder? TEXT("SHCNE_MKDIR") : TEXT("SHCNE_CREATE")); // Is there a thing of this name already?
if (S_OK == _SearchForPidl(psf1, pidlRealRel1, fFolder, &iIndex, &pEnumItem)) { TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s needs to be merged. Converting to Rename", pEnumItem->_pszDisplayName); // Yes; Then we need to merge this new pidl into the wrapped pidl, and change this
// to a rename, passing the Old wrapped pidl as the first arg, and the new wrapped pidl
// as the second arg. I have to be careful about the freeing:
// Free *ppidlOut1
// Clone pEnumItem->_pidlWrap -> *ppidlOut1.
// Add pidl1 to pEnumItem->_pidlWrap.
// Clone new pEnumItem->_pidlWrap -> *ppidlOut2. ASSERT(*ppidlOut2 == NULL)
*ppidlOut1 = ILCombineBase(pidl1, pEnumItem->_pidlWrap); if (*ppidlOut1) { AugMergeISF_WrapAddPidl(pidlRealRel1, iShellFolder1, &pEnumItem->_pidlWrap); *ppidlOut2 = ILCombineBase(pidl1, pEnumItem->_pidlWrap);
if (!*ppidlOut2) TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl2");
*plEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM; } else { TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl1"); }
} else { LPITEMIDLIST pidlWrap; CAugISFEnumItem* paugmEnum = new CAugISFEnumItem; if (paugmEnum) { if (SUCCEEDED(AugMergeISF_CreateWrap(pidlRealRel1, (UINT)iShellFolder1, &pidlWrap)) && paugmEnum->InitWithWrappedToOwn(SAFECAST(this, IAugmentedShellFolder2*), iShellFolder1, pidlWrap)) { AUGMISFSEARCHFORPIDL AugMSearch; AugMSearch.pszDisplayName = paugmEnum->_pszDisplayName; AugMSearch.fFolder = fFolder;
int iInsertIndex = DPA_Search(_hdpaObjects, (LPVOID)&AugMSearch, 0, AugMISFSearchForOnePidlByDisplayName, NULL, DPAS_SORTED | DPAS_INSERTAFTER);
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Creating new unmerged %s at %d", paugmEnum->_pszDisplayName, iInsertIndex);
if (iInsertIndex < 0) iInsertIndex = DA_LAST;
if (DPA_InsertPtr(_hdpaObjects, iInsertIndex, paugmEnum) == -1) { TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Was unable to add %s for some reason. Bailing", paugmEnum->_pszDisplayName); DestroyObjectsProc(paugmEnum, NULL); } else { *ppidlOut1 = ILCombineBase(pidl1, paugmEnum->_pidlWrap); } } else DestroyObjectsProc(paugmEnum, NULL); } }
} break;
case SHCNE_DELETE: case SHCNE_RMDIR: { TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s", fFolder? TEXT("SHCNE_RMDIR") : TEXT("SHCNE_DELETE")); int iDeleteIndex; // Is there a folder of this name already?
if (S_OK == _SearchForPidl(psf1, pidlRealRel1, fFolder, &iDeleteIndex, &pEnumItem)) { TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Found %s checking merge state.", pEnumItem->_pszDisplayName); // Yes; Then we need to unmerge this pidl from the wrapped pidl, and change this
// to a rename, passing the Old wrapped pidl as the first arg, and the new wrapped pidl
// as the second arg. I have to be careful about the freeing:
// Free *ppidlOut1
// Clone pEnumItem->_pidlWrap -> *ppidlOut1.
// Remove pidl1 from pEnumItem->_pidlWrap
// Convert to rename, pass new wrapped as second arg.
if (AugMergeISF_GetSourceCount( pEnumItem->_pidlWrap ) > 1 ) { TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is Merged. Removing pidl, convert to rename", pEnumItem->_pszDisplayName); *ppidlOut1 = ILCombineBase(pidl1, pEnumItem->_pidlWrap); if (*ppidlOut1) { EVAL(SUCCEEDED(AugMergeISF_WrapRemovePidl(pEnumItem->_pidlWrap, iShellFolder1, &pEnumItem->_pidlWrap)));
*ppidlOut2 = ILCombineBase(pidl1, pEnumItem->_pidlWrap);
if (!*ppidlOut2) TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl2");
*plEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM; } else { TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl1"); } } else { TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is not Merged. deleteing", pEnumItem->_pszDisplayName); pEnumItem = (CAugISFEnumItem*)DPA_DeletePtr(_hdpaObjects, iDeleteIndex);
if (EVAL(pEnumItem)) { *ppidlOut1 = ILCombineBase(pidl1, pEnumItem->_pidlWrap); DestroyObjectsProc(pEnumItem, NULL); } else { TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to get %d from DPA", iDeleteIndex); } }
}
} break;
case SHCNE_RENAMEITEM: case SHCNE_RENAMEFOLDER: { // BUGBUG (lamadio): When renaming an item in the menu, this code will split it into
// a Delete and a Create. We need to detect this situation and convert it to 1 rename. This
// will solve the problem of the lost order during a rename....
BOOL fEvent1Set = FALSE; BOOL fFirstPidlInNamespace = FALSE; TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s", fFolder? TEXT("SHCNE_RENAMEFOLDER") : TEXT("SHCNE_RENAMEITEM"));
// Is this item being renamed FROM the Folder?
if (iShellFolder1 != -1 && // Is this pidl a child of the Folder?
S_OK == _SearchForPidl(psf1, pidlRealRel1, fFolder, &iIndex, &pEnumItem)) // Is it found?
{ TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Old pidl %s is in the Folder", pEnumItem->_pszDisplayName); // Yes.
// Then we need to see if the item that it's being renamed from was Merged
// Need this for reentrancy
if (WrappedPidlContainsSrcID(pEnumItem->_pidlWrap, iShellFolder1)) { // Was it merged?
if (AugMergeISF_GetSourceCount(pEnumItem->_pidlWrap) > 1) // Case 3)
{ TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is Merged. Removing pidl. Convert to rename for event 1", pEnumItem->_pszDisplayName); // Yes;
// Then we need to unmerge that item.
*ppidlOut1 = ILCombineBase(pidl1, pEnumItem->_pidlWrap); if (*ppidlOut1) { // UnWrap
AugMergeISF_WrapRemovePidl(pEnumItem->_pidlWrap, iShellFolder1, &pEnumItem->_pidlWrap);
*ppidlOut2 = ILCombineBase(pidl1, pEnumItem->_pidlWrap);
if (!*ppidlOut2) TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl2");
// This We need to "Rename" the old wrapped pidl, to this new one
// that does not contain the old item.
fEvent1Set = TRUE; } else { TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl1"); } } else { TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is not merged. Nuking item Convert to Delete for event 1.", pEnumItem->_pszDisplayName); // No, This was not a wrapped pidl. Then, convert to a delete:
pEnumItem = (CAugISFEnumItem*)DPA_DeletePtr(_hdpaObjects, iIndex);
if (EVAL(pEnumItem)) { // If we're renaming from this folder, into this folder, Then the first event stays a rename.
if (iShellFolder2 == -1) { fEvent1Set = TRUE; *plEvent = fFolder? SHCNE_RMDIR : SHCNE_DELETE; } else { fFirstPidlInNamespace = TRUE; } *ppidlOut1 = ILCombineBase(pidl1, pEnumItem->_pidlWrap); DestroyObjectsProc(pEnumItem, NULL); } else { TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to find Item at %d", iIndex); }
} } else { TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Skipping this because we already processed it." "Dragging To Desktop?"); hres = E_FAIL; }
}
// Is this item is being rename INTO the Start Menu?
if (iShellFolder2 != -1) { TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: New pidl is in the Folder"); LPITEMIDLIST* ppidlNewWrapped1 = ppidlOut1; LPITEMIDLIST* ppidlNewWrapped2 = ppidlOut2; LONG* plNewEvent = plEvent;
if (fEvent1Set) { plNewEvent = plEvent2; ppidlNewWrapped1 = ppidlOut1Event2; ppidlNewWrapped2 = ppidlOut2Event2; }
if (S_OK == _SearchForPidl(psf2, pidlRealRel2, fFolder, &iIndex, &pEnumItem)) {
// If we're renaming from this folder, into this folder, Check to see if the destination has a
// conflict. If there is a confict (This case), then convert first event to a remove,
// and the second event to the rename.
if (fFirstPidlInNamespace) { fEvent1Set = TRUE; *plEvent = fFolder? SHCNE_RMDIR : SHCNE_DELETE; plNewEvent = plEvent2; ppidlNewWrapped1 = ppidlOut1Event2; ppidlNewWrapped2 = ppidlOut2Event2; } TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is in Folder", pEnumItem->_pszDisplayName); TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Adding pidl to %s. Convert to Rename for event %s", pEnumItem->_pszDisplayName, fEvent1Set? TEXT("2") : TEXT("1"));
// Then the destination needs to be merged.
*ppidlNewWrapped1 = ILCombineBase(pidl2, pEnumItem->_pidlWrap); if (*ppidlNewWrapped1) { TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Successfully created out pidl1"); AugMergeISF_WrapAddPidl(pidlRealRel2, iShellFolder2, &pEnumItem->_pidlWrap);
*ppidlNewWrapped2 = ILCombineBase(pidl2, pEnumItem->_pidlWrap);
*plNewEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM; } } else { LPITEMIDLIST pidlWrap; CAugISFEnumItem* paugmEnum = new CAugISFEnumItem;
if (paugmEnum) { if (SUCCEEDED(AugMergeISF_CreateWrap(pidlRealRel2, (UINT)iShellFolder2, &pidlWrap)) && paugmEnum->InitWithWrappedToOwn(SAFECAST(this, IAugmentedShellFolder2*), iShellFolder2, pidlWrap)) { AUGMISFSEARCHFORPIDL AugMSearch; AugMSearch.pszDisplayName = paugmEnum->_pszDisplayName; AugMSearch.fFolder = BOOLIFY(paugmEnum->_rgfAttrib & SFGAO_FOLDER);
int iInsertIndex = DPA_Search(_hdpaObjects, &AugMSearch, 0, AugMISFSearchForOnePidlByDisplayName, NULL, DPAS_SORTED | DPAS_INSERTAFTER);
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is a new item. Converting to Create", paugmEnum->_pszDisplayName);
if (iInsertIndex < 0) iInsertIndex = DA_LAST;
if (DPA_InsertPtr(_hdpaObjects, iInsertIndex, paugmEnum) == -1) { TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Was unable to add %s for some reason. Bailing", paugmEnum->_pszDisplayName); DestroyObjectsProc(paugmEnum, NULL); } else { TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Creating new item %s at %d for event %s", paugmEnum->_pszDisplayName, iInsertIndex, fEvent1Set? TEXT("2") : TEXT("1"));
// If we're renaming from this folder, into this folder, Then the first event stays
// a rename.
if (!fFirstPidlInNamespace) { *plNewEvent = fFolder ? SHCNE_MKDIR : SHCNE_CREATE; *ppidlNewWrapped1 = ILCombineBase(pidl2, pidlWrap); *ppidlNewWrapped2 = NULL; } else *ppidlOut2 = ILCombineBase(pidl2, pidlWrap);
} } else DestroyObjectsProc(paugmEnum, NULL);
} } } } break;
default: break; }
Cleanup: ILFree(pidlReal1); ILFree(pidlReal2);
#ifdef DEBUG
DumpObjects(); _fInternalGDNO = FALSE; #endif
return hres; }
BOOL CAugmentedMergeISF::IsChildIDInternal(LPCITEMIDLIST pidlKid, BOOL fImmediate, int* piShellFolder) { // This is basically the same Method as the interface method, but returns the shell folder
// that it came from.
BOOL fChild = FALSE;
//At this point we should have a translated pidl
if (pidlKid) { if (SUCCEEDED(AugMergeISF_IsWrap(pidlKid))) { LPCITEMIDLIST pidlRelKid = ILFindLastID(pidlKid); if (pidlRelKid) { UINT uiId; LPITEMIDLIST pidl; HANDLE hEnum = AugMergeISF_EnumFirstSrcPidl(pidlRelKid, &uiId, &pidl);
if (hEnum) { do { ILFree(pidl);
for (int i = 0; fChild == FALSE && i < DPA_GetPtrCount(_hdpaNamespaces); i++) { CNamespace * pns = (CNamespace *)DPA_GetPtr(_hdpaNamespaces, i); // reuse pidl
if (pns && (pidl = pns->GetPidl()) != NULL) { if (ILIsParent(pidl, pidlKid, fImmediate) && !ILIsEqual(pidl, pidlKid)) { fChild = TRUE; if (piShellFolder) *piShellFolder = i; } } } } while (fChild == FALSE && AugMergeISF_EnumNextSrcPidl(hEnum, &uiId, &pidl));
AugMergeISF_EndEnumSrcPidls(hEnum); } } } else { int cSrcs = NamespaceCount();
for(int i = 0; fChild == FALSE && i < cSrcs ; i++) { CNamespace* pSrc = Namespace(i); if (pSrc && ILIsParent(pSrc->GetPidl(), pidlKid, fImmediate) && !ILIsEqual(pSrc->GetPidl(), pidlKid)) { fChild = TRUE; if (piShellFolder) *piShellFolder = i; } } } }
return fChild; }
//-------------------------------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::IsChildID( LPCITEMIDLIST pidlKid, BOOL fImmediate) { return IsChildIDInternal(pidlKid, fImmediate, NULL) ? S_OK : S_FALSE; }
//-------------------------------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::IsEqualID( LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2 ) { // This used to return E_NOTIMPL. I'm kinda overloading the interface to mean:
// is this equal tp any of your namespaces.
HRESULT hres = S_FALSE; int cSrcs = NamespaceCount();
for(int i = 0; hres == S_FALSE && i < cSrcs ; i++) { CNamespace* pSrc = Namespace(i); if (pidl1) { if (pSrc && ILIsEqual(pSrc->GetPidl(), pidl1)) hres = S_OK; } else if (pidl2) // If you pass a pidl2 it means: Is pidl2 a parent of one of my namespaces?
{ if (pSrc && ILIsParent(pidl2, pSrc->GetPidl(), FALSE)) hres = S_OK; }
} return hres; }
//-------------------------------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::Register( HWND hwnd, UINT uMsg, long lEvents ) { int i, cSrcs ;
if( 0 >= (cSrcs = NamespaceCount()) ) return E_FAIL ; for( i = 0; i < cSrcs ; i++ ) { CNamespace* pSrc ; if( NULL != (pSrc = Namespace( i )) ) pSrc->RegisterNotify( hwnd, uMsg, lEvents ) ; } return S_OK ; }
//-------------------------------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::Unregister () { int i, cSrcs = NamespaceCount() ; if( cSrcs <= 0 ) return E_FAIL ; for( i = 0; i < cSrcs ; i++ ) { CNamespace* pSrc ; if( NULL != (pSrc = Namespace( i )) ) pSrc->UnregisterNotify() ; } return S_OK ; }
// *** IDropTarget methods ***
#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
HRESULT CAugmentedMergeISF::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { ASSERT(!_fCommon); ASSERT(_pdt == NULL); if (pDataObj) { InitClipboardFormats(); FORMATETC fmte = {g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium;
medium.pUnkForRelease = NULL; medium.hGlobal = NULL;
if (SUCCEEDED(pDataObj->GetData(&fmte, &medium))) { LPIDA pida = (LPIDA)GlobalLock(medium.hGlobal); if (pida) { LPCITEMIDLIST pidlItem = HIDA_GetPIDLFolder(pida);
_fCommon = BOOLIFY(_IsCommonPidl(pidlItem)); GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); }
CNamespace *pSrc = NULL; ULONG gdnsAttribs = 0; if (!_fCommon) gdnsAttribs = ASFF_DEFNAMESPACE_ALL; if (SUCCEEDED(GetDefNamespace(gdnsAttribs, (PVOID*)&pSrc, NULL, NULL))) { if (SUCCEEDED(pSrc->ShellFolder()->CreateViewObject(_hwnd, IID_IDropTarget, (void **)&_pdt))) { _pdt->DragEnter(pDataObj, grfKeyState, pt, pdwEffect); } } }
_grfDragEnterKeyState = grfKeyState; _dwDragEnterEffect = *pdwEffect;
return S_OK; }
HRESULT CAugmentedMergeISF::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { HRESULT hres = S_OK;
if (_pdt) hres = _pdt->DragOver(grfKeyState, pt, pdwEffect); return hres; }
HRESULT CAugmentedMergeISF::DragLeave(void) { HRESULT hres = S_OK;
_fCommon = 0; if (_pdt) { hres = _pdt->DragLeave(); ATOMICRELEASE(_pdt); } return hres; }
HRESULT CAugmentedMergeISF::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { HRESULT hres = S_OK; BOOL bNoUI = !_fCommon; BOOL bConfirmed = !_fCommon;
if (!_pdt && pDataObj) { LPITEMIDLIST pidlParent = NULL, pidlOther = NULL; int csidl = _fCommon ? CSIDL_COMMON_STARTMENU : CSIDL_STARTMENU, csidlOther = _fCommon ? CSIDL_STARTMENU : CSIDL_COMMON_STARTMENU;
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, csidl, &pidlParent)) && SUCCEEDED(SHGetSpecialFolderLocation(NULL, csidlOther, &pidlOther))) { FORMATETC fmte = {g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; medium.pUnkForRelease = NULL; medium.hGlobal = NULL;
if (SUCCEEDED(pDataObj->GetData(&fmte, &medium))) { LPIDA pida = (LPIDA)GlobalLock(medium.hGlobal); if (pida) { IShellFolder *psfParent = NULL, *psfOther = NULL;
if (SUCCEEDED(IEBindToObject(pidlParent, &psfParent)) && SUCCEEDED(IEBindToObject(pidlOther, &psfOther))) { LPCITEMIDLIST pidlItem, pidl; LPITEMIDLIST pidlRel; pidlItem = HIDA_GetPIDLItem(pida, 0);
// we came here because we don't have pdt which means that
// there is only one folder in our namespace and that's not
// the one we have to drop IDataObj on.
CNamespace* pCNamespace = Namespace(0);
if (pCNamespace) { pidl = pCNamespace->GetPidl(); // don't need to free pidl.
if (pidl) { pidlRel = ILClone(ILFindChild(pidlOther, pidl)); if (pidlRel) { STRRET strret; TCHAR szDir[MAX_PATH];
strret.uType = STRRET_CSTR; if (SUCCEEDED(psfParent->GetDisplayNameOf(pidlRel, SHGDN_FORPARSING, &strret)) && SUCCEEDED(StrRetToBuf(&strret, pidlRel, szDir, ARRAYSIZE(szDir)))) { if (_fCommon) { bConfirmed = AffectAllUsers(_hwnd); bNoUI = TRUE; }
if (bConfirmed) { BOOL bCreated = FALSE;
switch (SHCreateDirectory(_hwnd, szDir)) { case ERROR_FILENAME_EXCED_RANGE: case ERROR_FILE_EXISTS: case ERROR_ALREADY_EXISTS: case 0: // It was created successfully.
bCreated = TRUE; }
if (bCreated) { IShellFolder *psf; if (SUCCEEDED(psfParent->BindToObject(pidlRel, NULL, IID_IShellFolder, (void **)&psf))) { psf->CreateViewObject(_hwnd, IID_IDropTarget, (void **)&_pdt); // we're going to call drop on it, call dragenter first
if (_pdt) _pdt->DragEnter(pDataObj, _grfDragEnterKeyState, pt, &_dwDragEnterEffect); psf->Release(); } } } } ILFree(pidlRel); } } } } if (psfParent) psfParent->Release(); if (psfOther) psfOther->Release();
GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); } } ILFree(pidlParent); ILFree(pidlOther); }
if (_pdt) { hres = E_FAIL; if ((bNoUI || (bConfirmed = AffectAllUsers(_hwnd))) && bConfirmed) hres = _pdt->Drop(pDataObj, grfKeyState, pt, pdwEffect); else hres = _pdt->DragLeave();
ATOMICRELEASE(_pdt); } _fCommon = 0;
return hres; }
//-------------------------------------------------------------------------------------------------//
LPITEMIDLIST CAugmentedMergeISF::GetNativePidl(LPCITEMIDLIST pidlWrap, LPARAM nSrcID /*int nID*/) { LPITEMIDLIST pidlRet = NULL;
if (SUCCEEDED(AugMergeISF_GetSrcPidl(pidlWrap, (UINT)nSrcID, &pidlRet))) return pidlRet ;
// not wrapped by me.
return NULL; }
BOOL AffectAllUsers(HWND hwnd) { TCHAR szMessage[255]; TCHAR szTitle[20]; BOOL bRet = FALSE;
if (MLLoadShellLangString(IDS_ALLUSER_WARNING, szMessage, ARRAYSIZE(szMessage)) > 0 && MLLoadShellLangString(IDS_ALLUSER_WARNING_TITLE, szTitle, ARRAYSIZE(szTitle)) > 0) { bRet = IDYES == MessageBox(hwnd, szMessage, szTitle, MB_YESNO | MB_ICONINFORMATION); } return bRet; }
BOOL CAugmentedMergeISF::_IsCommonPidl(LPCITEMIDLIST pidlItem) { BOOL bRet = FALSE; LPITEMIDLIST pidlCommon; if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &pidlCommon))) { bRet = ILIsParent(pidlCommon, pidlItem, FALSE); ILFree(pidlCommon); } if (!bRet && SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidlCommon))) { bRet = ILIsParent(pidlCommon, pidlItem, FALSE); ILFree(pidlCommon); } if (!bRet && SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, &pidlCommon))) { bRet = ILIsParent(pidlCommon, pidlItem, FALSE); ILFree(pidlCommon); }
return bRet; }
HRESULT CAugmentedMergeISF::_SearchForPidl(IShellFolder* psf, LPCITEMIDLIST pidl, BOOL fFolder, int* piIndex, CAugISFEnumItem** ppEnumItem) { STRRET str; TCHAR szDisplayName[MAX_PATH]; int iIndex = -1;
*ppEnumItem = NULL;
if (SUCCEEDED(psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str)) && SUCCEEDED(StrRetToBuf(&str, pidl, szDisplayName, ARRAYSIZE(szDisplayName)))) { AUGMISFSEARCHFORPIDL SearchFor; SearchFor.pszDisplayName = szDisplayName; SearchFor.fFolder = fFolder;
iIndex = DPA_Search(_hdpaObjects, (LPVOID)&SearchFor, 0, AugMISFSearchForOnePidlByDisplayName, NULL, DPAS_SORTED);
if (iIndex >= 0) { *ppEnumItem = DPA_GETPTR( _hdpaObjects, iIndex, CAugISFEnumItem); } }
if (piIndex) *piIndex = iIndex;
if (*ppEnumItem) return S_OK;
return S_FALSE; }
// given a wrapped pidl
// f-n returns common and user namespaces (if they are in wrapped pidl) -- note that they are not addrefed
// unwrapped pidl, and if the unwrapped pidl is folder or not
HRESULT CAugmentedMergeISF::_GetNamespaces(LPCITEMIDLIST pidlWrap, CNamespace** ppnsCommon, UINT* pnCommonID, CNamespace** ppnsUser, UINT* pnUserID, LPITEMIDLIST* ppidl, BOOL *pbIsFolder) { HRESULT hres; UINT nSrcID; CNamespace * pns; int cWrapped; HANDLE hEnum; ASSERT(ppnsCommon && ppnsUser && ppidl);
*ppnsCommon = NULL; *ppnsUser = NULL; *ppidl = NULL;
ASSERT(SUCCEEDED(AugMergeISF_IsWrap(pidlWrap))); cWrapped = AugMergeISF_GetSourceCount(pidlWrap);
if (NULL == _hdpaNamespaces || 0 >= cWrapped || NULL == (hEnum = AugMergeISF_EnumFirstSrcPidl(pidlWrap, &nSrcID, ppidl))) return E_FAIL; hres = QueryNameSpace(nSrcID, (void **)&pns); if (EVAL(SUCCEEDED(hres))) { IShellFolder * psf; ULONG rgf = SFGAO_FOLDER;
psf = pns->ShellFolder(); // no addref
ASSERT(psf); if (SUCCEEDED(psf->GetAttributesOf(1, (LPCITEMIDLIST*)ppidl, &rgf))) { if (pbIsFolder) *pbIsFolder = rgf & SFGAO_FOLDER; LPITEMIDLIST pidlItem; UINT nCommonID; CNamespace* pnsCommonTemp;
// get common namespace (attribs = 0)
hres = GetDefNamespace(0, (void **)&pnsCommonTemp, &nCommonID, NULL); ASSERT(NamespaceCount() == 2 && SUCCEEDED(hres) || NamespaceCount() == 1); if (FAILED(hres)) nCommonID = 1;
if (nCommonID == nSrcID) { *ppnsCommon = pns; if (pnCommonID) *pnCommonID = nCommonID; } else { *ppnsUser = pns; if (pnUserID) *pnUserID = nSrcID; } if (AugMergeISF_EnumNextSrcPidl(hEnum, &nSrcID, &pidlItem)) { ASSERT(ILIsEqual(*ppidl, pidlItem)); ILFree(pidlItem); if (SUCCEEDED(QueryNameSpace(nSrcID, (void **)&pns))) { ASSERT(pns); if (nCommonID == nSrcID) { *ppnsCommon = pns; if (pnCommonID) *pnCommonID = nCommonID; } else { *ppnsUser = pns; if (pnUserID) *pnUserID = nSrcID; } } }
hres = S_OK; } } AugMergeISF_EndEnumSrcPidls(hEnum);
return hres; }
HRESULT CAugmentedMergeISF::_GetContextMenu(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl, UINT * prgfInOut, void ** ppvOut) { HRESULT hres; LPITEMIDLIST pidl; BOOL bIsFolder; CNamespace * pnsCommon; CNamespace * pnsUser;
ASSERT(cidl == 1);
// psfCommon and psfUser are not addrefed
hres = _GetNamespaces(apidl[0], &pnsCommon, NULL, &pnsUser, NULL, &pidl, &bIsFolder); if (SUCCEEDED(hres)) { ASSERT(pnsCommon || pnsUser); if (bIsFolder) { // folder? need our context menu
IShellFolder * psfCommon = NULL; IShellFolder * psfUser = NULL; LPCITEMIDLIST pidlCommon = NULL; LPCITEMIDLIST pidlUser = NULL;
if (pnsCommon) { psfCommon = pnsCommon->ShellFolder(); pidlCommon = pnsCommon->GetPidl(); } if (pnsUser) { psfUser = pnsUser->ShellFolder(); pidlUser = pnsUser->GetPidl(); } CAugMergeISFContextMenu * pcm = CreateMergeISFContextMenu(psfCommon, pidlCommon, psfUser, pidlUser, pidl, hwnd, prgfInOut);
if (pcm) { hres = pcm->QueryInterface(IID_IContextMenu, ppvOut); pcm->Release(); } else hres = E_OUTOFMEMORY; } else { // it's not a folder
// delegate to the isf
IShellFolder * psf = pnsUser ? pnsUser->ShellFolder() : pnsCommon->ShellFolder();
hres = psf->GetUIObjectOf(hwnd, 1, (LPCITEMIDLIST*)&pidl, IID_IContextMenu, prgfInOut, ppvOut); } ILFree(pidl); }
return hres; }
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetDefNamespace( LPCITEMIDLIST pidlWrap, ULONG dwAttrib, OUT IShellFolder** ppsf, OUT LPITEMIDLIST* ppidlItem ) { HRESULT hr ; LPITEMIDLIST pidl ; CNamespace* pSrc ; ULONG dwDefAttrib = dwAttrib & ASFF_DEFNAMESPACE_ALL ; int cWrapped ; UINT nSrcID ; HANDLE hEnum ;
ASSERT( ppsf ) ; *ppsf = NULL ; if (ppidlItem) *ppidlItem = NULL ;
if (FAILED((hr = AugMergeISF_IsWrap( pidlWrap )))) return hr ; cWrapped = AugMergeISF_GetSourceCount( pidlWrap ) ;
// No namespaces?
if (NULL == _hdpaNamespaces || 0 >= cWrapped || NULL == (hEnum = AugMergeISF_EnumFirstSrcPidl( pidlWrap, &nSrcID, &pidl))) return E_FAIL ;
// Only one namespace in wrap? Give up the shell folder and item ID.
if (1 == cWrapped || 0==dwDefAttrib) { AugMergeISF_EndEnumSrcPidls( hEnum ) ; // no need to go further
// Retrieve the namespace object identified by nSrcID.
if( SUCCEEDED( (hr = QueryNameSpace( nSrcID, (PVOID*)&pSrc )) ) ) { *ppsf = pSrc->ShellFolder() ; if( ppidlItem ) *ppidlItem = pidl ; return S_OK ; }
ILFree( pidl ) ; return hr ; }
// More than one namespace in wrap?
if( cWrapped > 1 ) { LPITEMIDLIST pidl0 = NULL ; CNamespace* pSrc0 = NULL ; // get this below.
for (BOOL bEnum = TRUE ; bEnum ; bEnum = AugMergeISF_EnumNextSrcPidl(hEnum, &nSrcID, &pidl)) { if (SUCCEEDED((hr = QueryNameSpace(nSrcID, (PVOID*)&pSrc)))) { if (dwDefAttrib & pSrc->Attrib()) { // Matched attributes; we're done.
AugMergeISF_EndEnumSrcPidls(hEnum); *ppsf = pSrc->ShellFolder() ; if (ppidlItem) *ppidlItem = pidl; if(pidl0) ILFree(pidl0); return S_OK ; }
// Stash first namespace object and item pidl.
// We'll default to these if
if( NULL == pSrc0 ) { pSrc0 = pSrc ; pidl0 = ILClone( pidl ) ; } } ILFree( pidl ) ; } AugMergeISF_EndEnumSrcPidls( hEnum ) ; // Default to first namespace
if( pSrc0 && pidl0 ) { *ppsf = pSrc0->ShellFolder() ; if( ppidlItem ) *ppidlItem = pidl0 ; return S_OK ; } } return E_UNEXPECTED ; }
//-------------------------------------------------------------------------//
// Retrieves the default namespaces for the indicated attibutes.
// The dwAttrib arg must be initialized prior to function entry,
STDMETHODIMP CAugmentedMergeISF::GetDefNamespace( ULONG dwAttrib, OUT PVOID* ppSrc, UINT *pnSrcID, PVOID* ppSrc0 ) { CNamespace* pSrc ; ULONG dwDefAttrib = dwAttrib & ASFF_DEFNAMESPACE_ALL ;
// this is an internal helper so we better make sure we pass the correct params!
//if (NULL == ppSrc)
// return E_INVALIDARG ;
*ppSrc = NULL ; if( ppSrc0 ) *ppSrc0 = NULL ;
for( int i = 0, cSrcs = NamespaceCount(); i < cSrcs ; i++ ) { if( NULL != (pSrc = Namespace( i )) ) { if( 0 == i && ppSrc0 ) *ppSrc0 = pSrc ;
if( dwDefAttrib & pSrc->Attrib() || dwDefAttrib == 0 && !(pSrc->Attrib() & ASFF_DEFNAMESPACE_ALL)) { *ppSrc = pSrc; if (NULL != pnSrcID) *pnSrcID = i; return S_OK ; } } }
return E_FAIL ; }
// BUGBUG(lamadio): Move this to a better location, This is a nice generic function
#ifdef DEBUG
BOOL DPA_VerifySorted(HDPA hdpa, PFNDPACOMPARE pfn, LPARAM lParam) { if (!EVAL(hdpa)) return FALSE;
for (int i = 0; i < DPA_GetPtrCount(hdpa) - 1; i++) { if (pfn(DPA_FastGetPtr(hdpa, i), DPA_FastGetPtr(hdpa, i + 1), lParam) > 0) return FALSE; }
return TRUE; } #else
#define DPA_VerifySorted(hdpa, pfn, lParam)
#endif
int CAugmentedMergeISF::AcquireObjects() { HDPA hdpa2 = NULL;
DEBUG_CODE(_fInternalGDNO = TRUE); for (int i = 0; i < DPA_GETPTRCOUNT(_hdpaNamespaces); i++) { CNamespace * pns = DPA_GETPTR(_hdpaNamespaces, i, CNamespace); IShellFolder * psf; IEnumIDList * peid; HDPA * phdpa;
ASSERT(pns); psf = pns->ShellFolder(); // no addref here!
if (i == 0) { phdpa = &_hdpaObjects; _hdpaObjects = DPA_Create(4); // We should always create the DPA
} else { ASSERT(i == 1); // no support for more than 2 isf's
phdpa = &hdpa2; } HRESULT hres = psf->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &peid); if (SUCCEEDED(hres)) { if (!*phdpa) *phdpa = DPA_Create(4);
if (*phdpa) { LPITEMIDLIST pidl; ULONG cEnum; while (SUCCEEDED(peid->Next(1, &pidl, &cEnum)) && 1 == cEnum) { CAugISFEnumItem* paugmEnum = new CAugISFEnumItem;
if (paugmEnum) { if (paugmEnum->Init(SAFECAST(this, IAugmentedShellFolder2*), i, pidl)) { if (DPA_AppendPtr(*phdpa, paugmEnum) == -1) DestroyObjectsProc(paugmEnum, NULL); } else delete paugmEnum; } ILFree(pidl); } } peid->Release(); } else { TraceMsg(TF_WARNING, "CAugMISF::AcquireObjects: Failed to get enumerator 0x%x", hres);
} }
// now that we have both hdpa's (or one) let's merge them.
if (DPA_GETPTRCOUNT(_hdpaNamespaces) == 2 && hdpa2) { DPA_Merge(_hdpaObjects, hdpa2, DPAM_UNION, AugmEnumCompare, AugmEnumMerge, (LPARAM)0); DPA_DESTROY(hdpa2, DestroyObjectsProc); } else { DPA_Sort(_hdpaObjects, AugmEnumCompare, 0); }
ASSERT(DPA_VerifySorted(_hdpaObjects, AugmEnumCompare, 0));
DEBUG_CODE(_fInternalGDNO = FALSE);
#ifdef DEBUG
TraceMsg(TF_AUGM, "CAugMISF::AcquireObjects"); DumpObjects(); #endif
_count = DPA_GETPTRCOUNT(_hdpaObjects); return _count; }
//-------------------------------------------------------------------------//
void CAugmentedMergeISF::FreeObjects() { DPA_DESTROY( _hdpaObjects, DestroyObjectsProc ) ; _hdpaObjects = NULL; _count = 0 ; }
//-------------------------------------------------------------------------//
int CAugmentedMergeISF::DestroyObjectsProc( LPVOID pv, LPVOID pvData ) { CAugISFEnumItem* paugmEnum = (CAugISFEnumItem*)pv;
if (EVAL(NULL != paugmEnum)) { delete paugmEnum; } return TRUE ; }
//-------------------------------------------------------------------------//
void CAugmentedMergeISF::FreeNamespaces() { DPA_DESTROY( _hdpaNamespaces, DestroyNamespacesProc ) ; }
//-------------------------------------------------------------------------//
int CAugmentedMergeISF::DestroyNamespacesProc( LPVOID pv, LPVOID pvData ) { CNamespace* p ; if( NULL != (p = (CNamespace*)pv) ) delete p ; return TRUE ; }
STDMETHODIMP CAugmentedMergeISF::GetPidl(int* piPos, DWORD grfEnumFlags, LPITEMIDLIST* ppidl) { *ppidl = NULL;
if (_hdpaObjects == NULL) AcquireObjects();
if (_hdpaObjects == NULL) return E_OUTOFMEMORY;
BOOL fWantFolders = 0 != (grfEnumFlags & SHCONTF_FOLDERS), fWantNonFolders = 0 != (grfEnumFlags & SHCONTF_NONFOLDERS), fWantHidden = 0 != (grfEnumFlags & SHCONTF_INCLUDEHIDDEN) ;
while (*piPos < _count) { CAugISFEnumItem* paugmEnum = DPA_GETPTR( _hdpaObjects, *piPos, CAugISFEnumItem); if ( NULL != paugmEnum ) { BOOL fFolder = 0 != (paugmEnum->_rgfAttrib & SFGAO_FOLDER), fHidden = 0 != (paugmEnum->_rgfAttrib & SFGAO_HIDDEN); if ((!fHidden || fWantHidden) && ((fFolder && fWantFolders) || (!fFolder && fWantNonFolders))) { // Copy out the pidl ;
*ppidl = ILClone(paugmEnum->_pidlWrap); break; } else { (*piPos)++; } } }
if (*ppidl) return S_OK;
return S_FALSE; }
//-------------------------------------------------------------------------//
CEnum::CEnum(IAugmentedMergedShellFolderInternal* psmsfi, DWORD grfEnumFlags, int iPos) : _cRef(1), _iPos(iPos), _psmsfi(psmsfi), _grfEnumFlags(grfEnumFlags)
{ _psmsfi->AddRef(); }
CEnum::~CEnum() { ATOMICRELEASE(_psmsfi); }
//-------------------------------------------------------------------------//
// class CEnum - IUnknown methods
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
STDMETHODIMP CEnum::QueryInterface( REFIID riid, LPVOID * ppvObj ) { static const QITAB qit[] = { QITABENT(CEnum, IEnumIDList), { 0 } }; return QISearch(this, qit, riid, ppvObj); } //-------------------------------------------------------------------------//
STDMETHODIMP_(ULONG) CEnum::AddRef () { return InterlockedIncrement(&_cRef); } //-------------------------------------------------------------------------//
STDMETHODIMP_(ULONG) CEnum::Release () { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
//-------------------------------------------------------------------------//
// class CEnum - IEnumIDList methods
//-------------------------------------------------------------------------//
STDMETHODIMP CEnum::Next( ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched ) { int iStart = _iPos; int cFetched = 0; HRESULT hres = S_OK;
if( !(celt > 0 && rgelt) || (NULL == pceltFetched && celt > 1 ) ) return E_INVALIDARG ; *rgelt = 0;
while(hres == S_OK && (_iPos - iStart) < (int)celt) { LPITEMIDLIST pidl; hres = _psmsfi->GetPidl(&_iPos, _grfEnumFlags, &pidl); if (hres == S_OK) { rgelt[cFetched] = pidl; cFetched++ ; } _iPos++; } if( pceltFetched ) *pceltFetched = cFetched ; return celt == (ULONG)cFetched ? S_OK : S_FALSE ; }
//-------------------------------------------------------------------------//
STDMETHODIMP CEnum::Skip(ULONG celt) { _iPos += celt; return S_OK ; } //-------------------------------------------------------------------------//
STDMETHODIMP CEnum::Reset() { _iPos = 0; return S_OK ; } //-------------------------------------------------------------------------//
// REVIEW: Can probably be E_NOTIMPL
STDMETHODIMP CEnum::Clone( IEnumIDList **ppenum ) { if( NULL == (*ppenum = new CEnum( _psmsfi, _grfEnumFlags, _iPos )) ) return E_OUTOFMEMORY;
return S_OK; }
BOOL CAugISFEnumItem::Init(IShellFolder* psf, int iShellFolder, LPCITEMIDLIST pidl) { // This is ok, the memory just gets written to twice.
if (SUCCEEDED(AugMergeISF_CreateWrap(pidl, iShellFolder, &_pidlWrap))) { // Takes ownership of passed in pidl.
return InitWithWrappedToOwn(psf, iShellFolder, _pidlWrap); }
return FALSE; }
BOOL CAugISFEnumItem::InitWithWrappedToOwn(IShellFolder* psf, int iShellFolder, LPITEMIDLIST pidl) { BOOL fRet = FALSE; STRRET str; TCHAR szDisplayName[MAX_PATH];
_pidlWrap = pidl; _rgfAttrib = SFGAO_FOLDER | SFGAO_HIDDEN;
psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidl, &_rgfAttrib);
if (SUCCEEDED(psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str)) && SUCCEEDED(StrRetToBuf(&str, pidl, szDisplayName, ARRAYSIZE(szDisplayName)))) { SetDisplayName(szDisplayName); fRet = TRUE; } return fRet; }
|