📄 uicoolmenu.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.
//
// ==========================================================================
// HISTORY:
// ==========================================================================
// 1.01 13 Aug 1998 - Andrew Bancroft [ABancroft@lgc.com] - Since we've already
// added the entire toolbar to the imagelist we need to
// increment nNextImage even if we didn't add this button to
// m_mapIDtoImage in the LoadToolbar() method.
// 1.01a 13 Aug 1998 - Peter Tewkesbury - Added AddSingleBitmap(...)
// method for adding a single bitmap to a pulldown
// menu item.
// 1.02 13 Aug 1998 - Omar L Francisco - Fixed bug with lpds->CtlType
// and lpds->itemData item checking.
// 1.03 12 Nov 1998 - Fixes debug assert in system menu. - Wang Jun
// 1.04 17 Nov 1998 - Fixes debug assert when you maximize a view - Wang Jun
// window, then try to use the system menu for the view.
// 1.05 09 Jan 1998 - Seain B. Conover [sc@tarasoft.com] - Fix for virtual
// key names.
// 1.06 24 Feb 1999 - Michael Lange [michael.home@topdogg.com] - Fix for memory
// leak in CMyItemData structure, added a destructor that
// calls text.Empty().
// - Boris Kartamishev [kbv@omegasoftware.com] - Fix for resource
// ID bug.
// - Jeremy Horgan [jeremyhorgan@hotmail.com] - During
// accelerator key processing OnInitMenuPopup() calls
// ConvertMenu() which allocates a new CMyItemData for each
// menu item. This is memory is normally freed by a call to
// OnMenuSelect(), which is not called when processing
// accelerator keys. This results in a memory leak. This was
// fixed by modifying the ~CCoolMenuManager() destructor.
// 1.07 24 Feb 1999 - Koji MATSUNAMI [kmatsu@inse.co.jp] - Fixed problem with
// popup menus being drawn correctly as cool menus.
// 1.08 7 Jul 2000 - Philip Oldaker [philip@masmex.com] - Added support for
// IContextMenu2 as used by the windows shell for owner draw menus
// ==========================================================================
//
/////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "UICoolMenu.h"
#include <afxpriv.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define safe_delete(p){if(p){delete p;p=NULL;}}
#define USES_ICON -2
CCoolMenuManager g_CoolMenuManager;
// helpers
void PLFillRect(CDC& dc, const CRect& rc, COLORREF color);
void PLDrawEmbossed(CDC& dc, CImageList& il, int i,
CPoint p, BOOL bColor=FALSE);
HBITMAP PLLoadSysColorBitmap(LPCTSTR lpResName, BOOL bMono=FALSE);
inline HBITMAP PLLoadSysColorBitmap(UINT nResID, BOOL bMono=FALSE) {
return PLLoadSysColorBitmap(MAKEINTRESOURCE(nResID), bMono);
}
// 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(_T('m'),_T('i')),MAKEWORD(_T('d'),_T('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 (or USES_ICON)
HICON hIcon;
void * pContext; // item data
LPCONTEXTMENU lpcm; // if set this will handle menu messages (windows shell)
CMyItemData() { lpcm=NULL; magicNum = MYITEMID; iButton = -1; hIcon = 0; pContext = NULL; }
~CMyItemData() { text.Empty(); if (hIcon) ::DestroyIcon(hIcon); if (lpcm) lpcm->Release(); }
BOOL IsMyItemData() { return this != NULL && magicNum == MYITEMID; }
};
static CMyItemData *GetMyItemData(HMENU hMenu, UINT uItem,
BOOL bByPosition = FALSE)
{
MENUITEMINFO mii;
ZeroMemory(&mii,sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_DATA;
::GetMenuItemInfo(hMenu, uItem, bByPosition, &mii);
return (CMyItemData *)mii.dwItemData;
}
static void SetMyItemData(CMyItemData *pmd, HMENU hMenu, UINT uItem,
BOOL bByPosition = FALSE)
{
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_DATA;
mii.dwItemData = (DWORD)pmd;
::SetMenuItemInfo(hMenu, uItem, bByPosition, &mii);
}
IMPLEMENT_DYNAMIC(CCoolMenuManager, CSubclassWnd)
CCoolMenuManager::CCoolMenuManager()
{
m_lpcm = NULL;
m_szBitmap = CSize(0,0); // will compute later
m_szButton = CSize(0,0);
m_bShowButtons = TRUE; // show buttons by default
m_bAutoAccel = TRUE; // auto accelerators by default
m_pAccel = NULL; // no accelerators loaded yet
m_bUseDrawState = FALSE; // use DrawEmbossed by default
m_bDrawDisabledButtonsInColor = FALSE; // use color for disabled buttons
FixMFCDotBitmap();
}
CCoolMenuManager::~CCoolMenuManager()
{
// Jeremy Horgan [jeremyhorgan@hotmail.com]
while (!m_menuList.IsEmpty())
{
UnconvertMenu(CMenu::FromHandle((HMENU)m_menuList.RemoveHead()));
}
Destroy();
}
//////////////////
// Destroy everything. Called from destructor and Refresh.
//
void CCoolMenuManager::Destroy()
{
m_ilButtons.DeleteImageList();
m_ilTemp.DeleteImageList();
m_mapIDtoImage.RemoveAll();
m_mapHMENUtoID.RemoveAll();
m_szBitmap = m_szButton = CSize(0,0);
m_arToolbarID.RemoveAll();
m_fontMenu.DeleteObject();
DestroyAccel();
}
/////////////////
// Destroy accelerators
//
void CCoolMenuManager::DestroyAccel()
{
m_mapIDtoAccel.RemoveAll(); // delete ACCEL entries in map
safe_delete(m_pAccel); // delete current accelerators
}
//////////////////
// Call this to install the menu manager. Install(NULL) to un-install.
//
void CCoolMenuManager::Install(CWnd* pFrame)
{
m_pFrame = pFrame;
HookWindow(pFrame); // install message hook
}
void CCoolMenuManager::Uninstall()
{
m_pFrame = NULL;
HookWindow((HWND)NULL);
}
// Addition: Phliip Oldaker
// call this to enable help text on the status bar
void CCoolMenuManager::SetShellContextMenu(LPCONTEXTMENU lpcm,UINT nIDFirst,UINT nIDLast)
{
m_lpcm = lpcm;
m_idShellMenuFirst = nIDFirst;
m_idShellMenuLast = nIDLast;
}
//////////////////
// 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
};
// Addition: Philip Oldaker
CSize CCoolMenuManager::SetButtonSize(CSize &sz)
{
// Set defaults
if (sz.cx == 0)
sz.cx = 16;
if (sz.cy == 0)
sz.cy = 15;
CSize szOld(m_szBitmap);
m_szBitmap = sz;
m_szButton = sz + CSize(CXBUTTONMARGIN<<1, CYBUTTONMARGIN<<1);
return szOld;
}
//////////////////
// 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 = PLLoadSysColorBitmap(nIDToolbar);
if (!hbmToolbar) {
TRACE(_T("*** Can't load bitmap for toolbar %d!\n"), nIDToolbar);
return FALSE;
}
// 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_COLOR32|ILC_MASK, 0, 10));
VERIFY(m_ilTemp. Create(sz.cx, sz.cy, ILC_COLOR32|ILC_MASK, 0, 10));
} else if (m_szBitmap != sz) {
// button sizes different -- oops
TRACE(_T("*** Toolbar %d button size differs!\n"), nIDToolbar);
return FALSE;
}
m_ilButtons.SetBkColor(::GetSysColor(COLOR_3DFACE));
// 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(CBitmap::FromHandle(hbmToolbar), GetSysColor(COLOR_3DFACE));
for (int i = 0; i < ptbd->wItemCount; i++) {
UINT 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);
TRACE(_T("CCoolMenuManager::LoadToolbar(). Added Menu Id %d, Button Number %d\n"), nID, iNextImage-1);
}
// AB. 13-08-98. Since we've already added the entire toolbar to the imagelist
// we need to increment nNextImage even if we didn't add this button to
// m_mapIDtoImage.
iNextImage++;
}
}
m_arToolbarID.Add(nIDToolbar); // remember toolbar ID for Refresh
::DeleteObject(hbmToolbar);
return TRUE; // success!
}
//////////////////
// Virtual CCoolMenuSubclassWnd 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)*/0, (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();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -