Leaked source code of windows server 2003
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.

2700 lines
77 KiB

  1. #include "shellprv.h"
  2. #include "common.h"
  3. #include "sftbar.h"
  4. #include "resource.h"
  5. #include "dpastuff.h"
  6. #include "shlwapi.h"
  7. #include "cobjsafe.h"
  8. #include <iimgctx.h>
  9. #include "uemapp.h"
  10. #include "util.h"
  11. #include "brutil.h"
  12. #include "dobjutil.h"
  13. #include "idlcomm.h"
  14. extern UINT g_idFSNotify;
  15. #define TF_SFTBAR TF_MENUBAND
  16. #define PGMP_RECALCSIZE 200
  17. CSFToolbar::CSFToolbar()
  18. {
  19. #ifdef CASCADE_DEBUG
  20. _fCascadeFolder = TRUE;
  21. #endif
  22. _dwStyle = TBSTYLE_TOOLTIPS;
  23. _fDirty = TRUE; // we havn't enumerated, so our state is dirty
  24. _fRegisterChangeNotify = TRUE;
  25. _fAllowReorder = TRUE;
  26. _tbim.iButton = -1;
  27. _iDragSource = -1;
  28. _lEvents = SHCNE_DRIVEADD|SHCNE_CREATE|SHCNE_MKDIR|SHCNE_DRIVEREMOVED|
  29. SHCNE_DELETE|SHCNE_RMDIR|SHCNE_RENAMEITEM|SHCNE_RENAMEFOLDER|
  30. SHCNE_MEDIAINSERTED|SHCNE_MEDIAREMOVED|SHCNE_NETUNSHARE|SHCNE_NETSHARE|
  31. SHCNE_UPDATEITEM|SHCNE_UPDATEIMAGE|SHCNE_ASSOCCHANGED|
  32. SHCNE_UPDATEDIR|SHCNE_EXTENDED_EVENT;
  33. #define SHCNE_PIDL1ISCHILD \
  34. (SHCNE_DRIVEADD|SHCNE_CREATE|SHCNE_MKDIR|SHCNE_DRIVEREMOVED|\
  35. SHCNE_DELETE|SHCNE_RMDIR|SHCNE_NETUNSHARE|SHCNE_NETSHARE|\
  36. SHCNE_UPDATEITEM)
  37. }
  38. CSFToolbar::~CSFToolbar()
  39. {
  40. ATOMICRELEASE(_pcmSF);
  41. ATOMICRELEASE(_piml);
  42. _ReleaseShellFolder();
  43. ILFree(_pidl);
  44. OrderList_Destroy(&_hdpa);
  45. if (_hwndWorkerWindow)
  46. DestroyWindow(_hwndWorkerWindow);
  47. OrderList_Destroy(&_hdpaOrder);
  48. }
  49. HRESULT CSFToolbar::QueryInterface(REFIID riid, void **ppvObj)
  50. {
  51. static const QITAB qit[] = {
  52. QITABENT(CSFToolbar, IWinEventHandler),
  53. QITABENT(CSFToolbar, IShellChangeNotify),
  54. QITABENT(CSFToolbar, IDropTarget),
  55. QITABENT(CSFToolbar, IContextMenu),
  56. QITABENT(CSFToolbar, IShellFolderBand),
  57. { 0 },
  58. };
  59. return QISearch(this, qit, riid, ppvObj);
  60. }
  61. HRESULT CSFToolbar::SetShellFolder(IShellFolder* psf, LPCITEMIDLIST pidl)
  62. {
  63. HRESULT hr = E_INVALIDARG;
  64. // Save the old values
  65. LPITEMIDLIST pidlSave = _pidl;
  66. IShellFolder *psfSave = _psf;
  67. ITranslateShellChangeNotify *ptscnSave = _ptscn;
  68. _psf = NULL;
  69. _pidl = NULL;
  70. _ptscn = NULL;
  71. ASSERT(NULL == psf || IS_VALID_CODE_PTR(psf, IShellFolder));
  72. ASSERT(NULL == pidl || IS_VALID_PIDL(pidl));
  73. if (psf || pidl)
  74. {
  75. if (psf)
  76. {
  77. _psf = psf;
  78. _psf->AddRef();
  79. _psf->QueryInterface(IID_PPV_ARG(ITranslateShellChangeNotify, &_ptscn));
  80. }
  81. if (pidl)
  82. _pidl = ILClone(pidl);
  83. hr = S_OK;
  84. }
  85. if (SUCCEEDED(hr))
  86. {
  87. ILFree(pidlSave);
  88. if (psfSave)
  89. psfSave->Release();
  90. if (ptscnSave)
  91. ptscnSave->Release();
  92. }
  93. else
  94. {
  95. ASSERT(_psf == NULL);
  96. ASSERT(_pidl == NULL);
  97. ASSERT(_ptscn == NULL);
  98. // we failed -- restore the old values
  99. _psf = psfSave;
  100. _pidl = pidlSave;
  101. _ptscn = ptscnSave;
  102. }
  103. // This code is here for ShellFolderToolbar reuse. When setting a new shell folder
  104. // into an existing band, we will refresh. Note that this is a noop on a new band.
  105. _RememberOrder();
  106. _SetDirty(TRUE);
  107. if (_fShow)
  108. _FillToolbar();
  109. return hr;
  110. }
  111. HWND CSFToolbar::_CreatePager(HWND hwndParent)
  112. {
  113. if (!_fMulticolumn)
  114. {
  115. _hwndPager = CreateWindowEx(0, WC_PAGESCROLLER, NULL,
  116. WS_CHILD | WS_TABSTOP |
  117. WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
  118. 0, 0, 0, 0, hwndParent, (HMENU) 0, HINST_THISDLL, NULL);
  119. if (_hwndPager)
  120. {
  121. hwndParent = _hwndPager;
  122. }
  123. }
  124. return hwndParent;
  125. }
  126. HRESULT CSFToolbar::_CreateToolbar(HWND hwndParent)
  127. {
  128. if (!_hwndTB)
  129. {
  130. hwndParent = _CreatePager(hwndParent);
  131. _hwndTB = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAME, NULL,
  132. WS_VISIBLE | WS_CHILD | TBSTYLE_FLAT |
  133. WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
  134. CCS_NODIVIDER | CCS_NOPARENTALIGN |
  135. CCS_NORESIZE | _dwStyle,
  136. 0, 0, 0, 0, hwndParent, (HMENU) 0, HINST_THISDLL, NULL);
  137. if (!_hwndTB)
  138. {
  139. TraceMsg(TF_ERROR, "_hwndTB failed");
  140. return HRESULT_FROM_WIN32(GetLastError());
  141. }
  142. if (_hwndPager)
  143. SendMessage(_hwndPager, PGM_SETCHILD, 0, (LPARAM)_hwndTB);
  144. SendMessage(_hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
  145. // Set the format to ANSI or UNICODE as appropriate.
  146. ToolBar_SetUnicodeFormat(_hwndTB, DLL_IS_UNICODE);
  147. if (_hwndPager)
  148. {
  149. // Set the format to ANSI or UNICODE as appropriate.
  150. ToolBar_SetUnicodeFormat(_hwndPager, DLL_IS_UNICODE);
  151. }
  152. // Make sure we're on the same wavelength.
  153. SendMessage(_hwndTB, CCM_SETVERSION, COMCTL32_VERSION, 0);
  154. SendMessage(_hwndTB, TB_SETEXTENDEDSTYLE, TBSTYLE_EX_DOUBLEBUFFER, TBSTYLE_EX_DOUBLEBUFFER);
  155. RECT rc;
  156. SIZE size;
  157. SystemParametersInfoA(SPI_GETWORKAREA, sizeof(RECT), &rc, FALSE);
  158. if (!_hwndPager)
  159. {
  160. size.cx = RECTWIDTH(rc);
  161. size.cy = GetSystemMetrics(SM_CYSCREEN) - (2 * GetSystemMetrics(SM_CYEDGE)); // Need to subrtact off the borders
  162. }
  163. else
  164. {
  165. //HACKHACK: THIS WILL FORCE NO WRAP TO HAPPEN FOR PROPER WIDTH CALC WHEN PAGER IS PRESENT.
  166. size.cx = RECTWIDTH(rc);
  167. size.cy = 32000;
  168. }
  169. ToolBar_SetBoundingSize(_hwndTB, &size);
  170. if (!_SubclassWindow(_hwndTB))
  171. {
  172. _fRegisterChangeNotify = FALSE;
  173. }
  174. }
  175. else
  176. {
  177. if (_hwndPager && GetParent(_hwndPager) != hwndParent)
  178. SetParent(_hwndPager, hwndParent);
  179. }
  180. if (FAILED(_GetTopBrowserWindow(&_hwndDD)))
  181. _hwndDD = GetParent(_hwndTB);
  182. return S_OK;
  183. }
  184. #define MAX_COMMANDID 0xFFFF // We're allowed one word of command ids (tested at 5)
  185. int CSFToolbar::_GetCommandID()
  186. {
  187. int id = -1;
  188. if (!_fCheckIds)
  189. {
  190. id = _nNextCommandID++;
  191. }
  192. else
  193. {
  194. // We are reusing command ids and must verify that
  195. // the current one is not in use. This is slow, but
  196. // I assume the number of buttons on one of these
  197. // bands is relatively few.
  198. //
  199. for (int i = 0 ; i <= MAX_COMMANDID ; i++)
  200. {
  201. TBBUTTONINFO tbbiDummy;
  202. tbbiDummy.cbSize = sizeof(tbbiDummy);
  203. tbbiDummy.dwMask = 0; // we don't care about data, just existence
  204. if (-1 != ToolBar_GetButtonInfo(_hwndTB, _nNextCommandID, &tbbiDummy))
  205. {
  206. // A button by this id wasn't found, so the id must be free
  207. //
  208. id = _nNextCommandID++;
  209. break;
  210. }
  211. _nNextCommandID++;
  212. _nNextCommandID %= MAX_COMMANDID;
  213. }
  214. }
  215. if (_nNextCommandID > MAX_COMMANDID)
  216. {
  217. _nNextCommandID = 0;
  218. _fCheckIds = TRUE;
  219. }
  220. return id;
  221. }
  222. /*----------------------------------------------------------
  223. Purpose: This function determines the toolbar button style for the
  224. given pidl.
  225. Returns S_OK if pdwMIFFlags is also set (i.e., the object
  226. supported IMenuBandItem to provide more info). S_FALSE if only
  227. *pdwTBStyle is set.
  228. */
  229. HRESULT CSFToolbar::_TBStyleForPidl(LPCITEMIDLIST pidl, DWORD *pdwTBStyle, DWORD *pdwTBState, DWORD *pdwMIFFlags, int* piIcon)
  230. {
  231. DWORD dwStyle = TBSTYLE_BUTTON;
  232. if (!_fAccelerators)
  233. dwStyle |= TBSTYLE_NOPREFIX;
  234. *pdwMIFFlags = 0;
  235. *pdwTBStyle = dwStyle;
  236. *piIcon = -1;
  237. *pdwTBState = TBSTATE_ENABLED;
  238. return S_FALSE;
  239. }
  240. PIBDATA CSFToolbar::_CreateItemData(PORDERITEM poi)
  241. {
  242. return new IBDATA(poi);
  243. }
  244. //
  245. // poiOrIndex can be...
  246. //
  247. // A valid pointer (which will be treated as a PORDERITEM)
  248. // A MAKEINTRESOURCE value (which will be treated as a sentinel value)
  249. //
  250. PIBDATA CSFToolbar::_AddOrderItemTB(PORDERITEM poiOrIndex, int index, TBBUTTON* ptbb)
  251. {
  252. TCHAR szName[MAX_PATH];
  253. LPCITEMIDLIST pidlOI;
  254. PORDERITEM poi;
  255. if (IS_INTRESOURCE(poiOrIndex))
  256. {
  257. poi = NULL;
  258. pidlOI = (LPCITEMIDLIST)poiOrIndex;
  259. }
  260. else
  261. {
  262. poi = poiOrIndex;
  263. pidlOI = poi->pidl;
  264. }
  265. // We need to do this even for NULL because _ObtainPIDLName cooks
  266. // up the word "(Empty)" as necessary.
  267. _ObtainPIDLName(pidlOI, szName, SIZECHARS(szName));
  268. TBBUTTON tbb = {0};
  269. DWORD dwMIFFlags;
  270. DWORD dwStyle;
  271. DWORD dwState;
  272. int iIcon;
  273. int iCommandID = _GetCommandID();
  274. BOOL bNoIcon = FALSE;
  275. if (!ptbb)
  276. ptbb = &tbb;
  277. if (S_OK == _TBStyleForPidl(pidlOI, &dwStyle, &dwState, &dwMIFFlags,&iIcon) &&
  278. !(dwMIFFlags & SMIF_ICON))
  279. {
  280. bNoIcon = TRUE;
  281. }
  282. PIBDATA pibdata = _CreateItemData(poi);
  283. if (pibdata)
  284. {
  285. pibdata->SetFlags(dwMIFFlags);
  286. pibdata->SetNoIcon(bNoIcon);
  287. if (!bNoIcon && iIcon != -1)
  288. ptbb->iBitmap = iIcon;
  289. else
  290. ptbb->iBitmap = I_IMAGECALLBACK;
  291. ptbb->idCommand = iCommandID;
  292. ptbb->fsState = (BYTE)dwState;
  293. ptbb->fsStyle = (BYTE)dwStyle;
  294. ptbb->dwData = (DWORD_PTR)pibdata;
  295. ptbb->iString = (INT_PTR)szName;
  296. // Disregard variablewidth if we are vertical
  297. if (_fVariableWidth && !_fVertical)
  298. ptbb->fsStyle |= TBSTYLE_AUTOSIZE;
  299. if (ptbb->idCommand != -1)
  300. {
  301. if (SendMessage(_hwndTB, TB_INSERTBUTTON, index, (LPARAM)ptbb))
  302. {
  303. TraceMsg(TF_BAND, "SFToolbar::_AddPidl %d 0x%x [%s]", ptbb->idCommand, ptbb->dwData, ptbb->iString);
  304. }
  305. else
  306. {
  307. delete pibdata;
  308. pibdata = NULL;
  309. }
  310. }
  311. }
  312. return pibdata;
  313. }
  314. void CSFToolbar::_ObtainPIDLName(LPCITEMIDLIST pidl, LPTSTR psz, int cchMax)
  315. {
  316. DisplayNameOf(_psf, pidl, SHGDN_NORMAL, psz, cchMax);
  317. }
  318. int CSFToolbar::_GetBitmap(int iCommandID, PIBDATA pibdata, BOOL fUseCache)
  319. {
  320. int iBitmap;
  321. if (_fNoIcons || !pibdata || pibdata->GetNoIcon())
  322. {
  323. iBitmap = -1;
  324. }
  325. else
  326. {
  327. iBitmap = OrderItem_GetSystemImageListIndex(pibdata->GetOrderItem(), _psf, fUseCache);
  328. }
  329. return iBitmap;
  330. }
  331. void CSFToolbar::_OnGetDispInfo(LPNMHDR pnm, BOOL fUnicode)
  332. {
  333. LPNMTBDISPINFO pdi = (LPNMTBDISPINFO)pnm;
  334. PIBDATA pibdata = (PIBDATA)pdi->lParam;
  335. if (pdi->dwMask & TBNF_IMAGE)
  336. {
  337. pdi->iImage = _GetBitmap(pdi->idCommand, pibdata, TRUE);
  338. }
  339. if (pdi->dwMask & TBNF_TEXT)
  340. {
  341. if (pdi->pszText)
  342. {
  343. if (fUnicode)
  344. {
  345. pdi->pszText[0] = TEXT('\0');
  346. }
  347. else
  348. {
  349. pdi->pszText[0] = 0;
  350. }
  351. }
  352. }
  353. pdi->dwMask |= TBNF_DI_SETITEM;
  354. }
  355. // Adds pidl as a new button, handles ILFree(pidl) for the caller
  356. //
  357. BOOL CSFToolbar::_AddPidl(LPITEMIDLIST pidl, DWORD dwFlags, int index)
  358. {
  359. if (_hdpa)
  360. {
  361. PORDERITEM poi = OrderItem_Create(pidl, index);
  362. if (poi)
  363. {
  364. int iPos = DPA_InsertPtr(_hdpa, index, poi);
  365. if (-1 != iPos)
  366. {
  367. // If we did not load an order, then new items should
  368. // show up alphabetically in the list, not at the bottom.
  369. if (!_fHasOrder && !(dwFlags & FSNA_BULKADD))
  370. {
  371. // Sort by name
  372. _SortDPA(_hdpa);
  373. // Find the index of the order item. We use this index as
  374. // the toolbar insert index.
  375. index = DPA_GetPtrIndex(_hdpa, poi);
  376. }
  377. if (_AddOrderItemTB(poi, index, NULL))
  378. {
  379. return TRUE;
  380. }
  381. DPA_DeletePtr(_hdpa, iPos);
  382. }
  383. OrderItem_Free(poi);
  384. return FALSE;
  385. }
  386. }
  387. ILFree(pidl);
  388. return FALSE;
  389. }
  390. BOOL CSFToolbar::_FilterPidl(LPCITEMIDLIST pidl)
  391. {
  392. return FALSE;
  393. }
  394. void CSFToolbar::s_NewItem(void *pvParam, LPCITEMIDLIST pidl)
  395. {
  396. CSFToolbar* psft = (CSFToolbar*)pvParam;
  397. psft->v_NewItem(pidl);
  398. }
  399. HRESULT CSFToolbar::_GetIEnumIDList(DWORD dwEnumFlags, IEnumIDList **ppenum)
  400. {
  401. ASSERT(_psf);
  402. // Pass in a NULL hwnd so the enumerator does not show any UI while
  403. // we're filling a band.
  404. return IShellFolder_EnumObjects(_psf, NULL, dwEnumFlags, ppenum);
  405. }
  406. void CSFToolbar::_FillDPA(HDPA hdpa, HDPA hdpaSort, DWORD dwEnumFlags)
  407. {
  408. IEnumIDList* penum;
  409. int cItems = 0;
  410. if (!_psf)
  411. return;
  412. if (S_OK == _GetIEnumIDList(dwEnumFlags, &penum))
  413. {
  414. LPITEMIDLIST pidl;
  415. ULONG ul;
  416. while (S_OK == penum->Next(1, &pidl, &ul))
  417. {
  418. cItems++;
  419. if (_FilterPidl(pidl) || !OrderList_Append(hdpa, pidl, -1))
  420. {
  421. TraceMsg(TF_MENUBAND, "SFToolbar (0x%x)::_FillDPA : Did not Add Pidl (0x%x).", this, pidl);
  422. ILFree(pidl);
  423. }
  424. }
  425. penum->Release();
  426. }
  427. ORDERINFO oinfo;
  428. int iInsertIndex = _tbim.iButton + 1; // This is the button where the cursor sat.
  429. // So, We want to insert after that
  430. if (iInsertIndex >= ToolBar_ButtonCount(_hwndTB)) // But, if it's at the end,
  431. iInsertIndex = -1; // Convert the insert to an append.
  432. // - Comments in rhyme by lamadio
  433. oinfo.psf = _psf;
  434. (oinfo.psf)->AddRef();
  435. oinfo.dwSortBy = (_fHasOrder || _fDropping)? ((_fNoNameSort ? OI_SORTBYORDINAL : OI_SORTBYNAME)): OI_MERGEBYNAME;
  436. OrderList_Merge(hdpa, hdpaSort, _fDropping ? iInsertIndex : _DefaultInsertIndex(), (LPARAM) &oinfo,
  437. s_NewItem, (void *)this);
  438. ATOMICRELEASE(oinfo.psf);
  439. }
  440. // This function re-enumerates the IShellFolder, keeping things ordered correctly.
  441. //
  442. void CSFToolbar::_FillToolbar()
  443. {
  444. HDPA hdpaSort;
  445. HDPA hdpa;
  446. if (!_fDirty || !_psf)
  447. return;
  448. // If we have an order array, use that, otherwise
  449. // use the currently viewed items
  450. // remove the ref for the member variable since we can get reentered
  451. // this would be better with real refcounting but this'll do
  452. BOOL fTakeOrderRef = FALSE;
  453. if (_hdpaOrder)
  454. {
  455. hdpaSort = _hdpaOrder; // already sorted by name
  456. // we set it to null, so we have complete ownership of it.
  457. // at the end we're going to nuke _hdpaOrder anyway in _RememberOrder.
  458. _hdpaOrder = NULL;
  459. fTakeOrderRef = TRUE;
  460. }
  461. else
  462. {
  463. hdpaSort = _hdpa;
  464. _SortDPA(hdpaSort);
  465. }
  466. hdpa = DPA_Create(hdpaSort ? DPA_GetPtrCount(hdpaSort) : 12);
  467. if (hdpa)
  468. {
  469. _FillDPA(hdpa, hdpaSort, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS);
  470. HDPA hdpaToRemove = DPA_Create(4);
  471. if (hdpaToRemove)
  472. {
  473. HDPA hdpaToAdd = DPA_Create(4);
  474. if (hdpaToAdd)
  475. {
  476. int i, j;
  477. BOOL fReleaseAdd = TRUE;
  478. if (_hdpa)
  479. {
  480. // if there isn't anything in the hdpaSort list (which is all the stuff that's already there),
  481. // add a null element. this is so it'll generate a "remove this null element" later.
  482. // otherwise, we might end up with an (Empty) item left over.
  483. if (DPA_GetPtrCount(hdpaSort) == 0)
  484. {
  485. OrderList_Append(hdpaSort, NULL, 0);
  486. }
  487. ORDERINFO oi = {0};
  488. oi.dwSortBy = OI_SORTBYNAME;
  489. oi.psf = _psf;
  490. _psf->AddRef();
  491. DPA_Sort(hdpaSort, OrderItem_Compare, (LPARAM) &oi);
  492. DPA_Sort(hdpa, OrderItem_Compare, (LPARAM) &oi);
  493. i = 0;
  494. j = 0;
  495. while ((i < DPA_GetPtrCount(hdpaSort)) && (j < DPA_GetPtrCount(hdpa)))
  496. {
  497. void *pv1 = DPA_FastGetPtr(hdpaSort, i);
  498. void *pv2 = DPA_FastGetPtr(hdpa, j);
  499. int nCmp = OrderItem_Compare(pv1, pv2, (LPARAM) &oi);
  500. if (nCmp > 0)
  501. {
  502. DPA_AppendPtr(hdpaToAdd, pv2);
  503. j++;
  504. }
  505. else if (nCmp < 0)
  506. {
  507. DPA_AppendPtr(hdpaToRemove, pv1);
  508. i++;
  509. }
  510. else
  511. {
  512. i++;
  513. j++;
  514. }
  515. }
  516. while (i < DPA_GetPtrCount(hdpaSort))
  517. {
  518. DPA_AppendPtr(hdpaToRemove, DPA_FastGetPtr(hdpaSort, i));
  519. i++;
  520. }
  521. while (j < DPA_GetPtrCount(hdpa))
  522. {
  523. DPA_AppendPtr(hdpaToAdd, DPA_FastGetPtr(hdpa, j));
  524. j++;
  525. }
  526. _psf->Release();
  527. }
  528. else
  529. {
  530. DPA_Destroy(hdpaToAdd);
  531. hdpaToAdd = hdpa;
  532. fReleaseAdd = FALSE;
  533. _hdpa = DPA_Create(DPA_GetPtrCount(hdpa));
  534. }
  535. SendMessage(_hwndTB, WM_SETREDRAW, FALSE, 0);
  536. if (_hdpa)
  537. {
  538. _NotifyBulkOperation(TRUE);
  539. // add buttons back in
  540. for (i = 0; i < DPA_GetPtrCount(hdpaToAdd); i++)
  541. {
  542. PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(hdpaToAdd, i);
  543. _OnFSNotifyAdd(poi->pidl, FSNA_BULKADD, poi->nOrder);
  544. }
  545. // remove buttons that die
  546. for (i = 0; i < DPA_GetPtrCount(hdpaToRemove); i++)
  547. {
  548. PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(hdpaToRemove, i);
  549. _OnFSNotifyRemove(poi->pidl);
  550. }
  551. _NotifyBulkOperation(FALSE);
  552. }
  553. if (fReleaseAdd)
  554. {
  555. DPA_Destroy(hdpaToAdd);
  556. }
  557. }
  558. DPA_Destroy(hdpaToRemove);
  559. }
  560. OrderList_Destroy(&hdpa);
  561. }
  562. SendMessage(_hwndTB, WM_SETREDRAW, TRUE, 0);
  563. if (fTakeOrderRef)
  564. {
  565. OrderList_Destroy(&hdpaSort);
  566. }
  567. _RememberOrder();
  568. _UpdateButtons();
  569. _SetDirty(FALSE);
  570. if (!SHIsTempDisplayMode())
  571. {
  572. _ToolbarChanged();
  573. }
  574. TraceMsg(TF_BAND, "SFToolbar::_FillToolbar found %d items", DPA_GetPtrCount(_hdpa));
  575. }
  576. void CSFToolbar::EmptyToolbar()
  577. {
  578. if (_hwndTB)
  579. {
  580. TraceMsg(TF_BAND, "SFToolbar::EmptyToolbar %d items", _hdpa ? DPA_GetPtrCount(_hdpa) : 0);
  581. while (InlineDeleteButton(0))
  582. {
  583. // delete the buttons
  584. }
  585. }
  586. OrderList_Destroy(&_hdpa);
  587. _fDirty = TRUE;
  588. _nNextCommandID = 0;
  589. }
  590. void CSFToolbar::_SetDirty(BOOL fDirty)
  591. {
  592. _fDirty = fDirty;
  593. }
  594. UINT CSFToolbar::_IndexToID(int iTBIndex)
  595. {
  596. TBBUTTON tbb;
  597. if (SendMessage(_hwndTB, TB_GETBUTTON, iTBIndex, (LPARAM)&tbb))
  598. {
  599. return tbb.idCommand;
  600. }
  601. return (UINT)-1;
  602. }
  603. // if ptbbi is specified, dwMask must be filled in
  604. //
  605. // if pIndex is specified, it receives the DPA index, not the toolbar index
  606. HRESULT CSFToolbar::_GetButtonFromPidl(LPCITEMIDLIST pidl, TBBUTTONINFO * ptbbi, int * pIndex, LPITEMIDLIST *ppidlOut)
  607. {
  608. if (ppidlOut)
  609. *ppidlOut = NULL;
  610. if (!_hdpa)
  611. return E_FAIL;
  612. for (int i = DPA_GetPtrCount(_hdpa) - 1 ; i >= 0 ; i--)
  613. {
  614. HRESULT hr;
  615. PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(_hdpa, i);
  616. ASSERT(poi);
  617. if (pidl == poi->pidl)
  618. {
  619. hr = 0;
  620. }
  621. else
  622. {
  623. hr = (!pidl || !poi->pidl) ? E_FAIL : _psf->CompareIDs(0, pidl, poi->pidl);
  624. }
  625. if (ResultFromShort(0) == hr)
  626. {
  627. if (pIndex)
  628. *pIndex = i;
  629. if (ptbbi)
  630. {
  631. int id = _IndexToID(v_DPAIndexToTBIndex(i));
  632. if (id != -1)
  633. {
  634. ptbbi->cbSize = sizeof(*ptbbi);
  635. if (-1 == ToolBar_GetButtonInfo(_hwndTB, id, ptbbi))
  636. {
  637. ZeroMemory(ptbbi, sizeof(*ptbbi));
  638. }
  639. }
  640. else
  641. {
  642. ZeroMemory(ptbbi, sizeof(*ptbbi));
  643. }
  644. }
  645. if (ppidlOut)
  646. *ppidlOut = poi->pidl;
  647. return S_OK;
  648. }
  649. }
  650. return E_FAIL;
  651. }
  652. // On an add, tack the new button on the end
  653. void CSFToolbar::_OnFSNotifyAdd(LPCITEMIDLIST pidl, DWORD dwFlags, int nIndex)
  654. {
  655. // be paranoid and make sure we don't duplicate an item
  656. //
  657. if ((dwFlags & FSNA_BULKADD) || FAILED(_GetButtonFromPidl(pidl, NULL, NULL, NULL)))
  658. {
  659. LPITEMIDLIST pidlNew;
  660. if (_fFSNotify && !_ptscn && pidl && !(dwFlags & FSNA_BULKADD))
  661. {
  662. if (FAILED(SHGetRealIDL(_psf, pidl, &pidlNew)))
  663. pidlNew = NULL;
  664. }
  665. else
  666. {
  667. pidlNew = pidl ? ILClone(pidl) : NULL;
  668. }
  669. if ((dwFlags & FSNA_BULKADD) || !_FilterPidl(pidlNew))
  670. {
  671. int index = (dwFlags & FSNA_ADDDEFAULT) ? _DefaultInsertIndex() : nIndex;
  672. if (_fDropping)
  673. {
  674. if (-1 == _tbim.iButton)
  675. index = 0; // if qlinks has no items, _tbim.iButton is -1, but you can't insert there...
  676. else if (_tbim.dwFlags & TBIMHT_AFTER)
  677. index = _tbim.iButton + 1;
  678. else
  679. index = _tbim.iButton;
  680. // We need to store this as the new order because a drag and drop has occured.
  681. // We will store this order and use it until the end of time.
  682. _fHasOrder = TRUE;
  683. _fChangedOrder = TRUE;
  684. }
  685. _AddPidl(pidlNew, dwFlags, index);
  686. if (!(dwFlags & FSNA_BULKADD))
  687. {
  688. OrderList_Reorder(_hdpa);
  689. }
  690. if (_fDropping)
  691. {
  692. _Dropped(index, FALSE);
  693. _fDropping = FALSE;
  694. }
  695. // NOTE: i'm nuking this call to SetDirty as it doesn't seem
  696. // necessary and we don't have a matching call to _SetDirty(FALSE);
  697. // mismatch of those calls causes nt5 bug #173868. [tjgreen 5-15-98]
  698. //_SetDirty(TRUE);
  699. }
  700. else
  701. {
  702. ILFree(pidlNew);
  703. }
  704. }
  705. }
  706. // This function syncronously removes the button, and deletes it's contents.
  707. // iTBIndex is a toolbar index, not a DPA index.
  708. // This avoids Reentrancy problems, as well as Leaks caused by unhooked toolbars
  709. BOOL_PTR CSFToolbar::InlineDeleteButton(int iTBIndex)
  710. {
  711. BOOL_PTR fRet = FALSE;
  712. TBBUTTONINFO tbbi = {0};
  713. tbbi.cbSize = sizeof(tbbi);
  714. tbbi.dwMask = TBIF_LPARAM | TBIF_BYINDEX;
  715. if (ToolBar_GetButtonInfo(_hwndTB, iTBIndex, &tbbi) >= 0)
  716. {
  717. PIBDATA pibdata = (PIBDATA)tbbi.lParam;
  718. tbbi.lParam = NULL;
  719. ToolBar_SetButtonInfo(_hwndTB, iTBIndex, &tbbi);
  720. fRet = SendMessage(_hwndTB, TB_DELETEBUTTON, iTBIndex, 0);
  721. if (pibdata)
  722. delete pibdata;
  723. }
  724. return fRet;
  725. }
  726. // On a remove, rip out the old button and adjust existing ones
  727. void CSFToolbar::_OnFSNotifyRemove(LPCITEMIDLIST pidl)
  728. {
  729. int i;
  730. LPITEMIDLIST pidlButton;
  731. if (SUCCEEDED(_GetButtonFromPidl(pidl, NULL, &i, &pidlButton)))
  732. {
  733. // remove it from the DPA before nuking the button. There is a rentrancy issue here.
  734. DPA_DeletePtr(_hdpa, i);
  735. InlineDeleteButton(v_DPAIndexToTBIndex(i));
  736. ILFree(pidlButton);
  737. _fChangedOrder = TRUE;
  738. }
  739. }
  740. // On a rename, just change the text of the old button
  741. //
  742. void CSFToolbar::_OnFSNotifyRename(LPCITEMIDLIST pidlFrom, LPCITEMIDLIST pidlTo)
  743. {
  744. TBBUTTONINFO tbbi;
  745. LPITEMIDLIST pidlButton;
  746. int i;
  747. tbbi.dwMask = TBIF_COMMAND | TBIF_LPARAM;
  748. if (SUCCEEDED(_GetButtonFromPidl(pidlFrom, &tbbi, &i, &pidlButton)))
  749. {
  750. LPITEMIDLIST pidlNew;
  751. if (_fFSNotify && !_ptscn)
  752. {
  753. if (FAILED(SHGetRealIDL(_psf, pidlTo, &pidlNew)))
  754. pidlNew = NULL;
  755. }
  756. else
  757. {
  758. pidlNew = ILClone(pidlTo);
  759. }
  760. if (pidlNew)
  761. {
  762. LPITEMIDLIST pidlFree = pidlNew;
  763. PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(_hdpa, i);
  764. if (EVAL(poi))
  765. {
  766. pidlFree = poi->pidl;
  767. poi->pidl = pidlNew;
  768. TCHAR szName[MAX_PATH];
  769. if (SUCCEEDED(DisplayNameOf(_psf, pidlNew, SHGDN_NORMAL, szName, ARRAYSIZE(szName))))
  770. {
  771. // _GetButtonFromPidl filled in tbbi.cbSize and tbbi.idCommand
  772. //
  773. PIBDATA pibdata = (PIBDATA)tbbi.lParam;
  774. if (pibdata)
  775. pibdata->SetOrderItem(poi);
  776. tbbi.dwMask = TBIF_TEXT;
  777. tbbi.pszText = szName;
  778. EVAL(ToolBar_SetButtonInfo(_hwndTB, tbbi.idCommand, &tbbi));
  779. // Just so that it's new location gets persisted
  780. _fChangedOrder = TRUE;
  781. // sync up the orderlist right now so if an updatedir comes in
  782. // it won't think the renamed pidl is new
  783. _RememberOrder();
  784. }
  785. }
  786. ILFree(pidlFree);
  787. }
  788. }
  789. }
  790. // On a complete update remove the old button and add it again
  791. //
  792. void CSFToolbar::_OnFSNotifyUpdate(LPCITEMIDLIST pidl)
  793. {
  794. TBBUTTONINFO tbbi;
  795. tbbi.dwMask = TBIF_COMMAND;
  796. LPITEMIDLIST pidlButton;
  797. if (SUCCEEDED(_GetButtonFromPidl(pidl, &tbbi, NULL, &pidlButton)))
  798. {
  799. TCHAR szName[MAX_PATH];
  800. if (SUCCEEDED(DisplayNameOf(_psf, pidlButton, SHGDN_NORMAL, szName, ARRAYSIZE(szName))))
  801. {
  802. int iBitmap = _GetBitmap(tbbi.idCommand, _IDToPibData(tbbi.idCommand, NULL), FALSE);
  803. if (iBitmap >= 0)
  804. {
  805. tbbi.dwMask = TBIF_IMAGE | TBIF_TEXT;
  806. tbbi.iImage = iBitmap;
  807. tbbi.pszText = szName;
  808. ToolBar_SetButtonInfo(_hwndTB, tbbi.idCommand, &tbbi);
  809. }
  810. }
  811. }
  812. }
  813. void CSFToolbar::_Refresh()
  814. {
  815. if (!_hdpa)
  816. return;
  817. _RememberOrder();
  818. EmptyToolbar();
  819. if (_fShow)
  820. {
  821. _FillToolbar();
  822. }
  823. }
  824. LRESULT CSFToolbar::_OnTimer(WPARAM wParam)
  825. {
  826. return 0;
  827. }
  828. LRESULT CSFToolbar::_DefWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  829. {
  830. switch (uMsg)
  831. {
  832. case WM_DRAWITEM:
  833. case WM_MEASUREITEM:
  834. case WM_INITMENUPOPUP:
  835. case WM_MENUSELECT:
  836. if (_pcm2)
  837. _pcm2->HandleMenuMsg(uMsg, wParam, lParam);
  838. break;
  839. case WM_MENUCHAR:
  840. {
  841. LRESULT lres = 0;
  842. IContextMenu3* pcm3;
  843. if (_pcm2 && SUCCEEDED(_pcm2->QueryInterface(IID_PPV_ARG(IContextMenu3, &pcm3))))
  844. {
  845. pcm3->HandleMenuMsg2(uMsg, wParam, lParam, &lres);
  846. pcm3->Release();
  847. }
  848. return lres;
  849. }
  850. break;
  851. case WM_TIMER:
  852. if (_OnTimer(wParam))
  853. {
  854. return 1;
  855. }
  856. break;
  857. }
  858. return CNotifySubclassWndProc::_DefWindowProc(hwnd, uMsg, wParam, lParam);
  859. }
  860. /*----------------------------------------------------------
  861. Purpose:
  862. For future use. when renaming a parent of this shell folder
  863. we should rebind to it and refill us.
  864. S_OK Indicates successful handling of this notification
  865. S_FALSE Indicates the notification is not a handled situation.
  866. The caller should handle the notification in this case.
  867. Other Failure code indicates a problem. Caller should abort
  868. operation or handle the notification itself.
  869. */
  870. HRESULT CSFToolbar::_OnRenameFolder(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  871. {
  872. return S_FALSE;
  873. }
  874. HRESULT CSFToolbar::OnChange(LONG lEvent, LPCITEMIDLIST pidlOrg1, LPCITEMIDLIST pidlOrg2)
  875. {
  876. HRESULT hr = S_OK;
  877. LPITEMIDLIST pidl1 = (LPITEMIDLIST)pidlOrg1;
  878. LPITEMIDLIST pidl2 = (LPITEMIDLIST)pidlOrg2;
  879. LPITEMIDLIST pidl1ToFree = NULL; // Used if we allocate a pidl that needs to be freed. (::TranslateIDs())
  880. LPITEMIDLIST pidl2ToFree = NULL;
  881. LPITEMIDLIST pidlOut1Event2 = NULL; // Used if we allocate a pidl that needs to be freed. (::TranslateIDs())
  882. LPITEMIDLIST pidlOut2Event2 = NULL;
  883. LONG lEvent2 = (LONG)-1;
  884. AddRef(); // This object could be released during this call
  885. if (_ptscn)
  886. {
  887. hr = _ptscn->TranslateIDs(&lEvent, pidlOrg1, pidlOrg2, &pidl1, &pidl2,
  888. &lEvent2, &pidlOut1Event2, &pidlOut2Event2);
  889. if (SUCCEEDED(hr))
  890. {
  891. // if pidl1 doesn't equal pidlOrg1, then pidl1 was allocated and needs to be freed.
  892. pidl1ToFree = ((pidlOrg1 == pidl1) ? NULL : pidl1);
  893. pidl2ToFree = ((pidlOrg2 == pidl2) ? NULL : pidl2);
  894. ASSERT(NULL == pidl1 || IS_VALID_PIDL(pidl1));
  895. ASSERT(NULL == pidl2 || IS_VALID_PIDL(pidl2));
  896. }
  897. }
  898. if (SUCCEEDED(hr))
  899. {
  900. hr = OnTranslatedChange(lEvent, pidl1, pidl2);
  901. // Do we have a second event to process?
  902. if (SUCCEEDED(hr) && lEvent2 != (LONG)-1)
  903. {
  904. // Yes, then go do it.
  905. hr = OnTranslatedChange(lEvent2, pidlOut1Event2, pidlOut2Event2);
  906. }
  907. ILFree(pidlOut1Event2);
  908. ILFree(pidlOut2Event2);
  909. ILFree(pidl1ToFree);
  910. ILFree(pidl2ToFree);
  911. }
  912. Release();
  913. return hr;
  914. }
  915. #ifdef DEBUG
  916. void DBPrPidl(LPCSTR szPre, LPCITEMIDLIST pidl)
  917. {
  918. TCHAR szName[MAX_PATH];
  919. szName[0] = '\0';
  920. if (pidl)
  921. SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szName, SIZECHARS(szName), NULL);
  922. TraceMsg(TF_WARNING, "%hs%s", szPre, szName);
  923. return;
  924. }
  925. #endif
  926. HRESULT CSFToolbar::OnTranslatedChange(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  927. {
  928. HRESULT hr = S_OK;
  929. BOOL fSizeChanged = FALSE;
  930. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: lEvent=%x", lEvent);
  931. // If we weren't given a pidl we won't register for
  932. // SHChangeNotify calls, but our IShellChange interface
  933. // can still be QI()d so someone could errantly call us.
  934. //
  935. // If we change to using QS() for IShellChange interface
  936. // then we can put this check there...
  937. //
  938. if (NULL == _pidl)
  939. {
  940. // HACKHACK (scotth): resource-based menus (CMenuISF) don't set _pidl.
  941. // Right now allow SHCNE_UPDATEDIR thru...
  942. if (SHCNE_UPDATEDIR == lEvent)
  943. goto HandleUpdateDir;
  944. TraceMsg(TF_WARNING, "CSFToolbar::OnChange - _pidl is NULL");
  945. hr = E_FAIL;
  946. goto CleanUp;
  947. }
  948. if (lEvent & SHCNE_PIDL1ISCHILD)
  949. {
  950. // We only handle notifications for immediate kids. (except SHCNE_RENAMEFOLDER)
  951. //
  952. if (!_IsChildID(pidl1, TRUE))
  953. {
  954. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Not a child. Bailing");
  955. hr = E_FAIL;
  956. goto CleanUp;
  957. }
  958. }
  959. // Have we been shown yet?
  960. if (_hdpa == NULL)
  961. {
  962. // No. Well, then punt this. We'll catch it on the first enum.
  963. hr = E_FAIL;
  964. goto CleanUp;
  965. }
  966. switch (lEvent)
  967. {
  968. case SHCNE_EXTENDED_EVENT:
  969. {
  970. SHChangeDWORDAsIDList UNALIGNED * pdwidl = (SHChangeDWORDAsIDList UNALIGNED *)pidl1;
  971. if (pdwidl->dwItem1 == SHCNEE_ORDERCHANGED)
  972. {
  973. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Reorder event");
  974. // Do this first so that we can say "We can handle it". This prevents the
  975. // mnfolder code that works around a bug in some installers where they don't
  976. // send a Create Folder before the create item in that folder. It causes an
  977. // update dir...
  978. if (!pidl2 || ILIsEqual(_pidl, pidl2))
  979. {
  980. // if this reorder came from us, blow it off
  981. if (!SHChangeMenuWasSentByMe(this, pidl1))
  982. {
  983. // load new order stream
  984. _LoadOrderStream();
  985. // rebuild toolbar
  986. _SetDirty(TRUE);
  987. if (_fShow)
  988. _FillToolbar();
  989. }
  990. hr = S_OK;
  991. }
  992. }
  993. }
  994. break;
  995. case SHCNE_DRIVEADD:
  996. case SHCNE_CREATE:
  997. case SHCNE_MKDIR:
  998. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Adding item");
  999. pidl1 = ILFindLastID(pidl1);
  1000. _OnFSNotifyAdd(pidl1, FSNA_ADDDEFAULT, 0);
  1001. _RememberOrder();
  1002. fSizeChanged = TRUE;
  1003. break;
  1004. case SHCNE_DRIVEREMOVED:
  1005. case SHCNE_DELETE:
  1006. case SHCNE_RMDIR:
  1007. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Removing item");
  1008. pidl1 = ILFindLastID(pidl1);
  1009. _OnFSNotifyRemove(pidl1);
  1010. _RememberOrder();
  1011. fSizeChanged = TRUE;
  1012. break;
  1013. case SHCNE_RENAMEFOLDER:
  1014. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: RenameFolder");
  1015. // Break if notif is handled or if this is not for our kid.
  1016. //
  1017. hr = _OnRenameFolder(pidl1, pidl2);
  1018. if (S_OK == hr)
  1019. {
  1020. fSizeChanged = TRUE;
  1021. break;
  1022. }
  1023. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: RenameFolder Falling through to RenameItem");
  1024. // fall through
  1025. case SHCNE_RENAMEITEM:
  1026. {
  1027. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: RenameItem");
  1028. BOOL fOurKid1, fOurKid2;
  1029. LPCITEMIDLIST p1 = pidl1;
  1030. LPCITEMIDLIST p2 = pidl2;
  1031. pidl1 = ILFindLastID(pidl1);
  1032. pidl2 = ILFindLastID(pidl2);
  1033. // An item can be renamed out of this folder.
  1034. // Convert that into a remove.
  1035. //
  1036. fOurKid1 = _IsChildID(p1, TRUE);
  1037. fOurKid2 = _IsChildID(p2, TRUE);
  1038. if (fOurKid1 && fOurKid2)
  1039. {
  1040. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Rename: Both are children");
  1041. _OnFSNotifyRename(pidl1, pidl2);
  1042. fSizeChanged = TRUE;
  1043. hr = S_OK;
  1044. break;
  1045. }
  1046. else if (fOurKid1)
  1047. {
  1048. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Rename: Only one is a child. Removing pidl 1");
  1049. _OnFSNotifyRemove(pidl1);
  1050. fSizeChanged = TRUE;
  1051. break;
  1052. }
  1053. else if (fOurKid2)
  1054. {
  1055. // An item can be renamed into this folder.
  1056. // Convert that into an add.
  1057. //
  1058. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Rename: Only one is a child. Adding pidl2");
  1059. _OnFSNotifyAdd(pidl2, FSNA_ADDDEFAULT, 0);
  1060. fSizeChanged = TRUE;
  1061. break;
  1062. }
  1063. else
  1064. {
  1065. // (we get here for guys below us who we don't care about,
  1066. // and also for the fallthru from SHCNE_RENAMEFOLDER)
  1067. TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Rename: Not our children");
  1068. /*NOTHING*/
  1069. hr = E_FAIL;
  1070. }
  1071. break;
  1072. }
  1073. case SHCNE_MEDIAINSERTED:
  1074. case SHCNE_MEDIAREMOVED:
  1075. case SHCNE_NETUNSHARE:
  1076. if (_IsEqualID(pidl1))
  1077. goto HandleUpdateDir;
  1078. case SHCNE_NETSHARE:
  1079. case SHCNE_UPDATEITEM:
  1080. if (_IsChildID(pidl1, TRUE))
  1081. {
  1082. pidl1 = ILFindLastID(pidl1);
  1083. _OnFSNotifyUpdate(pidl1);
  1084. fSizeChanged = TRUE;
  1085. }
  1086. break;
  1087. case SHCNE_UPDATEDIR:
  1088. // in OnChange we picked off update dir notify and we didn't translate ids
  1089. // now we can use ILIsEqual -- translate ids won't translate pidls in case
  1090. // of update dir because it looks for immediate child of its, and fails when
  1091. // it receives its own pidl
  1092. // NOTE: When sftbar is registered recursivly, we only get the pidl of the
  1093. // top pane. It is forwarded down to the children. Since this is now a "Child"
  1094. // of the top pane, we check to see if this pidl is a child of that pidl, hence the
  1095. // ILIsParent(pidl1, _pidl)
  1096. // HACKHACK, HUGE HACK: normaly w/ update dir pidl2 is NULL but in start menu
  1097. // augmergeisf can change some other notify (e.g. rename folder) to update dir
  1098. // in which case pidl2 is not null and we have to see if it is our child to do the
  1099. // update (11/18/98) reljai
  1100. if (_IsEqualID(pidl1) || // Calling UpdateDir on _THIS_ folder
  1101. _IsChildID(pidl1, FALSE) || // FEATURE (lamadio) Is this needed?
  1102. _IsChildID(pidl2, FALSE) || // A changed to update (see comment)
  1103. _IsParentID(pidl1)) // Some parent in the chain (because it's recursive)
  1104. {
  1105. HandleUpdateDir:
  1106. // NOTE: if a series of UPDATEIMAGE notifies gets
  1107. // translated to UPDATEDIR and we flicker-perf
  1108. // _FillToolbar, we may lose image updates
  1109. // (in which case, _Refresh would fix it)
  1110. //
  1111. _SetDirty(TRUE);
  1112. _FillToolbar();
  1113. }
  1114. break;
  1115. case SHCNE_ASSOCCHANGED:
  1116. IEInvalidateImageList(); // We may need to use different icons.
  1117. _Refresh(); // full refresh for now.
  1118. break;
  1119. case SHCNE_UPDATEIMAGE: // global
  1120. if (pidl1)
  1121. {
  1122. int iImage = *(int UNALIGNED *)((BYTE *)pidl1 + 2);
  1123. IEInvalidateImageList(); // We may need to use different icons.
  1124. if (pidl2)
  1125. {
  1126. iImage = SHHandleUpdateImage(pidl2);
  1127. if (iImage == -1)
  1128. {
  1129. break;
  1130. }
  1131. }
  1132. if (iImage == -1 || TBHasImage(_hwndTB, iImage))
  1133. {
  1134. _UpdateIconSize(_uIconSize, TRUE);
  1135. _Refresh();
  1136. }
  1137. fSizeChanged = TRUE;
  1138. }
  1139. else
  1140. {
  1141. _Refresh();
  1142. }
  1143. break;
  1144. default:
  1145. hr = E_FAIL;
  1146. break;
  1147. }
  1148. if (fSizeChanged)
  1149. {
  1150. if (_hwndPager)
  1151. SendMessage(_hwndPager, PGMP_RECALCSIZE, (WPARAM) 0, (LPARAM) 0);
  1152. _ToolbarChanged();
  1153. }
  1154. CleanUp:
  1155. return hr;
  1156. }
  1157. BOOL TBHasImage(HWND hwnd, int iImageIndex)
  1158. {
  1159. BOOL fRefresh = FALSE;
  1160. for (int i = ToolBar_ButtonCount(hwnd) - 1 ; i >= 0 ; i--)
  1161. {
  1162. TBBUTTON tbb;
  1163. if (SendMessage(hwnd, TB_GETBUTTON, i, (LPARAM)&tbb))
  1164. {
  1165. if (tbb.iBitmap == iImageIndex)
  1166. {
  1167. fRefresh = TRUE;
  1168. break;
  1169. }
  1170. }
  1171. }
  1172. return fRefresh;
  1173. }
  1174. void CSFToolbar::_SetToolbarState()
  1175. {
  1176. SHSetWindowBits(_hwndTB, GWL_STYLE, TBSTYLE_LIST,
  1177. (_uIconSize != ISFBVIEWMODE_SMALLICONS || _fNoShowText) ? 0 : TBSTYLE_LIST);
  1178. }
  1179. int CSFToolbar::_DefaultInsertIndex()
  1180. {
  1181. return DA_LAST;
  1182. }
  1183. BOOL CSFToolbar::_IsParentID(LPCITEMIDLIST pidl)
  1184. {
  1185. // Is the pidl passed in a parent of one of the IDs in the namespace
  1186. // or the only one i've got?
  1187. if (_ptscn)
  1188. return S_OK == _ptscn->IsEqualID(NULL, pidl);
  1189. else
  1190. return ILIsParent(pidl, _pidl, FALSE);
  1191. }
  1192. BOOL CSFToolbar::_IsEqualID(LPCITEMIDLIST pidl)
  1193. {
  1194. if (_ptscn)
  1195. return S_OK == _ptscn->IsEqualID(pidl, NULL);
  1196. else
  1197. return ILIsEqual(_pidl, pidl);
  1198. }
  1199. BOOL CSFToolbar::_IsChildID(LPCITEMIDLIST pidlChild, BOOL fImmediate)
  1200. {
  1201. BOOL fRet = FALSE;
  1202. if (pidlChild)
  1203. {
  1204. if (_ptscn)
  1205. fRet = S_OK == _ptscn->IsChildID(pidlChild, fImmediate);
  1206. else
  1207. fRet = ILIsParent(_pidl, pidlChild, fImmediate);
  1208. }
  1209. return fRet;
  1210. }
  1211. void CSFToolbar::v_CalcWidth(int* pcxMin, int* pcxMax)
  1212. {
  1213. ASSERT(IS_VALID_WRITE_PTR(pcxMin, int));
  1214. ASSERT(IS_VALID_WRITE_PTR(pcxMax, int));
  1215. // Calculate a decent button width given current state
  1216. HIMAGELIST himl;
  1217. int cxMax = 0;
  1218. int cxMin = 0;
  1219. himl = (HIMAGELIST)SendMessage(_hwndTB, TB_GETIMAGELIST, 0, 0);
  1220. if (himl)
  1221. {
  1222. int cy;
  1223. // Start with the width of the button
  1224. ImageList_GetIconSize(himl, &cxMax, &cy);
  1225. // We want at least a bit of space around the icon
  1226. if (_uIconSize != ISFBVIEWMODE_SMALLICONS)
  1227. cxMax += 20;
  1228. else
  1229. cxMax += 4 * GetSystemMetrics(SM_CXEDGE);
  1230. }
  1231. // Add in any additional space needed
  1232. // Text takes up a bit more space
  1233. if (!_fNoShowText)
  1234. {
  1235. cxMax += 20;
  1236. // Horizontal text takes up a lot
  1237. // if we're smallicon with text (horizontal button)
  1238. // mode, use the minimized metric to mimic the taskbar
  1239. if (_uIconSize == ISFBVIEWMODE_SMALLICONS)
  1240. cxMax = GetSystemMetrics(SM_CXMINIMIZED);
  1241. }
  1242. *pcxMin = cxMin;
  1243. *pcxMax = cxMax;
  1244. }
  1245. // Adjust buttons based on current state.
  1246. //
  1247. void CSFToolbar::_UpdateButtons()
  1248. {
  1249. if (_hwndTB)
  1250. {
  1251. // set "list" (text on right) or not (text underneath)
  1252. // NOTE: list mode always displays some text, don't do it if no text
  1253. _SetToolbarState();
  1254. v_CalcWidth(&_cxMin, &_cxMax);
  1255. SendMessage(_hwndTB, TB_SETBUTTONWIDTH, 0, MAKELONG(_cxMin, _cxMax));
  1256. // We just changed the layout
  1257. //
  1258. SendMessage(_hwndTB, TB_AUTOSIZE, 0, 0);
  1259. if (_hwndPager)
  1260. {
  1261. LRESULT lButtonSize = SendMessage(_hwndTB, TB_GETBUTTONSIZE, 0, 0);
  1262. Pager_SetScrollInfo(_hwndPager, 50, 1, HIWORD(lButtonSize));
  1263. SendMessage(_hwndPager, PGMP_RECALCSIZE, (WPARAM) 0, (LPARAM) 0);
  1264. }
  1265. }
  1266. }
  1267. /*----------------------------------------------------------
  1268. Purpose: Helper function that calls IShellFolder::GetUIObjectOf().
  1269. Returns: pointer to the requested interface
  1270. NULL if failed
  1271. */
  1272. void *CSFToolbar::_GetUIObjectOfPidl(LPCITEMIDLIST pidl, REFIID riid)
  1273. {
  1274. LPCITEMIDLIST * apidl = &pidl;
  1275. void *pv;
  1276. if (FAILED(_psf->GetUIObjectOf(GetHWNDForUIObject(), 1, apidl, riid, 0, &pv)))
  1277. {
  1278. pv = NULL;
  1279. }
  1280. return pv;
  1281. }
  1282. INT_PTR CALLBACK CSFToolbar::_RenameDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1283. {
  1284. switch (uMsg)
  1285. {
  1286. case WM_INITDIALOG:
  1287. {
  1288. ASSERT(lParam);
  1289. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  1290. EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
  1291. // cross-lang platform support
  1292. SHSetDefaultDialogFont(hDlg, IDD_NAME);
  1293. HWND hwndEdit = GetDlgItem(hDlg, IDD_NAME);
  1294. SendMessage(hwndEdit, EM_LIMITTEXT, MAX_PATH - 1, 0);
  1295. TCHAR szText[MAX_PATH + 80];
  1296. TCHAR szTemplate[80];
  1297. HWND hwndLabel = GetDlgItem(hDlg, IDD_PROMPT);
  1298. GetWindowText(hwndLabel, szTemplate, ARRAYSIZE(szTemplate));
  1299. StringCchPrintf(szText, ARRAYSIZE(szText), szTemplate, lParam); // ok to truncate
  1300. SetWindowText(hwndLabel, szText);
  1301. SetWindowText(hwndEdit, (LPTSTR)lParam);
  1302. break;
  1303. }
  1304. case WM_DESTROY:
  1305. SHRemoveDefaultDialogFont(hDlg);
  1306. return FALSE;
  1307. case WM_COMMAND:
  1308. switch (GET_WM_COMMAND_ID(wParam, lParam))
  1309. {
  1310. case IDD_NAME:
  1311. {
  1312. if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_UPDATE)
  1313. {
  1314. LPTSTR lpstrName = (LPTSTR) GetWindowLongPtr(hDlg, DWLP_USER);
  1315. EnableOKButtonFromID(hDlg, IDD_NAME);
  1316. GetDlgItemText(hDlg, IDD_NAME, lpstrName, MAX_PATH);
  1317. }
  1318. break;
  1319. }
  1320. case IDOK:
  1321. {
  1322. TCHAR szTmp[MAX_PATH];
  1323. HRESULT hr = StringCchCopy(szTmp, ARRAYSIZE(szTmp), (LPTSTR)GetWindowLongPtr(hDlg, DWLP_USER));
  1324. if (SUCCEEDED(hr))
  1325. {
  1326. if (PathCleanupSpec(NULL,szTmp))
  1327. {
  1328. HWND hwnd;
  1329. ShellMessageBox(HINST_THISDLL, hDlg,
  1330. MAKEINTRESOURCE(IDS_FAVS_INVALIDFN),
  1331. MAKEINTRESOURCE(IDS_FAVS_ADDTOFAVORITES), MB_OK | MB_ICONHAND);
  1332. hwnd = GetDlgItem(hDlg, IDD_NAME);
  1333. SetWindowText(hwnd, TEXT("\0"));
  1334. EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
  1335. SetFocus(hwnd);
  1336. break;
  1337. }
  1338. }
  1339. }
  1340. // fall through
  1341. case IDCANCEL:
  1342. EndDialog(hDlg, GET_WM_COMMAND_ID(wParam, lParam));
  1343. break;
  1344. default:
  1345. return FALSE;
  1346. }
  1347. break;
  1348. default:
  1349. return FALSE;
  1350. }
  1351. return TRUE;
  1352. }
  1353. // This window proc is used for a temporary worker window that is used to position dialogs
  1354. // as well as maintain the correct Z-Order
  1355. // NOTE: This is used in mnfolder as well.
  1356. LRESULT CALLBACK HiddenWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1357. {
  1358. switch(uMsg)
  1359. {
  1360. // Make sure activation tracks back to the parent.
  1361. case WM_ACTIVATE:
  1362. {
  1363. if (WA_ACTIVE != LOWORD(wParam))
  1364. goto DefWnd;
  1365. SetActiveWindow(GetParent(hwnd));
  1366. return FALSE;
  1367. }
  1368. case WM_WINDOWPOSCHANGING:
  1369. {
  1370. WINDOWPOS* pwp = (WINDOWPOS*)lParam;
  1371. pwp->flags |= SWP_NOOWNERZORDER;
  1372. }
  1373. break;
  1374. }
  1375. DefWnd:
  1376. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1377. }
  1378. HWND CSFToolbar::CreateWorkerWindow()
  1379. {
  1380. if (!_hwndWorkerWindow)
  1381. {
  1382. _hwndWorkerWindow = SHCreateWorkerWindow(HiddenWndProc, GetHWNDForUIObject(), WS_EX_TOOLWINDOW /*| WS_EX_TOPMOST */, WS_POPUP, 0, _hwndTB);
  1383. }
  1384. return _hwndWorkerWindow;
  1385. }
  1386. HRESULT CSFToolbar::_OnRename(POINT *ppt, int id)
  1387. {
  1388. ASSERT(_psf);
  1389. TCHAR szName[MAX_PATH];
  1390. LPITEMIDLIST pidl = ILClone(_IDToPidl(id));
  1391. if (!pidl)
  1392. return E_OUTOFMEMORY;
  1393. _ObtainPIDLName(pidl, szName, ARRAYSIZE(szName));
  1394. // create a temp window so that placement of the dialog will be close to the point.
  1395. // do this so that we'll use USER's code to get placement correctly w/ respect to multimon and work area
  1396. _hwndWorkerWindow = CreateWorkerWindow();
  1397. SetWindowPos(_hwndWorkerWindow, NULL, ppt->x, ppt->y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
  1398. // Now the horrible work of disabling our UI parent window so we can go modal.
  1399. // In an ideal world, we would pass our true parent window and USER will do
  1400. // all the work of modality, but we have to use our worker window thingie
  1401. // to get the dialog positioned correctly with respect to multimon,
  1402. // so we have to find the modal parent and disable him the hard way.
  1403. //
  1404. IUnknown *punkSite;
  1405. IUnknown *punkTLB;
  1406. // Doesn't matter what we SAFECAST "this" to; just pick something to keep the compiler happy
  1407. IUnknown_GetSite(SAFECAST(this, IWinEventHandler*), IID_PPV_ARG(IUnknown, &punkSite));
  1408. IUnknown_QueryService(punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IUnknown, &punkTLB));
  1409. // Tell OLE to go modal
  1410. HRESULT hrModeless = IUnknown_EnableModless(punkTLB, FALSE);
  1411. // Tell USER to go modal
  1412. HWND hwndDisable;
  1413. IUnknown_GetWindow(punkTLB, &hwndDisable);
  1414. BOOL bPrevEnabled = FALSE;
  1415. while (hwndDisable && (GetWindowLong(hwndDisable, GWL_STYLE) & WS_CHILD))
  1416. hwndDisable = GetParent(hwndDisable);
  1417. if (hwndDisable)
  1418. bPrevEnabled = !EnableWindow(hwndDisable, FALSE); // return value of EnableWindow needs to be negated.
  1419. while (1)
  1420. {
  1421. if (DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_ISFBANDRENAME), _hwndWorkerWindow, _RenameDlgProc, (LPARAM)szName) != IDOK)
  1422. break;
  1423. WCHAR wsz[MAX_PATH];
  1424. SHTCharToUnicode(szName, wsz, ARRAYSIZE(wsz));
  1425. // Must re-assert TOPMOSTness so SetNameOf UI will be visible.
  1426. // (We lose it when the user dismisses the dialog box above.)
  1427. // Curiously, the worker window is owned by the app's window, not the
  1428. // menu, so the worker window ends up fighting with the menu to see who is on top
  1429. SetWindowPos(_hwndWorkerWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  1430. if (SUCCEEDED(_psf->SetNameOf(_hwndWorkerWindow, pidl, wsz, 0, NULL)))
  1431. {
  1432. SHChangeNotifyHandleEvents();
  1433. _SaveOrderStream();
  1434. break;
  1435. }
  1436. }
  1437. // (must undo modality in reverse order)
  1438. // Tell USER to return to modeless (as appropriate)
  1439. if (hwndDisable)
  1440. EnableWindow(hwndDisable, bPrevEnabled);
  1441. // Tell OLE to return to modeless (as appropriate)
  1442. if (SUCCEEDED(hrModeless))
  1443. IUnknown_EnableModless(punkTLB, TRUE);
  1444. ATOMICRELEASE(punkTLB);
  1445. ATOMICRELEASE(punkSite);
  1446. ILFree(pidl);
  1447. return S_OK;
  1448. }
  1449. BOOL CSFToolbar::_UpdateIconSize(UINT uIconSize, BOOL fUpdateButtons)
  1450. {
  1451. BOOL fChanged = (_uIconSize != uIconSize);
  1452. _uIconSize = uIconSize;
  1453. TraceMsg(TF_BAND, "ISFBand::_UpdateIconSize going %hs", (_uIconSize == ISFBVIEWMODE_LARGEICONS ? "LARGE" : (_uIconSize == ISFBVIEWMODE_SMALLICONS ? "SMALL" : "LOGOS")));
  1454. if (_hwndTB)
  1455. {
  1456. ATOMICRELEASE(_piml);
  1457. if (!_fNoIcons)
  1458. {
  1459. int iImageList = (_uIconSize == ISFBVIEWMODE_LARGEICONS) ? SHIL_LARGE : SHIL_SYSSMALL;
  1460. SHGetImageList(iImageList, IID_PPV_ARG(IImageList, &_piml));
  1461. }
  1462. // sending a null himl is significant.. it means no image list
  1463. SendMessage(_hwndTB, TB_SETIMAGELIST, 0, (LPARAM)_piml);
  1464. if (fUpdateButtons)
  1465. _UpdateButtons();
  1466. }
  1467. return fChanged;
  1468. }
  1469. HMENU CSFToolbar::_GetContextMenu(IContextMenu* pcm, int* pid)
  1470. {
  1471. HMENU hmenu = CreatePopupMenu();
  1472. if (hmenu)
  1473. {
  1474. UINT fFlags = CMF_CANRENAME;
  1475. if (0 > GetKeyState(VK_SHIFT))
  1476. fFlags |= CMF_EXTENDEDVERBS;
  1477. pcm->QueryContextMenu(hmenu, 0, *pid, CONTEXTMENU_IDCMD_LAST, fFlags);
  1478. }
  1479. return hmenu;
  1480. }
  1481. void CSFToolbar::_OnDefaultContextCommand(int idCmd)
  1482. {
  1483. }
  1484. HRESULT CSFToolbar::_GetTopBrowserWindow(HWND* phwnd)
  1485. {
  1486. IUnknown * punkSite;
  1487. HRESULT hr = IUnknown_GetSite(SAFECAST(this, IWinEventHandler*), IID_PPV_ARG(IUnknown, &punkSite));
  1488. if (SUCCEEDED(hr))
  1489. {
  1490. hr = SHGetTopBrowserWindow(punkSite, phwnd);
  1491. punkSite->Release();
  1492. }
  1493. return hr;
  1494. }
  1495. HRESULT CSFToolbar::_OnOpen(int id, BOOL fExplore)
  1496. {
  1497. HRESULT hr = E_FAIL;
  1498. LPCITEMIDLIST pidl = _IDToPidl(id);
  1499. if (pidl)
  1500. {
  1501. IUnknown* punkSite;
  1502. hr = IUnknown_GetSite(SAFECAST(this, IWinEventHandler*), IID_PPV_ARG(IUnknown, &punkSite));
  1503. if (SUCCEEDED(hr))
  1504. {
  1505. DWORD dwFlags = SBSP_DEFBROWSER | SBSP_DEFMODE;
  1506. if (fExplore)
  1507. dwFlags |= SBSP_EXPLOREMODE;
  1508. hr = SHNavigateToFavorite(_psf, pidl, punkSite, dwFlags);
  1509. punkSite->Release();
  1510. }
  1511. }
  1512. return hr;
  1513. }
  1514. HRESULT CSFToolbar::_HandleSpecialCommand(IContextMenu* pcm, PPOINT ppt, int id, int idCmd)
  1515. {
  1516. TCHAR szCommandString[40];
  1517. HRESULT hr = ContextMenu_GetCommandStringVerb(pcm, idCmd, szCommandString, ARRAYSIZE(szCommandString));
  1518. if (SUCCEEDED(hr))
  1519. {
  1520. if (lstrcmpi(szCommandString, TEXT("rename")) == 0)
  1521. return _OnRename(ppt, id);
  1522. else if (lstrcmpi(szCommandString, TEXT("open")) == 0)
  1523. return _OnOpen(id, FALSE);
  1524. else if (lstrcmpi(szCommandString, TEXT("explore")) == 0)
  1525. return _OnOpen(id, TRUE);
  1526. }
  1527. return S_FALSE;
  1528. }
  1529. LRESULT CSFToolbar::_DoContextMenu(IContextMenu* pcm, LPPOINT ppt, int id, LPRECT prcExclude)
  1530. {
  1531. LRESULT lres = 0;
  1532. int idCmdFirst = CONTEXTMENU_IDCMD_FIRST;
  1533. HMENU hmContext = _GetContextMenu(pcm, &idCmdFirst);
  1534. if (hmContext)
  1535. {
  1536. int idCmd;
  1537. if (_hwndToolTips)
  1538. SendMessage(_hwndToolTips, TTM_ACTIVATE, FALSE, 0L);
  1539. TPMPARAMS tpm;
  1540. TPMPARAMS * ptpm = NULL;
  1541. if (prcExclude)
  1542. {
  1543. tpm.cbSize = sizeof(tpm);
  1544. tpm.rcExclude = *((LPRECT)prcExclude);
  1545. ptpm = &tpm;
  1546. }
  1547. idCmd = TrackPopupMenuEx(hmContext,
  1548. TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  1549. ppt->x, ppt->y, _hwndTB, ptpm);
  1550. if (_hwndToolTips)
  1551. SendMessage(_hwndToolTips, TTM_ACTIVATE, TRUE, 0L);
  1552. if (idCmd)
  1553. {
  1554. // FUSION: When we call out to 3rd party code we want it to use
  1555. // the process default context. This means that the 3rd party code will get
  1556. // v5 in the explorer process. However, if shell32 is hosted in a v6 process,
  1557. // then the 3rd party code will still get v6.
  1558. ULONG_PTR cookie = 0;
  1559. ActivateActCtx(NULL, &cookie);
  1560. if (idCmd < idCmdFirst)
  1561. {
  1562. _OnDefaultContextCommand(idCmd);
  1563. }
  1564. else
  1565. {
  1566. idCmd -= idCmdFirst;
  1567. if (_HandleSpecialCommand(pcm, ppt, id, idCmd) != S_OK)
  1568. {
  1569. _hwndWorkerWindow = CreateWorkerWindow();
  1570. SetWindowPos(_hwndWorkerWindow, NULL, ppt->x, ppt->y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
  1571. CMINVOKECOMMANDINFO ici = {
  1572. sizeof(CMINVOKECOMMANDINFO),
  1573. 0,
  1574. _hwndWorkerWindow,
  1575. MAKEINTRESOURCEA(idCmd),
  1576. NULL, NULL,
  1577. SW_NORMAL,
  1578. };
  1579. pcm->InvokeCommand(&ici);
  1580. }
  1581. }
  1582. if (cookie != 0)
  1583. {
  1584. DeactivateActCtx(0, cookie);
  1585. }
  1586. }
  1587. // if we get this far
  1588. // we need to return handled so that WM_CONTEXTMENU doesn't come through
  1589. lres = 1;
  1590. DestroyMenu(hmContext);
  1591. }
  1592. return lres;
  1593. }
  1594. LRESULT CSFToolbar::_OnContextMenu(WPARAM wParam, LPARAM lParam)
  1595. {
  1596. LRESULT lres = 0;
  1597. RECT rc;
  1598. LPRECT prcExclude = NULL;
  1599. POINT pt;
  1600. int i;
  1601. if (lParam != (LPARAM)-1)
  1602. {
  1603. pt.x = GET_X_LPARAM(lParam);
  1604. pt.y = GET_Y_LPARAM(lParam);
  1605. POINT pt2 = pt;
  1606. MapWindowPoints(HWND_DESKTOP, _hwndTB, &pt2, 1);
  1607. i = ToolBar_HitTest(_hwndTB, &pt2);
  1608. }
  1609. else
  1610. {
  1611. // keyboard context menu.
  1612. i = (int)SendMessage(_hwndTB, TB_GETHOTITEM, 0, 0);
  1613. if (i >= 0)
  1614. {
  1615. SendMessage(_hwndTB, TB_GETITEMRECT, i, (LPARAM)&rc);
  1616. MapWindowPoints(_hwndTB, HWND_DESKTOP, (LPPOINT)&rc, 2);
  1617. pt.x = rc.left;
  1618. pt.y = rc.bottom;
  1619. prcExclude = &rc;
  1620. }
  1621. }
  1622. TraceMsg(TF_BAND, "NM_RCLICK %d,%d = %d", pt.x, pt.y, i);
  1623. if (i >= 0)
  1624. {
  1625. UINT id = _IndexToID(i);
  1626. if (-1 != id)
  1627. {
  1628. LPCITEMIDLIST pidl = _IDToPidl(id, NULL);
  1629. if (pidl)
  1630. {
  1631. LPCONTEXTMENU pcm = (LPCONTEXTMENU)_GetUIObjectOfPidl(pidl, IID_IContextMenu);
  1632. if (pcm)
  1633. {
  1634. // grab pcm2 for owner draw support
  1635. pcm->QueryInterface(IID_PPV_ARG(IContextMenu2, &_pcm2));
  1636. ToolBar_MarkButton(_hwndTB, id, TRUE);
  1637. lres = _DoContextMenu(pcm, &pt, id, prcExclude);
  1638. ToolBar_MarkButton(_hwndTB, id, FALSE);
  1639. if (lres)
  1640. _FlushNotifyMessages(_hwndTB);
  1641. ATOMICRELEASE(_pcm2);
  1642. pcm->Release();
  1643. }
  1644. }
  1645. }
  1646. }
  1647. return lres;
  1648. }
  1649. LRESULT CSFToolbar::_OnCustomDraw(NMCUSTOMDRAW* pnmcd)
  1650. {
  1651. return CDRF_DODEFAULT;
  1652. }
  1653. void CSFToolbar::_OnDragBegin(int iItem, DWORD dwPreferredEffect)
  1654. {
  1655. LPCITEMIDLIST pidl = _IDToPidl(iItem, &_iDragSource);
  1656. ToolBar_SetHotItem(_hwndTB, _iDragSource);
  1657. if (_hwndTB && pidl)
  1658. DragDrop(_hwndTB, _psf, pidl, dwPreferredEffect, NULL);
  1659. _iDragSource = -1;
  1660. }
  1661. LRESULT CSFToolbar::_OnHotItemChange(NMTBHOTITEM * pnm)
  1662. {
  1663. LPNMTBHOTITEM lpnmhi = (LPNMTBHOTITEM)pnm;
  1664. if (_hwndPager && (lpnmhi->dwFlags & (HICF_ARROWKEYS | HICF_ACCELERATOR)))
  1665. {
  1666. int iOldPos, iNewPos;
  1667. RECT rc, rcPager;
  1668. int heightPager;
  1669. int iSelected = lpnmhi->idNew;
  1670. iOldPos = (int)SendMessage(_hwndPager, PGM_GETPOS, (WPARAM)0, (LPARAM)0);
  1671. iNewPos = iOldPos;
  1672. SendMessage(_hwndTB, TB_GETITEMRECT, (WPARAM)iSelected, (LPARAM)&rc);
  1673. if (rc.top < iOldPos)
  1674. {
  1675. iNewPos =rc.top;
  1676. }
  1677. GetClientRect(_hwndPager, &rcPager);
  1678. heightPager = RECTHEIGHT(rcPager);
  1679. if (rc.top >= iOldPos + heightPager)
  1680. {
  1681. iNewPos += (rc.bottom - (iOldPos + heightPager)) ;
  1682. }
  1683. if (iNewPos != iOldPos)
  1684. SendMessage(_hwndPager, PGM_SETPOS, (WPARAM)0, (LPARAM)iNewPos);
  1685. }
  1686. return 0;
  1687. }
  1688. void CSFToolbar::_OnToolTipsCreated(NMTOOLTIPSCREATED* pnm)
  1689. {
  1690. _hwndToolTips = pnm->hwndToolTips;
  1691. SHSetWindowBits(_hwndToolTips, GWL_STYLE, TTS_ALWAYSTIP | TTS_TOPMOST | TTS_NOPREFIX, TTS_ALWAYSTIP | TTS_TOPMOST | TTS_NOPREFIX);
  1692. // set the AutoPopTime (the duration of showing the tooltip) to a large value
  1693. SendMessage(_hwndToolTips, TTM_SETDELAYTIME, TTDT_AUTOPOP, (LPARAM)MAXSHORT);
  1694. }
  1695. LRESULT CSFToolbar::_OnNotify(LPNMHDR pnm)
  1696. {
  1697. LRESULT lres = 0;
  1698. //The following statement traps all pager control notification messages.
  1699. if ((pnm->code <= PGN_FIRST) && (pnm->code >= PGN_LAST))
  1700. {
  1701. return SendMessage(_hwndTB, WM_NOTIFY, (WPARAM)0, (LPARAM)pnm);
  1702. }
  1703. switch (pnm->code)
  1704. {
  1705. case TBN_DRAGOUT:
  1706. {
  1707. TBNOTIFY *ptbn = (TBNOTIFY*)pnm;
  1708. _OnDragBegin(ptbn->iItem, 0);
  1709. lres = 1;
  1710. break;
  1711. }
  1712. case TBN_HOTITEMCHANGE:
  1713. _OnHotItemChange((LPNMTBHOTITEM)pnm);
  1714. break;
  1715. case TBN_GETINFOTIP:
  1716. {
  1717. LPNMTBGETINFOTIP pnmTT = (LPNMTBGETINFOTIP)pnm;
  1718. UINT uiCmd = pnmTT->iItem;
  1719. DWORD dwFlags = _fNoShowText ? QITIPF_USENAME | QITIPF_LINKNOTARGET : QITIPF_LINKNOTARGET;
  1720. if (!GetInfoTipEx(_psf, dwFlags, _IDToPidl(uiCmd), pnmTT->pszText, pnmTT->cchTextMax))
  1721. {
  1722. TBBUTTONINFO tbbi;
  1723. tbbi.cbSize = sizeof(tbbi);
  1724. tbbi.dwMask = TBIF_TEXT;
  1725. tbbi.pszText = pnmTT->pszText;
  1726. tbbi.cchText = pnmTT->cchTextMax;
  1727. lres = (-1 != ToolBar_GetButtonInfo(_hwndTB, uiCmd, &tbbi));
  1728. }
  1729. break;
  1730. }
  1731. //WARNING: Right now I am calling the same function for both A and W version if this notification supports
  1732. // Strings then it needs to thunk. Right now its only used for image
  1733. case TBN_GETDISPINFOA:
  1734. _OnGetDispInfo(pnm, FALSE);
  1735. break;
  1736. case TBN_GETDISPINFOW:
  1737. _OnGetDispInfo(pnm, TRUE);
  1738. break;
  1739. case NM_TOOLTIPSCREATED:
  1740. _OnToolTipsCreated((NMTOOLTIPSCREATED*)pnm);
  1741. break;
  1742. case NM_RCLICK:
  1743. lres = _OnContextMenu(NULL, GetMessagePos());
  1744. break;
  1745. case NM_CUSTOMDRAW:
  1746. return _OnCustomDraw((NMCUSTOMDRAW*)pnm);
  1747. }
  1748. return lres;
  1749. }
  1750. DWORD CSFToolbar::_GetAttributesOfPidl(LPCITEMIDLIST pidl, DWORD dwAttribs)
  1751. {
  1752. if (FAILED(_psf->GetAttributesOf(1, &pidl, &dwAttribs)))
  1753. dwAttribs = 0;
  1754. return dwAttribs;
  1755. }
  1756. PIBDATA CSFToolbar::_IDToPibData(UINT uiCmd, int * piPos)
  1757. {
  1758. PIBDATA pibdata = NULL;
  1759. // Initialize to NULL in case the GetButtonInfo Fails.
  1760. TBBUTTONINFO tbbi = {0};
  1761. tbbi.cbSize = sizeof(tbbi);
  1762. tbbi.dwMask = TBIF_LPARAM;
  1763. int iPos = ToolBar_GetButtonInfo(_hwndTB, uiCmd, &tbbi);
  1764. if (iPos >= 0)
  1765. pibdata = (PIBDATA)tbbi.lParam;
  1766. if (piPos)
  1767. *piPos = iPos;
  1768. return pibdata;
  1769. }
  1770. LPCITEMIDLIST CSFToolbar::_IDToPidl(UINT uiCmd, int *piPos)
  1771. {
  1772. LPCITEMIDLIST pidl;
  1773. PIBDATA pibdata = _IDToPibData(uiCmd, piPos);
  1774. if (pibdata)
  1775. pidl = pibdata->GetPidl();
  1776. else
  1777. pidl = NULL;
  1778. return pidl;
  1779. }
  1780. /*----------------------------------------------------------
  1781. Purpose: IWinEventHandler::OnWinEvent method
  1782. Processes messages passed on from the bandsite.
  1783. */
  1784. HRESULT CSFToolbar::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
  1785. {
  1786. *plres = 0;
  1787. // We are addref'n here because during the course of the
  1788. // Context menu, the view could be changed which free's the menu.
  1789. // We will release after we're sure the this pointer is no longer needed.
  1790. AddRef();
  1791. switch (uMsg)
  1792. {
  1793. case WM_SYSCOLORCHANGE:
  1794. SendMessage(_hwndTB, uMsg, wParam, lParam);
  1795. InvalidateRect(_hwndTB, NULL, TRUE);
  1796. break;
  1797. case WM_PALETTECHANGED:
  1798. InvalidateRect(_hwndTB, NULL, FALSE);
  1799. SendMessage(_hwndTB, uMsg, wParam, lParam);
  1800. break;
  1801. case WM_COMMAND:
  1802. *plres = _OnCommand(wParam, lParam);
  1803. break;
  1804. case WM_NOTIFY:
  1805. *plres = _OnNotify((LPNMHDR)lParam);
  1806. break;
  1807. case WM_CONTEXTMENU:
  1808. *plres = _OnContextMenu(wParam, lParam);
  1809. break;
  1810. }
  1811. Release();
  1812. return S_OK;
  1813. }
  1814. // Map the information loaded (or ctor) into _psf, [_pidl]
  1815. //
  1816. HRESULT CSFToolbar::_AfterLoad()
  1817. {
  1818. HRESULT hr = S_OK;
  1819. // if we have a pidl then we need to get ready
  1820. // for notifications...
  1821. //
  1822. if (_pidl)
  1823. {
  1824. // pidls must be rooted off the desktop
  1825. //
  1826. _fFSNotify = TRUE;
  1827. // shortcut -- just specifying a pidl is good enough
  1828. //
  1829. if (!_psf)
  1830. {
  1831. _fPSFBandDesktop = TRUE;
  1832. hr = IEBindToObject(_pidl, &_psf);
  1833. }
  1834. }
  1835. return hr;
  1836. }
  1837. // IDropTarget implementation
  1838. HRESULT CSFToolbar::GetWindowsDDT(HWND * phwndLock, HWND * phwndScroll)
  1839. {
  1840. *phwndLock = _hwndTB;
  1841. *phwndScroll = _hwndTB;
  1842. return S_OK;
  1843. }
  1844. HRESULT CSFToolbar::HitTestDDT(UINT nEvent, LPPOINT ppt, DWORD_PTR * pdwId, DWORD *pdwEffect)
  1845. {
  1846. TBINSERTMARK tbim;
  1847. switch (nEvent)
  1848. {
  1849. case HTDDT_ENTER:
  1850. return S_OK;
  1851. case HTDDT_OVER:
  1852. {
  1853. int iButton = IBHT_BACKGROUND; // assume we hit the background
  1854. // if we're the source, this may be a move operation
  1855. //
  1856. *pdwEffect = (_iDragSource >= 0) ? DROPEFFECT_MOVE : DROPEFFECT_NONE;
  1857. if (!ToolBar_InsertMarkHitTest(_hwndTB, ppt, &tbim))
  1858. {
  1859. if (tbim.dwFlags & TBIMHT_BACKGROUND)
  1860. {
  1861. RECT rc;
  1862. GetClientRect(_hwndTB, &rc);
  1863. // are we outside the toolbar window entirely?
  1864. if (!PtInRect(&rc, *ppt))
  1865. {
  1866. // rebar already did the hittesting so we are on the rebar
  1867. // but not the toolbar => we are in the title part
  1868. if (!_AllowDropOnTitle())
  1869. {
  1870. // yes; don't allow drop here
  1871. iButton = IBHT_OUTSIDEWINDOW;
  1872. *pdwEffect = DROPEFFECT_NONE;
  1873. }
  1874. // set tbim.iButton to invalid value so we don't draw insert mark
  1875. tbim.iButton = -1;
  1876. }
  1877. }
  1878. else
  1879. {
  1880. // nope, we hit a real button
  1881. //
  1882. if (tbim.iButton == _iDragSource)
  1883. {
  1884. iButton = IBHT_SOURCE; // don't drop on the source button
  1885. }
  1886. else
  1887. {
  1888. iButton = tbim.iButton;
  1889. }
  1890. tbim.iButton = IBHT_BACKGROUND;
  1891. // we never force a move operation if we're on a real button
  1892. *pdwEffect = DROPEFFECT_NONE;
  1893. }
  1894. }
  1895. *pdwId = iButton;
  1896. }
  1897. break;
  1898. case HTDDT_LEAVE:
  1899. // Reset
  1900. tbim.iButton = IBHT_BACKGROUND;
  1901. tbim.dwFlags = 0;
  1902. break;
  1903. default:
  1904. return E_INVALIDARG;
  1905. }
  1906. // update ui
  1907. if (tbim.iButton != _tbim.iButton || tbim.dwFlags != _tbim.dwFlags)
  1908. {
  1909. if (ppt)
  1910. _tbim = tbim;
  1911. // for now I don't want to rely on non-filesystem IShellFolder
  1912. // implementations to call our OnChange method when a drop occurs,
  1913. // so don't even show the insert mark.
  1914. //
  1915. if (_fFSNotify || _iDragSource >= 0)
  1916. {
  1917. DAD_ShowDragImage(FALSE);
  1918. ToolBar_SetInsertMark(_hwndTB, &tbim);
  1919. DAD_ShowDragImage(TRUE);
  1920. }
  1921. }
  1922. return S_OK;
  1923. }
  1924. HRESULT CSFToolbar::GetObjectDDT(DWORD_PTR dwId, REFIID riid, void ** ppvObj)
  1925. {
  1926. HRESULT hr = E_NOINTERFACE;
  1927. *ppvObj = NULL;
  1928. if ((IBHT_SOURCE == dwId) || (IBHT_OUTSIDEWINDOW == dwId))
  1929. {
  1930. // do nothing
  1931. }
  1932. else if (IBHT_BACKGROUND == dwId)
  1933. {
  1934. // nash:41937: not sure how, but _psf can be NULL...
  1935. if (EVAL(_psf))
  1936. hr = _psf->CreateViewObject(_hwndTB, riid, ppvObj);
  1937. }
  1938. else
  1939. {
  1940. LPCITEMIDLIST pidl = _IDToPidl((UINT)dwId, NULL);
  1941. if (pidl)
  1942. {
  1943. *ppvObj = _GetUIObjectOfPidl(pidl, riid);
  1944. if (*ppvObj)
  1945. hr = S_OK;
  1946. }
  1947. }
  1948. //TraceMsg(TF_BAND, "SFToolbar::GetObject(%d) returns %x", dwId, hr);
  1949. return hr;
  1950. }
  1951. HRESULT CSFToolbar::_SaveOrderStream()
  1952. {
  1953. if (_fChangedOrder)
  1954. {
  1955. // Notify everyone that the order changed
  1956. SHSendChangeMenuNotify(this, SHCNEE_ORDERCHANGED, 0, _pidl);
  1957. _fChangedOrder = FALSE;
  1958. return S_OK;
  1959. }
  1960. else
  1961. return S_FALSE;
  1962. }
  1963. void CSFToolbar::_Dropped(int nIndex, BOOL fDroppedOnSource)
  1964. {
  1965. _fDropped = TRUE;
  1966. _fChangedOrder = TRUE;
  1967. // Save new order stream
  1968. _SaveOrderStream();
  1969. if (fDroppedOnSource)
  1970. _FlushNotifyMessages(_hwndTB);
  1971. }
  1972. /*----------------------------------------------------------
  1973. Purpose: CDelegateDropTarget::OnDropDDT
  1974. */
  1975. HRESULT CSFToolbar::OnDropDDT(IDropTarget *pdt, IDataObject *pdtobj, DWORD * pgrfKeyState, POINTL pt, DWORD *pdwEffect)
  1976. {
  1977. // Are we NOT the drag source?
  1978. if (_iDragSource == -1)
  1979. {
  1980. // No, we're not. Well, then the source may be the chevron menu
  1981. // representing the hidden items in this menu. Let's check
  1982. LPITEMIDLIST pidl;
  1983. if (SUCCEEDED(SHPidlFromDataObject2(pdtobj, &pidl)))
  1984. {
  1985. // We've got a pidl, Are we the parent? Do we have a button?
  1986. int iIndex;
  1987. if (ILIsParent(_pidl, pidl, TRUE) &&
  1988. SUCCEEDED(_GetButtonFromPidl(ILFindLastID(pidl), NULL, &iIndex, NULL)))
  1989. {
  1990. // We are the parent! Then let's copy that down and set it
  1991. // as the drag source so that down below we reorder.
  1992. _iDragSource = iIndex;
  1993. }
  1994. ILFree(pidl);
  1995. }
  1996. }
  1997. if (_iDragSource >= 0)
  1998. {
  1999. if (_fAllowReorder)
  2000. {
  2001. TraceMsg(TF_BAND, "SFToolbar::OnDrop reorder %d to %d %s", _iDragSource, _tbim.iButton, _tbim.dwFlags & TBIMHT_AFTER ? "A" : "B");
  2002. int iNewLocation = _tbim.iButton;
  2003. if (_tbim.dwFlags & TBIMHT_AFTER)
  2004. iNewLocation++;
  2005. if (iNewLocation > _iDragSource)
  2006. iNewLocation--;
  2007. if (ToolBar_MoveButton(_hwndTB, _iDragSource, iNewLocation))
  2008. {
  2009. PORDERITEM poi = (PORDERITEM)DPA_DeletePtr(_hdpa, v_TBIndexToDPAIndex(_iDragSource));
  2010. if (poi)
  2011. {
  2012. DPA_InsertPtr(_hdpa, v_TBIndexToDPAIndex(iNewLocation), poi);
  2013. OrderList_Reorder(_hdpa);
  2014. // If we're dropping again, then we don't need the _hdpaOrder...
  2015. OrderList_Destroy(&_hdpaOrder);
  2016. // A reorder has occurred. We need to use the order stream as the order...
  2017. _fHasOrder = TRUE;
  2018. _fDropping = TRUE;
  2019. _Dropped(iNewLocation, TRUE);
  2020. _fDropping = FALSE;
  2021. _RememberOrder();
  2022. _SetDirty(TRUE);
  2023. }
  2024. }
  2025. }
  2026. // Don't forget to reset this!
  2027. _iDragSource = -1;
  2028. DragLeave();
  2029. }
  2030. else
  2031. {
  2032. // We want to override the default to be LINK (SHIFT+CONTROL)
  2033. if (0 == DataObj_GetDWORD(pdtobj, g_cfPreferredDropEffect, 0))
  2034. {
  2035. if (!(*pgrfKeyState & (MK_CONTROL | MK_SHIFT | MK_ALT)))
  2036. {
  2037. // NOTE: not all data objects will allow us to call SetData()
  2038. DataObj_SetDWORD(pdtobj, g_cfPreferredDropEffect, DROPEFFECT_LINK);
  2039. }
  2040. }
  2041. _fDropping = TRUE;
  2042. return S_OK;
  2043. }
  2044. return S_FALSE;
  2045. }
  2046. void CSFToolbar::_SortDPA(HDPA hdpa)
  2047. {
  2048. // If we don't have a _psf, then we certainly can't sort it
  2049. // If we don't have a hdpa, then we certainly can't sort it
  2050. // If the hdpa is empty, then there's no point in sorting it
  2051. if (_psf && hdpa && DPA_GetPtrCount(hdpa))
  2052. {
  2053. ORDERINFO oinfo;
  2054. oinfo.psf = _psf;
  2055. oinfo.psf->AddRef();
  2056. oinfo.dwSortBy = (_fNoNameSort ? OI_SORTBYORDINAL : OI_SORTBYNAME);
  2057. DPA_Sort(hdpa, OrderItem_Compare, (LPARAM)&oinfo);
  2058. oinfo.psf->Release();
  2059. }
  2060. }
  2061. void CSFToolbar::_RememberOrder()
  2062. {
  2063. OrderList_Destroy(&_hdpaOrder);
  2064. if (_hdpa)
  2065. {
  2066. _hdpaOrder = OrderList_Clone(_hdpa);
  2067. _SortDPA(_hdpaOrder);
  2068. }
  2069. }
  2070. HMENU CSFToolbar::_GetBaseContextMenu()
  2071. {
  2072. HMENU hmenu = SHLoadMenuPopup(HINST_THISDLL, MENU_ISFBAND);
  2073. // no logo view, remove the menu item...
  2074. HMENU hView = GetSubMenu(hmenu, 0);
  2075. DeleteMenu(hView, ISFBIDM_LOGOS, MF_BYCOMMAND);
  2076. return hmenu;
  2077. }
  2078. HMENU CSFToolbar::_GetContextMenu()
  2079. {
  2080. HMENU hmenuSrc = _GetBaseContextMenu();
  2081. if (hmenuSrc)
  2082. {
  2083. MENUITEMINFO mii;
  2084. mii.cbSize = sizeof(mii);
  2085. mii.fMask = MIIM_STATE;
  2086. mii.fState = MF_CHECKED;
  2087. UINT uCmdId = ISFBIDM_LOGOS;
  2088. if (_uIconSize != ISFBVIEWMODE_LOGOS)
  2089. uCmdId = (_uIconSize == ISFBVIEWMODE_LARGEICONS ? ISFBIDM_LARGE : ISFBIDM_SMALL);
  2090. SetMenuItemInfo(hmenuSrc, uCmdId, MF_BYCOMMAND, &mii);
  2091. if (!_fNoShowText)
  2092. SetMenuItemInfo(hmenuSrc, ISFBIDM_SHOWTEXT, MF_BYCOMMAND, &mii);
  2093. if (!_fFSNotify || !_pidl || ILIsEmpty(_pidl))
  2094. DeleteMenu(hmenuSrc, ISFBIDM_OPEN, MF_BYCOMMAND);
  2095. HMENU hView = GetSubMenu(hmenuSrc, 0);
  2096. DeleteMenu(hView, ISFBIDM_LOGOS, MF_BYCOMMAND);
  2097. }
  2098. return hmenuSrc;
  2099. }
  2100. // IContextMenu implementation
  2101. //
  2102. HRESULT CSFToolbar::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  2103. {
  2104. HMENU hmenuSrc = _GetContextMenu();
  2105. int i = 0;
  2106. if (hmenuSrc)
  2107. {
  2108. i += Shell_MergeMenus(hmenu, hmenuSrc, indexMenu, idCmdFirst, idCmdLast, 0);
  2109. DestroyMenu(hmenuSrc);
  2110. }
  2111. if (!_pcmSF && _fAllowRename && _psf)
  2112. {
  2113. _psf->CreateViewObject(_hwndTB, IID_PPV_ARG(IContextMenu, &_pcmSF));
  2114. }
  2115. if (_pcmSF)
  2116. {
  2117. _idCmdSF = i - idCmdFirst;
  2118. HRESULT hrT = _pcmSF->QueryContextMenu(hmenu, indexMenu + i, i, 0x7fff, CMF_BANDCMD);
  2119. if (SUCCEEDED(hrT))
  2120. i += HRESULT_CODE(hrT);
  2121. }
  2122. return i;
  2123. }
  2124. BOOL CSFToolbar::_UpdateShowText(BOOL fNoShowText)
  2125. {
  2126. BOOL fChanged = (!_fNoShowText != !fNoShowText);
  2127. _fNoShowText = (fNoShowText != 0);
  2128. TraceMsg(TF_BAND, "ISFBand::_UpdateShowText turning text %hs", _fNoShowText ? "OFF" : "ON");
  2129. if (_hwndTB)
  2130. {
  2131. SendMessage(_hwndTB, TB_SETMAXTEXTROWS, _fNoShowText ? 0 : 1, 0L);
  2132. _UpdateButtons();
  2133. }
  2134. return fChanged;
  2135. }
  2136. HRESULT CSFToolbar::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
  2137. {
  2138. BOOL fChanged = FALSE;
  2139. int idCmd = -1;
  2140. if (!HIWORD(lpici->lpVerb))
  2141. idCmd = LOWORD(lpici->lpVerb);
  2142. switch (idCmd)
  2143. {
  2144. case ISFBIDM_REFRESH:
  2145. _Refresh();
  2146. break;
  2147. case ISFBIDM_OPEN:
  2148. OpenFolderPidl(_pidl);
  2149. break;
  2150. case ISFBIDM_LARGE:
  2151. fChanged = _UpdateIconSize(ISFBVIEWMODE_LARGEICONS, TRUE);
  2152. break;
  2153. case ISFBIDM_SMALL:
  2154. fChanged = _UpdateIconSize(ISFBVIEWMODE_SMALLICONS, TRUE);
  2155. break;
  2156. case ISFBIDM_SHOWTEXT:
  2157. fChanged = _UpdateShowText(!_fNoShowText);
  2158. break;
  2159. default:
  2160. if (_pcmSF && idCmd >= _idCmdSF)
  2161. {
  2162. LPCSTR lpOldVerb = lpici->lpVerb;
  2163. lpici->lpVerb = MAKEINTRESOURCEA(idCmd -= _idCmdSF);
  2164. _pcmSF->InvokeCommand(lpici);
  2165. _FlushNotifyMessages(_hwndTB);
  2166. lpici->lpVerb = lpOldVerb;
  2167. }
  2168. else
  2169. TraceMsg(TF_BAND, "SFToolbar::InvokeCommand %d not handled", idCmd);
  2170. break;
  2171. }
  2172. // Our minimum sizes have changed, notify the bandsite
  2173. //
  2174. if (fChanged)
  2175. _ToolbarChanged();
  2176. return S_OK;
  2177. }
  2178. HRESULT CSFToolbar::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
  2179. {
  2180. return E_NOTIMPL;
  2181. }
  2182. void CSFToolbar::_RegisterToolbar()
  2183. {
  2184. // Since _SubclassWindow protects against multiply subclassing,
  2185. // This call is safe, and ensures that the toolbar is subclassed before
  2186. // even trying to register it for change notify.
  2187. if (_hwndTB && _fRegisterChangeNotify)
  2188. _RegisterChangeNotify();
  2189. CDelegateDropTarget::Init();
  2190. }
  2191. void CSFToolbar::_UnregisterToolbar()
  2192. {
  2193. if (_hwndTB)
  2194. {
  2195. if (_fRegisterChangeNotify)
  2196. _UnregisterChangeNotify();
  2197. _UnsubclassWindow(_hwndTB);
  2198. }
  2199. }
  2200. void CSFToolbar::_RegisterChangeNotify()
  2201. {
  2202. // Since we want to register for change notify ONLY once,
  2203. // and only if this is a file system toolbar.
  2204. if (!_fFSNRegistered && _fFSNotify)
  2205. {
  2206. if (_ptscn)
  2207. _ptscn->Register(_hwndTB, g_idFSNotify, _lEvents);
  2208. else
  2209. _RegisterWindow(_hwndTB, _pidl, _lEvents);
  2210. _fFSNRegistered = TRUE;
  2211. }
  2212. }
  2213. void CSFToolbar::_UnregisterChangeNotify()
  2214. {
  2215. // Only unregister if we have been registered.
  2216. if (_hwndTB && _fFSNRegistered && _fFSNotify)
  2217. {
  2218. _fFSNRegistered = FALSE;
  2219. if (_ptscn)
  2220. _ptscn->Unregister();
  2221. else
  2222. _UnregisterWindow(_hwndTB);
  2223. }
  2224. }
  2225. void CSFToolbar::_ReleaseShellFolder()
  2226. {
  2227. if (_psf)
  2228. {
  2229. IUnknown_SetOwner(_psf, NULL);
  2230. ATOMICRELEASE(_psf);
  2231. }
  2232. ATOMICRELEASE(_ptscn);
  2233. }
  2234. // IWinEventHandler::IsWindowOwner
  2235. HRESULT CSFToolbar::IsWindowOwner(HWND hwnd)
  2236. {
  2237. if (hwnd == _hwndTB ||
  2238. hwnd == _hwndToolTips ||
  2239. hwnd == _hwndPager)
  2240. return S_OK;
  2241. return S_FALSE;
  2242. }