|
|
#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; }
|