#include "StdAfx.h" #include "..\include\skinmenubar.h" #include "..\include\SkinMenuItem.h" #include "..\include\WindowSkin.h" static CSkinMenuBar* g_pMenuBar = NULL; static HHOOK g_hMsgHook = NULL; /****************************************** * 函数名 : MenuInputFilter * 功能 : *******************************************/ LRESULT CALLBACK CSkinMenuBar::MenuInputFilter(int code, WPARAM wp, LPARAM lp) { return (code==MSGF_MENU && g_pMenuBar && g_pMenuBar->OnMenuInput(*((MSG*)lp))) ? TRUE : CallNextHookEx(g_hMsgHook, code, wp, lp); } /****************************************** * 函数名 : CSkinMenuBar * 功能 : *******************************************/ CSkinMenuBar::CSkinMenuBar(void) { m_pHot = NULL; m_iTrackingState = TRACK_NONE; // initial state: not tracking m_iPopupTracking = m_iNewPopup = -1; // invalid m_pSkinWindow = GetSkin().GetWindowSkin(); _InitCommonResources(); } /****************************************** * 函数名 : CSkinMenuBar * 功能 : *******************************************/ CSkinMenuBar::~CSkinMenuBar(void) { DeleteItems(); } /****************************************** * 函数名 : InitItems * 功能 : *******************************************/ BOOL CSkinMenuBar::InitItems() { ASSERT(m_hMenu); // clean up all items DeleteItems(); // buttons CRect rtWindow; GetWindowRect(m_hWnd,rtWindow); rtWindow.OffsetRect(-rtWindow.left,-rtWindow.top); CRect rtMargins = m_pSkinWindow->GetMargins(); CRect rtMenu; m_rtPosition.left = rtWindow.left + rtMargins.left; m_rtPosition.top = rtWindow.top + rtMargins.top; m_rtPosition.right = rtWindow.right - rtMargins.right; m_rtPosition.bottom = rtWindow.top + rtMargins.top + GetSystemMetrics(SM_CYMENU); CRect rtItem ( m_rtPosition.left + 2, m_rtPosition.top + 1, m_rtPosition.right - 2, m_rtPosition.bottom - 1 ); int i = ::GetMenuItemCount(m_hMenu); for (int i = 0; i < ::GetMenuItemCount(m_hMenu); ++i) { CMenuButton *pButton = new CMenuButton(m_hMenu, i); m_arrItem.Add(pButton); pButton->Layout(CPoint(rtItem.left ,rtItem.top),true); CSize size = pButton->GetHorizontalSize(); rtItem.left = rtItem.left + size.cx; } return TRUE; } /****************************************** * 函数名 : DeleteItems * 功能 : *******************************************/ void CSkinMenuBar::DeleteItems() { for(int i = 0; i < m_arrItem.GetSize(); ++i) { CMenuItem* pItem = m_arrItem[i]; delete pItem; } m_arrItem.RemoveAll(); } /****************************************** * 函数名 : DeleteItems * 功能 : *******************************************/ int CSkinMenuBar::HitTestOnTrack(CPoint point) { for (int i = 0; i < GetItemCount(); ++i) { CMenuItem* pItem = m_arrItem[i]; CRect rcItem = pItem->GetRect(); if (rcItem.PtInRect(point)) return i; } return -1; } /****************************************** * 函数名 : GetIndex * 功能 : *******************************************/ CMenuItem* CSkinMenuBar::GetIndex(int nIndex) const { if(IsValidIndex(nIndex)) return m_arrItem[nIndex]; else return NULL; } /****************************************** * 函数名 : SetMenu * 功能 : *******************************************/ void CSkinMenuBar::SetMenu(HMENU hMenu,HWND hWnd) { m_hMenu = hMenu; if(hWnd != NULL) m_hWnd = hWnd; if ( !m_hMenu ) return; InitItems(); } /****************************************** * 函数名 : TrackPopup * 功能 : *******************************************/ void CSkinMenuBar::TrackPopup(int iButton) { CMenu menu; menu.Attach(m_hMenu); int nMenuItems = menu.GetMenuItemCount(); while (iButton >= 0) { // while user selects another menu m_iNewPopup = -1; // assume quit after this m_pPressed = GetIndex(iButton); // press the button DrawMenuBar(); // and force repaint now // post a simulated arrow-down into the message stream // so TrackPopupMenu will read it and move to the first item // PostMessage(m_hWnd,WM_KEYDOWN, VK_DOWN, 1); // PostMessage(m_hWnd,WM_KEYUP, VK_DOWN, 1); SetTrackingState(TRACK_POPUP, iButton); // enter tracking state ASSERT(g_pMenuBar == NULL); g_pMenuBar = this; ASSERT(g_hMsgHook == NULL); g_hMsgHook = SetWindowsHookEx(WH_MSGFILTER, MenuInputFilter, NULL, ::GetCurrentThreadId()); CMenuButton *pItem= (CMenuButton*)GetIndex(iButton); m_hMenuTracking = pItem->m_hSubMenu; CRect rtWindow; GetWindowRect(m_hWnd,&rtWindow); CRect rc = pItem->m_rcItem; rc.OffsetRect(CPoint(rtWindow.left,rtWindow.top)); CPoint pt ; pt.x = rc.left; pt.y = rc.bottom; BOOL bRet = TrackPopupMenuEx(m_hMenuTracking, TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL, pt.x, pt.y, m_hWnd, NULL); m_hMenuTracking = NULL; // uninstall hook. ::UnhookWindowsHookEx(g_hMsgHook); g_hMsgHook = NULL; g_pMenuBar = NULL; m_pPressed = NULL; // un-press button DrawMenuBar(); // If the user exited the menu loop by pressing Escape, // return to track-button state; otherwise normal non-tracking state. SetTrackingState(m_bEscapeWasPressed ? TRACK_BUTTON : TRACK_NONE, iButton); // If the user moved mouse to a new top-level popup (eg from File to // Edit button), I will have posted a WM_CANCELMODE to quit // the first popup, and set m_iNewPopup to the new menu to show. // Otherwise, m_iNewPopup will be -1 as set above. // So just set iButton to the next popup menu and keep looping... iButton = m_iNewPopup; } menu.Detach(); } /****************************************** * 函数名 : OnMenuInput * 功能 : *******************************************/ BOOL CSkinMenuBar::OnMenuInput(MSG& m) { ASSERT(m_iTrackingState == TRACK_POPUP); // sanity check int msg = m.message; if (msg==WM_KEYDOWN) { // handle left/right-arow. TCHAR vkey = m.wParam; if ((vkey == VK_LEFT && m_bProcessLeftArrow) || (vkey == VK_RIGHT && m_bProcessRightArrow)) { CancelMenuAndTrackNewOne( GetNextOrPrevButton(m_iPopupTracking, vkey==VK_LEFT)); return TRUE; // eat it } else if (vkey == VK_ESCAPE) { m_bEscapeWasPressed = TRUE; // (menu will abort itself) } } else if (msg==WM_MOUSEMOVE || msg==WM_LBUTTONDOWN) { // handle mouse move or click CPoint pt = m.lParam; CRect rtWindow; GetWindowRect(m_hWnd,&rtWindow); pt.Offset(-rtWindow.left,-rtWindow.top); if (msg == WM_MOUSEMOVE) { if (pt != m_ptMouse) { int iButton = HitTestOnTrack(pt); if (IsValidIndex(iButton) && iButton != m_iPopupTracking) { // user moved mouse over a different button: track its popup CancelMenuAndTrackNewOne(iButton); } m_ptMouse = pt; } } else if (msg == WM_LBUTTONDOWN) { if (HitTestOnTrack(pt) == m_iPopupTracking) { // user clicked on same button I am tracking: cancel menu CancelMenuAndTrackNewOne(-1); return TRUE; // eat it } } } return FALSE; // not handled } /****************************************** * 函数名 : SetTrackingState * 功能 : *******************************************/ void CSkinMenuBar::SetTrackingState(TRACKINGSTATE iState, int iButton) { if (iState != m_iTrackingState) { if (iState == TRACK_NONE) iButton = -1; // SetHotItem(iButton); // could be none (-1) m_pHot = GetIndex(iButton); if (iState==TRACK_POPUP) { // set related state stuff m_bEscapeWasPressed = FALSE; // assume Esc key not pressed m_bProcessRightArrow = // assume left/right arrow.. m_bProcessLeftArrow = TRUE; // ..will move to prev/next popup m_iPopupTracking = iButton; // which popup I'm tracking } m_iTrackingState = iState; } } /****************************************** * 函数名 : GetNextOrPrevButton * 功能 : *******************************************/ int CSkinMenuBar::GetNextOrPrevButton(int iButton, BOOL bPrev) { if (bPrev) { iButton--; if (iButton <0) iButton = GetItemCount() - 1; } else { iButton++; if (iButton >= GetItemCount()) iButton = 0; } return iButton; } /****************************************** * 函数名 : ToggleTrackButtonMode * 功能 : *******************************************/ void CSkinMenuBar::ToggleTrackButtonMode() { if (m_iTrackingState == TRACK_NONE || m_iTrackingState == TRACK_BUTTON) { SetTrackingState(m_iTrackingState == TRACK_NONE ? TRACK_BUTTON : TRACK_NONE, 0); } } /****************************************** * 函数名 : CancelMenuAndTrackNewOne * 功能 : *******************************************/ void CSkinMenuBar::CancelMenuAndTrackNewOne(int iNewPopup) { if (iNewPopup != m_iPopupTracking) { PostMessage(m_hWnd,WM_CANCELMODE,0,0); // quit menu loop m_iNewPopup = iNewPopup; // go to this popup (-1 = quit) } } /****************************************** * 函数名 : OnLButtonDown * 功能 : *******************************************/ void CSkinMenuBar::OnLButtonDown(UINT nFlags, CPoint point) { int iButton = HitTestOnTrack(point); if (iButton >= 0 && iButton= 0) { m_iTrackingState = TRACK_BUTTON; if (IsValidIndex(iHot) && pt != m_ptMouse) { m_pHot = GetIndex(iHot); } else m_pHot = NULL; } else m_pHot = NULL; m_ptMouse = pt; // remember point DrawMenuBar(); } } /****************************************** * 函数名 : CalcMenuBarPos * 功能 : *******************************************/ void CSkinMenuBar::CalcMenuBarPos() { CRect rtWindow; GetWindowRect(m_hWnd,rtWindow); rtWindow.OffsetRect(-rtWindow.left,-rtWindow.top); CRect rtMargins = m_pSkinWindow->GetMargins(); CRect rtMenu; m_rtPosition.left = rtWindow.left + rtMargins.left; m_rtPosition.top = rtWindow.top + rtMargins.top; m_rtPosition.right = rtWindow.right - rtMargins.right; m_rtPosition.bottom = rtWindow.top + rtMargins.top + GetSystemMetrics(SM_CYMENU); } /****************************************** * 函数名 : DrawMenuBar * 功能 : *******************************************/ void CSkinMenuBar::DrawMenuBar() { CWnd *pWnd = CWnd::FromHandle(m_hWnd); CWindowDC dc(CWnd::FromHandle(m_hWnd)); CRect rtWindow; GetWindowRect(m_hWnd,rtWindow); rtWindow.OffsetRect(-rtWindow.left,-rtWindow.top); CRect rtMargins = m_pSkinWindow->GetMargins(); CRect rtMenu; m_rtPosition.left = rtWindow.left + rtMargins.left; m_rtPosition.top = rtWindow.top + rtMargins.top; m_rtPosition.right = rtWindow.right - rtMargins.right; m_rtPosition.bottom = rtWindow.top + rtMargins.top + GetSystemMetrics(SM_CYMENU); CMemDC menmDC(&dc,&m_rtPosition); if(m_pSkinWindow) m_pSkinWindow->DrawImageSection(&menmDC,m_rtPosition,m_pSkinWindow->m_skinMenuBar.imageBackground); CRect rcItem( m_rtPosition.left + 2, m_rtPosition.top + 1, m_rtPosition.right - 2, m_rtPosition.bottom - 1 ); if ( m_arrItem.GetCount() == 0 ) return; for ( int i = 0 ; i < m_arrItem.GetCount() ; i++ ) { CMenuItem* pItem = m_arrItem.GetAt(i); if( pItem == m_pPressed) { pItem->ModifyState(MISTATE_HOT, MISTATE_PRESSED); pItem->Update(&menmDC); } else if(m_iTrackingState ==TRACK_BUTTON && pItem == m_pHot) { pItem->ModifyState(MISTATE_PRESSED, MISTATE_HOT); pItem->Update(&menmDC); } else { pItem->ModifyState(MISTATE_PRESSED|MISTATE_HOT, 0); pItem->Update(&menmDC); } } }