|
|
#include "shellprv.h"
#include <brfcasep.h>
#include "filefldr.h"
#include "brfcase.h"
#include "datautil.h"
#include "prop.h"
#include "ids.h"
#include "defview.h" // for WM_DSV_FSNOTIFY
#include "basefvcb.h"
#include "views.h"
#define MAX_NAME 32
#define HACK_IGNORETYPE 0x04000000
// Values for CBriefcase::_FindNextState
#define FNS_UNDETERMINED 1
#define FNS_STALE 2
#define FNS_DELETED 3
typedef struct { TCHAR szOrigin[MAX_PATH]; TCHAR szStatus[MAX_NAME]; BOOL bDetermined:1; BOOL bUpToDate:1; BOOL bDeleted:1; } BRFINFO;
typedef struct { LPITEMIDLIST pidl; // Indexed value
BRFINFO bi; } BRFINFOHDR;
class CBriefcaseViewCB;
class CBriefcase : public CFSFolder { friend CBriefcaseViewCB;
public: CBriefcase(IUnknown *punkOuter); STDMETHODIMP Init(); // initialize the critical section
// IShellFolder
STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); STDMETHODIMP CreateViewObject (HWND hwndOwner, REFIID riid, void **ppv); STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut); STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv);
// IShellFolder2
STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails); STDMETHOD (MapColumnToSCID)(UINT iColumn, SHCOLUMNID *pscid);
private: ~CBriefcase();
static DWORD CALLBACK _CalcDetailsThreadProc(void *pv); DWORD _CalcDetailsThread();
void _EnterCS(); void _LeaveCS(); static int CALLBACK _CompareIDCallBack(void *pv1, void *pv2, LPARAM lParam); BOOL _CreateDetailsThread(); BOOL _InitDetailsInfoAndThread(IBriefcaseStg *pbrfstg, HWND hwndMain, HANDLE hMutexDelay); void _Free(); void _Reset(); BOOL _FindCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi); BOOL _DeleteCachedName(LPCITEMIDLIST pidl); BOOL _FindNextState(UINT uState, BRFINFOHDR *pbihdrOut); void _CalcCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi); void _CachedNameIsStale(LPCITEMIDLIST pidl, BOOL bDeleted); void _AllNamesAreStale(); BOOL _AddCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi); HRESULT _CreateView(HWND hwnd, IShellView **ppsv);
HWND _hwndMain; // evil, view related state
IBriefcaseStg *_pbrfstg; // evil, view related state
// accessed by background thread
HDPA _hdpa; int _idpaStaleCur; int _idpaUndeterminedCur; int _idpaDeletedCur; HANDLE _hSemPending; // Pending semaphore
CRITICAL_SECTION _cs; BOOL _fcsInit; HANDLE _hEventDie; HANDLE _hThreadCalcDetails; HANDLE _hMutexDelay; // alias given out by the _pbrfstg
BOOL _bFreePending; #ifdef DEBUG
UINT _cUndetermined; UINT _cStale; UINT _cDeleted; UINT _cCSRef; #endif
};
class CBriefcaseViewCB : public CBaseShellFolderViewCB { public: CBriefcaseViewCB(CBriefcase *pfolder); HRESULT _InitStgForDetails();
STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
private: ~CBriefcaseViewCB(); LPCITEMIDLIST _FolderPidl() { return _pfolder->_pidl; }
HRESULT OnWINDOWCREATED(DWORD pv, HWND hwndView); HRESULT OnWINDOWDESTROY(DWORD pv, HWND wP); HRESULT OnMergeMenu(DWORD pv, QCMINFO*lP); HRESULT OnINVOKECOMMAND(DWORD pv, UINT wP); HRESULT OnGetHelpOrTooltipText(BOOL bHelp, UINT wPl, UINT cch, LPTSTR psz); HRESULT OnINITMENUPOPUP(DWORD pv, UINT wPl, UINT wPh, HMENU lP); HRESULT OnGETBUTTONINFO(DWORD pv, TBINFO* ptbinfo); HRESULT OnGETBUTTONS(DWORD pv, UINT wPl, UINT wPh, TBBUTTON*lP); HRESULT OnSELCHANGE(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP); HRESULT OnQUERYFSNOTIFY(DWORD pv, SHChangeNotifyEntry*lP); HRESULT OnFSNOTIFY(DWORD pv, LPCITEMIDLIST*wP, LPARAM lP); HRESULT OnQUERYCOPYHOOK(DWORD pv); HRESULT OnNOTIFYCOPYHOOK(DWORD pv, COPYHOOKINFO*lP); HRESULT OnINSERTITEM(DWORD pv, LPCITEMIDLIST wP); HRESULT OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE *lP); HRESULT OnSupportsIdentity(DWORD pv); HRESULT OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA * phtd); HRESULT OnDELAYWINDOWCREATE(DWORD pv, HWND hwnd); HRESULT _GetSelectedObjects(IDataObject **ppdtobj); HRESULT _HandleFSNotifyForDefView(LPARAM lEvent, LPCITEMIDLIST * ppidl, LPTSTR pszBuf); int _GetSelectedCount();
CBriefcase *_pfolder;
IBriefcaseStg *_pbrfstg; LPITEMIDLIST _pidlRoot; // Root of briefcase
HANDLE _hMutexDelay; ULONG _uSCNRExtra; // Extra SHChangeNotifyRegister for our pidl...
TCHAR _szDBName[MAX_PATH];
// Web View implementation
HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData); HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData); HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks); public: static HRESULT _OnUpdate(IUnknown* pv,IShellItemArray *psiItemArray, IBindCtx *pbc); };
CBriefcase::CBriefcase(IUnknown *punkOuter) : CFSFolder(punkOuter) { _clsidBind = CLSID_BriefcaseFolder; // in CFSFolder
_fcsInit = FALSE; }
CBriefcase::~CBriefcase() { if (_fcsInit) { DeleteCriticalSection(&_cs); } }
STDMETHODIMP CBriefcase::Init() { _fcsInit = InitializeCriticalSectionAndSpinCount(&_cs, 0); return _fcsInit ? S_OK : E_FAIL; }
enum { ICOL_BRIEFCASE_NAME = 0, ICOL_BRIEFCASE_ORIGIN, ICOL_BRIEFCASE_STATUS, ICOL_BRIEFCASE_SIZE, ICOL_BRIEFCASE_TYPE, ICOL_BRIEFCASE_MODIFIED, };
const COLUMN_INFO s_briefcase_cols[] = { DEFINE_COL_STR_ENTRY(SCID_NAME, 30, IDS_NAME_COL), DEFINE_COL_STR_ENTRY(SCID_SYNCCOPYIN, 24, IDS_SYNCCOPYIN_COL), DEFINE_COL_STR_ENTRY(SCID_STATUS, 18, IDS_STATUS_COL), DEFINE_COL_SIZE_ENTRY(SCID_SIZE, IDS_SIZE_COL), DEFINE_COL_STR_ENTRY(SCID_TYPE, 18, IDS_TYPE_COL), DEFINE_COL_STR_ENTRY(SCID_WRITETIME, 18, IDS_MODIFIED_COL), };
#ifdef DEBUG
#define _AssertInCS() ASSERT(0 < (this)->_cCSRef)
#define _AssertNotInCS() ASSERT(0 == (this)->_cCSRef)
#else
#define _AssertInCS()
#define _AssertNotInCS()
#endif
void CBriefcase::_EnterCS() { ASSERT(_fcsInit); EnterCriticalSection(&_cs); #ifdef DEBUG
_cCSRef++; #endif
}
void CBriefcase::_LeaveCS() { ASSERT(_fcsInit); _AssertInCS(); #ifdef DEBUG
_cCSRef--; #endif
LeaveCriticalSection(&_cs); }
//---------------------------------------------------------------------------
// Brfview functions: Expensive cache stuff
//---------------------------------------------------------------------------
// Comparison function for the DPA list
int CALLBACK CBriefcase::_CompareIDCallBack(void *pv1, void *pv2, LPARAM lParam) { BRFINFOHDR *pbihdr1 = (BRFINFOHDR *)pv1; BRFINFOHDR *pbihdr2 = (BRFINFOHDR *)pv2; CBriefcase *pfolder = (CBriefcase *)lParam; HRESULT hr = pfolder->CompareIDs(HACK_IGNORETYPE, pbihdr1->pidl, pbihdr2->pidl); ASSERT(SUCCEEDED(hr)); return (short)SCODE_CODE(GetScode(hr)); // (the short cast is important!)
}
// Create the secondary thread for the expensive cache
BOOL CBriefcase::_CreateDetailsThread() { BOOL bRet = FALSE; // The semaphore is used to determine whether anything
// needs to be refreshed in the cache.
_hSemPending = CreateSemaphore(NULL, 0, MAXLONG, NULL); if (_hSemPending) { #ifdef DEBUG
_cStale = 0; _cUndetermined = 0; _cDeleted = 0; #endif
ASSERT(NULL == _hEventDie); _hEventDie = CreateEvent(NULL, FALSE, FALSE, NULL); if (_hEventDie) { // Create the thread that will calculate expensive data
DWORD idThread; _hThreadCalcDetails = CreateThread(NULL, 0, _CalcDetailsThreadProc, this, CREATE_SUSPENDED, &idThread); if (_hThreadCalcDetails) { ResumeThread(_hThreadCalcDetails); bRet = TRUE; } else { CloseHandle(_hEventDie); _hEventDie = NULL; CloseHandle(_hSemPending); _hSemPending = NULL; } } else { CloseHandle(_hSemPending); _hSemPending = NULL; } } return bRet; }
// view callback inits the folder with data it needs to run the GetDetailsOf() stuff
// on a background thread
BOOL CBriefcase::_InitDetailsInfoAndThread(IBriefcaseStg *pbrfstg, HWND hwndMain, HANDLE hMutexDelay) { BOOL bRet = FALSE;
ASSERT(pbrfstg && hwndMain && hMutexDelay); // from the init call
_EnterCS(); { if (_hdpa) { bRet = TRUE; } else { _hwndMain = hwndMain; _hMutexDelay = hMutexDelay; _idpaStaleCur = 0; _idpaUndeterminedCur = 0; _idpaDeletedCur = 0; _hdpa = DPA_Create(8); if (_hdpa) { bRet = _CreateDetailsThread(); if (bRet) { ASSERT(NULL == _pbrfstg); _pbrfstg = pbrfstg; pbrfstg->AddRef(); } else { // Failed
DPA_Destroy(_hdpa); _hdpa = NULL; } } } } _LeaveCS(); return bRet; }
// Clean up the cache of expensive data
void CBriefcase::_Free() { _EnterCS(); { if (_hEventDie) { if (_hThreadCalcDetails) { HANDLE hThread = _hThreadCalcDetails; SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST); // Signal the secondary thread to end
SetEvent(_hEventDie); // Make sure we are not in the critical section when
// we wait for the secondary thread to exit. Without
// this check, hitting F5 twice in a row could deadlock.
_LeaveCS(); { // Wait for the threads to exit
_AssertNotInCS(); WaitForSendMessageThread(hThread, INFINITE); } _EnterCS(); DebugMsg(DM_TRACE, TEXT("Briefcase Secondary thread ended")); CloseHandle(_hThreadCalcDetails); _hThreadCalcDetails = NULL; } CloseHandle(_hEventDie); _hEventDie = NULL; } if (_hdpa) { int idpa = DPA_GetPtrCount(_hdpa); while (--idpa >= 0) { BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa); ILFree(pbihdr->pidl); LocalFree((HLOCAL)pbihdr); } DPA_Destroy(_hdpa); _hdpa = NULL; } if (_hSemPending) { CloseHandle(_hSemPending); _hSemPending = NULL; } if (_pbrfstg) { _pbrfstg->Release(); _pbrfstg = NULL;
_hMutexDelay = NULL; // invalidate our alias
} } _LeaveCS(); }
// Resets the expensive data cache
void CBriefcase::_Reset() { _AssertNotInCS(); _EnterCS(); { IBriefcaseStg *pbrfstg = _pbrfstg; if (!_bFreePending && pbrfstg) { HWND hwndMain = _hwndMain; HANDLE hMutex = _hMutexDelay; pbrfstg->AddRef(); // Since we won't be in the critical section when we
// wait for the paint thread to exit, set this flag to
// avoid nasty re-entrant calls.
_bFreePending = TRUE; // Reset by freeing and reinitializing.
_LeaveCS(); { _Free(); // whacky re-init of ourselevs
_InitDetailsInfoAndThread(pbrfstg, hwndMain, hMutex); } _EnterCS(); _bFreePending = FALSE; pbrfstg->Release(); } } _LeaveCS(); }
// Finds a cached name structure and returns a copy of it in *pbi.
BOOL CBriefcase::_FindCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi) { BOOL bRet = FALSE; _EnterCS(); { if (_hdpa) { BRFINFOHDR bihdr = {0}; bihdr.pidl = (LPITEMIDLIST)pidl; // const -> non const
int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED); if (DPA_ERR != idpa) { // Yes
BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa); ASSERT(pbihdr); *pbi = pbihdr->bi; bRet = TRUE; } } } _LeaveCS(); return bRet; }
// Deletes a cached name structure.
BOOL CBriefcase::_DeleteCachedName(LPCITEMIDLIST pidl) { BOOL bRet = FALSE; _EnterCS(); { if (_hdpa) { BRFINFOHDR bihdr = {0}; bihdr.pidl = (LPITEMIDLIST)pidl; // const -> non const
int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED); if (DPA_ERR != idpa) { #ifdef DEBUG
BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa); ASSERT(pbihdr); _cDeleted--; if (!pbihdr->bi.bDetermined) _cUndetermined--; else if (!pbihdr->bi.bUpToDate) _cStale--; #endif
// Keep index pointers current
if (_idpaStaleCur >= idpa) _idpaStaleCur--; if (_idpaUndeterminedCur >= idpa) _idpaUndeterminedCur--; if (_idpaDeletedCur >= idpa) _idpaDeletedCur--; DPA_DeletePtr(_hdpa, idpa); bRet = TRUE; } } } _LeaveCS(); return bRet; }
// Finds the next cached name structure that matches the requested state.
BOOL CBriefcase::_FindNextState(UINT uState, BRFINFOHDR *pbihdrOut) { BOOL bRet = FALSE; ASSERT(pbihdrOut); _EnterCS(); { if (_hdpa) { HDPA hdpa = _hdpa; int idpaCur; int idpa; BRFINFOHDR *pbihdr; int cdpaMax = DPA_GetPtrCount(hdpa); switch (uState) { case FNS_UNDETERMINED: // Iterate thru the entire list starting at idpa. We roll this
// loop out to be two loops: the first iterates the last portion
// of the list, the second iterates the first portion if the former
// failed to find anything.
idpaCur = _idpaUndeterminedCur + 1; for (idpa = idpaCur; idpa < cdpaMax; idpa++) { pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa); if (!pbihdr->bi.bDetermined) { goto Found; // Found it
} } ASSERT(idpaCur <= cdpaMax); for (idpa = 0; idpa < idpaCur; idpa++) { pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa); if (!pbihdr->bi.bDetermined) { goto Found; // Found it
} } ASSERT(0 == _cUndetermined); break; case FNS_STALE: // Iterate thru the entire list starting at idpa. We roll this
// loop out to be two loops: the first iterates the last portion
// of the list, the second iterates the first portion if the former
// failed to find anything.
idpaCur = _idpaStaleCur + 1; for (idpa = idpaCur; idpa < cdpaMax; idpa++) { pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa); if (!pbihdr->bi.bUpToDate) { goto Found; // Found it
} } ASSERT(idpaCur <= cdpaMax); for (idpa = 0; idpa < idpaCur; idpa++) { pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa); if (!pbihdr->bi.bUpToDate) { goto Found; // Found it
} } ASSERT(0 == _cStale); break; case FNS_DELETED: // Iterate thru the entire list starting at idpa. We roll this
// loop out to be two loops: the first iterates the last portion
// of the list, the second iterates the first portion if the former
// failed to find anything.
idpaCur = _idpaDeletedCur + 1; for (idpa = idpaCur; idpa < cdpaMax; idpa++) { pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa); if (pbihdr->bi.bDeleted) { goto Found; // Found it
} } ASSERT(idpaCur <= cdpaMax); for (idpa = 0; idpa < idpaCur; idpa++) { pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa); if (pbihdr->bi.bDeleted) { goto Found; // Found it
} } ASSERT(0 == _cDeleted); break; default: ASSERT(0); // should never get here
break; } goto Done; Found: ASSERT(0 <= idpa && idpa < cdpaMax); // Found the next item of the requested state
switch (uState) { case FNS_UNDETERMINED: _idpaUndeterminedCur = idpa; break; case FNS_STALE: _idpaStaleCur = idpa; break; case FNS_DELETED: _idpaDeletedCur = idpa; break; } *pbihdrOut = *pbihdr; pbihdrOut->pidl = ILClone(pbihdr->pidl); if (pbihdrOut->pidl) bRet = TRUE; } Done:; } _LeaveCS(); return bRet; } // Recalculates a cached name structure. This can be an expensive operation
void CBriefcase::_CalcCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi) { _EnterCS(); { if (_hdpa && _pbrfstg) { LPCIDFOLDER pidf = (LPCIDFOLDER)pidl; IBriefcaseStg *pbrfstg = _pbrfstg; pbrfstg->AddRef(); // Make sure we're out of the critical section when we call
// the expensive functions!
_LeaveCS(); { TCHAR szTmp[MAX_PATH]; _CopyName(pidf, szTmp, ARRAYSIZE(szTmp)); pbrfstg->GetExtraInfo(szTmp, GEI_ORIGIN, (WPARAM)ARRAYSIZE(pbi->szOrigin), (LPARAM)pbi->szOrigin); pbrfstg->GetExtraInfo(szTmp, GEI_STATUS, (WPARAM)ARRAYSIZE(pbi->szStatus), (LPARAM)pbi->szStatus); } _EnterCS(); pbrfstg->Release(); // Check again if we are valid
if (_hdpa) { // Is the pidl still around so we can update it?
BRFINFOHDR bihdr = {0}; bihdr.pidl = (LPITEMIDLIST)pidf; int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED); if (DPA_ERR != idpa) { // Yes; update it
BRFINFOHDR * pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa); ASSERT(!pbihdr->bi.bUpToDate || !pbihdr->bi.bDetermined) // This entry may have been marked for deletion while the
// expensive calculations were in process above. Check for
// it now.
if (pbihdr->bi.bDeleted) { _DeleteCachedName(pidl); } else { pbihdr->bi = *pbi; pbihdr->bi.bUpToDate = TRUE; pbihdr->bi.bDetermined = TRUE; #ifdef DEBUG
if (!pbi->bDetermined) _cUndetermined--; else if (!pbi->bUpToDate) _cStale--; else ASSERT(0); #endif
} } } } _LeaveCS(); } }
// Finds a cached name structure and marks it stale
// WARNING: pidl can be a fully qualified pidl that is comming through as a change notify
void CBriefcase::_CachedNameIsStale(LPCITEMIDLIST pidl, BOOL bDeleted) { _EnterCS(); { if (_hdpa) { BRFINFOHDR bihdr = {0}; bihdr.pidl = ILFindLastID(pidl); // hope this is all ours
int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED); if (DPA_ERR != idpa) { // Yes; mark it stale
BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa); // Is this cached name pending calculation yet?
if (pbihdr->bi.bDetermined && pbihdr->bi.bUpToDate && !pbihdr->bi.bDeleted) { // No; signal the calculation thread
if (bDeleted) { pbihdr->bi.bDeleted = TRUE; #ifdef DEBUG
_cDeleted++; #endif
} else { pbihdr->bi.bUpToDate = FALSE; #ifdef DEBUG
_cStale++; #endif
} // Notify the calculating thread of an item that is pending
// calculation
ReleaseSemaphore(_hSemPending, 1, NULL); } else if (bDeleted) { // Yes; but mark for deletion anyway
pbihdr->bi.bDeleted = TRUE; #ifdef DEBUG
_cDeleted++; #endif
} } } } _LeaveCS(); } // Marks all cached name structures stale
void CBriefcase::_AllNamesAreStale() { _EnterCS(); { if (_pbrfstg) { UINT uFlags; // Dirty the briefcase storage cache
_pbrfstg->Notify(NULL, NOE_DIRTYALL, &uFlags, NULL); } } _LeaveCS(); // (It is important that we call CBriefcase::_Reset outside of the critical
// section. Otherwise, we can deadlock when this function is called
// while the secondary thread is calculating (hit F5 twice in a row).)
// Clear the entire expensive data cache
_Reset(); }
// Adds a new item with default values to the extra info list
BOOL CBriefcase::_AddCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi) { BOOL bRet = FALSE; ASSERT(_pbrfstg && _hwndMain && _hMutexDelay); _EnterCS(); { if (_hdpa) { BRFINFOHDR * pbihdr = (BRFINFOHDR *)LocalAlloc(LPTR, sizeof(*pbihdr)); if (pbihdr) { pbihdr->pidl = ILClone(pidl); if (pbihdr->pidl) { int idpa = DPA_AppendPtr(_hdpa, pbihdr); if (DPA_ERR != idpa) { pbihdr->bi.bUpToDate = FALSE; pbihdr->bi.bDetermined = FALSE; pbihdr->bi.bDeleted = FALSE; LoadString(HINST_THISDLL, IDS_DETAILSUNKNOWN, pbihdr->bi.szOrigin, ARRAYSIZE(pbihdr->bi.szOrigin)); LoadString(HINST_THISDLL, IDS_DETAILSUNKNOWN, pbihdr->bi.szStatus, ARRAYSIZE(pbihdr->bi.szStatus)); #ifdef DEBUG
_cUndetermined++; #endif
DPA_Sort(_hdpa, _CompareIDCallBack, (LPARAM)this); // Notify the calculating thread of an item that is pending
// calculation
ReleaseSemaphore(_hSemPending, 1, NULL); *pbi = pbihdr->bi; bRet = TRUE; } else { // Failed. Cleanup
ILFree(pbihdr->pidl); LocalFree((HLOCAL)pbihdr); } } else { // Failed. Cleanup
LocalFree((HLOCAL)pbihdr); } } } } _LeaveCS(); return bRet; }
DWORD CBriefcase::_CalcDetailsThread() { HANDLE rghObjPending[2] = {_hEventDie, _hSemPending}; HANDLE rghObjDelay[2] = {_hEventDie, _hMutexDelay}; while (TRUE) { // Wait for an end event or for a job to do
DWORD dwRet = WaitForMultipleObjects(ARRAYSIZE(rghObjPending), rghObjPending, FALSE, INFINITE); if (WAIT_OBJECT_0 == dwRet) { // Exit thread
break; } else { #ifdef DEBUG
_EnterCS(); { ASSERT(0 < _cUndetermined || 0 < _cStale || 0 < _cDeleted); } _LeaveCS(); #endif
// Now wait for an end event or for the delay-calculation mutex
dwRet = WaitForMultipleObjects(ARRAYSIZE(rghObjDelay), rghObjDelay, FALSE, INFINITE); if (WAIT_OBJECT_0 == dwRet) { // Exit thread
break; } else { // Address deleted entries first
BRFINFOHDR bihdr; if (_FindNextState(FNS_DELETED, &bihdr)) { _DeleteCachedName(bihdr.pidl); ILFree(bihdr.pidl); } // Calculate undetermined entries before stale entries
// to fill the view as quickly as possible
else if (_FindNextState(FNS_UNDETERMINED, &bihdr) || _FindNextState(FNS_STALE, &bihdr)) { _CalcCachedName(bihdr.pidl, &bihdr.bi); #if 1
// ugly way
ShellFolderView_RefreshObject(_hwndMain, &bihdr.pidl); #else
// right way, but we don't have _punkSite, here, that is on another thread!
IShellFolderView *psfv; if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IShellFolderView, &psfv)))) { UINT uScratch; psfv->RefreshObject(&bihdr.pidl, &uScratch); psfv->Release(); } #endif
ILFree(bihdr.pidl); } else { ASSERT(0); // Should never get here
} ReleaseMutex(_hMutexDelay); } } } return 0; }
DWORD CALLBACK CBriefcase::_CalcDetailsThreadProc(void *pv) { return ((CBriefcase *)pv)->_CalcDetailsThread(); }
// IShellFolder2::GetDetailsOf
STDMETHODIMP CBriefcase::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails) { HRESULT hr = S_OK; TCHAR szTemp[MAX_PATH]; LPCIDFOLDER pidf = _IsValidID(pidl);
pDetails->str.uType = STRRET_CSTR; pDetails->str.cStr[0] = 0; if (!pidf) { hr = GetDetailsOfInfo(s_briefcase_cols, ARRAYSIZE(s_briefcase_cols), iColumn, pDetails); } else { switch (iColumn) { case ICOL_BRIEFCASE_NAME: _CopyName(pidf, szTemp, ARRAYSIZE(szTemp)); hr = StringToStrRet(szTemp, &pDetails->str); break; case ICOL_BRIEFCASE_ORIGIN: case ICOL_BRIEFCASE_STATUS: // only works if the view callback has set us up for this
if (_pbrfstg) { BRFINFO bi;
// Did we find extra info for this file or
// was the new item added to the extra info list?
if (_FindCachedName(pidl, &bi) || _AddCachedName(pidl, &bi)) { LPTSTR psz = ICOL_BRIEFCASE_ORIGIN == iColumn ? bi.szOrigin : bi.szStatus; hr = StringToStrRet(psz, &pDetails->str); } } break; case ICOL_BRIEFCASE_SIZE: if (!_IsFolder(pidf)) { StrFormatKBSize(pidf->dwSize, szTemp, ARRAYSIZE(szTemp)); hr = StringToStrRet(szTemp, &pDetails->str); } break; case ICOL_BRIEFCASE_TYPE: _GetTypeNameBuf(pidf, szTemp, ARRAYSIZE(szTemp)); hr = StringToStrRet(szTemp, &pDetails->str); break; case ICOL_BRIEFCASE_MODIFIED: DosTimeToDateTimeString(pidf->dateModified, pidf->timeModified, szTemp, ARRAYSIZE(szTemp), pDetails->fmt & LVCFMT_DIRECTION_MASK); hr = StringToStrRet(szTemp, &pDetails->str); break; } } return hr; }
// IShellFolder2::MapColumnToSCID
STDMETHODIMP CBriefcase::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid) { return MapColumnToSCIDImpl(s_briefcase_cols, ARRAYSIZE(s_briefcase_cols), iColumn, pscid); }
// IShellFolder::CompareIDs
STDMETHODIMP CBriefcase::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { LPCIDFOLDER pidf1 = _IsValidID(pidl1); LPCIDFOLDER pidf2 = _IsValidID(pidl2); if (!pidf1 || !pidf2) { return E_INVALIDARG; } HRESULT hr = _CompareFolderness(pidf1, pidf2); if (hr != ResultFromShort(0)) return hr; switch (lParam & SHCIDS_COLUMNMASK) { case ICOL_BRIEFCASE_SIZE: if (pidf1->dwSize < pidf2->dwSize) return ResultFromShort(-1); if (pidf1->dwSize > pidf2->dwSize) return ResultFromShort(1); goto DoDefault; case ICOL_BRIEFCASE_TYPE: hr = _CompareFileTypes(pidf1, pidf2); if (!hr) goto DoDefault; break; case ICOL_BRIEFCASE_MODIFIED: hr = _CompareModifiedDate(pidf1, pidf2); if (!hr) goto DoDefault; break; case ICOL_BRIEFCASE_NAME: // We need to treat this differently from others bacause
// pidf1/2 might not be simple.
hr = CFSFolder::_CompareNames(pidf1, pidf2, TRUE, FALSE); // REVIEW: (Possible performance gain with some extra code)
// We should probably avoid bindings by walking down
// the IDList here instead of calling this helper function.
//
if (hr == ResultFromShort(0)) { hr = ILCompareRelIDs((IShellFolder *)this, pidl1, pidl2, lParam); } goto DoDefaultModification; case ICOL_BRIEFCASE_ORIGIN: case ICOL_BRIEFCASE_STATUS: { BRFINFO bi1, bi2; BOOL bVal1 = _FindCachedName(pidl1, &bi1); BOOL bVal2 = _FindCachedName(pidl2, &bi2); // Do we have this info in our cache?
if (!bVal1 || !bVal2) { // No; one or both of them are missing. Have unknowns gravitate
// to the bottom of the list.
// (Don't bother adding them)
if (!bVal1 && !bVal2) hr = ResultFromShort(0); else if (!bVal1) hr = ResultFromShort(1); else hr = ResultFromShort(-1); } else { // Found the info; do a comparison
if (ICOL_BRIEFCASE_ORIGIN == (lParam & SHCIDS_COLUMNMASK)) { hr = ResultFromShort(lstrcmp(bi1.szOrigin, bi2.szOrigin)); } else { ASSERT(ICOL_BRIEFCASE_STATUS == (lParam & SHCIDS_COLUMNMASK)); hr = ResultFromShort(lstrcmp(bi1.szStatus, bi2.szStatus)); } } } break; default: DoDefault: // Sort it based on the primary (long) name -- ignore case.
{ TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
_CopyName(pidf1, szName1, ARRAYSIZE(szName1)); _CopyName(pidf2, szName2, ARRAYSIZE(szName2));
hr = ResultFromShort(lstrcmpi(szName1, szName2)); }
DoDefaultModification: if (hr == S_OK && (lParam & SHCIDS_ALLFIELDS)) { // Must sort by modified date to pick up any file changes!
hr = _CompareModifiedDate(pidf1, pidf2); if (!hr) hr = _CompareAttribs(pidf1, pidf2); } } return hr; }
// This function creates an instance of IShellView.
HRESULT CBriefcase::_CreateView(HWND hwnd, IShellView **ppsv) { *ppsv = NULL; // assume failure
HRESULT hr; CBriefcaseViewCB *pvcb = new CBriefcaseViewCB(this); if (pvcb) { hr = pvcb->_InitStgForDetails(); if (SUCCEEDED(hr)) { SFV_CREATE sSFV = {0};
hr = pvcb->QueryInterface(IID_PPV_ARG(IShellFolderViewCB, &sSFV.psfvcb)); if (SUCCEEDED(hr)) { sSFV.cbSize = sizeof(sSFV); sSFV.pshf = (IShellFolder *)this;
hr = SHCreateShellFolderView(&sSFV, ppsv); } } pvcb->Release(); } else { hr = E_OUTOFMEMORY; } return hr; }
// IShellFolder::CreateViewObject
STDMETHODIMP CBriefcase::CreateViewObject(HWND hwnd, REFIID riid, void **ppv) { HRESULT hr;
if (IsEqualIID(riid, IID_IShellView)) { hr = _CreateView(hwnd, (IShellView **)ppv); } else { // delegate to base class
hr = CFSFolder::CreateViewObject(hwnd, riid, ppv); }
ASSERT(FAILED(hr) ? (NULL == *ppv) : TRUE); return hr; }
// IShellFolder::GetAttributesOf
STDMETHODIMP CBriefcase::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG * prgfInOut) { // Validate this pidl?
if (*prgfInOut & SFGAO_VALIDATE) { // Yes; dirty the briefcase storage entry by sending an update
// notification
DebugMsg(DM_TRACE, TEXT("Briefcase: Receiving F5, dirty entire briefcase storage")); _AllNamesAreStale(); } // delegate to base
return CFSFolder::GetAttributesOf(cidl, apidl, prgfInOut); }
// IShellFolder::GetUIObjectOf
STDMETHODIMP CBriefcase::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *prgfInOut, void **ppv) { HRESULT hr;
if (cidl > 0 && IsEqualIID(riid, IID_IDataObject)) { // Create an IDataObject interface instance with our
// own vtable because we support the CFSTR_BRIEFOBJECT clipboard format
hr = CBrfData_CreateDataObj(_pidl, cidl, (LPCITEMIDLIST *)apidl, (IDataObject **)ppv); } else { // delegate to base class
hr = CFSFolder::GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv); } return hr; }
// CFSBrfFolder constructor
STDAPI CFSBrfFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { HRESULT hr; CBriefcase *pbf = new CBriefcase(punkOuter); if (pbf) { hr = pbf->Init(); if (SUCCEEDED(hr)) { hr = pbf->QueryInterface(riid, ppv); } pbf->Release(); } else { *ppv = NULL; hr = E_OUTOFMEMORY; } return hr; }
const TBBUTTON c_tbBrfCase[] = { { 0, FSIDM_UPDATEALL, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 }, { 1, FSIDM_UPDATESELECTION, 0, TBSTYLE_BUTTON, {0,0}, 0L, -1 }, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP , {0,0}, 0L, -1 }, };
#define BRFVIEW_EVENTS \
SHCNE_DISKEVENTS | \ SHCNE_ASSOCCHANGED | \ SHCNE_GLOBALEVENTS
HRESULT CBriefcaseViewCB::OnWINDOWCREATED(DWORD pv, HWND hwndView) { SHChangeNotifyEntry fsne;
ASSERT(_pbrfstg && _hwndMain && _hMutexDelay); // from the init call
// view hands folder info needed to details (status, sync path)
_pfolder->_InitDetailsInfoAndThread(_pbrfstg, _hwndMain, _hMutexDelay);
// Register an extra SHChangeNotifyRegister for our pidl to try to catch things
// like UpdateDir
fsne.pidl = _FolderPidl(); fsne.fRecursive = FALSE; _uSCNRExtra = SHChangeNotifyRegister(hwndView, SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel, SHCNE_DISKEVENTS, WM_DSV_FSNOTIFY, 1, &fsne); return S_OK; }
HRESULT CBriefcaseViewCB::OnWINDOWDESTROY(DWORD pv, HWND wP) { _pfolder->_Free();
// need to release pbrfstg as well
if (_pbrfstg) { _pbrfstg->Release(); _pbrfstg = NULL;
_hMutexDelay = NULL; // invalidate our alias
}
if (_uSCNRExtra) { SHChangeNotifyDeregister(_uSCNRExtra); _uSCNRExtra = 0; }
return S_OK; }
HRESULT CBriefcaseViewCB::OnMergeMenu(DWORD pv, QCMINFO *pinfo) { // Merge the briefcase menu onto the menu that CDefView created.
if (pinfo->hmenu) { HMENU hmSync = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(POPUP_BRIEFCASE)); if (hmSync) { Shell_MergeMenus(pinfo->hmenu, hmSync, pinfo->indexMenu, pinfo->idCmdFirst, pinfo->idCmdLast, MM_SUBMENUSHAVEIDS); DestroyMenu(hmSync); } } return S_OK; }
HRESULT CBriefcaseViewCB::_GetSelectedObjects(IDataObject **ppdtobj) { IFolderView *pfv; HRESULT hr = IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv)); if (SUCCEEDED(hr)) { hr = pfv->Items(SVGIO_SELECTION, IID_PPV_ARG(IDataObject, ppdtobj)); pfv->Release(); } return hr; }
HRESULT CBriefcaseViewCB::OnINVOKECOMMAND(DWORD pv, UINT uID) { IDataObject *pdtobj; switch (uID) { case FSIDM_UPDATEALL: // Update the entire briefcase
if (SUCCEEDED(SHGetUIObjectFromFullPIDL(_pidlRoot, NULL, IID_PPV_ARG(IDataObject, &pdtobj)))) { _pbrfstg->UpdateObject(pdtobj, _hwndMain); pdtobj->Release(); } break; case FSIDM_UPDATESELECTION: // Update the selected objects
if (SUCCEEDED(_GetSelectedObjects(&pdtobj))) { _pbrfstg->UpdateObject(pdtobj, _hwndMain); pdtobj->Release(); } break; case FSIDM_SPLIT: // Split the selected objects
if (SUCCEEDED(_GetSelectedObjects(&pdtobj))) { _pbrfstg->ReleaseObject(pdtobj, _hwndMain); pdtobj->Release(); } break; } return S_OK; }
HRESULT CBriefcaseViewCB::OnGetHelpOrTooltipText(BOOL bHelp, UINT wPl, UINT cch, LPTSTR psz) { LoadString(HINST_THISDLL, wPl + (bHelp ? IDS_MH_FSIDM_FIRST : IDS_TT_FSIDM_FIRST), psz, cch); return S_OK; }
int CBriefcaseViewCB::_GetSelectedCount() { int cItems = 0; IFolderView *pfv; if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv)))) { pfv->ItemCount(SVGIO_SELECTION, &cItems); pfv->Release(); } return cItems; }
HRESULT CBriefcaseViewCB::OnINITMENUPOPUP(DWORD pv, UINT idCmdFirst, UINT nIndex, HMENU hmenu) { BOOL bEnabled = _GetSelectedCount() > 0; EnableMenuItem(hmenu, idCmdFirst+FSIDM_UPDATESELECTION, bEnabled ? MF_ENABLED : MF_GRAYED); EnableMenuItem(hmenu, idCmdFirst+FSIDM_SPLIT, bEnabled ? MF_ENABLED : MF_GRAYED); return S_OK; }
HRESULT CBriefcaseViewCB::OnGETBUTTONINFO(DWORD pv, TBINFO* ptbinfo) { ptbinfo->cbuttons = ARRAYSIZE(c_tbBrfCase); ptbinfo->uFlags = TBIF_PREPEND; return S_OK; }
HRESULT CBriefcaseViewCB::OnGETBUTTONS(DWORD pv, UINT idCmdFirst, UINT wPh, TBBUTTON *ptbbutton) { IShellBrowser* psb; HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb)); if (SUCCEEDED(hr)) { LRESULT iBtnOffset; TBADDBITMAP ab; // add the toolbar button bitmap, get it's offset
ab.hInst = HINST_THISDLL; ab.nID = IDB_BRF_TB_SMALL; // std bitmaps
psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 2, (LPARAM)&ab, &iBtnOffset); for (int i = 0; i < ARRAYSIZE(c_tbBrfCase); i++) { ptbbutton[i] = c_tbBrfCase[i]; if (!(c_tbBrfCase[i].fsStyle & TBSTYLE_SEP)) { ptbbutton[i].idCommand += idCmdFirst; ptbbutton[i].iBitmap += (int) iBtnOffset; } } psb->Release(); } return S_OK; }
HRESULT CBriefcaseViewCB::OnSELCHANGE(DWORD pv, UINT idCmdFirst, UINT wPh, SFVM_SELCHANGE_DATA*lP) { IShellBrowser* psb; HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb)); if (SUCCEEDED(hr)) { psb->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON, idCmdFirst + FSIDM_UPDATESELECTION, (LPARAM)(_GetSelectedCount() > 0), NULL); psb->Release(); } return E_FAIL; // (we did not update the status area)
}
HRESULT CBriefcaseViewCB::OnQUERYFSNOTIFY(DWORD pv, SHChangeNotifyEntry *pfsne) { // Register to receive global events
pfsne->pidl = NULL; pfsne->fRecursive = TRUE; return NOERROR; }
HRESULT CBriefcaseViewCB::_HandleFSNotifyForDefView(LPARAM lEvent, LPCITEMIDLIST * ppidl, LPTSTR pszBuf) { HRESULT hr; switch (lEvent) { case SHCNE_RENAMEITEM: case SHCNE_RENAMEFOLDER: if (!ILIsParent(_FolderPidl(), ppidl[0], TRUE)) { // move to this folder
hr = _HandleFSNotifyForDefView(SHCNE_CREATE, &ppidl[1], pszBuf); } else if (!ILIsParent(_FolderPidl(), ppidl[1], TRUE)) { // move from this folder
hr = _HandleFSNotifyForDefView(SHCNE_DELETE, &ppidl[0], pszBuf); } else { // have the defview handle it
_pfolder->_CachedNameIsStale(ppidl[0], TRUE); hr = NOERROR; } break; case SHCNE_DELETE: case SHCNE_RMDIR: _pfolder->_CachedNameIsStale(ppidl[0], TRUE); hr = NOERROR; break; default: hr = NOERROR; break; } return hr; }
// Converts a shell change notify event to a briefcase storage event.
LONG NOEFromSHCNE(LPARAM lEvent) { switch (lEvent) { case SHCNE_RENAMEITEM: return NOE_RENAME; case SHCNE_RENAMEFOLDER: return NOE_RENAMEFOLDER; case SHCNE_CREATE: return NOE_CREATE; case SHCNE_MKDIR: return NOE_CREATEFOLDER; case SHCNE_DELETE: return NOE_DELETE; case SHCNE_RMDIR: return NOE_DELETEFOLDER; case SHCNE_UPDATEITEM: return NOE_DIRTY; case SHCNE_UPDATEDIR: return NOE_DIRTYFOLDER; default: return 0; } }
HRESULT CBriefcaseViewCB::OnFSNOTIFY(DWORD pv, LPCITEMIDLIST *ppidl, LPARAM lEvent) { HRESULT hr; TCHAR szPath[MAX_PATH * 2]; // we are in the process of being freed, but changenotify's can still come in because we are not freed atomically
if (!_pbrfstg) { return S_FALSE; }
if (lEvent == SHCNE_UPDATEIMAGE || lEvent == SHCNE_FREESPACE) { return S_FALSE; } if (ppidl && !ILIsEmpty(ppidl[0]) && SHGetPathFromIDList(ppidl[0], szPath)) { UINT uFlags; LONG lEventNOE; if ((SHCNE_RENAMEFOLDER == lEvent) || (SHCNE_RENAMEITEM == lEvent)) { ASSERT(ppidl[1]); ASSERT(ARRAYSIZE(szPath) >= lstrlen(szPath)*2); // rough estimate
// Tack the new name after the old name, separated by the null
SHGetPathFromIDList(ppidl[1], &szPath[lstrlen(szPath)+1]); } // Tell the briefcase the path has potentially changed
lEventNOE = NOEFromSHCNE(lEvent); _pbrfstg->Notify(szPath, lEventNOE, &uFlags, _hwndMain); // Was this item marked?
if (uFlags & NF_ITEMMARKED) { // Yes; mark it stale in the expensive cache
_pfolder->_CachedNameIsStale(ppidl[0], FALSE); } // Does the window need to be refreshed?
if (uFlags & NF_REDRAWWINDOW) { // Yes
IShellView *psv; if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IShellView, &psv)))) { psv->Refresh(); psv->Release(); } } // Did this event occur in this folder?
if (NULL == ppidl || ILIsParent(_FolderPidl(), ppidl[0], TRUE) || (((SHCNE_RENAMEITEM == lEvent) || (SHCNE_RENAMEFOLDER == lEvent)) && ILIsParent(_FolderPidl(), ppidl[1], TRUE)) || (SHCNE_UPDATEDIR == lEvent && ILIsEqual(_FolderPidl(), ppidl[0]))) { // Yes; deal with it
hr = _HandleFSNotifyForDefView(lEvent, ppidl, szPath); } else { // No
hr = S_FALSE; } } else { // ASSERT(0);
hr = S_FALSE; } return hr; }
HRESULT CBriefcaseViewCB::OnQUERYCOPYHOOK(DWORD pv) { return S_OK; }
HRESULT CBriefcaseViewCB::OnNOTIFYCOPYHOOK(DWORD pv, COPYHOOKINFO *pchi) { HRESULT hr = NOERROR; // Is this a pertinent operation?
if (FO_MOVE == pchi->wFunc || FO_RENAME == pchi->wFunc || FO_DELETE == pchi->wFunc) { // Yes; don't allow the briefcase root or a parent folder to get moved
// while the briefcase is still open. (The database is locked while
// the briefcase is open, and will fail the move/rename operation
// in an ugly way.)
LPITEMIDLIST pidl = ILCreateFromPath(pchi->pszSrcFile); if (pidl) { // Is the folder that is being moved or renamed a parent or equal
// of the Briefcase root?
if (ILIsParent(pidl, _pidlRoot, FALSE) || ILIsEqual(pidl, _pidlRoot)) { // Yes; don't allow it until the briefcase is closed.
int ids; if (FO_MOVE == pchi->wFunc || FO_RENAME == pchi->wFunc) { ids = IDS_MOVEBRIEFCASE; } else { ASSERT(FO_DELETE == pchi->wFunc); ids = IDS_DELETEBRIEFCASE; } ShellMessageBox(HINST_THISDLL, _hwndMain, MAKEINTRESOURCE(ids), NULL, MB_OK | MB_ICONINFORMATION); hr = IDCANCEL; } ILFree(pidl); } } return hr; }
HRESULT CBriefcaseViewCB::OnINSERTITEM(DWORD pv, LPCITEMIDLIST pidl) { HRESULT hr; TCHAR szPath[MAX_PATH]; if (SHGetPathFromIDList(pidl, szPath)) { // Always hide the desktop.ini and the database file.
LPTSTR pszName = PathFindFileName(szPath); if (0 == lstrcmpi(pszName, c_szDesktopIni) || 0 == lstrcmpi(pszName, _szDBName)) hr = S_FALSE; // don't add
else hr = S_OK; } else hr = S_OK; // Let it be added...
return hr; }
HRESULT CBriefcaseViewCB::OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE*lP) { *lP = FVM_DETAILS; return S_OK; }
HRESULT CBriefcaseViewCB::OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA * phtd) { HRESULT hr; if (IsOS(OS_ANYSERVER)) { hr = StringCchCopy(phtd->wszHelpFile, ARRAYSIZE(phtd->wszHelpFile), L"brief.chm"); } else { hr = StringCchCopy(phtd->wszHelpTopic, ARRAYSIZE(phtd->wszHelpTopic), L"hcp://services/subsite?node=Unmapped/Briefcase"); } return hr; }
CBriefcaseViewCB::CBriefcaseViewCB(CBriefcase *pfolder) : CBaseShellFolderViewCB(pfolder->_pidl, BRFVIEW_EVENTS), _pfolder(pfolder) { _pfolder->AddRef(); }
CBriefcaseViewCB::~CBriefcaseViewCB() { if (_pbrfstg) _pbrfstg->Release();
if (_pidlRoot) ILFree(_pidlRoot);
_pfolder->Release(); }
HRESULT CBriefcaseViewCB::_InitStgForDetails() { ASSERT(NULL == _pbrfstg);
HRESULT hr = CreateBrfStgFromIDList(_FolderPidl(), _hwndMain, &_pbrfstg); if (SUCCEEDED(hr)) { ASSERT(NULL == _hMutexDelay); _pbrfstg->GetExtraInfo(NULL, GEI_DELAYHANDLE, 0, (LPARAM)&_hMutexDelay); ASSERT(0 == _szDBName[0]); _pbrfstg->GetExtraInfo(NULL, GEI_DATABASENAME, ARRAYSIZE(_szDBName), (LPARAM)_szDBName);
TCHAR szPath[MAX_PATH]; hr = _pbrfstg->GetExtraInfo(NULL, GEI_ROOT, (WPARAM)ARRAYSIZE(szPath), (LPARAM)szPath); if (SUCCEEDED(hr)) hr = SHILCreateFromPath(szPath, &_pidlRoot, NULL); } return hr; }
STDMETHODIMP CBriefcaseViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWINDOWCREATED); HANDLE_MSG(0, SFVM_WINDOWDESTROY, OnWINDOWDESTROY); HANDLE_MSG(0, SFVM_MERGEMENU, OnMergeMenu); HANDLE_MSG(0, SFVM_INVOKECOMMAND, OnINVOKECOMMAND); HANDLE_MSG(TRUE , SFVM_GETHELPTEXT , OnGetHelpOrTooltipText); HANDLE_MSG(FALSE, SFVM_GETTOOLTIPTEXT, OnGetHelpOrTooltipText); HANDLE_MSG(0, SFVM_INITMENUPOPUP, OnINITMENUPOPUP); HANDLE_MSG(0, SFVM_GETBUTTONINFO, OnGETBUTTONINFO); HANDLE_MSG(0, SFVM_GETBUTTONS, OnGETBUTTONS); HANDLE_MSG(0, SFVM_SELCHANGE, OnSELCHANGE); HANDLE_MSG(0, SFVM_QUERYFSNOTIFY, OnQUERYFSNOTIFY); HANDLE_MSG(0, SFVM_FSNOTIFY, OnFSNOTIFY); HANDLE_MSG(0, SFVM_QUERYCOPYHOOK, OnQUERYCOPYHOOK); HANDLE_MSG(0, SFVM_NOTIFYCOPYHOOK, OnNOTIFYCOPYHOOK); HANDLE_MSG(0, SFVM_INSERTITEM, OnINSERTITEM); HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDEFVIEWMODE); HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages); HANDLE_MSG(0, SFVM_GETHELPTOPIC, OnGetHelpTopic); HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout); HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent); HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks); HANDLE_MSG(0, SFVM_DELAYWINDOWCREATE, OnDELAYWINDOWCREATE);
default: return E_FAIL; }
return NOERROR; }
STDAPI CreateBrfStgFromPath(LPCTSTR pszPath, HWND hwnd, IBriefcaseStg **ppbs) { IBriefcaseStg *pbrfstg; HRESULT hr = CoCreateInstance(CLSID_Briefcase, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBriefcaseStg, &pbrfstg)); if (SUCCEEDED(hr)) { hr = pbrfstg->Initialize(pszPath, hwnd); if (SUCCEEDED(hr)) { hr = pbrfstg->QueryInterface(IID_PPV_ARG(IBriefcaseStg, ppbs)); } pbrfstg->Release(); } return hr; }
STDAPI CreateBrfStgFromIDList(LPCITEMIDLIST pidl, HWND hwnd, IBriefcaseStg **ppbs) { HRESULT hr = E_FAIL; // Create an instance of IBriefcaseStg
TCHAR szFolder[MAX_PATH]; if (SHGetPathFromIDList(pidl, szFolder)) { hr = CreateBrfStgFromPath(szFolder, hwnd, ppbs); } return hr; }
HRESULT CBriefcaseViewCB::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData) { ZeroMemory(pData, sizeof(*pData)); pData->dwLayout = SFVMWVL_NORMAL | SFVMWVL_FILES; return S_OK; }
HRESULT CBriefcaseViewCB::_OnUpdate(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CBriefcaseViewCB* pThis = (CBriefcaseViewCB*)(void*)pv; IDataObject *pdo; HRESULT hr = S_OK;
if (!psiItemArray) { IFolderView *pfv; hr = IUnknown_QueryService(pThis->_punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv)); if (SUCCEEDED(hr)) { hr = pfv->Items(SVGIO_ALLVIEW, IID_PPV_ARG(IDataObject, &pdo)); pfv->Release(); } } else { hr = psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo)); }
if (SUCCEEDED(hr)) { hr = SHInvokeCommandOnDataObject(pThis->_hwndMain, NULL, pdo, 0, "update"); pdo->Release(); }
return hr; }
const WVTASKITEM c_BriefcaseTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_BRIEFCASE, IDS_HEADER_BRIEFCASE_TT); const WVTASKITEM c_BriefcaseTaskList[] = { WVTI_ENTRY_ALL_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_UPDATE_ALL, IDS_TASK_UPDATE_ITEM, IDS_TASK_UPDATE_ITEM, IDS_TASK_UPDATE_ITEMS, IDS_TASK_UPDATE_ITEM_TT, IDI_TASK_UPDATEITEMS, NULL, CBriefcaseViewCB::_OnUpdate), };
HRESULT CBriefcaseViewCB::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData) { ZeroMemory(pData, sizeof(*pData));
Create_IUIElement(&c_BriefcaseTaskHeader, &(pData->pSpecialTaskHeader));
return S_OK; }
HRESULT CBriefcaseViewCB::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks) { ZeroMemory(pTasks, sizeof(*pTasks));
Create_IEnumUICommand((IUnknown*)(void*)this, c_BriefcaseTaskList, ARRAYSIZE(c_BriefcaseTaskList), &pTasks->penumSpecialTasks);
return S_OK; }
HRESULT CBriefcaseViewCB::OnDELAYWINDOWCREATE(DWORD pv, HWND hwnd) { HRESULT hr; TCHAR szPath[MAX_PATH];
_pfolder->_GetPath(szPath, ARRAYSIZE(szPath)); if (!PathAppend(szPath, c_szDesktopIni)) { hr = E_FAIL; } else { BOOL bRunWizard = GetPrivateProfileInt(STRINI_CLASSINFO, TEXT("RunWizard"), 0, szPath); // Run the wizard?
if (bRunWizard) { // work around old bug where FILE_ATTRIBUTE_READONLY was set
SetFileAttributes(szPath, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
// Delete the .ini entry
WritePrivateProfileString(STRINI_CLASSINFO, TEXT("RunWizard"), NULL, szPath);
SHRunDLLThread(hwnd, TEXT("SYNCUI.DLL,Briefcase_Intro"), SW_SHOW); } hr = S_OK; } return hr; }
|