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.

747 lines
23 KiB

  1. #include "priv.h"
  2. #include "sccls.h"
  3. #include "nscband.h"
  4. #include "resource.h"
  5. #include "uemapp.h" // KMTF: Included for instrumentation
  6. #include "shlguid.h"
  7. #include <dpa.h>
  8. #include <mluisupp.h>
  9. #include "varutil.h"
  10. #include "apithk.h"
  11. #define TF_EXPLORERBAND 0
  12. typedef struct
  13. {
  14. LPITEMIDLIST pidl;
  15. IShellFolder *psf;
  16. } SFCITEM;
  17. class CExplorerBand : public CNSCBand,
  18. public IDispatch
  19. {
  20. public:
  21. // *** IUnknown ***
  22. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  23. STDMETHODIMP_(ULONG) AddRef(void) { return CNSCBand::AddRef(); };
  24. STDMETHODIMP_(ULONG) Release(void) { return CNSCBand::Release(); };
  25. // *** IOleCommandTarget methods ***
  26. STDMETHODIMP QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext);
  27. STDMETHODIMP Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut);
  28. // *** IDockingWindow methods ***
  29. STDMETHODIMP CloseDW(DWORD dw);
  30. STDMETHODIMP ShowDW(BOOL fShow);
  31. // *** IObjectWithSite methods ***
  32. STDMETHODIMP SetSite(IUnknown* punkSite);
  33. // *** INamespaceProxy methods ***
  34. STDMETHODIMP Invoke(LPCITEMIDLIST pidl);
  35. STDMETHODIMP OnSelectionChanged(LPCITEMIDLIST pidl);
  36. STDMETHODIMP CacheItem(LPCITEMIDLIST pidl) {_MaybeAddToLegacySFC(pidl); return S_OK;}
  37. // *** IDispatch methods ***
  38. STDMETHODIMP GetTypeInfoCount(UINT *pctinfo) {return E_NOTIMPL;}
  39. STDMETHODIMP GetTypeInfo(UINT itinfo,LCID lcid,ITypeInfo **pptinfo) {return E_NOTIMPL;}
  40. STDMETHODIMP GetIDsOfNames(REFIID riid,OLECHAR **rgszNames,UINT cNames, LCID lcid, DISPID * rgdispid) {return E_NOTIMPL;}
  41. STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
  42. DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo, UINT *puArgErr);
  43. protected:
  44. CExplorerBand() : _fCanSelect(TRUE), _fIgnoreSelection(TRUE)
  45. {}
  46. virtual ~CExplorerBand();
  47. virtual HRESULT _TranslatePidl(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlTarget, ULONG *pulAttrib);
  48. virtual BOOL _ShouldNavigateToPidl(LPCITEMIDLIST pidl, ULONG ulAttrib);
  49. virtual HRESULT _InitializeNsc();
  50. virtual DWORD _GetTVStyle();
  51. virtual DWORD _GetTVExStyle();
  52. virtual DWORD _GetEnumFlags();
  53. void _MaybeAddToLegacySFC(LPCITEMIDLIST pidl);
  54. void _AddToLegacySFC(LPCITEMIDLIST pidl, IShellFolder *psf);
  55. BOOL _IsInSFC(LPCITEMIDLIST pidl);
  56. BOOL _IsFloppy(LPCITEMIDLIST pidl);
  57. void _OnNavigate();
  58. HRESULT _ConnectToBrowser(BOOL fConnect);
  59. HRESULT _BrowserExec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut);
  60. friend HRESULT CExplorerBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi);
  61. static void s_DVEnumReadyCallback(void *pvData);
  62. CDSA<SFCITEM> *_pdsaLegacySFC;
  63. DWORD _dwcpCookie;
  64. LPITEMIDLIST _pidlView; //pidl view is navigated to
  65. BOOL _fCanSelect;
  66. BOOL _fIgnoreSelection; //so we don't navigate away from the web page when user opens explorer pane
  67. BOOL _fFloppyRefresh;
  68. };
  69. HRESULT CExplorerBand::_BrowserExec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  70. {
  71. return IUnknown_QueryServiceExec(_punkSite, SID_STopLevelBrowser, pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
  72. }
  73. HRESULT _UnwrapRootedPidl(LPCITEMIDLIST pidlRooted, BOOL bOnlyIfRooted, LPITEMIDLIST *ppidl)
  74. {
  75. HRESULT hr = E_FAIL;
  76. if (ILIsRooted(pidlRooted))
  77. {
  78. hr = SHILCombine(ILRootedFindIDList(pidlRooted), _ILNext(pidlRooted), ppidl);
  79. }
  80. else if (!bOnlyIfRooted)
  81. {
  82. hr = SHILClone(pidlRooted, ppidl);
  83. }
  84. return hr;
  85. }
  86. BOOL IsFTPPidl(LPCITEMIDLIST pidl)
  87. {
  88. BOOL fIsFTP = FALSE;
  89. IShellFolder * psf;
  90. if (pidl && SUCCEEDED(IEBindToObject(pidl, &psf)))
  91. {
  92. fIsFTP = IsFTPFolder(psf);
  93. psf->Release();
  94. }
  95. return fIsFTP;
  96. }
  97. void CExplorerBand::_OnNavigate()
  98. {
  99. IBrowserService* pbs;
  100. HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &pbs));
  101. if (SUCCEEDED(hr))
  102. {
  103. LPITEMIDLIST pidl;
  104. hr = pbs->GetPidl(&pidl);
  105. if (SUCCEEDED(hr))
  106. {
  107. LPITEMIDLIST pidlNew;
  108. hr = _UnwrapRootedPidl(pidl, FALSE, &pidlNew);
  109. if (SUCCEEDED(hr))
  110. {
  111. // We must go in this code path if the pidl is an FTP pidl. FTP pidls can contain
  112. // passwords so it needs to replace any existing pidl. Whistler #252206.
  113. if (!_pidlView || !ILIsEqual(pidlNew, _pidlView) || IsFTPPidl(pidlNew))
  114. {
  115. DWORD dwAttributes = SFGAO_FOLDER;
  116. // only let folders go through (to filter out Web pages)
  117. hr = IEGetAttributesOf(pidlNew, &dwAttributes);
  118. if (SUCCEEDED(hr) && (dwAttributes & SFGAO_FOLDER))
  119. {
  120. BOOL fExpand = (_pidlView == NULL); //the very first time we expand the folder the view is navigated to
  121. Pidl_Set(&_pidlView, pidlNew);
  122. _fIgnoreSelection = FALSE; //in the web page case we don't come here because the page does not have folder attribute
  123. if (_fCanSelect)
  124. {
  125. if (fExpand)
  126. {
  127. VARIANT var;
  128. hr = InitVariantFromIDList(&var, _pidlView);
  129. if (SUCCEEDED(hr))
  130. {
  131. IShellNameSpace *psns;
  132. hr = _pns->QueryInterface(IID_PPV_ARG(IShellNameSpace, &psns));
  133. if (SUCCEEDED(hr))
  134. {
  135. psns->Expand(var, 1);
  136. psns->Release();
  137. }
  138. VariantClear(&var);
  139. }
  140. }
  141. else
  142. {
  143. _pns->SetSelectedItem(_pidlView, TRUE, FALSE, 0);
  144. }
  145. }
  146. }
  147. }
  148. // view navigation is asynchronous so we don't know if it failed in OnSelectionChanged
  149. // but the view is getting navigated to the old pidl and _fCanSelect is false (which happens after we try
  150. // to navigate the view) so it is safe to assume that navigation failed.
  151. // we need to update the selection to match the view
  152. else if (ILIsEqual(pidlNew, _pidlView) && !_fCanSelect)
  153. {
  154. _pns->SetSelectedItem(_pidlView, TRUE, FALSE, 0);
  155. }
  156. _fCanSelect = TRUE;
  157. ILFree(pidlNew);
  158. }
  159. ILFree(pidl);
  160. }
  161. pbs->Release();
  162. }
  163. if (FAILED(hr))
  164. {
  165. Pidl_Set(&_pidlView, NULL);
  166. }
  167. }
  168. HRESULT CExplorerBand::Invoke(DISPID dispidMember, REFIID riid,LCID lcid, WORD wFlags,
  169. DISPPARAMS *pdispparams, VARIANT *pvarResult,
  170. EXCEPINFO *pexcepinfo, UINT *puArgErr)
  171. {
  172. HRESULT hr = S_OK;
  173. if (!pdispparams)
  174. return E_INVALIDARG;
  175. switch(dispidMember)
  176. {
  177. case DISPID_NAVIGATECOMPLETE2:
  178. case DISPID_DOCUMENTCOMPLETE:
  179. {
  180. BOOL fCallNavigateFinished = TRUE;
  181. IDVGetEnum *pdvge;
  182. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IDVGetEnum, &pdvge))))
  183. {
  184. // callback will call it
  185. fCallNavigateFinished = FALSE;
  186. if (dispidMember == DISPID_NAVIGATECOMPLETE2)
  187. pdvge->SetEnumReadyCallback(s_DVEnumReadyCallback, this);
  188. pdvge->Release();
  189. }
  190. _OnNavigate();
  191. if (fCallNavigateFinished && DISPID_DOCUMENTCOMPLETE == dispidMember)
  192. {
  193. // need to let nsc know the navigation finished in case we navigated to a 3rd party namespace extension (w/ its own view impl)
  194. // because it does not implement IDVGetEnum, hence s_DVEnumReadyCallback will not get called
  195. LPITEMIDLIST pidlClone = ILClone(_pidlView);
  196. // should we unwrap this pidl if rooted?
  197. if (pidlClone)
  198. _pns->RightPaneNavigationFinished(pidlClone); // takes ownership
  199. }
  200. }
  201. break;
  202. default:
  203. hr = E_INVALIDARG;
  204. break;
  205. }
  206. return hr;
  207. }
  208. void CExplorerBand::s_DVEnumReadyCallback(void *pvData)
  209. {
  210. CExplorerBand *peb = (CExplorerBand *) pvData;
  211. IBrowserService* pbs;
  212. if (SUCCEEDED(IUnknown_QueryService(peb->_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &pbs))))
  213. {
  214. LPITEMIDLIST pidlTemp;
  215. if (SUCCEEDED(pbs->GetPidl(&pidlTemp)))
  216. {
  217. LPITEMIDLIST pidl;
  218. if (SUCCEEDED(_UnwrapRootedPidl(pidlTemp, FALSE, &pidl)))
  219. {
  220. peb->_pns->RightPaneNavigationFinished(pidl); // takes ownership
  221. }
  222. ILFree(pidlTemp);
  223. }
  224. pbs->Release();
  225. }
  226. }
  227. const TCHAR c_szLink[] = TEXT("link");
  228. const TCHAR c_szRename[] = TEXT("rename");
  229. const TCHAR c_szMove[] = TEXT("cut");
  230. const TCHAR c_szPaste[] = TEXT("paste");
  231. const TCHAR c_szCopy[] = TEXT("copy");
  232. const TCHAR c_szDelete[] = TEXT("delete");
  233. const TCHAR c_szProperties[] = TEXT("properties");
  234. // IOleCommandTarget
  235. HRESULT CExplorerBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
  236. {
  237. if (pguidCmdGroup == NULL)
  238. {
  239. IContextMenu *pcm = NULL;
  240. HRESULT hr = _QueryContextMenuSelection(&pcm);
  241. if (SUCCEEDED(hr))
  242. {
  243. HMENU hmenu = CreatePopupMenu();
  244. if (hmenu)
  245. {
  246. hr = pcm->QueryContextMenu(hmenu, 0, 0, 255, 0);
  247. if (SUCCEEDED(hr))
  248. {
  249. UINT ilast = GetMenuItemCount(hmenu);
  250. for (UINT ipos=0; ipos < ilast; ipos++)
  251. {
  252. MENUITEMINFO mii = {0};
  253. TCHAR szVerb[40];
  254. UINT idCmd;
  255. mii.cbSize = SIZEOF(MENUITEMINFO);
  256. mii.fMask = MIIM_ID|MIIM_STATE;
  257. if (!GetMenuItemInfoWrap(hmenu, ipos, TRUE, &mii)) continue;
  258. if (0 != (mii.fState & (MF_GRAYED|MF_DISABLED))) continue;
  259. idCmd = mii.wID;
  260. hr = ContextMenu_GetCommandStringVerb(pcm, idCmd, szVerb, ARRAYSIZE(szVerb));
  261. if (SUCCEEDED(hr))
  262. {
  263. LPCTSTR szCmd = NULL;
  264. for (ULONG cItem = 0; cItem < cCmds; cItem++)
  265. {
  266. switch (rgCmds[cItem].cmdID)
  267. {
  268. case OLECMDID_CUT:
  269. szCmd = c_szMove;
  270. break;
  271. case OLECMDID_COPY:
  272. szCmd = c_szCopy;
  273. break;
  274. case OLECMDID_PASTE:
  275. szCmd = c_szPaste;
  276. break;
  277. case OLECMDID_DELETE:
  278. szCmd = c_szDelete;
  279. break;
  280. case OLECMDID_PROPERTIES:
  281. szCmd = c_szProperties;
  282. break;
  283. }
  284. if (StrCmpI(szVerb, szCmd)==0)
  285. {
  286. rgCmds[cItem].cmdf = OLECMDF_ENABLED;
  287. }
  288. }
  289. }
  290. }
  291. }
  292. DestroyMenu(hmenu);
  293. }
  294. else
  295. {
  296. hr = E_FAIL;
  297. }
  298. pcm->Release();
  299. }
  300. if (SUCCEEDED(hr))
  301. return hr;
  302. }
  303. return CNSCBand::QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext);
  304. }
  305. HRESULT CExplorerBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  306. {
  307. if (pguidCmdGroup == NULL)
  308. {
  309. HRESULT hr;
  310. switch(nCmdID)
  311. {
  312. case OLECMDID_CUT:
  313. hr = _InvokeCommandOnItem(c_szMove);
  314. break;
  315. case OLECMDID_COPY:
  316. hr = _InvokeCommandOnItem(c_szCopy);
  317. break;
  318. case OLECMDID_PASTE:
  319. hr = _InvokeCommandOnItem(c_szPaste);
  320. break;
  321. case OLECMDID_DELETE:
  322. hr = _InvokeCommandOnItem(c_szDelete);
  323. break;
  324. case OLECMDID_PROPERTIES:
  325. hr = _InvokeCommandOnItem(c_szProperties);
  326. break;
  327. default:
  328. hr = E_FAIL;
  329. break;
  330. }
  331. if (SUCCEEDED(hr))
  332. return hr;
  333. }
  334. return CNSCBand::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
  335. }
  336. // IDockingWindow
  337. HRESULT CExplorerBand::CloseDW(DWORD dw)
  338. {
  339. _ConnectToBrowser(FALSE);
  340. return CNSCBand::CloseDW(dw);
  341. }
  342. HRESULT CExplorerBand::ShowDW(BOOL fShow)
  343. {
  344. return CNSCBand::ShowDW(fShow);
  345. }
  346. // IObjectWithSite
  347. HRESULT CExplorerBand::SetSite(IUnknown* punkSite)
  348. {
  349. HRESULT hr = CNSCBand::SetSite(punkSite);
  350. if (punkSite)
  351. _ConnectToBrowser(TRUE);
  352. return hr;
  353. }
  354. int _SFCDestroyCB(SFCITEM *psfcItem, void *pv)
  355. {
  356. psfcItem->psf->Release();
  357. ILFree(psfcItem->pidl);
  358. return 1;
  359. }
  360. CExplorerBand::~CExplorerBand()
  361. {
  362. ILFree(_pidlView);
  363. if (_pdsaLegacySFC)
  364. {
  365. _pdsaLegacySFC->DestroyCallback(_SFCDestroyCB, NULL);
  366. delete _pdsaLegacySFC;
  367. }
  368. }
  369. HRESULT CExplorerBand::QueryInterface(REFIID riid, void **ppvObj)
  370. {
  371. static const QITAB qit[] = {
  372. QITABENT(CExplorerBand, IDispatch),
  373. { 0 },
  374. };
  375. HRESULT hr = QISearch(this, qit, riid, ppvObj);
  376. if (FAILED(hr))
  377. hr = CNSCBand::QueryInterface(riid, ppvObj);
  378. return hr;
  379. }
  380. DWORD CExplorerBand::_GetEnumFlags()
  381. {
  382. DWORD dwFlags = SHCONTF_FOLDERS;
  383. SHELLSTATE ss = {0};
  384. SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
  385. if (ss.fShowAllObjects)
  386. dwFlags |= SHCONTF_INCLUDEHIDDEN;
  387. return dwFlags;
  388. }
  389. DWORD CExplorerBand::_GetTVExStyle()
  390. {
  391. DWORD dwExStyle = 0;
  392. if (IsOS(OS_WHISTLERORGREATER) &&
  393. SHRegGetBoolUSValue(TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"),
  394. TEXT("FriendlyTree"), FALSE, TRUE))
  395. {
  396. dwExStyle |= TVS_EX_NOSINGLECOLLAPSE;
  397. }
  398. return dwExStyle;
  399. }
  400. DWORD CExplorerBand::_GetTVStyle()
  401. {
  402. DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | WS_HSCROLL | TVS_EDITLABELS | TVS_SHOWSELALWAYS;
  403. if (IsOS(OS_WHISTLERORGREATER) &&
  404. SHRegGetBoolUSValue(TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"),
  405. TEXT("FriendlyTree"), FALSE, TRUE))
  406. {
  407. dwStyle |= TVS_HASBUTTONS | TVS_SINGLEEXPAND | TVS_TRACKSELECT;
  408. }
  409. else
  410. {
  411. dwStyle |= TVS_HASBUTTONS | TVS_HASLINES;
  412. }
  413. // If the parent window is mirrored then the treeview window will inheret the mirroring flag
  414. // And we need the reading order to be Left to right, which is the right to left in the mirrored mode.
  415. if (_hwndParent && IS_WINDOW_RTL_MIRRORED(_hwndParent))
  416. {
  417. // This means left to right reading order because this window will be mirrored.
  418. _dwStyle |= TVS_RTLREADING;
  419. }
  420. return dwStyle;
  421. }
  422. HRESULT CExplorerBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  423. {
  424. // aggregation checking is handled in class factory
  425. CExplorerBand * peb = new CExplorerBand();
  426. if (!peb)
  427. return E_OUTOFMEMORY;
  428. if (SUCCEEDED(peb->_Init((LPCITEMIDLIST)CSIDL_DESKTOP)))
  429. {
  430. peb->_pns = CNscTree_CreateInstance();
  431. if (peb->_pns)
  432. {
  433. ASSERT(poi);
  434. peb->_poi = poi;
  435. // if you change this cast, fix up CFavBand_CreateInstance
  436. *ppunk = SAFECAST(peb, IDeskBand *);
  437. IUnknown_SetSite(peb->_pns, *ppunk);
  438. peb->_SetNscMode(MODE_NORMAL);
  439. return S_OK;
  440. }
  441. }
  442. peb->Release();
  443. return E_FAIL;
  444. }
  445. HRESULT CExplorerBand::_ConnectToBrowser(BOOL fConnect)
  446. {
  447. IBrowserService* pbs;
  448. HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &pbs));
  449. if (SUCCEEDED(hr))
  450. {
  451. if (fConnect)
  452. {
  453. LPITEMIDLIST pidlTemp = NULL;
  454. // try to get the pidl the browser is navigated to
  455. // this usually fails if user just opened Explorer window because navigation is asynchronous
  456. // so we're not initialized yet
  457. if (FAILED(pbs->GetPidl(&pidlTemp)))
  458. {
  459. IBrowserService2 *pbs2;
  460. if (SUCCEEDED(pbs->QueryInterface(IID_PPV_ARG(IBrowserService2, &pbs2))))
  461. {
  462. LPCBASEBROWSERDATA pbbd;
  463. // our last hope is the pidl browser is navigating to...
  464. if (SUCCEEDED(pbs2->GetBaseBrowserData(&pbbd)) && pbbd->_pidlPending)
  465. {
  466. pidlTemp = ILClone(pbbd->_pidlPending);
  467. }
  468. pbs2->Release();
  469. }
  470. }
  471. if (pidlTemp)
  472. {
  473. LPITEMIDLIST pidl;
  474. // see if we're dealing with a rooted namespace
  475. if (SUCCEEDED(_UnwrapRootedPidl(pidlTemp, TRUE, &pidl)))
  476. {
  477. _Init(pidl); //if so, reinitialize ourself with the rooted pidl
  478. ILFree(pidl);
  479. }
  480. ILFree(pidlTemp);
  481. }
  482. }
  483. IConnectionPointContainer* pcpc;
  484. hr = IUnknown_QueryService(pbs, SID_SWebBrowserApp, IID_PPV_ARG(IConnectionPointContainer, &pcpc));
  485. // Let's now have the Browser Window give us notification when something happens.
  486. if (SUCCEEDED(hr))
  487. {
  488. hr = ConnectToConnectionPoint(SAFECAST(this, IDispatch*), DIID_DWebBrowserEvents2, fConnect,
  489. pcpc, &_dwcpCookie, NULL);
  490. pcpc->Release();
  491. }
  492. pbs->Release();
  493. }
  494. ASSERT(SUCCEEDED(hr));
  495. return hr;
  496. }
  497. HRESULT CExplorerBand::_InitializeNsc()
  498. {
  499. HRESULT hr = _pns->Initialize(_pidl, _GetEnumFlags(), NSS_DROPTARGET | NSS_BROWSERSELECT);
  500. if (SUCCEEDED(hr))
  501. _OnNavigate();
  502. return hr;
  503. }
  504. HRESULT CExplorerBand::_TranslatePidl(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlTarget, ULONG *pulAttrib)
  505. {
  506. HRESULT hr = E_INVALIDARG;
  507. if (pidl && ppidlTarget && pulAttrib)
  508. {
  509. hr = IEGetAttributesOf(pidl, pulAttrib);
  510. if (SUCCEEDED(hr))
  511. {
  512. hr = SHILClone(pidl, ppidlTarget);
  513. }
  514. }
  515. return hr;
  516. }
  517. BOOL CExplorerBand::_ShouldNavigateToPidl(LPCITEMIDLIST pidl, ULONG ulAttrib)
  518. {
  519. return ulAttrib & SFGAO_FOLDER;
  520. }
  521. BOOL CExplorerBand::_IsFloppy(LPCITEMIDLIST pidl)
  522. {
  523. BOOL fRet = FALSE;
  524. WCHAR szPath[MAX_PATH];
  525. if (SUCCEEDED(SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL)))
  526. {
  527. if (DRIVE_REMOVABLE == GetDriveType(szPath))
  528. {
  529. fRet = (L'A' == szPath[0] || L'B' == szPath[0] || L'a' == szPath[0] || L'b' == szPath[0]);
  530. }
  531. }
  532. return fRet;
  533. }
  534. HRESULT CExplorerBand::Invoke(LPCITEMIDLIST pidl)
  535. {
  536. HRESULT hr;
  537. // allow user to navigate to an already selected item if they opened Explorer band in Web browser
  538. // (because we put selection on the root node but don't navigate away from the web page, if they click
  539. // on the root we don't navigate there, because selection never changed)
  540. if (!_pidlView)
  541. {
  542. _fIgnoreSelection = FALSE;
  543. hr = OnSelectionChanged(pidl);
  544. }
  545. else if (ILIsEqual(pidl, _pidlView) && _IsFloppy(pidl))
  546. {
  547. // If the drive is a floppy and the user reselects the drive refresh the contents. This enables
  548. // a user to refresh when a floppy is replaced.
  549. _fFloppyRefresh = TRUE;
  550. hr = OnSelectionChanged(pidl);
  551. _fFloppyRefresh = FALSE;
  552. }
  553. else
  554. {
  555. hr = S_OK;
  556. }
  557. return hr;
  558. }
  559. HRESULT CExplorerBand::OnSelectionChanged(LPCITEMIDLIST pidl)
  560. {
  561. HRESULT hr = E_INVALIDARG;
  562. if (!_fIgnoreSelection)
  563. {
  564. if (pidl)
  565. {
  566. ULONG ulAttrib = SFGAO_FOLDER;
  567. LPITEMIDLIST pidlTarget;
  568. hr = GetNavigateTarget(pidl, &pidlTarget, &ulAttrib);
  569. if (hr == S_OK)
  570. {
  571. if (!_pidlView || _fFloppyRefresh || !ILIsEqual(pidlTarget, _pidlView))
  572. {
  573. hr = CNSCBand::Invoke(pidlTarget);
  574. if (SUCCEEDED(hr))
  575. _fCanSelect = FALSE;
  576. _pns->RightPaneNavigationStarted(pidlTarget);
  577. pidlTarget = NULL; // ownership passed
  578. }
  579. ILFree(pidlTarget);
  580. }
  581. #ifdef DEBUG
  582. else if (hr == S_FALSE)
  583. {
  584. ASSERT(pidlTarget == NULL);
  585. }
  586. #endif
  587. }
  588. }
  589. else
  590. {
  591. _fIgnoreSelection = FALSE; //we ignore only first selection
  592. }
  593. return hr;
  594. }
  595. void CExplorerBand::_MaybeAddToLegacySFC(LPCITEMIDLIST pidl)
  596. {
  597. IShellFolder *psf = NULL;
  598. if (pidl && SUCCEEDED(SHBindToObjectEx(NULL, pidl, NULL, IID_PPV_ARG(IShellFolder, &psf))))
  599. {
  600. //
  601. // APPCOMPAT LEGACY - Compatibility. needs the Shell folder cache, - ZekeL - 4-MAY-99
  602. // some apps, specifically WS_FTP and AECO Zip Pro,
  603. // rely on having a shellfolder existing in order for them to work.
  604. // we pulled the SFC because it wasnt any perf win.
  605. //
  606. if (OBJCOMPATF_OTNEEDSSFCACHE & SHGetObjectCompatFlags(psf, NULL))
  607. _AddToLegacySFC(pidl, psf);
  608. psf->Release();
  609. }
  610. }
  611. BOOL CExplorerBand::_IsInSFC(LPCITEMIDLIST pidl)
  612. {
  613. BOOL bReturn = FALSE;
  614. ASSERT(_pdsaLegacySFC);
  615. for (int i=0; i<_pdsaLegacySFC->GetItemCount(); i++)
  616. {
  617. SFCITEM *psfcItem = _pdsaLegacySFC->GetItemPtr(i);
  618. if (ILIsEqual(psfcItem->pidl, pidl))
  619. {
  620. bReturn = TRUE;
  621. break;
  622. }
  623. }
  624. return bReturn;
  625. }
  626. void CExplorerBand::_AddToLegacySFC(LPCITEMIDLIST pidl, IShellFolder *psf)
  627. {
  628. if (!_pdsaLegacySFC)
  629. {
  630. _pdsaLegacySFC = new CDSA<SFCITEM>;
  631. if (_pdsaLegacySFC && !_pdsaLegacySFC->Create(4))
  632. {
  633. delete _pdsaLegacySFC;
  634. _pdsaLegacySFC = NULL;
  635. }
  636. }
  637. if (_pdsaLegacySFC)
  638. {
  639. LPITEMIDLIST pidlCache;
  640. if (!_IsInSFC(pidl) && SUCCEEDED(SHILClone(pidl, &pidlCache)))
  641. {
  642. SFCITEM sfc = {pidlCache, psf};
  643. if (-1 != _pdsaLegacySFC->InsertItem(0, &sfc))
  644. psf->AddRef();
  645. else
  646. ILFree(pidlCache);
  647. }
  648. }
  649. }