📄 coolmenu.cpp
字号:
////////////////////////////////////////////////////////////////
// CoolMenu 1997 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
#include "StdAfx.h"
#include "PixLib.h"
#include "CoolMenu.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// if you want to see extra TRACE diagnostics, set below to TRUE
BOOL CCoolMenuManager::bTRACE = FALSE;
#ifdef _DEBUG
#define CMTRACEFN \
CTraceFn __fooble; \
if (bTRACE) \
TRACE
#define CMTRACE \
if (bTRACE) \
TRACE
#else
#define CMTRACEFN TRACE
#define CMTRACE TRACE
#endif
// constants used for drawing
const CXGAP = 1; // num pixels between button and text
const CXTEXTMARGIN = 2; // num pixels after hilite to start text
const CXBUTTONMARGIN = 2; // num pixels wider button is than bitmap
const CYBUTTONMARGIN = 2; // ditto for height
// DrawText flags
const DT_MYSTANDARD = DT_SINGLELINE|DT_LEFT|DT_VCENTER;
// identifies owner-draw data as mine
const LONG MYITEMID = MAKELONG(MAKEWORD('m','i'),MAKEWORD('d','0'));
// private struct: one of these for each owner-draw menu item
struct CMyItemData
{
long magicNum; // magic number identifying me
CString text; // item text
UINT fType; // original item type flags
int iButton; // index of button image in image list
CMyItemData() { magicNum = MYITEMID; }
BOOL IsMyItemData() { return magicNum == MYITEMID; }
};
IMPLEMENT_DYNAMIC(CCoolMenuManager, CSubclassWnd)
CCoolMenuManager::CCoolMenuManager():
m_szBitmap(0,0),
m_szButton(0,0), // will compute later
m_bShowButtons(TRUE), // show buttons by default
m_bAutoAccel(FALSE), // auto accelerators by default
m_hAccel(NULL), // no accelerators loaded yet
m_pAccel(NULL), // no accelerators loaded yet
m_bUseDrawState(FALSE), // use DrawEmbossed by default
m_bDrawDisabledButtonsInColor(TRUE),// use color for disabled buttons
m_CheckSelected((UINT)-1), // bitmaps ids for checkmarks
m_CheckUnselected((UINT)-1) // if id == -1 no bitmap
{
FixMFCDotBitmap();
//Vytukas
CBitmap *bitmap = CBitmap::FromHandle(PxLib::CreateDitherBitmap());
if (bitmap != NULL)
{
m_DitherBrush.CreatePatternBrush(bitmap);
bitmap->DeleteObject();
}
//Vytukas
}
CCoolMenuManager::~CCoolMenuManager()
{
Destroy();
}
//////////////////
// Destroy everything. Called from destructor and Refresh.
//
void CCoolMenuManager::Destroy()
{
m_ilButtons.DeleteImageList();
m_mapIDtoImage.RemoveAll();
m_szBitmap = m_szButton = CSize(0,0);
m_arToolbarID.RemoveAll();
m_fontMenu.DeleteObject();
DestroyAccel();
m_DitherBrush.DeleteObject();
}
/////////////////
// Destroy accelerators
//
void CCoolMenuManager::DestroyAccel()
{
m_mapIDtoAccel.RemoveAll(); // delete ACCEL entries in map
delete m_pAccel; // delete current accelerators
m_pAccel = NULL; // ...
}
//////////////////
// Call this to install the menu manager. Install(NULL) to un-install.
//
void CCoolMenuManager::Install(CFrameWnd* pFrame)
{
ASSERT_VALID(pFrame);
m_pFrame = pFrame;
HookWindow(pFrame); // install message hook
}
//////////////////
// Load array of toolbar IDs.
//
BOOL CCoolMenuManager::LoadToolbars(const UINT* arID, int n)
{
ASSERT(arID);
BOOL bRet = TRUE;
for (int i=0; i<n; i++)
bRet |= LoadToolbar(arID[i]);
return bRet;
}
// structure of RT_TOOLBAR resource
struct TOOLBARDATA
{
WORD wVersion; // version # should be 1
WORD wWidth; // width of one bitmap
WORD wHeight; // height of one bitmap
WORD wItemCount; // number of items
WORD items[1]; // array of command IDs, actual size is wItemCount
};
//////////////////
// Load one toolbar. Assumes bg color is gray.
//
// * add toolbar bitmap to image list
// * add each button ID to button map
//
BOOL CCoolMenuManager::LoadToolbar(UINT nIDToolbar)
{
// load bitmap
HBITMAP hbmToolbar = PxLib::LoadSysColorBitmap(nIDToolbar);
if (!hbmToolbar)
{
TRACE(_T("*** Can't load bitmap for toolbar %d!\n"), nIDToolbar);
return FALSE;
}
CBitmap bmToolbar;
bmToolbar.Attach(hbmToolbar); // destructor will detach & destroy
// load toolbar
LPTSTR lpResName = MAKEINTRESOURCE(nIDToolbar);
HINSTANCE hInst;
HRSRC hRsrc;
TOOLBARDATA* ptbd;
if ((hInst= AfxFindResourceHandle(lpResName, RT_TOOLBAR)) == NULL ||
(hRsrc= FindResource(hInst, lpResName, RT_TOOLBAR)) == NULL ||
(ptbd = (TOOLBARDATA*)LoadResource(hInst, hRsrc)) == NULL)
{
TRACE(_T("*** Can't load toolbar %d!\n"), nIDToolbar);
return FALSE;
}
ASSERT(ptbd->wVersion==1);
// OK, I have the bitmap and toolbar.
CSize sz(ptbd->wWidth, ptbd->wHeight);
if (m_szBitmap.cx==0)
{
// First toolbar: initialized bitmap/button sizes and create image list.
m_szBitmap = sz;
m_szButton = sz + CSize(CXBUTTONMARGIN<<1, CYBUTTONMARGIN<<1);
VERIFY(m_ilButtons.Create(sz.cx, sz.cy, ILC_MASK, 0, 10));
}
else
{
if (m_szBitmap != sz)
{
// button sizes different -- oops
TRACE(_T("*** Toolbar %d button size differs!\n"), nIDToolbar);
return FALSE;
}
}
// I have a good toolbar: now add bitmap to the image list, and each
// command ID to m_mapIDtoImage array. Note that LoadSysColorBitmap will
// change gray -> COLOR_3DFACE, so use that for image list background.
//
int iNextImage = m_ilButtons.GetImageCount();
m_ilButtons.Add(&bmToolbar, GetSysColor(COLOR_3DFACE));
for (int i = 0; i < ptbd->wItemCount; i++)
{
WORD nID = ptbd->items[i];
if (nID > 0)
{
if (GetButtonIndex(nID) >= 0)
TRACE(_T("*** Duplicate button ID %d ignored\n"), nID);
else
m_mapIDtoImage.SetAt(nID, (void*)iNextImage++);
}
}
m_arToolbarID.Add(nIDToolbar); // remember toolbar ID for Refresh
bmToolbar.Detach();
return TRUE; // success!
}
//////////////////
// Virtual CSubclassWnd window proc. All messages come here before frame
// window. Isn't it cool? Just like in the old days!
//
LRESULT CCoolMenuManager::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
switch(msg)
{
case WM_SYSCOLORCHANGE:
case WM_SETTINGCHANGE:
Refresh();
break;
case WM_MEASUREITEM:
if (OnMeasureItem((MEASUREITEMSTRUCT*)lp))
return TRUE; // handled
break;
case WM_DRAWITEM:
if (OnDrawItem((DRAWITEMSTRUCT*)lp))
return TRUE; // handled
break;
case WM_INITMENUPOPUP:
// Very important: must let frame window handle it first!
// Because if someone calls CCmdUI::SetText, MFC will change item to
// MFT_STRING, so I must change back to MFT_OWNERDRAW.
//
CSubclassWnd::WindowProc(msg, wp, lp);
OnInitMenuPopup(CMenu::FromHandle((HMENU)wp),
(UINT)LOWORD(lp), (BOOL)HIWORD(lp));
return 0;
case WM_MENUSELECT:
OnMenuSelect((UINT)LOWORD(wp), (UINT)HIWORD(wp), (HMENU)lp);
break;
case WM_MENUCHAR:
LRESULT lr = OnMenuChar((TCHAR)LOWORD(wp), (UINT)HIWORD(wp),
CMenu::FromHandle((HMENU)lp));
if (lr!=0)
return lr;
break;
}
return CSubclassWnd::WindowProc(msg, wp, lp);
}
//////////////////
// Refresh all colors, fonts, etc. For WM_SETTINGCHANGE, WM_SYSCOLORCHANGE.
//
void CCoolMenuManager::Refresh()
{
// first copy list (array) of toolbar IDs now loaded.
CUIntArray arToolbarID;
arToolbarID.Copy(m_arToolbarID);
// destroy everything
Destroy();
// re-load toolbars.
int nToolbars = arToolbarID.GetSize();
for (int i = 0; i < nToolbars; i++)
LoadToolbar(arToolbarID[i]);
//Vytukas
CBitmap *bitmap = CBitmap::FromHandle(PxLib::CreateDitherBitmap());
if (bitmap != NULL)
{
m_DitherBrush.CreatePatternBrush(bitmap);
bitmap->DeleteObject();
}
//Vytukas
}
//////////////////
// Get menu font, creating if needed
//
CFont* CCoolMenuManager::GetMenuFont()
{
if (!(HFONT)m_fontMenu)
{
NONCLIENTMETRICS info;
info.cbSize = sizeof(info);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
VERIFY(m_fontMenu.CreateFontIndirect(&info.lfMenuFont));
}
return &m_fontMenu;
}
//////////////////
// Handle WM_MEASUREITEM on behalf of frame: compute menu item size.
//
BOOL CCoolMenuManager::OnMeasureItem(LPMEASUREITEMSTRUCT lpms)
{
ASSERT(lpms);
CMyItemData* pmd = (CMyItemData*)lpms->itemData;
// TRACE("pmd == NULL\n"); //pmd == NULL by Vytukas
if (lpms->CtlType != ODT_MENU || pmd == NULL || !pmd->IsMyItemData())
return FALSE; // not handled by me
if (pmd->fType & MFT_SEPARATOR)
{
// separator: use half system height and zero width
lpms->itemHeight = GetSystemMetrics(SM_CYMENU)>>1;
lpms->itemWidth = 0;
}
else
{
// compute size of text: use DrawText with DT_CALCRECT
CWindowDC dc(NULL); // screen DC--I won't actually draw on it
CRect rcText(0,0,0,0);
CFont* pOldFont = dc.SelectObject(GetMenuFont());
dc.DrawText(pmd->text, rcText, DT_MYSTANDARD|DT_CALCRECT);
dc.SelectObject(pOldFont);
// height of item is just height of a standard menu item
lpms->itemHeight= max(GetSystemMetrics(SM_CYMENU), rcText.Height());
// width is width of text plus a bunch of stuff
int cx = rcText.Width(); // text width
cx += CXTEXTMARGIN<<1; // L/R margin for readability
cx += CXGAP; // space between button and menu text
cx += m_szButton.cx<<1; // button width (L=button; R=empty margin)
// whatever value I return in lpms->itemWidth, Windows will add the
// width of a menu checkmark, so I must subtract to defeat Windows. Argh.
//
cx -= GetSystemMetrics(SM_CXMENUCHECK)-1;
lpms->itemWidth = cx; // done deal
CMTRACE(_T("OnMeasureItem for '%s':\tw=%d h=%d\n"), (LPCTSTR)pmd->text,
lpms->itemWidth, lpms->itemHeight);
}
return TRUE; // handled
}
/////////////////
// Handle WM_DRAWITEM on behalf of frame. Note: in all that goes
// below, can't assume rcItem.left=0 because of multi-column menus!
//
BOOL CCoolMenuManager::OnDrawItem(LPDRAWITEMSTRUCT lpds)
{
ASSERT(lpds);
CMyItemData* pmd = (CMyItemData*)lpds->itemData;
// TRACE("pmd == NULL\n"); //pmd == NULL by Vytukas
if (lpds->CtlType != ODT_MENU || pmd == NULL || !pmd->IsMyItemData())
return FALSE; // not handled by me
ASSERT(lpds->itemAction != ODA_FOCUS);
ASSERT(lpds->hDC);
CDC dc;
dc.Attach(lpds->hDC);
const CRect& rcItem = lpds->rcItem;
if (pmd->fType & MFT_SEPARATOR)
{
// draw separator
CRect rc = rcItem; // copy rect
rc.top += rc.Height()>>1; // vertical center
dc.DrawEdge(&rc, EDGE_ETCHED, BF_TOP); // draw separator line
}
else
{ // not a separator
CMTRACE(_T("OnDrawItem for '%s':\tw=%d h=%d\n"), (LPCTSTR)pmd->text,
rcItem.Width(), rcItem.Height());
BOOL bDisabled = lpds->itemState & ODS_GRAYED;
BOOL bSelected = lpds->itemState & ODS_SELECTED;
BOOL bChecked = lpds->itemState & ODS_CHECKED;
BOOL bHaveButn=FALSE;
// Paint button, or blank if none
CRect rcButn(rcItem.TopLeft(), m_szButton); // button rect
rcButn += CPoint(0, (rcItem.Height() - rcButn.Height())>>1 );// center vertically
int iButton = pmd->iButton;
if (iButton >= 0)
{
// this item has a button!
bHaveButn = TRUE;
// compute point to start drawing
CSize sz = rcButn.Size() - m_szBitmap;
sz.cx >>= 1;
sz.cy >>= 1;
CPoint p(rcButn.TopLeft() + sz);
// draw disabled or normal
if (!bDisabled)
{
if(bChecked && !bSelected)
PxLib::FillRect(dc, rcButn, &m_DitherBrush); //Vytukas
else
// normal: fill BG depending on state
PxLib::FillRect(dc, rcButn, GetSysColor(
(bChecked && !bSelected) ? COLOR_3DLIGHT : COLOR_MENU));
// draw pushed-in or popped-out edge
if (bSelected || bChecked)
{
CRect rc2 = rcButn;
dc.DrawEdge(rc2, bChecked ? BDR_SUNKENOUTER : BDR_RAISEDINNER,
BF_RECT);
}
// draw the button!
m_ilButtons.Draw(&dc, iButton, p, ILD_TRANSPARENT);
}
else
{
if (m_bUseDrawState)
{
// use DrawState to draw disabled button: must convert to icon
HICON hIcon=m_ilButtons.ExtractIcon(iButton);
ASSERT(hIcon);
dc.DrawState(p, CSize(0,0), hIcon, DSS_DISABLED, (HBRUSH)NULL);
DestroyIcon(hIcon);
}
else
{
// use DrawEmbossed to draw disabeld button, w/color flag
PxLib::DrawEmbossed(dc, m_ilButtons, iButton, p,
m_bDrawDisabledButtonsInColor);
}
}
}
else
{
// no button: look for custom checked/unchecked bitmaps
CMenuItemInfo info;
info.fMask = MIIM_CHECKMARKS;
GetMenuItemInfo((HMENU)lpds->hwndItem, lpds->itemID, MF_BYCOMMAND, &info);
if (bChecked || info.hbmpUnchecked)
{
bHaveButn = Draw3DCheckmark(dc, rcButn, bSelected,
bChecked ? info.hbmpChecked : info.hbmpUnchecked);
}
}
// Done with button, now paint text. First do background if needed.
int cxButn = m_szButton.cx; // width of button
COLORREF colorBG = GetSysColor(bSelected ? COLOR_HIGHLIGHT : COLOR_MENU);
if (bSelected || lpds->itemAction==ODA_SELECT)
{
// selected or selection state changed: paint text background
CRect rcBG = rcItem; // whole rectangle
if (bHaveButn) // if there's a button:
rcBG.left += cxButn + CXGAP; // don't paint over it!
PxLib::FillRect(dc, rcBG, colorBG); // paint it!
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -