WindowsXP-SP1/shell/shell32/duiinfo.cpp

1069 lines
37 KiB
C++

#include "shellprv.h"
#include "duiinfo.h"
#include "ids.h"
#include "datautil.h"
DWORD FormatMessageArg(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageID, DWORD dwLangID,
LPWSTR pwzBuffer, DWORD cchSize, ...)
{
va_list vaParamList;
va_start(vaParamList, cchSize);
DWORD dwResult = FormatMessageW(dwFlags, lpSource, dwMessageID, dwLangID, pwzBuffer,
cchSize, &vaParamList);
va_end(vaParamList);
return dwResult;
}
CNameSpaceItemUIProperty::~CNameSpaceItemUIProperty()
{
}
void CNameSpaceItemUIProperty::_SetParentAndItem(IShellFolder2 *psf, LPCITEMIDLIST pidl)
{
// set aliases to current values these are not refed
// it is assumed that all helpers that use these variables won't be called
// unless these have been set. since this can't fail this all works out fine
m_psf = psf; // alias, not refed
m_pidl = pidl; // alias, not cloned
}
STDMETHODIMP CNameSpaceItemUIProperty::GetPropertyDisplayName(SHCOLUMNID scid, WCHAR* pwszPropDisplayName, int cchPropDisplayName)
{
*pwszPropDisplayName = 0;
CComPtr<IPropertyUI> spPropertyUI;
HRESULT hr = _GetPropertyUI(&spPropertyUI);
if (SUCCEEDED(hr))
{
hr = spPropertyUI->GetDisplayName(
scid.fmtid,
scid.pid,
PUIFNF_DEFAULT,
pwszPropDisplayName,
cchPropDisplayName);
}
return hr;
}
STDMETHODIMP CNameSpaceItemUIProperty::GetPropertyDisplayValue(SHCOLUMNID scid, WCHAR* pszValue, int cch, PROPERTYUI_FORMAT_FLAGS flagsFormat)
{
*pszValue = 0;
HRESULT hr = E_FAIL;
// Use GetDisplayNameOf for the SCID_NAME property
if (IsEqualSCID(scid, SCID_NAME))
{
hr = DisplayNameOf(m_psf, m_pidl, SHGDN_INFOLDER, pszValue, cch);
}
else
{ // Use GetDetailsEx to get the value
CComVariant varPropDisplayValue;
if (m_psf->GetDetailsEx(m_pidl, &scid, &varPropDisplayValue) == S_OK) // S_FALSE means property wasn't there.
{
if (IsEqualSCID(scid, SCID_SIZE) &&
((varPropDisplayValue.vt == VT_UI8) && (varPropDisplayValue.ullVal <= 0)))
{
hr = E_FAIL; // Don't display 0 byte sizes
}
else
{
CComPtr<IPropertyUI> spPropertyUI;
hr = _GetPropertyUI(&spPropertyUI);
if (SUCCEEDED(hr))
{
hr = spPropertyUI->FormatForDisplay(scid.fmtid, scid.pid,
(PROPVARIANT*)&varPropDisplayValue, //cast from VARIANT to PROPVARIANT should be ok
flagsFormat, pszValue, cch);
}
}
}
}
return hr;
}
STDMETHODIMP CNameSpaceItemUIProperty::GetInfoString(SHCOLUMNID scid, WCHAR* pwszInfoString, int cchInfoString)
{
HRESULT hr = E_FAIL;
// No DisplayName for the following properties
if (IsEqualSCID(scid, SCID_NAME) ||
IsEqualSCID(scid, SCID_TYPE) ||
IsEqualSCID(scid, SCID_Comment))
{
hr = GetPropertyDisplayValue(scid, pwszInfoString, cchInfoString, PUIFFDF_DEFAULT);
}
else
{ // The other properties are in the format PropertyName: Value
// Get the display name
WCHAR wszPropertyDisplayName[50];
hr = GetPropertyDisplayName(scid, wszPropertyDisplayName, ARRAYSIZE(wszPropertyDisplayName));
if (SUCCEEDED(hr))
{
// Get the display value
PROPERTYUI_FORMAT_FLAGS flagsFormat = IsEqualSCID(scid, SCID_WRITETIME) ? PUIFFDF_FRIENDLYDATE : PUIFFDF_DEFAULT;
WCHAR wszPropertyDisplayValue[INTERNET_MAX_URL_LENGTH];
hr = GetPropertyDisplayValue(scid, wszPropertyDisplayValue, ARRAYSIZE(wszPropertyDisplayValue), flagsFormat);
// If the property display name or the property value is empty then we fail, so
// this property will not be displayed.
if (SUCCEEDED(hr))
{
hr = (wszPropertyDisplayName[0] && wszPropertyDisplayValue[0]) ? S_OK : E_FAIL;
}
// Now, combine the display name and value, seperated by a colon
if (SUCCEEDED(hr))
{
// ShellConstructMessageString here to form the string
WCHAR wszFormatStr[50];
LoadStringW(HINST_THISDLL, IDS_COLONSEPERATED, wszFormatStr, ARRAYSIZE(wszFormatStr));
if (FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, wszFormatStr, 0, 0,
pwszInfoString, cchInfoString, wszPropertyDisplayName,
wszPropertyDisplayValue))
{
hr = S_OK;
}
else
{
hr = E_FAIL;
}
}
}
}
return hr;
}
HRESULT CNameSpaceItemUIProperty::_GetPropertyUI(IPropertyUI **pppui)
{
HRESULT hr = E_FAIL;
if (!m_spPropertyUI)
{
hr = SHCoCreateInstance(NULL, &CLSID_PropertiesUI, NULL, IID_PPV_ARG(IPropertyUI, &m_spPropertyUI));
}
*pppui = m_spPropertyUI;
if (*pppui)
{
(*pppui)->AddRef();
hr = S_OK;
}
return hr;
}
CNameSpaceItemInfoList::~CNameSpaceItemInfoList()
{
if (m_pDUIView)
{
m_pDUIView->SetDetailsInfoMsgWindowPtr(NULL, this);
m_pDUIView->Release();
}
}
STDMETHODIMP CNameSpaceItemInfoList::Create(CDUIView* pDUIView, Value* pvDetailsSheet,
IShellItemArray *psiItemArray, Element** ppElement)
{
HRESULT hr;
*ppElement = NULL;
CNameSpaceItemInfoList* pNSIInfoList = HNewAndZero<CNameSpaceItemInfoList>();
if (!pNSIInfoList)
{
hr = E_OUTOFMEMORY;
}
else
{
hr = pNSIInfoList->Initialize(pDUIView, pvDetailsSheet, psiItemArray);
if (SUCCEEDED(hr))
*ppElement = pNSIInfoList;
else
pNSIInfoList->Destroy();
}
return hr;
}
STDMETHODIMP CNameSpaceItemInfoList::Initialize(CDUIView* pDUIView, Value* pvDetailsSheet,
IShellItemArray *psiItemArray)
{
HRESULT hr = Element::Initialize(0);
if (SUCCEEDED(hr))
{
IDataObject *pdtobj = NULL;
(m_pDUIView = pDUIView)->AddRef();
Value* pvLayout = NULL;
int arVLOptions[] = { FALSE, ALIGN_LEFT, ALIGN_JUSTIFY, ALIGN_TOP };
hr = VerticalFlowLayout::Create(ARRAYSIZE(arVLOptions), arVLOptions, &pvLayout);
if (SUCCEEDED(hr))
{
SetValue(LayoutProp, PI_Local, pvLayout);
pvLayout->Release();
}
if (pvDetailsSheet)
{
SetValue(SheetProp, PI_Local, pvDetailsSheet);
}
// the HIDA format has 2 forms, one is each item in the array is a
// fully qualified pidl. this is what the search folder produces
// the other is the items are relative to a sigle folder pidl
// the code below deals with both cases
// Should just use use the ShellItemArray instead of getting the HIDA
if (psiItemArray)
{
if (FAILED(psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdtobj))))
{
pdtobj = NULL;
}
}
hr = S_OK;
BOOL bDetailsAvailable = FALSE;
if (pdtobj)
{
STGMEDIUM medium;
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
if (pida)
{
IShellFolder2 *psfRoot;
LPCITEMIDLIST pidlFolder = HIDA_GetPIDLFolder(pida);
hr = SHBindToObjectEx(NULL, pidlFolder, NULL, IID_PPV_ARG(IShellFolder2, &psfRoot));
if (SUCCEEDED(hr))
{
if (pida->cidl == 1)
{
LPCITEMIDLIST pidlItem = IDA_GetIDListPtr(pida, 0);
IShellFolder2 *psf;
LPCITEMIDLIST pidl;
hr = SHBindToFolderIDListParent(psfRoot, pidlItem, IID_PPV_ARG(IShellFolder2, &psf), &pidl);
if (SUCCEEDED(hr))
{
if (!SHGetAttributes(psf, pidl, SFGAO_ISSLOW | SFGAO_FOLDER) && m_pDUIView->ShouldShowMiniPreview())
{
_AddMiniPreviewerToList(psf, pidl);
bDetailsAvailable = TRUE;
}
LPITEMIDLIST pidlFull;
if (SUCCEEDED(SHILCombine(pidlFolder, pidlItem, &pidlFull)))
{
if (SUCCEEDED(m_pDUIView->InitializeDetailsInfo(
CNameSpaceItemInfoList::WindowProc)))
{
m_pDUIView->SetDetailsInfoMsgWindowPtr(this, NULL);
m_pDUIView->StartInfoExtraction(pidlFull);
bDetailsAvailable = TRUE;
}
ILFree(pidlFull);
}
psf->Release();
}
}
else
{
hr = _OnMultiSelect(psfRoot, pida);
bDetailsAvailable = SUCCEEDED(hr);
}
psfRoot->Release();
}
HIDA_ReleaseStgMedium(pida, &medium);
}
pdtobj->Release();
}
if (!pdtobj || !bDetailsAvailable)
{
pDUIView->ShowDetails(FALSE);
}
}
return hr;
}
LRESULT CALLBACK CNameSpaceItemInfoList::WindowProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
CNameSpaceItemInfoList* pNSIInfoList = (CNameSpaceItemInfoList*)::GetWindowPtr(hwnd, 0);
switch(uMsg)
{
case WM_DESTROY:
// ignore late messages
{
MSG msg;
while (PeekMessage(&msg, hwnd, WM_DETAILS_INFO, WM_DETAILS_INFO, PM_REMOVE))
{
if (msg.lParam)
{
CDetailsInfoList* pDetailsInfoList = (CDetailsInfoList*)msg.lParam;
// The destructor will do the necessary cleanup
delete pDetailsInfoList;
}
}
SetWindowPtr(hwnd, 0, NULL);
}
break;
case WM_DETAILS_INFO:
{
// Check that pDetailsInfo is still alive and that you have a CDetailsInfoList object of the requested pidl
CDetailsInfoList* pDetailsInfoList = (CDetailsInfoList*)lParam;
if (pDetailsInfoList && pNSIInfoList
&& (wParam == pNSIInfoList->m_pDUIView->_dwDetailsInfoID))
{
BOOL fShow = FALSE;
StartDefer();
Element * peDetailsInfoArea = pNSIInfoList->GetParent();
if (peDetailsInfoArea)
{
peDetailsInfoArea->RemoveLocalValue(HeightProp);
}
for (int i = 0; i < pDetailsInfoList->_nProperties; i++)
{
if (!pDetailsInfoList->_diProperty[i].bstrValue)
{
continue;
}
// 253647 - surpress the comment field from showing in the
// Details section. Note, I left the support for Comments
// in the code below because this decision might be reversed.
if (IsEqualSCID(pDetailsInfoList->_diProperty[i].scid, SCID_Comment))
{
continue;
}
WCHAR wszInfoString[INTERNET_MAX_URL_LENGTH];
wszInfoString[0] = L'\0';
SHCOLUMNID scid = pDetailsInfoList->_diProperty[i].scid;
// No DisplayName if we don't have one
// or if it is one of the following properties
if ((!pDetailsInfoList->_diProperty[i].bstrDisplayName)
|| ( IsEqualSCID(scid, SCID_NAME)
|| IsEqualSCID(scid, SCID_TYPE)
|| IsEqualSCID(scid, SCID_Comment) ))
{
StrCpyNW(wszInfoString, pDetailsInfoList->_diProperty[i].bstrValue, ARRAYSIZE(wszInfoString));
}
else
{
// Now, combine the display name and value, seperated by a colon
// ShellConstructMessageString here to form the string
WCHAR wszFormatStr[50];
LoadStringW(HINST_THISDLL, IDS_COLONSEPERATED, wszFormatStr, ARRAYSIZE(wszFormatStr));
FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, wszFormatStr, 0, 0,
wszInfoString, ARRAYSIZE(wszInfoString),
pDetailsInfoList->_diProperty[i].bstrDisplayName,
pDetailsInfoList->_diProperty[i].bstrValue);
}
if (wszInfoString[0])
{
Element* pElement;
HRESULT hr = CNameSpaceItemInfo::Create(wszInfoString, &pElement);
if (SUCCEEDED(hr))
{
hr = pNSIInfoList->Add(pElement);
if (IsEqualSCID(scid, SCID_NAME))
{
pElement->SetID(L"InfoName");
}
else if (IsEqualSCID(scid, SCID_TYPE))
{
pElement->SetID(L"InfoType");
}
else if (IsEqualSCID(scid, SCID_Comment))
{
pElement->SetID(L"InfoTip");
}
fShow = TRUE;
}
}
}
pNSIInfoList->m_pDUIView->ShowDetails(fShow);
EndDefer();
}
if (pDetailsInfoList)
{
delete pDetailsInfoList; // The destructor will do the necessary cleanup
}
break;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return (LRESULT)0;
}
HRESULT CNameSpaceItemInfoList::_AddMiniPreviewerToList(IShellFolder2 *psf, LPCITEMIDLIST pidl)
{
Element* pElement;
HRESULT hr = CMiniPreviewer::Create(m_pDUIView, psf, pidl, &pElement);
if (SUCCEEDED(hr))
{
hr = Add(pElement);
}
return E_NOTIMPL;
}
#define MAX_FILES_FOR_COMPUTING_SIZE 100
HRESULT CNameSpaceItemInfoList::_OnMultiSelect(IShellFolder2 *psfRoot, LPIDA pida)
{
WCHAR wszText[INTERNET_MAX_URL_LENGTH];
// Get the format string for n selection text
WCHAR wszFormatStr[128];
LoadStringW(HINST_THISDLL, IDS_NSELECTED, wszFormatStr, ARRAYSIZE(wszFormatStr));
// Now, form the n selection text
wnsprintfW(wszText, ARRAYSIZE(wszText), wszFormatStr, pida->cidl);
WCHAR wszTemp[MAX_PATH];
wszTemp[0] = 0;
CComPtr<IPropertyUI> spPropertyUI;
HRESULT hr = _GetPropertyUI(&spPropertyUI);
if (SUCCEEDED(hr))
{
ULONGLONG ullSizeTotal = 0;
if (pida->cidl <= MAX_FILES_FOR_COMPUTING_SIZE)
{
// Compute the total size and the names of the selected files
for (UINT i = 0; i < pida->cidl; i++)
{
IShellFolder2 *psf;
LPCITEMIDLIST pidl;
hr = SHBindToFolderIDListParent(psfRoot, IDA_GetIDListPtr(pida, i), IID_PPV_ARG(IShellFolder2, &psf), &pidl);
if (SUCCEEDED(hr))
{
ULONGLONG ullSize;
if (SUCCEEDED(GetLongProperty(psf, pidl, &SCID_SIZE, &ullSize)))
{
ullSizeTotal += ullSize;
}
psf->Release();
}
}
}
// Get the display string for Total Size
if (ullSizeTotal > 0)
{
// Convert ullSizeTotal to a string
PROPVARIANT propvar;
propvar.vt = VT_UI8;
propvar.uhVal.QuadPart = ullSizeTotal;
WCHAR wszFormattedTotalSize[128];
if (SUCCEEDED(spPropertyUI->FormatForDisplay(SCID_SIZE.fmtid, SCID_SIZE.pid,
&propvar, PUIFFDF_DEFAULT, wszFormattedTotalSize,
ARRAYSIZE(wszFormattedTotalSize))))
{
// Get the format string for Total File Size text
LoadStringW(HINST_THISDLL, IDS_TOTALFILESIZE, wszFormatStr, ARRAYSIZE(wszFormatStr));
// Now, form the Total File Size text
wnsprintfW(wszTemp, ARRAYSIZE(wszTemp), wszFormatStr, wszFormattedTotalSize);
}
}
}
if (wszTemp[0])
{
// Append two line breaks
StrCatBuffW(wszText, L"\n\n", ARRAYSIZE(wszText));
// Append the Total Size string
StrCatBuffW(wszText, wszTemp, ARRAYSIZE(wszText));
}
// Now make a dui gadget for wszText
Element* pElement;
if (SUCCEEDED(CNameSpaceItemInfo::Create(wszText, &pElement)))
{
Add(pElement);
}
return S_OK;
}
IClassInfo* CNameSpaceItemInfoList::Class = NULL;
HRESULT CNameSpaceItemInfoList::Register()
{
return ClassInfo<CNameSpaceItemInfoList,Element>::Register(L"NameSpaceItemInfoList", NULL, 0);
}
STDMETHODIMP CNameSpaceItemInfo::Create(WCHAR* pwszInfoString, Element** ppElement)
{
*ppElement = NULL;
HRESULT hr = E_FAIL;
CNameSpaceItemInfo* pNSIInfo = HNewAndZero<CNameSpaceItemInfo>();
if (!pNSIInfo)
{
hr = E_OUTOFMEMORY;
}
else
{
hr = pNSIInfo->Initialize(pwszInfoString);
if (SUCCEEDED(hr))
*ppElement = pNSIInfo;
else
pNSIInfo->Destroy();
}
return hr;
}
STDMETHODIMP CNameSpaceItemInfo::Initialize(WCHAR* pwszInfoString)
{
HRESULT hr = Element::Initialize(0);
if (SUCCEEDED(hr))
{
hr = SetContentString(pwszInfoString);
}
return hr;
}
IClassInfo* CNameSpaceItemInfo::Class = NULL;
HRESULT CNameSpaceItemInfo::Register()
{
return ClassInfo<CNameSpaceItemInfo,Element>::Register(L"NameSpaceItemInfo", NULL, 0);
}
STDMETHODIMP CBitmapElement::Create(HBITMAP hBitmap, Element** ppElement)
{
*ppElement = NULL;
HRESULT hr;
CBitmapElement* pBitmapElement = HNewAndZero<CBitmapElement>();
if (!pBitmapElement)
{
hr = E_OUTOFMEMORY;
}
else
{
hr = pBitmapElement->Initialize(hBitmap);
if (SUCCEEDED(hr))
*ppElement = pBitmapElement;
else
pBitmapElement->Destroy();
}
return hr;
}
STDMETHODIMP CBitmapElement::Initialize(HBITMAP hBitmap)
{
HRESULT hr = Element::Initialize(0);
if (SUCCEEDED(hr))
{
if (hBitmap)
{
Value* pGraphic = Value::CreateGraphic(hBitmap);
if (pGraphic)
{
SetValue(ContentProp, PI_Local, pGraphic);
pGraphic->Release();
}
}
}
return hr;
}
IClassInfo* CBitmapElement::Class = NULL;
HRESULT CBitmapElement::Register()
{
return ClassInfo<CBitmapElement,Element>::Register(L"BitmapElement", NULL, 0);
}
CMiniPreviewer::~CMiniPreviewer()
{
// We are going away
if (m_pDUIView)
{
m_pDUIView->SetThumbnailMsgWindowPtr(NULL, this);
m_pDUIView->Release();
}
}
STDMETHODIMP CMiniPreviewer::Create(CDUIView* pDUIView, IShellFolder2* psf, LPCITEMIDLIST pidl, Element** ppElement)
{
HRESULT hr;
*ppElement = NULL;
CMiniPreviewer* pMiniPreviewer = HNewAndZero<CMiniPreviewer>();
if (!pMiniPreviewer)
{
hr = E_OUTOFMEMORY;
}
else
{
hr = pMiniPreviewer->Initialize(pDUIView, psf, pidl);
if (SUCCEEDED(hr))
*ppElement = pMiniPreviewer;
else
pMiniPreviewer->Destroy();
}
return hr;
}
STDMETHODIMP CMiniPreviewer::Initialize(CDUIView* pDUIView, IShellFolder2 *psf, LPCITEMIDLIST pidl)
{
HRESULT hr = Element::Initialize(0);
if (SUCCEEDED(hr))
{
(m_pDUIView = pDUIView)->AddRef();
LPITEMIDLIST pidlFull;
if (SUCCEEDED(SHFullIDListFromFolderAndItem(psf, pidl, &pidlFull)))
{
if (SUCCEEDED(m_pDUIView->InitializeThumbnail(CMiniPreviewer::WindowProc)))
{
m_pDUIView->SetThumbnailMsgWindowPtr(this, NULL);
m_pDUIView->StartBitmapExtraction(pidlFull);
}
ILFree(pidlFull);
}
}
return hr;
}
// Window procedure for catching the "image-extraction-done" message
// from m_pDUIView->_spThumbnailExtractor2
LRESULT CALLBACK CMiniPreviewer::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CMiniPreviewer* pMiniPreviewer = (CMiniPreviewer*)::GetWindowPtr(hwnd, 0);
switch(uMsg)
{
case WM_DESTROY:
// ignore late messages
{
MSG msg;
while (PeekMessage(&msg, hwnd, WM_HTML_BITMAP, WM_HTML_BITMAP, PM_REMOVE))
{
if (msg.lParam)
{
DeleteObject((HBITMAP)msg.lParam);
}
}
SetWindowPtr(hwnd, 0, NULL);
}
break;
case WM_HTML_BITMAP:
// Check that pMiniPreviewer is still alive and that you have an HBITMAP of the requested pidl
if (pMiniPreviewer && (wParam == pMiniPreviewer->m_pDUIView->_dwThumbnailID))
{
if (lParam) // This is the HBITMAP of the extracted image
{
Element* pElement;
HRESULT hr = CBitmapElement::Create((HBITMAP)lParam, &pElement);
if (SUCCEEDED(hr))
{
// The addition of the thumbnail comes in late. DUI is
// not currently set up to handle a DisableAnimations()/
// EnableAnimations() here, which we were originally
// doing to prevent jumpiness. This was discovered in
// RAID 389343, because our coming off the background
// thread and calling DisableAnimations() was screwing up
// other animations that were already underway. Talking
// with markfi, the problem is understood BUT not one to
// be fixed because of the negative perf impact it would
// have on DUI. So instead we'll StartDefer()/EndDefer()
// to minimize jumpiness from our two layout ops below.
StartDefer();
// Set the VerticalFlowLayout for our element. Otherwise,
// our control will not render.
Value* pvLayout = NULL;
hr = FillLayout::Create(0, NULL, &pvLayout);
if (SUCCEEDED(hr))
{
hr = pMiniPreviewer->SetValue(LayoutProp, PI_Local, pvLayout);
if (SUCCEEDED(hr))
{
hr = pMiniPreviewer->Add(pElement);
}
pvLayout->Release();
}
if (FAILED(hr))
{
pElement->Destroy();
}
EndDefer();
}
else
{
DeleteObject((HBITMAP)lParam);
}
}
}
else if (lParam) // This extraction got done too late.
// So, just delete the wasted HBITMAP.
{
DeleteObject((HBITMAP)lParam);
}
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return (LRESULT)0;
}
IClassInfo* CMiniPreviewer::Class = NULL;
HRESULT CMiniPreviewer::Register()
{
return ClassInfo<CMiniPreviewer,Element>::Register(L"MiniPreviewer", NULL, 0);
}
// ***** CDetailsInfoList *******
CDetailsInfoList::CDetailsInfoList() : _nProperties(0)
{
}
CDetailsInfoList::~CDetailsInfoList()
{
for (int i = 0; i < _nProperties; i++)
{
if (_diProperty[i].bstrValue)
{
SysFreeString(_diProperty[i].bstrValue);
}
if (_diProperty[i].bstrDisplayName)
{
SysFreeString(_diProperty[i].bstrDisplayName);
}
}
}
// ***** CDetailsSectionInfoTask *******
CDetailsSectionInfoTask::CDetailsSectionInfoTask(HRESULT *phr,
IShellFolder *psfContaining,
LPCITEMIDLIST pidlAbsolute,
HWND hwndMsg,
UINT uMsg,
DWORD dwDetailsInfoID)
: CRunnableTask(RTF_DEFAULT),
_hwndMsg(hwndMsg),
_uMsg(uMsg),
_dwDetailsInfoID(dwDetailsInfoID)
{
ASSERT(psfContaining && pidlAbsolute && hwndMsg);
_psfContaining = psfContaining;
_psfContaining->AddRef();
*phr = SHILClone(pidlAbsolute, &_pidlAbsolute);
}
CDetailsSectionInfoTask::~CDetailsSectionInfoTask()
{
_psfContaining->Release();
ILFree(_pidlAbsolute);
}
HRESULT CDetailsSectionInfoTask_CreateInstance(IShellFolder *psfContaining,
LPCITEMIDLIST pidlAbsolute,
HWND hwndMsg,
UINT uMsg,
DWORD dwDetailsInfoID,
CDetailsSectionInfoTask **ppTask)
{
*ppTask = NULL;
HRESULT hr;
CDetailsSectionInfoTask* pNewTask = new CDetailsSectionInfoTask(
&hr,
psfContaining,
pidlAbsolute,
hwndMsg,
uMsg,
dwDetailsInfoID);
if (pNewTask)
{
if (SUCCEEDED(hr))
*ppTask = pNewTask;
else
pNewTask->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}
STDMETHODIMP CDetailsSectionInfoTask::RunInitRT()
{
ASSERT(_pidlAbsolute);
BOOL bMsgPosted = FALSE;
HRESULT hr = E_FAIL;
CDetailsInfoList* pCDetailsInfoList = new CDetailsInfoList;
if (!pCDetailsInfoList)
{
hr = E_OUTOFMEMORY;
}
else
{
CComPtr<IShellFolder2> psf2;
LPCITEMIDLIST pidlLast;
hr = SHBindToIDListParent(_pidlAbsolute, IID_PPV_ARG(IShellFolder2, &psf2), &pidlLast);
if (SUCCEEDED(hr))
{
_SetParentAndItem(psf2, pidlLast);
WCHAR wszProperties[MAX_PATH];
hr = _GetDisplayedDetailsProperties(psf2, pidlLast, wszProperties, ARRAYSIZE(wszProperties));
if (SUCCEEDED(hr))
{
// pwszProperties is usually of the form "prop:Name;Type;Author"
CComPtr<IPropertyUI> spPropertyUI;
hr = _GetPropertyUI(&spPropertyUI);
if (SUCCEEDED(hr))
{
SHCOLUMNID scid;
WCHAR wszInfoString[INTERNET_MAX_URL_LENGTH];
ULONG chEaten = 0; // loop var, incremented by ParsePropertyName
for (pCDetailsInfoList->_nProperties = 0;
pCDetailsInfoList->_nProperties < ARRAYSIZE(pCDetailsInfoList->_diProperty)
&& SUCCEEDED(spPropertyUI->ParsePropertyName(wszProperties, &scid.fmtid, &scid.pid, &chEaten));
pCDetailsInfoList->_nProperties++)
{
pCDetailsInfoList->_diProperty[pCDetailsInfoList->_nProperties].scid = scid;
PROPERTYUI_FORMAT_FLAGS flagsFormat = IsEqualSCID(scid, SCID_WRITETIME) ? PUIFFDF_FRIENDLYDATE : PUIFFDF_DEFAULT;
// Get the display value
hr = GetPropertyDisplayValue(scid, wszInfoString, ARRAYSIZE(wszInfoString), flagsFormat);
if (SUCCEEDED(hr) && wszInfoString[0])
{
pCDetailsInfoList->_diProperty[pCDetailsInfoList->_nProperties].bstrValue = SysAllocString(wszInfoString);
}
// Get the display name
hr = GetPropertyDisplayName(scid, wszInfoString, ARRAYSIZE(wszInfoString));
if (SUCCEEDED(hr) && wszInfoString[0])
{
pCDetailsInfoList->_diProperty[pCDetailsInfoList->_nProperties].bstrDisplayName = SysAllocString(wszInfoString);
}
}
//The extraction is done. Now post a message.
if (PostMessage(_hwndMsg, WM_DETAILS_INFO,
(WPARAM)_dwDetailsInfoID, (LPARAM)pCDetailsInfoList))
{
bMsgPosted = TRUE;
}
}
}
}
}
if (!bMsgPosted && pCDetailsInfoList)
{
delete pCDetailsInfoList;
}
return S_OK;
}
HRESULT CDetailsSectionInfoTask::_GetDisplayedDetailsProperties(IShellFolder2* psf,
LPCITEMIDLIST pidl,
WCHAR* pwszProperties,
int cchProperties)
{
HRESULT hr = GetStringProperty(psf, pidl, &SCID_DetailsProperties, pwszProperties, cchProperties);
if (FAILED(hr)) // Default properties
{
if (SHGetAttributes(psf, pidl, SFGAO_ISSLOW))
{
// SCID_NAME;SCID_TYPE
StrCpyNW(pwszProperties, L"prop:Name;Type", cchProperties);
}
else
{
// SCID_NAME;SCID_TYPE;SCID_ATTRIBUTES_DESCRIPTION;SCID_Comment;SCID_WRITETIME;SCID_SIZE;SCID_Author;SCID_CSC_STATUS
StrCpyNW(pwszProperties, L"prop:Name;Type;AttributesDescription;DocComments;Write;Size;DocAuthor;CSCStatus", cchProperties);
}
}
// Augment properties to include "Location" if in CLSID_DocFindFolder.
IPersist *pPersist;
ASSERT(_psfContaining);
if (SUCCEEDED(_psfContaining->QueryInterface(IID_IPersist, (void**)&pPersist)))
{
CLSID clsid;
if (SUCCEEDED(pPersist->GetClassID(&clsid)) && IsEqualCLSID(clsid, CLSID_DocFindFolder))
_AugmentDisplayedDetailsProperties(pwszProperties, cchProperties);
pPersist->Release();
}
return S_OK;
}
void CDetailsSectionInfoTask::_AugmentDisplayedDetailsProperties(LPWSTR pszDetailsProperties, size_t cchDetailsProperties)
{
static WCHAR szDeclarator[] = L"prop:";
static size_t lenDeclarator = lstrlen(szDeclarator);
static WCHAR szName[64] = { 0 };
static size_t lenName = 0;
static WCHAR szType[64] = { 0 };
static size_t lenType = 0;
static WCHAR szDirectory[64] = { 0 };
static size_t lenDirectory = 0;
// Initialize statics once 'n only once.
if (!szName[0] || !szType[0] || !szDirectory[0])
{
HRESULT hr;
hr = SCIDCannonicalName((SHCOLUMNID *)&SCID_NAME, szName, ARRAYSIZE(szName));
ASSERT(SUCCEEDED(hr));
lenName = lstrlen(szName);
hr = SCIDCannonicalName((SHCOLUMNID *)&SCID_TYPE, szType, ARRAYSIZE(szType));
ASSERT(SUCCEEDED(hr));
lenType = lstrlen(szType);
hr = SCIDCannonicalName((SHCOLUMNID *)&SCID_DIRECTORY, szDirectory, ARRAYSIZE(szDirectory));
ASSERT(SUCCEEDED(hr));
lenDirectory = lstrlen(szDirectory);
}
// Attempt to merge the "Directory" property, in the following ways:
// "prop:Name;Type;Directory;..."
// "prop:Name;Directory;..."
// "prop:Directory;..."
//
size_t lenDetailsProperties = lstrlen(pszDetailsProperties);
size_t lenMerged = lenDetailsProperties + 1 + lenDirectory;
if (lenMerged < cchDetailsProperties && 0 == StrCmpNI(pszDetailsProperties, szDeclarator, lenDeclarator))
{
// Search for "Directory" property (in case it is already specified).
if (!_SearchDisplayedDetailsProperties(pszDetailsProperties, lenDetailsProperties, szDirectory, lenDirectory))
{
// Allocate a temporary buffer to merge into.
size_t cchMerged = cchDetailsProperties;
LPWSTR pszMerged = new WCHAR[cchMerged];
if (pszMerged)
{
// Determine offset in pszDetailsProperties to merge at.
size_t offsetInsert;
if (lenDeclarator < lenDetailsProperties)
{
// Search for "Name" property.
LPWSTR pszName = _SearchDisplayedDetailsProperties(
&pszDetailsProperties[lenDeclarator],
lenDetailsProperties - lenDeclarator,
szName,
lenName);
if (pszName)
{
// Search for "Type" property (immediately following "Name").
size_t offsetName = (pszName - pszDetailsProperties);
size_t offsetType = offsetName + lenName + 1;
size_t offsetRemainder = offsetType + lenType;
if ((offsetRemainder == lenDetailsProperties || (offsetRemainder < lenDetailsProperties && pszDetailsProperties[offsetRemainder] == ';')) &&
!StrCmpNI(&pszDetailsProperties[offsetType], szType, lenType))
{
offsetInsert = offsetRemainder;
}
else
offsetInsert = offsetName + lenName;
}
else
offsetInsert = lenDeclarator;
}
else
offsetInsert = lenDeclarator;
// Merge the "Directory" property.
StrCpyN(pszMerged, pszDetailsProperties, offsetInsert + 1); // + 1 to account for null terminator.
if (offsetInsert > lenDeclarator)
StrCatBuff(pszMerged, L";", cchMerged); // ';' prepend if necessary
StrCatBuff(pszMerged, szDirectory, cchMerged); // "Directory"
if (offsetInsert < lenDetailsProperties)
{
if (pszDetailsProperties[offsetInsert] != ';')
StrCatBuff(pszMerged, L";", cchMerged); // ';' append if necessary
StrCatBuff(pszMerged, &pszDetailsProperties[offsetInsert], cchMerged);
}
// Update in/out pszDetailsProperties.
StrCpyN(pszDetailsProperties, pszMerged, cchDetailsProperties);
ASSERT(lenMerged == lstrlen(pszMerged));
ASSERT(lenMerged < cchDetailsProperties);
delete[] pszMerged;
}
}
}
else
{
// Invalid format.
ASSERT(FALSE);
}
}
LPWSTR CDetailsSectionInfoTask::_SearchDisplayedDetailsProperties(LPWSTR pszDetailsProperties, size_t lenDetailsProperties, LPWSTR pszProperty, size_t lenProperty)
{
LPWSTR psz = StrStrI(pszDetailsProperties, pszProperty);
while (psz)
{
// Check start...
if (psz == pszDetailsProperties || psz[-1] == ';')
{
// ... and end.
size_t lenToEndOfProperty = (psz - pszDetailsProperties) + lenProperty;
if (lenToEndOfProperty == lenDetailsProperties || pszDetailsProperties[lenToEndOfProperty] == ';')
break;
}
psz = StrStrI(psz + lenProperty, pszProperty);
}
return psz;
}