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.

2595 lines
79 KiB

  1. /*--------------------------------------------------------------------------*
  2. *
  3. * Microsoft Windows
  4. * Copyright (C) Microsoft Corporation, 1992 - 1999
  5. *
  6. * File: menubar.cpp
  7. *
  8. * Contents: Implementation file for CMenuBar
  9. *
  10. * History: 14-Nov-97 JeffRo Created
  11. *
  12. *--------------------------------------------------------------------------*/
  13. #include "stdafx.h"
  14. #include "menubar.h"
  15. #include "mdiuisim.h"
  16. #include "amc.h"
  17. #include "amcdoc.h"
  18. #include "mainfrm.h"
  19. #include "tbtrack.h"
  20. #include "mnemonic.h"
  21. #include "childfrm.h"
  22. #include "menubtns.h"
  23. #include <algorithm>
  24. #include <mmsystem.h>
  25. #include <oleacc.h>
  26. #include "amcview.h"
  27. #include "favorite.h"
  28. #include "util.h"
  29. /*
  30. * if we're supporting old platforms, we need to get MSAA definitions
  31. * from somewhere other than winuser.h
  32. */
  33. #if (_WINNT_WIN32 < 0x0500)
  34. #include <winable.h>
  35. #endif
  36. /*--------*/
  37. /* SAccel */
  38. /*--------*/
  39. struct SAccel : public ACCEL
  40. {
  41. SAccel (WORD key_, WORD cmd_, BYTE fVirt_)
  42. {
  43. ZeroMemory (this, sizeof (*this));
  44. key = key_;
  45. cmd = cmd_;
  46. fVirt = fVirt_;
  47. }
  48. };
  49. /*--------------------*/
  50. /* CPopupTrackContext */
  51. /*--------------------*/
  52. class CPopupTrackContext
  53. {
  54. public:
  55. CPopupTrackContext (CMenuBar* pMenuBar, int nCurrentPopupIndex);
  56. ~CPopupTrackContext ();
  57. // control over monitoring
  58. void StartMonitoring();
  59. bool WasAnotherPopupRequested(int& iNewIdx);
  60. private:
  61. typedef std::vector<int> BoundaryCollection;
  62. typedef BoundaryCollection::iterator BoundIt;
  63. typedef BoundaryCollection::const_iterator BoundConstIt;
  64. BoundaryCollection m_ButtonBoundaries;
  65. CMenuBar* const m_pMenuBar;
  66. HHOOK m_hhkMouse;
  67. HHOOK m_hhkKeyboard;
  68. HHOOK m_hhkCallWnd;
  69. CRect m_rectAllButtons;
  70. CPoint m_ptLastMouseMove;
  71. int m_nCurrentPopupIndex;
  72. CWnd* m_pMaxedMDIChild;
  73. const CPoint m_ptLButtonDown;
  74. const UINT m_dwLButtonDownTime;
  75. const int m_cButtons;
  76. int m_cCascadedPopups;
  77. bool m_fCurrentlyOnPopupItem;
  78. bool m_bPopupMonitorHooksActive;
  79. int m_iRequestForNewPopup;
  80. LRESULT MouseProc (int nCode, UINT msg, LPMOUSEHOOKSTRUCT pmhs);
  81. LRESULT KeyboardProc (int nCode, int vkey, int cRepeat, bool fDown, LPARAM lParam);
  82. LRESULT CallWndProc (int nCode, BOOL bCurrentThread, LPCWPSTRUCT lpCWP);
  83. int HitTest (CPoint pt) const;
  84. int MapBoundaryIteratorToButtonIndex (BoundConstIt it) const;
  85. void MaybeCloseMDIChild (CPoint pt);
  86. void DismissCurrentPopup (bool fTrackingComplete);
  87. void NotifyNewPopup (int nNewPopupIndex);
  88. void SetPopupMonitorHooks();
  89. void RemovePopupMonitorHooks();
  90. static LRESULT CALLBACK MouseProc (int nCode, WPARAM wParam, LPARAM lParam);
  91. static LRESULT CALLBACK KeyboardProc (int nCode, WPARAM wParam, LPARAM lParam);
  92. static LRESULT CALLBACK CallWndProc (int nCode, WPARAM wParam, LPARAM lParam);
  93. static CPopupTrackContext* s_pTrackContext;
  94. };
  95. CPopupTrackContext* CPopupTrackContext::s_pTrackContext = NULL;
  96. const TCHAR chChildSysMenuMnemonic = _T('-');
  97. /*--------------------------------------------------------------------------*
  98. * AddMnemonic
  99. *
  100. *
  101. *--------------------------------------------------------------------------*/
  102. template<class OutputIterator>
  103. static void AddMnemonic (
  104. TCHAR chMnemonic,
  105. int cmd,
  106. BYTE fVirt,
  107. OutputIterator it)
  108. {
  109. ASSERT (chMnemonic != _T('\0'));
  110. TCHAR chLower = (TCHAR) tolower (chMnemonic);
  111. TCHAR chUpper = (TCHAR) toupper (chMnemonic);
  112. /*
  113. * add the lowercase mnemonic
  114. */
  115. *it++ = SAccel (chLower, (WORD) cmd, fVirt);
  116. /*
  117. * if the uppercase mnemonic character is different from the
  118. * lowercase character, add the uppercase mnemonic as well
  119. */
  120. if (chUpper != chLower)
  121. *it++ = SAccel (chUpper, (WORD) cmd, fVirt);
  122. }
  123. /*--------------------------------------------------------------------------*
  124. * PrepBandInfo
  125. *
  126. *
  127. *--------------------------------------------------------------------------*/
  128. static void PrepBandInfo (LPREBARBANDINFO prbi, UINT fMask)
  129. {
  130. ZeroMemory (prbi, sizeof (REBARBANDINFO));
  131. prbi->cbSize = sizeof (REBARBANDINFO);
  132. prbi->fMask = fMask;
  133. }
  134. /////////////////////////////////////////////////////////////////////////////
  135. // CMenuBar
  136. BEGIN_MESSAGE_MAP(CMenuBar, CMMCToolBarCtrlEx)
  137. //{{AFX_MSG_MAP(CMenuBar)
  138. ON_NOTIFY_REFLECT(TBN_DROPDOWN, OnDropDown)
  139. ON_NOTIFY_REFLECT(TBN_GETDISPINFO, OnGetDispInfo)
  140. ON_NOTIFY_REFLECT(TBN_HOTITEMCHANGE, OnHotItemChange)
  141. ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
  142. ON_WM_SYSCOMMAND()
  143. ON_WM_SETTINGCHANGE()
  144. ON_WM_DESTROY()
  145. ON_COMMAND(ID_MTB_ACTIVATE_CURRENT_POPUP, OnActivateCurrentPopup)
  146. //}}AFX_MSG_MAP
  147. ON_MESSAGE(WM_POPUP_ASYNC, OnPopupAsync)
  148. ON_COMMAND(CMMCToolBarCtrlEx::ID_MTBX_PRESS_HOT_BUTTON, OnActivateCurrentPopup)
  149. ON_COMMAND_RANGE(ID_MTB_MENU_FIRST, ID_MTB_MENU_LAST, OnAccelPopup)
  150. ON_UPDATE_COMMAND_UI_RANGE(ID_MTB_MENU_FIRST, ID_MTB_MENU_LAST, OnUpdateAllCmdUI)
  151. END_MESSAGE_MAP()
  152. /*--------------------------------------------------------------------------*
  153. * CMenuBar::GetMenuUISimAccel
  154. *
  155. * Manages the accelerator table singleton for CMenuBar
  156. *--------------------------------------------------------------------------*/
  157. const CAccel& CMenuBar::GetMenuUISimAccel ()
  158. {
  159. static ACCEL aaclTrack[] = {
  160. { FVIRTKEY, VK_RETURN, CMenuBar::ID_MTB_ACTIVATE_CURRENT_POPUP },
  161. { FVIRTKEY, VK_UP, CMenuBar::ID_MTB_ACTIVATE_CURRENT_POPUP },
  162. { FVIRTKEY, VK_DOWN, CMenuBar::ID_MTB_ACTIVATE_CURRENT_POPUP },
  163. };
  164. static const CAccel MenuUISimAccel (aaclTrack, countof (aaclTrack));
  165. return (MenuUISimAccel);
  166. }
  167. /*--------------------------------------------------------------------------*
  168. * CMenuBar::CMenuBar
  169. *
  170. *
  171. *--------------------------------------------------------------------------*/
  172. CMenuBar::CMenuBar ()
  173. {
  174. m_pMDIFrame = NULL;
  175. m_pwndLastActive = NULL;
  176. m_pRebar = NULL;
  177. m_hMenuLast = NULL;
  178. m_hMaxedChildIcon = NULL;
  179. m_fDestroyChildIcon = false;
  180. m_fDecorationsShowing = false;
  181. m_fMaxedChildIconIsInvalid = false;
  182. m_CommandIDUnUsed.clear();
  183. m_vMenuAccels.clear();
  184. m_vTrackingAccels.clear();
  185. m_bInProgressDisplayingPopup = false;
  186. }
  187. /*--------------------------------------------------------------------------*
  188. * CMenuBar::~CMenuBar
  189. *
  190. *
  191. *--------------------------------------------------------------------------*/
  192. CMenuBar::~CMenuBar ()
  193. {
  194. DeleteMaxedChildIcon();
  195. }
  196. /*--------------------------------------------------------------------------*
  197. * CMenuBar::Create
  198. *
  199. *
  200. *--------------------------------------------------------------------------*/
  201. BOOL CMenuBar::Create (
  202. CFrameWnd * pwndFrame,
  203. CRebarDockWindow* pParentRebar,
  204. DWORD dwStyle,
  205. UINT idWindow)
  206. {
  207. ASSERT_VALID (pwndFrame);
  208. ASSERT_VALID (pParentRebar);
  209. // create the window
  210. if (!CMMCToolBarCtrlEx::Create (NULL, dwStyle | TBSTYLE_LIST,
  211. g_rectEmpty, pParentRebar, idWindow))
  212. return (FALSE);
  213. // initialize to hidded accelerator state
  214. SendMessage( WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEACCEL | UISF_HIDEFOCUS));
  215. // insert a hidden button for the maximized child's system menu
  216. InsertButton (0, (LPCTSTR) NULL, ID_MTB_MENU_SYSMENU, NULL, 0, 0);
  217. TBBUTTONINFO btni;
  218. btni.cbSize = sizeof (btni);
  219. btni.dwMask = TBIF_STATE | TBIF_SIZE;
  220. btni.fsState = TBSTATE_HIDDEN;
  221. btni.cx = static_cast<WORD>(GetSystemMetrics (SM_CXSMICON));
  222. SetButtonInfo (ID_MTB_MENU_SYSMENU, &btni);
  223. #ifdef SHRINK_PADDING
  224. CSize sizePad = GetPadding();
  225. sizePad.cx = 3;
  226. SetPadding (sizePad);
  227. #endif // SHRINK_PADDING
  228. SetMenuFont ();
  229. m_pRebar = pParentRebar->GetRebar();
  230. return (TRUE);
  231. }
  232. /*--------------------------------------------------------------------------*
  233. * CMenuBar::Create
  234. *
  235. *
  236. *--------------------------------------------------------------------------*/
  237. BOOL CMenuBar::Create (
  238. CMDIFrameWnd * pwndFrame,
  239. CRebarDockWindow* pParentRebar,
  240. DWORD dwStyle,
  241. UINT idWindow)
  242. {
  243. if (!Create ((CFrameWnd*) pwndFrame, pParentRebar, dwStyle, idWindow))
  244. return (FALSE);
  245. m_pMDIFrame = pwndFrame;
  246. // this is a menu for an MDI frame window; create MDI decorations
  247. m_pMDIDec = std::auto_ptr<CMDIMenuDecoration>(new CMDIMenuDecoration);
  248. m_pMDIDec->Create (NULL, NULL,
  249. WS_CHILD | MMDS_MINIMIZE |
  250. MMDS_RESTORE | MMDS_CLOSE | MMDS_AUTOSIZE,
  251. g_rectEmpty, this, ID_MDIDECORATION);
  252. // the rebar will re-parent the decoration, make sure we remain the owner
  253. m_pMDIDec->SetOwner (this);
  254. // insert the MDI decoration band
  255. REBARBANDINFO rbi;
  256. PrepBandInfo (&rbi, RBBIM_CHILD | RBBIM_STYLE | RBBIM_ID);
  257. rbi.fStyle = RBBS_FIXEDSIZE | RBBS_HIDDEN;
  258. rbi.hwndChild = m_pMDIDec->m_hWnd;
  259. rbi.wID = ID_MDIDECORATION;
  260. ASSERT (m_pRebar != NULL);
  261. m_pRebar->InsertBand (&rbi);
  262. // resize the decoration window, *after* inserting the band
  263. SizeDecoration ();
  264. // there's bug in rebar which will show a band's
  265. // child, even with RBBS_HIDDEN, work around it
  266. m_pMDIDec->ShowWindow (SW_HIDE);
  267. return (TRUE);
  268. }
  269. /*--------------------------------------------------------------------------*
  270. * CMenuBar::PreTranslateMessage
  271. *
  272. *
  273. *--------------------------------------------------------------------------*/
  274. BOOL CMenuBar::PreTranslateMessage(MSG* pMsg)
  275. {
  276. // show accelerators, since user indicated he wants to control using the keyboard
  277. if ( (pMsg->message == WM_SYSKEYDOWN ) &&
  278. (!(pMsg->lParam & 0x40000000)/*not repeating*/) )
  279. {
  280. SendMessage( WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL | UISF_HIDEFOCUS));
  281. }
  282. if (CMMCToolBarCtrlEx::PreTranslateMessage (pMsg))
  283. return (TRUE);
  284. if ((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST))
  285. {
  286. CMainFrame* pFrame = AMCGetMainWnd();
  287. if ((pFrame == NULL) || !pFrame->IsMenuVisible())
  288. return (FALSE);
  289. // if we're in menu mode, check menu mode-only accelerators
  290. if (IsTrackingToolBar ())
  291. {
  292. const CAccel& MenuUISimAccel = GetMenuUISimAccel();
  293. ASSERT (MenuUISimAccel != NULL);
  294. if (MenuUISimAccel.TranslateAccelerator (m_hWnd, pMsg))
  295. return (TRUE);
  296. ASSERT (m_TrackingAccel != NULL);
  297. if (m_TrackingAccel.TranslateAccelerator (m_hWnd, pMsg))
  298. return (TRUE);
  299. }
  300. if ((m_MenuAccel != NULL) && m_MenuAccel.TranslateAccelerator (m_hWnd, pMsg))
  301. return (TRUE);
  302. // handle Alt+- when child is maximized
  303. if (m_fDecorationsShowing &&
  304. (pMsg->message == WM_SYSCHAR) &&
  305. (pMsg->wParam == chChildSysMenuMnemonic))
  306. {
  307. SendMessage (WM_COMMAND, ID_MTB_MENU_SYSMENU);
  308. return (TRUE);
  309. }
  310. }
  311. return (FALSE);
  312. }
  313. /*--------------------------------------------------------------------------*
  314. * CMenuBar::SetMenu
  315. *
  316. *
  317. *--------------------------------------------------------------------------*/
  318. void CMenuBar::SetMenu (CMenu* pMenu)
  319. {
  320. HMENU hMenu = pMenu->GetSafeHmenu();
  321. // don't need to do anything if we're setting the same menu as last time
  322. if (hMenu == m_hMenuLast)
  323. return;
  324. // remember this menu for optimization next time
  325. m_hMenuLast = hMenu;
  326. // delete all but the first existing buttons from the toolbar
  327. while (DeleteButton(1))
  328. {
  329. }
  330. // delete the previous dynamic accelerator table
  331. m_MenuAccel.DestroyAcceleratorTable ();
  332. m_strAccelerators.Empty ();
  333. // this should have been done in CMenuBar::Create
  334. ASSERT (GetBitmapSize().cx == 0);
  335. if (pMenu != NULL)
  336. {
  337. CString strMenuText;
  338. // Clear the accels before calling InsertButton
  339. // which adds accels for each button.
  340. m_vMenuAccels.clear();
  341. m_vTrackingAccels.clear();
  342. int cMenuItems = pMenu->GetMenuItemCount();
  343. // Initialize Unused command IDs pool.
  344. m_CommandIDUnUsed.clear();
  345. for (INT idCommand = ID_MTB_FIRST_COMMANDID;
  346. idCommand < ID_MTB_MENU_LAST;
  347. idCommand++)
  348. {
  349. m_CommandIDUnUsed.insert(idCommand);
  350. }
  351. for (int i = 0; i < cMenuItems; i++)
  352. {
  353. // get the menu text and add it to the toolbar
  354. pMenu->GetMenuString (i, strMenuText, MF_BYPOSITION);
  355. // sometimes empty items will be appended to the menu, ignore them
  356. if (strMenuText.IsEmpty ())
  357. continue;
  358. if (m_CommandIDUnUsed.empty())
  359. {
  360. ASSERT(FALSE);
  361. }
  362. // See if this top level menu has sub menus, if so when menu is clicked
  363. // it is popped up else it is Action or View or Favorites menu
  364. // for which submenu is dynamic and has to be constructured.
  365. // So we set the TBBUTTON.dwData member with submenu (for static menus)
  366. // or NULL for for dynamic menus like Action, View, Favorites.
  367. CMenu* pSubMenu = pMenu->GetSubMenu(i);
  368. DWORD_PTR dwMenuData = NULL;
  369. BYTE byState = 0;
  370. if (pSubMenu)
  371. {
  372. // Get an unused command id for this button.
  373. CommandIDPool::iterator itCommandID = m_CommandIDUnUsed.begin();
  374. idCommand = *itCommandID;
  375. m_CommandIDUnUsed.erase(itCommandID);
  376. dwMenuData = reinterpret_cast<DWORD_PTR>(pSubMenu->GetSafeHmenu());
  377. }
  378. else
  379. {
  380. UINT uMenuID = pMenu->GetMenuItemID(i);
  381. switch (uMenuID)
  382. {
  383. case ID_ACTION_MENU:
  384. idCommand = ID_MTB_MENU_ACTION;
  385. break;
  386. case ID_VIEW_MENU:
  387. idCommand = ID_MTB_MENU_VIEW;
  388. break;
  389. case ID_FAVORITES_MENU:
  390. idCommand = ID_MTB_MENU_FAVORITES;
  391. break;
  392. case ID_SNAPIN_MENU_PLACEHOLDER:
  393. /*
  394. * We add a hidden menu as a marker. Later when snapin
  395. * calls to insert a menu button we find the position
  396. * of this menu and add snapin menu before it.
  397. */
  398. idCommand = ID_MTB_MENU_SNAPIN_PLACEHOLDER;
  399. byState |= TBSTATE_HIDDEN; // Add this as hidden.
  400. break;
  401. default:
  402. ASSERT(FALSE);
  403. return;
  404. break;
  405. }
  406. bool bShow = IsStandardMenuAllowed(uMenuID);
  407. if (! bShow)
  408. byState |= TBSTATE_HIDDEN;
  409. }
  410. // append this button to the end of the toolbar
  411. InsertButton (-1, strMenuText, idCommand, dwMenuData, byState, TBSTYLE_AUTOSIZE);
  412. }
  413. // add the accelerator for the child system menu
  414. std::back_insert_iterator<AccelVector>
  415. itTrackingInserter = std::back_inserter (m_vTrackingAccels);
  416. AddMnemonic (chChildSysMenuMnemonic, ID_MTB_MENU_SYSMENU, 0, itTrackingInserter);
  417. }
  418. UpdateToolbarSize ();
  419. AutoSize ();
  420. }
  421. //+-------------------------------------------------------------------
  422. //
  423. // Member: InsertButton
  424. //
  425. // Synopsis: Insert a menu (button) to the main menu and then
  426. // if there is a mnemonic char add it to accelerator
  427. // and re-load the accelerators.
  428. //
  429. // Arguments: [nIndex] - index after which to insert.
  430. // [strText] - menu text.
  431. // [idCommand] - ID of command to notify with.
  432. // [dwMenuData] - either submenu to be displayed (for static menus)
  433. // or NULL for Action, View & Favorites.
  434. // [fsState] - additional button states.
  435. // [fsStyle] - additional button styles.
  436. //
  437. // Returns: BOOL
  438. //
  439. // Note: dwMenuData is handle to submenu for File, Window, Help menus whose
  440. // sub-menu is static. But for top level menus like Action, View, Favorites,
  441. // and Snapin menus it is NULL.
  442. //
  443. //--------------------------------------------------------------------
  444. BOOL CMenuBar::InsertButton (
  445. int nIndex,
  446. const CString& strText,
  447. int idCommand,
  448. DWORD_PTR dwMenuData,
  449. BYTE fsState,
  450. BYTE fsStyle)
  451. {
  452. TBBUTTON btn;
  453. btn.iBitmap = nIndex;
  454. btn.idCommand = idCommand;
  455. if (fsState & TBSTATE_HIDDEN)
  456. btn.fsState = fsState;
  457. else
  458. btn.fsState = TBSTATE_ENABLED | fsState;
  459. btn.fsStyle = TBSTYLE_DROPDOWN | fsStyle;
  460. btn.dwData = dwMenuData;
  461. btn.iString = AddString (strText);
  462. ASSERT(GetButtonCount() <= cMaxTopLevelMenuItems);
  463. ASSERT (btn.idCommand <= ID_MTB_MENU_LAST);
  464. BOOL bRet = CMMCToolBarCtrlEx::InsertButton (nIndex, &btn);
  465. if (bRet == FALSE)
  466. return bRet;
  467. // Successfully added the menu button. Now add
  468. // the accelerator for this item to our dynamic tables
  469. TCHAR chMnemonic = GetMnemonicChar (static_cast<LPCTSTR>(strText));
  470. if (chMnemonic != _T('\0'))
  471. {
  472. std::back_insert_iterator<AccelVector>
  473. itMenuInserter = std::back_inserter (m_vMenuAccels);
  474. std::back_insert_iterator<AccelVector>
  475. itTrackingInserter = std::back_inserter (m_vTrackingAccels);
  476. // add the Alt+<mnemonic> accelerator for use all the time
  477. AddMnemonic (chMnemonic, idCommand, FALT, itMenuInserter);
  478. // add the <mnemonic> accelerator for use when we're in menu mode
  479. AddMnemonic (chMnemonic, idCommand, 0, itTrackingInserter);
  480. m_strAccelerators += (TCHAR) tolower (chMnemonic);
  481. m_strAccelerators += (TCHAR) toupper (chMnemonic);
  482. }
  483. // Re-load the accelerators
  484. LoadAccels();
  485. return bRet;
  486. }
  487. //+-------------------------------------------------------------------
  488. //
  489. // Member: LoadAccels
  490. //
  491. // Synopsis: Load the accelerators. (This destorys old accel table
  492. // and creates accel table).
  493. //
  494. // Arguments: None.
  495. //
  496. // Returns: void
  497. //
  498. //--------------------------------------------------------------------
  499. void CMenuBar::LoadAccels()
  500. {
  501. // create the accelerator tables for the menu
  502. m_MenuAccel .CreateAcceleratorTable (m_vMenuAccels.begin (),
  503. m_vMenuAccels.size ());
  504. m_TrackingAccel.CreateAcceleratorTable (m_vTrackingAccels.begin (),
  505. m_vTrackingAccels.size ());
  506. }
  507. //+-------------------------------------------------------------------
  508. //
  509. // Member: InsertMenuButton
  510. //
  511. // Synopsis: Insert a menu button to the main menu, called by
  512. // CMenuButtonsMgr to add any snapin menus.
  513. //
  514. // Arguments: [lpszButtonText] - menu text.
  515. // [bHidden] - Is this menu to be inserted hidden or not.
  516. // [iPreferredPos] - The preferred position of this button.
  517. //
  518. // Returns: LONG, the command id of the inserted button.
  519. // -1 if failed.
  520. //
  521. // Note: The snapin added menus should be added before the Window menu.
  522. // For this a hidden menu is added (in SetMenu) which tells the
  523. // position where snapin menu is to be added. If iPreferredPos is -1
  524. // then find the position of this menu and add the menu before it.
  525. //
  526. //--------------------------------------------------------------------
  527. LONG CMenuBar::InsertMenuButton(LPCTSTR lpszButtonText,
  528. BOOL bHidden,
  529. int iPreferredPos)
  530. {
  531. AFX_MANAGE_STATE (AfxGetAppModuleState());
  532. if (m_CommandIDUnUsed.size() == 0)
  533. return -1;
  534. // Get a command id for this button from the pool
  535. CommandIDPool::iterator itCommandID = m_CommandIDUnUsed.begin();
  536. int idCommand = *itCommandID;
  537. m_CommandIDUnUsed.erase(itCommandID);
  538. CString str = lpszButtonText;
  539. // Add snapin menus before the SNAPIN_MENU_PLACE holder.
  540. // See CMenubar::SetMenu.
  541. if (-1 == iPreferredPos)
  542. iPreferredPos = CommandToIndex(ID_MTB_MENU_SNAPIN_PLACEHOLDER);
  543. BOOL bSuccess = InsertButton(iPreferredPos,
  544. str, idCommand,
  545. NULL,
  546. bHidden ? TBSTATE_HIDDEN : 0,
  547. TBSTYLE_AUTOSIZE);
  548. if (bSuccess)
  549. {
  550. UpdateToolbarSize ();
  551. AutoSize ();
  552. return idCommand;
  553. }
  554. return -1;
  555. }
  556. //+-------------------------------------------------------------------
  557. //
  558. // Member: DeleteMenuButton
  559. //
  560. // Synopsis: Delete a menu button from the main menu, called by
  561. // CMenuButtonsMgr
  562. //
  563. // Arguments: [nCommandID] - Command ID of the menu.
  564. //
  565. // Returns: BOOL
  566. //
  567. // Note: Delete should also delete the accelerator.
  568. //
  569. //--------------------------------------------------------------------
  570. BOOL CMenuBar::DeleteMenuButton(INT nCommandID)
  571. {
  572. AFX_MANAGE_STATE (AfxGetAppModuleState());
  573. int iIndex = CommandToIndex(nCommandID);
  574. // We need to delete the mnemonics. So let us get the old
  575. // mnemonic before deleting the button.
  576. // 1. Get the menu text (string) index.
  577. TBBUTTON tbbi;
  578. ZeroMemory(&tbbi, sizeof(tbbi));
  579. BOOL bSuccess = GetButton(iIndex, &tbbi);
  580. if (FALSE == bSuccess)
  581. return bSuccess;
  582. // 2. Delete the button.
  583. bSuccess = DeleteButton(iIndex);
  584. ASSERT(bSuccess);
  585. if (FALSE == bSuccess)
  586. return bSuccess;
  587. // Add the command id to the unused pool.
  588. m_CommandIDUnUsed.insert(nCommandID);
  589. // Get the mnemonic and Delete it.
  590. ASSERT(m_ToolbarStringPool.size() > tbbi.iString);
  591. CString strText = m_ToolbarStringPool[tbbi.iString];
  592. for (AccelVector::iterator itAccel = m_vMenuAccels.begin();
  593. itAccel != m_vMenuAccels.end();
  594. itAccel++)
  595. {
  596. if (itAccel->cmd == (WORD)nCommandID)
  597. {
  598. m_vMenuAccels.erase(itAccel);
  599. break;
  600. }
  601. }
  602. for (AccelVector::iterator itTrack = m_vTrackingAccels.begin();
  603. itTrack != m_vTrackingAccels.end();
  604. itTrack++)
  605. {
  606. if (itTrack->cmd == (WORD)nCommandID)
  607. {
  608. m_vTrackingAccels.erase(itTrack);
  609. break;
  610. }
  611. }
  612. // Delete the mnemonic from m_strAccelerators.
  613. TCHAR chMnemonicOld = GetMnemonicChar (static_cast<LPCTSTR>(strText));
  614. if (chMnemonicOld != _T('\0'))
  615. {
  616. // CString::Remove cannot be used as it is for VC6. We use the tstring
  617. // to remove chars from the mnemonic string.
  618. tstring tstrAccels = m_strAccelerators;
  619. tstring::iterator itNewEnd = std::remove(tstrAccels.begin(), tstrAccels.end(), toupper(chMnemonicOld));
  620. tstrAccels.erase(itNewEnd, tstrAccels.end());
  621. itNewEnd = std::remove(tstrAccels.begin(), tstrAccels.end(), tolower(chMnemonicOld));
  622. tstrAccels.erase(itNewEnd, tstrAccels.end());
  623. m_strAccelerators = tstrAccels.data();
  624. }
  625. return bSuccess;
  626. }
  627. //+-------------------------------------------------------------------
  628. //
  629. // Member: SetMenuButton
  630. //
  631. // Synopsis: Modify menu button text, called by CMenuButtonsMgr
  632. //
  633. // Arguments: [nCommandID] - Command ID.
  634. // [lpszButtonText] - New text.
  635. //
  636. // Returns: LONG, command id of the menu (-1 if failed to change).
  637. //
  638. // Note: We delete the old button and add a new button with this
  639. // name and button id.
  640. // The reason for not calling SetButtonInfo is it does not allow us
  641. // to change the string index (iString in TBBUTTON).
  642. //
  643. //--------------------------------------------------------------------
  644. LONG CMenuBar::SetMenuButton(INT nCommandID, LPCTSTR lpszButtonText)
  645. {
  646. AFX_MANAGE_STATE (AfxGetAppModuleState());
  647. // Note the index and hidden state.
  648. int iIndex = CommandToIndex(nCommandID);
  649. bool bHidden = IsButtonHidden(nCommandID);
  650. // Please see the note above about why we do Delete and Insert
  651. // instead of Set.
  652. BOOL bSuccess = DeleteMenuButton(nCommandID);
  653. if (bSuccess)
  654. return InsertMenuButton(lpszButtonText, bHidden, iIndex);
  655. return -1;
  656. }
  657. /*--------------------------------------------------------------------------*
  658. * CMenuBar::SetMenuFont
  659. *
  660. *
  661. *--------------------------------------------------------------------------*/
  662. void CMenuBar::SetMenuFont ()
  663. {
  664. // delete the old font
  665. m_MenuFont.DeleteObject ();
  666. // query the system for the current menu font
  667. NONCLIENTMETRICS ncm;
  668. ncm.cbSize = sizeof (ncm);
  669. SystemParametersInfo (SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
  670. // use it here, too
  671. m_MenuFont.CreateFontIndirect (&ncm.lfMenuFont);
  672. SetFont (&m_MenuFont, FALSE);
  673. /*
  674. * get the metrics for the menu text, so we can use it's height
  675. */
  676. TEXTMETRIC tm;
  677. CWindowDC dc(this);
  678. CFont* pOldFont = dc.SelectObject (&m_MenuFont);
  679. dc.GetTextMetrics (&tm);
  680. dc.SelectObject (pOldFont);
  681. /*
  682. * Menu item buttons will contain only text (no bitmaps), so set the
  683. * bitmap width to 0 so we don't have unwanted whitespace.
  684. *
  685. * We need to reserve height for the bitmap, though. If we don't do
  686. * this, then the toolbar will calculate its own height to be too
  687. * small when there aren't any buttons with text. (This occurs in MDI
  688. * user mode when the active child is maximized. In that case, the
  689. * system menu button is visible, but we have no menu items.)
  690. */
  691. SetBitmapSize (CSize (0, std::_MAX ((int) tm.tmHeight,
  692. GetSystemMetrics (SM_CXSMICON))));
  693. }
  694. /*--------------------------------------------------------------------------*
  695. * CMenuBar::OnActivateCurrentPopup
  696. *
  697. *
  698. *--------------------------------------------------------------------------*/
  699. void CMenuBar::OnActivateCurrentPopup ()
  700. {
  701. PopupMenu (GetHotItem(), false /*bHighlightFirstItem*/);
  702. }
  703. /*--------------------------------------------------------------------------*
  704. * CMenuBar::OnDestroy
  705. *
  706. * WM_DESTROY handler for CMenuBar.
  707. *--------------------------------------------------------------------------*/
  708. void CMenuBar::OnDestroy()
  709. {
  710. CMMCToolBarCtrlEx::OnDestroy();
  711. GetMaxedChildIcon (NULL);
  712. }
  713. /*--------------------------------------------------------------------------*
  714. * CMenuBar::OnSysCommand
  715. *
  716. * WM_SYSCOMMAND handler for CMenuBar.
  717. *
  718. * If we want to get the right sound effects for the action, we'll need to
  719. * let DefWindowProc handle the message.
  720. *--------------------------------------------------------------------------*/
  721. void CMenuBar::OnSysCommand(UINT nID, LPARAM lParam)
  722. {
  723. ASSERT (m_pMDIFrame != NULL);
  724. BOOL bMaximized;
  725. CMDIChildWnd* pwndActive = m_pMDIFrame->MDIGetActive (&bMaximized);
  726. // if the user has quick fingers he may succeed in issuing the command
  727. // while the document is being closed - thus there may not be any
  728. // child windows at all. We ignore the command in such case
  729. // see bug #119775: MMC Crashes when snapin delays in closing down.
  730. if (pwndActive == NULL)
  731. return;
  732. switch (nID & 0xFFF0)
  733. {
  734. case SC_MINIMIZE: pwndActive->ShowWindow (SW_MINIMIZE); break;
  735. case SC_MAXIMIZE: pwndActive->ShowWindow (SW_MAXIMIZE); break;
  736. case SC_RESTORE: pwndActive->ShowWindow (SW_RESTORE); break;
  737. case SC_CLOSE: pwndActive->SendMessage (WM_CLOSE); break;
  738. default:
  739. CMMCToolBarCtrlEx::OnSysCommand (nID, lParam);
  740. break;
  741. }
  742. }
  743. /*--------------------------------------------------------------------------*
  744. * CMenuBar::OnSettingChange
  745. *
  746. * WM_SETTINGCHANGE handler for CMenuBar.
  747. *--------------------------------------------------------------------------*/
  748. void CMenuBar::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
  749. {
  750. if (uFlags == SPI_SETNONCLIENTMETRICS)
  751. {
  752. // the system menu font may have changed; update it now
  753. SetMenuFont ();
  754. // resize the decoration window
  755. SizeDecoration ();
  756. // update the size of the system menu button
  757. TBBUTTONINFO btni;
  758. btni.cbSize = sizeof (btni);
  759. btni.dwMask = TBIF_SIZE;
  760. btni.cx = static_cast<WORD>(GetSystemMetrics (SM_CXSMICON));
  761. SetButtonInfo (ID_MTB_MENU_SYSMENU, &btni);
  762. m_fMaxedChildIconIsInvalid = true;
  763. // auto-size the toolbar
  764. UpdateToolbarSize ();
  765. AutoSize ();
  766. }
  767. }
  768. /*--------------------------------------------------------------------------*
  769. * CMenuBar::SizeDecoration
  770. *
  771. *
  772. *--------------------------------------------------------------------------*/
  773. void CMenuBar::SizeDecoration ()
  774. {
  775. if (m_pMDIDec.get() == NULL)
  776. return;
  777. // jiggle the window's size so it will auto-size...
  778. m_pMDIDec->SetWindowPos (NULL, 0, 0, 10, 10,
  779. SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
  780. CRect rect;
  781. m_pMDIDec->GetClientRect (rect);
  782. // ...and update its band to accomodate it
  783. REBARBANDINFO rbi;
  784. PrepBandInfo (&rbi, RBBIM_SIZE | RBBIM_CHILDSIZE);
  785. rbi.cx = rect.Width();
  786. rbi.cxMinChild = rect.Width();
  787. rbi.cyMinChild = rect.Height();
  788. m_pRebar->SetBandInfo (GetDecorationBandIndex (), &rbi);
  789. }
  790. /*--------------------------------------------------------------------------*
  791. * void CMenuBar::OnDropDown
  792. *
  793. * Reflected TBN_DROPDOWN handler for void CMenuBar.
  794. *--------------------------------------------------------------------------*/
  795. afx_msg void CMenuBar::OnDropDown (
  796. NMHDR * pHdr,
  797. LRESULT * pResult)
  798. {
  799. ASSERT (CWnd::FromHandle (pHdr->hwndFrom) == this);
  800. // pop up the menu. Use async method, because toolbar will
  801. // keeb the button hilited until this function returns
  802. PopupMenuAsync (CommandToIndex (((LPNMTOOLBAR) pHdr)->iItem));
  803. // drop down notification handled here
  804. *pResult = TBDDRET_DEFAULT;
  805. }
  806. /*--------------------------------------------------------------------------*
  807. * void CMenuBar::OnGetDispInfo
  808. *
  809. * Reflected TBN_GETDISPINFO handler for void CMenuBar.
  810. *--------------------------------------------------------------------------*/
  811. afx_msg void CMenuBar::OnGetDispInfo (
  812. NMHDR * pHdr,
  813. LRESULT * pResult)
  814. {
  815. ASSERT (CWnd::FromHandle (pHdr->hwndFrom) == this);
  816. if (m_fDecorationsShowing)
  817. {
  818. NMTBDISPINFO* ptbdi = reinterpret_cast<NMTBDISPINFO *>(pHdr);
  819. if ((ptbdi->dwMask & TBNF_IMAGE) &&
  820. (ptbdi->idCommand != ID_MTB_MENU_SYSMENU))
  821. {
  822. ptbdi->iImage = -1;
  823. }
  824. }
  825. }
  826. /*--------------------------------------------------------------------------*
  827. * void CMenuBar::OnCustomDraw
  828. *
  829. * Reflected NM_CUSTOMDRAW handler for void CMenuBar.
  830. *--------------------------------------------------------------------------*/
  831. afx_msg void CMenuBar::OnCustomDraw (
  832. NMHDR * pHdr,
  833. LRESULT * pResult)
  834. {
  835. ASSERT (CWnd::FromHandle (pHdr->hwndFrom) == this);
  836. LPNMCUSTOMDRAW pnmcd = reinterpret_cast<LPNMCUSTOMDRAW>(pHdr);
  837. switch (pnmcd->dwDrawStage)
  838. {
  839. case CDDS_PREPAINT:
  840. // notify for individual buttons
  841. *pResult = CDRF_NOTIFYITEMDRAW;
  842. break;
  843. case CDDS_ITEMPREPAINT:
  844. // draw the system menu button manually
  845. if (pnmcd->dwItemSpec == ID_MTB_MENU_SYSMENU)
  846. {
  847. if (m_fMaxedChildIconIsInvalid)
  848. GetMaxedChildIcon (m_pMDIFrame->MDIGetActive());
  849. if (m_hMaxedChildIcon != NULL)
  850. {
  851. /*
  852. * compute the location at which we'll draw,
  853. * biasing down and to the left
  854. */
  855. CRect rect = pnmcd->rc;
  856. int dx = (rect.Width() - GetSystemMetrics(SM_CXSMICON) ) / 2;
  857. int dy = (rect.Height() - GetSystemMetrics(SM_CYSMICON) + 1) / 2;
  858. /*
  859. * Preserve icon shape when BitBlitting it to a
  860. * mirrored DC.
  861. */
  862. DWORD dwLayout=0L;
  863. if ((dwLayout=GetLayout(pnmcd->hdc)) & LAYOUT_RTL)
  864. {
  865. SetLayout(pnmcd->hdc, dwLayout|LAYOUT_BITMAPORIENTATIONPRESERVED);
  866. }
  867. DrawIconEx (pnmcd->hdc,
  868. rect.left + dx,
  869. rect.top + dy,
  870. m_hMaxedChildIcon, 0, 0, 0,
  871. NULL, DI_NORMAL);
  872. /*
  873. * Restore the DC to its previous layout state.
  874. */
  875. if (dwLayout & LAYOUT_RTL)
  876. {
  877. SetLayout(pnmcd->hdc, dwLayout);
  878. }
  879. }
  880. // skip the default drawing
  881. *pResult = CDRF_SKIPDEFAULT;
  882. }
  883. else
  884. {
  885. // do the default drawing
  886. *pResult = CDRF_DODEFAULT;
  887. }
  888. break;
  889. }
  890. }
  891. /*--------------------------------------------------------------------------*
  892. * CMenuBar::PopupMenuAsync
  893. *
  894. *
  895. *--------------------------------------------------------------------------*/
  896. void CMenuBar::PopupMenuAsync (int nItemIdex)
  897. {
  898. PostMessage (WM_POPUP_ASYNC, nItemIdex);
  899. }
  900. /*--------------------------------------------------------------------------*
  901. * void CMenuBar::OnPopupAsync
  902. *
  903. * WM_POPUP_ASYNC handler for void CMenuBar.
  904. *--------------------------------------------------------------------------*/
  905. afx_msg LRESULT CMenuBar::OnPopupAsync (WPARAM wParam, LPARAM)
  906. {
  907. PopupMenu (wParam, false /*bHighlightFirstItem*/);
  908. return (0);
  909. }
  910. /***************************************************************************\
  911. *
  912. * METHOD: CMenuBar::OnHotItemChange
  913. *
  914. * PURPOSE: Called when item hiliting changes. Used here to detect when menu
  915. * is dissmissed to reset the UI
  916. *
  917. * PARAMETERS:
  918. * NMHDR* pNMHDR
  919. * LRESULT* pResult
  920. *
  921. * RETURNS:
  922. * void
  923. *
  924. \***************************************************************************/
  925. afx_msg void CMenuBar::OnHotItemChange(NMHDR* pNMHDR, LRESULT* pResult)
  926. {
  927. DECLARE_SC(sc, TEXT("CMenuBar::OnHotItemChange"));
  928. // parameter chack
  929. sc = ScCheckPointers(pNMHDR, pResult);
  930. if (sc)
  931. return;
  932. // init out parameter
  933. *pResult = 0;
  934. // let the base class do it's job
  935. CMMCToolBarCtrlEx::OnHotItemChange(pNMHDR, pResult);
  936. // if menu is dismissed and not because of popup being displayed,
  937. // we need to revert to initial state by hiding accelerators
  938. LPNMTBHOTITEM lpNmTbHotItem = (LPNMTBHOTITEM)pNMHDR;
  939. if ( (*pResult == 0) &&
  940. (lpNmTbHotItem->dwFlags & HICF_LEAVING) &&
  941. !m_bInProgressDisplayingPopup )
  942. {
  943. SendMessage( WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEACCEL | UISF_HIDEFOCUS));
  944. }
  945. }
  946. /*+-------------------------------------------------------------------------*
  947. *
  948. * CMenuBar::PopupMenu
  949. *
  950. * PURPOSE: Displays the popup menu specified by the index.
  951. *
  952. * PARAMETERS:
  953. * int nItemIndex :
  954. * bool bHighlightFirstItem : true to automatically highlight the first item
  955. *
  956. * RETURNS:
  957. * void
  958. *
  959. *+-------------------------------------------------------------------------*/
  960. void
  961. CMenuBar::PopupMenu (int nItemIndex, bool bHighlightFirstItem)
  962. {
  963. DECLARE_SC(sc, TEXT("CMenuBar::PopupMenu"));
  964. // get the rectangle that we don't want the popup to overlap
  965. CRect rectExclude;
  966. CPopupTrackContext popupMonitor(this, nItemIndex);
  967. /*
  968. * OnIdle updates various member variables such as
  969. * m_fDecorationsShowing. It must be called before
  970. * popup menu is created to ensure accurate information
  971. */
  972. OnIdle();
  973. // there is high possibility, that the code in following block
  974. // adds nothing to the current functionality. It is left as it was
  975. // in previos implementation, since it isn't obvios if removing it
  976. // does not break anything.
  977. // Even if the block is removed, call to EndTracking() at the end of method
  978. // must still be present to exit tracking if it was put by entering menu
  979. // via pressing the ALT key
  980. {
  981. /*
  982. * make sure the frame's tracking manager is in tracking mode
  983. */
  984. CMainFrame* pFrame = AMCGetMainWnd();
  985. if (pFrame != NULL)
  986. {
  987. CToolbarTracker* pTracker = pFrame->GetToolbarTracker();
  988. ASSERT (pTracker != NULL);
  989. if (!pTracker->IsTracking())
  990. pTracker->BeginTracking();
  991. }
  992. BeginTracking ();
  993. }
  994. // following is to indicate that the change in menu (if one does occurr)
  995. // is because of attempt to switch to another submenu, and should not be
  996. // treated as dismissing the menu. thus accelerator state should not be changed
  997. m_bInProgressDisplayingPopup = true;
  998. // There are two ways the popup menu is dismissed. it either:
  999. // - request to display the new popup (left & right arrows, mouse on other item, etc)
  1000. // - or request to close the menu ( command is selected, ESC is pressed )
  1001. // following loop will continue displaying menus until request to close the menu is received
  1002. do {
  1003. GetItemRect (nItemIndex, rectExclude);
  1004. MapWindowPoints (NULL, rectExclude);
  1005. // get the information for this button
  1006. TBBUTTON btn;
  1007. GetButton (nItemIndex, &btn);
  1008. // if the requested button is ignorable, punt
  1009. if (::IsIgnorableButton (btn))
  1010. break;
  1011. // get the popup menu to display
  1012. HMENU hPopupMenu = (HMENU) btn.dwData;
  1013. // For system menu hPopupMenu will be NULL.
  1014. // If system menu is requested get it now.
  1015. if (ID_MTB_MENU_SYSMENU == btn.idCommand)
  1016. {
  1017. ASSERT (m_fDecorationsShowing);
  1018. ASSERT (m_pMDIFrame != NULL);
  1019. CMDIChildWnd* pwndActive = m_pMDIFrame->MDIGetActive();
  1020. ASSERT (pwndActive != NULL);
  1021. if (pwndActive == NULL)
  1022. break;
  1023. hPopupMenu = pwndActive->GetSystemMenu(FALSE)->GetSafeHmenu();
  1024. }
  1025. // display the button's popup menu
  1026. TPMPARAMS tpm;
  1027. tpm.cbSize = sizeof(TPMPARAMS);
  1028. tpm.rcExclude = rectExclude;
  1029. SetHotItem (-1);
  1030. PressButton (btn.idCommand, TRUE);
  1031. // Get the point where the menu should be popped up.
  1032. bool fLayoutRTL = (GetExStyle() & WS_EX_LAYOUTRTL);
  1033. POINT pt;
  1034. pt.y = rectExclude.bottom;
  1035. pt.x = (fLayoutRTL) ? rectExclude.right : rectExclude.left;
  1036. /*
  1037. * Bug 17342: TrackPopupMenuEx doesn't place the menu well if the
  1038. * x-coordinate of its origin is off-screen to the left, so prevent
  1039. * this from occurring. TrackPopupMenuEx *does* work fine when the
  1040. * x-coordinate is off-screen to the right or if the y-coordinate is
  1041. * off-screen to the bottom, so we don't need to account for those
  1042. * cases. (The system prevents placing the window such that the
  1043. * y-coordinate would be off-screen to the top.)
  1044. *
  1045. * Bug 173543: We can't assume that an x-coordinate less than 0 is
  1046. * off the screen. For multimon systems with the primary monitor
  1047. * on the right, the left monitor will have negative x coordinates.
  1048. * Our left-most position will be the left edge of the monitor nearest
  1049. * where the menu will be displayed.
  1050. */
  1051. int xMin = 0;
  1052. HMONITOR hmonMenu = MonitorFromPoint (pt, MONITOR_DEFAULTTONEAREST);
  1053. if (hmonMenu != NULL)
  1054. {
  1055. MONITORINFO mi = { sizeof(mi) };
  1056. if (GetMonitorInfo (hmonMenu, &mi))
  1057. xMin = (fLayoutRTL) ? mi.rcWork.right : mi.rcWork.left;
  1058. }
  1059. if ((!fLayoutRTL && (pt.x < xMin)) || (fLayoutRTL && (pt.x > xMin)))
  1060. pt.x = xMin;
  1061. // HACK: post a bogus down arrow so the first menu item will be selected
  1062. if(bHighlightFirstItem)
  1063. {
  1064. CWnd* pwndFocus = GetFocus();
  1065. if (pwndFocus != NULL)
  1066. pwndFocus->PostMessage (WM_KEYDOWN, VK_DOWN, 1);
  1067. }
  1068. // monitor what's the popup menu fate
  1069. popupMonitor.StartMonitoring();
  1070. // Child window wont exist if there is no views, so check this ptr before using it.
  1071. CChildFrame* pChildFrame = dynamic_cast<CChildFrame*>(m_pMDIFrame->MDIGetActive());
  1072. // hPopupMenu exists only if the sub-menus were added thro resource like File, Window
  1073. // and Help menus. The Action, View, Favorites and any snapin added menu's sub-menus
  1074. // are not added thro resources so for them hPopupmenu is null.
  1075. if (! hPopupMenu)
  1076. {
  1077. sc = ScCheckPointers(pChildFrame, E_UNEXPECTED);
  1078. if (sc)
  1079. {
  1080. sc.TraceAndClear();
  1081. break;
  1082. }
  1083. CAMCView *pAMCView = pChildFrame->GetAMCView();
  1084. sc = ScCheckPointers(pAMCView, E_UNEXPECTED);
  1085. if (sc)
  1086. {
  1087. sc.TraceAndClear();
  1088. break;
  1089. }
  1090. switch(btn.idCommand)
  1091. {
  1092. case ID_MTB_MENU_ACTION:
  1093. pAMCView->OnActionMenu(pt, rectExclude);
  1094. break;
  1095. case ID_MTB_MENU_VIEW:
  1096. pAMCView->OnViewMenu(pt, rectExclude);
  1097. break;
  1098. case ID_MTB_MENU_FAVORITES:
  1099. pAMCView->OnFavoritesMenu(pt, rectExclude);
  1100. break;
  1101. // Assumption if none of the above then it is snapin added menu.
  1102. // We try to forward this to snapin else we get an error.
  1103. default:
  1104. {
  1105. // If this is one of the MenuButtons inserted by
  1106. // the CMenuButtonsMgrImpl, notify CMenuButtonsMgrImpl
  1107. // to do TrackPopupMenu.
  1108. // Get the CMenuButtonsMgrImpl from the ChildFrame.
  1109. CMenuButtonsMgrImpl* pMenuBtnsMgr = pChildFrame->GetMenuButtonsMgr();
  1110. sc = ScCheckPointers(pMenuBtnsMgr, E_UNEXPECTED);
  1111. if (sc)
  1112. break;
  1113. // Notify CMenuButtonsMgr to popup a menu.
  1114. sc = pMenuBtnsMgr->ScNotifyMenuClick(btn.idCommand, pt);
  1115. if (sc)
  1116. break;
  1117. }
  1118. break;
  1119. }
  1120. if (sc)
  1121. {
  1122. sc.TraceAndClear();
  1123. break;
  1124. }
  1125. }
  1126. else
  1127. {
  1128. ASSERT (::IsMenu (hPopupMenu));
  1129. HWND hwndMenuOwner = AfxGetMainWnd()->GetSafeHwnd();
  1130. TrackPopupMenuEx (hPopupMenu,
  1131. TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
  1132. pt.x, pt.y, hwndMenuOwner, &tpm);
  1133. }
  1134. // Clear the status bar.
  1135. if (pChildFrame)
  1136. sc = pChildFrame->ScSetStatusText(TEXT(""));
  1137. if (sc)
  1138. sc.TraceAndClear();
  1139. PressButton (btn.idCommand, FALSE);
  1140. SetHotItem (-1);
  1141. // the loop will continue if it was requested to display the new popup menu.
  1142. // if it was requested to simply close the menu, execution will exit the loop
  1143. }while ( popupMonitor.WasAnotherPopupRequested(nItemIndex) );
  1144. m_bInProgressDisplayingPopup = false;
  1145. //reset the UI by hiding the accelerators (since we are done)
  1146. SendMessage( WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEACCEL | UISF_HIDEFOCUS));
  1147. EndTracking();
  1148. }
  1149. /*--------------------------------------------------------------------------*
  1150. * CMenuBar::OnAccelPopup
  1151. *
  1152. * Keyboard accelerator handler for CMenuBar.
  1153. *--------------------------------------------------------------------------*/
  1154. void CMenuBar::OnAccelPopup (UINT cmd)
  1155. {
  1156. PopupMenu (CommandToIndex (cmd), true /*bHighlightFirstItem*/);
  1157. }
  1158. /*--------------------------------------------------------------------------*
  1159. * void CMenuBar::OnUpdateAllCmdUI
  1160. *
  1161. *
  1162. *--------------------------------------------------------------------------*/
  1163. void CMenuBar::OnUpdateAllCmdUI (CCmdUI* pCmdUI)
  1164. {
  1165. pCmdUI->Enable ();
  1166. }
  1167. /*--------------------------------------------------------------------------*
  1168. * CMenuBar::AddString
  1169. *
  1170. * The toolbar control doesn't provide a way to delete strings once they've
  1171. * been added, so we'll check our cache of strings that we've already added
  1172. * to the toolbar so we don't add wasteful duplicate strings.
  1173. *--------------------------------------------------------------------------*/
  1174. int CMenuBar::AddString (const CString& strAdd)
  1175. {
  1176. // -1 for empty strings
  1177. if (strAdd.IsEmpty())
  1178. return (-1);
  1179. // check the cache
  1180. ToolbarStringPool::iterator it = std::find (m_ToolbarStringPool.begin(),
  1181. m_ToolbarStringPool.end(),
  1182. strAdd);
  1183. // if we found a hit in the cache, return the cached index
  1184. if (it != m_ToolbarStringPool.end())
  1185. return (it - m_ToolbarStringPool.begin());
  1186. // new string, add it to the cache...
  1187. m_ToolbarStringPool.push_back (strAdd);
  1188. // ...and to the toolbar, including a double-NULL
  1189. int cchAdd = strAdd.GetLength() + 1;
  1190. LPTSTR pszAdd = (LPTSTR) _alloca ((cchAdd + 1) * sizeof (TCHAR));
  1191. _tcscpy (pszAdd, strAdd);
  1192. pszAdd[cchAdd] = 0;
  1193. int nIndex = AddStrings (pszAdd);
  1194. // make sure the toolbar's string index matches the cache's string index
  1195. ASSERT (nIndex == m_ToolbarStringPool.end()-m_ToolbarStringPool.begin()-1);
  1196. return (nIndex);
  1197. }
  1198. /*--------------------------------------------------------------------------*
  1199. * CMenuBar::GetMenuBandIndex
  1200. *
  1201. *
  1202. *--------------------------------------------------------------------------*/
  1203. int CMenuBar::GetMenuBandIndex () const
  1204. {
  1205. return (m_pRebar->IdToIndex (GetDlgCtrlID ()));
  1206. }
  1207. /*--------------------------------------------------------------------------*
  1208. * CMenuBar::GetDecorationBandIndex
  1209. *
  1210. *
  1211. *--------------------------------------------------------------------------*/
  1212. int CMenuBar::GetDecorationBandIndex () const
  1213. {
  1214. return (m_pRebar->IdToIndex (ID_MDIDECORATION));
  1215. }
  1216. /*--------------------------------------------------------------------------*
  1217. * CMenuBar::GetFirstButtonIndex
  1218. *
  1219. *
  1220. *--------------------------------------------------------------------------*/
  1221. int CMenuBar::GetFirstButtonIndex ()
  1222. {
  1223. // make sure the system menu isn't the first one activated
  1224. return (GetNextButtonIndex (0));
  1225. }
  1226. /*--------------------------------------------------------------------------*
  1227. * CMenuBar::OnIdle
  1228. *
  1229. * WM_IDLE handler for CMenuBar.
  1230. *--------------------------------------------------------------------------*/
  1231. void CMenuBar::OnIdle ()
  1232. {
  1233. /*----------------------------------------------------------*/
  1234. /* If there's no MDI frame, that means this menu is serving */
  1235. /* an SDI window. We don't have to do any special stuff to */
  1236. /* simulate the MDI menu UI, so bail now. */
  1237. /*----------------------------------------------------------*/
  1238. if (m_pMDIFrame == NULL)
  1239. return;
  1240. ProgramMode eMode = AMCGetApp()->GetMode();
  1241. // if we're in SDI User mode, bail now as well
  1242. if (eMode == eMode_User_SDI)
  1243. {
  1244. #ifdef DBG
  1245. // the decorations should be hidden
  1246. REBARBANDINFO rbi;
  1247. PrepBandInfo (&rbi, RBBIM_STYLE);
  1248. m_pRebar->GetBandInfo (GetDecorationBandIndex(), &rbi);
  1249. ASSERT (rbi.fStyle & RBBS_HIDDEN);
  1250. #endif
  1251. return;
  1252. }
  1253. /*---------------------------------------------------------------*/
  1254. /* We should be able to use MDIGetActive(&fMaximized) to tell */
  1255. /* whether the active window is maximized, instead of calling */
  1256. /* pwndActive->IsZoomed(). However, fMaximized doesn't always */
  1257. /* get initialized correctly in certain low memory/slow machine */
  1258. /* situations. This was the cause of Bug 133179, logged by SQL. */
  1259. /*---------------------------------------------------------------*/
  1260. CMDIChildWnd* pwndActive = m_pMDIFrame->MDIGetActive ();
  1261. bool fShow = (pwndActive != NULL) && pwndActive->IsZoomed();
  1262. REBARBANDINFO rbi;
  1263. PrepBandInfo (&rbi, RBBIM_STYLE);
  1264. m_pRebar->GetBandInfo (GetMenuBandIndex(), &rbi);
  1265. // if the menu bar is hidden, the decorations must be hidden as well
  1266. if (rbi.fStyle & RBBS_HIDDEN)
  1267. fShow = false;
  1268. if (fShow != m_fDecorationsShowing)
  1269. {
  1270. // show/hide the MDI decorations
  1271. m_pRebar->ShowBand (GetDecorationBandIndex(), fShow);
  1272. GetMaxedChildIcon (pwndActive);
  1273. HideButton (ID_MTB_MENU_SYSMENU, !fShow);
  1274. UpdateToolbarSize ();
  1275. // remember the new setting
  1276. m_fDecorationsShowing = fShow;
  1277. }
  1278. // otherwise, see if a window was maximized before but a different one is maxed now
  1279. else if (fShow && (m_pwndLastActive != pwndActive))
  1280. {
  1281. // get the new active window's icon
  1282. GetMaxedChildIcon (pwndActive);
  1283. // repaint the menu and MDI decorations
  1284. InvalidateRect (NULL);
  1285. m_pMDIDec->InvalidateRect (NULL);
  1286. }
  1287. // remember the currently active window
  1288. m_pwndLastActive = pwndActive;
  1289. // insure that it's safe to keep this pointer around
  1290. ASSERT ((m_pwndLastActive == NULL) ||
  1291. (CWnd::FromHandlePermanent (m_pwndLastActive->m_hWnd) != NULL));
  1292. }
  1293. /*--------------------------------------------------------------------------*
  1294. * CMenuBar::DeleteMaxedChildIcon
  1295. *
  1296. *
  1297. *--------------------------------------------------------------------------*/
  1298. void
  1299. CMenuBar::DeleteMaxedChildIcon()
  1300. {
  1301. // destroy the previous icon, if we need to
  1302. if (m_fDestroyChildIcon)
  1303. {
  1304. ASSERT (m_hMaxedChildIcon != NULL);
  1305. DestroyIcon (m_hMaxedChildIcon);
  1306. m_hMaxedChildIcon = NULL;
  1307. m_fDestroyChildIcon = false;
  1308. }
  1309. }
  1310. /*--------------------------------------------------------------------------*
  1311. * CMenuBar::GetMaxedChildIcon
  1312. *
  1313. *
  1314. *--------------------------------------------------------------------------*/
  1315. void CMenuBar::GetMaxedChildIcon (CWnd* pwnd)
  1316. {
  1317. DeleteMaxedChildIcon();
  1318. // get the small icon for the given window
  1319. if (IsWindow (pwnd->GetSafeHwnd()))
  1320. {
  1321. HICON hOriginalIcon = pwnd->GetIcon (false /*bBigIcon*/);
  1322. m_hMaxedChildIcon = (HICON) CopyImage (
  1323. hOriginalIcon, IMAGE_ICON,
  1324. GetSystemMetrics (SM_CXSMICON),
  1325. GetSystemMetrics (SM_CYSMICON),
  1326. LR_COPYFROMRESOURCE | LR_COPYRETURNORG);
  1327. // if the system had to create a new icon, we'll have to destroy it later
  1328. if ((m_hMaxedChildIcon != NULL) && (m_hMaxedChildIcon != hOriginalIcon))
  1329. m_fDestroyChildIcon = true;
  1330. }
  1331. m_fMaxedChildIconIsInvalid = false;
  1332. }
  1333. /*+-------------------------------------------------------------------------*
  1334. * CMenuBar::GetAccelerators
  1335. *
  1336. *
  1337. *--------------------------------------------------------------------------*/
  1338. void CMenuBar::GetAccelerators (int cchBuffer, LPTSTR lpBuffer) const
  1339. {
  1340. lstrcpyn (lpBuffer, m_strAccelerators, cchBuffer);
  1341. }
  1342. //+-------------------------------------------------------------------
  1343. //
  1344. // Member: CMenuBar::IsStandardMenuAllowed
  1345. //
  1346. // Synopsis: Is this standard MMC menu allowed or not.
  1347. //
  1348. // Arguments: uMenuID - The menu ID.
  1349. //
  1350. // Returns: bool
  1351. //
  1352. //--------------------------------------------------------------------
  1353. bool CMenuBar::IsStandardMenuAllowed(UINT uMenuID)
  1354. {
  1355. DECLARE_SC(sc, _T("CMenuBar::IsStandardMenuAllowed"));
  1356. /*
  1357. * We add a hidden menu as a marker. Later when snapin
  1358. * calls to insert a menu button we find the position
  1359. * of this menu and add snapin menu before it.
  1360. * So this acts as Std menu which is always allowed.
  1361. */
  1362. if (uMenuID == ID_SNAPIN_MENU_PLACEHOLDER)
  1363. return true;
  1364. // First make sure it is one of the std menus.
  1365. if ( (uMenuID != ID_FAVORITES_MENU) &&
  1366. (uMenuID != ID_ACTION_MENU) &&
  1367. (uMenuID != ID_VIEW_MENU))
  1368. {
  1369. sc = E_INVALIDARG;
  1370. return true;
  1371. }
  1372. // Ask view data if std menus are allowed.
  1373. CMainFrame* pMainFrame = AMCGetMainWnd();
  1374. sc = ScCheckPointers(pMainFrame, E_UNEXPECTED);
  1375. if (sc)
  1376. return false;
  1377. CAMCView *pAMCView = pMainFrame->GetActiveAMCView();
  1378. sc = ScCheckPointers(pAMCView, E_UNEXPECTED);
  1379. if (sc)
  1380. return false;
  1381. SViewData* pViewData = pAMCView->GetViewData();
  1382. sc = ScCheckPointers(pViewData, E_UNEXPECTED);
  1383. if (sc)
  1384. return false;
  1385. if (! pViewData->IsStandardMenusAllowed())
  1386. return false;
  1387. if (uMenuID != ID_FAVORITES_MENU)
  1388. return true;
  1389. /*
  1390. * Display the Favorites menu button if we are in author mode, or if
  1391. * we're in user mode and we have at least one favorite. If we're in
  1392. * user mode and no favorites are defined, hide the Favorites button.
  1393. */
  1394. bool fShowFavorites = true;
  1395. CAMCApp* pApp = AMCGetApp();
  1396. if (pApp != NULL)
  1397. {
  1398. /*
  1399. * show favorites in author mode
  1400. */
  1401. fShowFavorites = (pApp->GetMode() == eMode_Author);
  1402. /*
  1403. * not author mode? see if we have any favorites
  1404. */
  1405. if (!fShowFavorites)
  1406. {
  1407. CAMCDoc* pDoc = CAMCDoc::GetDocument ();
  1408. if (pDoc != NULL)
  1409. {
  1410. CFavorites* pFavorites = pDoc->GetFavorites();
  1411. if (pFavorites != NULL)
  1412. fShowFavorites = !pFavorites->IsEmpty();
  1413. }
  1414. }
  1415. }
  1416. return fShowFavorites;
  1417. }
  1418. //+-------------------------------------------------------------------
  1419. //
  1420. // Member: CMenuBar::ScShowMMCMenus
  1421. //
  1422. // Synopsis: Show or Hide the MMC menus (Action, View & Favorites).
  1423. // Called from customize view.
  1424. //
  1425. // Arguments:
  1426. //
  1427. // Returns: SC
  1428. //
  1429. // Note: As this is called from Customize view no need to look
  1430. // at viewdata::IsStandardMenusAllowed.
  1431. // Also Favorites button is not added in first place
  1432. // if it is not allowed. So no need for Favorites menu
  1433. // special case.
  1434. //
  1435. //--------------------------------------------------------------------
  1436. SC CMenuBar::ScShowMMCMenus (bool bShow)
  1437. {
  1438. DECLARE_SC(sc, _T("CMenuBar::ScShowMMCMenus"));
  1439. // Go through the menu buttons & find Action, View & Favorites.
  1440. TBBUTTON btn;
  1441. int cButtons = GetButtonCount();
  1442. for (int i = 0; i < cButtons; ++i)
  1443. {
  1444. GetButton (i, &btn);
  1445. // Skip if button is not action/view/favs.
  1446. if ( (btn.idCommand != ID_MTB_MENU_FAVORITES) &&
  1447. (btn.idCommand != ID_MTB_MENU_ACTION) &&
  1448. (btn.idCommand != ID_MTB_MENU_VIEW) )
  1449. {
  1450. continue;
  1451. }
  1452. // For favorites menu see if it is appropriate to un-hide it.
  1453. // In non-author mode if there is no favorites then this menu
  1454. // is hidden.
  1455. if ( bShow &&
  1456. (btn.idCommand == ID_MTB_MENU_FAVORITES) &&
  1457. (! IsStandardMenuAllowed(ID_FAVORITES_MENU)) )
  1458. continue;
  1459. HideButton(btn.idCommand, !bShow);
  1460. }
  1461. return (sc);
  1462. }
  1463. /*+-------------------------------------------------------------------------*
  1464. * CMenuBar::ScInsertAccPropIDs
  1465. *
  1466. * Inserts the IDs of the accessibility properties supported by CMenuBar
  1467. * (see ScGetPropValue).
  1468. *--------------------------------------------------------------------------*/
  1469. SC CMenuBar::ScInsertAccPropIDs (PropIDCollection& v)
  1470. {
  1471. DECLARE_SC (sc, _T("CMenuBar::ScInsertAccPropIDs"));
  1472. /*
  1473. * let the base class do its thing
  1474. */
  1475. sc = CMMCToolBarCtrlEx::ScInsertAccPropIDs (v);
  1476. if (sc)
  1477. return (sc);
  1478. /*
  1479. * add our own properties
  1480. */
  1481. v.push_back (PROPID_ACC_ROLE);
  1482. return (sc);
  1483. }
  1484. /*+-------------------------------------------------------------------------*
  1485. * CMenuBar::ScGetPropValue
  1486. *
  1487. * Returns accessibility properties supported by CMenuBar.
  1488. *
  1489. * If a property is returned, fGotProp is set to true. If it is not
  1490. * returned, the value of fGotProp is unchanged, since the property might
  1491. * have been provided by a base/derived class.
  1492. *--------------------------------------------------------------------------*/
  1493. SC CMenuBar::ScGetPropValue (
  1494. HWND hwnd, // I:accessible window
  1495. DWORD idObject, // I:accessible object
  1496. DWORD idChild, // I:accessible child object
  1497. const MSAAPROPID& idProp, // I:property requested
  1498. VARIANT& varValue, // O:returned property value
  1499. BOOL& fGotProp) // O:was a property returned?
  1500. {
  1501. DECLARE_SC (sc, _T("CMenuBar::ScGetPropValue"));
  1502. /*
  1503. * call the base class
  1504. */
  1505. sc = CMMCToolBarCtrlEx::ScGetPropValue (hwnd, idObject, idChild, idProp, varValue, fGotProp);
  1506. if (sc)
  1507. return (sc);
  1508. /*
  1509. * now handle requests for properties we support...role first
  1510. */
  1511. if (idProp == PROPID_ACC_ROLE)
  1512. {
  1513. /*
  1514. * don't override the property for child elements,
  1515. * don't return a property
  1516. */
  1517. if (idChild != CHILDID_SELF)
  1518. {
  1519. Trace (tagToolbarAccessibility, _T("GetPropValue: no role for child %d"), idChild);
  1520. return (sc);
  1521. }
  1522. /*
  1523. * the control itself has a role of menubar
  1524. */
  1525. V_VT(&varValue) = VT_I4;
  1526. V_I4(&varValue) = ROLE_SYSTEM_MENUBAR;
  1527. fGotProp = true;
  1528. Trace (tagToolbarAccessibility, _T("GetPropValue: Returning 0x%08x"), V_I4(&varValue));
  1529. }
  1530. else if (idProp == PROPID_ACC_STATE)
  1531. {
  1532. /*
  1533. * Bug 148132: if the base class returned a property, append
  1534. * STATE_SYSTEM_HASPOPUP so Narrator et al will announce "has a
  1535. * submenu" when the menu item is highlighted
  1536. */
  1537. if (fGotProp)
  1538. {
  1539. ASSERT (V_VT(&varValue) == VT_I4);
  1540. V_I4(&varValue) |= STATE_SYSTEM_HASPOPUP;
  1541. Trace (tagToolbarAccessibility, _T("GetPropValue: Appending STATE_SYSTEM_HASPOPUP, Returning 0x%08x"), V_I4(&varValue));
  1542. }
  1543. else
  1544. {
  1545. V_VT(&varValue) = VT_I4;
  1546. V_I4(&varValue) = STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_HASPOPUP;
  1547. fGotProp = true;
  1548. if (IsTrackingToolBar() && (GetHotItem() == (idChild-1) /*0-based*/))
  1549. V_I4(&varValue) |= STATE_SYSTEM_FOCUSED | STATE_SYSTEM_HOTTRACKED;
  1550. Trace (tagToolbarAccessibility, _T("GetPropValue: Returning 0x%08x"), V_I4(&varValue));
  1551. }
  1552. }
  1553. return (sc);
  1554. }
  1555. /*+-------------------------------------------------------------------------*
  1556. * CMenuBar::BeginTracking2
  1557. *
  1558. * Fires EVENT_SYSTEM_MENUSTART event accessibility event, then call base
  1559. * class.
  1560. *--------------------------------------------------------------------------*/
  1561. void CMenuBar::BeginTracking2 (CToolbarTrackerAuxWnd* pAuxWnd)
  1562. {
  1563. NotifyWinEvent (EVENT_SYSTEM_MENUSTART, m_hWnd, OBJID_CLIENT, CHILDID_SELF);
  1564. CMMCToolBarCtrlEx::BeginTracking2 (pAuxWnd);
  1565. }
  1566. /*+-------------------------------------------------------------------------*
  1567. * CMenuBar::EndTracking2
  1568. *
  1569. * Fires EVENT_SYSTEM_MENUEND event accessibility event, then call base
  1570. * class.
  1571. *--------------------------------------------------------------------------*/
  1572. void CMenuBar::EndTracking2 (CToolbarTrackerAuxWnd* pAuxWnd)
  1573. {
  1574. NotifyWinEvent (EVENT_SYSTEM_MENUEND, m_hWnd, OBJID_CLIENT, CHILDID_SELF);
  1575. CMMCToolBarCtrlEx::EndTracking2 (pAuxWnd);
  1576. }
  1577. /////////////////////////////////////////////////////////////////////////////
  1578. /////////////////////////////////////////////////////////////////////////////
  1579. // CPopupTrackContext
  1580. /*--------------------------------------------------------------------------*
  1581. * CPopupTrackContext::CPopupTrackContext
  1582. *
  1583. *
  1584. *--------------------------------------------------------------------------*/
  1585. CPopupTrackContext::CPopupTrackContext (
  1586. CMenuBar* pMenuBar,
  1587. int nCurrentPopupIndex)
  1588. :
  1589. m_pMenuBar (pMenuBar),
  1590. m_cButtons (pMenuBar->GetButtonCount()),
  1591. m_ptLastMouseMove (GetMessagePos()),
  1592. m_ptLButtonDown (GetMessagePos()),
  1593. m_dwLButtonDownTime (GetMessageTime()),
  1594. m_bPopupMonitorHooksActive(false),
  1595. m_iRequestForNewPopup(-1)
  1596. {
  1597. ASSERT_VALID (pMenuBar);
  1598. ASSERT (s_pTrackContext == NULL);
  1599. m_nCurrentPopupIndex = nCurrentPopupIndex;
  1600. m_fCurrentlyOnPopupItem = false;
  1601. m_cCascadedPopups = 0;
  1602. ASSERT (m_nCurrentPopupIndex < m_cButtons);
  1603. ASSERT (m_nCurrentPopupIndex >= 0);
  1604. // build the vector of button boundaries
  1605. m_ButtonBoundaries.resize (m_cButtons + 1, 0);
  1606. ASSERT (m_ButtonBoundaries.size() == m_cButtons + 1);
  1607. ASSERT (m_ButtonBoundaries.size() >= 2);
  1608. CRect rectButton (0, 0, 0, 0);
  1609. POINT ptTopLeft = rectButton.TopLeft();
  1610. for (int i = 0; i < m_cButtons; i++)
  1611. {
  1612. // GetItemRect will fail (acceptably) for hidden buttons,
  1613. // but should otherwise succeed.
  1614. VERIFY (pMenuBar->GetItemRect(i, rectButton) ||
  1615. pMenuBar->IsButtonHidden(pMenuBar->IndexToCommand(i)) );
  1616. // Do not map rectButton from Client To Screen.
  1617. // Map a copy of it (in ptTopLeft). So if a hidden
  1618. // button follows, it can use the rectButton.TopLeft()
  1619. // value and map it.
  1620. ptTopLeft = rectButton.TopLeft();
  1621. pMenuBar->ClientToScreen (&ptTopLeft);
  1622. m_ButtonBoundaries[i] = ptTopLeft.x;
  1623. // make m_rectAllButtons as a union of all buttons
  1624. if (i == 0)
  1625. m_rectAllButtons = rectButton;
  1626. else
  1627. m_rectAllButtons |= rectButton;
  1628. }
  1629. ptTopLeft = rectButton.BottomRight();
  1630. pMenuBar->ClientToScreen (&ptTopLeft);
  1631. m_ButtonBoundaries[m_cButtons] = ptTopLeft.x;
  1632. pMenuBar->ClientToScreen (&m_rectAllButtons);
  1633. // decrease m_rectAllButtons slightly
  1634. m_rectAllButtons.left = m_rectAllButtons.left + 1;
  1635. m_rectAllButtons.right = m_rectAllButtons.right - 1;
  1636. #ifdef DBG
  1637. {
  1638. // the button boundaries should naturally fall in ascending(for LTR) order
  1639. for (int j = 0; j < m_ButtonBoundaries.size()-1; j++)
  1640. {
  1641. if (0 == (m_pMenuBar->GetExStyle() & WS_EX_LAYOUTRTL))
  1642. ASSERT (m_ButtonBoundaries[j] <= m_ButtonBoundaries[j+1]);
  1643. else
  1644. ASSERT (m_ButtonBoundaries[j] >= m_ButtonBoundaries[j+1]);
  1645. }
  1646. }
  1647. #endif
  1648. /*--------------------------------------------------------------------*/
  1649. /* see if we might need to simulate a double-click on the system menu */
  1650. /*--------------------------------------------------------------------*/
  1651. m_pMaxedMDIChild = NULL;
  1652. // only deal with the system menu if the MDI decorations are showing
  1653. if (m_pMenuBar->m_fDecorationsShowing)
  1654. {
  1655. ASSERT (m_pMenuBar->m_pMDIFrame != NULL);
  1656. CWnd* pMDIChild = m_pMenuBar->m_pMDIFrame->MDIGetActive();
  1657. // nothing to do if child is already gone
  1658. if ( pMDIChild == NULL )
  1659. return;
  1660. ASSERT (pMDIChild->IsZoomed());
  1661. // if the mouse is over the system menu, remember the maximized child
  1662. // (non-NULL m_pMaxedMDIChild is the key later on in MaybeCloseMDIChild)
  1663. if (HitTest (m_ptLButtonDown) == 0)
  1664. m_pMaxedMDIChild = pMDIChild;
  1665. }
  1666. }
  1667. /*--------------------------------------------------------------------------*
  1668. * CPopupTrackContext::~CPopupTrackContext
  1669. *
  1670. *
  1671. *--------------------------------------------------------------------------*/
  1672. CPopupTrackContext::~CPopupTrackContext ()
  1673. {
  1674. // release the mouse and keyboard hooks
  1675. RemovePopupMonitorHooks();
  1676. }
  1677. /*--------------------------------------------------------------------------*
  1678. * CPopupTrackContext::RemovePopupMonitorHooks
  1679. *
  1680. * Unhooks from the system and stops watching the events
  1681. *--------------------------------------------------------------------------*/
  1682. void CPopupTrackContext::RemovePopupMonitorHooks ()
  1683. {
  1684. // ignore if not monitoring yet
  1685. if (m_bPopupMonitorHooksActive)
  1686. {
  1687. // we MUST be the active monitor if we came here
  1688. if (s_pTrackContext != this)
  1689. {
  1690. ASSERT(FALSE && "Popup monitor uninstalled from outside");
  1691. return;
  1692. }
  1693. // release the mouse and keyboard hooks
  1694. UnhookWindowsHookEx (m_hhkMouse);
  1695. UnhookWindowsHookEx (m_hhkKeyboard);
  1696. UnhookWindowsHookEx (m_hhkCallWnd);
  1697. m_bPopupMonitorHooksActive = false;
  1698. // uninstall itself as hook monitor
  1699. s_pTrackContext = NULL;
  1700. }
  1701. }
  1702. /*--------------------------------------------------------------------------*
  1703. * CPopupTrackContext::SetPopupMonitorHooks
  1704. *
  1705. * Hooks into the system and begins watching the events
  1706. *--------------------------------------------------------------------------*/
  1707. void CPopupTrackContext::SetPopupMonitorHooks ()
  1708. {
  1709. // ignore if already set
  1710. if (!m_bPopupMonitorHooksActive)
  1711. {
  1712. // there is only one active menu per app. There is no place for anybody else
  1713. if (s_pTrackContext)
  1714. {
  1715. ASSERT(FALSE && "Popup menu overrun");
  1716. return;
  1717. }
  1718. // install itself as hook monitor
  1719. s_pTrackContext = this;
  1720. DWORD idCurrentThread = ::GetCurrentThreadId();
  1721. // hook the mouse for hot tracking
  1722. m_hhkMouse = SetWindowsHookEx (WH_MOUSE, MouseProc, NULL, idCurrentThread);
  1723. // hook the keyboard for navigation
  1724. m_hhkKeyboard = SetWindowsHookEx (WH_KEYBOARD, KeyboardProc, NULL, idCurrentThread);
  1725. // hook send messages for Menu_Is_Closed detection
  1726. m_hhkCallWnd = SetWindowsHookEx (WH_CALLWNDPROC, CallWndProc, NULL, idCurrentThread);
  1727. m_bPopupMonitorHooksActive = true;
  1728. }
  1729. }
  1730. /*--------------------------------------------------------------------------*
  1731. * CPopupTrackContext::StartMonitoring
  1732. *
  1733. * Public method to start popup monitoring
  1734. * Hooks into the system and begins watching the events
  1735. *--------------------------------------------------------------------------*/
  1736. void CPopupTrackContext::StartMonitoring()
  1737. {
  1738. // reset the request to activate another popup upon finish
  1739. m_iRequestForNewPopup = -1;
  1740. // setup hooks and watch...
  1741. SetPopupMonitorHooks();
  1742. }
  1743. /*--------------------------------------------------------------------------*
  1744. * CPopupTrackContext::WasAnotherPopupRequested
  1745. *
  1746. * Used to retrieve the cause the menu was dismissed.
  1747. * If new popupu is requested, button index is returned
  1748. *--------------------------------------------------------------------------*/
  1749. bool CPopupTrackContext::WasAnotherPopupRequested(int& iNewIdx)
  1750. {
  1751. if (m_iRequestForNewPopup >= 0)
  1752. {
  1753. iNewIdx = m_iRequestForNewPopup;
  1754. return true;
  1755. }
  1756. return false;
  1757. }
  1758. /*--------------------------------------------------------------------------*
  1759. * CPopupTrackContext::MouseProc
  1760. *
  1761. *
  1762. *--------------------------------------------------------------------------*/
  1763. LRESULT CALLBACK CPopupTrackContext::MouseProc (int nCode, WPARAM wParam, LPARAM lParam)
  1764. {
  1765. CPopupTrackContext* this_ = s_pTrackContext;
  1766. ASSERT (this_ != NULL);
  1767. if (nCode < 0)
  1768. return (CallNextHookEx (this_->m_hhkMouse, nCode, wParam, lParam));
  1769. return (this_->MouseProc (nCode, wParam, (LPMOUSEHOOKSTRUCT) lParam));
  1770. }
  1771. /*--------------------------------------------------------------------------*
  1772. * CPopupTrackContext::MouseProc
  1773. *
  1774. *
  1775. *--------------------------------------------------------------------------*/
  1776. LRESULT CPopupTrackContext::MouseProc (int nCode, UINT msg, LPMOUSEHOOKSTRUCT pmhs)
  1777. {
  1778. // if this is a mouse message within the menu bar...
  1779. if (m_rectAllButtons.PtInRect (pmhs->pt))
  1780. {
  1781. // eat the button down so we don't get into a TBN_DROPDOWN loop
  1782. if (msg == WM_LBUTTONDOWN)
  1783. {
  1784. DismissCurrentPopup (true);
  1785. MaybeCloseMDIChild (pmhs->pt);
  1786. return (1);
  1787. }
  1788. // for (non-duplicate) mouse moves, follow the mouse with the active menu
  1789. if ((msg == WM_MOUSEMOVE) &&
  1790. (nCode == HC_ACTION) &&
  1791. (m_ptLastMouseMove != pmhs->pt))
  1792. {
  1793. // determine which button is being tracked over
  1794. m_ptLastMouseMove = pmhs->pt;
  1795. int nNewPopupIndex = HitTest (m_ptLastMouseMove);
  1796. ASSERT (nNewPopupIndex != -1);
  1797. // if we're not over the same button we were last
  1798. // time, display the popup for the new button
  1799. if (nNewPopupIndex != m_nCurrentPopupIndex)
  1800. NotifyNewPopup (m_nCurrentPopupIndex = nNewPopupIndex);
  1801. }
  1802. }
  1803. return (CallNextHookEx (m_hhkMouse, nCode, msg, (LPARAM) pmhs));
  1804. }
  1805. /*--------------------------------------------------------------------------*
  1806. * CPopupTrackContext::KeyboardProc
  1807. *
  1808. *
  1809. *--------------------------------------------------------------------------*/
  1810. LRESULT CALLBACK CPopupTrackContext::KeyboardProc (int nCode, WPARAM wParam, LPARAM lParam)
  1811. {
  1812. CPopupTrackContext* this_ = s_pTrackContext;
  1813. ASSERT (this_ != NULL);
  1814. if (nCode < 0)
  1815. return (CallNextHookEx (this_->m_hhkKeyboard, nCode, wParam, lParam));
  1816. int cRepeat = LOWORD (lParam);
  1817. bool fDown = (lParam & (1 << 31)) == 0;
  1818. return (this_->KeyboardProc (nCode, wParam, cRepeat, fDown, lParam));
  1819. }
  1820. /*--------------------------------------------------------------------------*
  1821. * CPopupTrackContext::KeyboardProc
  1822. *
  1823. *
  1824. *--------------------------------------------------------------------------*/
  1825. LRESULT CPopupTrackContext::KeyboardProc (
  1826. int nCode,
  1827. int vkey,
  1828. int cRepeat,
  1829. bool fDown,
  1830. LPARAM lParam)
  1831. {
  1832. // if this isn't a real message, ignore it
  1833. if (nCode != HC_ACTION)
  1834. return (CallNextHookEx (m_hhkKeyboard, nCode, vkey, lParam));
  1835. // if this is a left or right message...
  1836. if ((vkey == VK_LEFT) || (vkey == VK_RIGHT))
  1837. {
  1838. // eat the key release, but don't do anything with it
  1839. if (!fDown)
  1840. return (1);
  1841. /*
  1842. * let the menu code handle cascaded popups
  1843. */
  1844. // need to do everything in opposite direction on RTL layout
  1845. // see bug #402620 ntbug9 05/23/2001
  1846. const bool fNext = ( (m_pMenuBar->GetExStyle() & WS_EX_LAYOUTRTL) ? (vkey != VK_RIGHT) : (vkey == VK_RIGHT) ) ;
  1847. if (m_fCurrentlyOnPopupItem && fNext)
  1848. m_cCascadedPopups++;
  1849. else if ((m_cCascadedPopups > 0) && !fNext)
  1850. m_cCascadedPopups--;
  1851. /*
  1852. * not right on a popup item, or left on a popped-up menu
  1853. */
  1854. else
  1855. {
  1856. m_cCascadedPopups = 0;
  1857. // figure out the next button
  1858. int nNewPopupIndex = fNext
  1859. ? m_pMenuBar->GetNextButtonIndex (m_nCurrentPopupIndex, cRepeat)
  1860. : m_pMenuBar->GetPrevButtonIndex (m_nCurrentPopupIndex, cRepeat);
  1861. // activate the new button's popup, if it's different from the current one
  1862. if (nNewPopupIndex != m_nCurrentPopupIndex)
  1863. NotifyNewPopup (m_nCurrentPopupIndex = nNewPopupIndex);
  1864. // eat the key press
  1865. return (1);
  1866. }
  1867. }
  1868. return (CallNextHookEx (m_hhkKeyboard, nCode, vkey, lParam));
  1869. }
  1870. /*--------------------------------------------------------------------------*
  1871. * CPopupTrackContext::CallWndProc
  1872. *
  1873. *
  1874. *--------------------------------------------------------------------------*/
  1875. LRESULT CALLBACK CPopupTrackContext::CallWndProc(
  1876. int nCode, // hook code
  1877. WPARAM wParam, // current-process flag
  1878. LPARAM lParam // address of structure with message data
  1879. )
  1880. {
  1881. // get the active monitor
  1882. CPopupTrackContext* this_ = s_pTrackContext;
  1883. ASSERT (this_ != NULL);
  1884. // ignore special cases
  1885. if (nCode < 0)
  1886. return (CallNextHookEx (this_->m_hhkCallWnd, nCode, wParam, lParam));
  1887. BOOL bCurrentThread = wParam;
  1888. LPCWPSTRUCT lpCWP = reinterpret_cast<LPCWPSTRUCT>(lParam);
  1889. // forward the request to monitor
  1890. return (this_->CallWndProc (nCode, bCurrentThread, lpCWP));
  1891. }
  1892. /*--------------------------------------------------------------------------*
  1893. * CPopupTrackContext::CallWndProc
  1894. *
  1895. *
  1896. *--------------------------------------------------------------------------*/
  1897. LRESULT CPopupTrackContext::CallWndProc (int nCode, BOOL bCurrentThread, LPCWPSTRUCT lpCWP)
  1898. {
  1899. ASSERT(lpCWP != NULL);
  1900. if (lpCWP)
  1901. {
  1902. // watch for message
  1903. if (lpCWP->message == WM_MENUSELECT)
  1904. {
  1905. // decode params
  1906. const UINT fuFlags = (UINT) HIWORD(lpCWP->wParam); // menu flags
  1907. const HMENU hmenu = (HMENU) lpCWP->lParam; // handle to menu clicked
  1908. if (fuFlags == 0xFFFF && hmenu == NULL)
  1909. {
  1910. // menu is closed! no more hooking needed
  1911. RemovePopupMonitorHooks ();
  1912. }
  1913. else
  1914. {
  1915. // we stepped on the popup (will use the info when arrows are pressed)
  1916. m_fCurrentlyOnPopupItem = (fuFlags & MF_POPUP);
  1917. }
  1918. }
  1919. }
  1920. // done
  1921. return (CallNextHookEx (m_hhkCallWnd, nCode, bCurrentThread, (LPARAM)lpCWP));
  1922. }
  1923. /*--------------------------------------------------------------------------*
  1924. * CPopupTrackContext::DismissCurrentPopup
  1925. *
  1926. *
  1927. *--------------------------------------------------------------------------*/
  1928. void CPopupTrackContext::DismissCurrentPopup (bool fTrackingComplete)
  1929. {
  1930. // If the snapin does TrackPopupMenu with a window other than
  1931. // MainFrame as parent then that window should be asked to
  1932. // close the menu by sending WM_CANCELMODE. The window
  1933. // is found by calling GetCapture().
  1934. CWnd* pwndMode = CWnd::GetCapture();
  1935. if (pwndMode == NULL)
  1936. pwndMode = AfxGetMainWnd();
  1937. pwndMode->SendMessage (WM_CANCELMODE);
  1938. }
  1939. /*--------------------------------------------------------------------------*
  1940. * CPopupTrackContext::NotifyNewPopup
  1941. *
  1942. * Notify the menu toolbar that it needs to display a new popup menu.
  1943. * Note that this must be accomplished asynchronously so that we can
  1944. * allow CMenuBar::PopupMenu to unwind after the WM_CANCELMODE from
  1945. * DismissCurrentPopup.
  1946. *--------------------------------------------------------------------------*/
  1947. void CPopupTrackContext::NotifyNewPopup (int nNewPopupIndex)
  1948. {
  1949. // dismiss the existing popup
  1950. DismissCurrentPopup (false);
  1951. // ask to activate the new popup after this one is closed
  1952. m_iRequestForNewPopup = nNewPopupIndex;
  1953. }
  1954. /*--------------------------------------------------------------------------*
  1955. * CPopupTrackContext::HitTest
  1956. *
  1957. * Returns the index of the button under the given point, -1 if none.
  1958. *--------------------------------------------------------------------------*/
  1959. int CPopupTrackContext::HitTest (CPoint pt) const
  1960. {
  1961. /*----------------------------------------------------------------*/
  1962. /* Find the range of "hit" buttons. The range will span more */
  1963. /* than one button only if there are hidden buttons in the range, */
  1964. /* and in that case, there will be exactly one non-hidden button */
  1965. /* in the range. */
  1966. /*----------------------------------------------------------------*/
  1967. std::pair<BoundConstIt, BoundConstIt> range;
  1968. if ( m_pMenuBar->GetExStyle() & WS_EX_LAYOUTRTL )
  1969. {
  1970. range = std::equal_range (m_ButtonBoundaries.begin(),
  1971. m_ButtonBoundaries.end(), pt.x,
  1972. std::greater<BoundaryCollection::value_type>() );
  1973. }
  1974. else
  1975. {
  1976. range = std::equal_range (m_ButtonBoundaries.begin(),
  1977. m_ButtonBoundaries.end(), pt.x);
  1978. }
  1979. int nLowerHitIndex = MapBoundaryIteratorToButtonIndex (range.first);
  1980. int nUpperHitIndex = MapBoundaryIteratorToButtonIndex (range.second);
  1981. /*
  1982. * equal_range returns values that are less_than and greater_than
  1983. * given value. The m_ButtonBoundaries has duplicate values (due
  1984. * to hidden buttons). So if the less_than value is one of duplicate
  1985. * values (not unique) then equal_range returns the iterator to
  1986. * last dup item, not first dup item.
  1987. *
  1988. * Below we try to find the first dup item.
  1989. */
  1990. // Find the first item with value m_ButtonBoundaries[nLowerHitIndex].
  1991. for (int iIndex = 0; iIndex < nLowerHitIndex; ++iIndex)
  1992. {
  1993. if (m_ButtonBoundaries[iIndex] == m_ButtonBoundaries[nLowerHitIndex])
  1994. {
  1995. // Found first item.
  1996. nLowerHitIndex = iIndex;
  1997. break;
  1998. }
  1999. }
  2000. ASSERT (nLowerHitIndex <= nUpperHitIndex);
  2001. int nHitIndex;
  2002. // lower equal upper? no hidden buttons
  2003. if (nLowerHitIndex == nUpperHitIndex)
  2004. nHitIndex = nLowerHitIndex;
  2005. // otherwise we have some hidden buttons, or we are precisely on a button border
  2006. else
  2007. {
  2008. nHitIndex = -1;
  2009. if (nUpperHitIndex == -1)
  2010. nUpperHitIndex = m_cButtons;
  2011. for (int i = nLowerHitIndex;
  2012. i <= nUpperHitIndex; // We should check till we hit nUpperHitIndex? AnandhaG
  2013. ++i)
  2014. {
  2015. // See if this button is not hidden.
  2016. if (!m_pMenuBar->IsButtonHidden (m_pMenuBar->IndexToCommand(i)))
  2017. {
  2018. nHitIndex = i;
  2019. break;
  2020. }
  2021. }
  2022. // we should have found a visible button
  2023. ASSERT (nHitIndex != -1);
  2024. }
  2025. ASSERT (nHitIndex >= -1);
  2026. ASSERT (nHitIndex < m_cButtons);
  2027. return (nHitIndex);
  2028. }
  2029. /*--------------------------------------------------------------------------*
  2030. * CPopupTrackContext::MapBoundaryIteratorToButtonIndex
  2031. *
  2032. * Returns the button index corresponding to the input m_ButtonBoundaries
  2033. * index, -1 for not found.
  2034. *--------------------------------------------------------------------------*/
  2035. int CPopupTrackContext::MapBoundaryIteratorToButtonIndex (BoundConstIt it) const
  2036. {
  2037. return ((it != m_ButtonBoundaries.end())
  2038. ? it - m_ButtonBoundaries.begin() - 1
  2039. : -1);
  2040. }
  2041. /*--------------------------------------------------------------------------*
  2042. * CPopupTrackContext::MaybeCloseMDIChild
  2043. *
  2044. *
  2045. *--------------------------------------------------------------------------*/
  2046. void CPopupTrackContext::MaybeCloseMDIChild (CPoint pt)
  2047. {
  2048. // if we didn't find a maxed MDI child when this all started, punt
  2049. if (m_pMaxedMDIChild == NULL)
  2050. return;
  2051. // if the click didn't happen on the system menu toolbar button, punt
  2052. if (HitTest (pt) != 0)
  2053. return;
  2054. /*-------------------------------------------------------------------*/
  2055. /* if the double-click time has elapsed, punt */
  2056. /* */
  2057. /* Note: this is called from a mouse hook, which means the value */
  2058. /* returned by GetMessageTime hasn't been updated for this message, */
  2059. /* so it really reflects the time of the *previous* message (most */
  2060. /* likely WM_LBUTTONUP). */
  2061. /* */
  2062. /* GetTickCount returns a close enough approximation to */
  2063. /* GetMessageTime, except when we're debugging through this routine, */
  2064. /* in which case the tick count will continue to spin (and this test */
  2065. /* will always fail) but the message time would have remained fixed. */
  2066. /*-------------------------------------------------------------------*/
  2067. // if ((GetMessageTime() - m_dwLButtonDownTime) > GetDoubleClickTime())
  2068. if ((GetTickCount() - m_dwLButtonDownTime) > GetDoubleClickTime())
  2069. return;
  2070. // if the second click occurred outside the double-click space, punt
  2071. if ((abs (m_ptLButtonDown.x - pt.x) > GetSystemMetrics (SM_CXDOUBLECLK)) ||
  2072. (abs (m_ptLButtonDown.y - pt.y) > GetSystemMetrics (SM_CYDOUBLECLK)))
  2073. return;
  2074. // if the window doesn't have a system menu, or its Close item is disabled, punt
  2075. CMenu* pSysMenu = m_pMaxedMDIChild->GetSystemMenu (FALSE);
  2076. if (pSysMenu == NULL)
  2077. return;
  2078. UINT nCloseState = pSysMenu->GetMenuState (SC_CLOSE, MF_BYCOMMAND);
  2079. if ((nCloseState == 0xFFFFFFFF) ||
  2080. (nCloseState & (MF_GRAYED | MF_DISABLED)))
  2081. return;
  2082. // here: we've identified a double-click on a maximized child's
  2083. // system menu; close it
  2084. m_pMaxedMDIChild->PostMessage (WM_SYSCOMMAND, SC_CLOSE);
  2085. }