Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2989 lines
94 KiB

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