Source code of Windows XP (NT5)
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.

1838 lines
54 KiB

  1. #include "shellprv.h"
  2. #include <brfcasep.h>
  3. #include "filefldr.h"
  4. #include "brfcase.h"
  5. #include "datautil.h"
  6. #include "prop.h"
  7. #include "ids.h"
  8. #include "defview.h" // for WM_DSV_FSNOTIFY
  9. #include "basefvcb.h"
  10. #include "views.h"
  11. #define MAX_NAME 32
  12. #define HACK_IGNORETYPE 0x04000000
  13. TCHAR g_szDetailsUnknown[MAX_NAME] = TEXT("");
  14. // Values for CBriefcase::_FindNextState
  15. #define FNS_UNDETERMINED 1
  16. #define FNS_STALE 2
  17. #define FNS_DELETED 3
  18. typedef struct
  19. {
  20. TCHAR szOrigin[MAX_PATH];
  21. TCHAR szStatus[MAX_NAME];
  22. BOOL bDetermined:1;
  23. BOOL bUpToDate:1;
  24. BOOL bDeleted:1;
  25. } BRFINFO;
  26. typedef struct
  27. {
  28. LPITEMIDLIST pidl; // Indexed value
  29. BRFINFO bi;
  30. } BRFINFOHDR;
  31. class CBriefcaseViewCB;
  32. class CBriefcase : public CFSFolder
  33. {
  34. friend CBriefcaseViewCB;
  35. public:
  36. CBriefcase(IUnknown *punkOuter);
  37. // IShellFolder
  38. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  39. STDMETHODIMP CreateViewObject (HWND hwndOwner, REFIID riid, void **ppv);
  40. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut);
  41. STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv);
  42. // IShellFolder2
  43. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
  44. STDMETHOD (MapColumnToSCID)(UINT iColumn, SHCOLUMNID *pscid);
  45. private:
  46. ~CBriefcase();
  47. static DWORD CALLBACK _CalcDetailsThreadProc(void *pv);
  48. DWORD _CalcDetailsThread();
  49. void _EnterCS();
  50. void _LeaveCS();
  51. static int CALLBACK _CompareIDCallBack(void *pv1, void *pv2, LPARAM lParam);
  52. BOOL _CreateDetailsThread();
  53. BOOL _InitDetailsInfoAndThread(IBriefcaseStg *pbrfstg, HWND hwndMain, HANDLE hMutexDelay);
  54. void _Free();
  55. void _Reset();
  56. BOOL _FindCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi);
  57. BOOL _DeleteCachedName(LPCITEMIDLIST pidl);
  58. BOOL _FindNextState(UINT uState, BRFINFOHDR *pbihdrOut);
  59. void _CalcCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi);
  60. void _CachedNameIsStale(LPCITEMIDLIST pidl, BOOL bDeleted);
  61. void _AllNamesAreStale();
  62. BOOL _AddCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi);
  63. HRESULT _CreateView(HWND hwnd, IShellView **ppsv);
  64. HWND _hwndMain; // evil, view related state
  65. IBriefcaseStg *_pbrfstg; // evil, view related state
  66. // accessed by background thread
  67. HDPA _hdpa;
  68. int _idpaStaleCur;
  69. int _idpaUndeterminedCur;
  70. int _idpaDeletedCur;
  71. HANDLE _hSemPending; // Pending semaphore
  72. CRITICAL_SECTION _cs;
  73. HANDLE _hEventDie;
  74. HANDLE _hThreadCalcDetails;
  75. HANDLE _hMutexDelay; // alias given out by the _pbrfstg
  76. BOOL _bFreePending;
  77. #ifdef DEBUG
  78. UINT _cUndetermined;
  79. UINT _cStale;
  80. UINT _cDeleted;
  81. UINT _cCSRef;
  82. #endif
  83. };
  84. class CBriefcaseViewCB : public CBaseShellFolderViewCB
  85. {
  86. public:
  87. CBriefcaseViewCB(CBriefcase *pfolder);
  88. HRESULT _InitStgForDetails();
  89. STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
  90. private:
  91. ~CBriefcaseViewCB();
  92. LPCITEMIDLIST _FolderPidl() { return _pfolder->_pidl; }
  93. HRESULT OnWINDOWCREATED(DWORD pv, HWND hwndView);
  94. HRESULT OnWINDOWDESTROY(DWORD pv, HWND wP);
  95. HRESULT OnMergeMenu(DWORD pv, QCMINFO*lP);
  96. HRESULT OnINVOKECOMMAND(DWORD pv, UINT wP);
  97. HRESULT OnGetHelpOrTooltipText(BOOL bHelp, UINT wPl, UINT cch, LPTSTR psz);
  98. HRESULT OnINITMENUPOPUP(DWORD pv, UINT wPl, UINT wPh, HMENU lP);
  99. HRESULT OnGETBUTTONINFO(DWORD pv, TBINFO* ptbinfo);
  100. HRESULT OnGETBUTTONS(DWORD pv, UINT wPl, UINT wPh, TBBUTTON*lP);
  101. HRESULT OnSELCHANGE(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP);
  102. HRESULT OnQUERYFSNOTIFY(DWORD pv, SHChangeNotifyEntry*lP);
  103. HRESULT OnFSNOTIFY(DWORD pv, LPCITEMIDLIST*wP, LPARAM lP);
  104. HRESULT OnQUERYCOPYHOOK(DWORD pv);
  105. HRESULT OnNOTIFYCOPYHOOK(DWORD pv, COPYHOOKINFO*lP);
  106. HRESULT OnINSERTITEM(DWORD pv, LPCITEMIDLIST wP);
  107. HRESULT OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE *lP);
  108. HRESULT OnSupportsIdentity(DWORD pv);
  109. HRESULT OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA * phtd);
  110. HRESULT OnDELAYWINDOWCREATE(DWORD pv, HWND hwnd);
  111. HRESULT _GetSelectedObjects(IDataObject **ppdtobj);
  112. HRESULT _HandleFSNotifyForDefView(LPARAM lEvent, LPCITEMIDLIST * ppidl, LPTSTR pszBuf);
  113. int _GetSelectedCount();
  114. CBriefcase *_pfolder;
  115. IBriefcaseStg *_pbrfstg;
  116. LPITEMIDLIST _pidlRoot; // Root of briefcase
  117. HANDLE _hMutexDelay;
  118. ULONG _uSCNRExtra; // Extra SHChangeNotifyRegister for our pidl...
  119. TCHAR _szDBName[MAX_PATH];
  120. // Web View implementation
  121. HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData);
  122. HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData);
  123. HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks);
  124. public:
  125. static HRESULT _OnUpdate(IUnknown* pv,IShellItemArray *psiItemArray, IBindCtx *pbc);
  126. };
  127. CBriefcase::CBriefcase(IUnknown *punkOuter) : CFSFolder(punkOuter)
  128. {
  129. _clsidBind = CLSID_BriefcaseFolder; // in CFSFolder
  130. InitializeCriticalSection(&_cs);
  131. }
  132. CBriefcase::~CBriefcase()
  133. {
  134. DeleteCriticalSection(&_cs);
  135. }
  136. enum
  137. {
  138. ICOL_BRIEFCASE_NAME = 0,
  139. ICOL_BRIEFCASE_ORIGIN,
  140. ICOL_BRIEFCASE_STATUS,
  141. ICOL_BRIEFCASE_SIZE,
  142. ICOL_BRIEFCASE_TYPE,
  143. ICOL_BRIEFCASE_MODIFIED,
  144. };
  145. const COLUMN_INFO s_briefcase_cols[] =
  146. {
  147. DEFINE_COL_STR_ENTRY(SCID_NAME, 30, IDS_NAME_COL),
  148. DEFINE_COL_STR_ENTRY(SCID_SYNCCOPYIN, 24, IDS_SYNCCOPYIN_COL),
  149. DEFINE_COL_STR_ENTRY(SCID_STATUS, 18, IDS_STATUS_COL),
  150. DEFINE_COL_SIZE_ENTRY(SCID_SIZE, IDS_SIZE_COL),
  151. DEFINE_COL_STR_ENTRY(SCID_TYPE, 18, IDS_TYPE_COL),
  152. DEFINE_COL_STR_ENTRY(SCID_WRITETIME, 18, IDS_MODIFIED_COL),
  153. };
  154. #ifdef DEBUG
  155. #define _AssertInCS() ASSERT(0 < (this)->_cCSRef)
  156. #define _AssertNotInCS() ASSERT(0 == (this)->_cCSRef)
  157. #else
  158. #define _AssertInCS()
  159. #define _AssertNotInCS()
  160. #endif
  161. void CBriefcase::_EnterCS()
  162. {
  163. EnterCriticalSection(&_cs);
  164. #ifdef DEBUG
  165. _cCSRef++;
  166. #endif
  167. }
  168. void CBriefcase::_LeaveCS()
  169. {
  170. _AssertInCS();
  171. #ifdef DEBUG
  172. _cCSRef--;
  173. #endif
  174. LeaveCriticalSection(&_cs);
  175. }
  176. //---------------------------------------------------------------------------
  177. // Brfview functions: Expensive cache stuff
  178. //---------------------------------------------------------------------------
  179. // Comparison function for the DPA list
  180. int CALLBACK CBriefcase::_CompareIDCallBack(void *pv1, void *pv2, LPARAM lParam)
  181. {
  182. BRFINFOHDR *pbihdr1 = (BRFINFOHDR *)pv1;
  183. BRFINFOHDR *pbihdr2 = (BRFINFOHDR *)pv2;
  184. CBriefcase *pfolder = (CBriefcase *)lParam;
  185. HRESULT hr = pfolder->CompareIDs(HACK_IGNORETYPE, pbihdr1->pidl, pbihdr2->pidl);
  186. ASSERT(SUCCEEDED(hr));
  187. return (short)SCODE_CODE(GetScode(hr)); // (the short cast is important!)
  188. }
  189. // Create the secondary thread for the expensive cache
  190. BOOL CBriefcase::_CreateDetailsThread()
  191. {
  192. BOOL bRet = FALSE;
  193. // The semaphore is used to determine whether anything
  194. // needs to be refreshed in the cache.
  195. _hSemPending = CreateSemaphore(NULL, 0, MAXLONG, NULL);
  196. if (_hSemPending)
  197. {
  198. #ifdef DEBUG
  199. _cStale = 0;
  200. _cUndetermined = 0;
  201. _cDeleted = 0;
  202. #endif
  203. ASSERT(NULL == _hEventDie);
  204. _hEventDie = CreateEvent(NULL, FALSE, FALSE, NULL);
  205. if (_hEventDie)
  206. {
  207. // Create the thread that will calculate expensive data
  208. DWORD idThread;
  209. _hThreadCalcDetails = CreateThread(NULL, 0, _CalcDetailsThreadProc, this, CREATE_SUSPENDED, &idThread);
  210. if (_hThreadCalcDetails)
  211. {
  212. ResumeThread(_hThreadCalcDetails);
  213. bRet = TRUE;
  214. }
  215. else
  216. {
  217. CloseHandle(_hEventDie);
  218. _hEventDie = NULL;
  219. CloseHandle(_hSemPending);
  220. _hSemPending = NULL;
  221. }
  222. }
  223. else
  224. {
  225. CloseHandle(_hSemPending);
  226. _hSemPending = NULL;
  227. }
  228. }
  229. return bRet;
  230. }
  231. // view callback inits the folder with data it needs to run the GetDetailsOf() stuff
  232. // on a background thread
  233. BOOL CBriefcase::_InitDetailsInfoAndThread(IBriefcaseStg *pbrfstg, HWND hwndMain, HANDLE hMutexDelay)
  234. {
  235. BOOL bRet = FALSE;
  236. ASSERT(pbrfstg && hwndMain && hMutexDelay); // from the init call
  237. _EnterCS();
  238. {
  239. if (_hdpa)
  240. {
  241. bRet = TRUE;
  242. }
  243. else
  244. {
  245. LoadString(HINST_THISDLL, IDS_DETAILSUNKNOWN, g_szDetailsUnknown, ARRAYSIZE(g_szDetailsUnknown));
  246. _hwndMain = hwndMain;
  247. _hMutexDelay = hMutexDelay;
  248. _idpaStaleCur = 0;
  249. _idpaUndeterminedCur = 0;
  250. _idpaDeletedCur = 0;
  251. _hdpa = DPA_Create(8);
  252. if (_hdpa)
  253. {
  254. bRet = _CreateDetailsThread();
  255. if (bRet)
  256. {
  257. ASSERT(NULL == _pbrfstg);
  258. _pbrfstg = pbrfstg;
  259. pbrfstg->AddRef();
  260. }
  261. else
  262. {
  263. // Failed
  264. DPA_Destroy(_hdpa);
  265. _hdpa = NULL;
  266. }
  267. }
  268. }
  269. }
  270. _LeaveCS();
  271. return bRet;
  272. }
  273. // Clean up the cache of expensive data
  274. void CBriefcase::_Free()
  275. {
  276. _EnterCS();
  277. {
  278. if (_hEventDie)
  279. {
  280. if (_hThreadCalcDetails)
  281. {
  282. HANDLE hThread = _hThreadCalcDetails;
  283. SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST);
  284. // Signal the secondary thread to end
  285. SetEvent(_hEventDie);
  286. // Make sure we are not in the critical section when
  287. // we wait for the secondary thread to exit. Without
  288. // this check, hitting F5 twice in a row could deadlock.
  289. _LeaveCS();
  290. {
  291. // Wait for the threads to exit
  292. _AssertNotInCS();
  293. WaitForSendMessageThread(hThread, INFINITE);
  294. }
  295. _EnterCS();
  296. DebugMsg(DM_TRACE, TEXT("Briefcase Secondary thread ended"));
  297. CloseHandle(_hThreadCalcDetails);
  298. _hThreadCalcDetails = NULL;
  299. }
  300. CloseHandle(_hEventDie);
  301. _hEventDie = NULL;
  302. }
  303. if (_hdpa)
  304. {
  305. int idpa = DPA_GetPtrCount(_hdpa);
  306. while (--idpa >= 0)
  307. {
  308. BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa);
  309. ILFree(pbihdr->pidl);
  310. LocalFree((HLOCAL)pbihdr);
  311. }
  312. DPA_Destroy(_hdpa);
  313. _hdpa = NULL;
  314. }
  315. if (_hSemPending)
  316. {
  317. CloseHandle(_hSemPending);
  318. _hSemPending = NULL;
  319. }
  320. if (_pbrfstg)
  321. {
  322. _pbrfstg->Release();
  323. _pbrfstg = NULL;
  324. _hMutexDelay = NULL; // invalidate our alias
  325. }
  326. }
  327. _LeaveCS();
  328. }
  329. // Resets the expensive data cache
  330. void CBriefcase::_Reset()
  331. {
  332. _AssertNotInCS();
  333. _EnterCS();
  334. {
  335. IBriefcaseStg *pbrfstg = _pbrfstg;
  336. if (!_bFreePending && pbrfstg)
  337. {
  338. HWND hwndMain = _hwndMain;
  339. HANDLE hMutex = _hMutexDelay;
  340. pbrfstg->AddRef();
  341. // Since we won't be in the critical section when we
  342. // wait for the paint thread to exit, set this flag to
  343. // avoid nasty re-entrant calls.
  344. _bFreePending = TRUE;
  345. // Reset by freeing and reinitializing.
  346. _LeaveCS();
  347. {
  348. _Free();
  349. // whacky re-init of ourselevs
  350. _InitDetailsInfoAndThread(pbrfstg, hwndMain, hMutex);
  351. }
  352. _EnterCS();
  353. _bFreePending = FALSE;
  354. pbrfstg->Release();
  355. }
  356. }
  357. _LeaveCS();
  358. }
  359. // Finds a cached name structure and returns a copy of it in *pbi.
  360. BOOL CBriefcase::_FindCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi)
  361. {
  362. BOOL bRet = FALSE;
  363. _EnterCS();
  364. {
  365. if (_hdpa)
  366. {
  367. BRFINFOHDR bihdr = {0};
  368. bihdr.pidl = (LPITEMIDLIST)pidl; // const -> non const
  369. int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED);
  370. if (DPA_ERR != idpa)
  371. {
  372. // Yes
  373. BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa);
  374. ASSERT(pbihdr);
  375. *pbi = pbihdr->bi;
  376. bRet = TRUE;
  377. }
  378. }
  379. }
  380. _LeaveCS();
  381. return bRet;
  382. }
  383. // Deletes a cached name structure.
  384. BOOL CBriefcase::_DeleteCachedName(LPCITEMIDLIST pidl)
  385. {
  386. BOOL bRet = FALSE;
  387. _EnterCS();
  388. {
  389. if (_hdpa)
  390. {
  391. BRFINFOHDR bihdr = {0};
  392. bihdr.pidl = (LPITEMIDLIST)pidl; // const -> non const
  393. int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED);
  394. if (DPA_ERR != idpa)
  395. {
  396. #ifdef DEBUG
  397. BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa);
  398. ASSERT(pbihdr);
  399. _cDeleted--;
  400. if (!pbihdr->bi.bDetermined)
  401. _cUndetermined--;
  402. else if (!pbihdr->bi.bUpToDate)
  403. _cStale--;
  404. #endif
  405. // Keep index pointers current
  406. if (_idpaStaleCur >= idpa)
  407. _idpaStaleCur--;
  408. if (_idpaUndeterminedCur >= idpa)
  409. _idpaUndeterminedCur--;
  410. if (_idpaDeletedCur >= idpa)
  411. _idpaDeletedCur--;
  412. DPA_DeletePtr(_hdpa, idpa);
  413. bRet = TRUE;
  414. }
  415. }
  416. }
  417. _LeaveCS();
  418. return bRet;
  419. }
  420. // Finds the next cached name structure that matches the requested state.
  421. BOOL CBriefcase::_FindNextState(UINT uState, BRFINFOHDR *pbihdrOut)
  422. {
  423. BOOL bRet = FALSE;
  424. ASSERT(pbihdrOut);
  425. _EnterCS();
  426. {
  427. if (_hdpa)
  428. {
  429. HDPA hdpa = _hdpa;
  430. int idpaCur;
  431. int idpa;
  432. BRFINFOHDR *pbihdr;
  433. int cdpaMax = DPA_GetPtrCount(hdpa);
  434. switch (uState)
  435. {
  436. case FNS_UNDETERMINED:
  437. // Iterate thru the entire list starting at idpa. We roll this
  438. // loop out to be two loops: the first iterates the last portion
  439. // of the list, the second iterates the first portion if the former
  440. // failed to find anything.
  441. idpaCur = _idpaUndeterminedCur + 1;
  442. for (idpa = idpaCur; idpa < cdpaMax; idpa++)
  443. {
  444. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  445. if (!pbihdr->bi.bDetermined)
  446. {
  447. goto Found; // Found it
  448. }
  449. }
  450. ASSERT(idpaCur <= cdpaMax);
  451. for (idpa = 0; idpa < idpaCur; idpa++)
  452. {
  453. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  454. if (!pbihdr->bi.bDetermined)
  455. {
  456. goto Found; // Found it
  457. }
  458. }
  459. ASSERT(0 == _cUndetermined);
  460. break;
  461. case FNS_STALE:
  462. // Iterate thru the entire list starting at idpa. We roll this
  463. // loop out to be two loops: the first iterates the last portion
  464. // of the list, the second iterates the first portion if the former
  465. // failed to find anything.
  466. idpaCur = _idpaStaleCur + 1;
  467. for (idpa = idpaCur; idpa < cdpaMax; idpa++)
  468. {
  469. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  470. if (!pbihdr->bi.bUpToDate)
  471. {
  472. goto Found; // Found it
  473. }
  474. }
  475. ASSERT(idpaCur <= cdpaMax);
  476. for (idpa = 0; idpa < idpaCur; idpa++)
  477. {
  478. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  479. if (!pbihdr->bi.bUpToDate)
  480. {
  481. goto Found; // Found it
  482. }
  483. }
  484. ASSERT(0 == _cStale);
  485. break;
  486. case FNS_DELETED:
  487. // Iterate thru the entire list starting at idpa. We roll this
  488. // loop out to be two loops: the first iterates the last portion
  489. // of the list, the second iterates the first portion if the former
  490. // failed to find anything.
  491. idpaCur = _idpaDeletedCur + 1;
  492. for (idpa = idpaCur; idpa < cdpaMax; idpa++)
  493. {
  494. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  495. if (pbihdr->bi.bDeleted)
  496. {
  497. goto Found; // Found it
  498. }
  499. }
  500. ASSERT(idpaCur <= cdpaMax);
  501. for (idpa = 0; idpa < idpaCur; idpa++)
  502. {
  503. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  504. if (pbihdr->bi.bDeleted)
  505. {
  506. goto Found; // Found it
  507. }
  508. }
  509. ASSERT(0 == _cDeleted);
  510. break;
  511. default:
  512. ASSERT(0); // should never get here
  513. break;
  514. }
  515. goto Done;
  516. Found:
  517. ASSERT(0 <= idpa && idpa < cdpaMax);
  518. // Found the next item of the requested state
  519. switch (uState)
  520. {
  521. case FNS_UNDETERMINED:
  522. _idpaUndeterminedCur = idpa;
  523. break;
  524. case FNS_STALE:
  525. _idpaStaleCur = idpa;
  526. break;
  527. case FNS_DELETED:
  528. _idpaDeletedCur = idpa;
  529. break;
  530. }
  531. *pbihdrOut = *pbihdr;
  532. pbihdrOut->pidl = ILClone(pbihdr->pidl);
  533. if (pbihdrOut->pidl)
  534. bRet = TRUE;
  535. }
  536. Done:;
  537. }
  538. _LeaveCS();
  539. return bRet;
  540. }
  541. // Recalculates a cached name structure. This can be an expensive operation
  542. void CBriefcase::_CalcCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi)
  543. {
  544. _EnterCS();
  545. if (_hdpa && _pbrfstg)
  546. {
  547. LPCIDFOLDER pidf = (LPCIDFOLDER)pidl;
  548. IBriefcaseStg *pbrfstg = _pbrfstg;
  549. pbrfstg->AddRef();
  550. // Make sure we're out of the critical section when we call
  551. // the expensive functions!
  552. _LeaveCS();
  553. {
  554. TCHAR szTmp[MAX_PATH];
  555. _CopyName(pidf, szTmp, ARRAYSIZE(szTmp));
  556. pbrfstg->GetExtraInfo(szTmp, GEI_ORIGIN, (WPARAM)ARRAYSIZE(pbi->szOrigin), (LPARAM)pbi->szOrigin);
  557. pbrfstg->GetExtraInfo(szTmp, GEI_STATUS, (WPARAM)ARRAYSIZE(pbi->szStatus), (LPARAM)pbi->szStatus);
  558. }
  559. _EnterCS();
  560. pbrfstg->Release();
  561. // Check again if we are valid
  562. if (_hdpa)
  563. {
  564. // Is the pidl still around so we can update it?
  565. BRFINFOHDR bihdr = {0};
  566. bihdr.pidl = (LPITEMIDLIST)pidf;
  567. int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED);
  568. if (DPA_ERR != idpa)
  569. {
  570. // Yes; update it
  571. BRFINFOHDR * pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa);
  572. ASSERT(!pbihdr->bi.bUpToDate || !pbihdr->bi.bDetermined)
  573. // This entry may have been marked for deletion while the
  574. // expensive calculations were in process above. Check for
  575. // it now.
  576. if (pbihdr->bi.bDeleted)
  577. {
  578. _DeleteCachedName(pidl);
  579. }
  580. else
  581. {
  582. pbihdr->bi = *pbi;
  583. pbihdr->bi.bUpToDate = TRUE;
  584. pbihdr->bi.bDetermined = TRUE;
  585. #ifdef DEBUG
  586. if (!pbi->bDetermined)
  587. _cUndetermined--;
  588. else if (!pbi->bUpToDate)
  589. _cStale--;
  590. else
  591. ASSERT(0);
  592. #endif
  593. }
  594. }
  595. }
  596. }
  597. _LeaveCS();
  598. }
  599. // Finds a cached name structure and marks it stale
  600. // WARNING: pidl can be a fully qualified pidl that is comming through as a change notify
  601. void CBriefcase::_CachedNameIsStale(LPCITEMIDLIST pidl, BOOL bDeleted)
  602. {
  603. _EnterCS();
  604. {
  605. if (_hdpa)
  606. {
  607. BRFINFOHDR bihdr = {0};
  608. bihdr.pidl = ILFindLastID(pidl); // hope this is all ours
  609. int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED);
  610. if (DPA_ERR != idpa)
  611. {
  612. // Yes; mark it stale
  613. BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa);
  614. // Is this cached name pending calculation yet?
  615. if (pbihdr->bi.bDetermined && pbihdr->bi.bUpToDate &&
  616. !pbihdr->bi.bDeleted)
  617. {
  618. // No; signal the calculation thread
  619. if (bDeleted)
  620. {
  621. pbihdr->bi.bDeleted = TRUE;
  622. #ifdef DEBUG
  623. _cDeleted++;
  624. #endif
  625. }
  626. else
  627. {
  628. pbihdr->bi.bUpToDate = FALSE;
  629. #ifdef DEBUG
  630. _cStale++;
  631. #endif
  632. }
  633. // Notify the calculating thread of an item that is pending
  634. // calculation
  635. ReleaseSemaphore(_hSemPending, 1, NULL);
  636. }
  637. else if (bDeleted)
  638. {
  639. // Yes; but mark for deletion anyway
  640. pbihdr->bi.bDeleted = TRUE;
  641. #ifdef DEBUG
  642. _cDeleted++;
  643. #endif
  644. }
  645. }
  646. }
  647. }
  648. _LeaveCS();
  649. }
  650. // Marks all cached name structures stale
  651. void CBriefcase::_AllNamesAreStale()
  652. {
  653. _EnterCS();
  654. {
  655. if (_pbrfstg)
  656. {
  657. UINT uFlags;
  658. // Dirty the briefcase storage cache
  659. _pbrfstg->Notify(NULL, NOE_DIRTYALL, &uFlags, NULL);
  660. }
  661. }
  662. _LeaveCS();
  663. // (It is important that we call CBriefcase::_Reset outside of the critical
  664. // section. Otherwise, we can deadlock when this function is called
  665. // while the secondary thread is calculating (hit F5 twice in a row).)
  666. // Clear the entire expensive data cache
  667. _Reset();
  668. }
  669. // Adds a new item with default values to the extra info list
  670. BOOL CBriefcase::_AddCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi)
  671. {
  672. BOOL bRet = FALSE;
  673. ASSERT(_pbrfstg && _hwndMain && _hMutexDelay);
  674. _EnterCS();
  675. {
  676. if (_hdpa)
  677. {
  678. BRFINFOHDR * pbihdr = (BRFINFOHDR *)LocalAlloc(LPTR, sizeof(*pbihdr));
  679. if (pbihdr)
  680. {
  681. pbihdr->pidl = ILClone(pidl);
  682. if (pbihdr->pidl)
  683. {
  684. int idpa = DPA_AppendPtr(_hdpa, pbihdr);
  685. if (DPA_ERR != idpa)
  686. {
  687. pbihdr->bi.bUpToDate = FALSE;
  688. pbihdr->bi.bDetermined = FALSE;
  689. pbihdr->bi.bDeleted = FALSE;
  690. lstrcpy(pbihdr->bi.szOrigin, g_szDetailsUnknown);
  691. lstrcpy(pbihdr->bi.szStatus, g_szDetailsUnknown);
  692. #ifdef DEBUG
  693. _cUndetermined++;
  694. #endif
  695. DPA_Sort(_hdpa, _CompareIDCallBack, (LPARAM)this);
  696. // Notify the calculating thread of an item that is pending
  697. // calculation
  698. ReleaseSemaphore(_hSemPending, 1, NULL);
  699. *pbi = pbihdr->bi;
  700. bRet = TRUE;
  701. }
  702. else
  703. {
  704. // Failed. Cleanup
  705. ILFree(pbihdr->pidl);
  706. LocalFree((HLOCAL)pbihdr);
  707. }
  708. }
  709. else
  710. {
  711. // Failed. Cleanup
  712. LocalFree((HLOCAL)pbihdr);
  713. }
  714. }
  715. }
  716. }
  717. _LeaveCS();
  718. return bRet;
  719. }
  720. DWORD CBriefcase::_CalcDetailsThread()
  721. {
  722. HANDLE rghObjPending[2] = {_hEventDie, _hSemPending};
  723. HANDLE rghObjDelay[2] = {_hEventDie, _hMutexDelay};
  724. while (TRUE)
  725. {
  726. // Wait for an end event or for a job to do
  727. DWORD dwRet = WaitForMultipleObjects(ARRAYSIZE(rghObjPending), rghObjPending, FALSE, INFINITE);
  728. if (WAIT_OBJECT_0 == dwRet)
  729. {
  730. // Exit thread
  731. break;
  732. }
  733. else
  734. {
  735. #ifdef DEBUG
  736. _EnterCS();
  737. {
  738. ASSERT(0 < _cUndetermined ||
  739. 0 < _cStale ||
  740. 0 < _cDeleted);
  741. }
  742. _LeaveCS();
  743. #endif
  744. // Now wait for an end event or for the delay-calculation mutex
  745. dwRet = WaitForMultipleObjects(ARRAYSIZE(rghObjDelay), rghObjDelay, FALSE, INFINITE);
  746. if (WAIT_OBJECT_0 == dwRet)
  747. {
  748. // Exit thread
  749. break;
  750. }
  751. else
  752. {
  753. // Address deleted entries first
  754. BRFINFOHDR bihdr;
  755. if (_FindNextState(FNS_DELETED, &bihdr))
  756. {
  757. _DeleteCachedName(bihdr.pidl);
  758. ILFree(bihdr.pidl);
  759. }
  760. // Calculate undetermined entries before stale entries
  761. // to fill the view as quickly as possible
  762. else if (_FindNextState(FNS_UNDETERMINED, &bihdr) ||
  763. _FindNextState(FNS_STALE, &bihdr))
  764. {
  765. _CalcCachedName(bihdr.pidl, &bihdr.bi);
  766. #if 1
  767. // ugly way
  768. ShellFolderView_RefreshObject(_hwndMain, &bihdr.pidl);
  769. #else
  770. // right way, but we don't have _punkSite, here, that is on another thread!
  771. IShellFolderView *psfv;
  772. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IShellFolderView, &psfv))))
  773. {
  774. UINT uScratch;
  775. psfv->RefreshObject(&bihdr.pidl, &uScratch);
  776. psfv->Release();
  777. }
  778. #endif
  779. ILFree(bihdr.pidl);
  780. }
  781. else
  782. {
  783. ASSERT(0); // Should never get here
  784. }
  785. ReleaseMutex(_hMutexDelay);
  786. }
  787. }
  788. }
  789. return 0;
  790. }
  791. DWORD CALLBACK CBriefcase::_CalcDetailsThreadProc(void *pv)
  792. {
  793. return ((CBriefcase *)pv)->_CalcDetailsThread();
  794. }
  795. // IShellFolder2::GetDetailsOf
  796. STDMETHODIMP CBriefcase::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails)
  797. {
  798. HRESULT hr = S_OK;
  799. TCHAR szTemp[MAX_PATH];
  800. LPCIDFOLDER pidf = _IsValidID(pidl);
  801. pDetails->str.uType = STRRET_CSTR;
  802. pDetails->str.cStr[0] = 0;
  803. if (!pidf)
  804. {
  805. return GetDetailsOfInfo(s_briefcase_cols, ARRAYSIZE(s_briefcase_cols), iColumn, pDetails);
  806. }
  807. switch (iColumn)
  808. {
  809. case ICOL_BRIEFCASE_NAME:
  810. _CopyName(pidf, szTemp, ARRAYSIZE(szTemp));
  811. hr = StringToStrRet(szTemp, &pDetails->str);
  812. break;
  813. case ICOL_BRIEFCASE_ORIGIN:
  814. case ICOL_BRIEFCASE_STATUS:
  815. // only works if the view callback has set us up for this
  816. if (_pbrfstg)
  817. {
  818. BRFINFO bi;
  819. // Did we find extra info for this file or
  820. // was the new item added to the extra info list?
  821. if (_FindCachedName(pidl, &bi) ||
  822. _AddCachedName(pidl, &bi))
  823. {
  824. LPTSTR psz = ICOL_BRIEFCASE_ORIGIN == iColumn ? bi.szOrigin : bi.szStatus;
  825. hr = StringToStrRet(psz, &pDetails->str);
  826. }
  827. }
  828. break;
  829. case ICOL_BRIEFCASE_SIZE:
  830. if (!_IsFolder(pidf))
  831. {
  832. StrFormatKBSize(pidf->dwSize, szTemp, ARRAYSIZE(szTemp));
  833. hr = StringToStrRet(szTemp, &pDetails->str);
  834. }
  835. break;
  836. case ICOL_BRIEFCASE_TYPE:
  837. _GetTypeNameBuf(pidf, szTemp, ARRAYSIZE(szTemp));
  838. hr = StringToStrRet(szTemp, &pDetails->str);
  839. break;
  840. case ICOL_BRIEFCASE_MODIFIED:
  841. DosTimeToDateTimeString(pidf->dateModified, pidf->timeModified, szTemp, ARRAYSIZE(szTemp), pDetails->fmt & LVCFMT_DIRECTION_MASK);
  842. hr = StringToStrRet(szTemp, &pDetails->str);
  843. break;
  844. }
  845. return hr;
  846. }
  847. // IShellFolder2::MapColumnToSCID
  848. STDMETHODIMP CBriefcase::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
  849. {
  850. return MapColumnToSCIDImpl(s_briefcase_cols, ARRAYSIZE(s_briefcase_cols), iColumn, pscid);
  851. }
  852. // IShellFolder::CompareIDs
  853. STDMETHODIMP CBriefcase::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  854. {
  855. LPCIDFOLDER pidf1 = _IsValidID(pidl1);
  856. LPCIDFOLDER pidf2 = _IsValidID(pidl2);
  857. if (!pidf1 || !pidf2)
  858. {
  859. ASSERT(0);
  860. return E_INVALIDARG;
  861. }
  862. HRESULT hr = _CompareFolderness(pidf1, pidf2);
  863. if (hr != ResultFromShort(0))
  864. return hr;
  865. switch (lParam & SHCIDS_COLUMNMASK)
  866. {
  867. case ICOL_BRIEFCASE_SIZE:
  868. if (pidf1->dwSize < pidf2->dwSize)
  869. return ResultFromShort(-1);
  870. if (pidf1->dwSize > pidf2->dwSize)
  871. return ResultFromShort(1);
  872. goto DoDefault;
  873. case ICOL_BRIEFCASE_TYPE:
  874. hr = _CompareFileTypes(pidf1, pidf2);
  875. if (!hr)
  876. goto DoDefault;
  877. break;
  878. case ICOL_BRIEFCASE_MODIFIED:
  879. hr = _CompareModifiedDate(pidf1, pidf2);
  880. if (!hr)
  881. goto DoDefault;
  882. break;
  883. case ICOL_BRIEFCASE_NAME:
  884. // We need to treat this differently from others bacause
  885. // pidf1/2 might not be simple.
  886. hr = CFSFolder::_CompareNames(pidf1, pidf2, TRUE, FALSE);
  887. // REVIEW: (Possible performance gain with some extra code)
  888. // We should probably avoid bindings by walking down
  889. // the IDList here instead of calling this helper function.
  890. //
  891. if (hr == ResultFromShort(0))
  892. {
  893. hr = ILCompareRelIDs((IShellFolder *)this, pidl1, pidl2, lParam);
  894. }
  895. goto DoDefaultModification;
  896. case ICOL_BRIEFCASE_ORIGIN:
  897. case ICOL_BRIEFCASE_STATUS:
  898. {
  899. BRFINFO bi1, bi2;
  900. BOOL bVal1 = _FindCachedName(pidl1, &bi1);
  901. BOOL bVal2 = _FindCachedName(pidl2, &bi2);
  902. // Do we have this info in our cache?
  903. if (!bVal1 || !bVal2)
  904. {
  905. // No; one or both of them are missing. Have unknowns gravitate
  906. // to the bottom of the list.
  907. // (Don't bother adding them)
  908. if (!bVal1 && !bVal2)
  909. hr = ResultFromShort(0);
  910. else if (!bVal1)
  911. hr = ResultFromShort(1);
  912. else
  913. hr = ResultFromShort(-1);
  914. }
  915. else
  916. {
  917. // Found the info; do a comparison
  918. if (ICOL_BRIEFCASE_ORIGIN == (lParam & SHCIDS_COLUMNMASK))
  919. {
  920. hr = ResultFromShort(lstrcmp(bi1.szOrigin, bi2.szOrigin));
  921. }
  922. else
  923. {
  924. ASSERT(ICOL_BRIEFCASE_STATUS == (lParam & SHCIDS_COLUMNMASK));
  925. hr = ResultFromShort(lstrcmp(bi1.szStatus, bi2.szStatus));
  926. }
  927. }
  928. }
  929. break;
  930. default:
  931. DoDefault:
  932. // Sort it based on the primary (long) name -- ignore case.
  933. {
  934. TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
  935. _CopyName(pidf1, szName1, ARRAYSIZE(szName1));
  936. _CopyName(pidf2, szName2, ARRAYSIZE(szName2));
  937. hr = ResultFromShort(lstrcmpi(szName1, szName2));
  938. }
  939. DoDefaultModification:
  940. if (hr == S_OK && (lParam & SHCIDS_ALLFIELDS))
  941. {
  942. // Must sort by modified date to pick up any file changes!
  943. hr = _CompareModifiedDate(pidf1, pidf2);
  944. if (!hr)
  945. hr = _CompareAttribs(pidf1, pidf2);
  946. }
  947. }
  948. return hr;
  949. }
  950. // This function creates an instance of IShellView.
  951. HRESULT CBriefcase::_CreateView(HWND hwnd, IShellView **ppsv)
  952. {
  953. *ppsv = NULL; // assume failure
  954. HRESULT hr;
  955. CBriefcaseViewCB *pvcb = new CBriefcaseViewCB(this);
  956. if (pvcb)
  957. {
  958. hr = pvcb->_InitStgForDetails();
  959. if (SUCCEEDED(hr))
  960. {
  961. SFV_CREATE sSFV = {0};
  962. hr = pvcb->QueryInterface(IID_PPV_ARG(IShellFolderViewCB, &sSFV.psfvcb));
  963. if (SUCCEEDED(hr))
  964. {
  965. sSFV.cbSize = sizeof(sSFV);
  966. sSFV.pshf = (IShellFolder *)this;
  967. hr = SHCreateShellFolderView(&sSFV, ppsv);
  968. }
  969. }
  970. pvcb->Release();
  971. }
  972. else
  973. {
  974. hr = E_OUTOFMEMORY;
  975. }
  976. return hr;
  977. }
  978. // IShellFolder::CreateViewObject
  979. STDMETHODIMP CBriefcase::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  980. {
  981. HRESULT hr;
  982. if (IsEqualIID(riid, IID_IShellView))
  983. {
  984. hr = _CreateView(hwnd, (IShellView **)ppv);
  985. }
  986. else
  987. {
  988. // delegate to base class
  989. hr = CFSFolder::CreateViewObject(hwnd, riid, ppv);
  990. }
  991. ASSERT(FAILED(hr) ? (NULL == *ppv) : TRUE);
  992. return hr;
  993. }
  994. // IShellFolder::GetAttributesOf
  995. STDMETHODIMP CBriefcase::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG * prgfInOut)
  996. {
  997. // Validate this pidl?
  998. if (*prgfInOut & SFGAO_VALIDATE)
  999. {
  1000. // Yes; dirty the briefcase storage entry by sending an update
  1001. // notification
  1002. DebugMsg(DM_TRACE, TEXT("Briefcase: Receiving F5, dirty entire briefcase storage"));
  1003. _AllNamesAreStale();
  1004. }
  1005. // delegate to base
  1006. return CFSFolder::GetAttributesOf(cidl, apidl, prgfInOut);
  1007. }
  1008. // IShellFolder::GetUIObjectOf
  1009. STDMETHODIMP CBriefcase::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  1010. REFIID riid, UINT *prgfInOut, void **ppv)
  1011. {
  1012. HRESULT hr;
  1013. if (cidl > 0 && IsEqualIID(riid, IID_IDataObject))
  1014. {
  1015. // Create an IDataObject interface instance with our
  1016. // own vtable because we support the CFSTR_BRIEFOBJECT clipboard format
  1017. hr = CBrfData_CreateDataObj(_pidl, cidl, (LPCITEMIDLIST *)apidl, (IDataObject **)ppv);
  1018. }
  1019. else
  1020. {
  1021. // delegate to base class
  1022. hr = CFSFolder::GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv);
  1023. }
  1024. return hr;
  1025. }
  1026. // CFSBrfFolder constructor
  1027. STDAPI CFSBrfFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  1028. {
  1029. HRESULT hr;
  1030. CBriefcase *pbf = new CBriefcase(punkOuter);
  1031. if (pbf)
  1032. {
  1033. hr = pbf->QueryInterface(riid, ppv);
  1034. pbf->Release();
  1035. }
  1036. else
  1037. {
  1038. *ppv = NULL;
  1039. hr = E_OUTOFMEMORY;
  1040. }
  1041. return hr;
  1042. }
  1043. const TBBUTTON c_tbBrfCase[] = {
  1044. { 0, FSIDM_UPDATEALL, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
  1045. { 1, FSIDM_UPDATESELECTION, 0, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
  1046. { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP , {0,0}, 0L, -1 },
  1047. };
  1048. #define BRFVIEW_EVENTS \
  1049. SHCNE_DISKEVENTS | \
  1050. SHCNE_ASSOCCHANGED | \
  1051. SHCNE_GLOBALEVENTS
  1052. HRESULT CBriefcaseViewCB::OnWINDOWCREATED(DWORD pv, HWND hwndView)
  1053. {
  1054. SHChangeNotifyEntry fsne;
  1055. ASSERT(_pbrfstg && _hwndMain && _hMutexDelay); // from the init call
  1056. // view hands folder info needed to details (status, sync path)
  1057. _pfolder->_InitDetailsInfoAndThread(_pbrfstg, _hwndMain, _hMutexDelay);
  1058. // Register an extra SHChangeNotifyRegister for our pidl to try to catch things
  1059. // like UpdateDir
  1060. fsne.pidl = _FolderPidl();
  1061. fsne.fRecursive = FALSE;
  1062. _uSCNRExtra = SHChangeNotifyRegister(hwndView, SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
  1063. SHCNE_DISKEVENTS, WM_DSV_FSNOTIFY, 1, &fsne);
  1064. return S_OK;
  1065. }
  1066. HRESULT CBriefcaseViewCB::OnWINDOWDESTROY(DWORD pv, HWND wP)
  1067. {
  1068. _pfolder->_Free();
  1069. // need to release pbrfstg as well
  1070. if (_pbrfstg)
  1071. {
  1072. _pbrfstg->Release();
  1073. _pbrfstg = NULL;
  1074. _hMutexDelay = NULL; // invalidate our alias
  1075. }
  1076. if (_uSCNRExtra)
  1077. {
  1078. SHChangeNotifyDeregister(_uSCNRExtra);
  1079. _uSCNRExtra = 0;
  1080. }
  1081. return S_OK;
  1082. }
  1083. HRESULT CBriefcaseViewCB::OnMergeMenu(DWORD pv, QCMINFO *pinfo)
  1084. {
  1085. // Merge the briefcase menu onto the menu that CDefView created.
  1086. if (pinfo->hmenu)
  1087. {
  1088. HMENU hmSync = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(POPUP_BRIEFCASE));
  1089. if (hmSync)
  1090. {
  1091. Shell_MergeMenus(pinfo->hmenu, hmSync, pinfo->indexMenu,
  1092. pinfo->idCmdFirst, pinfo->idCmdLast, MM_SUBMENUSHAVEIDS);
  1093. DestroyMenu(hmSync);
  1094. }
  1095. }
  1096. return S_OK;
  1097. }
  1098. HRESULT CBriefcaseViewCB::_GetSelectedObjects(IDataObject **ppdtobj)
  1099. {
  1100. IFolderView *pfv;
  1101. HRESULT hr = IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv));
  1102. if (SUCCEEDED(hr))
  1103. {
  1104. hr = pfv->Items(SVGIO_SELECTION, IID_PPV_ARG(IDataObject, ppdtobj));
  1105. pfv->Release();
  1106. }
  1107. return hr;
  1108. }
  1109. HRESULT CBriefcaseViewCB::OnINVOKECOMMAND(DWORD pv, UINT uID)
  1110. {
  1111. IDataObject *pdtobj;
  1112. switch (uID)
  1113. {
  1114. case FSIDM_UPDATEALL:
  1115. // Update the entire briefcase
  1116. if (SUCCEEDED(SHGetUIObjectFromFullPIDL(_pidlRoot, NULL, IID_PPV_ARG(IDataObject, &pdtobj))))
  1117. {
  1118. _pbrfstg->UpdateObject(pdtobj, _hwndMain);
  1119. pdtobj->Release();
  1120. }
  1121. break;
  1122. case FSIDM_UPDATESELECTION:
  1123. // Update the selected objects
  1124. if (SUCCEEDED(_GetSelectedObjects(&pdtobj)))
  1125. {
  1126. _pbrfstg->UpdateObject(pdtobj, _hwndMain);
  1127. pdtobj->Release();
  1128. }
  1129. break;
  1130. case FSIDM_SPLIT:
  1131. // Split the selected objects
  1132. if (SUCCEEDED(_GetSelectedObjects(&pdtobj)))
  1133. {
  1134. _pbrfstg->ReleaseObject(pdtobj, _hwndMain);
  1135. pdtobj->Release();
  1136. }
  1137. break;
  1138. }
  1139. return S_OK;
  1140. }
  1141. HRESULT CBriefcaseViewCB::OnGetHelpOrTooltipText(BOOL bHelp, UINT wPl, UINT cch, LPTSTR psz)
  1142. {
  1143. LoadString(HINST_THISDLL, wPl + (bHelp ? IDS_MH_FSIDM_FIRST : IDS_TT_FSIDM_FIRST), psz, cch);
  1144. return S_OK;
  1145. }
  1146. int CBriefcaseViewCB::_GetSelectedCount()
  1147. {
  1148. int cItems = 0;
  1149. IFolderView *pfv;
  1150. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv))))
  1151. {
  1152. pfv->ItemCount(SVGIO_SELECTION, &cItems);
  1153. pfv->Release();
  1154. }
  1155. return cItems;
  1156. }
  1157. HRESULT CBriefcaseViewCB::OnINITMENUPOPUP(DWORD pv, UINT idCmdFirst, UINT nIndex, HMENU hmenu)
  1158. {
  1159. BOOL bEnabled = _GetSelectedCount() > 0;
  1160. EnableMenuItem(hmenu, idCmdFirst+FSIDM_UPDATESELECTION, bEnabled ? MF_ENABLED : MF_GRAYED);
  1161. EnableMenuItem(hmenu, idCmdFirst+FSIDM_SPLIT, bEnabled ? MF_ENABLED : MF_GRAYED);
  1162. return S_OK;
  1163. }
  1164. HRESULT CBriefcaseViewCB::OnGETBUTTONINFO(DWORD pv, TBINFO* ptbinfo)
  1165. {
  1166. ptbinfo->cbuttons = ARRAYSIZE(c_tbBrfCase);
  1167. ptbinfo->uFlags = TBIF_PREPEND;
  1168. return S_OK;
  1169. }
  1170. HRESULT CBriefcaseViewCB::OnGETBUTTONS(DWORD pv, UINT idCmdFirst, UINT wPh, TBBUTTON *ptbbutton)
  1171. {
  1172. IShellBrowser* psb;
  1173. HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
  1174. if (SUCCEEDED(hr))
  1175. {
  1176. LRESULT iBtnOffset;
  1177. TBADDBITMAP ab;
  1178. // add the toolbar button bitmap, get it's offset
  1179. ab.hInst = HINST_THISDLL;
  1180. ab.nID = IDB_BRF_TB_SMALL; // std bitmaps
  1181. psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 2, (LPARAM)&ab, &iBtnOffset);
  1182. for (int i = 0; i < ARRAYSIZE(c_tbBrfCase); i++)
  1183. {
  1184. ptbbutton[i] = c_tbBrfCase[i];
  1185. if (!(c_tbBrfCase[i].fsStyle & TBSTYLE_SEP))
  1186. {
  1187. ptbbutton[i].idCommand += idCmdFirst;
  1188. ptbbutton[i].iBitmap += (int) iBtnOffset;
  1189. }
  1190. }
  1191. psb->Release();
  1192. }
  1193. return S_OK;
  1194. }
  1195. HRESULT CBriefcaseViewCB::OnSELCHANGE(DWORD pv, UINT idCmdFirst, UINT wPh, SFVM_SELCHANGE_DATA*lP)
  1196. {
  1197. IShellBrowser* psb;
  1198. HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
  1199. if (SUCCEEDED(hr))
  1200. {
  1201. psb->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON,
  1202. idCmdFirst + FSIDM_UPDATESELECTION,
  1203. (LPARAM)(_GetSelectedCount() > 0), NULL);
  1204. psb->Release();
  1205. }
  1206. return E_FAIL; // (we did not update the status area)
  1207. }
  1208. HRESULT CBriefcaseViewCB::OnQUERYFSNOTIFY(DWORD pv, SHChangeNotifyEntry *pfsne)
  1209. {
  1210. // Register to receive global events
  1211. pfsne->pidl = NULL;
  1212. pfsne->fRecursive = TRUE;
  1213. return NOERROR;
  1214. }
  1215. HRESULT CBriefcaseViewCB::_HandleFSNotifyForDefView(LPARAM lEvent, LPCITEMIDLIST * ppidl, LPTSTR pszBuf)
  1216. {
  1217. HRESULT hr;
  1218. switch (lEvent)
  1219. {
  1220. case SHCNE_RENAMEITEM:
  1221. case SHCNE_RENAMEFOLDER:
  1222. if (!ILIsParent(_FolderPidl(), ppidl[0], TRUE))
  1223. {
  1224. // move to this folder
  1225. hr = _HandleFSNotifyForDefView(SHCNE_CREATE, &ppidl[1], pszBuf);
  1226. }
  1227. else if (!ILIsParent(_FolderPidl(), ppidl[1], TRUE))
  1228. {
  1229. // move from this folder
  1230. hr = _HandleFSNotifyForDefView(SHCNE_DELETE, &ppidl[0], pszBuf);
  1231. }
  1232. else
  1233. {
  1234. // have the defview handle it
  1235. _pfolder->_CachedNameIsStale(ppidl[0], TRUE);
  1236. hr = NOERROR;
  1237. }
  1238. break;
  1239. case SHCNE_DELETE:
  1240. case SHCNE_RMDIR:
  1241. _pfolder->_CachedNameIsStale(ppidl[0], TRUE);
  1242. hr = NOERROR;
  1243. break;
  1244. default:
  1245. hr = NOERROR;
  1246. break;
  1247. }
  1248. return hr;
  1249. }
  1250. // Converts a shell change notify event to a briefcase storage event.
  1251. LONG NOEFromSHCNE(LPARAM lEvent)
  1252. {
  1253. switch (lEvent)
  1254. {
  1255. case SHCNE_RENAMEITEM: return NOE_RENAME;
  1256. case SHCNE_RENAMEFOLDER: return NOE_RENAMEFOLDER;
  1257. case SHCNE_CREATE: return NOE_CREATE;
  1258. case SHCNE_MKDIR: return NOE_CREATEFOLDER;
  1259. case SHCNE_DELETE: return NOE_DELETE;
  1260. case SHCNE_RMDIR: return NOE_DELETEFOLDER;
  1261. case SHCNE_UPDATEITEM: return NOE_DIRTY;
  1262. case SHCNE_UPDATEDIR: return NOE_DIRTYFOLDER;
  1263. default: return 0;
  1264. }
  1265. }
  1266. HRESULT CBriefcaseViewCB::OnFSNOTIFY(DWORD pv, LPCITEMIDLIST *ppidl, LPARAM lEvent)
  1267. {
  1268. HRESULT hr;
  1269. TCHAR szPath[MAX_PATH * 2];
  1270. // we are in the process of being freed, but changenotify's can still come in because we are not freed atomically
  1271. if (!_pbrfstg)
  1272. {
  1273. return S_FALSE;
  1274. }
  1275. if (lEvent == SHCNE_UPDATEIMAGE || lEvent == SHCNE_FREESPACE)
  1276. {
  1277. return S_FALSE;
  1278. }
  1279. if (ppidl && !ILIsEmpty(ppidl[0]) && SHGetPathFromIDList(ppidl[0], szPath))
  1280. {
  1281. UINT uFlags;
  1282. LONG lEventNOE;
  1283. if ((SHCNE_RENAMEFOLDER == lEvent) || (SHCNE_RENAMEITEM == lEvent))
  1284. {
  1285. ASSERT(ppidl[1]);
  1286. ASSERT(ARRAYSIZE(szPath) >= lstrlen(szPath)*2); // rough estimate
  1287. // Tack the new name after the old name, separated by the null
  1288. SHGetPathFromIDList(ppidl[1], &szPath[lstrlen(szPath)+1]);
  1289. }
  1290. // Tell the briefcase the path has potentially changed
  1291. lEventNOE = NOEFromSHCNE(lEvent);
  1292. _pbrfstg->Notify(szPath, lEventNOE, &uFlags, _hwndMain);
  1293. // Was this item marked?
  1294. if (uFlags & NF_ITEMMARKED)
  1295. {
  1296. // Yes; mark it stale in the expensive cache
  1297. _pfolder->_CachedNameIsStale(ppidl[0], FALSE);
  1298. }
  1299. // Does the window need to be refreshed?
  1300. if (uFlags & NF_REDRAWWINDOW)
  1301. {
  1302. // Yes
  1303. IShellView *psv;
  1304. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IShellView, &psv))))
  1305. {
  1306. psv->Refresh();
  1307. psv->Release();
  1308. }
  1309. }
  1310. // Did this event occur in this folder?
  1311. if (NULL == ppidl ||
  1312. ILIsParent(_FolderPidl(), ppidl[0], TRUE) ||
  1313. (((SHCNE_RENAMEITEM == lEvent) || (SHCNE_RENAMEFOLDER == lEvent)) && ILIsParent(_FolderPidl(), ppidl[1], TRUE)) ||
  1314. (SHCNE_UPDATEDIR == lEvent && ILIsEqual(_FolderPidl(), ppidl[0])))
  1315. {
  1316. // Yes; deal with it
  1317. hr = _HandleFSNotifyForDefView(lEvent, ppidl, szPath);
  1318. }
  1319. else
  1320. {
  1321. // No
  1322. hr = S_FALSE;
  1323. }
  1324. }
  1325. else
  1326. {
  1327. // ASSERT(0);
  1328. hr = S_FALSE;
  1329. }
  1330. return hr;
  1331. }
  1332. HRESULT CBriefcaseViewCB::OnQUERYCOPYHOOK(DWORD pv)
  1333. {
  1334. return S_OK;
  1335. }
  1336. HRESULT CBriefcaseViewCB::OnNOTIFYCOPYHOOK(DWORD pv, COPYHOOKINFO *pchi)
  1337. {
  1338. HRESULT hr = NOERROR;
  1339. // Is this a pertinent operation?
  1340. if (FO_MOVE == pchi->wFunc ||
  1341. FO_RENAME == pchi->wFunc ||
  1342. FO_DELETE == pchi->wFunc)
  1343. {
  1344. // Yes; don't allow the briefcase root or a parent folder to get moved
  1345. // while the briefcase is still open. (The database is locked while
  1346. // the briefcase is open, and will fail the move/rename operation
  1347. // in an ugly way.)
  1348. LPITEMIDLIST pidl = ILCreateFromPath(pchi->pszSrcFile);
  1349. if (pidl)
  1350. {
  1351. // Is the folder that is being moved or renamed a parent or equal
  1352. // of the Briefcase root?
  1353. if (ILIsParent(pidl, _pidlRoot, FALSE) ||
  1354. ILIsEqual(pidl, _pidlRoot))
  1355. {
  1356. // Yes; don't allow it until the briefcase is closed.
  1357. int ids;
  1358. if (FO_MOVE == pchi->wFunc ||
  1359. FO_RENAME == pchi->wFunc)
  1360. {
  1361. ids = IDS_MOVEBRIEFCASE;
  1362. }
  1363. else
  1364. {
  1365. ASSERT(FO_DELETE == pchi->wFunc);
  1366. ids = IDS_DELETEBRIEFCASE;
  1367. }
  1368. ShellMessageBox(HINST_THISDLL, _hwndMain,
  1369. MAKEINTRESOURCE(ids), NULL, MB_OK | MB_ICONINFORMATION);
  1370. hr = IDCANCEL;
  1371. }
  1372. ILFree(pidl);
  1373. }
  1374. }
  1375. return hr;
  1376. }
  1377. HRESULT CBriefcaseViewCB::OnINSERTITEM(DWORD pv, LPCITEMIDLIST pidl)
  1378. {
  1379. HRESULT hr;
  1380. TCHAR szPath[MAX_PATH];
  1381. if (SHGetPathFromIDList(pidl, szPath))
  1382. {
  1383. // Always hide the desktop.ini and the database file.
  1384. LPTSTR pszName = PathFindFileName(szPath);
  1385. if (0 == lstrcmpi(pszName, c_szDesktopIni) ||
  1386. 0 == lstrcmpi(pszName, _szDBName))
  1387. hr = S_FALSE; // don't add
  1388. else
  1389. hr = S_OK;
  1390. }
  1391. else
  1392. hr = S_OK; // Let it be added...
  1393. return hr;
  1394. }
  1395. HRESULT CBriefcaseViewCB::OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE*lP)
  1396. {
  1397. *lP = FVM_DETAILS;
  1398. return S_OK;
  1399. }
  1400. HRESULT CBriefcaseViewCB::OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA * phtd)
  1401. {
  1402. if (IsOS(OS_ANYSERVER))
  1403. {
  1404. StrCpyW(phtd->wszHelpFile, L"brief.chm");
  1405. }
  1406. else
  1407. {
  1408. StrCpyW(phtd->wszHelpTopic, L"hcp://services/subsite?node=Unmapped/Briefcase");
  1409. }
  1410. return S_OK;
  1411. }
  1412. CBriefcaseViewCB::CBriefcaseViewCB(CBriefcase *pfolder) : CBaseShellFolderViewCB(pfolder->_pidl, BRFVIEW_EVENTS), _pfolder(pfolder)
  1413. {
  1414. _pfolder->AddRef();
  1415. }
  1416. CBriefcaseViewCB::~CBriefcaseViewCB()
  1417. {
  1418. if (_pbrfstg)
  1419. _pbrfstg->Release();
  1420. if (_pidlRoot)
  1421. ILFree(_pidlRoot);
  1422. _pfolder->Release();
  1423. }
  1424. HRESULT CBriefcaseViewCB::_InitStgForDetails()
  1425. {
  1426. ASSERT(NULL == _pbrfstg);
  1427. HRESULT hr = CreateBrfStgFromIDList(_FolderPidl(), _hwndMain, &_pbrfstg);
  1428. if (SUCCEEDED(hr))
  1429. {
  1430. ASSERT(NULL == _hMutexDelay);
  1431. _pbrfstg->GetExtraInfo(NULL, GEI_DELAYHANDLE, 0, (LPARAM)&_hMutexDelay);
  1432. ASSERT(0 == _szDBName[0]);
  1433. _pbrfstg->GetExtraInfo(NULL, GEI_DATABASENAME, ARRAYSIZE(_szDBName), (LPARAM)_szDBName);
  1434. TCHAR szPath[MAX_PATH];
  1435. hr = _pbrfstg->GetExtraInfo(NULL, GEI_ROOT, (WPARAM)ARRAYSIZE(szPath), (LPARAM)szPath);
  1436. if (SUCCEEDED(hr))
  1437. hr = SHILCreateFromPath(szPath, &_pidlRoot, NULL);
  1438. }
  1439. return hr;
  1440. }
  1441. STDMETHODIMP CBriefcaseViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  1442. {
  1443. switch (uMsg)
  1444. {
  1445. HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWINDOWCREATED);
  1446. HANDLE_MSG(0, SFVM_WINDOWDESTROY, OnWINDOWDESTROY);
  1447. HANDLE_MSG(0, SFVM_MERGEMENU, OnMergeMenu);
  1448. HANDLE_MSG(0, SFVM_INVOKECOMMAND, OnINVOKECOMMAND);
  1449. HANDLE_MSG(TRUE , SFVM_GETHELPTEXT , OnGetHelpOrTooltipText);
  1450. HANDLE_MSG(FALSE, SFVM_GETTOOLTIPTEXT, OnGetHelpOrTooltipText);
  1451. HANDLE_MSG(0, SFVM_INITMENUPOPUP, OnINITMENUPOPUP);
  1452. HANDLE_MSG(0, SFVM_GETBUTTONINFO, OnGETBUTTONINFO);
  1453. HANDLE_MSG(0, SFVM_GETBUTTONS, OnGETBUTTONS);
  1454. HANDLE_MSG(0, SFVM_SELCHANGE, OnSELCHANGE);
  1455. HANDLE_MSG(0, SFVM_QUERYFSNOTIFY, OnQUERYFSNOTIFY);
  1456. HANDLE_MSG(0, SFVM_FSNOTIFY, OnFSNOTIFY);
  1457. HANDLE_MSG(0, SFVM_QUERYCOPYHOOK, OnQUERYCOPYHOOK);
  1458. HANDLE_MSG(0, SFVM_NOTIFYCOPYHOOK, OnNOTIFYCOPYHOOK);
  1459. HANDLE_MSG(0, SFVM_INSERTITEM, OnINSERTITEM);
  1460. HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDEFVIEWMODE);
  1461. HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages);
  1462. HANDLE_MSG(0, SFVM_GETHELPTOPIC, OnGetHelpTopic);
  1463. HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout);
  1464. HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent);
  1465. HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks);
  1466. HANDLE_MSG(0, SFVM_DELAYWINDOWCREATE, OnDELAYWINDOWCREATE);
  1467. default:
  1468. return E_FAIL;
  1469. }
  1470. return NOERROR;
  1471. }
  1472. STDAPI CreateBrfStgFromPath(LPCTSTR pszPath, HWND hwnd, IBriefcaseStg **ppbs)
  1473. {
  1474. IBriefcaseStg *pbrfstg;
  1475. HRESULT hr = CoCreateInstance(CLSID_Briefcase, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBriefcaseStg, &pbrfstg));
  1476. if (SUCCEEDED(hr))
  1477. {
  1478. hr = pbrfstg->Initialize(pszPath, hwnd);
  1479. if (SUCCEEDED(hr))
  1480. {
  1481. hr = pbrfstg->QueryInterface(IID_PPV_ARG(IBriefcaseStg, ppbs));
  1482. }
  1483. pbrfstg->Release();
  1484. }
  1485. return hr;
  1486. }
  1487. STDAPI CreateBrfStgFromIDList(LPCITEMIDLIST pidl, HWND hwnd, IBriefcaseStg **ppbs)
  1488. {
  1489. HRESULT hr = E_FAIL;
  1490. // Create an instance of IBriefcaseStg
  1491. TCHAR szFolder[MAX_PATH];
  1492. if (SHGetPathFromIDList(pidl, szFolder))
  1493. {
  1494. hr = CreateBrfStgFromPath(szFolder, hwnd, ppbs);
  1495. }
  1496. return hr;
  1497. }
  1498. HRESULT CBriefcaseViewCB::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData)
  1499. {
  1500. ZeroMemory(pData, sizeof(*pData));
  1501. pData->dwLayout = SFVMWVL_NORMAL | SFVMWVL_FILES;
  1502. return S_OK;
  1503. }
  1504. HRESULT CBriefcaseViewCB::_OnUpdate(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  1505. {
  1506. CBriefcaseViewCB* pThis = (CBriefcaseViewCB*)(void*)pv;
  1507. IDataObject *pdo;
  1508. HRESULT hr = S_OK;
  1509. if (!psiItemArray)
  1510. {
  1511. IFolderView *pfv;
  1512. hr = IUnknown_QueryService(pThis->_punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv));
  1513. if (SUCCEEDED(hr))
  1514. {
  1515. hr = pfv->Items(SVGIO_ALLVIEW, IID_PPV_ARG(IDataObject, &pdo));
  1516. pfv->Release();
  1517. }
  1518. }
  1519. else
  1520. {
  1521. hr = psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo));
  1522. }
  1523. if (SUCCEEDED(hr))
  1524. {
  1525. hr = SHInvokeCommandOnDataObject(pThis->_hwndMain, NULL, pdo, 0, "update");
  1526. pdo->Release();
  1527. }
  1528. return hr;
  1529. }
  1530. const WVTASKITEM c_BriefcaseTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_BRIEFCASE, IDS_HEADER_BRIEFCASE_TT);
  1531. const WVTASKITEM c_BriefcaseTaskList[] =
  1532. {
  1533. 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),
  1534. };
  1535. HRESULT CBriefcaseViewCB::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData)
  1536. {
  1537. ZeroMemory(pData, sizeof(*pData));
  1538. Create_IUIElement(&c_BriefcaseTaskHeader, &(pData->pSpecialTaskHeader));
  1539. return S_OK;
  1540. }
  1541. HRESULT CBriefcaseViewCB::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks)
  1542. {
  1543. ZeroMemory(pTasks, sizeof(*pTasks));
  1544. Create_IEnumUICommand((IUnknown*)(void*)this, c_BriefcaseTaskList, ARRAYSIZE(c_BriefcaseTaskList), &pTasks->penumSpecialTasks);
  1545. return S_OK;
  1546. }
  1547. HRESULT CBriefcaseViewCB::OnDELAYWINDOWCREATE(DWORD pv, HWND hwnd)
  1548. {
  1549. TCHAR szPath[MAX_PATH];
  1550. _pfolder->_GetPath(szPath);
  1551. PathAppend(szPath, c_szDesktopIni);
  1552. BOOL bRunWizard = GetPrivateProfileInt(STRINI_CLASSINFO, TEXT("RunWizard"), 0, szPath);
  1553. // Run the wizard?
  1554. if (bRunWizard)
  1555. {
  1556. // work around old bug where FILE_ATTRIBUTE_READONLY was set
  1557. SetFileAttributes(szPath, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
  1558. // Delete the .ini entry
  1559. WritePrivateProfileString(STRINI_CLASSINFO, TEXT("RunWizard"), NULL, szPath);
  1560. SHRunDLLThread(hwnd, TEXT("SYNCUI.DLL,Briefcase_Intro"), SW_SHOW);
  1561. }
  1562. return S_OK;
  1563. }