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.

2605 lines
82 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)CharLower((LPTSTR)chMnemonic);
  111. TCHAR chUpper = (TCHAR)CharUpper((LPTSTR)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)CharLower((LPTSTR)chMnemonic);
  481. m_strAccelerators += (TCHAR)CharUpper((LPTSTR)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(), (TCHAR)CharUpper((LPTSTR)chMnemonicOld));
  620. tstrAccels.erase(itNewEnd, tstrAccels.end());
  621. itNewEnd = std::remove(tstrAccels.begin(), tstrAccels.end(), (TCHAR)CharLower((LPTSTR)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. // Execution of some commands may cause the child window to close
  1136. // Check if it still exists (RAID# 755824)
  1137. if (pChildFrame && IsWindow(pChildFrame->m_hWnd))
  1138. {
  1139. // Additional check to guard against HWND re-use
  1140. if (m_pMDIFrame && m_pMDIFrame->IsChild(pChildFrame))
  1141. sc = pChildFrame->ScSetStatusText(TEXT(""));
  1142. }
  1143. if (sc)
  1144. sc.TraceAndClear();
  1145. PressButton (btn.idCommand, FALSE);
  1146. SetHotItem (-1);
  1147. // the loop will continue if it was requested to display the new popup menu.
  1148. // if it was requested to simply close the menu, execution will exit the loop
  1149. }while ( popupMonitor.WasAnotherPopupRequested(nItemIndex) );
  1150. m_bInProgressDisplayingPopup = false;
  1151. //reset the UI by hiding the accelerators (since we are done)
  1152. SendMessage( WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEACCEL | UISF_HIDEFOCUS));
  1153. EndTracking();
  1154. }
  1155. /*--------------------------------------------------------------------------*
  1156. * CMenuBar::OnAccelPopup
  1157. *
  1158. * Keyboard accelerator handler for CMenuBar.
  1159. *--------------------------------------------------------------------------*/
  1160. void CMenuBar::OnAccelPopup (UINT cmd)
  1161. {
  1162. PopupMenu (CommandToIndex (cmd), true /*bHighlightFirstItem*/);
  1163. }
  1164. /*--------------------------------------------------------------------------*
  1165. * void CMenuBar::OnUpdateAllCmdUI
  1166. *
  1167. *
  1168. *--------------------------------------------------------------------------*/
  1169. void CMenuBar::OnUpdateAllCmdUI (CCmdUI* pCmdUI)
  1170. {
  1171. pCmdUI->Enable ();
  1172. }
  1173. /*--------------------------------------------------------------------------*
  1174. * CMenuBar::AddString
  1175. *
  1176. * The toolbar control doesn't provide a way to delete strings once they've
  1177. * been added, so we'll check our cache of strings that we've already added
  1178. * to the toolbar so we don't add wasteful duplicate strings.
  1179. *--------------------------------------------------------------------------*/
  1180. int CMenuBar::AddString (const CString& strAdd)
  1181. {
  1182. DECLARE_SC(sc, TEXT("CMenuBar::AddString"));
  1183. // -1 for empty strings
  1184. if (strAdd.IsEmpty())
  1185. return (-1);
  1186. // check the cache
  1187. ToolbarStringPool::iterator it = std::find (m_ToolbarStringPool.begin(),
  1188. m_ToolbarStringPool.end(),
  1189. strAdd);
  1190. // if we found a hit in the cache, return the cached index
  1191. if (it != m_ToolbarStringPool.end())
  1192. return (it - m_ToolbarStringPool.begin());
  1193. // new string, add it to the cache...
  1194. m_ToolbarStringPool.push_back (strAdd);
  1195. // ...and to the toolbar, including a double-NULL
  1196. int cchAdd = strAdd.GetLength() + 1;
  1197. LPTSTR pszAdd = (LPTSTR) _alloca ((cchAdd + 1) * sizeof (TCHAR));
  1198. sc = StringCchCopy(pszAdd, cchAdd, strAdd);
  1199. if (sc)
  1200. return -1;
  1201. // And the second NULL to terminate strings...
  1202. pszAdd[cchAdd] = 0;
  1203. int nIndex = AddStrings (pszAdd);
  1204. // make sure the toolbar's string index matches the cache's string index
  1205. ASSERT (nIndex == m_ToolbarStringPool.end()-m_ToolbarStringPool.begin()-1);
  1206. return (nIndex);
  1207. }
  1208. /*--------------------------------------------------------------------------*
  1209. * CMenuBar::GetMenuBandIndex
  1210. *
  1211. *
  1212. *--------------------------------------------------------------------------*/
  1213. int CMenuBar::GetMenuBandIndex () const
  1214. {
  1215. return (m_pRebar->IdToIndex (GetDlgCtrlID ()));
  1216. }
  1217. /*--------------------------------------------------------------------------*
  1218. * CMenuBar::GetDecorationBandIndex
  1219. *
  1220. *
  1221. *--------------------------------------------------------------------------*/
  1222. int CMenuBar::GetDecorationBandIndex () const
  1223. {
  1224. return (m_pRebar->IdToIndex (ID_MDIDECORATION));
  1225. }
  1226. /*--------------------------------------------------------------------------*
  1227. * CMenuBar::GetFirstButtonIndex
  1228. *
  1229. *
  1230. *--------------------------------------------------------------------------*/
  1231. int CMenuBar::GetFirstButtonIndex ()
  1232. {
  1233. // make sure the system menu isn't the first one activated
  1234. return (GetNextButtonIndex (0));
  1235. }
  1236. /*--------------------------------------------------------------------------*
  1237. * CMenuBar::OnIdle
  1238. *
  1239. * WM_IDLE handler for CMenuBar.
  1240. *--------------------------------------------------------------------------*/
  1241. void CMenuBar::OnIdle ()
  1242. {
  1243. /*----------------------------------------------------------*/
  1244. /* If there's no MDI frame, that means this menu is serving */
  1245. /* an SDI window. We don't have to do any special stuff to */
  1246. /* simulate the MDI menu UI, so bail now. */
  1247. /*----------------------------------------------------------*/
  1248. if (m_pMDIFrame == NULL)
  1249. return;
  1250. ProgramMode eMode = AMCGetApp()->GetMode();
  1251. // if we're in SDI User mode, bail now as well
  1252. if (eMode == eMode_User_SDI)
  1253. {
  1254. #ifdef DBG
  1255. // the decorations should be hidden
  1256. REBARBANDINFO rbi;
  1257. PrepBandInfo (&rbi, RBBIM_STYLE);
  1258. m_pRebar->GetBandInfo (GetDecorationBandIndex(), &rbi);
  1259. ASSERT (rbi.fStyle & RBBS_HIDDEN);
  1260. #endif
  1261. return;
  1262. }
  1263. /*---------------------------------------------------------------*/
  1264. /* We should be able to use MDIGetActive(&fMaximized) to tell */
  1265. /* whether the active window is maximized, instead of calling */
  1266. /* pwndActive->IsZoomed(). However, fMaximized doesn't always */
  1267. /* get initialized correctly in certain low memory/slow machine */
  1268. /* situations. This was the cause of Bug 133179, logged by SQL. */
  1269. /*---------------------------------------------------------------*/
  1270. CMDIChildWnd* pwndActive = m_pMDIFrame->MDIGetActive ();
  1271. bool fShow = (pwndActive != NULL) && pwndActive->IsZoomed();
  1272. REBARBANDINFO rbi;
  1273. PrepBandInfo (&rbi, RBBIM_STYLE);
  1274. m_pRebar->GetBandInfo (GetMenuBandIndex(), &rbi);
  1275. // if the menu bar is hidden, the decorations must be hidden as well
  1276. if (rbi.fStyle & RBBS_HIDDEN)
  1277. fShow = false;
  1278. if (fShow != m_fDecorationsShowing)
  1279. {
  1280. // show/hide the MDI decorations
  1281. m_pRebar->ShowBand (GetDecorationBandIndex(), fShow);
  1282. GetMaxedChildIcon (pwndActive);
  1283. HideButton (ID_MTB_MENU_SYSMENU, !fShow);
  1284. UpdateToolbarSize ();
  1285. // remember the new setting
  1286. m_fDecorationsShowing = fShow;
  1287. }
  1288. // otherwise, see if a window was maximized before but a different one is maxed now
  1289. else if (fShow && (m_pwndLastActive != pwndActive))
  1290. {
  1291. // get the new active window's icon
  1292. GetMaxedChildIcon (pwndActive);
  1293. // repaint the menu and MDI decorations
  1294. InvalidateRect (NULL);
  1295. m_pMDIDec->InvalidateRect (NULL);
  1296. }
  1297. // remember the currently active window
  1298. m_pwndLastActive = pwndActive;
  1299. // insure that it's safe to keep this pointer around
  1300. ASSERT ((m_pwndLastActive == NULL) ||
  1301. (CWnd::FromHandlePermanent (m_pwndLastActive->m_hWnd) != NULL));
  1302. }
  1303. /*--------------------------------------------------------------------------*
  1304. * CMenuBar::DeleteMaxedChildIcon
  1305. *
  1306. *
  1307. *--------------------------------------------------------------------------*/
  1308. void
  1309. CMenuBar::DeleteMaxedChildIcon()
  1310. {
  1311. // destroy the previous icon, if we need to
  1312. if (m_fDestroyChildIcon)
  1313. {
  1314. ASSERT (m_hMaxedChildIcon != NULL);
  1315. DestroyIcon (m_hMaxedChildIcon);
  1316. m_hMaxedChildIcon = NULL;
  1317. m_fDestroyChildIcon = false;
  1318. }
  1319. }
  1320. /*--------------------------------------------------------------------------*
  1321. * CMenuBar::GetMaxedChildIcon
  1322. *
  1323. *
  1324. *--------------------------------------------------------------------------*/
  1325. void CMenuBar::GetMaxedChildIcon (CWnd* pwnd)
  1326. {
  1327. DeleteMaxedChildIcon();
  1328. // get the small icon for the given window
  1329. if (IsWindow (pwnd->GetSafeHwnd()))
  1330. {
  1331. HICON hOriginalIcon = pwnd->GetIcon (false /*bBigIcon*/);
  1332. m_hMaxedChildIcon = (HICON) CopyImage (
  1333. hOriginalIcon, IMAGE_ICON,
  1334. GetSystemMetrics (SM_CXSMICON),
  1335. GetSystemMetrics (SM_CYSMICON),
  1336. LR_COPYFROMRESOURCE | LR_COPYRETURNORG);
  1337. // if the system had to create a new icon, we'll have to destroy it later
  1338. if ((m_hMaxedChildIcon != NULL) && (m_hMaxedChildIcon != hOriginalIcon))
  1339. m_fDestroyChildIcon = true;
  1340. }
  1341. m_fMaxedChildIconIsInvalid = false;
  1342. }
  1343. /*+-------------------------------------------------------------------------*
  1344. * CMenuBar::GetAccelerators
  1345. *
  1346. *
  1347. *--------------------------------------------------------------------------*/
  1348. void CMenuBar::GetAccelerators (int cchBuffer, LPTSTR lpBuffer) const
  1349. {
  1350. lstrcpyn (lpBuffer, m_strAccelerators, cchBuffer);
  1351. }
  1352. //+-------------------------------------------------------------------
  1353. //
  1354. // Member: CMenuBar::IsStandardMenuAllowed
  1355. //
  1356. // Synopsis: Is this standard MMC menu allowed or not.
  1357. //
  1358. // Arguments: uMenuID - The menu ID.
  1359. //
  1360. // Returns: bool
  1361. //
  1362. //--------------------------------------------------------------------
  1363. bool CMenuBar::IsStandardMenuAllowed(UINT uMenuID)
  1364. {
  1365. DECLARE_SC(sc, _T("CMenuBar::IsStandardMenuAllowed"));
  1366. /*
  1367. * We add a hidden menu as a marker. Later when snapin
  1368. * calls to insert a menu button we find the position
  1369. * of this menu and add snapin menu before it.
  1370. * So this acts as Std menu which is always allowed.
  1371. */
  1372. if (uMenuID == ID_SNAPIN_MENU_PLACEHOLDER)
  1373. return true;
  1374. // First make sure it is one of the std menus.
  1375. if ( (uMenuID != ID_FAVORITES_MENU) &&
  1376. (uMenuID != ID_ACTION_MENU) &&
  1377. (uMenuID != ID_VIEW_MENU))
  1378. {
  1379. sc = E_INVALIDARG;
  1380. return true;
  1381. }
  1382. // Ask view data if std menus are allowed.
  1383. CMainFrame* pMainFrame = AMCGetMainWnd();
  1384. sc = ScCheckPointers(pMainFrame, E_UNEXPECTED);
  1385. if (sc)
  1386. return false;
  1387. CAMCView *pAMCView = pMainFrame->GetActiveAMCView();
  1388. sc = ScCheckPointers(pAMCView, E_UNEXPECTED);
  1389. if (sc)
  1390. return false;
  1391. SViewData* pViewData = pAMCView->GetViewData();
  1392. sc = ScCheckPointers(pViewData, E_UNEXPECTED);
  1393. if (sc)
  1394. return false;
  1395. if (! pViewData->IsStandardMenusAllowed())
  1396. return false;
  1397. if (uMenuID != ID_FAVORITES_MENU)
  1398. return true;
  1399. /*
  1400. * Display the Favorites menu button if we are in author mode, or if
  1401. * we're in user mode and we have at least one favorite. If we're in
  1402. * user mode and no favorites are defined, hide the Favorites button.
  1403. */
  1404. bool fShowFavorites = true;
  1405. CAMCApp* pApp = AMCGetApp();
  1406. if (pApp != NULL)
  1407. {
  1408. /*
  1409. * show favorites in author mode
  1410. */
  1411. fShowFavorites = (pApp->GetMode() == eMode_Author);
  1412. /*
  1413. * not author mode? see if we have any favorites
  1414. */
  1415. if (!fShowFavorites)
  1416. {
  1417. CAMCDoc* pDoc = CAMCDoc::GetDocument ();
  1418. if (pDoc != NULL)
  1419. {
  1420. CFavorites* pFavorites = pDoc->GetFavorites();
  1421. if (pFavorites != NULL)
  1422. fShowFavorites = !pFavorites->IsEmpty();
  1423. }
  1424. }
  1425. }
  1426. return fShowFavorites;
  1427. }
  1428. //+-------------------------------------------------------------------
  1429. //
  1430. // Member: CMenuBar::ScShowMMCMenus
  1431. //
  1432. // Synopsis: Show or Hide the MMC menus (Action, View & Favorites).
  1433. // Called from customize view.
  1434. //
  1435. // Arguments:
  1436. //
  1437. // Returns: SC
  1438. //
  1439. // Note: As this is called from Customize view no need to look
  1440. // at viewdata::IsStandardMenusAllowed.
  1441. // Also Favorites button is not added in first place
  1442. // if it is not allowed. So no need for Favorites menu
  1443. // special case.
  1444. //
  1445. //--------------------------------------------------------------------
  1446. SC CMenuBar::ScShowMMCMenus (bool bShow)
  1447. {
  1448. DECLARE_SC(sc, _T("CMenuBar::ScShowMMCMenus"));
  1449. // Go through the menu buttons & find Action, View & Favorites.
  1450. TBBUTTON btn;
  1451. int cButtons = GetButtonCount();
  1452. for (int i = 0; i < cButtons; ++i)
  1453. {
  1454. GetButton (i, &btn);
  1455. // Skip if button is not action/view/favs.
  1456. if ( (btn.idCommand != ID_MTB_MENU_FAVORITES) &&
  1457. (btn.idCommand != ID_MTB_MENU_ACTION) &&
  1458. (btn.idCommand != ID_MTB_MENU_VIEW) )
  1459. {
  1460. continue;
  1461. }
  1462. // For favorites menu see if it is appropriate to un-hide it.
  1463. // In non-author mode if there is no favorites then this menu
  1464. // is hidden.
  1465. if ( bShow &&
  1466. (btn.idCommand == ID_MTB_MENU_FAVORITES) &&
  1467. (! IsStandardMenuAllowed(ID_FAVORITES_MENU)) )
  1468. continue;
  1469. HideButton(btn.idCommand, !bShow);
  1470. }
  1471. return (sc);
  1472. }
  1473. /*+-------------------------------------------------------------------------*
  1474. * CMenuBar::ScInsertAccPropIDs
  1475. *
  1476. * Inserts the IDs of the accessibility properties supported by CMenuBar
  1477. * (see ScGetPropValue).
  1478. *--------------------------------------------------------------------------*/
  1479. SC CMenuBar::ScInsertAccPropIDs (PropIDCollection& v)
  1480. {
  1481. DECLARE_SC (sc, _T("CMenuBar::ScInsertAccPropIDs"));
  1482. /*
  1483. * let the base class do its thing
  1484. */
  1485. sc = CMMCToolBarCtrlEx::ScInsertAccPropIDs (v);
  1486. if (sc)
  1487. return (sc);
  1488. /*
  1489. * add our own properties
  1490. */
  1491. v.push_back (PROPID_ACC_ROLE);
  1492. return (sc);
  1493. }
  1494. /*+-------------------------------------------------------------------------*
  1495. * CMenuBar::ScGetPropValue
  1496. *
  1497. * Returns accessibility properties supported by CMenuBar.
  1498. *
  1499. * If a property is returned, fGotProp is set to true. If it is not
  1500. * returned, the value of fGotProp is unchanged, since the property might
  1501. * have been provided by a base/derived class.
  1502. *--------------------------------------------------------------------------*/
  1503. SC CMenuBar::ScGetPropValue (
  1504. HWND hwnd, // I:accessible window
  1505. DWORD idObject, // I:accessible object
  1506. DWORD idChild, // I:accessible child object
  1507. const MSAAPROPID& idProp, // I:property requested
  1508. VARIANT& varValue, // O:returned property value
  1509. BOOL& fGotProp) // O:was a property returned?
  1510. {
  1511. DECLARE_SC (sc, _T("CMenuBar::ScGetPropValue"));
  1512. /*
  1513. * call the base class
  1514. */
  1515. sc = CMMCToolBarCtrlEx::ScGetPropValue (hwnd, idObject, idChild, idProp, varValue, fGotProp);
  1516. if (sc)
  1517. return (sc);
  1518. /*
  1519. * now handle requests for properties we support...role first
  1520. */
  1521. if (idProp == PROPID_ACC_ROLE)
  1522. {
  1523. /*
  1524. * don't override the property for child elements,
  1525. * don't return a property
  1526. */
  1527. if (idChild != CHILDID_SELF)
  1528. {
  1529. Trace (tagToolbarAccessibility, _T("GetPropValue: no role for child %d"), idChild);
  1530. return (sc);
  1531. }
  1532. /*
  1533. * the control itself has a role of menubar
  1534. */
  1535. V_VT(&varValue) = VT_I4;
  1536. V_I4(&varValue) = ROLE_SYSTEM_MENUBAR;
  1537. fGotProp = true;
  1538. Trace (tagToolbarAccessibility, _T("GetPropValue: Returning 0x%08x"), V_I4(&varValue));
  1539. }
  1540. else if (idProp == PROPID_ACC_STATE)
  1541. {
  1542. /*
  1543. * Bug 148132: if the base class returned a property, append
  1544. * STATE_SYSTEM_HASPOPUP so Narrator et al will announce "has a
  1545. * submenu" when the menu item is highlighted
  1546. */
  1547. if (fGotProp)
  1548. {
  1549. ASSERT (V_VT(&varValue) == VT_I4);
  1550. V_I4(&varValue) |= STATE_SYSTEM_HASPOPUP;
  1551. Trace (tagToolbarAccessibility, _T("GetPropValue: Appending STATE_SYSTEM_HASPOPUP, Returning 0x%08x"), V_I4(&varValue));
  1552. }
  1553. else
  1554. {
  1555. V_VT(&varValue) = VT_I4;
  1556. V_I4(&varValue) = STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_HASPOPUP;
  1557. fGotProp = true;
  1558. if (IsTrackingToolBar() && (GetHotItem() == (idChild-1) /*0-based*/))
  1559. V_I4(&varValue) |= STATE_SYSTEM_FOCUSED | STATE_SYSTEM_HOTTRACKED;
  1560. Trace (tagToolbarAccessibility, _T("GetPropValue: Returning 0x%08x"), V_I4(&varValue));
  1561. }
  1562. }
  1563. return (sc);
  1564. }
  1565. /*+-------------------------------------------------------------------------*
  1566. * CMenuBar::BeginTracking2
  1567. *
  1568. * Fires EVENT_SYSTEM_MENUSTART event accessibility event, then call base
  1569. * class.
  1570. *--------------------------------------------------------------------------*/
  1571. void CMenuBar::BeginTracking2 (CToolbarTrackerAuxWnd* pAuxWnd)
  1572. {
  1573. NotifyWinEvent (EVENT_SYSTEM_MENUSTART, m_hWnd, OBJID_CLIENT, CHILDID_SELF);
  1574. CMMCToolBarCtrlEx::BeginTracking2 (pAuxWnd);
  1575. }
  1576. /*+-------------------------------------------------------------------------*
  1577. * CMenuBar::EndTracking2
  1578. *
  1579. * Fires EVENT_SYSTEM_MENUEND event accessibility event, then call base
  1580. * class.
  1581. *--------------------------------------------------------------------------*/
  1582. void CMenuBar::EndTracking2 (CToolbarTrackerAuxWnd* pAuxWnd)
  1583. {
  1584. NotifyWinEvent (EVENT_SYSTEM_MENUEND, m_hWnd, OBJID_CLIENT, CHILDID_SELF);
  1585. CMMCToolBarCtrlEx::EndTracking2 (pAuxWnd);
  1586. }
  1587. /////////////////////////////////////////////////////////////////////////////
  1588. /////////////////////////////////////////////////////////////////////////////
  1589. // CPopupTrackContext
  1590. /*--------------------------------------------------------------------------*
  1591. * CPopupTrackContext::CPopupTrackContext
  1592. *
  1593. *
  1594. *--------------------------------------------------------------------------*/
  1595. CPopupTrackContext::CPopupTrackContext (
  1596. CMenuBar* pMenuBar,
  1597. int nCurrentPopupIndex)
  1598. :
  1599. m_pMenuBar (pMenuBar),
  1600. m_cButtons (pMenuBar->GetButtonCount()),
  1601. m_ptLastMouseMove (GetMessagePos()),
  1602. m_ptLButtonDown (GetMessagePos()),
  1603. m_dwLButtonDownTime (GetMessageTime()),
  1604. m_bPopupMonitorHooksActive(false),
  1605. m_iRequestForNewPopup(-1)
  1606. {
  1607. ASSERT_VALID (pMenuBar);
  1608. ASSERT (s_pTrackContext == NULL);
  1609. m_nCurrentPopupIndex = nCurrentPopupIndex;
  1610. m_fCurrentlyOnPopupItem = false;
  1611. m_cCascadedPopups = 0;
  1612. ASSERT (m_nCurrentPopupIndex < m_cButtons);
  1613. ASSERT (m_nCurrentPopupIndex >= 0);
  1614. // build the vector of button boundaries
  1615. m_ButtonBoundaries.resize (m_cButtons + 1, 0);
  1616. ASSERT (m_ButtonBoundaries.size() == m_cButtons + 1);
  1617. ASSERT (m_ButtonBoundaries.size() >= 2);
  1618. CRect rectButton (0, 0, 0, 0);
  1619. POINT ptTopLeft = rectButton.TopLeft();
  1620. for (int i = 0; i < m_cButtons; i++)
  1621. {
  1622. // GetItemRect will fail (acceptably) for hidden buttons,
  1623. // but should otherwise succeed.
  1624. VERIFY (pMenuBar->GetItemRect(i, rectButton) ||
  1625. pMenuBar->IsButtonHidden(pMenuBar->IndexToCommand(i)) );
  1626. // Do not map rectButton from Client To Screen.
  1627. // Map a copy of it (in ptTopLeft). So if a hidden
  1628. // button follows, it can use the rectButton.TopLeft()
  1629. // value and map it.
  1630. ptTopLeft = rectButton.TopLeft();
  1631. pMenuBar->ClientToScreen (&ptTopLeft);
  1632. m_ButtonBoundaries[i] = ptTopLeft.x;
  1633. // make m_rectAllButtons as a union of all buttons
  1634. if (i == 0)
  1635. m_rectAllButtons = rectButton;
  1636. else
  1637. m_rectAllButtons |= rectButton;
  1638. }
  1639. ptTopLeft = rectButton.BottomRight();
  1640. pMenuBar->ClientToScreen (&ptTopLeft);
  1641. m_ButtonBoundaries[m_cButtons] = ptTopLeft.x;
  1642. pMenuBar->ClientToScreen (&m_rectAllButtons);
  1643. // decrease m_rectAllButtons slightly
  1644. m_rectAllButtons.left = m_rectAllButtons.left + 1;
  1645. m_rectAllButtons.right = m_rectAllButtons.right - 1;
  1646. #ifdef DBG
  1647. {
  1648. // the button boundaries should naturally fall in ascending(for LTR) order
  1649. for (int j = 0; j < m_ButtonBoundaries.size()-1; j++)
  1650. {
  1651. if (0 == (m_pMenuBar->GetExStyle() & WS_EX_LAYOUTRTL))
  1652. ASSERT (m_ButtonBoundaries[j] <= m_ButtonBoundaries[j+1]);
  1653. else
  1654. ASSERT (m_ButtonBoundaries[j] >= m_ButtonBoundaries[j+1]);
  1655. }
  1656. }
  1657. #endif
  1658. /*--------------------------------------------------------------------*/
  1659. /* see if we might need to simulate a double-click on the system menu */
  1660. /*--------------------------------------------------------------------*/
  1661. m_pMaxedMDIChild = NULL;
  1662. // only deal with the system menu if the MDI decorations are showing
  1663. if (m_pMenuBar->m_fDecorationsShowing)
  1664. {
  1665. ASSERT (m_pMenuBar->m_pMDIFrame != NULL);
  1666. CWnd* pMDIChild = m_pMenuBar->m_pMDIFrame->MDIGetActive();
  1667. // nothing to do if child is already gone
  1668. if ( pMDIChild == NULL )
  1669. return;
  1670. ASSERT (pMDIChild->IsZoomed());
  1671. // if the mouse is over the system menu, remember the maximized child
  1672. // (non-NULL m_pMaxedMDIChild is the key later on in MaybeCloseMDIChild)
  1673. if (HitTest (m_ptLButtonDown) == 0)
  1674. m_pMaxedMDIChild = pMDIChild;
  1675. }
  1676. }
  1677. /*--------------------------------------------------------------------------*
  1678. * CPopupTrackContext::~CPopupTrackContext
  1679. *
  1680. *
  1681. *--------------------------------------------------------------------------*/
  1682. CPopupTrackContext::~CPopupTrackContext ()
  1683. {
  1684. // release the mouse and keyboard hooks
  1685. RemovePopupMonitorHooks();
  1686. }
  1687. /*--------------------------------------------------------------------------*
  1688. * CPopupTrackContext::RemovePopupMonitorHooks
  1689. *
  1690. * Unhooks from the system and stops watching the events
  1691. *--------------------------------------------------------------------------*/
  1692. void CPopupTrackContext::RemovePopupMonitorHooks ()
  1693. {
  1694. // ignore if not monitoring yet
  1695. if (m_bPopupMonitorHooksActive)
  1696. {
  1697. // we MUST be the active monitor if we came here
  1698. if (s_pTrackContext != this)
  1699. {
  1700. ASSERT(FALSE && "Popup monitor uninstalled from outside");
  1701. return;
  1702. }
  1703. // release the mouse and keyboard hooks
  1704. UnhookWindowsHookEx (m_hhkMouse);
  1705. UnhookWindowsHookEx (m_hhkKeyboard);
  1706. UnhookWindowsHookEx (m_hhkCallWnd);
  1707. m_bPopupMonitorHooksActive = false;
  1708. // uninstall itself as hook monitor
  1709. s_pTrackContext = NULL;
  1710. }
  1711. }
  1712. /*--------------------------------------------------------------------------*
  1713. * CPopupTrackContext::SetPopupMonitorHooks
  1714. *
  1715. * Hooks into the system and begins watching the events
  1716. *--------------------------------------------------------------------------*/
  1717. void CPopupTrackContext::SetPopupMonitorHooks ()
  1718. {
  1719. // ignore if already set
  1720. if (!m_bPopupMonitorHooksActive)
  1721. {
  1722. // there is only one active menu per app. There is no place for anybody else
  1723. if (s_pTrackContext)
  1724. {
  1725. ASSERT(FALSE && "Popup menu overrun");
  1726. return;
  1727. }
  1728. // install itself as hook monitor
  1729. s_pTrackContext = this;
  1730. DWORD idCurrentThread = ::GetCurrentThreadId();
  1731. // hook the mouse for hot tracking
  1732. m_hhkMouse = SetWindowsHookEx (WH_MOUSE, MouseProc, NULL, idCurrentThread);
  1733. // hook the keyboard for navigation
  1734. m_hhkKeyboard = SetWindowsHookEx (WH_KEYBOARD, KeyboardProc, NULL, idCurrentThread);
  1735. // hook send messages for Menu_Is_Closed detection
  1736. m_hhkCallWnd = SetWindowsHookEx (WH_CALLWNDPROC, CallWndProc, NULL, idCurrentThread);
  1737. m_bPopupMonitorHooksActive = true;
  1738. }
  1739. }
  1740. /*--------------------------------------------------------------------------*
  1741. * CPopupTrackContext::StartMonitoring
  1742. *
  1743. * Public method to start popup monitoring
  1744. * Hooks into the system and begins watching the events
  1745. *--------------------------------------------------------------------------*/
  1746. void CPopupTrackContext::StartMonitoring()
  1747. {
  1748. // reset the request to activate another popup upon finish
  1749. m_iRequestForNewPopup = -1;
  1750. // setup hooks and watch...
  1751. SetPopupMonitorHooks();
  1752. }
  1753. /*--------------------------------------------------------------------------*
  1754. * CPopupTrackContext::WasAnotherPopupRequested
  1755. *
  1756. * Used to retrieve the cause the menu was dismissed.
  1757. * If new popupu is requested, button index is returned
  1758. *--------------------------------------------------------------------------*/
  1759. bool CPopupTrackContext::WasAnotherPopupRequested(int& iNewIdx)
  1760. {
  1761. if (m_iRequestForNewPopup >= 0)
  1762. {
  1763. iNewIdx = m_iRequestForNewPopup;
  1764. return true;
  1765. }
  1766. return false;
  1767. }
  1768. /*--------------------------------------------------------------------------*
  1769. * CPopupTrackContext::MouseProc
  1770. *
  1771. *
  1772. *--------------------------------------------------------------------------*/
  1773. LRESULT CALLBACK CPopupTrackContext::MouseProc (int nCode, WPARAM wParam, LPARAM lParam)
  1774. {
  1775. CPopupTrackContext* this_ = s_pTrackContext;
  1776. ASSERT (this_ != NULL);
  1777. if (nCode < 0)
  1778. return (CallNextHookEx (this_->m_hhkMouse, nCode, wParam, lParam));
  1779. return (this_->MouseProc (nCode, wParam, (LPMOUSEHOOKSTRUCT) lParam));
  1780. }
  1781. /*--------------------------------------------------------------------------*
  1782. * CPopupTrackContext::MouseProc
  1783. *
  1784. *
  1785. *--------------------------------------------------------------------------*/
  1786. LRESULT CPopupTrackContext::MouseProc (int nCode, UINT msg, LPMOUSEHOOKSTRUCT pmhs)
  1787. {
  1788. // if this is a mouse message within the menu bar...
  1789. if (m_rectAllButtons.PtInRect (pmhs->pt))
  1790. {
  1791. // eat the button down so we don't get into a TBN_DROPDOWN loop
  1792. if (msg == WM_LBUTTONDOWN)
  1793. {
  1794. DismissCurrentPopup (true);
  1795. MaybeCloseMDIChild (pmhs->pt);
  1796. return (1);
  1797. }
  1798. // for (non-duplicate) mouse moves, follow the mouse with the active menu
  1799. if ((msg == WM_MOUSEMOVE) &&
  1800. (nCode == HC_ACTION) &&
  1801. (m_ptLastMouseMove != pmhs->pt))
  1802. {
  1803. // determine which button is being tracked over
  1804. m_ptLastMouseMove = pmhs->pt;
  1805. int nNewPopupIndex = HitTest (m_ptLastMouseMove);
  1806. ASSERT (nNewPopupIndex != -1);
  1807. // if we're not over the same button we were last
  1808. // time, display the popup for the new button
  1809. if (nNewPopupIndex != m_nCurrentPopupIndex)
  1810. NotifyNewPopup (m_nCurrentPopupIndex = nNewPopupIndex);
  1811. }
  1812. }
  1813. return (CallNextHookEx (m_hhkMouse, nCode, msg, (LPARAM) pmhs));
  1814. }
  1815. /*--------------------------------------------------------------------------*
  1816. * CPopupTrackContext::KeyboardProc
  1817. *
  1818. *
  1819. *--------------------------------------------------------------------------*/
  1820. LRESULT CALLBACK CPopupTrackContext::KeyboardProc (int nCode, WPARAM wParam, LPARAM lParam)
  1821. {
  1822. CPopupTrackContext* this_ = s_pTrackContext;
  1823. ASSERT (this_ != NULL);
  1824. if (nCode < 0)
  1825. return (CallNextHookEx (this_->m_hhkKeyboard, nCode, wParam, lParam));
  1826. int cRepeat = LOWORD (lParam);
  1827. bool fDown = (lParam & (1 << 31)) == 0;
  1828. return (this_->KeyboardProc (nCode, wParam, cRepeat, fDown, lParam));
  1829. }
  1830. /*--------------------------------------------------------------------------*
  1831. * CPopupTrackContext::KeyboardProc
  1832. *
  1833. *
  1834. *--------------------------------------------------------------------------*/
  1835. LRESULT CPopupTrackContext::KeyboardProc (
  1836. int nCode,
  1837. int vkey,
  1838. int cRepeat,
  1839. bool fDown,
  1840. LPARAM lParam)
  1841. {
  1842. // if this isn't a real message, ignore it
  1843. if (nCode != HC_ACTION)
  1844. return (CallNextHookEx (m_hhkKeyboard, nCode, vkey, lParam));
  1845. // if this is a left or right message...
  1846. if ((vkey == VK_LEFT) || (vkey == VK_RIGHT))
  1847. {
  1848. // eat the key release, but don't do anything with it
  1849. if (!fDown)
  1850. return (1);
  1851. /*
  1852. * let the menu code handle cascaded popups
  1853. */
  1854. // need to do everything in opposite direction on RTL layout
  1855. // see bug #402620 ntbug9 05/23/2001
  1856. const bool fNext = ( (m_pMenuBar->GetExStyle() & WS_EX_LAYOUTRTL) ? (vkey != VK_RIGHT) : (vkey == VK_RIGHT) ) ;
  1857. if (m_fCurrentlyOnPopupItem && fNext)
  1858. m_cCascadedPopups++;
  1859. else if ((m_cCascadedPopups > 0) && !fNext)
  1860. m_cCascadedPopups--;
  1861. /*
  1862. * not right on a popup item, or left on a popped-up menu
  1863. */
  1864. else
  1865. {
  1866. m_cCascadedPopups = 0;
  1867. // figure out the next button
  1868. int nNewPopupIndex = fNext
  1869. ? m_pMenuBar->GetNextButtonIndex (m_nCurrentPopupIndex, cRepeat)
  1870. : m_pMenuBar->GetPrevButtonIndex (m_nCurrentPopupIndex, cRepeat);
  1871. // activate the new button's popup, if it's different from the current one
  1872. if (nNewPopupIndex != m_nCurrentPopupIndex)
  1873. NotifyNewPopup (m_nCurrentPopupIndex = nNewPopupIndex);
  1874. // eat the key press
  1875. return (1);
  1876. }
  1877. }
  1878. return (CallNextHookEx (m_hhkKeyboard, nCode, vkey, lParam));
  1879. }
  1880. /*--------------------------------------------------------------------------*
  1881. * CPopupTrackContext::CallWndProc
  1882. *
  1883. *
  1884. *--------------------------------------------------------------------------*/
  1885. LRESULT CALLBACK CPopupTrackContext::CallWndProc(
  1886. int nCode, // hook code
  1887. WPARAM wParam, // current-process flag
  1888. LPARAM lParam // address of structure with message data
  1889. )
  1890. {
  1891. // get the active monitor
  1892. CPopupTrackContext* this_ = s_pTrackContext;
  1893. ASSERT (this_ != NULL);
  1894. // ignore special cases
  1895. if (nCode < 0)
  1896. return (CallNextHookEx (this_->m_hhkCallWnd, nCode, wParam, lParam));
  1897. BOOL bCurrentThread = wParam;
  1898. LPCWPSTRUCT lpCWP = reinterpret_cast<LPCWPSTRUCT>(lParam);
  1899. // forward the request to monitor
  1900. return (this_->CallWndProc (nCode, bCurrentThread, lpCWP));
  1901. }
  1902. /*--------------------------------------------------------------------------*
  1903. * CPopupTrackContext::CallWndProc
  1904. *
  1905. *
  1906. *--------------------------------------------------------------------------*/
  1907. LRESULT CPopupTrackContext::CallWndProc (int nCode, BOOL bCurrentThread, LPCWPSTRUCT lpCWP)
  1908. {
  1909. ASSERT(lpCWP != NULL);
  1910. if (lpCWP)
  1911. {
  1912. // watch for message
  1913. if (lpCWP->message == WM_MENUSELECT)
  1914. {
  1915. // decode params
  1916. const UINT fuFlags = (UINT) HIWORD(lpCWP->wParam); // menu flags
  1917. const HMENU hmenu = (HMENU) lpCWP->lParam; // handle to menu clicked
  1918. if (fuFlags == 0xFFFF && hmenu == NULL)
  1919. {
  1920. // menu is closed! no more hooking needed
  1921. RemovePopupMonitorHooks ();
  1922. }
  1923. else
  1924. {
  1925. // we stepped on the popup (will use the info when arrows are pressed)
  1926. m_fCurrentlyOnPopupItem = (fuFlags & MF_POPUP);
  1927. }
  1928. }
  1929. }
  1930. // done
  1931. return (CallNextHookEx (m_hhkCallWnd, nCode, bCurrentThread, (LPARAM)lpCWP));
  1932. }
  1933. /*--------------------------------------------------------------------------*
  1934. * CPopupTrackContext::DismissCurrentPopup
  1935. *
  1936. *
  1937. *--------------------------------------------------------------------------*/
  1938. void CPopupTrackContext::DismissCurrentPopup (bool fTrackingComplete)
  1939. {
  1940. // If the snapin does TrackPopupMenu with a window other than
  1941. // MainFrame as parent then that window should be asked to
  1942. // close the menu by sending WM_CANCELMODE. The window
  1943. // is found by calling GetCapture().
  1944. CWnd* pwndMode = CWnd::GetCapture();
  1945. if (pwndMode == NULL)
  1946. pwndMode = AfxGetMainWnd();
  1947. pwndMode->SendMessage (WM_CANCELMODE);
  1948. }
  1949. /*--------------------------------------------------------------------------*
  1950. * CPopupTrackContext::NotifyNewPopup
  1951. *
  1952. * Notify the menu toolbar that it needs to display a new popup menu.
  1953. * Note that this must be accomplished asynchronously so that we can
  1954. * allow CMenuBar::PopupMenu to unwind after the WM_CANCELMODE from
  1955. * DismissCurrentPopup.
  1956. *--------------------------------------------------------------------------*/
  1957. void CPopupTrackContext::NotifyNewPopup (int nNewPopupIndex)
  1958. {
  1959. // dismiss the existing popup
  1960. DismissCurrentPopup (false);
  1961. // ask to activate the new popup after this one is closed
  1962. m_iRequestForNewPopup = nNewPopupIndex;
  1963. }
  1964. /*--------------------------------------------------------------------------*
  1965. * CPopupTrackContext::HitTest
  1966. *
  1967. * Returns the index of the button under the given point, -1 if none.
  1968. *--------------------------------------------------------------------------*/
  1969. int CPopupTrackContext::HitTest (CPoint pt) const
  1970. {
  1971. /*----------------------------------------------------------------*/
  1972. /* Find the range of "hit" buttons. The range will span more */
  1973. /* than one button only if there are hidden buttons in the range, */
  1974. /* and in that case, there will be exactly one non-hidden button */
  1975. /* in the range. */
  1976. /*----------------------------------------------------------------*/
  1977. std::pair<BoundConstIt, BoundConstIt> range;
  1978. if ( m_pMenuBar->GetExStyle() & WS_EX_LAYOUTRTL )
  1979. {
  1980. range = std::equal_range (m_ButtonBoundaries.begin(),
  1981. m_ButtonBoundaries.end(), pt.x,
  1982. std::greater<BoundaryCollection::value_type>() );
  1983. }
  1984. else
  1985. {
  1986. range = std::equal_range (m_ButtonBoundaries.begin(),
  1987. m_ButtonBoundaries.end(), pt.x);
  1988. }
  1989. int nLowerHitIndex = MapBoundaryIteratorToButtonIndex (range.first);
  1990. int nUpperHitIndex = MapBoundaryIteratorToButtonIndex (range.second);
  1991. /*
  1992. * equal_range returns values that are less_than and greater_than
  1993. * given value. The m_ButtonBoundaries has duplicate values (due
  1994. * to hidden buttons). So if the less_than value is one of duplicate
  1995. * values (not unique) then equal_range returns the iterator to
  1996. * last dup item, not first dup item.
  1997. *
  1998. * Below we try to find the first dup item.
  1999. */
  2000. // Find the first item with value m_ButtonBoundaries[nLowerHitIndex].
  2001. for (int iIndex = 0; iIndex < nLowerHitIndex; ++iIndex)
  2002. {
  2003. if (m_ButtonBoundaries[iIndex] == m_ButtonBoundaries[nLowerHitIndex])
  2004. {
  2005. // Found first item.
  2006. nLowerHitIndex = iIndex;
  2007. break;
  2008. }
  2009. }
  2010. ASSERT (nLowerHitIndex <= nUpperHitIndex);
  2011. int nHitIndex;
  2012. // lower equal upper? no hidden buttons
  2013. if (nLowerHitIndex == nUpperHitIndex)
  2014. nHitIndex = nLowerHitIndex;
  2015. // otherwise we have some hidden buttons, or we are precisely on a button border
  2016. else
  2017. {
  2018. nHitIndex = -1;
  2019. if (nUpperHitIndex == -1)
  2020. nUpperHitIndex = m_cButtons;
  2021. for (int i = nLowerHitIndex;
  2022. i <= nUpperHitIndex; // We should check till we hit nUpperHitIndex? AnandhaG
  2023. ++i)
  2024. {
  2025. // See if this button is not hidden.
  2026. if (!m_pMenuBar->IsButtonHidden (m_pMenuBar->IndexToCommand(i)))
  2027. {
  2028. nHitIndex = i;
  2029. break;
  2030. }
  2031. }
  2032. // we should have found a visible button
  2033. ASSERT (nHitIndex != -1);
  2034. }
  2035. ASSERT (nHitIndex >= -1);
  2036. ASSERT (nHitIndex < m_cButtons);
  2037. return (nHitIndex);
  2038. }
  2039. /*--------------------------------------------------------------------------*
  2040. * CPopupTrackContext::MapBoundaryIteratorToButtonIndex
  2041. *
  2042. * Returns the button index corresponding to the input m_ButtonBoundaries
  2043. * index, -1 for not found.
  2044. *--------------------------------------------------------------------------*/
  2045. int CPopupTrackContext::MapBoundaryIteratorToButtonIndex (BoundConstIt it) const
  2046. {
  2047. return ((it != m_ButtonBoundaries.end())
  2048. ? it - m_ButtonBoundaries.begin() - 1
  2049. : -1);
  2050. }
  2051. /*--------------------------------------------------------------------------*
  2052. * CPopupTrackContext::MaybeCloseMDIChild
  2053. *
  2054. *
  2055. *--------------------------------------------------------------------------*/
  2056. void CPopupTrackContext::MaybeCloseMDIChild (CPoint pt)
  2057. {
  2058. // if we didn't find a maxed MDI child when this all started, punt
  2059. if (m_pMaxedMDIChild == NULL)
  2060. return;
  2061. // if the click didn't happen on the system menu toolbar button, punt
  2062. if (HitTest (pt) != 0)
  2063. return;
  2064. /*-------------------------------------------------------------------*/
  2065. /* if the double-click time has elapsed, punt */
  2066. /* */
  2067. /* Note: this is called from a mouse hook, which means the value */
  2068. /* returned by GetMessageTime hasn't been updated for this message, */
  2069. /* so it really reflects the time of the *previous* message (most */
  2070. /* likely WM_LBUTTONUP). */
  2071. /* */
  2072. /* GetTickCount returns a close enough approximation to */
  2073. /* GetMessageTime, except when we're debugging through this routine, */
  2074. /* in which case the tick count will continue to spin (and this test */
  2075. /* will always fail) but the message time would have remained fixed. */
  2076. /*-------------------------------------------------------------------*/
  2077. // if ((GetMessageTime() - m_dwLButtonDownTime) > GetDoubleClickTime())
  2078. if ((GetTickCount() - m_dwLButtonDownTime) > GetDoubleClickTime())
  2079. return;
  2080. // if the second click occurred outside the double-click space, punt
  2081. if ((abs (m_ptLButtonDown.x - pt.x) > GetSystemMetrics (SM_CXDOUBLECLK)) ||
  2082. (abs (m_ptLButtonDown.y - pt.y) > GetSystemMetrics (SM_CYDOUBLECLK)))
  2083. return;
  2084. // if the window doesn't have a system menu, or its Close item is disabled, punt
  2085. CMenu* pSysMenu = m_pMaxedMDIChild->GetSystemMenu (FALSE);
  2086. if (pSysMenu == NULL)
  2087. return;
  2088. UINT nCloseState = pSysMenu->GetMenuState (SC_CLOSE, MF_BYCOMMAND);
  2089. if ((nCloseState == 0xFFFFFFFF) ||
  2090. (nCloseState & (MF_GRAYED | MF_DISABLED)))
  2091. return;
  2092. // here: we've identified a double-click on a maximized child's
  2093. // system menu; close it
  2094. m_pMaxedMDIChild->PostMessage (WM_SYSCOMMAND, SC_CLOSE);
  2095. }