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.

3373 lines
102 KiB

  1. #include "shellprv.h"
  2. #include "common.h"
  3. #include "menuband.h"
  4. #include "dpastuff.h" // COrderList_*
  5. #include "resource.h"
  6. #include "mnbase.h"
  7. #include "oleacc.h"
  8. #include "mnfolder.h"
  9. #include "icotask.h"
  10. #include "util.h"
  11. #include <uxtheme.h>
  12. #define PGMP_RECALCSIZE 200
  13. HRESULT IUnknown_RefreshParent(IUnknown* punk, LPCITEMIDLIST pidl, DWORD dwFlags)
  14. {
  15. IShellMenu* psm;
  16. HRESULT hr = IUnknown_QueryService(punk, SID_SMenuBandParent, IID_PPV_ARG(IShellMenu, &psm));
  17. if (SUCCEEDED(hr))
  18. {
  19. LPITEMIDLIST pidlParent = ILClone(pidl);
  20. if (pidlParent)
  21. {
  22. SMDATA smd;
  23. ILRemoveLastID(pidlParent);
  24. smd.dwMask = SMDM_SHELLFOLDER;
  25. smd.pidlFolder = pidlParent;
  26. smd.pidlItem = ILFindLastID(pidl);
  27. hr = psm->InvalidateItem(&smd, dwFlags);
  28. ILFree(pidlParent);
  29. }
  30. psm->Release();
  31. }
  32. return hr;
  33. }
  34. void CMenuData::SetSubMenu(IUnknown* punk)
  35. {
  36. ATOMICRELEASE(_punkSubmenu);
  37. _punkSubmenu = punk;
  38. if (_punkSubmenu)
  39. _punkSubmenu->AddRef();
  40. }
  41. HRESULT CMenuData::GetSubMenu(const GUID* pguidService, REFIID riid, void** ppv)
  42. {
  43. // pguidService is for asking specifically for the Shell Folder portion or the Static portion
  44. if (_punkSubmenu)
  45. {
  46. if (pguidService)
  47. {
  48. return IUnknown_QueryService(_punkSubmenu, *pguidService, riid, ppv);
  49. }
  50. else
  51. return _punkSubmenu->QueryInterface(riid, ppv);
  52. }
  53. else
  54. return E_NOINTERFACE;
  55. }
  56. CMenuData::~CMenuData()
  57. {
  58. ATOMICRELEASE(_punkSubmenu);
  59. }
  60. STDMETHODIMP CMenuSFToolbar::QueryInterface(REFIID riid, void** ppvObj)
  61. {
  62. HRESULT hr = CMenuToolbarBase::QueryInterface(riid, ppvObj);
  63. if (FAILED(hr))
  64. hr = CSFToolbar::QueryInterface(riid, ppvObj);
  65. return hr;
  66. }
  67. //-------------------------------------------------------------------------
  68. //
  69. // CMenuSFToolbar class
  70. //
  71. //-------------------------------------------------------------------------
  72. #define SENTINEL_EMPTY 0
  73. #define SENTINEL_CHEVRON 1
  74. #define SENTINEL_SEP 2
  75. #define PIDLSENTINEL(i) ((LPCITEMIDLIST)MAKEINTRESOURCE(i))
  76. #define POISENTINEL(i) ((PORDERITEM) MAKEINTRESOURCE(i))
  77. STDMETHODIMP CMenuSFToolbar::SetSite(IUnknown* punkSite)
  78. {
  79. HRESULT hr = CMenuToolbarBase::SetSite(punkSite);
  80. if (SUCCEEDED(hr))
  81. {
  82. _fMulticolumnMB = BOOLIFY(_pcmb->_dwFlags & SMINIT_MULTICOLUMN);
  83. _fMulticolumn = _fMulticolumnMB;
  84. _fVertical = _fVerticalMB;
  85. if (_fVerticalMB)
  86. _dwStyle |= CCS_VERT;
  87. }
  88. return hr;
  89. }
  90. CMenuSFToolbar::CMenuSFToolbar(CMenuBand* pmb, IShellFolder* psf, LPCITEMIDLIST pidl, HKEY hKey, DWORD dwFlags)
  91. : CMenuToolbarBase(pmb, dwFlags)
  92. , _idCmdSep(-1)
  93. {
  94. // Change this to IStream
  95. _hKey = hKey;
  96. // Do we have a place to persist our reorder?
  97. if (_hKey == NULL)
  98. {
  99. // No, then don't allow it.
  100. _fAllowReorder = FALSE;
  101. }
  102. _dwStyle |= TBSTYLE_REGISTERDROP;
  103. _dwStyle &= ~TBSTYLE_TOOLTIPS; // We handle our own tooltips.
  104. _iDefaultIconIndex = -1;
  105. SetShellFolder(psf, pidl);
  106. _AfterLoad();
  107. }
  108. HRESULT CMenuSFToolbar::SetShellFolder(IShellFolder* psf, LPCITEMIDLIST pidl)
  109. {
  110. HRESULT hr = CSFToolbar::SetShellFolder(psf, pidl);
  111. ATOMICRELEASE(_pasf2);
  112. if (psf)
  113. psf->QueryInterface(IID_PPV_ARG(IAugmentedShellFolder2, &_pasf2));
  114. if (!_pasf2)
  115. {
  116. // SMSET_SEPARATEMERGEFOLDER requires IAugmentedShellFolder2 support
  117. _dwFlags &= ~SMSET_SEPARATEMERGEFOLDER;
  118. }
  119. if (_dwFlags & SMSET_SEPARATEMERGEFOLDER)
  120. {
  121. // Remember the namespace GUID of the one to highlight
  122. DWORD dwNSId;
  123. if (SUCCEEDED(_pasf2->EnumNameSpace(0, &dwNSId)))
  124. {
  125. _pasf2->QueryNameSpace(dwNSId, &_guidAboveSep, NULL);
  126. }
  127. }
  128. return hr;
  129. }
  130. CMenuSFToolbar::~CMenuSFToolbar()
  131. {
  132. ASSERT(_pcmb->_cRef == 0 || _pcmb->_pmtbShellFolder == NULL);
  133. _hwndWorkerWindow = NULL; // This is destroyed by the _pmbState destructor.
  134. // Prevent a double delete which happens in the base class.
  135. ATOMICRELEASE(_pasf2);
  136. if (_hKey)
  137. RegCloseKey(_hKey);
  138. }
  139. void CMenuSFToolbar::v_Close()
  140. {
  141. CMenuToolbarBase::EmptyToolbar();
  142. _UnregisterToolbar();
  143. if (_hwndPager)
  144. {
  145. DestroyWindow(_hwndPager); // Should Destroy Toolbar.
  146. }
  147. else if (_hwndMB)
  148. {
  149. // In the MultiColumn case, there is no pager so we have to
  150. // manually destroy the Toolbar
  151. DestroyWindow(_hwndMB);
  152. }
  153. _hwndPager = NULL;
  154. _hwndMB = NULL;
  155. _hwndTB = NULL;
  156. }
  157. PIBDATA CMenuSFToolbar::_CreateItemData(PORDERITEM poi)
  158. {
  159. return (PIBDATA)new CMenuData(poi);
  160. }
  161. HRESULT CMenuSFToolbar::_AfterLoad()
  162. {
  163. HRESULT hr = CSFToolbar::_AfterLoad();
  164. if (SUCCEEDED(hr))
  165. _LoadOrderStream();
  166. return hr;
  167. }
  168. HRESULT CMenuSFToolbar::_LoadOrderStream()
  169. {
  170. OrderList_Destroy(&_hdpaOrder);
  171. IStream* pstm;
  172. HRESULT hr = E_FAIL;
  173. if (_hKey)
  174. {
  175. // We use "Menu" for Backwards compatibility with shdoc401 start menu, but having no
  176. // sub key is more correct (Other places use it) so on NT5 we use the new method.
  177. pstm = SHOpenRegStream(_hKey, (_pcmb->_dwFlags & SMINIT_LEGACYMENU) ? TEXT("Menu") : TEXT(""),
  178. TEXT("Order"), STGM_READ);
  179. }
  180. else
  181. {
  182. if (S_FALSE == CallCB(NULL, SMC_GETSFOBJECT, (WPARAM)(GUID*)&IID_IStream, (LPARAM)(void**)&pstm))
  183. pstm = NULL;
  184. }
  185. if (pstm)
  186. {
  187. hr = OrderList_LoadFromStream(pstm, &_hdpaOrder, _psf);
  188. _fHasOrder = FALSE;
  189. _fAllowReorder = TRUE;
  190. // Check to see if we have a persisted order. If we don't have a persisted order,
  191. // then all of the items are -1. If just one of those has a number other than
  192. // -1, then we do have "Order" and should use that instead of alphabetizing.
  193. if (_hdpaOrder)
  194. {
  195. for (int i = 0; !_fHasOrder && i < DPA_GetPtrCount(_hdpaOrder); i++)
  196. {
  197. PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(_hdpaOrder, i);
  198. if (poi->nOrder != MNFOLDER_NORODER)
  199. _fHasOrder = TRUE;
  200. }
  201. }
  202. pstm->Release();
  203. }
  204. return hr;
  205. }
  206. HRESULT CMenuSFToolbar::_SaveOrderStream()
  207. {
  208. IStream* pstm;
  209. HRESULT hr = E_FAIL;
  210. // Persist the new order out to the registry
  211. // It is reasonable to assume that if we don't have an _hdpa we have
  212. // not filled the toolbar yet. Since we have not filled it, we haven't changed
  213. // the order, so we don't need to persist out that order information.
  214. if (_hdpa)
  215. {
  216. // Always save this information
  217. _FindMinPromotedItems(TRUE);
  218. // Did we load an order stream when we initialized this pane?
  219. if (!_fHasOrder)
  220. {
  221. // No; Then we do not want to persist the order. We will initialize
  222. // all of the order items to -1. This is backward compatible because
  223. // IE 4 will merge alphabetically, but revert to a persited order when saving.
  224. for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++)
  225. {
  226. PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(_hdpa, i);
  227. poi->nOrder = MNFOLDER_NORODER;
  228. }
  229. }
  230. if (_hKey)
  231. {
  232. pstm = SHOpenRegStream(_hKey, (_pcmb->_dwFlags & SMINIT_LEGACYMENU) ? TEXT("Menu") : TEXT(""),
  233. TEXT("Order"), STGM_CREATE | STGM_WRITE);
  234. }
  235. else
  236. {
  237. if (S_OK != CallCB(NULL, SMC_GETSFOBJECT, (WPARAM)(GUID*)&IID_IStream, (LPARAM)(void**)&pstm))
  238. pstm = NULL;
  239. }
  240. if (pstm)
  241. {
  242. hr = OrderList_SaveToStream(pstm, _hdpaOrder ? _hdpaOrder : _hdpa, _psf);
  243. if (SUCCEEDED(hr))
  244. {
  245. CallCB(NULL, SMC_SETSFOBJECT, (WPARAM)(GUID*)&IID_IStream, (LPARAM)(void**)&pstm);
  246. }
  247. pstm->Release();
  248. }
  249. }
  250. if (SUCCEEDED(hr))
  251. hr = CSFToolbar::_SaveOrderStream();
  252. return hr;
  253. }
  254. void CMenuSFToolbar::_Dropped(int nIndex, BOOL fDroppedOnSource)
  255. {
  256. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  257. ASSERT(_fDropping);
  258. CSFToolbar::_Dropped(nIndex, fDroppedOnSource);
  259. SHPlaySound(TEXT("MoveMenuItem"));
  260. // Set this to false here because it is ugly that we don't behave like a menu right after a drop.
  261. _fEditMode = FALSE;
  262. // Notify the toplevel menuband of the drop in case it was popped open
  263. // because of the drag/drop event.
  264. //
  265. // (There are some functionality/activation problems if we keep the
  266. // menu up after this case. So to avoid those things at this late date,
  267. // we're going to cancel the menu after a timeout.)
  268. IOleCommandTarget * poct;
  269. _pcmb->QueryService(SID_SMenuBandTop, IID_PPV_ARG(IOleCommandTarget, &poct));
  270. if (poct)
  271. {
  272. poct->Exec(&CGID_MenuBand, MBANDCID_ITEMDROPPED, 0, NULL, NULL);
  273. poct->Release();
  274. }
  275. _pcmb->_fDragEntered = FALSE;
  276. }
  277. HMENU CMenuSFToolbar::_GetContextMenu(IContextMenu* pcm, int* pid)
  278. {
  279. *pid += MNIDM_LAST;
  280. HMENU hmenu = CSFToolbar::_GetContextMenu(pcm, pid);
  281. HMENU hmenu2 = SHLoadMenuPopup(HINST_THISDLL, MENU_MNFOLDERCONTEXT);
  282. // now find the properties insertion point and
  283. int iCount = GetMenuItemCount(hmenu);
  284. for (int i = 0; i < iCount; i++)
  285. {
  286. TCHAR szCommand[40];
  287. UINT id = GetMenuItemID(hmenu, i);
  288. if (IsInRange(id, *pid, 0x7fff))
  289. {
  290. id -= *pid;
  291. ContextMenu_GetCommandStringVerb(pcm, id, szCommand, ARRAYSIZE(szCommand));
  292. if (!lstrcmpi(szCommand, TEXT("properties")))
  293. {
  294. break;
  295. }
  296. }
  297. }
  298. Shell_MergeMenus(hmenu, hmenu2, i, 0, 0x7FFF, 0);
  299. DestroyMenu(hmenu2);
  300. return hmenu;
  301. }
  302. void CMenuSFToolbar::_OnDefaultContextCommand(int idCmd)
  303. {
  304. switch (idCmd)
  305. {
  306. case MNIDM_RESORT:
  307. {
  308. // We used to blow away the order stream and refill, but since we use the order stream
  309. // for calculating the presence of new items, this promoted all of the items were were
  310. // sorting.
  311. HDPA hdpa = _hdpa;
  312. // if we have a _hdpaOrder it may be out of sync and we just want to resort the
  313. // ones in _hdpa anyway so ditch the orderlist.
  314. OrderList_Destroy(&_hdpaOrder);
  315. _SortDPA(hdpa);
  316. OrderList_Reorder(hdpa);
  317. _fChangedOrder = TRUE;
  318. // This call knows about _hdpa and _hdpaOrder
  319. _SaveOrderStream();
  320. // MIKESH: this is needed because otherwise FillToolbar will use the current _hdpa
  321. // and nothing gets changed... I think it's because OrderItem_Compare returns failure on some of the pidls
  322. CMenuToolbarBase::EmptyToolbar();
  323. _SetDirty(TRUE);
  324. _LoadOrderStream();
  325. if (_fShow)
  326. {
  327. _FillToolbar();
  328. }
  329. break;
  330. }
  331. }
  332. }
  333. LRESULT CMenuSFToolbar::_OnContextMenu(WPARAM wParam, LPARAM lParam)
  334. {
  335. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  336. //
  337. // When the NoSetTaskbar restriction is set, this code will disallow
  338. // Context menus. It querys up to the Start menu to ask for permission
  339. // to set.
  340. LRESULT lres = 0;
  341. // No UEM on Context Menus. This avoids the problem where we expand the menubands
  342. // with a context menu present.
  343. _fSuppressUserMonitor = TRUE;
  344. // Allow the selected item to blow away the menus. This is explicitly for the Verbs "Open"
  345. // "Print" and such that launch another process. Inprocess commands are unaffected by this.
  346. LockSetForegroundWindow(LSFW_UNLOCK);
  347. BOOL fOwnerIsTopmost = (WS_EX_TOPMOST & GetWindowLong(_pcmb->_pmbState->GetSubclassedHWND(), GWL_EXSTYLE));
  348. if (fOwnerIsTopmost)
  349. {
  350. ::SetWindowPos(_pcmb->_pmbState->GetSubclassedHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  351. }
  352. KillTimer(_hwndMB, MBTIMER_INFOTIP);
  353. _pcmb->_pmbState->HideTooltip(FALSE);
  354. if (!(_pcmb->_dwFlags & SMINIT_RESTRICT_CONTEXTMENU))
  355. lres = CSFToolbar::_OnContextMenu(wParam, lParam);
  356. if (fOwnerIsTopmost)
  357. {
  358. ::SetWindowPos(_pcmb->_pmbState->GetSubclassedHWND(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
  359. IUnknown_QueryServiceExec(SAFECAST(_pcmb, IOleCommandTarget*), SID_SMenuBandTop,
  360. &CGID_MenuBand, MBANDCID_REPOSITION, TRUE, NULL, NULL);
  361. }
  362. // Take the capture back after the context menu
  363. GetMessageFilter()->RetakeCapture();
  364. return lres;
  365. }
  366. HRESULT CMenuSFToolbar::_GetInfo(LPCITEMIDLIST pidl, SMINFO* psminfo)
  367. {
  368. if (psminfo->dwMask & SMIM_TYPE)
  369. {
  370. psminfo->dwType = SMIT_STRING;
  371. }
  372. if (psminfo->dwMask & SMIM_FLAGS)
  373. {
  374. psminfo->dwFlags = SMIF_ICON | SMIF_DROPTARGET;
  375. }
  376. if (psminfo->dwMask & SMIM_ICON)
  377. {
  378. psminfo->dwMask &= ~SMIM_ICON;
  379. psminfo->iIcon = -1;
  380. }
  381. DWORD dwAttr = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_BROWSABLE;
  382. // Folders that behave like shortcuts should not be considered
  383. // as cascading menu items. Channels are an example.
  384. // HACKHACK: to detect channels, we originally planned to GetUIObject
  385. // IShellLink. But this doesn't work on browser-only b/c it doesn't
  386. // pipe down to the shell extension. So as a hack, we'll key off
  387. // the absence of SFGAO_FILESYSTEM.
  388. // Is this a folder?
  389. // And is it NOT a browseable folder? If it's a Browseable folder, this means that it's a namespace
  390. // such as the Internet Namespace. The Internet name space's shell folder does not return real items, so it
  391. // makes it useless in menus. So, filter it out, and treat it like an item.
  392. HRESULT hr = _psf->GetAttributesOf(1, &pidl, &dwAttr);
  393. if (SUCCEEDED(hr) && ((dwAttr & (SFGAO_FOLDER | SFGAO_BROWSABLE)) == SFGAO_FOLDER))
  394. {
  395. // Since SHIsExpandableFolder is such an expensive call, and we only need
  396. // it for legacy Channels support, only do this call where channels are:
  397. // Favorites menu and Start Menu | Favorites.
  398. if (_dwFlags & SMSET_HASEXPANDABLEFOLDERS)
  399. {
  400. // Yes; but does it also behave like a shortcut?
  401. if (SHIsExpandableFolder(_psf, pidl))
  402. psminfo->dwFlags |= SMIF_SUBMENU;
  403. }
  404. else
  405. {
  406. // We're going to assume that if it's a folder, it really is a folder.
  407. psminfo->dwFlags |= SMIF_SUBMENU;
  408. }
  409. }
  410. CallCB(pidl, SMC_GETSFINFO, 0, (LPARAM)psminfo);
  411. return hr;
  412. }
  413. /*----------------------------------------------------------
  414. Purpose: This function determines the toolbar button style for the
  415. given pidl.
  416. Returns S_OK if pdwMIFFlags is also set (i.e., the object
  417. supported IMenuBandItem to provide more info). S_FALSE if only
  418. *pdwStyle is set.
  419. */
  420. HRESULT CMenuSFToolbar::_TBStyleForPidl(LPCITEMIDLIST pidl,
  421. DWORD * pdwStyle, DWORD* pdwState, DWORD * pdwMIFFlags, int * piIcon)
  422. {
  423. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  424. HRESULT hr = S_FALSE;
  425. DWORD dwStyle = TBSTYLE_BUTTON | TBSTYLE_DROPDOWN | TBSTYLE_NOPREFIX;
  426. *pdwState = TBSTATE_ENABLED;
  427. *pdwMIFFlags = 0;
  428. *piIcon = -1;
  429. if (!IS_INTRESOURCE(pidl))
  430. {
  431. SMINFO sminfo;
  432. sminfo.dwMask = SMIM_TYPE | SMIM_FLAGS | SMIM_ICON;
  433. if (SUCCEEDED(_GetInfo(pidl, &sminfo)))
  434. {
  435. *pdwMIFFlags = sminfo.dwFlags;
  436. if (sminfo.dwFlags & SMIF_ACCELERATOR)
  437. dwStyle &= ~TBSTYLE_NOPREFIX;
  438. if (sminfo.dwType & SMIT_SEPARATOR)
  439. {
  440. dwStyle &= ~TBSTYLE_BUTTON;
  441. dwStyle |= TBSTYLE_SEP;
  442. }
  443. if (sminfo.dwFlags & SMIF_ICON)
  444. *piIcon = sminfo.iIcon;
  445. if (sminfo.dwFlags & SMIF_DEMOTED &&
  446. !_pcmb->_fExpanded)
  447. {
  448. *pdwState |= TBSTATE_HIDDEN;
  449. _fHasDemotedItems = TRUE;
  450. }
  451. if (sminfo.dwFlags & SMIF_HIDDEN)
  452. *pdwState |= TBSTATE_HIDDEN;
  453. hr = S_OK;
  454. }
  455. }
  456. else if (pidl == PIDLSENTINEL(SENTINEL_EMPTY) ||
  457. pidl == PIDLSENTINEL(SENTINEL_CHEVRON))
  458. {
  459. // For null pidls ("empty" menuitems), there is no icon.
  460. // SMIF_DROPTTARGET is set so the user can drop into an empty submenu.
  461. *pdwMIFFlags = SMIF_DROPTARGET;
  462. // Return S_OK so the pdwMIFFlags is examined.
  463. hr = S_OK;
  464. }
  465. else if (pidl == PIDLSENTINEL(SENTINEL_SEP))
  466. {
  467. dwStyle &= ~TBSTYLE_BUTTON;
  468. dwStyle |= TBSTYLE_SEP;
  469. hr = S_OK;
  470. }
  471. *pdwStyle = dwStyle;
  472. return hr;
  473. }
  474. BOOL CMenuSFToolbar::_FilterPidl(LPCITEMIDLIST pidl)
  475. {
  476. BOOL fRet = FALSE;
  477. if (pidl)
  478. {
  479. fRet = (S_OK == CallCB(pidl, SMC_FILTERPIDL, 0, 0));
  480. }
  481. return fRet;
  482. }
  483. //
  484. // Note: This must return exactly TRUE or FALSE.
  485. //
  486. BOOL CMenuSFToolbar::_IsAboveNSSeparator(LPCITEMIDLIST pidl)
  487. {
  488. GUID guidNS;
  489. return (_dwFlags & SMSET_SEPARATEMERGEFOLDER) &&
  490. SUCCEEDED(_pasf2->GetNameSpaceID(pidl, &guidNS)) &&
  491. IsEqualGUID(guidNS, _guidAboveSep);
  492. }
  493. void CMenuSFToolbar::_FillDPA(HDPA hdpa, HDPA hdpaSort, DWORD dwEnumFlags)
  494. {
  495. _fHasSubMenu = FALSE;
  496. CallCB(NULL, SMC_BEGINENUM, (WPARAM)&dwEnumFlags, 0);
  497. CSFToolbar::_FillDPA(hdpa, hdpaSort, dwEnumFlags);
  498. //
  499. // If we are doing separators, make sure all the "above" items
  500. // come before the "below" items.
  501. //
  502. // The items are almost always in the correct order already,
  503. // with maybe one or two exceptions, so optimize for that case.
  504. //
  505. if (_dwFlags & SMSET_SEPARATEMERGEFOLDER)
  506. {
  507. // Invariant: Items 0..i-1 are _guidAboveSep
  508. // Items i..j-1 are not _guidAboveSep
  509. // Items j... are unknown
  510. int i, j;
  511. for (i = j = 0; j < DPA_GetPtrCount(hdpa); j++)
  512. {
  513. PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(hdpa, j);
  514. if (_IsAboveNSSeparator(poi->pidl))
  515. {
  516. if (i != j)
  517. {
  518. DPA_InsertPtr(hdpa, i, DPA_DeletePtr(hdpa, j));
  519. }
  520. i++;
  521. }
  522. }
  523. OrderList_Reorder(hdpa); // Recompute item numbers after we shuffled them around
  524. }
  525. // End of separator enforcement
  526. CallCB(NULL, SMC_ENDENUM, 0, 0);
  527. if (0 == DPA_GetPtrCount(hdpa) && _psf)
  528. {
  529. OrderList_Append(hdpa, NULL, 0); // Add a bogus pidl
  530. _fEmpty = TRUE;
  531. _fHasDemotedItems = FALSE;
  532. if (_dwFlags & SMSET_NOEMPTY)
  533. _fDontShowEmpty = TRUE;
  534. }
  535. else
  536. {
  537. _fEmpty = FALSE;
  538. if (_dwFlags & SMSET_NOEMPTY)
  539. _fDontShowEmpty = FALSE;
  540. }
  541. }
  542. void CMenuSFToolbar::_AddChevron()
  543. {
  544. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  545. // Does this menu get a chevron button?
  546. if (_fHasDemotedItems && !_pcmb->_fExpanded && _idCmdChevron == -1)
  547. {
  548. // Yes; (we shouldn't get here if the menu is empty)
  549. ASSERT(!_fEmpty);
  550. int iIndex;
  551. // Add the chevron to the top or the bottom
  552. if (_dwFlags & SMSET_TOP && _pcmb->_pmtbTop != _pcmb->_pmtbBottom)
  553. iIndex = 0; // add to top
  554. else
  555. iIndex = ToolBar_ButtonCount(_hwndTB); // append to bottom
  556. PIBDATA pibd = _AddOrderItemTB(POISENTINEL(SENTINEL_CHEVRON), iIndex, NULL);
  557. // Remember where the Chevron ended up
  558. if (pibd)
  559. {
  560. _idCmdChevron = GetButtonCmd(_hwndTB, iIndex);
  561. }
  562. }
  563. }
  564. void CMenuSFToolbar::_RemoveChevron()
  565. {
  566. if (-1 != _idCmdChevron)
  567. {
  568. // Yes; remove the chevron
  569. int iPos = ToolBar_CommandToIndex(_hwndTB, _idCmdChevron);
  570. InlineDeleteButton(iPos);
  571. _idCmdChevron = -1;
  572. }
  573. }
  574. //
  575. // Return the index of the first item that goes below the separator.
  576. //
  577. // For large directories this can get called a lot so try to keep
  578. // the running time sublinear.
  579. //
  580. int CMenuSFToolbar::_GetNSSeparatorPlacement()
  581. {
  582. //
  583. // Invariant:
  584. //
  585. // if... then DPA_GetPtrCount(i) is...
  586. // ------------------------ -----------------------------
  587. // i < iLow above the separator
  588. // iLow <= i < iHigh unknown
  589. // iHigh <= i below the separator
  590. //
  591. // where we pretend that item -1 is above the separator and all items
  592. // past the end of the DPA are below the separator.
  593. //
  594. if (!_hdpa) return -1; // Weird low-memory condition
  595. int iLow = 0;
  596. int iHigh = DPA_GetPtrCount(_hdpa);
  597. while (iLow < iHigh)
  598. {
  599. int iMid = (iLow + iHigh) / 2;
  600. PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(_hdpa, iMid);
  601. if (_IsAboveNSSeparator(poi->pidl))
  602. {
  603. iLow = iMid + 1;
  604. }
  605. else
  606. {
  607. iHigh = iMid;
  608. }
  609. }
  610. return iLow;
  611. }
  612. void CMenuSFToolbar::_AddNSSeparator()
  613. {
  614. if (_idCmdSep == -1 && (_dwFlags & SMSET_SEPARATEMERGEFOLDER))
  615. {
  616. int iPos = _GetNSSeparatorPlacement();
  617. if (iPos > 0 && iPos < DPA_GetPtrCount(_hdpa))
  618. {
  619. PIBDATA pibd = _AddOrderItemTB(POISENTINEL(SENTINEL_SEP), iPos, NULL);
  620. if (pibd)
  621. {
  622. _idCmdSep = GetButtonCmd(_hwndTB, iPos);
  623. }
  624. }
  625. }
  626. }
  627. void CMenuSFToolbar::_RemoveNSSeparator()
  628. {
  629. if (-1 != _idCmdSep)
  630. {
  631. // Yes; remove the Sep
  632. int iPos = ToolBar_CommandToIndex(_hwndTB, _idCmdSep);
  633. InlineDeleteButton(iPos);
  634. _idCmdSep = -1;
  635. }
  636. }
  637. // Note! This must return exactly TRUE or FALSE.
  638. BOOL CMenuSFToolbar::_IsBelowNSSeparator(int iIndex)
  639. {
  640. if (_idCmdSep != -1)
  641. {
  642. int iPos = ToolBar_CommandToIndex(_hwndTB, _idCmdSep);
  643. if (iPos >= 0 && iIndex >= iPos)
  644. {
  645. return TRUE;
  646. }
  647. }
  648. return FALSE;
  649. }
  650. //
  651. // Our separator line isn't recorded in the DPA so we need to
  652. // subtract it out...
  653. //
  654. int CMenuSFToolbar::v_TBIndexToDPAIndex(int iTBIndex)
  655. {
  656. if (_IsBelowNSSeparator(iTBIndex))
  657. {
  658. iTBIndex--;
  659. }
  660. return iTBIndex;
  661. }
  662. int CMenuSFToolbar::v_DPAIndexToTBIndex(int iIndex)
  663. {
  664. if (_IsBelowNSSeparator(iIndex))
  665. {
  666. iIndex++;
  667. }
  668. return iIndex;
  669. }
  670. void CMenuSFToolbar::_ToolbarChanged()
  671. {
  672. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  673. _pcmb->_fForceButtonUpdate = TRUE;
  674. // We shouldn't change the size of the menubar while we're in the middle
  675. // of a delete. Wait until we're done...
  676. if (!_fPreventToolbarChange && _fShow && !_fEmptyingToolbar)
  677. {
  678. // Resize the MenuBar
  679. HWND hwndP = _hwndPager ? GetParent(_hwndPager): GetParent(_hwndTB);
  680. if (hwndP && (GetDesktopWindow() != hwndP))
  681. {
  682. RECT rcOld = {0};
  683. RECT rcNew = {0};
  684. GetClientRect(hwndP, &rcOld);
  685. _pcmb->ResizeMenuBar();
  686. GetClientRect(hwndP, &rcNew);
  687. // If the rect sizes haven't changed, then we need to re-layout the
  688. // band because the button widths may have changed.
  689. if (EqualRect(&rcOld, &rcNew))
  690. NegotiateSize();
  691. // This pane may have changed sizes. If there is a sub menu, then
  692. // we need to have them reposition themselves
  693. if (_pcmb->_fInSubMenu && _pcmb->_pmtbTracked)
  694. {
  695. _pcmb->_pmtbTracked->PositionSubmenu(-1);
  696. IUnknown_QueryServiceExec(_pcmb->_pmpSubMenu, SID_SMenuBandChild,
  697. &CGID_MenuBand, MBANDCID_REPOSITION, 0, NULL, NULL);
  698. }
  699. }
  700. }
  701. }
  702. void CMenuSFToolbar::_FillToolbar()
  703. {
  704. // Don't fill the toolbar if we're not dirty or we're emptying the toolbar
  705. // If we try and fill the toolbar while we're emptying we enter a race condition
  706. // where we could AV. This fixes a bug where when dragging a folder into the
  707. // start menu, and cascade a menu, we empty one toolbar, which causes the
  708. // other toolbar to get destroyed, unregister itself, flush the change notify
  709. // queue, causing the original window to empty again... (lamadio) 7.16.98
  710. if (_fDirty && !_fEmptyingToolbar)
  711. {
  712. LPITEMIDLIST pidlItem = NULL;
  713. IShellMenu* psmSubMenu = NULL;
  714. // Populating the menu will take a long time since we're hitting
  715. // the disk. Give the user some feedback if the cursor is
  716. // IDC_ARROW. (If the cursor is something else, then don't
  717. // mess with it.) Note that we have to use (HCURSOR)-1 as a
  718. // sentinel, because it's possible that the current cursor is NULL.
  719. // Prevent _ToolbarChanged from Doing things. (Perf)
  720. _fPreventToolbarChange = TRUE;
  721. _RemoveNSSeparator();
  722. _RemoveChevron(); // Remove the chevron...
  723. if (S_OK == CallCB(NULL, SMC_DUMPONUPDATE, 0, 0))
  724. {
  725. EmptyToolbar();
  726. }
  727. CSFToolbar::_FillToolbar();
  728. // If we had a Chevron before we refreshed the toolbar,
  729. // then we need to add it back.
  730. _AddChevron();
  731. _AddNSSeparator(); // See if we need a separator now
  732. if (_hwndPager)
  733. SendMessage(_hwndPager, PGMP_RECALCSIZE, (WPARAM) 0, (LPARAM) 0);
  734. _fPreventToolbarChange = FALSE;
  735. _ToolbarChanged();
  736. }
  737. }
  738. void CMenuSFToolbar::v_OnDeleteButton(void *pData)
  739. {
  740. CMenuData* pmd = (CMenuData*)pData;
  741. if (pmd)
  742. delete pmd;
  743. }
  744. void CMenuSFToolbar::v_OnEmptyToolbar()
  745. {
  746. CMenuToolbarBase::v_OnEmptyToolbar();
  747. OrderList_Destroy(&_hdpa);
  748. _fDirty = TRUE;
  749. _nNextCommandID = 0;
  750. }
  751. void CMenuSFToolbar::_ObtainPIDLName(LPCITEMIDLIST pidl, LPTSTR psz, int cchMax)
  752. {
  753. // We overload this function because we have some special sentinel pidls
  754. if (!IS_INTRESOURCE(pidl))
  755. {
  756. CSFToolbar::_ObtainPIDLName(pidl, psz, cchMax);
  757. }
  758. else if (pidl == PIDLSENTINEL(SENTINEL_EMPTY))
  759. {
  760. LoadString(HINST_THISDLL, IDS_EMPTY, psz, cchMax);
  761. }
  762. else
  763. {
  764. ASSERT(pidl == PIDLSENTINEL(SENTINEL_CHEVRON) ||
  765. pidl == PIDLSENTINEL(SENTINEL_SEP));
  766. StrCpyN(psz, TEXT(""), cchMax);
  767. }
  768. }
  769. void CMenuSFToolbar::v_NewItem(LPCITEMIDLIST pidl)
  770. {
  771. // This is called when an item is present in the filesystem
  772. // that is not in the order stream. This occurs when an item is
  773. // created when the menu is not up.
  774. // New items are going to have a weird Promotion state
  775. // if there are multiple clients. Each client is going to be the create, and try to increment this.
  776. // We have to syncronize access to this. I'm not sure how to do this.
  777. // Note, this has not been a problem.
  778. // New items get promoted.
  779. CallCB(pidl, SMC_NEWITEM, 0, 0);
  780. // Since this is a new item, we want to increment the promoted items
  781. // so that we can do chevron tracking.
  782. _cPromotedItems++;
  783. }
  784. void CMenuSFToolbar::_SetDirty(BOOL fDirty)
  785. {
  786. if (fDirty)
  787. _pcmb->_fForceButtonUpdate = TRUE;
  788. CSFToolbar::_SetDirty(fDirty);
  789. }
  790. void CMenuSFToolbar::_NotifyBulkOperation(BOOL fStart)
  791. {
  792. if (fStart)
  793. {
  794. _RemoveNSSeparator();
  795. _RemoveChevron();
  796. }
  797. else
  798. {
  799. _AddChevron();
  800. _AddNSSeparator();
  801. }
  802. }
  803. void CMenuSFToolbar::_OnFSNotifyAdd(LPCITEMIDLIST pidl, DWORD dwFlags, int nIndex)
  804. {
  805. DWORD dwEnumFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
  806. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  807. if (!(dwFlags & FSNA_BULKADD))
  808. {
  809. // Removing the separator shuffles the indices around, so compensate for it here
  810. if (_IsBelowNSSeparator(nIndex))
  811. {
  812. nIndex--;
  813. }
  814. if (_fDropping && _tbim.iButton != -1 && _IsBelowNSSeparator(_tbim.iButton))
  815. {
  816. _tbim.iButton--;
  817. }
  818. _NotifyBulkOperation(TRUE); // pretend this is a one-item bulk operation
  819. }
  820. CallCB(NULL, SMC_BEGINENUM, (WPARAM)&dwEnumFlags, 0);
  821. if ((dwFlags & FSNA_ADDDEFAULT) && (_dwFlags & SMSET_SEPARATEMERGEFOLDER) &&
  822. _IsAboveNSSeparator(pidl))
  823. {
  824. dwFlags &= ~FSNA_ADDDEFAULT;
  825. nIndex = _GetNSSeparatorPlacement(); // inserts above the separator
  826. }
  827. CSFToolbar::_OnFSNotifyAdd(pidl, dwFlags, nIndex);
  828. CallCB(NULL, SMC_ENDENUM, 0, 0);
  829. // When we add something to this, we want to promote our parent.
  830. IUnknown_RefreshParent(_pcmb->_punkSite, _pidl, SMINV_PROMOTE);
  831. if (!(dwFlags & FSNA_BULKADD))
  832. {
  833. _NotifyBulkOperation(FALSE); // pretend this is a one-item bulk operation
  834. _SaveOrderStream();
  835. }
  836. }
  837. UINT ToolBar_GetVisibleCount(HWND hwnd)
  838. {
  839. UINT cVis = 0;
  840. int cItems = ToolBar_ButtonCount(hwnd) - 1;
  841. for (; cItems >= 0; cItems--)
  842. {
  843. TBBUTTONINFO tbinfo;
  844. tbinfo.cbSize = sizeof(tbinfo);
  845. tbinfo.dwMask = TBIF_BYINDEX | TBIF_STATE;
  846. if (ToolBar_GetButtonInfo(hwnd, cItems, &tbinfo))
  847. {
  848. if (!(tbinfo.fsState & TBSTATE_HIDDEN))
  849. {
  850. cVis ++;
  851. }
  852. }
  853. }
  854. return cVis;
  855. }
  856. void CMenuSFToolbar::_OnFSNotifyRemove(LPCITEMIDLIST pidl)
  857. {
  858. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  859. int i;
  860. _RemoveNSSeparator();
  861. _RemoveChevron();
  862. // Check to see if this item is a promoted guy...
  863. LPITEMIDLIST pidlButton;
  864. if (SUCCEEDED(_GetButtonFromPidl(pidl, NULL, &i, &pidlButton)))
  865. {
  866. int idCmd = GetButtonCmd(_hwndMB, i);
  867. // Is he promoted?
  868. if (!(v_GetFlags(idCmd) & SMIF_DEMOTED))
  869. {
  870. // Yes, then we need to decrement the promoted count because
  871. // we are removing a promoted guy.
  872. _cPromotedItems--;
  873. // We should expand if we go to zero
  874. if (_cPromotedItems == 0)
  875. {
  876. // Demote the parent
  877. IUnknown_RefreshParent(_pcmb->_punkSite, _pidl, SMINV_DEMOTE | SMINV_NEXTSHOW);
  878. Expand(TRUE);
  879. }
  880. }
  881. if (_pcmb->_fInSubMenu && _pcmb->_nItemSubMenu == idCmd)
  882. _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);
  883. }
  884. CSFToolbar::_OnFSNotifyRemove(pidl);
  885. //Oooppsss, we removed the only string. Replace with our "(Empty)"
  886. // handler....
  887. if (0 == DPA_GetPtrCount(_hdpa) && _psf && _fVerticalMB)
  888. {
  889. ASSERT(_fEmpty == FALSE);
  890. // If we are Empty, then we cannot have any demoted items
  891. // NOTE: We can have no demoted items and not be empty, so one does
  892. // not imply the other.
  893. _fHasDemotedItems = FALSE;
  894. _AddPidl(NULL, FALSE, 0);
  895. _fEmpty = TRUE;
  896. if (_dwFlags & SMSET_NOEMPTY)
  897. _fDontShowEmpty = TRUE;
  898. }
  899. if (_dwFlags & SMSET_COLLAPSEONEMPTY &&
  900. ToolBar_GetVisibleCount(_hwndMB) == 0)
  901. {
  902. // When we don't want to be shown when empty, collapse.
  903. _pcmb->_SiteOnSelect(MPOS_FULLCANCEL);
  904. }
  905. _AddChevron();
  906. _AddNSSeparator();
  907. }
  908. void CMenuSFToolbar::_OnFSNotifyRename(LPCITEMIDLIST pidlFrom, LPCITEMIDLIST pidlTo)
  909. {
  910. if ((_dwFlags & SMSET_SEPARATEMERGEFOLDER) &&
  911. _IsAboveNSSeparator(pidlFrom) != _IsAboveNSSeparator(pidlTo))
  912. {
  913. // This is a rename of something "to itself" but which crosses
  914. // the separator line. Don't handle it as an internal rename;
  915. // just ignore it and let the upcoming updatedir do the real work.
  916. }
  917. else
  918. {
  919. CSFToolbar::_OnFSNotifyRename(pidlFrom, pidlTo);
  920. }
  921. }
  922. void CMenuSFToolbar::NegotiateSize()
  923. {
  924. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  925. HWND hwndP = _hwndPager ? GetParent(_hwndPager): GetParent(_hwndTB);
  926. if (hwndP && (GetDesktopWindow() != hwndP))
  927. {
  928. RECT rc = {0};
  929. GetClientRect(hwndP, &rc);
  930. _pcmb->OnPosRectChangeDB(&rc);
  931. }
  932. }
  933. /*----------------------------------------------------------
  934. Purpose: CDelegateDropTarget::DragEnter
  935. Informs Menuband that a drag has entered it's window.
  936. */
  937. STDMETHODIMP CMenuSFToolbar::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  938. {
  939. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  940. _pcmb->_fDragEntered = TRUE;
  941. IOleCommandTarget * poct;
  942. _pcmb->QueryService(SID_SMenuBandTop, IID_PPV_ARG(IOleCommandTarget, &poct));
  943. if (poct)
  944. {
  945. poct->Exec(&CGID_MenuBand, MBANDCID_DRAGENTER, 0, NULL, NULL);
  946. poct->Release();
  947. }
  948. return CSFToolbar::DragEnter(pdtobj, grfKeyState, pt, pdwEffect);
  949. }
  950. /*----------------------------------------------------------
  951. Purpose: CDelegateDropTarget::DragLeave
  952. Informs Menuband that a drag has left it's window.
  953. */
  954. STDMETHODIMP CMenuSFToolbar::DragLeave(void)
  955. {
  956. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  957. _pcmb->_fDragEntered = FALSE;
  958. IOleCommandTarget * poct;
  959. _pcmb->QueryService(SID_SMenuBandTop, IID_PPV_ARG(IOleCommandTarget, &poct));
  960. if (poct)
  961. {
  962. poct->Exec(&CGID_MenuBand, MBANDCID_DRAGLEAVE, 0, NULL, NULL);
  963. poct->Release();
  964. }
  965. return CSFToolbar::DragLeave();
  966. }
  967. /*----------------------------------------------------------
  968. Purpose: CDelegateDropTarget::HitTestDDT
  969. Returns the ID to pass to GetObject.
  970. 30
  971. */
  972. HRESULT CMenuSFToolbar::HitTestDDT(UINT nEvent, LPPOINT ppt, DWORD_PTR *pdwId, DWORD *pdwEffect)
  973. {
  974. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  975. TBINSERTMARK tbim;
  976. DWORD dwFlags = 0;
  977. BOOL fOnButton = FALSE;
  978. // If we're in drag and drop, Take UEM out of the picture
  979. _fSuppressUserMonitor = TRUE;
  980. // Unlike the CISFBand implementation, we always want to insert
  981. // b/t the menu items. So we return a negative index so the
  982. // GetObject method will treat all the drops as if we're dropping
  983. // in b/t the items, even if the cursor is over a menuitem.
  984. switch (nEvent)
  985. {
  986. case HTDDT_ENTER:
  987. // OLE is in its modal drag/drop loop, and it has the capture.
  988. // We shouldn't take the capture back during this time.
  989. if (!(_pcmb->_dwFlags & SMINIT_RESTRICT_DRAGDROP) &&
  990. (S_FALSE == CallCB(NULL, SMC_SFDDRESTRICTED, NULL, NULL)))
  991. {
  992. // Since we've been entered, set the global state as
  993. // having the drag. If at some point the whole menu
  994. // heirarchy does not have the drag inside of it, we want to
  995. // collapse the menu. This is to prevent the hanging menu syndrome.
  996. _pcmb->_pmbState->HasDrag(TRUE);
  997. KillTimer(_hwndMB, MBTIMER_DRAGPOPDOWN);
  998. GetMessageFilter()->PreventCapture(TRUE);
  999. return S_OK;
  1000. }
  1001. else
  1002. return S_FALSE;
  1003. case HTDDT_OVER:
  1004. BLOCK
  1005. {
  1006. int iButton = -1;
  1007. *pdwEffect = DROPEFFECT_NONE;
  1008. POINT pt = *ppt;
  1009. ClientToScreen(_hwndTB, &pt);
  1010. if (WindowFromPoint(pt) == _hwndPager)
  1011. {
  1012. iButton = IBHT_PAGER;
  1013. tbim.iButton = -1;
  1014. tbim.dwFlags = 0;
  1015. }
  1016. else
  1017. {
  1018. // Are we sitting BETWEEN buttons?
  1019. if (ToolBar_InsertMarkHitTest(_hwndTB, ppt, &tbim))
  1020. {
  1021. // Yes.
  1022. // Is this on the source button?
  1023. if (!(tbim.dwFlags & TBIMHT_BACKGROUND) &&
  1024. tbim.iButton == _iDragSource)
  1025. {
  1026. iButton = IBHT_SOURCE; // Yes; don't drop on the source button
  1027. }
  1028. else
  1029. {
  1030. iButton = tbim.iButton;
  1031. }
  1032. }
  1033. // No we're either sitting on a button or the background. Button?
  1034. else if (tbim.iButton != -1 && !(tbim.dwFlags & TBIMHT_BACKGROUND))
  1035. {
  1036. // On a Button. Cool.
  1037. iButton = tbim.iButton;
  1038. fOnButton = TRUE;
  1039. }
  1040. // Can this drop target even accept the drop?
  1041. int idBtn = GetButtonCmd(_hwndTB, tbim.iButton);
  1042. dwFlags = v_GetFlags(idBtn);
  1043. if (_idCmdChevron != idBtn &&
  1044. !(dwFlags & (SMIF_DROPTARGET | SMIF_DROPCASCADE)) ||
  1045. ((_pcmb->_dwFlags & SMINIT_RESTRICT_DRAGDROP) ||
  1046. (S_OK == CallCB(NULL, SMC_SFDDRESTRICTED, NULL, NULL))))
  1047. {
  1048. // No
  1049. return E_FAIL;
  1050. }
  1051. }
  1052. *pdwId = iButton;
  1053. }
  1054. break;
  1055. case HTDDT_LEAVE:
  1056. // If the dropped occured in this band, then we don't want to collapse the menu
  1057. if (!_fHasDrop)
  1058. {
  1059. // Since we've been left, set the global state. If moving between panes
  1060. // then the pane that will be entered will reset this within the timeout period
  1061. _pcmb->_pmbState->HasDrag(FALSE);
  1062. _SetTimer(MBTIMER_DRAGPOPDOWN);
  1063. }
  1064. // We can take the capture back anytime now
  1065. GetMessageFilter()->PreventCapture(FALSE);
  1066. if (!_fVerticalMB)
  1067. {
  1068. tbim = _tbim;
  1069. }
  1070. else
  1071. {
  1072. // Turn off the insertion mark
  1073. tbim.iButton = -1;
  1074. tbim.dwFlags = 0;
  1075. DAD_ShowDragImage(FALSE);
  1076. ToolBar_SetInsertMark(_hwndTB, &tbim);
  1077. UpdateWindow(_hwndTB);
  1078. DAD_ShowDragImage(TRUE);
  1079. }
  1080. break;
  1081. }
  1082. // Did the drop target change?
  1083. if (tbim.iButton != _tbim.iButton || tbim.dwFlags != _tbim.dwFlags)
  1084. {
  1085. DAD_ShowDragImage(FALSE);
  1086. // Yes
  1087. // If we're sitting on a button, highlight it. Otherwise remove the hightlight.
  1088. //ToolBar_SetHotItem(_hwndTB, fOnButton? tbim.iButton : -1);
  1089. // No.
  1090. // We pop open submenus here during drag and drop. But only
  1091. // if the button has changed (not the flags). Otherwise we'd
  1092. // get flashing submenus as the cursor moves w/in a single item.
  1093. if (tbim.iButton != _tbim.iButton)
  1094. {
  1095. _SetTimer(MBTIMER_DRAGOVER);
  1096. BOOL_PTR fOldAnchor = ToolBar_SetAnchorHighlight(_hwndTB, FALSE);
  1097. ToolBar_SetHotItem(_hwndTB, -1);
  1098. _pcmb->_SiteOnSelect(MPOS_CHILDTRACKING);
  1099. ToolBar_SetAnchorHighlight(_hwndTB, fOldAnchor);
  1100. }
  1101. // for now I don't want to rely on non-filesystem IShellFolder
  1102. // implementations to call our OnChange method when a drop occurs,
  1103. // so don't even show the insert mark.
  1104. // We do not want to display the Insert mark if we do not allow reorder.
  1105. if ((_fFSNotify || _iDragSource >= 0) && (dwFlags & SMIF_DROPTARGET) && _fAllowReorder)
  1106. {
  1107. ToolBar_SetInsertMark(_hwndTB, &tbim);
  1108. }
  1109. if (ppt)
  1110. _tbim = tbim;
  1111. UpdateWindow(_hwndTB);
  1112. DAD_ShowDragImage(TRUE);
  1113. }
  1114. if (!_fVerticalMB && HTDDT_LEAVE == nEvent)
  1115. {
  1116. // Cursor leaving menuband, reset
  1117. _tbim.iButton = -1;
  1118. _iDragSource = -1;
  1119. }
  1120. return S_OK;
  1121. }
  1122. HRESULT CMenuSFToolbar::GetObjectDDT(DWORD_PTR dwId, REFIID riid, void **ppvObj)
  1123. {
  1124. HRESULT hr = E_NOINTERFACE;
  1125. int nID = (int)dwId;
  1126. *ppvObj = NULL;
  1127. if (nID == IBHT_PAGER)
  1128. {
  1129. SendMessage(_hwndPager, PGM_GETDROPTARGET, 0, (LPARAM)ppvObj);
  1130. }
  1131. // Is the target the source?
  1132. else if (nID >= 0)
  1133. {
  1134. // No; does the shellfolder support IDropTarget?
  1135. PORDERITEM poi = (PORDERITEM)DPA_GetPtr(_hdpa, v_TBIndexToDPAIndex(nID));
  1136. if (poi)
  1137. {
  1138. IShellFolder *psfCVO;
  1139. hr = _GetFolderForCreateViewObject(_IsAboveNSSeparator(poi->pidl), &psfCVO);
  1140. if (SUCCEEDED(hr))
  1141. {
  1142. // We want to pass the subclassed HWND, because all we want the parent of the context menus to be
  1143. // the Subclassed window. This is so we don't loose focus and collapse.
  1144. hr = psfCVO->CreateViewObject(_pcmb->_pmbState->GetWorkerWindow(_hwndMB), riid, ppvObj);
  1145. psfCVO->Release();
  1146. }
  1147. }
  1148. }
  1149. if (*ppvObj)
  1150. hr = S_OK;
  1151. //TraceMsg(TF_BAND, "ISFBand::GetObject(%d) returns %x", dwId, hr);
  1152. return hr;
  1153. }
  1154. HRESULT CMenuSFToolbar::_GetFolderForCreateViewObject(BOOL fAbove, IShellFolder **ppsf)
  1155. {
  1156. HRESULT hr;
  1157. *ppsf = NULL;
  1158. if (!(_dwFlags & SMSET_SEPARATEMERGEFOLDER))
  1159. {
  1160. *ppsf = _psf;
  1161. _psf->AddRef();
  1162. return S_OK;
  1163. }
  1164. //
  1165. // Is this folder a mix of items above and below the line?
  1166. //
  1167. DWORD dwIndex;
  1168. DWORD dwNSId;
  1169. GUID guid;
  1170. int cAbove = 0;
  1171. int cBelow = 0;
  1172. for (dwIndex = 0; SUCCEEDED(_pasf2->EnumNameSpace(dwIndex, &dwNSId)) &&
  1173. SUCCEEDED(_pasf2->QueryNameSpace(dwNSId, &guid, NULL));
  1174. dwIndex++)
  1175. {
  1176. if (IsEqualGUID(guid, _guidAboveSep))
  1177. {
  1178. cAbove++;
  1179. }
  1180. else
  1181. {
  1182. cBelow++;
  1183. }
  1184. }
  1185. if (cAbove == 0 || cBelow == 0)
  1186. {
  1187. // No, it's all above or all below; we can (and indeed must) use it.
  1188. // We must use it because it might be User\Start Menu\Subfolder,
  1189. // but we want it to "realize" that it can accept drops from
  1190. // a common folder and should create All Users\Start Menu\Subfolder
  1191. // as a result.
  1192. *ppsf = _psf;
  1193. _psf->AddRef();
  1194. return S_OK;
  1195. }
  1196. //
  1197. // Create a subset of the merged shell folder to describe
  1198. // only the part above or below the line.
  1199. //
  1200. //
  1201. // Create another instance of whatever shellfolder we have.
  1202. //
  1203. CLSID clsid;
  1204. hr = IUnknown_GetClassID(_psf, &clsid);
  1205. if (FAILED(hr))
  1206. {
  1207. return hr;
  1208. }
  1209. IAugmentedShellFolder *pasfNew;
  1210. hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC, IID_PPV_ARG(IAugmentedShellFolder, &pasfNew));
  1211. if (SUCCEEDED(hr))
  1212. {
  1213. // See how many namespaces there are...
  1214. hr = _pasf2->EnumNameSpace((DWORD)-1, NULL);
  1215. if (SUCCEEDED(hr))
  1216. {
  1217. // Allocate enough memory to keep track of them...
  1218. DWORD cNamespacesAlloc = HRESULT_CODE(hr);
  1219. // There had better be some namespaces because we don't even get
  1220. // here unless we find one "above" and one "below" namespace.
  1221. ASSERT(cNamespacesAlloc);
  1222. DWORD cNamespaces = 0;
  1223. QUERYNAMESPACEINFO *rgqnsi = new QUERYNAMESPACEINFO[cNamespacesAlloc];
  1224. if (rgqnsi)
  1225. {
  1226. IAugmentedShellFolder3 *pasf3;
  1227. hr = _pasf2->QueryInterface(IID_PPV_ARG(IAugmentedShellFolder3, &pasf3));
  1228. if (SUCCEEDED(hr))
  1229. {
  1230. // Collect information about them all, keep the ones we like.
  1231. //
  1232. // Note that in this loop we do not save the error failure from EnumNameSpace
  1233. // because we expect it to fail (with NO_MORE_ITEMS) and we don't want
  1234. // that to cause us to panic and bail out
  1235. DWORD dwFlagsMissing = ASFF_DEFNAMESPACE_ALL;
  1236. QUERYNAMESPACEINFO *pqnsi = rgqnsi;
  1237. for (dwIndex = 0; SUCCEEDED(pasf3->EnumNameSpace(dwIndex, &dwNSId)); dwIndex++)
  1238. {
  1239. ASSERT(cNamespaces < cNamespacesAlloc); // if this assert fires, then EnumNameSpace lied to us!
  1240. pqnsi->cbSize = sizeof(QUERYNAMESPACEINFO);
  1241. pqnsi->dwMask = ASFQNSI_FLAGS | ASFQNSI_FOLDER | ASFQNSI_GUID | ASFQNSI_PIDL;
  1242. hr = pasf3->QueryNameSpace2(dwNSId, pqnsi);
  1243. if (FAILED(hr))
  1244. {
  1245. break; // Fail!
  1246. }
  1247. if (BOOLIFY(IsEqualGUID(pqnsi->guidObject, _guidAboveSep)) == fAbove)
  1248. {
  1249. dwFlagsMissing &= ~pqnsi->dwFlags;
  1250. pqnsi++;
  1251. cNamespaces++;
  1252. }
  1253. else
  1254. {
  1255. ATOMICRELEASE(pqnsi->psf);
  1256. ILFree(pqnsi->pidl);
  1257. }
  1258. }
  1259. // Any ASFF_DEFNAMESPACE_* flags that nobody claimed,
  1260. // give them to the first namespace.
  1261. if (cNamespaces)
  1262. {
  1263. rgqnsi[0].dwFlags |= dwFlagsMissing;
  1264. }
  1265. // Add in all the namespaces
  1266. for (dwIndex = 0; SUCCEEDED(hr) && dwIndex < cNamespaces; dwIndex++)
  1267. {
  1268. hr = pasfNew->AddNameSpace(&rgqnsi[dwIndex].guidObject,
  1269. rgqnsi[dwIndex].psf,
  1270. rgqnsi[dwIndex].pidl,
  1271. rgqnsi[dwIndex].dwFlags);
  1272. }
  1273. pasf3->Release();
  1274. } // QueryInterface
  1275. //
  1276. // Now free all the memory we allocated...
  1277. //
  1278. for (dwIndex = 0; dwIndex < cNamespaces; dwIndex++)
  1279. {
  1280. ATOMICRELEASE(rgqnsi[dwIndex].psf);
  1281. ILFree(rgqnsi[dwIndex].pidl);
  1282. }
  1283. delete [] rgqnsi;
  1284. }
  1285. else
  1286. {
  1287. hr = E_OUTOFMEMORY; // "new" failed
  1288. }
  1289. }
  1290. if (SUCCEEDED(hr))
  1291. {
  1292. *ppsf = pasfNew; // transfer ownership to caller
  1293. }
  1294. else
  1295. {
  1296. pasfNew->Release();
  1297. }
  1298. }
  1299. return hr;
  1300. }
  1301. // S_OK if the drop was handled. Otherwise S_FALSE.
  1302. HRESULT CMenuSFToolbar::OnDropDDT(IDropTarget *pdt, IDataObject *pdtobj, DWORD * pgrfKeyState, POINTL pt, DWORD *pdwEffect)
  1303. {
  1304. // Since the modal drag-drop loop released the capture, take it
  1305. // back so we behave properly.
  1306. KillTimer(_hwndMB, MBTIMER_DRAGPOPDOWN);
  1307. HRESULT hr = S_FALSE;
  1308. // If this drop came from us, but it came from the other side of the
  1309. // separator, then act as if it didn't come from us after all.
  1310. if (_iDragSource >= 0 &&
  1311. (_dwFlags & SMSET_SEPARATEMERGEFOLDER) &&
  1312. _IsBelowNSSeparator(_iDragSource) != _IsBelowNSSeparator(_tbim.iButton))
  1313. {
  1314. _iDragSource = -1; // act as if the object came from somewhere else
  1315. }
  1316. // We need to say that the last drag leave is really the drop.
  1317. _fHasDrop = TRUE;
  1318. _idCmdDragging = -1;
  1319. LockSetForegroundWindow(LSFW_LOCK);
  1320. // Only send an hwnd to the callback if the drop source is external
  1321. if (!(_pcmb->_dwFlags & SMINIT_RESTRICT_DRAGDROP) &&
  1322. (S_FALSE == CallCB(NULL, SMC_SFDDRESTRICTED, (WPARAM)pdtobj,
  1323. (LPARAM)(_iDragSource < 0 ? GetHWNDForUIObject() : NULL))))
  1324. {
  1325. int iButtonOrig = _tbim.iButton;
  1326. // Removing the separator shuffles the indices around, so compensate for it here
  1327. if (_iDragSource >= 0 && _IsBelowNSSeparator(_iDragSource))
  1328. {
  1329. _iDragSource--;
  1330. }
  1331. if (_tbim.iButton != -1 && _IsBelowNSSeparator(_tbim.iButton))
  1332. {
  1333. _tbim.iButton--;
  1334. }
  1335. _RemoveNSSeparator();
  1336. _RemoveChevron();
  1337. hr = CSFToolbar::OnDropDDT(pdt, pdtobj, pgrfKeyState, pt, pdwEffect);
  1338. _AddChevron();
  1339. _AddNSSeparator();
  1340. _tbim.iButton = iButtonOrig;
  1341. }
  1342. LockSetForegroundWindow(LSFW_UNLOCK);
  1343. return hr;
  1344. }
  1345. PIBDATA CMenuSFToolbar::_AddOrderItemTB(PORDERITEM poi, int index, TBBUTTON* ptbb)
  1346. {
  1347. PIBDATA pibd = CSFToolbar::_AddOrderItemTB(poi, index, ptbb);
  1348. if (pibd)
  1349. {
  1350. if (pibd->GetFlags() & SMIF_SUBMENU)
  1351. {
  1352. _fHasSubMenu = TRUE;
  1353. }
  1354. }
  1355. return pibd;
  1356. }
  1357. BOOL CMenuSFToolbar::_AddPidl(LPITEMIDLIST pidl, DWORD dwFlags, int index)
  1358. {
  1359. BOOL bRet;
  1360. // Is this item being added to an empty menu?
  1361. // also, if the pidl is null then that means we're trying to add an "empty"
  1362. // tag. in that case we don't want to run through the code of preemptively
  1363. // removing the "empty" menu item and then adding it back on failure,
  1364. // so we go right to the else statement.
  1365. if (_fEmpty && pidl)
  1366. {
  1367. // Yes; remove the empty menu item
  1368. InlineDeleteButton(0);
  1369. DPA_DeletePtr(_hdpa, 0);
  1370. _fEmpty = FALSE;
  1371. if (_dwFlags & SMSET_NOEMPTY)
  1372. _fDontShowEmpty = FALSE;
  1373. bRet = CSFToolbar::_AddPidl(pidl, dwFlags, index);
  1374. // Failed to add new item?
  1375. if (!bRet)
  1376. {
  1377. // Yes; add the empty menu item back
  1378. OrderList_Append(_hdpa, NULL, 0); // Add a bogus pidl
  1379. _fEmpty = TRUE;
  1380. _fHasDemotedItems = FALSE;
  1381. if (_dwFlags & SMSET_NOEMPTY)
  1382. _fDontShowEmpty = TRUE;
  1383. }
  1384. }
  1385. else
  1386. bRet = CSFToolbar::_AddPidl(pidl, dwFlags, index);
  1387. return bRet;
  1388. }
  1389. BOOL CMenuSFToolbar::_ReBindToFolder(LPCITEMIDLIST pidl)
  1390. {
  1391. // We may be able to share this code with the code in _FillToolbar, but the difference is,
  1392. // in Fill Toolbar, the Toolbar Button does not have a Sub Menu. We reinitialize one we save away,
  1393. // and force it back into the child button. Here, we have the luxury of having the Sub Menu still
  1394. // in the toolbar button. I may be able to extract common code into a separate function. Left
  1395. // as an exercise to the reader.
  1396. // Need special Handling for this. We need to free the sub menu and
  1397. // rebind to it ifit's up.
  1398. BOOL fBound = FALSE;
  1399. TBBUTTONINFO tbinfo = {0};
  1400. tbinfo.dwMask = TBIF_COMMAND | TBIF_LPARAM;
  1401. LPITEMIDLIST pidlItem;
  1402. if (SUCCEEDED(_GetButtonFromPidl(ILFindLastID(pidl), &tbinfo, NULL, &pidlItem)))
  1403. {
  1404. CMenuData* pmd = (CMenuData*)tbinfo.lParam;
  1405. if (EVAL(pmd))
  1406. {
  1407. IShellFolderBand* psfb;
  1408. // We have the Toolbar button into, we should see if it has a sub menu associated with it.
  1409. if (SUCCEEDED(pmd->GetSubMenu(&SID_MenuShellFolder, IID_PPV_ARG(IShellFolderBand, &psfb))))
  1410. {
  1411. // It does. Then reuse!
  1412. LPITEMIDLIST pidlFull = NULL;
  1413. IShellFolder* psf = NULL;
  1414. if (_pasf2)
  1415. {
  1416. LPITEMIDLIST pidlFolder, pidlChild;
  1417. // Remember: Folder pidls must be unwrapped.
  1418. _pasf2->UnWrapIDList(pidlItem, 1, NULL, &pidlFolder, &pidlChild, NULL);
  1419. pidlFull = ILCombine(pidlFolder, pidlChild);
  1420. ILFree(pidlChild);
  1421. ILFree(pidlFolder);
  1422. }
  1423. else
  1424. {
  1425. // Not a wrapped guy, Sweet!
  1426. pidlFull = ILCombine(_pidl, pidlItem);
  1427. }
  1428. _psf->BindToObject(pidlItem, NULL, IID_PPV_ARG(IShellFolder, &psf));
  1429. if (psf)
  1430. {
  1431. if (pidlFull)
  1432. {
  1433. fBound = SUCCEEDED(psfb->InitializeSFB(psf, pidlFull));
  1434. if (fBound)
  1435. {
  1436. _pcmb->_nItemSubMenu = tbinfo.idCommand;
  1437. }
  1438. }
  1439. psf->Release();
  1440. }
  1441. ILFree(pidlFull);
  1442. psfb->Release();
  1443. }
  1444. }
  1445. }
  1446. return fBound;
  1447. }
  1448. HRESULT CMenuSFToolbar::OnTranslatedChange(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1449. {
  1450. HRESULT hr = CSFToolbar::OnTranslatedChange(lEvent, pidl1, pidl2);
  1451. if (SUCCEEDED(hr))
  1452. {
  1453. switch(lEvent)
  1454. {
  1455. case SHCNE_RENAMEFOLDER:
  1456. if (_IsChildID(pidl2, TRUE))
  1457. {
  1458. _ReBindToFolder(pidl2);
  1459. }
  1460. break;
  1461. case SHCNE_RMDIR:
  1462. if (_IsChildID(pidl1, TRUE))
  1463. {
  1464. _ReBindToFolder(pidl1);
  1465. }
  1466. break;
  1467. case SHCNE_EXTENDED_EVENT:
  1468. {
  1469. SHChangeDWORDAsIDList UNALIGNED * pdwidl = (SHChangeDWORDAsIDList UNALIGNED *)pidl1;
  1470. if (_IsChildID(pidl2, TRUE))
  1471. {
  1472. if (!SHChangeMenuWasSentByMe(this, pidl1))
  1473. {
  1474. DWORD dwFlags = SMINV_NOCALLBACK; // So that we don't doubly increment
  1475. SMDATA smd = {0};
  1476. smd.dwMask = SMDM_SHELLFOLDER;
  1477. smd.pidlFolder = _pidl;
  1478. smd.pidlItem = ILFindLastID(pidl2);
  1479. // Syncronize Promotion state.
  1480. if (pdwidl->dwItem1 == SHCNEE_PROMOTEDITEM)
  1481. {
  1482. dwFlags |= SMINV_PROMOTE;
  1483. }
  1484. else if (pdwidl->dwItem1 == SHCNEE_DEMOTEDITEM)
  1485. {
  1486. dwFlags |= SMINV_DEMOTE;
  1487. }
  1488. // Are we actually doing something?
  1489. if (SMINV_NOCALLBACK != dwFlags)
  1490. {
  1491. v_InvalidateItem(&smd, dwFlags);
  1492. }
  1493. }
  1494. }
  1495. }
  1496. break;
  1497. default:
  1498. break;
  1499. }
  1500. }
  1501. return hr;
  1502. }
  1503. // IShellChangeNotify::OnChange
  1504. HRESULT CMenuSFToolbar::OnChange(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1505. {
  1506. HRESULT hr = E_FAIL;
  1507. // If we're in the middle of being destroyed, don't process this.
  1508. if (!_hwndMB)
  1509. return S_OK;
  1510. AddRef();
  1511. _pcmb->_pmbState->PushChangeNotify();
  1512. SMCSHCHANGENOTIFYSTRUCT shns;
  1513. shns.lEvent = lEvent;
  1514. shns.pidl1 = pidl1;
  1515. shns.pidl2 = pidl2;
  1516. CallCB(NULL, SMC_SHCHANGENOTIFY, NULL, (LPARAM)&shns); // Ignore return value. Notify only.
  1517. // Since we may be removing the selected item, we want the selection to move to the next item
  1518. int iHot = ToolBar_GetHotItem(_hwndMB);
  1519. hr = CSFToolbar::OnChange(lEvent, pidl1, pidl2);
  1520. // Is this a child of this toolbar is some shape or form?
  1521. // 1) The changing pidl is a child of this pane.
  1522. // 2) What the pidl is changing to is in this pane (For renames)
  1523. // 3) Updatedirs. Recursive change notifies must forward update dirs all the way down the chain.
  1524. // 4) EXTENDED events with a pidl2 == NULL. This means Reorder all your items.
  1525. if (_IsChildID(pidl1, FALSE) ||
  1526. _IsChildID(pidl2, FALSE) ||
  1527. lEvent == SHCNE_UPDATEDIR ||
  1528. (lEvent == SHCNE_EXTENDED_EVENT &&
  1529. pidl2 == NULL))
  1530. {
  1531. // We need to forward this down then.
  1532. HRESULT hrInner = _pcmb->ForwardChangeNotify(lEvent, pidl1, pidl2);
  1533. // Did either of us handle this change?
  1534. if (SUCCEEDED(hrInner) || SUCCEEDED(hr))
  1535. {
  1536. hr = S_OK;
  1537. }
  1538. else if (lEvent != SHCNE_EXTENDED_EVENT) // Don't bother with extended events...
  1539. {
  1540. // Ok so neither of us handled this?
  1541. // Must be the SHChangeNotifyCollapsing code that collapses
  1542. // the Directory Create and item create into a single item create.
  1543. // We need to force an update dir on ourselves so that we get this change.
  1544. hr = CSFToolbar::OnChange(SHCNE_UPDATEDIR, pidl1, pidl2);
  1545. }
  1546. }
  1547. // Set the hot item back, wrapping if necessary.
  1548. if (ToolBar_GetHotItem(_hwndMB) != iHot)
  1549. SetHotItem(1, iHot, -1, 0);
  1550. _pcmb->_pmbState->PopChangeNotify();
  1551. Release();
  1552. return hr;
  1553. }
  1554. void CMenuSFToolbar::_OnDragBegin(int iItem, DWORD dwPreferredEffect)
  1555. {
  1556. // During drag and drop, allow dialogs to collapse menu.
  1557. LockSetForegroundWindow(LSFW_UNLOCK);
  1558. LPCITEMIDLIST pidl = _IDToPidl(iItem, NULL);
  1559. dwPreferredEffect = DROPEFFECT_MOVE;
  1560. CallCB(pidl, SMC_BEGINDRAG, (WPARAM)&dwPreferredEffect, 0);
  1561. CSFToolbar::_OnDragBegin(iItem, dwPreferredEffect);
  1562. if (_fEditMode)
  1563. SetTimer(_hwndTB, MBTIMER_ENDEDIT, MBTIMER_ENDEDITTIME, 0);
  1564. }
  1565. void CMenuSFToolbar::v_SendMenuNotification(UINT idCmd, BOOL fClear)
  1566. {
  1567. if (fClear)
  1568. {
  1569. // If we're clearing, tell the browser
  1570. PostMessage(_pcmb->_pmbState->GetSubclassedHWND(), WM_MENUSELECT,
  1571. MAKEWPARAM(0, -1), NULL);
  1572. }
  1573. else
  1574. {
  1575. PIBDATA pibdata = _IDToPibData(idCmd);
  1576. LPCITEMIDLIST pidl;
  1577. // Only send notifications for non submenu items
  1578. if (EVAL(pibdata) && (pidl = pibdata->GetPidl()))
  1579. {
  1580. CallCB(pidl, SMC_SFSELECTITEM, 0, 0);
  1581. // Don't free Pidl
  1582. }
  1583. }
  1584. }
  1585. LRESULT CMenuSFToolbar::_OnGetObject(NMOBJECTNOTIFY* pnmon)
  1586. {
  1587. pnmon->hResult = QueryInterface(*pnmon->piid, &pnmon->pObject);
  1588. return 1;
  1589. }
  1590. LRESULT CMenuSFToolbar::_OnNotify(LPNMHDR pnm)
  1591. {
  1592. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1593. LRESULT lres = 0;
  1594. // These are notifies we handle even when disengaged from the message hook.
  1595. switch (pnm->code)
  1596. {
  1597. case TBN_DELETINGBUTTON:
  1598. if (_fEmptyingToolbar)
  1599. return 0;
  1600. else
  1601. goto DoDefault;
  1602. break;
  1603. case TBN_GETDISPINFOA:
  1604. case TBN_GETDISPINFOW:
  1605. case NM_CUSTOMDRAW:
  1606. goto DoDefault;
  1607. }
  1608. // Pager notifications MUST be forwarded even when the message hook is disengaged.
  1609. if ((pnm->code <= PGN_FIRST) && (pnm->code >= PGN_LAST))
  1610. {
  1611. goto DoNotify;
  1612. }
  1613. // Is the Global Message filter Disengaged? This will happen when the Subclassed window
  1614. // looses activation to a dialog box of some kind.
  1615. if (lres == 0 && !GetMessageFilter()->IsEngaged())
  1616. {
  1617. // Yes; We've lost activation so we don't want to track like a normal menu...
  1618. // For hot item change, return 1 so that the toolbar does not change the hot item.
  1619. if (pnm->code == TBN_HOTITEMCHANGE && _pcmb->_fMenuMode)
  1620. return 1;
  1621. // For all other items, don't do anything....
  1622. return 0;
  1623. }
  1624. DoNotify:
  1625. switch (pnm->code)
  1626. {
  1627. case PGN_SCROLL:
  1628. KillTimer(_hwndMB, MBTIMER_DRAGPOPDOWN);
  1629. if (_pcmb->_fInSubMenu)
  1630. _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);
  1631. _fSuppressUserMonitor = TRUE;
  1632. break;
  1633. case TBN_GETOBJECT:
  1634. lres = _OnGetObject((NMOBJECTNOTIFY*)pnm);
  1635. break;
  1636. case TBN_DRAGOUT:
  1637. {
  1638. TBNOTIFY *ptbn = (TBNOTIFY*)pnm;
  1639. if (!_fEmpty && !_IsSpecialCmd(ptbn->iItem) &&
  1640. !(_pcmb->_dwFlags & SMINIT_RESTRICT_DRAGDROP) &&
  1641. (S_FALSE == CallCB(NULL, SMC_SFDDRESTRICTED, NULL, NULL)))
  1642. {
  1643. // We're now in edit mode
  1644. _fEditMode = TRUE;
  1645. _idCmdDragging = ptbn->iItem;
  1646. _MarkItem(ptbn->iItem);
  1647. lres = 1; // Allow the drag to occur
  1648. goto DoDefault;
  1649. }
  1650. else
  1651. lres = 0; // Do not allow the drag out.
  1652. }
  1653. break;
  1654. default:
  1655. DoDefault:
  1656. lres = CMenuToolbarBase::_OnNotify(pnm);
  1657. if (lres == 0)
  1658. {
  1659. lres = CSFToolbar::_OnNotify(pnm);
  1660. }
  1661. break;
  1662. }
  1663. return lres;
  1664. }
  1665. HRESULT CMenuSFToolbar::CreateToolbar(HWND hwndParent)
  1666. {
  1667. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1668. HRESULT hr = CSFToolbar::_CreateToolbar(hwndParent);
  1669. if (SUCCEEDED(hr))
  1670. {
  1671. if (_hwndPager)
  1672. {
  1673. SHSetWindowBits(_hwndPager, GWL_STYLE, PGS_DRAGNDROP, PGS_DRAGNDROP);
  1674. SHSetWindowBits(_hwndPager, GWL_STYLE, PGS_AUTOSCROLL, PGS_AUTOSCROLL);
  1675. SHSetWindowBits(_hwndPager, GWL_STYLE, PGS_HORZ|PGS_VERT,
  1676. _fVertical ? PGS_VERT : PGS_HORZ);
  1677. }
  1678. _hwndMB = _hwndTB;
  1679. SetWindowTheme(_hwndMB, L"", L"");
  1680. hr = CMenuToolbarBase::CreateToolbar(hwndParent);
  1681. // By "Registering optimized" means that someone else is going to pass the change to us,
  1682. // we don't need to register for it. This is for the disjoint Fast Items | Programs menu case.
  1683. // We still need top level change notify registration for Favorites, Documents, Printers and Control
  1684. // Panel (Depending on their visibility)
  1685. //
  1686. if (_pcmb->_uId == MNFOLDER_IS_PARENT ||
  1687. (_dwFlags & SMSET_DONTREGISTERCHANGENOTIFY))
  1688. _fRegisterChangeNotify = FALSE;
  1689. // This is a good as spot as any to do this:
  1690. _RegisterToolbar();
  1691. }
  1692. return hr;
  1693. }
  1694. HKEY CMenuSFToolbar::_GetKey(LPCITEMIDLIST pidl)
  1695. {
  1696. HKEY hMenuKey;
  1697. DWORD dwDisp;
  1698. TCHAR szDisplay[MAX_PATH];
  1699. if (!_hKey)
  1700. return NULL;
  1701. _ObtainPIDLName(pidl, szDisplay, ARRAYSIZE(szDisplay));
  1702. RegCreateKeyEx(_hKey, szDisplay, NULL, NULL,
  1703. REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  1704. NULL, &hMenuKey, &dwDisp);
  1705. TraceMsg(TF_MENUBAND, "%d is setting %s\'s Key to %d", _hKey, szDisplay, hMenuKey);
  1706. return hMenuKey;
  1707. }
  1708. //***
  1709. // NOTES
  1710. // idtCmd is currently always -1. we'll need other values when we're
  1711. // called from CallCB. however we can't do that until we move idtCmd
  1712. // 'down' into CallCB.
  1713. HRESULT CMenuSFToolbar::v_GetState(int idtCmd, LPSMDATA psmd)
  1714. {
  1715. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1716. HRESULT hr = E_FAIL;
  1717. CMenuData* pdata;
  1718. LPITEMIDLIST pidl = NULL;
  1719. psmd->dwMask = SMDM_SHELLFOLDER;
  1720. if (idtCmd == -1)
  1721. idtCmd = GetButtonCmd(_hwndTB, ToolBar_GetHotItem(_hwndTB));
  1722. pdata = (CMenuData*)_IDToPibData(idtCmd);
  1723. if (EVAL(pdata))
  1724. {
  1725. pidl = pdata->GetPidl();
  1726. ASSERT(IS_VALID_PIDL(pidl));
  1727. }
  1728. if (pidl)
  1729. {
  1730. if (_pasf2 && S_OK == _pasf2->UnWrapIDList(pidl, 1, &psmd->psf, &psmd->pidlFolder, &psmd->pidlItem, NULL))
  1731. {
  1732. /*NOTHING*/
  1733. ;
  1734. }
  1735. else
  1736. {
  1737. // Then it must be a straight ShellFolder.
  1738. psmd->psf = _psf;
  1739. if (EVAL(psmd->psf))
  1740. psmd->psf->AddRef();
  1741. psmd->pidlFolder = ILClone(_pidl);
  1742. psmd->pidlItem = ILClone(ILFindLastID(pidl));
  1743. }
  1744. psmd->uIdParent = _pcmb->_uId;
  1745. psmd->punk = SAFECAST(_pcmb, IShellMenu*);
  1746. psmd->punk->AddRef();
  1747. hr = S_OK;
  1748. }
  1749. return hr;
  1750. }
  1751. HRESULT CMenuSFToolbar::CallCB(LPCITEMIDLIST pidl, DWORD dwMsg, WPARAM wParam, LPARAM lParam)
  1752. {
  1753. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1754. if (!_pcmb->_psmcb)
  1755. return S_FALSE;
  1756. SMDATA smd;
  1757. HRESULT hr = S_FALSE;
  1758. BOOL fDestroy = FALSE;
  1759. // todo: call v_GetState (but need idCmd for pidl)
  1760. smd.dwMask = SMDM_SHELLFOLDER;
  1761. if (pidl)
  1762. {
  1763. // We used to unwrap the pidl here in the case of AUGMISF, but why? In the Callback, we only
  1764. // needed the Full pidl for Executing and for Darwin. The unwrap is an expensive call that in
  1765. // the majority case wasn't even used. Put it on the client to unwrap it. Start Menu is the
  1766. // only user of Augmented shell folders anyway....
  1767. smd.psf = _psf;
  1768. smd.pidlFolder = _pidl;
  1769. smd.pidlItem = (LPITEMIDLIST)pidl;
  1770. }
  1771. else
  1772. {
  1773. // Null pidl means tell the callback about me...
  1774. smd.pidlItem = ILClone(ILFindLastID(_pidl));
  1775. smd.pidlFolder = ILClone(_pidl);
  1776. ILRemoveLastID(smd.pidlFolder);
  1777. smd.psf = NULL; // Incase bind fails.
  1778. IEBindToObject(smd.pidlFolder, &smd.psf);
  1779. fDestroy = TRUE;
  1780. }
  1781. smd.uIdParent = _pcmb->_uId;
  1782. smd.uIdAncestor = _pcmb->_uIdAncestor;
  1783. smd.punk = SAFECAST(_pcmb, IShellMenu*);
  1784. smd.pvUserData = _pcmb->_pvUserData;
  1785. hr = _pcmb->_psmcb->CallbackSM(&smd, dwMsg, wParam, lParam);
  1786. if (fDestroy)
  1787. {
  1788. ATOMICRELEASE(smd.psf);
  1789. ILFree(smd.pidlFolder);
  1790. ILFree(smd.pidlItem);
  1791. }
  1792. return hr;
  1793. }
  1794. HRESULT CMenuSFToolbar::v_CallCBItem(int idtCmd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1795. {
  1796. LPCITEMIDLIST pidl = NULL;
  1797. CMenuData* pdata = NULL;
  1798. // Optimization:
  1799. if (uMsg == SMC_CUSTOMDRAW)
  1800. {
  1801. NMCUSTOMDRAW * pnmcd = (NMCUSTOMDRAW *)lParam;
  1802. if (pnmcd ->dwDrawStage & CDDS_ITEM)
  1803. {
  1804. pdata = (CMenuData*)pnmcd ->lItemlParam;
  1805. ASSERT(pdata);
  1806. }
  1807. }
  1808. else
  1809. {
  1810. pdata = (CMenuData*)_IDToPibData(idtCmd);
  1811. ASSERT(pdata);
  1812. }
  1813. if (pdata)
  1814. {
  1815. ASSERT(pdata->GetPidl() == NULL || IS_VALID_PIDL(pdata->GetPidl()));
  1816. pidl = pdata->GetPidl();
  1817. }
  1818. return CallCB(pidl, uMsg, wParam, lParam);
  1819. }
  1820. HRESULT CMenuSFToolbar::v_GetSubMenu(int idCmd, const GUID* pguidService, REFIID riid, void** ppvObj)
  1821. {
  1822. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1823. CMenuData* pdata = (CMenuData*)_IDToPibData(idCmd);
  1824. HRESULT hr;
  1825. ASSERT(IS_VALID_WRITE_PTR(ppvObj, void*));
  1826. *ppvObj = NULL;
  1827. if (pdata && pdata->GetFlags() & SMIF_SUBMENU)
  1828. {
  1829. hr = pdata->GetSubMenu(pguidService, riid, (void**)ppvObj);
  1830. if (FAILED(hr) && IsEqualGUID(riid, IID_IShellMenu))
  1831. {
  1832. hr = CallCB(pdata->GetPidl(), SMC_GETSFOBJECT, (WPARAM)&riid, (LPARAM)ppvObj);
  1833. if (SUCCEEDED(hr))
  1834. {
  1835. BOOL fCache = TRUE;
  1836. if (S_OK != hr)
  1837. {
  1838. hr = E_FAIL;
  1839. IShellMenu* psm = (IShellMenu*) new CMenuBand();
  1840. if (psm)
  1841. {
  1842. IShellFolder* psf = NULL;
  1843. LPITEMIDLIST pidlItem = pdata->GetPidl();
  1844. LPITEMIDLIST pidlFolder = _pidl;
  1845. BOOL fDestroy = FALSE;
  1846. IShellMenuCallback* psmcb;
  1847. // Ask the callback if they want to supply a different callback
  1848. // object for this sub menu. If they do, then use what they
  1849. // pass back NOTE: If they pass back S_OK, it's perfectly Ok,
  1850. // for them to pass back a NULL psmcb. This means, I don't want
  1851. // my child to have a callback. Use the default.
  1852. // If they don't handle it, then use their pointer.
  1853. if (S_FALSE == CallCB(pdata->GetPidl(), SMC_GETSFOBJECT,
  1854. (WPARAM)&IID_IShellMenuCallback, (LPARAM)&psmcb))
  1855. {
  1856. psmcb = _pcmb->_psmcb;
  1857. if (psmcb)
  1858. psmcb->AddRef();
  1859. }
  1860. // This has to be before the unwrap because it does name resolution through
  1861. // the Augmented ISF.
  1862. HKEY hMenuKey = _GetKey(pidlItem);
  1863. if (_pasf2)
  1864. {
  1865. if (S_OK == _pasf2->UnWrapIDList(pdata->GetPidl(), 1, &psf, &pidlFolder, &pidlItem, NULL))
  1866. {
  1867. psf->Release(); // I don't need this
  1868. psf = NULL;
  1869. fDestroy = TRUE;
  1870. }
  1871. _pasf2->BindToObject(pdata->GetPidl(), NULL, IID_PPV_ARG(IShellFolder, &psf));
  1872. }
  1873. // Inherit the flags from the parent...
  1874. DWORD dwFlags = SMINIT_VERTICAL |
  1875. (_pcmb->_dwFlags & (SMINIT_RESTRICT_CONTEXTMENU |
  1876. SMINIT_RESTRICT_DRAGDROP |
  1877. SMINIT_MULTICOLUMN));
  1878. LPITEMIDLIST pidlFull = ILCombine(pidlFolder, pidlItem);
  1879. if (psf == NULL)
  1880. {
  1881. hr = _psf->BindToObject(pidlItem, NULL, IID_PPV_ARG(IShellFolder, &psf));
  1882. }
  1883. LPCITEMIDLIST pidlWrappedItem = pdata->GetPidl();
  1884. // _psf can be an augmented shell folder. Use the wrapped item....
  1885. DWORD dwAttrib = SFGAO_LINK | SFGAO_FOLDER;
  1886. if (SUCCEEDED(_psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlWrappedItem, &dwAttrib)) &&
  1887. (dwAttrib & (SFGAO_LINK | SFGAO_FOLDER)) == (SFGAO_LINK | SFGAO_FOLDER))
  1888. {
  1889. // folder shortcuts, We're not going to persist anything
  1890. RegCloseKey(hMenuKey);
  1891. hMenuKey = NULL;
  1892. psmcb = NULL; // We're not going to pass a callback. NOTE: We don't need to release this
  1893. dwFlags &= ~SMINIT_MULTICOLUMN; // No multi on FShortcut...
  1894. fCache = FALSE;
  1895. }
  1896. UINT uIdAncestor = _pcmb->_uIdAncestor;
  1897. if (uIdAncestor == ANCESTORDEFAULT)
  1898. uIdAncestor = idCmd;
  1899. psm->Initialize(psmcb, MNFOLDER_IS_PARENT, uIdAncestor, dwFlags);
  1900. if (psf)
  1901. {
  1902. hr = psm->SetShellFolder(psf, pidlFull, hMenuKey,
  1903. _dwFlags & (SMSET_HASEXPANDABLEFOLDERS | SMSET_USEBKICONEXTRACTION));
  1904. if (SUCCEEDED(hr))
  1905. {
  1906. hr = psm->QueryInterface(riid, ppvObj);
  1907. }
  1908. psf->Release();
  1909. }
  1910. ILFree(pidlFull);
  1911. _SetMenuBand(psm);
  1912. psm->Release();
  1913. if (psmcb)
  1914. psmcb->Release();
  1915. if (fDestroy)
  1916. {
  1917. ILFree(pidlFolder);
  1918. ILFree(pidlItem);
  1919. }
  1920. }
  1921. }
  1922. if (*ppvObj)
  1923. {
  1924. if (fCache)
  1925. {
  1926. pdata->SetSubMenu((IUnknown*)*ppvObj);
  1927. }
  1928. VARIANT Var;
  1929. Var.vt = VT_UNKNOWN;
  1930. Var.byref = SAFECAST(_pcmb->_pmbm, IUnknown*);
  1931. // Set the CMenuBandMetrics into the new menuband
  1932. IUnknown_Exec((IUnknown*)*ppvObj, &CGID_MenuBand, MBANDCID_SETFONTS, 0, &Var, NULL);
  1933. // Set the CMenuBandState into the new menuband
  1934. Var.vt = VT_INT_PTR;
  1935. Var.byref = _pcmb->_pmbState;
  1936. IUnknown_Exec((IUnknown*)*ppvObj, &CGID_MenuBand, MBANDCID_SETSTATEOBJECT, 0, &Var, NULL);
  1937. }
  1938. }
  1939. }
  1940. }
  1941. else
  1942. {
  1943. hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  1944. }
  1945. return hr;
  1946. }
  1947. DWORD CMenuSFToolbar::v_GetFlags(int idCmd)
  1948. {
  1949. CMenuData* pdata = (CMenuData*)_IDToPibData(idCmd);
  1950. // Toolbar is allowed to pass a bad command in the case of background erase
  1951. if (pdata)
  1952. return pdata->GetFlags();
  1953. else
  1954. return 0;
  1955. }
  1956. // This is to tell all other clients that we updated the promotion state of something.
  1957. void CMenuSFToolbar::BroadcastIntelliMenuState(LPCITEMIDLIST pidlItem, BOOL fPromoted)
  1958. {
  1959. LPITEMIDLIST pidlFolder;
  1960. LPITEMIDLIST pidlItemUnwrapped;
  1961. LPITEMIDLIST pidlFull;
  1962. if (_pasf2 && S_OK == _pasf2->UnWrapIDList(pidlItem, 1, NULL, &pidlFolder, &pidlItemUnwrapped, NULL))
  1963. {
  1964. pidlFull = ILCombine(pidlFolder, pidlItemUnwrapped);
  1965. ILFree(pidlFolder);
  1966. ILFree(pidlItemUnwrapped);
  1967. }
  1968. else
  1969. {
  1970. pidlFull = ILCombine(_pidl, pidlItem);
  1971. }
  1972. SHSendChangeMenuNotify(this,
  1973. fPromoted ? SHCNEE_PROMOTEDITEM : SHCNEE_DEMOTEDITEM,
  1974. 0, pidlFull);
  1975. ILFree(pidlFull);
  1976. }
  1977. STDAPI SHInvokeCommandWithFlags(HWND hwnd, IShellFolder* psf, LPCITEMIDLIST pidlItem, DWORD dwFlags, LPCSTR lpVerb)
  1978. {
  1979. HRESULT hr = E_FAIL;
  1980. if (psf)
  1981. {
  1982. IContextMenu *pcm;
  1983. if (SUCCEEDED(psf->GetUIObjectOf(hwnd, 1, &pidlItem, IID_X_PPV_ARG(IContextMenu, 0, &pcm))))
  1984. {
  1985. dwFlags |= IsOS(OS_WHISTLERORGREATER) ? CMIC_MASK_FLAG_LOG_USAGE : 0;
  1986. hr = SHInvokeCommandsOnContextMenu(hwnd, NULL, pcm, dwFlags, lpVerb ? &lpVerb : NULL, lpVerb ? 1 : 0);
  1987. pcm->Release();
  1988. }
  1989. }
  1990. return hr;
  1991. }
  1992. HRESULT CMenuSFToolbar::v_ExecItem(int idCmd)
  1993. {
  1994. CMenuData* pdata = (CMenuData*)_IDToPibData(idCmd);
  1995. HRESULT hr = E_FAIL;
  1996. if (pdata && !_fEmpty && !(_IsSpecialCmd(idCmd)))
  1997. {
  1998. // STRESS: pdata was becomming 0x8 for some reason after the InvokeDefault.
  1999. // I assume that this call was causing a flush, which frees our list of pidls.
  2000. // So, I'm cloning it. I also changed the order, so that we'll just fire the
  2001. // UEM event.
  2002. LPITEMIDLIST pidl = ILClone(pdata->GetPidl());
  2003. if (pidl)
  2004. {
  2005. ASSERT(IS_VALID_PIDL(pidl));
  2006. SMDATA smd;
  2007. smd.dwMask = SMDM_SHELLFOLDER;
  2008. smd.pidlFolder = _pidl;
  2009. smd.pidlItem = pidl;
  2010. // SMINV_FORCE here also tells the Start Menu that the invoke
  2011. // came from an Exec
  2012. v_InvalidateItem(&smd, SMINV_PROMOTE | SMINV_FORCE);
  2013. hr = CallCB(pidl, SMC_SFEXEC, 0, 0);
  2014. // Did the Callback handle this execute for us?
  2015. if (hr == S_FALSE)
  2016. {
  2017. // No, Ok, do it ourselves.
  2018. hr = SHInvokeCommandWithFlags(_hwndTB, _psf, pidl, CMIC_MASK_ASYNCOK, NULL);
  2019. }
  2020. ILFree(pidl);
  2021. }
  2022. }
  2023. return hr;
  2024. }
  2025. HRESULT CMenuSFToolbar::v_GetInfoTip(int idCmd, LPTSTR psz, UINT cch)
  2026. {
  2027. CMenuData* pdata = (CMenuData*)_IDToPibData(idCmd);
  2028. HRESULT hr = E_FAIL;
  2029. if (_fEmpty || !pdata)
  2030. return hr;
  2031. // make a copy of the pidl that we're using, since we can get reentered in the sendmessage
  2032. // and free the data from the dpa.
  2033. LPITEMIDLIST pidlCopy = ILClone(pdata->GetPidl());
  2034. // dont worry about failure -- pdata->GetPidl() can be NULL anyway,
  2035. // in which case ILClone will return NULL, and CallCB and GetInfoTip already have NULL checks.
  2036. hr = CallCB(pidlCopy, SMC_GETSFINFOTIP, (WPARAM)psz, (LPARAM)cch);
  2037. if (S_FALSE == hr)
  2038. {
  2039. hr = E_FAIL;
  2040. if (GetInfoTip(_psf, pidlCopy, psz, cch))
  2041. {
  2042. hr = S_OK;
  2043. }
  2044. }
  2045. ILFree(pidlCopy);
  2046. return hr;
  2047. }
  2048. void CMenuSFToolbar::v_ForwardMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  2049. {
  2050. RECT rc;
  2051. POINT pt;
  2052. HWND hwndFwd;
  2053. // These are in screen coords
  2054. pt.x = GET_X_LPARAM(lParam);
  2055. pt.y = GET_Y_LPARAM(lParam);
  2056. hwndFwd = _hwndPager ? _hwndPager : _hwndTB;
  2057. GetWindowRect(hwndFwd, &rc);
  2058. if (PtInRect(&rc, pt))
  2059. {
  2060. MapWindowPoints(NULL, hwndFwd, &pt, 1);
  2061. HWND hwnd = ChildWindowFromPoint(hwndFwd, pt);
  2062. if (hwnd)
  2063. {
  2064. MapWindowPoints(hwndFwd, hwnd, &pt, 1);
  2065. }
  2066. else
  2067. {
  2068. hwnd = hwndFwd;
  2069. }
  2070. SendMessage(hwnd, uMsg, wParam, MAKELONG(pt.x, pt.y));
  2071. }
  2072. }
  2073. HRESULT CMenuSFToolbar::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
  2074. {
  2075. switch(uMsg)
  2076. {
  2077. case WM_SYSCOLORCHANGE:
  2078. if (_hwndPager)
  2079. Pager_SetBkColor(_hwndPager, GetSysColor(COLOR_MENU));
  2080. // Change the color, so that we can see it.
  2081. ToolBar_SetInsertMarkColor(_hwndMB, GetSysColor(COLOR_MENUTEXT));
  2082. break;
  2083. }
  2084. HRESULT hr = CMenuToolbarBase::OnWinEvent(hwnd, uMsg, wParam, lParam, plres);
  2085. if (hr != S_OK)
  2086. hr = CSFToolbar::OnWinEvent(hwnd, uMsg, wParam, lParam, plres);
  2087. return hr;
  2088. }
  2089. BOOL CMenuSFToolbar::v_UpdateIconSize(UINT uIconSize, BOOL fUpdateButtons)
  2090. {
  2091. if (uIconSize == -1)
  2092. uIconSize = _uIconSize;
  2093. _uIconSizeMB = uIconSize;
  2094. return _UpdateIconSize(_uIconSizeMB, fUpdateButtons);
  2095. }
  2096. HRESULT CMenuSFToolbar::GetShellFolder(LPITEMIDLIST* ppidl, REFIID riid, void** ppvObj)
  2097. {
  2098. HRESULT hr = E_FAIL;
  2099. *ppvObj = NULL;
  2100. if (_psf)
  2101. {
  2102. hr = _psf->QueryInterface(riid, ppvObj);
  2103. }
  2104. if (SUCCEEDED(hr) && ppidl)
  2105. {
  2106. *ppidl = ILClone(_pidl);
  2107. if (! *ppidl)
  2108. {
  2109. (*(IUnknown**)ppvObj)->Release();
  2110. *ppvObj = NULL;
  2111. hr = E_FAIL;
  2112. }
  2113. }
  2114. return hr;
  2115. }
  2116. LRESULT CMenuSFToolbar::_OnTimer(WPARAM wParam)
  2117. {
  2118. switch(wParam)
  2119. {
  2120. case MBTIMER_ENDEDIT:
  2121. KillTimer(_hwndTB, wParam);
  2122. _fEditMode = FALSE;
  2123. break;
  2124. case MBTIMER_CLICKUNHANDLE:
  2125. KillTimer(_hwndTB, wParam);
  2126. _fClickHandled = FALSE;
  2127. break;
  2128. default:
  2129. return CMenuToolbarBase::_OnTimer(wParam);
  2130. }
  2131. return 1;
  2132. }
  2133. LRESULT CMenuSFToolbar::_OnDropDown(LPNMTOOLBAR pnmtb)
  2134. {
  2135. if (GetAsyncKeyState(VK_LBUTTON) < 0 && _fEditMode)
  2136. {
  2137. // Are we in edit mode?
  2138. if (_fEditMode)
  2139. {
  2140. // Yes, mark the item as the item that is subject to moving
  2141. _MarkItem(pnmtb->iItem);
  2142. }
  2143. return TBDDRET_TREATPRESSED;
  2144. }
  2145. return CMenuToolbarBase::_OnDropDown(pnmtb);
  2146. }
  2147. // In the context of a menuband, marking means putting
  2148. // a black rectangle around the item currently being dragged.
  2149. void CMenuSFToolbar::_MarkItem(int idCmd)
  2150. {
  2151. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  2152. // Un-highlight the previously moved button
  2153. if (0 <= _pcmb->_nItemMove)
  2154. {
  2155. // Should item move be a member of SFToolbar?
  2156. ToolBar_MarkButton(_hwndTB, _pcmb->_nItemMove, FALSE);
  2157. _pcmb->_nItemMove = -1;
  2158. }
  2159. if (_fEditMode)
  2160. {
  2161. _pcmb->_nItemMove = idCmd;
  2162. ToolBar_MarkButton(_hwndTB, _pcmb->_nItemMove, TRUE);
  2163. }
  2164. }
  2165. STDMETHODIMP CMenuSFToolbar::IsWindowOwner(HWND hwnd)
  2166. {
  2167. if (_hwndTB == hwnd || _hwndPager == hwnd || HWND_BROADCAST == hwnd)
  2168. {
  2169. return S_OK;
  2170. }
  2171. else
  2172. {
  2173. return S_FALSE;
  2174. }
  2175. }
  2176. void CMenuSFToolbar::SetWindowPos(LPSIZE psize, LPRECT prc, DWORD dwFlags)
  2177. {
  2178. if (!_hwndPager)
  2179. {
  2180. CMenuToolbarBase::SetWindowPos(psize, prc, dwFlags);
  2181. return;
  2182. }
  2183. DWORD rectWidth = RECTWIDTH(*prc);
  2184. TraceMsg(TF_MENUBAND, "CMSFTB::SetWindowPos %d - (%d,%d,%d,%d)", psize?psize->cx:0,
  2185. prc->left, prc->top, prc->right, prc->bottom);
  2186. ShowWindow(_hwndPager, SW_SHOW);
  2187. ::SetWindowPos(_hwndPager, NULL, prc->left, prc->top,
  2188. rectWidth, RECTHEIGHT(*prc), SWP_NOZORDER | SWP_NOACTIVATE | dwFlags);
  2189. if (psize)
  2190. {
  2191. int cx = psize->cx;
  2192. ToolBar_SetButtonWidth(_hwndTB, cx, cx);
  2193. }
  2194. SendMessage(_hwndPager, PGMP_RECALCSIZE, 0L, 0L);
  2195. }
  2196. void CMenuSFToolbar::SetParent(HWND hwndParent)
  2197. {
  2198. int nCmdShow = SW_SHOW;
  2199. if (hwndParent)
  2200. {
  2201. if (!_hwndTB)
  2202. CreateToolbar(hwndParent);
  2203. else
  2204. {
  2205. // make sure width is set correctly . . .
  2206. SendMessage(_hwndTB, TB_SETBUTTONWIDTH, 0, MAKELONG(_cxMin, _cxMax));
  2207. }
  2208. }
  2209. else
  2210. {
  2211. // As an optimization, we implement "disowning" ourselves
  2212. // as just moving ourselves offscreen. The previous parent
  2213. // still owns us. The parent is invariably the menusite.
  2214. RECT rc = {-1,-1,-1,-1};
  2215. SetWindowPos(NULL, &rc, 0);
  2216. nCmdShow = SW_HIDE;
  2217. }
  2218. HWND hwnd = _hwndPager ? _hwndPager: _hwndTB;
  2219. if (IsWindow(hwnd)) // JANK : Fix for bug #98253
  2220. {
  2221. if (nCmdShow == SW_HIDE)
  2222. {
  2223. ShowWindow(hwnd, nCmdShow);
  2224. }
  2225. ::SetParent(hwnd, hwndParent);
  2226. SendMessage(hwnd, TB_SETPARENT, (WPARAM)hwndParent, NULL);
  2227. if (nCmdShow == SW_SHOW)
  2228. {
  2229. ShowWindow(hwnd, nCmdShow);
  2230. }
  2231. }
  2232. }
  2233. void CMenuSFToolbar::Expand(BOOL fExpand)
  2234. {
  2235. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  2236. TBBUTTON tbb;
  2237. DAD_ShowDragImage(FALSE);
  2238. // Since we're not sure if the Chevron is going to be visible we should remove it here
  2239. // Later we'll add it back in if it's needed.
  2240. _RemoveNSSeparator();
  2241. _RemoveChevron();
  2242. // Loop through and apply the fExpand
  2243. int iNumButtons = ToolBar_ButtonCount(_hwndTB);
  2244. // We reset these when iterating.
  2245. _cPromotedItems = 0;
  2246. _fHasDemotedItems = FALSE;
  2247. int iHotItem = ToolBar_GetHotItem(_hwndMB);
  2248. // SendMessage(_hwndMB, WM_SETREDRAW, FALSE, 0);
  2249. for (int i = 0; i < iNumButtons; i++)
  2250. {
  2251. if (!ToolBar_GetButton(_hwndMB, i, &tbb))
  2252. continue;
  2253. CMenuData* pmd = (CMenuData*)tbb.dwData;
  2254. // Get the toolbar state. Toolbar can set things like
  2255. // TBSTATE_WRAP that we would go nuke.
  2256. DWORD dwState = tbb.fsState;
  2257. DWORD dwFlags = pmd ? pmd->GetFlags() : 0;
  2258. if (dwFlags & SMIF_DEMOTED)
  2259. {
  2260. // Are we expanding?
  2261. if (fExpand)
  2262. {
  2263. //Yes; Enable the button and remove the hidden state
  2264. dwState |= TBSTATE_ENABLED;
  2265. dwState &= ~TBSTATE_HIDDEN;
  2266. }
  2267. else
  2268. {
  2269. //No; Remove the Enabled state and hide the button
  2270. dwState |= TBSTATE_HIDDEN;
  2271. dwState &= ~TBSTATE_ENABLED;
  2272. }
  2273. _fHasDemotedItems = TRUE;
  2274. }
  2275. else if (dwFlags & SMIF_HIDDEN)
  2276. {
  2277. dwState |= TBSTATE_HIDDEN;
  2278. dwState &= ~TBSTATE_ENABLED;
  2279. }
  2280. else if (tbb.idCommand != _idCmdChevron)
  2281. {
  2282. dwState |= TBSTATE_ENABLED;
  2283. dwState &= ~TBSTATE_HIDDEN;
  2284. _cPromotedItems++;
  2285. }
  2286. // If the state has changed, then set it into the toolbar.
  2287. if (dwState != tbb.fsState)
  2288. ToolBar_SetState(_hwndTB, tbb.idCommand, dwState);
  2289. }
  2290. // _fExpand means "Draw as Expanded". We do not want to
  2291. // draw expanded when we have no demoted items.
  2292. _pcmb->_fExpanded = _fHasDemotedItems? fExpand : FALSE;
  2293. if (fExpand)
  2294. {
  2295. if (_pcmb->_pmbState)
  2296. {
  2297. _pcmb->_pmbState->SetExpand(TRUE);
  2298. _pcmb->_pmbState->HideTooltip(TRUE);
  2299. }
  2300. }
  2301. else
  2302. {
  2303. _AddChevron();
  2304. }
  2305. _AddNSSeparator();
  2306. // Have the menubar think about changing its height
  2307. IUnknown_QueryServiceExec(_pcmb->_punkSite, SID_SMenuPopup, &CGID_MENUDESKBAR,
  2308. MBCID_SETEXPAND, _fHasDemotedItems?(int)_pcmb->_pmbState->GetExpand():FALSE, NULL, NULL);
  2309. // SendMessage(_hwndMB, WM_SETREDRAW, TRUE, 0);
  2310. _ToolbarChanged();
  2311. ToolBar_SetHotItem(_hwndMB, iHotItem);
  2312. if (_hwndPager)
  2313. UpdateWindow(_hwndPager);
  2314. UpdateWindow(_hwndTB);
  2315. // DAD_ShowDragImage(TRUE);
  2316. }
  2317. void CMenuSFToolbar::GetSize(SIZE* psize)
  2318. {
  2319. CMenuToolbarBase::GetSize(psize);
  2320. if (_fEmpty && _fDontShowEmpty)
  2321. {
  2322. psize->cy = 0;
  2323. TraceMsg(TF_MENUBAND, "CMSFT::GetSize (%d, %d)", psize->cx, psize->cy);
  2324. }
  2325. }
  2326. void CMenuSFToolbar::_RefreshInfo()
  2327. {
  2328. int cButton = ToolBar_ButtonCount(_hwndMB);
  2329. for (int iButton = 0; iButton < cButton; iButton++)
  2330. {
  2331. int idCmd = GetButtonCmd(_hwndTB, iButton);
  2332. if (!_IsSpecialCmd(idCmd))
  2333. {
  2334. // Get the information from that button.
  2335. CMenuData* pmd = (CMenuData*)_IDToPibData(idCmd);
  2336. if (pmd)
  2337. {
  2338. SMINFO sminfo;
  2339. sminfo.dwMask = SMIM_FLAGS;
  2340. if (SUCCEEDED(_GetInfo(pmd->GetPidl(), &sminfo)))
  2341. {
  2342. pmd->SetFlags(sminfo.dwFlags);
  2343. }
  2344. }
  2345. }
  2346. }
  2347. }
  2348. void CMenuSFToolbar::_FindMinPromotedItems(BOOL fSetOrderStream)
  2349. {
  2350. // We need to iterate through the buttons and set the Promoted flag.
  2351. int cButton = ToolBar_ButtonCount(_hwndMB);
  2352. for (int iButton = 0; iButton < cButton; iButton++)
  2353. {
  2354. int idCmd = GetButtonCmd(_hwndTB, iButton);
  2355. if (!_IsSpecialCmd(idCmd))
  2356. {
  2357. // Get the information from that button.
  2358. CMenuData* pmd = (CMenuData*)_IDToPibData(idCmd);
  2359. if (pmd)
  2360. {
  2361. PORDERITEM poi = pmd->GetOrderItem();
  2362. if (fSetOrderStream)
  2363. {
  2364. DWORD dwFlags = pmd->GetFlags();
  2365. OrderItem_SetFlags(poi, dwFlags);
  2366. }
  2367. else // Query the order stream
  2368. {
  2369. DWORD dwFlags = OrderItem_GetFlags(poi);
  2370. DWORD dwOldFlags = pmd->GetFlags();
  2371. // When reading the flags from the registry, we only care about the demote flag.
  2372. if (dwFlags & SMIF_DEMOTED)
  2373. {
  2374. dwOldFlags |= SMIF_DEMOTED;
  2375. }
  2376. else if (!(dwOldFlags & SMIF_SUBMENU)) // Don't promote sub menus.
  2377. {
  2378. // Force a promote
  2379. CallCB(pmd->GetPidl(), SMC_PROMOTE, 0, 0);
  2380. dwOldFlags &= ~SMIF_DEMOTED;
  2381. }
  2382. pmd->SetFlags(dwOldFlags);
  2383. }
  2384. }
  2385. }
  2386. }
  2387. }
  2388. void CMenuSFToolbar::v_Show(BOOL fShow, BOOL fForceUpdate)
  2389. {
  2390. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  2391. CMenuToolbarBase::v_Show(fShow, fForceUpdate);
  2392. if (fShow)
  2393. {
  2394. BOOL fDirty = _fDirty;
  2395. _fClickHandled = FALSE;
  2396. _RegisterToolbar();
  2397. // this is to flush the whole thing if we got invalidated (like the
  2398. // darwin hack makes us do). if we don't clear out, then some
  2399. // stale data is left in and _FillToolbar doesn't flush it any more.
  2400. if (fDirty)
  2401. {
  2402. EmptyToolbar();
  2403. }
  2404. _FillToolbar();
  2405. _pcmb->SetTracked(NULL); // Since hot item is NULL
  2406. ToolBar_SetHotItem(_hwndTB, -1);
  2407. if (_fEmpty && (_dwFlags & SMSET_NOEMPTY))
  2408. {
  2409. _fDontShowEmpty = TRUE;
  2410. }
  2411. else if (_fRefreshInfo && !fDirty) // Do we need to refresh our information?
  2412. {
  2413. // Yes;
  2414. _RefreshInfo();
  2415. }
  2416. // HACKHACK (lamadio) : There is a sizing issue, where the sizing between the
  2417. // toolbars gets preemted by a resize of the menubar before the size calculation completes.
  2418. // So:
  2419. // ShowDW - Asks each toolbar to calc it's width
  2420. // CMenuSFToolbar::v_Show - Does a _FillToolbar. Since (in this senario) an item
  2421. // Has been added, it calls _ToolbarChanged
  2422. // _ToolbarChanged - This says to the menubar, I've changed sizes, recalc.
  2423. // ResizeMenuBar - In the depths, it eventually calls OnPosRectChanged, which asks each
  2424. // Toolbar what it's size is. Since the menu portion has not calculated it yet,
  2425. // It has the old size which is has the old size of the sftoolbar. So everything
  2426. // Gets reset to that size.
  2427. //
  2428. // We only want to Call expand if we are dirty or the expand state has changed. We
  2429. // call for the Dirty case, because Expand does some neat stuff in calculating the
  2430. // number of promoted items. If the state has changed, we want to reflect that.
  2431. BOOL fExpand = _pcmb->_pmbState ? _pcmb->_pmbState->GetExpand() : FALSE;
  2432. if ((BOOL)_pcmb->_fExpanded != fExpand || fDirty || _fRefreshInfo)
  2433. {
  2434. fForceUpdate = TRUE;
  2435. Expand(fExpand);
  2436. }
  2437. // Only do this in the beginning.
  2438. if (_fFirstTime)
  2439. {
  2440. CallCB(NULL, SMC_GETMINPROMOTED, 0, (LPARAM)&_cMinPromotedItems);
  2441. if (_cPromotedItems < _cMinPromotedItems)
  2442. {
  2443. _FindMinPromotedItems(FALSE);
  2444. Expand(fExpand);
  2445. }
  2446. }
  2447. // Have the menubar think about changing its height
  2448. // we need to do this here because the menubar may have changed it's
  2449. // expand state independant of the pane.
  2450. IUnknown_QueryServiceExec(_pcmb->_punkSite, SID_SMenuPopup, &CGID_MENUDESKBAR,
  2451. MBCID_SETEXPAND, (int)_pcmb->_fExpanded, NULL, NULL);
  2452. // If we're dirty, have our parent consider promoting itself if there
  2453. // are promoted items in the menu, or demoting itself if there arn't.
  2454. // Don't worry, the parent won't do anything if it's already in that state.
  2455. if (fDirty)
  2456. {
  2457. IUnknown_RefreshParent(_pcmb->_punkSite, _pidl,
  2458. ((_cPromotedItems == 0)? SMINV_DEMOTE : SMINV_PROMOTE) | SMINV_NEXTSHOW);
  2459. }
  2460. // If it is empty, we want to auto expand.
  2461. // We have to do this before the update buttons, so that the size is calculate correctly.
  2462. if (_cPromotedItems == 0 && !_pcmb->_fExpanded)
  2463. Expand(TRUE);
  2464. if (fForceUpdate)
  2465. _UpdateButtons();
  2466. if (_fHasDemotedItems)
  2467. {
  2468. if (S_OK == CallCB(NULL, SMC_DISPLAYCHEVRONTIP, 0, 0))
  2469. {
  2470. _FlashChevron();
  2471. }
  2472. }
  2473. _fFirstTime = FALSE;
  2474. _fRefreshInfo = FALSE;
  2475. }
  2476. else
  2477. {
  2478. KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);
  2479. }
  2480. _fShowMB = _fShow = fShow;
  2481. // Reset these so we don't have stale information for the next drag drop cycle. NT #287914 (lamadio) 3.22.99
  2482. _tbim.iButton = -1;
  2483. _tbim.dwFlags = 0;
  2484. _idCmdDragging = -1;
  2485. // n.b. for !fShow, we don't kill the tracked site chain. we
  2486. // count on this in startmnu.cpp!CStartMenuCallback::_OnExecItem,
  2487. // where we walk up the chain to find all hit 'nodes'. if we need
  2488. // to change this we could fire a 'pre-exec' event.
  2489. }
  2490. void CMenuSFToolbar::v_UpdateButtons(BOOL fNegotiateSize)
  2491. {
  2492. CSFToolbar::_UpdateButtons();
  2493. if (_hwndTB && fNegotiateSize && _fVerticalMB)
  2494. NegotiateSize();
  2495. }
  2496. // this method invalidates a single item in the toolbar
  2497. HRESULT CMenuSFToolbar::v_InvalidateItem(LPSMDATA psmd, DWORD dwFlags)
  2498. {
  2499. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  2500. // Default to not not handling this event.
  2501. HRESULT hr = S_FALSE;
  2502. if (NULL == psmd)
  2503. {
  2504. if (dwFlags & SMINV_REFRESH)
  2505. {
  2506. _Refresh();
  2507. hr = S_OK;
  2508. }
  2509. }
  2510. else if (psmd->dwMask & SMDM_SHELLFOLDER)
  2511. {
  2512. // Yes;
  2513. int i;
  2514. LPITEMIDLIST pidlButton = NULL;
  2515. SMINFO sminfo;
  2516. sminfo.dwMask = SMIM_FLAGS;
  2517. // Since this pidl is comming from an outside source,
  2518. // we may need to translate it to a wrapped pidl.
  2519. // Do we have a pidl Translator?
  2520. if (_ptscn)
  2521. {
  2522. // Yes;
  2523. LPITEMIDLIST pidlTranslated;
  2524. LPITEMIDLIST pidlDummy = NULL;
  2525. LPITEMIDLIST pidlToTranslate = ILCombine(psmd->pidlFolder, psmd->pidlItem);
  2526. if (pidlToTranslate)
  2527. {
  2528. LONG lEvent = 0, lEvent2;
  2529. LPITEMIDLIST pidlDummy1, pidlDummy2;
  2530. if (SUCCEEDED(_ptscn->TranslateIDs(&lEvent, pidlToTranslate, NULL, &pidlTranslated, &pidlDummy,
  2531. &lEvent2, &pidlDummy1, &pidlDummy2)))
  2532. {
  2533. // Get the button in the toolbar that corresponds to this pidl.
  2534. _GetButtonFromPidl(ILFindLastID(pidlTranslated), NULL, &i, &pidlButton);
  2535. // if pidl does not get translated TranslateIDs returns the same pidl passed
  2536. // to the function
  2537. if (pidlTranslated != pidlToTranslate)
  2538. ILFree(pidlTranslated);
  2539. // Don't need to delete pidlDummy because it's not set.
  2540. ASSERT(pidlDummy == NULL);
  2541. ASSERT(pidlDummy1 == NULL);
  2542. ASSERT(pidlDummy2 == NULL);
  2543. }
  2544. ILFree(pidlToTranslate);
  2545. }
  2546. }
  2547. // Did we come from a non-augmented shell folder, or
  2548. // did the caller pass a wrapped pidl?
  2549. if (!pidlButton)
  2550. {
  2551. // Seems like it, we'll try to find the pidl they passed in
  2552. // Get the button in the toolbar that corresponds to this pidl.
  2553. _GetButtonFromPidl(psmd->pidlItem, NULL, &i, &pidlButton);
  2554. }
  2555. // Did we find this pidl in the toolbar?
  2556. if (pidlButton)
  2557. {
  2558. int idCmd = GetButtonCmd(_hwndTB, v_DPAIndexToTBIndex(i));
  2559. // Yes, Get the information from that button.
  2560. CMenuData* pmd = (CMenuData*)_IDToPibData(idCmd);
  2561. if (pmd)
  2562. {
  2563. BOOL fRefresh = FALSE;
  2564. DWORD dwFlagsUp = dwFlags;
  2565. DWORD dwOldItemFlags = pmd->GetFlags();
  2566. DWORD dwNewItemFlags = dwOldItemFlags;
  2567. if ((dwFlags & SMINV_DEMOTE) &&
  2568. (!(dwOldItemFlags & SMIF_DEMOTED) || dwFlags & SMINV_FORCE))
  2569. {
  2570. if (!(dwFlags & SMINV_NOCALLBACK))
  2571. {
  2572. CallCB(pidlButton, SMC_DEMOTE, 0, 0);
  2573. BroadcastIntelliMenuState(pidlButton, FALSE);
  2574. }
  2575. dwNewItemFlags |= SMIF_DEMOTED;
  2576. dwFlagsUp |= SMINV_DEMOTE;
  2577. }
  2578. else if ((dwFlags & SMINV_PROMOTE) &&
  2579. ((dwOldItemFlags & SMIF_DEMOTED) || dwFlags & SMINV_FORCE))
  2580. {
  2581. if (!(dwFlags & SMINV_NOCALLBACK))
  2582. {
  2583. CallCB(pidlButton, SMC_PROMOTE, dwFlags, 0);
  2584. BroadcastIntelliMenuState(pidlButton, TRUE);
  2585. }
  2586. dwNewItemFlags &= ~SMIF_DEMOTED;
  2587. dwFlagsUp |= SMINV_PROMOTE;
  2588. }
  2589. // Was it promoted and now Demoted or
  2590. // Was it demoted and now promoted
  2591. if ((dwNewItemFlags & SMIF_DEMOTED) ^
  2592. (dwOldItemFlags & SMIF_DEMOTED))
  2593. {
  2594. fRefresh = TRUE;
  2595. if (dwNewItemFlags & SMIF_DEMOTED)
  2596. {
  2597. // Yes; Then decrement the Promoted count
  2598. _cPromotedItems--;
  2599. // If we're decementing, then we not have a demoted item.
  2600. _fHasDemotedItems = TRUE;
  2601. // Have we dropped off the face of the earth?
  2602. if (_cPromotedItems == 0)
  2603. {
  2604. dwFlagsUp |= SMINV_DEMOTE;
  2605. Expand(TRUE);
  2606. }
  2607. else
  2608. {
  2609. fRefresh = FALSE;
  2610. }
  2611. }
  2612. else
  2613. {
  2614. int cButtons = ToolBar_ButtonCount(_hwndMB);
  2615. _cPromotedItems++;
  2616. if (cButtons == _cPromotedItems)
  2617. {
  2618. // if the button count is the number of promoted items,
  2619. // then we can't have any demoted items
  2620. // then we need to reset the _fHasDemotedItems flag so that
  2621. // we don't get a chevron and stuff...
  2622. _fHasDemotedItems = FALSE;
  2623. }
  2624. dwFlagsUp |= SMINV_PROMOTE;
  2625. fRefresh = TRUE;
  2626. }
  2627. }
  2628. if (fRefresh || dwFlags & SMINV_FORCE)
  2629. IUnknown_RefreshParent(_pcmb->_punkSite, _pidl, dwFlagsUp);
  2630. if (dwOldItemFlags != dwNewItemFlags || dwFlags & SMINV_FORCE)
  2631. {
  2632. if (dwFlags & SMINV_NEXTSHOW || !_fShow)
  2633. {
  2634. _fRefreshInfo = TRUE;
  2635. }
  2636. else
  2637. {
  2638. // Since we updated the flags, set them into the cache
  2639. pmd->SetFlags(dwNewItemFlags);
  2640. // Based on the new flags, do we enable?
  2641. DWORD dwState = ToolBar_GetState(_hwndTB, idCmd);
  2642. dwState |= TBSTATE_ENABLED;
  2643. if (dwNewItemFlags & SMIF_DEMOTED &&
  2644. !_pcmb->_fExpanded)
  2645. {
  2646. // No; We're not expanded and this is a demoted item
  2647. dwState |= TBSTATE_HIDDEN;
  2648. dwState &= ~TBSTATE_ENABLED;
  2649. _fHasDemotedItems = TRUE;
  2650. // Just in case the chevron is not there, we should
  2651. // try and add it. This call will never add more than 1
  2652. _AddChevron();
  2653. }
  2654. else if (!_fHasDemotedItems)
  2655. {
  2656. _RemoveChevron();
  2657. }
  2658. // Also recalculate the NS separator
  2659. _RemoveNSSeparator();
  2660. _AddNSSeparator();
  2661. // Adjust the state of the button in the toolbar.
  2662. ToolBar_SetState(_hwndTB, idCmd, dwState);
  2663. _ToolbarChanged();
  2664. }
  2665. }
  2666. }
  2667. }
  2668. // We handled this one.
  2669. hr = S_OK;
  2670. }
  2671. return hr;
  2672. }
  2673. LRESULT CMenuSFToolbar::_DefWindowProc(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
  2674. {
  2675. switch (uMessage)
  2676. {
  2677. case WM_GETOBJECT:
  2678. // Yet another poor design choice on the part of the accessibility team.
  2679. // Typically, if you do not answer a WM_* you return 0. They choose 0 as their success
  2680. // code.
  2681. return _DefWindowProcMB(hwnd, uMessage, wParam, lParam);
  2682. break;
  2683. }
  2684. return CSFToolbar::_DefWindowProc(hwnd, uMessage, wParam, lParam);
  2685. }
  2686. void CMenuSFToolbar::_SetFontMetrics()
  2687. {
  2688. CMenuToolbarBase::_SetFontMetrics();
  2689. if (_hwndPager && _pcmb->_pmbm)
  2690. Pager_SetBkColor(_hwndPager, _pcmb->_pmbm->_clrBackground);
  2691. }
  2692. int CMenuSFToolbar::_GetBitmap(int iCommandID, PIBDATA pibdata, BOOL fUseCache)
  2693. {
  2694. int iIcon = -1;
  2695. // If we don't have a pibdata, or we can't get an icon return.
  2696. if (!pibdata || pibdata->GetNoIcon())
  2697. return -1;
  2698. if (_dwFlags & SMSET_USEBKICONEXTRACTION)
  2699. {
  2700. LPITEMIDLIST pidlItem = pibdata->GetPidl();
  2701. // If the caller is using background icon extraction, we need them to provide a
  2702. // default icon that we are going to display until we get the real one. This is
  2703. // specifically to make favorites fast.
  2704. if (_iDefaultIconIndex == -1)
  2705. {
  2706. TCHAR szIconPath [MAX_PATH];
  2707. if (S_OK == CallCB(NULL, SMC_DEFAULTICON, (WPARAM)szIconPath, (LPARAM)&iIcon))
  2708. {
  2709. _iDefaultIconIndex = Shell_GetCachedImageIndex(szIconPath, iIcon, 0);
  2710. }
  2711. }
  2712. iIcon = _iDefaultIconIndex;
  2713. DWORD dwAttrib = SFGAO_FOLDER;
  2714. if (pidlItem && SUCCEEDED(_psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlItem, &dwAttrib)))
  2715. {
  2716. if (dwAttrib & SFGAO_FOLDER)
  2717. iIcon = II_FOLDER;
  2718. }
  2719. IShellTaskScheduler* pScheduler = _pcmb->_pmbState->GetScheduler();
  2720. if (pScheduler)
  2721. {
  2722. IShellFolder* psf = NULL;
  2723. LPITEMIDLIST pidlFolder = _pidl;
  2724. LPITEMIDLIST pidlItemUnwrapped;
  2725. // Since this can be an augmented shell folder, we should do the correct thing so that
  2726. // the icon extraction with the full pidl takes place correctly.
  2727. if (_pasf2 &&
  2728. S_OK == _pasf2->UnWrapIDList(pidlItem, 1, NULL, &pidlFolder, &pidlItemUnwrapped, NULL))
  2729. {
  2730. pidlItem = ILCombine(pidlFolder, pidlItemUnwrapped);
  2731. ILFree(pidlFolder);
  2732. ILFree(pidlItemUnwrapped);
  2733. }
  2734. else
  2735. {
  2736. psf = _psf;
  2737. }
  2738. // AddIconTask takes ownership of the pidl when psf is NULL and will free it.
  2739. HRESULT hr = AddIconTask(pScheduler, psf, pidlFolder, pidlItem,
  2740. s_IconCallback, (void *)_hwndTB, iCommandID, NULL);
  2741. pScheduler->Release();
  2742. if (FAILED(hr))
  2743. {
  2744. // If that call failed for some reason, default to the shell32 impl.
  2745. goto DoSyncMap;
  2746. }
  2747. }
  2748. else
  2749. goto DoSyncMap;
  2750. }
  2751. else
  2752. {
  2753. DoSyncMap:
  2754. iIcon = CSFToolbar::_GetBitmap(iCommandID, pibdata, fUseCache);
  2755. }
  2756. return iIcon;
  2757. }
  2758. void CMenuSFToolbar::s_IconCallback(void * pvData, UINT uId, UINT iIconIndex)
  2759. {
  2760. HWND hwnd = (HWND)pvData;
  2761. if (hwnd && IsWindow(hwnd))
  2762. {
  2763. DAD_ShowDragImage(FALSE);
  2764. SendMessage(hwnd, TB_CHANGEBITMAP, uId, iIconIndex);
  2765. DAD_ShowDragImage(TRUE);
  2766. }
  2767. }
  2768. HWND CMenuSFToolbar::GetHWNDForUIObject()
  2769. {
  2770. HWND hwnd = _pcmb->_pmbState->GetWorkerWindow(_hwndMB);
  2771. if (hwnd)
  2772. ::SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER);
  2773. return hwnd;
  2774. }
  2775. HWND CMenuSFToolbar::CreateWorkerWindow()
  2776. {
  2777. return GetHWNDForUIObject();
  2778. }
  2779. LRESULT CMenuSFToolbar::v_OnCustomDraw(NMCUSTOMDRAW * pnmcd)
  2780. {
  2781. LRESULT lRes = CMenuToolbarBase::v_OnCustomDraw(pnmcd);
  2782. #ifdef FLATMENU_ICONBAR
  2783. // We may have a background banner in Flat Menu Mode.
  2784. UINT cBits = GetDeviceCaps(pnmcd->hdc, BITSPIXEL);
  2785. if (pnmcd->dwDrawStage == CDDS_PREERASE &&
  2786. _pcmb->_pmbm->_fFlatMenuMode && // Only in flat mode
  2787. _uIconSizeMB != ISFBVIEWMODE_LARGEICONS && // And designers didn't like it in the large icon mode
  2788. cBits > 8) // and only if we're in 16 bit color
  2789. {
  2790. RECT rcClient;
  2791. GetClientRect(_hwndMB, &rcClient);
  2792. rcClient.right = GetTBImageListWidth(_hwndMB) + ICONBACKGROUNDFUDGE;
  2793. SHFillRectClr(pnmcd->hdc, &rcClient, _pcmb->_pmbm->_clrMenuGrad);
  2794. }
  2795. #endif
  2796. return lRes;
  2797. }