SHLFLDR.CPP

/************************************************************************** 
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Copyright 1997 Microsoft Corporation. All Rights Reserved.
**************************************************************************/

/**************************************************************************

File: ShlFldr.cpp

Description: Implements CShellFolder.

**************************************************************************/

/**************************************************************************
#include statements
**************************************************************************/

#include "ShlFldr.h"
#include "ShlView.h"
#include "InfoTip.h"
#include "ExtrIcon.h"
#include "ContMenu.h"
#include "Guid.h"
#include "resource.h"

/**************************************************************************
global variables
**************************************************************************/

HKEY g_ahKey[] = { HKEY_CLASSES_ROOT,
HKEY_CURRENT_USER,
HKEY_LOCAL_MACHINE,
HKEY_USERS,
HKEY_PERFORMANCE_DATA,
HKEY_CURRENT_CONFIG,
HKEY_DYN_DATA,
(HKEY)-1};

/**************************************************************************

CShellFolder::CShellFolder()

**************************************************************************/

CShellFolder::CShellFolder(CShellFolder *pParent, LPCITEMIDLIST pidl)
{
m_hKeyRoot = NULL;
m_lpszSubKey = NULL;

m_pSFParent = pParent;
if(m_pSFParent)
m_pSFParent->AddRef();

m_pPidlMgr = new CPidlMgr();
if(!m_pPidlMgr)
{
delete this;
return;
}

//get the shell's IMalloc pointer
//we'll keep this until we get destroyed
if(FAILED(SHGetMalloc(&m_pMalloc)))
{
delete this;
return;
}

m_pidlRel = m_pPidlMgr->Copy(pidl);
m_pidlFQ = NULL;

if(m_pidlRel)
{
//determine if this PIDL has an HKEY or a sub key string
if(m_pPidlMgr->IsRootKey(m_pidlRel))
{
//this is a root key
m_pPidlMgr->GetRootKey(m_pidlRel, &m_hKeyRoot);
}

DWORD dwSize = m_pPidlMgr->GetSubKeyText(m_pidlRel, NULL, 0);
m_lpszSubKey = new TCHAR[dwSize];
if(m_lpszSubKey)
{
m_pPidlMgr->GetSubKeyText(m_pidlRel, m_lpszSubKey, dwSize);
}
}

m_ObjRefCount = 1;

g_DllRefCount++;
}

/**************************************************************************

CShellFolder::~CShellFolder()

**************************************************************************/

CShellFolder::~CShellFolder()
{
if(m_pidlRel)
{
m_pPidlMgr->Delete(m_pidlRel);
m_pidlRel = NULL;
}

if(m_pidlFQ)
{
m_pPidlMgr->Delete(m_pidlFQ);
m_pidlFQ = NULL;
}

if(m_lpszSubKey)
delete m_lpszSubKey;

if(m_pSFParent)
m_pSFParent->Release();

if(m_pMalloc)
{
m_pMalloc->Release();
}

if(m_pPidlMgr)
{
delete m_pPidlMgr;
}

g_DllRefCount--;
}

///////////////////////////////////////////////////////////////////////////
//
// IUnknown Implementation
//

/**************************************************************************

CShellFolder::QueryInterface

**************************************************************************/

STDMETHODIMP CShellFolder::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

//IUnknown
if(IsEqualIID(riid, IID_IUnknown))
{
*ppReturn = this;
}

//IPersistFolder
else if(IsEqualIID(riid, IID_IPersistFolder))
{
*ppReturn = (IPersistFolder*)this;
}

//IShellFolder
else if(IsEqualIID(riid, IID_IShellFolder))
{
*ppReturn = (IShellFolder*)this;
}

if(*ppReturn)
{
(*(LPUNKNOWN*)ppReturn)->AddRef();
return S_OK;
}

return E_NOINTERFACE;
}

/**************************************************************************

CShellFolder::AddRef

**************************************************************************/

STDMETHODIMP_(DWORD) CShellFolder::AddRef()
{
return ++m_ObjRefCount;
}

/**************************************************************************

CShellFolder::Release

**************************************************************************/

STDMETHODIMP_(DWORD) CShellFolder::Release()
{
if(--m_ObjRefCount == 0)
{
delete this;
return 0;
}

return m_ObjRefCount;
}

///////////////////////////////////////////////////////////////////////////
//
// IPersist Implementation
//

/**************************************************************************

CShellView::GetClassID()

**************************************************************************/

STDMETHODIMP CShellFolder::GetClassID(LPCLSID lpClassID)
{
*lpClassID = CLSID_RegViewNameSpace;

return S_OK;
}

///////////////////////////////////////////////////////////////////////////
//
// IPersistFolder Implementation
//

/**************************************************************************

CShellView::Initialize()

**************************************************************************/

STDMETHODIMP CShellFolder::Initialize(LPCITEMIDLIST pidl)
{
if(m_pidlFQ)
{
m_pPidlMgr->Delete(m_pidlFQ);
m_pidlFQ = NULL;
}

m_pidlFQ = m_pPidlMgr->Copy(pidl);

return S_OK;
}

///////////////////////////////////////////////////////////////////////////
//
// IShellFolder Implementation
//

/**************************************************************************

CShellFolder::BindToObject()

**************************************************************************/

STDMETHODIMP CShellFolder::BindToObject( LPCITEMIDLIST pidl,
LPBC pbcReserved,
REFIID riid,
LPVOID *ppvOut)
{
*ppvOut = NULL;

CShellFolder *pShellFolder = new CShellFolder(this, pidl);
if(!pShellFolder)
return E_OUTOFMEMORY;

LPITEMIDLIST pidlFQ;

pidlFQ = m_pPidlMgr->Concatenate(m_pidlFQ, pidl);
pShellFolder->Initialize(pidlFQ);
m_pPidlMgr->Delete(pidlFQ);

HRESULT hr = pShellFolder->QueryInterface(riid, ppvOut);

pShellFolder->Release();

return hr;
}

/**************************************************************************

CShellFolder::BindToStorage()

**************************************************************************/

STDMETHODIMP CShellFolder::BindToStorage( LPCITEMIDLIST pidl,
LPBC pbcReserved,
REFIID riid,
LPVOID *ppvOut)
{
*ppvOut = NULL;

return E_NOTIMPL;
}

/**************************************************************************

CShellFolder::CompareIDs()

**************************************************************************/

STDMETHODIMP CShellFolder::CompareIDs( LPARAM lParam,
LPCITEMIDLIST pidl1,
LPCITEMIDLIST pidl2)
{
TCHAR szString1[MAX_PATH] = TEXT("");
TCHAR szString2[MAX_PATH] = TEXT("");
HKEY hkey1 = NULL,
hkey2 = NULL;
int nReturn;

/*
Special case - If one of the items is a key and the other is a value, always
make the key come before the value.
*/
LPCITEMIDLIST pidlTemp1 = pidl1,
pidlTemp2 = pidl2;

//get the last item in each list
while((m_pPidlMgr->GetNextItem(pidlTemp1))->mkid.cb)
pidlTemp1 = m_pPidlMgr->GetNextItem(pidlTemp1);
while((m_pPidlMgr->GetNextItem(pidlTemp2))->mkid.cb)
pidlTemp2 = m_pPidlMgr->GetNextItem(pidlTemp2);

//at this point, both pidlTemp1 and pidlTemp2 point to the last item in the list
if(m_pPidlMgr->IsValue(pidlTemp1) != m_pPidlMgr->IsValue(pidlTemp2))
{
if(m_pPidlMgr->IsValue(pidlTemp1))
return 1;

return -1;
}

//get the numeric value of the root key
m_pPidlMgr->GetRootKey(pidl1, &hkey1);
m_pPidlMgr->GetRootKey(pidl2, &hkey2);

//compare the root keys
if(hkey1 != hkey2)
{
return (int)((DWORD)hkey1 - (DWORD)hkey2);
}

//now compare the subkey strings
m_pPidlMgr->GetSubKeyText( pidl1,
szString1,
sizeof(szString1));
m_pPidlMgr->GetSubKeyText( pidl2,
szString2,
sizeof(szString2));
nReturn = lstrcmpi(szString1, szString2);
if(nReturn)
return nReturn;

//now compare the value strings
m_pPidlMgr->GetValueText( pidl1,
szString1,
sizeof(szString1));
m_pPidlMgr->GetValueText( pidl2,
szString2,
sizeof(szString2));
return lstrcmpi(szString1, szString2);
}

/**************************************************************************

CShellFolder::CreateViewObject()

**************************************************************************/

STDMETHODIMP CShellFolder::CreateViewObject( HWND hwndOwner,
REFIID riid,
LPVOID *ppvOut)
{
HRESULT hr;
CShellView *pShellView;

*ppvOut = NULL;

pShellView = new CShellView(this, m_pidlRel);
if(!pShellView)
return E_OUTOFMEMORY;

hr = pShellView->QueryInterface(riid, ppvOut);

pShellView->Release();

return hr;
}

/**************************************************************************

CShellFolder::EnumObjects()

**************************************************************************/

STDMETHODIMP CShellFolder::EnumObjects( HWND hwndOwner,
DWORD dwFlags,
LPENUMIDLIST *ppEnumIDList)
{
HRESULT hr;

*ppEnumIDList = NULL;

*ppEnumIDList = new CEnumIDList(GetRootKey(), m_lpszSubKey, dwFlags, &hr);
if(!*ppEnumIDList)
return hr;

return S_OK;
}

/**************************************************************************

CShellFolder::GetAttributesOf()

**************************************************************************/

STDMETHODIMP CShellFolder::GetAttributesOf( UINT uCount,
LPCITEMIDLIST aPidls[],
LPDWORD pdwAttribs)
{
UINT i;

*pdwAttribs = (DWORD)-1;

for(i = 0; i < uCount; i++)
{
DWORD dwAttribs = 0;

//flags common to all items
dwAttribs |= 0;

//is this item a key?
if(!m_pPidlMgr->IsValue(m_pPidlMgr->GetLastItem(aPidls[i])))
{
dwAttribs |= SFGAO_FOLDER;

//does this item have a sub folder?
if(m_pPidlMgr->HasSubKeys(GetRootKey(), m_lpszSubKey, aPidls[i]))
dwAttribs |= SFGAO_HASSUBFOLDER;
}

*pdwAttribs &= dwAttribs;
}

return S_OK;
}

/**************************************************************************

CShellFolder::GetUIObjectOf()

**************************************************************************/

STDMETHODIMP CShellFolder::GetUIObjectOf( HWND hwndOwner,
UINT uCount,
LPCITEMIDLIST *pPidl,
REFIID riid,
LPUINT puReserved,
LPVOID *ppvReturn)
{
*ppvReturn = NULL;

if(IsEqualIID(riid, IID_IContextMenu))
{
CContextMenu *pcm = new CContextMenu(this, pPidl, uCount);

if(pcm)
{
*ppvReturn = pcm;
return S_OK;
}
}

if(uCount != 1)
return E_FAIL;

if(IsEqualIID(riid, IID_IExtractIcon))
{
CExtractIcon *pei;
LPITEMIDLIST pidl;

pidl = m_pPidlMgr->Concatenate(m_pidlRel, pPidl[0]);

pei = new CExtractIcon(pidl);

/*
The temp PIDL can be deleted because the new CExtractIcon either failed or
made its own copy of it.
*/
m_pPidlMgr->Delete(pidl);

if(pei)
{
*ppvReturn = pei;
return S_OK;
}

return E_OUTOFMEMORY;
}

#if (_WIN32_IE >= 0x0400)
if(IsEqualIID(riid, IID_IQueryInfo))
{
CQueryInfo *pqit;
LPITEMIDLIST pidl;

pidl = m_pPidlMgr->Concatenate(m_pidlRel, pPidl[0]);

pqit = new CQueryInfo(pidl);

/*
The temp PIDL can be deleted because the new CQueryInfo either failed or
made its own copy of it.
*/
m_pPidlMgr->Delete(pidl);

if(pqit)
{
*ppvReturn = pqit;
return S_OK;
}

return E_OUTOFMEMORY;
}
#endif //#if (_WIN32_IE >= 0x0400)

return E_NOINTERFACE;
}

/**************************************************************************

CShellFolder::GetDisplayNameOf()

**************************************************************************/

#define GET_SHGDN_FOR(dwFlags) ((DWORD)dwFlags & (DWORD)0x0000FF00)
#define GET_SHGDN_RELATION(dwFlags) ((DWORD)dwFlags & (DWORD)0x000000FF)

STDMETHODIMP CShellFolder::GetDisplayNameOf( LPCITEMIDLIST pidl,
DWORD dwFlags,
LPSTRRET lpName)
{
TCHAR szText[MAX_PATH];
int cchOleStr;

switch(GET_SHGDN_RELATION(dwFlags))
{
case SHGDN_NORMAL:
//get the full name
m_pPidlMgr->GetPidlPath(pidl, szText, sizeof(szText));

//If the text is NULL and this is a value, get the default item name.
if(!*szText && m_pPidlMgr->IsValue(m_pPidlMgr->GetLastItem(pidl)))
{
LoadString(g_hInst, IDS_DEFAULT, szText, sizeof(szText));
}
break;

case SHGDN_INFOLDER:
{
LPITEMIDLIST pidlTemp = m_pPidlMgr->GetLastItem(pidl);

//get the relative name
m_pPidlMgr->GetItemText(pidlTemp, szText, sizeof(szText));

//If the text is NULL and this is a value, get the default item name.
if(!*szText && m_pPidlMgr->IsValue(pidlTemp))
{
LoadString(g_hInst, IDS_DEFAULT, szText, sizeof(szText));
}
}
break;

default:
return E_INVALIDARG;
}

//get the number of characters required
cchOleStr = lstrlen(szText) + 1;

//allocate the wide character string
lpName->pOleStr = (LPWSTR)m_pMalloc->Alloc(cchOleStr * sizeof(WCHAR));
if(!lpName->pOleStr)
return E_OUTOFMEMORY;

lpName->uType = STRRET_WSTR;

LocalToWideChar(lpName->pOleStr, szText, cchOleStr);

return S_OK;
}

/**************************************************************************

CShellFolder::ParseDisplayName()

**************************************************************************/

STDMETHODIMP CShellFolder::ParseDisplayName( HWND hwndOwner,
LPBC pbcReserved,
LPOLESTR lpDisplayName,
LPDWORD pdwEaten,
LPITEMIDLIST *pPidlNew,
LPDWORD pdwAttributes)
{
HRESULT hr = E_OUTOFMEMORY;
LPITEMIDLIST pidlFull = NULL;
DWORD dwChars = lstrlenW(lpDisplayName) + 1;
LPTSTR pszTemp = (LPTSTR)GlobalAlloc(GPTR, dwChars * sizeof(TCHAR));

if(pszTemp)
{
hr = E_FAIL;

WideCharToLocal(pszTemp, lpDisplayName, dwChars);

if(*pszTemp)
{
LPTSTR pszNext;
TCHAR szElement[MAX_PATH];
int i;
BOOL fValue;

//get the root key

pszNext = GetNextRegElement(pszTemp, szElement, ARRAYSIZE(szElement));

for(i = 0; (-1 != (int)g_ahKey[i]) && !pidlFull; i++)
{
TCHAR szKeyText[MAX_PATH];

GetRootKeyText(g_ahKey[i], szKeyText, ARRAYSIZE(szKeyText));

if(0 == lstrcmpi(szElement, szKeyText))
{
pidlFull = m_pPidlMgr->CreateRootKey(g_ahKey[i]);
}
}

if(pidlFull)
{
LPITEMIDLIST pidlTemp = NULL;
LPITEMIDLIST pidlOld = NULL;
HKEY hKey;

//check to see if the last item is a value or a key
if(NOERROR == RegOpenKeyEx( g_ahKey[i],
pszNext,
0,
KEY_READ,
&hKey))
{
fValue = FALSE;
RegCloseKey(hKey);
}
else
fValue = TRUE;

//get the remaining keys
while(pszNext = GetNextRegElement(pszNext, szElement, ARRAYSIZE(szElement)))
{
//if this is the last element, check to see if it is a value or a key
if(!*pszNext && fValue)
pidlTemp = m_pPidlMgr->CreateValue(szElement);
else
pidlTemp = m_pPidlMgr->CreateSubKey(szElement);

pidlOld = pidlFull;
pidlFull = m_pPidlMgr->Concatenate(pidlFull, pidlTemp);
m_pPidlMgr->Delete(pidlOld);
}

hr = S_OK;
}
}

GlobalFree((HGLOBAL)pszTemp);
}

*pPidlNew = pidlFull;

return hr;
}

/**************************************************************************

CShellFolder::SetNameOf()

**************************************************************************/

STDMETHODIMP CShellFolder::SetNameOf( HWND hwndOwner,
LPCITEMIDLIST pidl,
LPCOLESTR lpName,
DWORD dw,
LPITEMIDLIST *pPidlOut)
{
return E_NOTIMPL;
}

/**************************************************************************

CShellFolder::GetFolderPath()

**************************************************************************/

BOOL CShellFolder::GetFolderPath(LPTSTR lpszOut, DWORD dwOutSize)
{
TCHAR szTemp[MAX_PATH] = TEXT("");
DWORD dwSize;

if(!lpszOut)
return FALSE;

*lpszOut = 0;

//get the text for the root key
GetRootKeyText(m_hKeyRoot, szTemp, sizeof(szTemp));

dwSize = lstrlen(szTemp);
if(m_lpszSubKey)
dwSize += lstrlen(m_lpszSubKey) + 1;

//is the output buffer big enough?
if(dwSize > dwOutSize)
return FALSE;

//add the text we already have
lstrcpy(lpszOut, szTemp);

//add this folder's text if present
if(m_lpszSubKey && *m_lpszSubKey)
{
lstrcat(lpszOut, TEXT("\\"));
lstrcat(lpszOut, m_lpszSubKey);
}

return TRUE;
}

/**************************************************************************

CShellFolder::GetFolderText()

**************************************************************************/

BOOL CShellFolder::GetFolderText(LPTSTR lpszOut, DWORD dwOutSize)
{
if(!lpszOut)
return FALSE;

*lpszOut = 0;

//add this folder's text if present
if(m_lpszSubKey && *m_lpszSubKey)
{
lstrcpyn(lpszOut, m_lpszSubKey, dwOutSize);
}

return TRUE;
}

/**************************************************************************

CShellFolder::GetRootKey()

**************************************************************************/

HKEY CShellFolder::GetRootKey(void)
{
HKEY hkReturn = NULL;
CShellFolder *pTemp = this;

while(pTemp)
{
if(pTemp->m_hKeyRoot)
{
hkReturn = pTemp->m_hKeyRoot;
break;
}

pTemp = pTemp->m_pSFParent;
}

return hkReturn;
}