You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1868 lines
57 KiB
1868 lines
57 KiB
#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;
|
|
}
|
|
|
|
|