MenuBar.cpp
上传用户:liudazhe
上传日期:2007-01-02
资源大小:51k
文件大小:58k
源码类别:

菜单

开发平台:

Visual C++

  1. // MenuBar.cpp : 
  2. //
  3. #include "stdafx.h"
  4. #include "MenuBar.h"
  5. #include "resource.h"
  6. #include <afxpriv.h>
  7. // pasted from MFC source
  8. #define _AfxGetDlgCtrlID(hWnd)          ((UINT)(WORD)::GetDlgCtrlID(hWnd))
  9. #define HORZF(dw) (dw & CBRS_ORIENT_HORZ)
  10. #define VERTF(dw) (dw & CBRS_ORIENT_VERT)
  11. static void AdjustRectangle(CRect& rect, CPoint pt)
  12. {
  13. int nXOffset = (pt.x < rect.left) ? (pt.x - rect.left) :
  14. (pt.x > rect.right) ? (pt.x - rect.right) : 0;
  15. int nYOffset = (pt.y < rect.top) ? (pt.y - rect.top) :
  16. (pt.y > rect.bottom) ? (pt.y - rect.bottom) : 0;
  17. rect.OffsetRect(nXOffset, nYOffset);
  18. }
  19. #include <math.h>
  20. #ifdef _DEBUG
  21. #define new DEBUG_NEW
  22. #undef THIS_FILE
  23. static char THIS_FILE[] = __FILE__;
  24. #endif
  25. /////////////////////////////////////////////////////////////////////////////
  26. // CMenuBar 
  27. // I want overide EndDrag, but it's not virtual.
  28. // So I have to overide StartDrag!
  29. class CMenuBarDockContext : public CDockContext
  30. {
  31. public:
  32. CMenuBarDockContext(CControlBar* pBar) : CDockContext(pBar) { }
  33. virtual void StartDrag(CPoint pt);
  34. private:
  35. BOOL _Track();
  36. void _EndDrag();
  37. };
  38. namespace {
  39. // hook
  40. CMenuBar* g_pMenuBar = NULL;
  41. HHOOK   g_hMsgHook = NULL;
  42. // message
  43. const UINT MB_SET_MENU_NULL = WM_USER + 1100;
  44. // layout
  45. const int CXGAP    = 5;
  46. const int CYGAP    = 5;
  47. const int CYGAPVERT    = 3;
  48. const int CXGRIPPER    = 7;
  49. int cyMenuButton = 0;
  50. int cxBorder2 = ::GetSystemMetrics(SM_CXBORDER) * 2;//bWin4 ? CX_BORDER*2 : CX_BORDER;
  51. int cyBorder2 = ::GetSystemMetrics(SM_CYBORDER) * 2;//bWin4 ? CY_BORDER*2 : CY_BORDER;
  52. #ifdef _DEBUG
  53. // if you won't output TRACE in debug mode, make it FALSE;
  54. BOOL bTraceOn = TRUE;
  55. #endif
  56. }
  57. #ifdef _DEBUG
  58. #define LTRACE if (bTraceOn) TRACE
  59. #else
  60. #define LTRACE
  61. #endif
  62. BOOL CMenuBar::m_bMDIApp = FALSE;
  63. BEGIN_MESSAGE_MAP(CMenuBar, CControlBar)
  64. //{{AFX_MSG_MAP(CMenuBar)
  65. ON_WM_LBUTTONDOWN()
  66. ON_WM_MOUSEMOVE()
  67. ON_WM_TIMER()
  68. ON_WM_KILLFOCUS()
  69. ON_WM_CREATE()
  70. ON_WM_LBUTTONUP()
  71. ON_WM_DESTROY()
  72. ON_MESSAGE(MB_SET_MENU_NULL, OnSetMenuNull)
  73. ON_MESSAGE(WM_SYSCOLORCHANGE, OnSettingChange)
  74. //}}AFX_MSG_MAP
  75. ON_MESSAGE(WM_SETTINGCHANGE, OnSettingChange)
  76. END_MESSAGE_MAP()
  77. CMenuBar::CMenuBar()
  78. {
  79. m_nCurIndex  = -1;
  80. m_nTrackingState = none;
  81. m_bProcessRightArrow = m_bProcessLeftArrow = TRUE;
  82. m_bIgnoreAlt = FALSE;
  83. m_bDown  = FALSE;
  84. m_hMenu  = NULL;
  85. m_nIDEvent  = NULL;
  86. m_bIcon  = FALSE;
  87. m_bMDIMaximized = FALSE;
  88. m_hWndMDIClient = NULL;
  89. m_hWndActiveChild = NULL;
  90. m_pMenuIcon = NULL;
  91. m_pMenuControl = NULL;
  92. m_nCmdShow = SW_SHOW;
  93. }
  94. BOOL CMenuBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID)
  95. {
  96. ASSERT_VALID(pParentWnd); // must have a parent
  97. ASSERT (!((dwStyle & CBRS_SIZE_FIXED) && (dwStyle & CBRS_SIZE_DYNAMIC)));
  98. // save the style
  99. m_dwStyle = dwStyle & CBRS_ALL;   // fixed by Mark Gentry, thanx!
  100. m_dwStyle |= CBRS_SIZE_DYNAMIC;
  101. CString strClass = AfxRegisterWndClass(
  102. CS_HREDRAW | CS_VREDRAW |
  103. CS_DBLCLKS, // don't forget!
  104. AfxGetApp()->LoadStandardCursor(IDC_ARROW),
  105. (HBRUSH)(COLOR_BTNFACE + 1));
  106. return CWnd::Create(strClass, _T("MenuBar"), dwStyle, CRect(), pParentWnd, nID);
  107. }
  108. int CMenuBar::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  109. {
  110. if (CControlBar::OnCreate(lpCreateStruct) == -1)
  111. return -1;
  112. CWnd* pFrame = GetOwner();
  113. ASSERT_VALID(pFrame);
  114. // hook frame window to trap WM_MENUSELECT
  115. m_hookFrame.Install(this, pFrame->GetSafeHwnd());
  116. // If this is an MDI app, hook client window to trap WM_MDISETMENU
  117. if (pFrame->IsKindOf(RUNTIME_CLASS(CMDIFrameWnd))) {
  118. CMenuBar::m_bMDIApp = TRUE;
  119. m_hWndMDIClient = ((CMDIFrameWnd*)pFrame)->m_hWndMDIClient;
  120. ASSERT(m_hWndMDIClient);
  121. m_hookMDIClient.Install(this, m_hWndMDIClient);
  122. }
  123. // my own DockContext!
  124. m_pDockContext = new CMenuBarDockContext(this);
  125. return 0;
  126. }
  127. BOOL CMenuBar::InitItems()
  128. {
  129. ASSERT(m_hMenu);
  130. // clean up all items
  131. DeleteItems();
  132. // a little suitable
  133. int nCount = ::GetMenuItemCount(m_hMenu);
  134. ASSERT(nCount > 0);
  135. m_arrItem.SetSize(nCount);
  136. if (!CMenuButton::InitCommonResource()) {
  137. TRACE("Failed to create bar resourcen");
  138. return FALSE;
  139. }
  140. // buttons
  141. for (int i = 0; i < nCount; ++i) {
  142. m_arrItem[i] = new CMenuButton(m_hMenu, i);
  143. }
  144. cyMenuButton = m_arrItem[0]->m_sizeHorz.cy;
  145. // icon
  146. m_pMenuIcon = new CMenuIcon(this);
  147. m_arrItem.InsertAt(0, m_pMenuIcon);
  148. // frame control
  149. m_pMenuControl = new CMenuControl(this);
  150. m_arrItem.Add(m_pMenuControl);
  151. // reinitializing
  152. m_hWndActiveChild = GetActiveChildWnd(m_bMDIMaximized);
  153. if (m_hWndActiveChild) {// re set 
  154. m_pMenuIcon->OnActivateChildWnd(m_hWndActiveChild);
  155. }
  156. if (m_bMDIMaximized) {
  157. m_pMenuIcon->Validate(TRUE);
  158. m_pMenuControl->Validate(TRUE);
  159. }
  160. return TRUE;
  161. }
  162. BOOL CMenuBar::LoadMenuBar(UINT nIDResource)
  163. {
  164. if (m_hMenu) {
  165. ::DestroyMenu(m_hMenu);
  166. m_hMenu = NULL;
  167. }
  168. ASSERT_VALID(m_pDockSite);
  169. if (m_pDockSite->GetMenu()) {
  170. PostMessage(MB_SET_MENU_NULL, (WPARAM)m_pDockSite->GetSafeHwnd());
  171. }
  172. m_hMenu = ::LoadMenu(AfxGetInstanceHandle(), MAKEINTRESOURCE(nIDResource));
  173. if (m_hMenu == NULL) {
  174. TRACE("Failed to load menun");
  175. return FALSE;
  176. }
  177. return InitItems();
  178. }
  179. void CMenuBar::RefreshBar()
  180. {
  181. InvalidateRect(NULL);
  182. CFrameWnd* pFrame = (CFrameWnd*)GetOwner();
  183. ASSERT_VALID(pFrame);
  184. ASSERT(pFrame->IsFrameWnd());
  185. pFrame->RecalcLayout();
  186. // floating frame
  187. pFrame = GetParentFrame();
  188. if (pFrame->IsKindOf(RUNTIME_CLASS(CMiniFrameWnd)))
  189. pFrame->RecalcLayout();
  190. }
  191. HMENU CMenuBar::LoadMenu(HMENU hMenu, HMENU hWindowMenu)
  192. {
  193. LTRACE("CMenuBar::LoadMenun");
  194. UINT iPrevID=(UINT)-1;
  195. ASSERT(::IsMenu(hMenu));
  196. ASSERT_VALID(this);
  197. CFrameWnd* pFrame = GetParentFrame();
  198. if (::GetMenu(pFrame->GetSafeHwnd()) != NULL) {
  199. // not to make MFC ignore SetMenu(NULL), post it.
  200. PostMessage(MB_SET_MENU_NULL, (WPARAM)pFrame->GetSafeHwnd());
  201. }
  202. HMENU hOldMenu = m_hMenu;
  203. m_hMenu = hMenu; // menu is shared with MFC
  204. // initialize Items 
  205. VERIFY(InitItems());
  206. if (hMenu) {
  207. m_hWindowMenu = hWindowMenu;
  208. RefreshBar(); // and menubar itself
  209. }
  210. return hOldMenu;
  211. }
  212. CMenuBar::~CMenuBar()
  213. {
  214. if (m_bMDIApp == FALSE && m_hMenu != NULL)
  215. ::DestroyMenu(m_hMenu);
  216. }
  217. /////////////////////////////////////////////////////////////////////////////
  218. // CMenuBar 傾僀僥儉偺忣曬
  219. /////////////////////////////////////////////////////////////////////////////
  220. // CMenuBar 傾僀僥儉昤夋
  221. void CMenuBar::UpdateBar(TrackingState nState, int nNewIndex)
  222. {
  223. if (m_nTrackingState == buttonmouse)
  224. m_bIgnoreAlt = FALSE; // if prev state is BUTTONMOUSE, always should be FALSE!
  225. m_nTrackingState = nState;
  226. #ifdef _DEBUG
  227. // static LPCTSTR lpszStates[] = { _T("NONE"), _T("BUTTON"), _T("POPUP"), _T("BUTTONMOUSE") };
  228. // LTRACE(_T("CMenuBar::UpdateBar state to %s, button=%dn"),
  229. // lpszStates[nState], nNewIndex);
  230. #endif
  231. // clean up
  232. if (IsValidIndex(m_nCurIndex)) {
  233. CDC* pDC = GetDC();
  234. m_arrItem[m_nCurIndex]->SetState(CMenuButton::none);
  235. m_arrItem[m_nCurIndex]->Update(pDC);
  236. CRect rcCross = m_pMenuControl->m_rcItem & m_arrItem[m_nCurIndex]->m_rcItem;
  237. if (!rcCross.IsRectEmpty()) {
  238. m_pMenuControl->ForceDrawControl(pDC);
  239. }
  240. ReleaseDC(pDC);
  241. m_nCurIndex = -1;
  242. }
  243. if (nState != none) {
  244. ASSERT(IsValidIndex(nNewIndex));
  245. m_nCurIndex = nNewIndex;
  246. CDC* pDC = GetDC();
  247. if (nState == button || nState == buttonmouse) {
  248. m_arrItem[m_nCurIndex]->SetState(CMenuButton::hot);
  249. m_arrItem[m_nCurIndex]->Update(pDC);
  250. }
  251. else if (nState == popup) {
  252. m_arrItem[m_nCurIndex]->SetState(CMenuButton::select);
  253. m_arrItem[m_nCurIndex]->Update(pDC);
  254. }
  255. CRect rcCross = m_pMenuControl->m_rcItem & m_arrItem[m_nCurIndex]->m_rcItem;
  256. if (!rcCross.IsRectEmpty()) {
  257. m_pMenuControl->ForceDrawControl(pDC);
  258. }
  259. ReleaseDC(pDC);
  260. }
  261. else {
  262. // must be default parameter
  263. ASSERT(nNewIndex == -1);
  264. }
  265. m_bProcessRightArrow = m_bProcessLeftArrow = TRUE;
  266. }
  267. /////////////////////////////////////////////////////////////////////////////
  268. // CMenuBar 儊僢僙乕僕 僴儞僪儔
  269. int CMenuBar::HitTestOnTrack(CPoint point)
  270. {
  271. for (int i = 0; i < GetItemCount(); ++i) {
  272. CMenuItem* pItem = m_arrItem[i];
  273. CRect rcItem = pItem->GetItemRect();
  274. if (pItem->IsValid() && pItem->CanTrack() &&
  275. rcItem.PtInRect(point))
  276. return i;
  277. }
  278. return -1;
  279. }
  280. void CMenuBar::OnLButtonDown(UINT nFlags, CPoint point) 
  281. {
  282. // LTRACE("CMenuBar::OnLButtonDownn");
  283. ASSERT(m_pMenuControl);
  284. if (m_pMenuControl->OnMouseMsg(WM_LBUTTONDOWN, nFlags, point)) {
  285. return; // eat it!
  286. }
  287. int nIndex = HitTestOnTrack(point);
  288. if (IsValidIndex(nIndex) && m_arrItem[nIndex]->CanTrack()) {
  289. /* HMENU hSubMenu = ::GetSubMenu(m_hMenu, nIndex);
  290. if (hSubMenu == NULL) {
  291. UpdateWindow(); // force to repaint
  292. CDC* pDC = GetDC();
  293. m_arrItem[m_nCurIndex]->SetState(CMenuButton::select);
  294. m_arrItem[nIndex]->Update(pDC);
  295. ReleaseDC(pDC);
  296. m_bDown = TRUE;
  297. }
  298. else {
  299. TrackPopup(nIndex);
  300. }
  301. */
  302. TrackPopup(nIndex);
  303. return; // eat it!
  304. }
  305. CControlBar::OnLButtonDown(nFlags, point);
  306. }
  307. void CMenuBar::OnMouseMove(UINT nFlags, CPoint point) 
  308. {
  309. // TRACE("CMenuBar::OnMouseMoven");
  310. if (!IsTopParentActive() || !GetTopLevelParent()->IsWindowEnabled()) {
  311. // window is not active, ignore
  312. CControlBar::OnMouseMove(nFlags, point);
  313. return;
  314. }
  315. ASSERT(m_pMenuControl);
  316. if (m_pMenuControl->OnMouseMsg(WM_MOUSEMOVE, nFlags, point)) {
  317. CControlBar::OnMouseMove(nFlags, point);
  318. return;
  319. }
  320. int nIndex = HitTestOnTrack(point);
  321. if (IsValidIndex(nIndex)) {
  322. if (m_nCurIndex == -1 || m_nCurIndex != nIndex) { // other button
  323. UpdateBar(buttonmouse, nIndex); // button tracked with mouse
  324. // I wanna know when mouse is away,
  325. // but SetCapture makes ALT+F4 uncatchable
  326. // and WM_CAPTURECHANGED is never sent(why?), so we have to set timer
  327. _KillTimer();
  328. m_nIDEvent = SetTimer(1, 250, 0);
  329. }
  330. }
  331. else {
  332. UpdateBar();
  333. }
  334. CControlBar::OnMouseMove(nFlags, point);
  335. }
  336. void CMenuBar::OnTimer(UINT nIDEvent) 
  337. {
  338. if( nIDEvent == m_nIDEvent && m_nTrackingState == buttonmouse) {
  339. CPoint pt; ::GetCursorPos(&pt);
  340. CRect rect;
  341. GetWindowRect(&rect);
  342. if (!rect.PtInRect(pt)) {
  343. UpdateBar();
  344. _KillTimer();
  345. }
  346. }
  347. CControlBar::OnTimer(nIDEvent);
  348. }
  349. void CMenuBar::OnKillFocus(CWnd* pNewWnd) 
  350. {
  351. CControlBar::OnKillFocus(pNewWnd);
  352. // TODO: 
  353. UpdateBar();
  354. }
  355. LRESULT CMenuBar::OnSetMenuNull(WPARAM wParam, LPARAM)
  356. {
  357. HWND hWnd = (HWND)wParam;
  358. ASSERT(::IsWindow(hWnd));
  359. ::SetMenu(hWnd, NULL);
  360. return 0;
  361. }
  362. LRESULT CMenuBar::OnSettingChange(WPARAM wParam, LPARAM lParam)
  363. {
  364. InitItems();
  365. CFrameWnd* pFrame = GetParentFrame();
  366. ASSERT_VALID(pFrame);
  367. pFrame->RecalcLayout();
  368. return 0;
  369. }
  370. /////////////////////////////////////////////////////////////////////////////
  371. // CMenuBar
  372. void CMenuBar::OnMenuSelect(HMENU hMenu, UINT nIndex)
  373. {
  374. if (m_nTrackingState == popup) {
  375. m_bProcessRightArrow = (::GetSubMenu(hMenu, nIndex) == NULL);
  376. HMENU hSubMenu = ::GetSubMenu(hMenu, m_nCurIndex);
  377. if (hSubMenu == NULL)
  378. return;
  379. m_bProcessLeftArrow = (hMenu == hSubMenu);
  380. }
  381. }
  382. LRESULT CALLBACK CMenuBar::MenuInputFilter(int code, WPARAM wParam, LPARAM lParam)
  383. {
  384. return (
  385. code == MSGF_MENU &&
  386. g_pMenuBar &&
  387. g_pMenuBar->OnMenuInput( *((MSG*)lParam) )
  388. ) ? TRUE : CallNextHookEx(g_hMsgHook, code, wParam, lParam);
  389. }
  390. void CMenuBar::TrackPopup(int nIndex)
  391. {
  392. ASSERT_VALID(this);
  393. m_nCurIndex = nIndex;
  394. m_bLoop = TRUE;
  395. while (m_bLoop == TRUE) {
  396. UpdateWindow(); // force to repaint when button hidden by other window
  397. UpdateBar(popup, m_nCurIndex);
  398. // install hook
  399. ASSERT(g_pMenuBar == NULL);
  400. g_pMenuBar = this;
  401. ASSERT(g_hMsgHook == NULL);
  402. m_bLoop = FALSE;
  403. g_hMsgHook = ::SetWindowsHookEx(WH_MSGFILTER,
  404. MenuInputFilter, NULL, AfxGetApp()->m_nThreadID);// m_bLoop may become TRUE
  405. // popup!!
  406. m_nTrackingState = popup;
  407. m_arrItem[m_nCurIndex]->TrackPopup(this);
  408. // uninstall hook
  409. ::UnhookWindowsHookEx(g_hMsgHook);
  410. g_hMsgHook = NULL;
  411. g_pMenuBar = NULL;
  412. }
  413. UpdateBar();
  414. }
  415. BOOL CMenuBar::OnMenuInput(MSG& m)
  416. {
  417. ASSERT_VALID(this);
  418. int nMsg = m.message;
  419. CPoint pt = m.lParam;
  420. ScreenToClient(&pt);
  421. switch (nMsg) {
  422. case WM_MOUSEMOVE:
  423. if (pt != m_ptMouse) {
  424. int nIndex = HitTestOnTrack(pt);
  425. if (IsValidIndex(nIndex) && nIndex != m_nCurIndex) {
  426. // defferent button clicked
  427. GetOwner()->PostMessage(WM_CANCELMODE); // destroy popupped menu
  428. UpdateBar(); // clean up
  429. m_nCurIndex = nIndex;
  430. m_bLoop = TRUE; // continue loop
  431. }
  432. m_ptMouse = pt;
  433. }
  434. break;
  435. case WM_LBUTTONDOWN:
  436. if (HitTestOnTrack(pt) != -1 && HitTestOnTrack(pt) == m_nCurIndex) {
  437. // same button clicked
  438. GetOwner()->PostMessage(WM_CANCELMODE); // destroy popupped menu
  439. UpdateBar(button, m_nCurIndex);
  440. return TRUE; // eat it!
  441. }
  442. break;
  443. case WM_KEYDOWN: {
  444. TCHAR vKey = m.wParam;
  445. if (m_dwStyle & CBRS_ORIENT_VERT) { // if vertical
  446. break; // do nothing
  447. }
  448. if ((vKey == VK_LEFT  && m_bProcessLeftArrow) ||
  449. (vKey == VK_RIGHT && m_bProcessRightArrow)) {
  450. // no submenu
  451. int nNewIndex = GetNextOrPrevButton(m_nCurIndex, vKey==VK_LEFT);
  452. GetOwner()->PostMessage(WM_CANCELMODE); // destroy popupped menu
  453. UpdateBar();
  454. m_nCurIndex = nNewIndex;
  455. m_bLoop = TRUE; // continue loop
  456. return TRUE; // eat it!
  457. }
  458. }
  459. break;
  460. case WM_SYSKEYDOWN:
  461. // LTRACE("    m_bIgnore = TRUEn");
  462. m_bIgnoreAlt = TRUE; // next SysKeyUp will be ignored
  463. break;
  464. }
  465. return FALSE; // pass along...
  466. }
  467. BOOL CMenuBar::TranslateFrameMessage(MSG* pMsg)
  468. {
  469. ASSERT_VALID(this);
  470. ASSERT(pMsg);
  471. UINT nMsg = pMsg->message;
  472. if (WM_LBUTTONDOWN <= nMsg && nMsg <= WM_MOUSELAST) {
  473. if (pMsg->hwnd != m_hWnd && m_nTrackingState > 0) {
  474. // clicked outside
  475. UpdateBar();
  476. }
  477. }
  478. else if (nMsg == WM_SYSKEYDOWN || nMsg == WM_SYSKEYUP || nMsg == WM_KEYDOWN) {
  479. BOOL bAlt = HIWORD(pMsg->lParam) & KF_ALTDOWN; // Alt key presed?
  480. TCHAR vkey = pMsg->wParam; // + X key
  481. if (vkey == VK_MENU ||
  482. (vkey == VK_F10 && !((GetKeyState(VK_SHIFT) & 0x80000000) ||
  483.                    (GetKeyState(VK_CONTROL) & 0x80000000) || bAlt))) {
  484. // only alt key pressed
  485. if (nMsg == WM_SYSKEYUP) {
  486. switch (m_nTrackingState) {
  487. case none:
  488. if (m_bIgnoreAlt == TRUE) {
  489. // LTRACE("    ignore ALT key upn");
  490. m_bIgnoreAlt = FALSE;
  491. break;
  492. }
  493. UpdateBar(button, GetNextOrPrevButton(0, FALSE));
  494. break;
  495. case button:
  496. UpdateBar();
  497. break;
  498. case buttonmouse:
  499. break; // do nothing
  500. }
  501. }
  502. return TRUE;
  503. }
  504. else if ((nMsg == WM_SYSKEYDOWN || nMsg == WM_KEYDOWN)) {
  505. if (m_nTrackingState == button) {
  506. if (m_dwStyle & CBRS_ORIENT_HORZ) { // if horizontal
  507. switch (vkey) {
  508. case VK_LEFT:
  509. case VK_RIGHT: {
  510. int nNewIndex  = GetNextOrPrevButton(m_nCurIndex, vkey == VK_LEFT);
  511. UpdateBar(button, nNewIndex);
  512. return TRUE;
  513.    }
  514. case VK_SPACE:
  515. case VK_UP:
  516. case VK_DOWN:
  517. TrackPopup(m_nCurIndex);
  518. return TRUE;
  519. case VK_ESCAPE:
  520. UpdateBar();
  521. return TRUE;
  522. }
  523. }
  524. else { // if vertical
  525. switch (vkey) {
  526. case VK_UP:
  527. case VK_DOWN:{
  528. int nNewIndex = GetNextOrPrevButton(m_nCurIndex, vkey == VK_UP);
  529. UpdateBar(button, nNewIndex);
  530. return TRUE;
  531.    }
  532. case VK_SPACE:
  533. case VK_RIGHT:
  534. case VK_LEFT:
  535. TrackPopup(m_nCurIndex);
  536. return TRUE;
  537. case VK_ESCAPE:
  538. UpdateBar();
  539. return TRUE;
  540. }
  541. }
  542. }
  543. // Alt + X pressed
  544. if ((bAlt || m_nTrackingState == button) && isalnum(vkey)) {
  545. int nIndex;
  546. if (MapAccessKey(vkey, nIndex) == TRUE) {
  547. UpdateBar();
  548. TrackPopup(nIndex);
  549. return TRUE; // eat it!
  550. }
  551. else if (m_nTrackingState==button && !bAlt) {
  552. // MessageBeep(0); // if you want
  553. return TRUE;
  554. }
  555. }
  556. if (m_nTrackingState > 0) { // unknown key
  557. if (m_nTrackingState != buttonmouse) { // if tracked with mouse, don't update bar
  558. UpdateBar();
  559. }
  560. }
  561. }
  562. }
  563. return FALSE; // pass along...
  564. }
  565. BOOL CMenuBar::MapAccessKey(TCHAR cAccessKey, int& nIndex)
  566. {
  567. for (int i = 0; i < GetItemCount(); ++i) {
  568. TCHAR cKey;
  569. if (m_arrItem[i]->GetAccessKey(cKey) == TRUE &&
  570. cKey == cAccessKey) {
  571. nIndex = i;
  572. return TRUE;
  573. }
  574. }
  575. return FALSE;
  576. }
  577. /////////////////////////////////////////////////////////////////////////////
  578. // CMenuBar layout
  579. // calculate only layout from m_bWrapped
  580. void CMenuBar::CalcFloatingLayout()
  581. {
  582. ASSERT_VALID(this);
  583. ASSERT(::IsWindow(m_hWnd));
  584. int xStart = CXGAP;
  585. if (!IsFloating()) {
  586. xStart += CXGRIPPER;
  587. }
  588. int xOffset = xStart;
  589. int yOffset = CYGAP;
  590. for (int i = 0; i < GetItemCount(); ++i) {
  591. CMenuItem* pItem = m_arrItem[i];
  592. if (pItem->IsValid()) {
  593. CPoint ptItem(xOffset, yOffset);
  594. pItem->Layout(ptItem, TRUE); // layout by itself!
  595. if (pItem->m_bWrapped == TRUE) {
  596. xOffset = xStart; // reset xOffset
  597. yOffset += pItem->m_sizeHorz.cy;
  598. }
  599. else
  600. xOffset += pItem->m_sizeHorz.cx;
  601. }
  602. }
  603. }
  604. // calulate ordinary layout and size without m_bWrapped 
  605. CSize CMenuBar::CalcLayout(DWORD dwMode, int nLength)
  606. {
  607. ASSERT_VALID(this);
  608. ASSERT(::IsWindow(m_hWnd));
  609. if (dwMode & LM_HORZDOCK)
  610. ASSERT(dwMode & LM_HORZ);
  611. CSize sizeResult(0, 0);
  612. if (!(dwMode & LM_HORZ))  { // if vertical
  613. int yOffset = CXGAP;
  614. if (!IsFloating())
  615. yOffset += CXGRIPPER;
  616. for (int i = 0; i < GetItemCount(); ++i) {
  617. CMenuItem* pItem = m_arrItem[i];
  618. if (pItem->IsValid()) {
  619. CPoint ptItem(CYGAPVERT, yOffset);
  620. pItem->Layout(ptItem, FALSE); // layout by itself
  621. yOffset += pItem->m_rcItem.Height();
  622. }
  623. }
  624. int cxBar = max(::GetSystemMetrics(SM_CXSMICON), cyMenuButton + (CYGAPVERT)*2);
  625. sizeResult =  CSize(cxBar, yOffset+CXGAP);
  626. }
  627. else { // if horizontal
  628. int xOffset = CXGAP;
  629. if (!IsFloating())
  630. xOffset += CXGRIPPER;
  631. for (int i = 0; i < GetItemCount(); ++i) {
  632. CMenuItem* pItem = m_arrItem[i];
  633. if (pItem->IsValid()) {
  634. CPoint ptItem(xOffset, CYGAP);
  635. pItem->Layout(ptItem, TRUE); // layout by itself
  636. xOffset += pItem->m_rcItem.Width();
  637. }
  638. }
  639. int cyBar = max(::GetSystemMetrics(SM_CYSMICON), cyMenuButton + CYGAP*2);
  640. sizeResult = CSize(xOffset+CXGAP, cyBar);
  641. }
  642. return sizeResult;
  643. }
  644. // in fact, it's never called
  645. CSize CMenuBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
  646. {
  647. // LTRACE("CMenuBar::CalcFixedLayoutn");
  648. DWORD dwMode = bStretch ? LM_STRETCH : 0;
  649. dwMode |= bHorz ? LM_HORZ : 0;
  650. return CalcLayout(dwMode);
  651. }
  652. CSize CMenuBar::CalcDynamicLayout(int nLength, DWORD dwMode)
  653. {
  654. LTRACE("CMenuBar::CalcDynamicLayoutn");
  655. // Who can understand what "dwMode" means?
  656. // 
  657. // If you want it be pricisely same as DevStudio style,
  658. // I think you have to create a "CDockContext" derived class.
  659. // This is the reason why everyone think "CControlBar class" junk ? 
  660. ASSERT_VALID(this);
  661. ASSERT_VALID(m_pDockSite);
  662. ASSERT_VALID(m_pDockBar);
  663. if (m_hMenu == NULL) // if have no menu yet, just return
  664. return CSize(0, 0);
  665. if (IsFloating()) {
  666. CFrameWnd* pFrame = GetParentFrame();
  667. ASSERT_VALID(pFrame);
  668. CMenu* pSysMenu = pFrame->GetSystemMenu(FALSE);
  669. ASSERT_VALID(pSysMenu);
  670. pSysMenu->EnableMenuItem(SC_CLOSE, MF_BYCOMMAND | MF_DISABLED);
  671. }
  672. if (dwMode & LM_HORZ) { // horizontal
  673. // LTRACE("    horizontaln");
  674. if (dwMode & LM_HORZDOCK) {
  675. if (IsFloating() || (m_dwStyle & CBRS_ORIENT_VERT)) { // if Floating->Docking, return min size
  676. // LTRACE("    return min sizen");
  677. return CalcLayout(dwMode);
  678. }
  679. else {
  680. if (m_pDockContext->m_pDC) { // now dragging
  681. // return DockBar size
  682. //LTRACE("    now dragging, so calc with DockBarn");
  683. CSize size = CalcSize(GetItemCount());//CalcLayout(dwMode);
  684. CRect rcFrame;
  685. m_pDockBar->GetWindowRect(rcFrame);
  686. CRect rcBar;
  687. GetWindowRect(rcBar);
  688. rcBar.right = rcFrame.right;
  689. return CSize(rcBar.Width() + cxBorder2, size.cy);
  690. }
  691. else {
  692. //LTRACE("    no draggin calc bigger sizen");
  693. CRect rcFrame;
  694. m_pDockSite->GetWindowRect(rcFrame);
  695. SizeMenuBar(GetItemCount(), rcFrame.Width() - cxBorder2*2, FALSE);
  696. CalcFloatingLayout();
  697. CSize size1 = CalcSize(GetItemCount());
  698. return CSize(rcFrame.Width(), size1.cy);
  699. }
  700. }
  701. }
  702. else if (dwMode & LM_MRUWIDTH) { // floating size
  703. //LTRACE("    return floating sizen");
  704. SizeMenuBar(GetItemCount(), m_nMRUWidth, FALSE); // load floating Bar Width! 
  705. CalcFloatingLayout();
  706. return CalcSize(GetItemCount());
  707. }
  708. else if (dwMode & LM_COMMIT) {
  709. //LTRACE("    commit, just calc: %dn", nLength);
  710. m_nMRUWidth = nLength; // save floating Bar Width!!! (used when SaveBarState)
  711. CalcFloatingLayout();
  712. return CSize(0, 0); // MFC does'nt use this Size
  713. }
  714. else if (dwMode & LM_LENGTHY) {
  715. //LTRACE("    nLength is height : %dn", nLength);
  716. SizeMenuBar(GetItemCount(), nLength, TRUE);
  717. return CalcSize(GetItemCount());
  718. }
  719. else {
  720. //LTRACE("    nLength is width  : %dn", nLength);
  721. SizeMenuBar(GetItemCount(), nLength, FALSE);
  722. return CalcSize(GetItemCount());
  723. }
  724. ASSERT(TRUE);
  725. }
  726. else { // vertical
  727. InvalidateRect(NULL); // force to repaint!!!
  728. //LTRACE("    verticaln");
  729. if (IsFloating() || (m_dwStyle & CBRS_ORIENT_HORZ)) { // if Floating->Docking, return min size
  730. //LTRACE("    return min sizen");
  731. return CalcLayout(dwMode);
  732. }
  733. else { // return large size
  734. if (m_pDockContext->m_pDC) { // CDockContext::m_bDragging is not helpful :(
  735. //LTRACE("    now dragging, so calc with DockBarn");
  736. CSize size = CalcLayout(dwMode);
  737. CRect rcFrame;
  738. m_pDockBar->GetWindowRect(rcFrame);
  739. CRect rcBar;
  740. GetWindowRect(rcBar);
  741. rcBar.bottom = rcFrame.bottom;
  742. return CSize(size.cx, rcBar.Height());
  743. }
  744. else {
  745. //LTRACE("    no dragging, return biggest sizen");
  746. CSize size = CalcLayout(dwMode);
  747. CRect rcFrame;
  748. m_pDockSite->GetWindowRect(rcFrame);
  749. CRect rcBar;
  750. GetWindowRect(rcBar);
  751. rcBar.bottom = rcFrame.bottom;
  752. return CSize(size.cx, rcFrame.Height());
  753. }
  754. }
  755. }
  756. ASSERT(TRUE); // never come here
  757. return CalcLayout(dwMode, nLength);
  758. }
  759. void CMenuBar::DrawGripper(CDC* pDC)
  760. {
  761. if( (m_dwStyle & CBRS_FLOATING) || m_dwDockStyle == 0 )
  762. return;
  763. CRect rcGrip;
  764. GetClientRect(&rcGrip);
  765. if(m_dwStyle & CBRS_ORIENT_HORZ) {
  766. // gripper at left
  767. rcGrip.DeflateRect(4, 3);
  768. rcGrip.right = rcGrip.left + 3;
  769. pDC->DrawEdge(rcGrip, BDR_RAISEDINNER, BF_RECT);
  770. rcGrip.OffsetRect(4, 0);
  771.         pDC->DrawEdge(rcGrip, BDR_RAISEDINNER, BF_RECT);
  772. }
  773. else {
  774. // gripper at top
  775. rcGrip.DeflateRect(3, 4);
  776. rcGrip.bottom = rcGrip.top + 3;
  777. pDC->DrawEdge(rcGrip, BDR_RAISEDINNER, BF_RECT);
  778. rcGrip.OffsetRect(0, 4);
  779. pDC->DrawEdge(rcGrip, BDR_RAISEDINNER, BF_RECT);
  780. }
  781. }
  782. #define CX_BORDER   1
  783. #define CY_BORDER   1
  784. void CMenuBar::DrawBorder(CDC* pDC)
  785. {
  786. if( (m_dwStyle & CBRS_FLOATING) || m_dwDockStyle == 0 )
  787. return;
  788. CRect rect;
  789. GetClientRect(rect);
  790. DWORD dwStyle = m_dwStyle;
  791. if (!(dwStyle & CBRS_BORDER_ANY))
  792. return;
  793. CRect rect3 = rect;
  794. if (m_dwStyle & CBRS_ORIENT_HORZ) {
  795. rect3.left += cxBorder2;//rect.DeflateRect(2, 0);
  796. pDC->DrawEdge(rect3, BDR_RAISEDINNER,  BF_LEFT);
  797. }
  798. else {
  799. rect3.top += cyBorder2;//rect.DeflateRect(0, 2);
  800. pDC->DrawEdge(rect3, BDR_RAISEDINNER,  BF_TOP);
  801. }
  802. // prepare for dark lines
  803. ASSERT(rect.top == 0 && rect.left == 0);
  804. CRect rect1, rect2;
  805. rect1 = rect;
  806. rect2 = rect;
  807. COLORREF clr = ::GetSysColor(COLOR_BTNSHADOW);//afxData.bWin4 ? afxData.clrBtnShadow : afxData.clrWindowFrame;
  808. // draw dark line one pixel back/up
  809. if (dwStyle & CBRS_BORDER_3D)
  810. {
  811. rect1.right -= CX_BORDER;
  812. rect1.bottom -= CY_BORDER;
  813. }
  814. if (dwStyle & CBRS_BORDER_TOP)
  815. rect2.top += CY_BORDER;//cyBorder2;
  816. if (dwStyle & CBRS_BORDER_BOTTOM)
  817. rect2.bottom -= CY_BORDER;
  818. if (dwStyle & CBRS_BORDER_LEFT)
  819. rect2.left += CX_BORDER;
  820. if (dwStyle & CBRS_BORDER_RIGHT)
  821. rect2.right -= CX_BORDER;
  822. if (dwStyle & CBRS_BORDER_3D)
  823. {
  824. // draw left and top
  825. if (dwStyle & CBRS_BORDER_LEFT)
  826. pDC->DrawEdge(rect2, BDR_RAISEDINNER, BF_LEFT);//pDC->FillSolidRect(1, rect2.top, CX_BORDER, rect2.Height(), clr);
  827. if (dwStyle & CBRS_BORDER_TOP)
  828. pDC->DrawEdge(rect2, BDR_RAISEDINNER, BF_TOP);//pDC->FillSolidRect(0, 1, rect.right, CY_BORDER, clr);
  829. // draw right and bottom
  830. if (dwStyle & CBRS_BORDER_RIGHT)
  831. pDC->DrawEdge(rect2, BDR_RAISEDINNER, BF_RIGHT);//pDC->FillSolidRect(rect.right, rect2.top, -CX_BORDER, rect2.Height(), clr);
  832. if (dwStyle & CBRS_BORDER_BOTTOM)
  833. pDC->DrawEdge(rect2, BDR_RAISEDINNER, BF_BOTTOM);//pDC->FillSolidRect(0, rect.bottom, rect.right, -CY_BORDER, clr);
  834. }
  835. }
  836. /////////////////////////////////////////////////////////////////////////////
  837. // CMenuBarFrameHook implementation
  838. CMenuBarFrameHook::CMenuBarFrameHook()
  839. {
  840. m_pMenuBar = NULL;
  841. }
  842. BOOL CMenuBarFrameHook::Install(CMenuBar* pMenuBar, HWND hWndToHook)
  843. {
  844. ASSERT_VALID(pMenuBar);
  845. ASSERT(m_pMenuBar == NULL);
  846. m_pMenuBar = pMenuBar;
  847. return HookWindow(hWndToHook);//CWnd::FromHandlePermanent(hWndToHook));
  848. }
  849. CMenuBarFrameHook::~CMenuBarFrameHook()
  850. {
  851. }
  852. LRESULT CMenuBarFrameHook::WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
  853. {
  854. ASSERT_VALID(m_pMenuBar);
  855. switch (nMsg) {
  856. case WM_MENUSELECT:
  857. m_pMenuBar->OnMenuSelect((HMENU)lParam, (UINT)LOWORD(wParam));
  858. break;
  859. // The following messages are trapped for the MDI client window
  860. case WM_INITMENUPOPUP:
  861. LTRACE("CMenuBar::WM_INITMENUPOPUPn");
  862. if (!HIWORD(lParam) && (HMENU)wParam == m_pMenuBar->m_hWindowMenu)
  863. m_pMenuBar->OnInitMenuPopup();
  864. break;
  865. case WM_MDISETMENU: // only sent to MDI client window
  866. // Setting new frame/window menu: bypass MDI. wParam is new menu.
  867. if (wParam) {
  868. LTRACE("CMenuBar::WM_MDISETMENU 0x%04xn", wParam);
  869. // HMENU hNewMenu = (HMENU)wParam;
  870. m_pMenuBar->OnSetMenu((HMENU)wParam, (HMENU)lParam);
  871. }
  872. return 0;
  873. case WM_MDIREFRESHMENU: // only sent to MDI client window
  874. // Normally, would call DrawMenuBar, but I have the menu, so eat it.
  875. // LTRACE("CMenuBar::WM_MDIREFRESHMENUn");
  876. return 0;
  877. case WM_PAINT:
  878. // After changing the MDI maximized state, the client window gets a
  879. // paint message. This is the most convenient place to find out; there
  880. // is no WM_MDIMAXIMIZED message.
  881. if (/*m_pWndHooked->*/m_hWnd == m_pMenuBar->m_hWndMDIClient)
  882. m_pMenuBar->OnPaintMDIClient();//CheckMinMaxState();
  883. break;
  884. }
  885. return CSubclassWnd::WindowProc(nMsg, wParam, lParam);
  886. }
  887. void CMenuBar::OnLButtonUp(UINT nFlags, CPoint point) 
  888. {
  889. //LTRACE("CMenuBar::OnLButtonUpn");
  890. // TODO: 
  891. ASSERT(m_pMenuControl);
  892. if (m_pMenuControl->OnMouseMsg(WM_LBUTTONUP, nFlags, point)) {
  893. CControlBar::OnLButtonUp(nFlags, point);
  894. return;
  895. }
  896. int nIndex = HitTestOnTrack(point);
  897. /*
  898. if (IsValidIndex(nIndex) && m_bDown == TRUE) {
  899. HMENU hSubMenu = ::GetSubMenu(m_hMenu, nIndex);
  900. if (hSubMenu == NULL) {
  901. UINT nID = ::GetMenuItemID(m_hMenu, nIndex);
  902. ASSERT(nID != -1);
  903. GetOwner()->SendMessage(WM_COMMAND, (WPARAM)nID, (LPARAM)GetSafeHwnd());
  904. UpdateBar();
  905. }
  906. }
  907. */
  908. CControlBar::OnLButtonUp(nFlags, point);
  909. }
  910. void CMenuBar::DoPaint(CDC* pDC)
  911. {
  912. //LTRACE("CMenuBar::DoPaintn");
  913. CRect rect;
  914. GetClientRect(rect);
  915. // draw buttons
  916. for (int i = 0; i < m_arrItem.GetSize(); ++i) {
  917. CMenuItem* pItem = m_arrItem[i];
  918. if (pItem->IsValid())
  919. m_arrItem[i]->Update(pDC);
  920. }
  921. // draw decorations
  922. DrawGripper(pDC);
  923. DrawBorder(pDC);
  924. // draw captions
  925. if (m_pMenuControl) {
  926. CRect rcDockBar;
  927. m_pDockBar->GetClientRect(rcDockBar);
  928. if (m_dwStyle & CBRS_ORIENT_HORZ) {
  929. m_pMenuControl->DelayLayoutAndDraw(pDC, CSize(rcDockBar.Width() + cxBorder2, rect.Height()));
  930. }
  931. else {
  932. m_pMenuControl->DelayLayoutAndDraw(pDC, CSize(rect.Width(), rcDockBar.Height()));
  933. }
  934. }
  935. }
  936. void CMenuBar::_KillTimer()
  937. {
  938. if (m_nIDEvent) {
  939. KillTimer(m_nIDEvent);
  940. m_nIDEvent = NULL;
  941. }
  942. }
  943. int getPrevValidIndex(int nIndex)
  944. {
  945. --nIndex;
  946. return nIndex;
  947. }
  948. // set only m_bWrapped by nWidth
  949. int CMenuBar::WrapMenuBar(int nCount, int nWidth)
  950. {
  951. // LTRACE("CMenuBar::WrapMenuBarn");
  952. int nResult = 0;
  953. int xStart = CXGAP;
  954. if (!IsFloating() && !m_pDockContext->m_pDC) // if not floating and ****not dragging!!!****
  955. xStart += CXGRIPPER;
  956. int x = xStart;
  957. for (int i = 0; i < nCount; ++i) {
  958. CMenuItem* pItem = m_arrItem[i];
  959. if (pItem->IsValid()) {
  960. if (i+1 == nCount)
  961. return ++nResult;
  962. if (x + pItem->m_sizeHorz.cx + CXGAP> nWidth) {// itself is over
  963. if (pItem->CanWrap()) {
  964. pItem->m_bWrapped = TRUE;
  965. ++nResult;
  966. x = xStart;
  967. }
  968. }
  969. else if (x + pItem->m_sizeHorz.cx + m_arrItem[i+1]->m_sizeHorz.cx + CXGAP > nWidth) {
  970. if (pItem->CanWrap()) {
  971. pItem->m_bWrapped = TRUE;
  972. ++nResult;
  973. x = xStart;
  974. }
  975. }
  976. else {
  977. pItem->m_bWrapped = FALSE;
  978. x += pItem->m_sizeHorz.cx;
  979. }
  980. }
  981. }
  982. /* //another algorithm
  983. int dx = pItem->m_sizeHorz.cx;
  984. if (dx + CXGAP > nWidth) { // bigger than nWidth
  985. if (pItem->CanWrap()) {
  986. pItem->m_bWrapped = TRUE;
  987. // if (i != nCount - 1)
  988. ++nResult;
  989. }
  990. }
  991. else if (dx + x + CXGAP > nWidth) {
  992. if (i > 0 && m_arrItem[i-1]->CanWrap()) {
  993. // LTRACE("index %d is wrapn", i-1);
  994. m_arrItem[i-1]->m_bWrapped = TRUE;
  995. pItem->m_bWrapped = FALSE;
  996. x = xStart;
  997. // if (i != nCount - 1)///////////////////////////
  998. ++nResult;
  999. }
  1000. }
  1001. else {
  1002. pItem->m_bWrapped = FALSE;
  1003. }
  1004. x += dx;
  1005. }
  1006. }
  1007. */
  1008. return nResult + 1;
  1009. }
  1010. // calc only size, by using m_bWrapped
  1011. CSize CMenuBar::CalcSize(int nCount)
  1012. {
  1013. ASSERT(nCount > 0);
  1014. CPoint cur(CXGAP, CYGAP);
  1015. CSize sizeResult(0, 0);
  1016. int nWrap = 0;
  1017. for (int i = 0; i < nCount; ++i) {
  1018. CMenuItem* pItem = m_arrItem[i];
  1019. if (pItem->IsValid()) {
  1020. sizeResult.cx = max(cur.x + pItem->m_sizeHorz.cx, sizeResult.cx);
  1021. sizeResult.cy = max(cur.y + pItem->m_sizeHorz.cy, sizeResult.cy);
  1022. cur.x += pItem->m_sizeHorz.cx;
  1023. if (pItem->m_bWrapped == TRUE) {
  1024. LTRACE("    nIndex:%d is wrappedn", i);
  1025. cur.x = CXGAP; // reset x pos
  1026. cur.y += pItem->m_sizeHorz.cy;
  1027. ++nWrap;
  1028. }
  1029. }
  1030. }
  1031. sizeResult.cy += CYGAP;
  1032. sizeResult.cx += CXGAP;
  1033. return sizeResult;
  1034. }
  1035. void CMenuBar::SizeMenuBar(int nCount, int nLength, BOOL bVert)
  1036. {
  1037. //LTRACE("CMenuBar::SizeMenuBarn");
  1038. ASSERT(nCount > 0);
  1039. if (!bVert) { // nLength is horizontal length
  1040. if (IsFloating()) { // half size wrapping
  1041. CSize sizeMax, sizeMin, sizeMid;
  1042. // Wrap MenuBar vertically
  1043. WrapMenuBar(nCount, 0);
  1044. sizeMin = CalcSize(nCount);
  1045. // Wrap MenuBar horizontally
  1046. WrapMenuBar(nCount, 32767);
  1047. sizeMax = CalcSize(nCount);
  1048. // we can never know this algorithm :), see CToolBar implementation
  1049. while (sizeMin.cx < sizeMax.cx) {
  1050. // LTRACE("looping sizeMin.cx:%d < sizeMax.cx:%dn", sizeMin.cx, sizeMax.cx);
  1051. sizeMid.cx = (sizeMin.cx + sizeMax.cx) / 2;
  1052. WrapMenuBar(nCount, sizeMid.cx);
  1053. sizeMid = CalcSize(nCount);
  1054. if (sizeMid.cx == sizeMax.cx) { // if you forget, it loops forever!
  1055. return;
  1056. }
  1057. // LTRACE("    sizeMid : %d %dn", sizeMid.cx, sizeMid.cy);
  1058. if (nLength >= sizeMax.cx) {
  1059. // LTRACE("    nLength:%d >= sizeMax.cx:%dn", nLength, sizeMax.cx);
  1060. if (sizeMin == sizeMid) {
  1061. WrapMenuBar(nCount, sizeMax.cx);
  1062. // LTRACE("out SizeMenuBarn");
  1063. return;
  1064. }
  1065. sizeMin = sizeMid;
  1066. }
  1067. else if (nLength < sizeMax.cx) {
  1068. // LTRACE("    nLength:%d < sizeMax.cx:%dn", nLength, sizeMax.cx);
  1069. sizeMax = sizeMid;
  1070. }
  1071. else {
  1072. // LTRACE("out SizeMenuBarn");
  1073. return;
  1074. }
  1075. }
  1076. }
  1077. else { // each one wrapping
  1078. LTRACE("    just each one wrappingn");
  1079. WrapMenuBar(nCount, nLength);
  1080. }
  1081. }
  1082. else { // nLength is vertical length
  1083. CSize sizeMax, sizeMin, sizeMid;
  1084. // Wrap MenuBar vertically
  1085. WrapMenuBar(nCount, 0);
  1086. sizeMin = CalcSize(nCount);
  1087. // Wrap MenuBar horizontally
  1088. WrapMenuBar(nCount, 32767);
  1089. sizeMax = CalcSize(nCount);
  1090. while (sizeMin.cx < sizeMax.cx) {
  1091. sizeMid.cx = (sizeMin.cx + sizeMax.cx) / 2;
  1092. WrapMenuBar(nCount, sizeMid.cx);
  1093. sizeMid = CalcSize(nCount);
  1094. if (sizeMid.cx == sizeMax.cx) {
  1095. return;
  1096. }
  1097. if (nLength < sizeMid.cy) {
  1098. if (sizeMin == sizeMid) {
  1099. WrapMenuBar(nCount, sizeMax.cx);
  1100. //LTRACE("out SizeMenuBarn");
  1101. return;
  1102. }
  1103. sizeMin = sizeMid;
  1104. }
  1105. else if (nLength > sizeMid.cy)
  1106. sizeMax = sizeMid;
  1107. else {
  1108. //LTRACE("out SizeMenuBarn");
  1109. return;
  1110. }
  1111. }
  1112. }
  1113. //LTRACE("out SizeMenuBarn");
  1114. }
  1115. void CMenuBar::DeleteItems()
  1116. {
  1117. for(int i = 0; i < m_arrItem.GetSize(); ++i) {
  1118. CMenuItem* pItem = m_arrItem[i];
  1119. delete pItem;
  1120. }
  1121. m_arrItem.RemoveAll();
  1122. m_pMenuIcon = NULL;
  1123. m_pMenuControl = NULL;
  1124. }
  1125. void CMenuBar::OnDestroy() 
  1126. {
  1127. CControlBar::OnDestroy();
  1128. // TODO: 偙偺埵抲偵儊僢僙乕僕 僴儞僪儔梡偺僐乕僪傪捛壛偟偰偔偩偝偄
  1129. _KillTimer();
  1130. DeleteItems();
  1131. }
  1132. int CMenuBar::GetNextOrPrevButton(int nIndex, BOOL bPrev)
  1133. {
  1134. int nCount = GetItemCount();
  1135. if (bPrev) { // <-
  1136. --nIndex;
  1137. if (nIndex < 0)
  1138. nIndex = nCount - 1;
  1139. }
  1140. else { // ->
  1141. ++nIndex;
  1142. if (nIndex >= nCount)
  1143. nIndex = 0;
  1144. }
  1145. if (!m_arrItem[nIndex]->CanTrack() || !m_arrItem[nIndex]->IsValid()) {
  1146. return GetNextOrPrevButton(nIndex, bPrev);
  1147. }
  1148. return nIndex;
  1149. }
  1150. void CMenuBar::AddIcon(HICON hIcon)
  1151. {
  1152. // ASSERT(m_bIcon == FALSE);
  1153. // m_bIcon = TRUE;
  1154. }
  1155. CMenuControl::CMenuControl(CControlBar* pBar)
  1156. {
  1157. m_bValid = FALSE;
  1158. m_pBar = pBar;
  1159. m_bDown = FALSE;
  1160. m_nTracking = -1;
  1161. int cxCaption = cyMenuButton - CYGAP + 1;
  1162. m_sizeHorz = CSize(cxCaption*3 + CXGAP/2, cyMenuButton);
  1163. }
  1164. CMenuControl::~CMenuControl()
  1165. {
  1166. }
  1167. void CMenuControl::Update(CDC* pDC)
  1168. {
  1169. // do nothing
  1170. }
  1171. void CMenuControl::Layout(CPoint point, BOOL bHorz)
  1172. {
  1173. LTRACE("CMenuControl::Layout bHorz:%dn", bHorz);
  1174. m_bHorz = bHorz;
  1175. // just layout easily
  1176. if (bHorz) {
  1177. m_rcItem = CRect(point, m_sizeHorz);
  1178. }
  1179. else {
  1180. m_rcItem = CRect(point, CSize(m_sizeHorz.cy, m_sizeHorz.cx));
  1181. }
  1182. }
  1183. void CMenuControl::DelayLayoutAndDraw(CDC* pDC, CSize sizeBar)
  1184. {
  1185. // if (!IsValid())
  1186. // return;
  1187. // layout
  1188. if (m_bHorz) {
  1189. int cxCaption = cyMenuButton - CYGAP + 1;
  1190. int cyCaption = cxCaption - 1;
  1191. CRect rcCaption;
  1192. rcCaption.right = sizeBar.cx;
  1193. rcCaption.bottom = sizeBar.cy - CYGAP;
  1194. rcCaption.left = rcCaption.right - cxCaption;
  1195. rcCaption.top = rcCaption.bottom - cyCaption;
  1196. m_arrCaption[0] = rcCaption;
  1197. rcCaption -= CPoint(cxCaption+CXGAP/2, 0);
  1198. m_arrCaption[1] = rcCaption;
  1199. rcCaption -= CPoint(cxCaption, 0);
  1200. m_arrCaption[2] = rcCaption;
  1201. m_rcItem = CRect(m_arrCaption[2].left, m_arrCaption[2].top,
  1202. m_arrCaption[0].right, m_arrCaption[0].bottom);
  1203. }
  1204. else {
  1205. int cxCaption = cyMenuButton - CYGAPVERT - 1;
  1206. int cyCaption = cxCaption - 1;
  1207. CRect rcCaption;
  1208. rcCaption.left = CYGAPVERT;
  1209. rcCaption.bottom = sizeBar.cy;
  1210. rcCaption.right = rcCaption.left + cxCaption;
  1211. rcCaption.top = rcCaption.bottom - cyCaption;
  1212. m_arrCaption[0] = rcCaption;
  1213. rcCaption -= CPoint(0, cyCaption+CXGAP/2);
  1214. m_arrCaption[1] = rcCaption;
  1215. rcCaption -= CPoint(0, cyCaption);
  1216. m_arrCaption[2] = rcCaption;
  1217. m_rcItem = CRect(m_arrCaption[2].left, m_arrCaption[2].top,
  1218. m_arrCaption[0].right, m_arrCaption[0].bottom);
  1219. }
  1220. if (!IsValid())
  1221. return;
  1222. // draw frame controls
  1223. for (int i = 0; i < 3; ++i) {
  1224. DrawControl(pDC, i, FALSE);
  1225. }
  1226. // pDC->DrawFrameControl(m_arrCaption[0], DFC_CAPTION, DFCS_CAPTIONCLOSE);
  1227. // pDC->DrawFrameControl(m_arrCaption[1], DFC_CAPTION, DFCS_CAPTIONRESTORE);
  1228. // pDC->DrawFrameControl(m_arrCaption[2], DFC_CAPTION, DFCS_CAPTIONMIN);
  1229. }
  1230. // pasted from CDockContext
  1231. void CMenuBarDockContext::StartDrag(CPoint pt)
  1232. {
  1233. ASSERT_VALID(m_pBar);
  1234. m_bDragging = TRUE;
  1235. InitLoop();
  1236. if (m_pBar->m_dwStyle & CBRS_SIZE_DYNAMIC)
  1237. {
  1238. // get TRUE bar size (including borders)
  1239. CRect rect;
  1240. m_pBar->GetWindowRect(rect);
  1241. m_ptLast = pt;
  1242. CSize sizeHorz = m_pBar->CalcDynamicLayout(0, LM_HORZ | LM_HORZDOCK);
  1243. CSize sizeVert = m_pBar->CalcDynamicLayout(0, LM_VERTDOCK);
  1244. CSize sizeFloat = m_pBar->CalcDynamicLayout(0, LM_HORZ | LM_MRUWIDTH);
  1245. m_rectDragHorz = CRect(rect.TopLeft(), sizeHorz);
  1246. m_rectDragVert = CRect(rect.TopLeft(), sizeVert);
  1247. // calculate frame dragging rectangle
  1248. m_rectFrameDragHorz = CRect(rect.TopLeft(), sizeFloat);
  1249. m_rectFrameDragVert = CRect(rect.TopLeft(), sizeFloat);
  1250. #ifdef _MAC
  1251. CMiniFrameWnd::CalcBorders(&m_rectFrameDragHorz,
  1252. WS_THICKFRAME | WS_CAPTION, WS_EX_FORCESIZEBOX);
  1253. CMiniFrameWnd::CalcBorders(&m_rectFrameDragVert,
  1254. WS_THICKFRAME | WS_CAPTION, WS_EX_FORCESIZEBOX);
  1255. #else
  1256. CMiniFrameWnd::CalcBorders(&m_rectFrameDragHorz);
  1257. CMiniFrameWnd::CalcBorders(&m_rectFrameDragVert);
  1258. #endif
  1259. m_rectFrameDragHorz.InflateRect(-cxBorder2, -cyBorder2);
  1260. m_rectFrameDragVert.InflateRect(-cxBorder2, -cyBorder2);
  1261. }
  1262. else if (m_pBar->m_dwStyle & CBRS_SIZE_FIXED)
  1263. {
  1264. // get TRUE bar size (including borders)
  1265. CRect rect;
  1266. m_pBar->GetWindowRect(rect);
  1267. m_ptLast = pt;
  1268. CSize sizeHorz = m_pBar->CalcDynamicLayout(-1, LM_HORZ | LM_HORZDOCK);
  1269. CSize sizeVert = m_pBar->CalcDynamicLayout(-1, LM_VERTDOCK);
  1270. // calculate frame dragging rectangle
  1271. m_rectFrameDragHorz = m_rectDragHorz = CRect(rect.TopLeft(), sizeHorz);
  1272. m_rectFrameDragVert = m_rectDragVert = CRect(rect.TopLeft(), sizeVert);
  1273. CMiniFrameWnd::CalcBorders(&m_rectFrameDragHorz);
  1274. CMiniFrameWnd::CalcBorders(&m_rectFrameDragVert);
  1275. m_rectFrameDragHorz.InflateRect(-cxBorder2, -cyBorder2);
  1276. m_rectFrameDragVert.InflateRect(-cxBorder2, -cyBorder2);
  1277. }
  1278. else
  1279. {
  1280. // get TRUE bar size (including borders)
  1281. CRect rect;
  1282. m_pBar->GetWindowRect(rect);
  1283. m_ptLast = pt;
  1284. BOOL bHorz = HORZF(m_dwStyle);
  1285. DWORD dwMode = !bHorz ? (LM_HORZ | LM_HORZDOCK) : LM_VERTDOCK;
  1286. CSize size = m_pBar->CalcDynamicLayout(-1, dwMode);
  1287. // calculate inverted dragging rect
  1288. if (bHorz)
  1289. {
  1290. m_rectDragHorz = rect;
  1291. m_rectDragVert = CRect(CPoint(pt.x - rect.Height()/2, rect.top), size);
  1292. }
  1293. else // vertical orientation
  1294. {
  1295. m_rectDragVert = rect;
  1296. m_rectDragHorz = CRect(CPoint(rect.left, pt.y - rect.Width()/2), size);
  1297. }
  1298. // calculate frame dragging rectangle
  1299. m_rectFrameDragHorz = m_rectDragHorz;
  1300. m_rectFrameDragVert = m_rectDragVert;
  1301. CMiniFrameWnd::CalcBorders(&m_rectFrameDragHorz);
  1302. CMiniFrameWnd::CalcBorders(&m_rectFrameDragVert);
  1303. m_rectFrameDragHorz.InflateRect(-cxBorder2, -cyBorder2);
  1304. m_rectFrameDragVert.InflateRect(-cxBorder2, -cyBorder2);
  1305. }
  1306. // adjust rectangles so that point is inside
  1307. AdjustRectangle(m_rectDragHorz, pt);
  1308. AdjustRectangle(m_rectDragVert, pt);
  1309. AdjustRectangle(m_rectFrameDragHorz, pt);
  1310. AdjustRectangle(m_rectFrameDragVert, pt);
  1311. // initialize tracking state and enter tracking loop
  1312. m_dwOverDockStyle = CanDock();
  1313. Move(pt);   // call it here to handle special keys
  1314. _Track();
  1315. }
  1316. BOOL CMenuBarDockContext::_Track()
  1317. {
  1318. // don't handle if capture already set
  1319. if (::GetCapture() != NULL)
  1320. return FALSE;
  1321. // set capture to the window which received this message
  1322. m_pBar->SetCapture();
  1323. ASSERT(m_pBar == CWnd::GetCapture());
  1324. #ifndef _MAC
  1325. // get messages until capture lost or cancelled/accepted
  1326. while (CWnd::GetCapture() == m_pBar)
  1327. {
  1328. MSG msg;
  1329. if (!::GetMessage(&msg, NULL, 0, 0))
  1330. {
  1331. AfxPostQuitMessage(msg.wParam);
  1332. break;
  1333. }
  1334. switch (msg.message)
  1335. {
  1336. case WM_LBUTTONUP:
  1337. if (m_bDragging)
  1338. _EndDrag();
  1339. else
  1340. EndResize();
  1341. return TRUE;
  1342. case WM_MOUSEMOVE:
  1343. if (m_bDragging)
  1344. Move(msg.pt);
  1345. else
  1346. Stretch(msg.pt);
  1347. break;
  1348. case WM_KEYUP:
  1349. if (m_bDragging)
  1350. OnKey((int)msg.wParam, FALSE);
  1351. break;
  1352. case WM_KEYDOWN:
  1353. if (m_bDragging)
  1354. OnKey((int)msg.wParam, TRUE);
  1355. if (msg.wParam == VK_ESCAPE)
  1356. {
  1357. CancelLoop();
  1358. return FALSE;
  1359. }
  1360. break;
  1361. case WM_RBUTTONDOWN:
  1362. CancelLoop();
  1363. return FALSE;
  1364. // just dispatch rest of the messages
  1365. default:
  1366. DispatchMessage(&msg);
  1367. break;
  1368. }
  1369. }
  1370. #else
  1371. Point   ptCur = {0};
  1372. // get messages until capture lost or cancelled/accepted
  1373. while (CWnd::GetCapture() == m_pBar)
  1374. {
  1375. EventRecord     er;
  1376. if (OSEventAvail(everyEvent, &er))
  1377. {
  1378. GetNextEvent(everyEvent, &er);
  1379. switch (er.what)
  1380. {
  1381. case mouseUp:
  1382. if (m_bDragging)
  1383. EndDrag();
  1384. else
  1385. EndResize();
  1386. return TRUE;
  1387. case keyDown:
  1388. case keyUp:
  1389. case autoKey:
  1390. case app2Evt:
  1391. {
  1392. MSG     msg;
  1393. if (WrapEvent(&er, &msg, PM_REMOVE))
  1394. {
  1395. if (m_bDragging)
  1396. OnKey((int)msg.wParam, msg.message == WM_KEYDOWN);
  1397. if (msg.message == WM_KEYUP && msg.wParam == VK_ESCAPE)
  1398. {
  1399. CancelLoop();
  1400. return FALSE;
  1401. }
  1402. }
  1403. break;
  1404. }
  1405. default:
  1406. break;
  1407. }
  1408. }
  1409. else
  1410. {
  1411. if (!EqualPt(er.where, ptCur))
  1412. {
  1413. POINT pt = {er.where.h, er.where.v};
  1414. if (m_bDragging)
  1415. Move(pt);
  1416. else
  1417. Stretch(pt);
  1418. }
  1419. }
  1420. }
  1421. #endif
  1422. CancelLoop();
  1423. return FALSE;
  1424. }
  1425. void CMenuBarDockContext::_EndDrag()
  1426. {
  1427. CancelLoop();
  1428. if (m_dwOverDockStyle != 0)
  1429. {
  1430. CDockBar* pDockBar = GetDockBar(m_dwOverDockStyle);
  1431. ASSERT(pDockBar != NULL);
  1432. CRect rect = (m_dwOverDockStyle & CBRS_ORIENT_VERT) ?
  1433. m_rectDragVert : m_rectDragHorz;
  1434. UINT uID = _AfxGetDlgCtrlID(pDockBar->m_hWnd);
  1435. if (uID >= AFX_IDW_DOCKBAR_TOP &&
  1436. uID <= AFX_IDW_DOCKBAR_BOTTOM)
  1437. {
  1438. m_uMRUDockID = uID;
  1439. m_rectMRUDockPos = rect;
  1440. pDockBar->ScreenToClient(&m_rectMRUDockPos);
  1441. }
  1442. // dock it at the specified position, RecalcLayout will snap
  1443. // insisting own line trick!
  1444. CRect rcDockBar;
  1445. pDockBar->GetWindowRect(rcDockBar);
  1446. if (m_dwOverDockStyle & CBRS_ORIENT_VERT) {
  1447. rect.top = rcDockBar.top - 1;
  1448. }
  1449. else {
  1450. rect.left = rcDockBar.left - 10;
  1451. }
  1452. m_pDockSite->DockControlBar(m_pBar, pDockBar, &rect);
  1453. m_pDockSite->RecalcLayout();
  1454. }
  1455. else if ((m_dwStyle & CBRS_SIZE_DYNAMIC) || (HORZF(m_dwStyle) && !m_bFlip) ||
  1456. (VERTF(m_dwStyle) && m_bFlip))
  1457. {
  1458. m_dwMRUFloatStyle = CBRS_ALIGN_TOP | (m_dwDockStyle & CBRS_FLOAT_MULTI);
  1459. m_ptMRUFloatPos = m_rectFrameDragHorz.TopLeft();
  1460. m_pDockSite->FloatControlBar(m_pBar, m_ptMRUFloatPos, m_dwMRUFloatStyle);
  1461. }
  1462. else // vertical float
  1463. {
  1464. m_dwMRUFloatStyle = CBRS_ALIGN_LEFT | (m_dwDockStyle & CBRS_FLOAT_MULTI);
  1465. m_ptMRUFloatPos = m_rectFrameDragVert.TopLeft();
  1466. m_pDockSite->FloatControlBar(m_pBar, m_ptMRUFloatPos, m_dwMRUFloatStyle);
  1467. }
  1468. }
  1469. void CMenuBar::OnPaintMDIClient()
  1470. {
  1471. LTRACE("CMenuBar::OnPaintMDIClientn");
  1472. if (m_hWndMDIClient) {
  1473. BOOL bMax = FALSE;
  1474. // get active MDI child window
  1475. HWND hWndChild = (HWND)::SendMessage(m_hWndMDIClient, WM_MDIGETACTIVE, 0,
  1476. (LPARAM)&bMax);
  1477. //ASSERT(::IsWindow(hWndChild)); crash! tell me why!
  1478. if (bMax != m_bMDIMaximized) {
  1479. LTRACE("    max state changedn");
  1480. m_bMDIMaximized = bMax;
  1481. if (bMax) {
  1482. LTRACE("        maximizedn");
  1483. m_pMenuControl->Validate(TRUE);
  1484. m_pMenuIcon->Validate(TRUE);
  1485. }
  1486. else {
  1487. LTRACE("        not maximizedn");
  1488. m_pMenuControl->Validate(FALSE);
  1489. m_pMenuIcon->Validate(FALSE);
  1490. }
  1491. RefreshBar();
  1492. }
  1493. }
  1494. }
  1495. void CMenuBar::OnInitMenuPopup()
  1496. {
  1497. CMenu menu;
  1498. menu.Attach((HMENU)m_hWindowMenu);
  1499. // scan for first window command
  1500. int n = menu.GetMenuItemCount();
  1501. BOOL bAddSeperator = TRUE;
  1502. for (int iPos=0; iPos<n; iPos++) {
  1503. if (menu.GetMenuItemID(iPos) >= AFX_IDM_FIRST_MDICHILD) {
  1504. bAddSeperator = FALSE;
  1505. break;
  1506. }
  1507. }
  1508. // iPos is either first window item, or end if none found.
  1509. // delete everything after.
  1510. while (iPos < (int)menu.GetMenuItemCount())
  1511. menu.RemoveMenu(iPos, MF_BYPOSITION);
  1512. // get active window so I can check its menu item
  1513. ASSERT(m_hWndMDIClient);
  1514. HWND hwndActive = (HWND)::SendMessage(m_hWndMDIClient,
  1515. WM_MDIGETACTIVE, 0, NULL);
  1516. // append window names in the form "# title"
  1517. int iWin=1;
  1518. for (HWND hwnd=::GetWindow(m_hWndMDIClient, GW_CHILD);
  1519.   hwnd;
  1520.   hwnd = ::GetWindow(hwnd, GW_HWNDNEXT)) {
  1521. if (bAddSeperator) {
  1522. menu.InsertMenu(iPos++, MF_BYPOSITION|MF_SEPARATOR);
  1523. bAddSeperator = FALSE;
  1524. }
  1525. // build item name and add it to the menu
  1526. CString sWinName, sMenuItem;
  1527. CWnd::FromHandle(hwnd)->GetWindowText(sWinName);
  1528. sMenuItem.Format(_T("&%d  %s"), iWin++, (LPCTSTR)sWinName);
  1529. menu.InsertMenu(iPos, MF_BYPOSITION,
  1530. ::GetDlgCtrlID(hwnd), sMenuItem);
  1531. if (hwnd==hwndActive)
  1532. menu.CheckMenuItem(iPos, MF_BYPOSITION|MF_CHECKED);
  1533. iPos++;
  1534. }
  1535. menu.Detach();
  1536. }
  1537. void CMenuBar::OnSetMenu(HMENU hNewMenu, HMENU hWindowMenu)
  1538. {
  1539. // We can get active MDI child window on this message!
  1540. BOOL bMax;
  1541. HWND hWndChild = GetActiveChildWnd(bMax);
  1542. if (!m_hWndActiveChild || m_hWndActiveChild != hWndChild) {
  1543. // active child window changed
  1544. LTRACE("    active child window changedn");
  1545. m_hWndActiveChild = hWndChild;
  1546. // tell MenuIcon child window has been changed
  1547. m_pMenuIcon->OnActivateChildWnd(hWndChild);
  1548. }
  1549. LTRACE("CMenuBar::OnSetMenun");
  1550. if (!m_hMenu || m_hMenu != hNewMenu) { // menu changed
  1551. LTRACE("    menu changedn");
  1552. LoadMenu(hNewMenu, hWindowMenu); // set toolbar menu
  1553. GetOwner()->SetMenu(NULL); // clear frame menu
  1554. }
  1555. }
  1556. HWND CMenuBar::GetActiveChildWnd(BOOL& bMaximized)
  1557. {
  1558. if (!m_hWndMDIClient)
  1559. return NULL;
  1560. BOOL bMax = FALSE;
  1561. HWND hWnd = (HWND)::SendMessage(m_hWndMDIClient,
  1562. WM_MDIGETACTIVE, 0, (LPARAM)&bMax);
  1563. bMaximized = bMax;
  1564. return hWnd;
  1565. }
  1566. CMenuItem::CMenuItem()
  1567. {
  1568. m_itemState = none;
  1569. m_rcItem = CRect(0, 0, 0, 0);
  1570. m_sizeHorz = CSize(0, 0);
  1571. m_bHorz = TRUE;
  1572. m_bWrapped = FALSE;
  1573. }
  1574. CMenuItem::~CMenuItem()
  1575. {
  1576. }
  1577. CPoint CMenuItem::ComputeMenuTrackPoint(CWnd* pWnd, HMENU hSubMenu, TPMPARAMS& tpm, CFont* pFont)
  1578. {
  1579. ASSERT_VALID(pWnd);
  1580. ASSERT_VALID(pFont);
  1581. ASSERT(::IsMenu(hSubMenu));
  1582. tpm.cbSize = sizeof(tpm);
  1583. CRect& rcExclude = (CRect&)tpm.rcExclude;
  1584. CWnd::GetDesktopWindow()->GetWindowRect(&rcExclude);
  1585. CPoint pt;
  1586. CRect  rcItem = m_rcItem;
  1587. pWnd->ClientToScreen(&rcItem);
  1588. // if (hSubMenu == NULL) // it's possible no sub menu
  1589. // return CPoint();
  1590. if (m_bHorz) { // horizontal
  1591. int nCount = ::GetMenuItemCount(hSubMenu);
  1592. ASSERT(nCount != -1);
  1593. int cyPopup = nCount * rcItem.Height(); // I want it be not owner drawn but ordinary menu..
  1594. if (rcItem.bottom + cyPopup > rcExclude.bottom) {
  1595. pt = CPoint(rcItem.left, rcItem.top); // over Screen
  1596. rcExclude.top = rcItem.top;
  1597. }
  1598. else {
  1599. pt = CPoint(rcItem.left, rcItem.bottom);
  1600. }
  1601. pt += CPoint(-1, 1); // precisely same as DevStudio
  1602. }
  1603. else { // vertical
  1604. // we never get the width of popup up menu, but I will try
  1605. int nCount = ::GetMenuItemCount(hSubMenu);
  1606. ASSERT(nCount != -1);
  1607. int cxPopup = 0;
  1608. CWindowDC dc(NULL);
  1609. CFont* pOldFont = dc.SelectObject(pFont);
  1610. for (int i = 0; i < nCount; ++i) {
  1611. char szName[256];
  1612. MENUITEMINFO info;
  1613. ::ZeroMemory(&info, sizeof(MENUITEMINFO));
  1614. info.cbSize = sizeof(MENUITEMINFO);
  1615. info.fMask = MIIM_ID | MIIM_TYPE;
  1616. info.dwTypeData = szName;
  1617. info.cch = sizeof(szName);
  1618. ::GetMenuItemInfo(hSubMenu, i, TRUE, &info);
  1619. CString strItem(szName);
  1620. CRect rcText(0, 0, 0, 0);
  1621. dc.DrawText(strItem, &rcText,
  1622. DT_SINGLELINE | DT_VCENTER | DT_CALCRECT | DT_NOPREFIX);
  1623. int cxOffset = ::GetSystemMetrics(SM_CXMENUCHECK) * 2;
  1624. cxPopup = max(rcText.Width() + 4*2 + CXGAP*2 + cxOffset, cxPopup);
  1625. }
  1626. dc.SelectObject(pOldFont);
  1627. if (rcItem.right + cxPopup > rcExclude.right) { // over right-side
  1628. pt = CPoint(rcItem.left, rcItem.top);
  1629. rcExclude.left = rcItem.left;
  1630. }
  1631. else {
  1632. pt = CPoint(rcItem.right, rcItem.top);
  1633. rcExclude.right = rcItem.right;
  1634. }
  1635. pt += CPoint(1, -1); // precisely same as DevStudio
  1636. }
  1637. return pt;
  1638. }
  1639. CMenuIcon::CMenuIcon(CWnd* pWnd)
  1640. {
  1641. m_hDocIcon = NULL;//hIcon;
  1642. m_sizeHorz = CSize(::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON));
  1643. m_bValid = FALSE;
  1644. m_hSysMenu = NULL;//::GetSystemMenu(pWnd->GetSafeHwnd(), FALSE);
  1645. }
  1646. CMenuIcon::~CMenuIcon()
  1647. {
  1648. }
  1649. void CMenuIcon::Update(CDC* pDC)
  1650. {
  1651. ASSERT(m_hDocIcon);
  1652. ASSERT(m_bValid);
  1653. ::DrawIconEx(pDC->m_hDC, m_rcItem.left, m_rcItem.top, m_hDocIcon,
  1654. m_sizeHorz.cx, m_sizeHorz.cy, 0, NULL, DI_NORMAL);
  1655. }
  1656. void CMenuIcon::TrackPopup(CWnd* pWnd)
  1657. {
  1658. ASSERT(m_hDocIcon);
  1659. ASSERT(m_bValid);
  1660. ASSERT(::IsMenu(m_hSysMenu));
  1661. NONCLIENTMETRICS info;
  1662. info.cbSize = sizeof(info);
  1663. ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
  1664. CFont fontSys;
  1665. if (!fontSys.CreateFontIndirect(&info.lfMenuFont))
  1666. return;
  1667. TPMPARAMS tpm;
  1668. CPoint pt = ComputeMenuTrackPoint(pWnd, m_hSysMenu, tpm, &fontSys);
  1669. ::TrackPopupMenuEx(m_hSysMenu,
  1670. TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
  1671. pt.x, pt.y, pWnd->GetOwner()->GetSafeHwnd(), &tpm);
  1672. }
  1673. void CMenuIcon::Layout(CPoint point, BOOL bHorz)
  1674. {
  1675. ASSERT(m_bValid);
  1676. m_bHorz = bHorz;
  1677. m_rcItem = CRect(point, m_sizeHorz);
  1678. }
  1679. void CMenuIcon::OnActivateChildWnd(HWND hWndChild)
  1680. {
  1681. TRACE("CMenuIcon::OnActivateChildWndn");
  1682. ASSERT(::IsWindow(hWndChild));
  1683. m_hSysMenu = ::GetSystemMenu(hWndChild, FALSE);
  1684. ASSERT(::IsMenu(m_hSysMenu));
  1685. m_hDocIcon = (HICON)GetClassLong(hWndChild, GCL_HICONSM);
  1686. }
  1687. BOOL CMenuControl::OnMouseMsg(UINT msg, UINT nFlags, CPoint pt)
  1688. {
  1689. if (!IsValid())
  1690. return FALSE;
  1691. if (msg == WM_LBUTTONDOWN) {
  1692. m_nTracking = HitTest(pt);
  1693. if (m_nTracking >= 0) {
  1694. CClientDC dc(m_pBar);
  1695. DrawControl(&dc, m_nTracking, TRUE);
  1696. m_bDown = TRUE;
  1697. m_pBar->SetCapture();   // grab mouse input
  1698. return TRUE;
  1699. }
  1700. }
  1701. else if ((msg == WM_MOUSEMOVE) && m_nTracking >= 0) {
  1702. // mouse moved, and I am tracking: possibly draw button up/down
  1703. BOOL bOldDown = m_bDown;
  1704. m_bDown = m_arrCaption[m_nTracking].PtInRect(pt);
  1705. if (bOldDown != m_bDown) {
  1706. // up/down state changed: need to redraw button
  1707. CClientDC dc(m_pBar);
  1708. DrawControl(&dc, m_nTracking, m_bDown);
  1709. }
  1710. return TRUE; // handled
  1711. }
  1712. else if (msg == WM_LBUTTONUP && m_nTracking >= 0) {
  1713. // user released the mouse and I am tracking: do button command
  1714. ReleaseCapture(); // let go the mouse
  1715. if (m_bDown) {
  1716. // if button was down when released: draw button up, and do system cmd
  1717. CClientDC dc(m_pBar);
  1718. DrawControl(&dc, m_nTracking, FALSE);
  1719. CFrameWnd* pFrame = m_pBar->GetTopLevelFrame()->GetActiveFrame();
  1720. ASSERT_VALID(pFrame);
  1721. static syscmd[3] =
  1722. { /*SC_MOUSEMENU,*/ SC_CLOSE, SC_RESTORE, SC_MINIMIZE };
  1723. pFrame->SendMessage(WM_SYSCOMMAND, syscmd[m_nTracking]);
  1724. }
  1725. m_nTracking = -1; // stop tracking
  1726. return TRUE;   // handled (eat)
  1727. }
  1728. return FALSE;
  1729. }
  1730. int CMenuControl::HitTest(CPoint point)
  1731. {
  1732. for (int i = 0; i < 3; ++i) {
  1733. if (m_arrCaption[i].PtInRect(point))
  1734. return i;
  1735. }
  1736. return -1;
  1737. }
  1738. void CMenuControl::DrawControl(CDC* pDC, int nIndex, BOOL bPressed)
  1739. {
  1740. // draw frame controls
  1741. CRect& rc = m_arrCaption[nIndex];
  1742. static UINT dfcs[3] = { DFCS_CAPTIONCLOSE, DFCS_CAPTIONRESTORE, DFCS_CAPTIONMIN };
  1743. UINT uState = dfcs[nIndex];
  1744. if (bPressed)
  1745. uState |= DFCS_PUSHED;
  1746. pDC->DrawFrameControl(rc, DFC_CAPTION, uState);
  1747. }
  1748. void CMenuControl::ForceDrawControl(CDC * pDC)
  1749. {
  1750. if (!IsValid())
  1751. return;
  1752. for (int i = 0; i < 3; ++i) {
  1753. DrawControl(pDC, i, FALSE);
  1754. }
  1755. }
  1756. namespace {
  1757. const int CXTEXTMARGIN   = 5;
  1758. int CYTEXTMARGIN   = 0;
  1759. const int CYTEXTMARGINMIN = 2;
  1760. CFont fontHorz, fontVert;
  1761. // const int CXGAP = 3;
  1762. }// anonymouse namespace
  1763. CMenuButton::CMenuButton(HMENU hMenu, int nIndex)
  1764. {
  1765. ASSERT(hMenu);
  1766. InitButtonStringAndSubMenuHandle(hMenu, nIndex);
  1767. InitHorizontalButtonSize();
  1768. InitAccessKeyAndVerticalLinePoint();
  1769. }
  1770. CMenuButton::~CMenuButton()
  1771. {
  1772. }
  1773. BOOL CMenuButton::InitCommonResource()
  1774. {
  1775. // clean up
  1776. fontHorz.DeleteObject();
  1777. fontVert.DeleteObject();
  1778. // create fonts
  1779. NONCLIENTMETRICS info;
  1780. info.cbSize = sizeof(info);
  1781. ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
  1782. if (!fontHorz.CreateFontIndirect(&info.lfMenuFont))
  1783. return FALSE;
  1784. info.lfMenuFont.lfEscapement = -900;
  1785. info.lfMenuFont.lfOrientation = -900;
  1786. if (!fontVert.CreateFontIndirect(&info.lfMenuFont))
  1787. return FALSE;
  1788. // get font height
  1789. LOGFONT logfont;
  1790. fontHorz.GetLogFont(&logfont);
  1791. int cyFont = abs(logfont.lfHeight);
  1792. // calc Y text margin
  1793. int cyText = cyFont + CYTEXTMARGINMIN*2;
  1794. int cyMenu = ::GetSystemMetrics(SM_CYMENU);
  1795. if (cyMenu > cyText) {
  1796. CYTEXTMARGIN = (cyMenu - cyFont)/2;
  1797. }
  1798. else {
  1799. CYTEXTMARGIN = CYTEXTMARGINMIN;
  1800. }
  1801. return TRUE;
  1802. }
  1803. void CMenuButton::InitButtonStringAndSubMenuHandle(HMENU hMenu, int nIndex)
  1804. {
  1805. // get menu button Text
  1806. char szText[256];
  1807. MENUITEMINFO info; ::ZeroMemory(&info, sizeof(MENUITEMINFO));
  1808. info.cbSize = sizeof(MENUITEMINFO);
  1809. info.fMask = MIIM_ID | MIIM_TYPE;
  1810. info.dwTypeData = szText;
  1811. info.cch = sizeof(szText);
  1812. ::GetMenuItemInfo(hMenu, nIndex, TRUE, &info);
  1813. m_strBtn = CString(szText);
  1814. m_hSubMenu = ::GetSubMenu(hMenu, nIndex);
  1815. }
  1816. void CMenuButton::InitHorizontalButtonSize()
  1817. {
  1818. // get menu button Text size
  1819. ASSERT(m_strBtn.IsEmpty() == FALSE);
  1820. CWindowDC dc(NULL);
  1821. CRect rcText(0, 0, 0, 0);
  1822. CFont* pOldFont = dc.SelectObject(&fontHorz);
  1823. dc.DrawText(m_strBtn, &rcText, DT_SINGLELINE | DT_CALCRECT);
  1824. dc.SelectObject(pOldFont);
  1825. m_sizeHorz.cx = rcText.Width() + CXTEXTMARGIN*2;
  1826. LOGFONT logfont;
  1827. fontHorz.GetLogFont(&logfont);
  1828. int cyFont = abs(logfont.lfHeight);
  1829. m_sizeHorz.cy = cyFont + CYTEXTMARGIN*2;
  1830. }
  1831. void CMenuButton::InitAccessKeyAndVerticalLinePoint()
  1832. {
  1833. int nIndex = m_strBtn.Find('&');
  1834. m_cAccessKey = m_strBtn[nIndex + 1]; // -1 + 1 = 0; it's ok
  1835. if (nIndex == -1) {
  1836. m_ptLineFrom = m_ptLineTo = CPoint(0, 0);
  1837. }
  1838. else if (nIndex == 0) {
  1839. CRect rcTo;
  1840. CWindowDC dc(NULL);
  1841. CFont* pOldFont = dc.SelectObject(&fontHorz);
  1842. dc.DrawText(m_strBtn.Left(nIndex+2), &rcTo, DT_SINGLELINE | DT_CALCRECT);
  1843. dc.SelectObject(pOldFont);
  1844. m_ptLineFrom = CPoint(CYTEXTMARGIN, CXTEXTMARGIN);
  1845. m_ptLineTo  = CPoint(CYTEXTMARGIN, CXTEXTMARGIN + rcTo.Width());
  1846. }
  1847. else {
  1848. CRect rcFrom, rcTo;
  1849. CWindowDC dc(NULL);
  1850. CFont* pOldFont = dc.SelectObject(&fontHorz);
  1851. dc.DrawText(m_strBtn.Left(nIndex), &rcFrom, DT_SINGLELINE | DT_CALCRECT);
  1852. dc.DrawText(m_strBtn.Left(nIndex+2), &rcTo, DT_SINGLELINE | DT_CALCRECT);
  1853. dc.SelectObject(pOldFont);
  1854. m_ptLineFrom = CPoint(CYTEXTMARGIN, CXTEXTMARGIN + rcFrom.Width());
  1855. m_ptLineTo = CPoint(CYTEXTMARGIN, CXTEXTMARGIN + rcTo.Width());
  1856. }
  1857. }
  1858. void CMenuButton::Layout(CPoint point, BOOL bHorz)
  1859. {
  1860. if (bHorz == TRUE) {
  1861. m_rcItem = CRect(point, m_sizeHorz);
  1862. }
  1863. else {
  1864. m_rcItem = CRect(point, CSize(m_sizeHorz.cy, m_sizeHorz.cx));
  1865. }
  1866. m_bHorz = bHorz;
  1867. }
  1868. void CMenuButton::Update(CDC* pDC)
  1869. {
  1870. // clean background
  1871. COLORREF clr = ::GetSysColor(COLOR_BTNFACE);
  1872. pDC->FillSolidRect(m_rcItem, clr);
  1873. switch (m_itemState) {
  1874. case hot:
  1875. DrawHot(pDC);
  1876. break;
  1877. case select:
  1878. DrawSelect(pDC);
  1879. break;
  1880. case none:
  1881. DrawNone(pDC);
  1882. break;
  1883. default:
  1884. ASSERT(TRUE);
  1885. }
  1886. }
  1887. void CMenuButton::TrackPopup(CWnd* pWnd)
  1888. {
  1889. TPMPARAMS tpm;
  1890. CPoint pt = ComputeMenuTrackPoint(pWnd, m_hSubMenu, tpm, &fontHorz);
  1891. ::TrackPopupMenuEx(m_hSubMenu,
  1892. TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
  1893. pt.x, pt.y, pWnd->GetOwner()->GetSafeHwnd(), &tpm);
  1894. }
  1895. void CMenuButton::DrawHorzText(CDC* pDC, CPoint ptOffset)
  1896. {
  1897. CRect rcBtn = m_rcItem;
  1898. pDC->SetBkMode(TRANSPARENT);
  1899. CFont* pOldFont = pDC->SelectObject(&fontHorz);
  1900. // I know precise text size
  1901. rcBtn.DeflateRect(CXTEXTMARGIN, CYTEXTMARGIN);
  1902. pDC->DrawText(m_strBtn, rcBtn + ptOffset,
  1903. DT_SINGLELINE);// | DT_CENTER | DT_VCENTER); no need
  1904. pDC->SelectObject(pOldFont);
  1905. }
  1906. void CMenuButton::DrawVertText(CDC* pDC, CPoint ptOffset)
  1907. {
  1908. CRect rcBtn = m_rcItem;
  1909. int nLength = m_strBtn.GetLength();
  1910. int nIndex = m_strBtn.Find('&');
  1911. CString strBtn = m_strBtn.Left(nIndex) + m_strBtn.Right(nLength - (nIndex+1));
  1912. pDC->SetBkMode(TRANSPARENT);
  1913. CFont* pOldFont = pDC->SelectObject(&fontVert);
  1914. // I must know precise text size
  1915. CRect rcString = CRect(
  1916. CPoint(rcBtn.right - CYTEXTMARGIN, rcBtn.top + CXTEXTMARGIN), m_sizeHorz);
  1917. pDC->DrawText(strBtn, rcString + ptOffset,
  1918. DT_SINGLELINE | DT_NOCLIP | DT_NOPREFIX); // don't forget DT_NOCLIP
  1919. pDC->SelectObject(pOldFont);
  1920. // DrawText is poor, so we have to draw vertical line by ourselves
  1921. pDC->MoveTo(rcBtn.TopLeft() + m_ptLineFrom + ptOffset);
  1922. pDC->LineTo(rcBtn.TopLeft() + m_ptLineTo + ptOffset);
  1923. }
  1924. void CMenuButton::DrawHot(CDC* pDC)
  1925. {
  1926. if (m_bHorz) {
  1927. // draw down button
  1928. pDC->DrawEdge(m_rcItem, BDR_RAISEDINNER, BF_RECT);
  1929. DrawHorzText(pDC);
  1930. }
  1931. else {
  1932. pDC->DrawEdge(m_rcItem, BDR_RAISEDINNER, BF_RECT);
  1933. DrawVertText(pDC);
  1934. }
  1935. }
  1936. void CMenuButton::DrawSelect(CDC* pDC)
  1937. {
  1938. if (m_bHorz) {
  1939. // draw pressed button
  1940. pDC->DrawEdge(m_rcItem, BDR_SUNKENOUTER, BF_RECT);
  1941. DrawHorzText(pDC, CPoint(1, 1));
  1942. }
  1943. else {
  1944. pDC->DrawEdge(m_rcItem, BDR_SUNKENOUTER, BF_RECT);
  1945. DrawVertText(pDC, CPoint(1, 1));
  1946. }
  1947. }
  1948. void CMenuButton::DrawNone(CDC* pDC)
  1949. {
  1950. if (m_bHorz) {
  1951. DrawHorzText(pDC);
  1952. }
  1953. else {
  1954. DrawVertText(pDC);
  1955. }
  1956. }
  1957. int CMenuBar::OnActivateFrame(int nCmdShow)
  1958. {
  1959. CFrameWnd* pFrame = GetParentFrame();
  1960. ASSERT_VALID(pFrame);
  1961. if (pFrame->GetMenu() != NULL) {
  1962. LTRACE(" has menun");
  1963. pFrame->SetMenu(NULL);
  1964. }
  1965. m_nCmdShow = nCmdShow;
  1966. return SW_HIDE;
  1967. }