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.

2655 lines
72 KiB

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