shortcutmanager.cpp
来自「管理项目进度工具的原代码」· C++ 代码 · 共 580 行
CPP
580 行
// ShortcutManager.cpp: implementation of the CShortcutManager class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ShortcutManager.h"
#include "wclassdefines.h"
#include "runtimedlg.h"
#include "winclasses.h"
#include <afxtempl.h>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
enum
{
VK_0 = 0x30,
VK_9 = 0x39,
VK_A = 0x41,
VK_Z = 0x5A,
};
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CShortcutManager::CShortcutManager(BOOL bAutoSendCmds)
: m_bAutoSendCmds(bAutoSendCmds), m_wInvalidComb(0), m_wFallbackModifiers(0)
{
}
CShortcutManager::~CShortcutManager()
{
}
BOOL CShortcutManager::Initialize(CWnd* pOwner, WORD wInvalidComb, WORD wFallbackModifiers)
{
if (wInvalidComb && !IsHooked())
{
if (pOwner && HookWindow(*pOwner))
{
m_wInvalidComb = wInvalidComb;
m_wFallbackModifiers = wFallbackModifiers;
LoadSettings();
return TRUE;
}
}
return FALSE;
}
BOOL CShortcutManager::Release()
{
if (!IsHooked())
return TRUE;
return HookWindow(NULL);
}
void CShortcutManager::SetShortcut(UINT nCmdID, WORD wVirtKeyCode, WORD wModifiers)
{
UINT nOtherCmdID = 0;
DWORD dwShortcut = MAKELONG(wVirtKeyCode, wModifiers);
// if the shortcut == 0 then remove the existing shortcut associated with nCmdID
if (!dwShortcut)
{
m_mapID2Shortcut.Lookup(nCmdID, dwShortcut);
m_mapShortcut2ID.RemoveKey(dwShortcut);
// mark these commands explicitly as having no shortcut so that
// the user's intent is clear.
m_mapID2Shortcut[nCmdID] = NO_SHORTCUT;
return;
}
// check for existing cmds using this shortcut to remove
else if (m_mapShortcut2ID.Lookup(dwShortcut, nOtherCmdID))
{
m_mapShortcut2ID.RemoveKey(dwShortcut);
// mark these commands explicitly as having no shortcut so that
// they subsequently will not be overwritten
m_mapID2Shortcut[nOtherCmdID] = NO_SHORTCUT;
}
// then simple add
AddShortcut(nCmdID, wVirtKeyCode, wModifiers);
}
void CShortcutManager::SetShortcut(UINT nCmdID, DWORD dwShortcut)
{
SetShortcut(nCmdID, LOWORD(dwShortcut), HIWORD(dwShortcut));
}
BOOL CShortcutManager::AddShortcut(UINT nCmdID, DWORD dwShortcut)
{
return AddShortcut(nCmdID, LOWORD(dwShortcut), HIWORD(dwShortcut));
}
BOOL CShortcutManager::AddShortcut(UINT nCmdID, WORD wVirtKeyCode, WORD wModifiers)
{
// test for invalid modifiers
if (ValidateModifiers(wModifiers, wVirtKeyCode) != wModifiers)
return FALSE;
// check for existing cmds using this shortcut
DWORD dwShortcut = MAKELONG(wVirtKeyCode, wModifiers);
if (!nCmdID || !dwShortcut)
return FALSE;
UINT nOtherCmdID = 0;
if (m_mapShortcut2ID.Lookup(dwShortcut, nOtherCmdID) && nOtherCmdID)
return FALSE;
// check for existing shortcut on this cmd that we'll need to clean up
DWORD dwOtherShortcut = 0;
if (m_mapID2Shortcut.Lookup(nCmdID, dwOtherShortcut))
m_mapShortcut2ID.RemoveKey(dwOtherShortcut);
m_mapShortcut2ID[dwShortcut] = nCmdID;
m_mapID2Shortcut[nCmdID] = dwShortcut;
return TRUE;
}
WORD CShortcutManager::ValidateModifiers(WORD wModifiers, WORD wVirtKeyCode) const
{
if (!m_wInvalidComb) // optimization
return wModifiers;
// check for our special modifiers first
if ((m_wInvalidComb & HKCOMB_EXFKEYS) && (wVirtKeyCode >= VK_F1 && wVirtKeyCode <= VK_F24))
return wModifiers;
// test for invalid combinations
BOOL bCtrl = (wModifiers & HOTKEYF_CONTROL);
BOOL bShift = (wModifiers & HOTKEYF_SHIFT);
BOOL bAlt = (wModifiers & HOTKEYF_ALT);
BOOL bExtended = (wModifiers & HOTKEYF_EXT);
BOOL bFail = ((m_wInvalidComb & HKCOMB_NONE) && !bCtrl && !bShift && !bAlt);
bFail |= ((m_wInvalidComb & HKCOMB_S) && !bCtrl && bShift && !bAlt);
bFail |= ((m_wInvalidComb & HKCOMB_C) && bCtrl && !bShift && !bAlt);
bFail |= ((m_wInvalidComb & HKCOMB_A) && !bCtrl && !bShift && bAlt);
bFail |= ((m_wInvalidComb & HKCOMB_SC) && bCtrl && bShift && !bAlt);
bFail |= ((m_wInvalidComb & HKCOMB_SA) && !bCtrl && bShift && bAlt);
bFail |= ((m_wInvalidComb & HKCOMB_CA) && bCtrl && !bShift && bAlt);
bFail |= ((m_wInvalidComb & HKCOMB_SCA) && bCtrl && bShift && bAlt);
if (bFail)
return (WORD)(m_wFallbackModifiers | (bExtended ? HOTKEYF_EXT : 0x0));
// else ok
return wModifiers;
}
DWORD CShortcutManager::GetShortcut(WORD wVirtKeyCode, BOOL bExtended) const
{
BOOL bCtrl = (GetKeyState(VK_CONTROL) & 0x8000);
BOOL bShift = (GetKeyState(VK_SHIFT) & 0x8000);
BOOL bAlt = (GetKeyState(VK_MENU) & 0x8000);
WORD wModifiers = (WORD)((bCtrl ? HOTKEYF_CONTROL : 0) |
(bShift ? HOTKEYF_SHIFT : 0) |
(bAlt ? HOTKEYF_ALT : 0) |
(bExtended ? HOTKEYF_EXT : 0));
return MAKELONG(wVirtKeyCode, wModifiers);
}
UINT CShortcutManager::ProcessMessage(const MSG* pMsg, DWORD* pShortcut) const
{
// only process accelerators if we are enabled and visible
if (!IsWindowEnabled() || !IsWindowVisible())
return FALSE;
// we only process keypresses
if (pMsg->message != WM_KEYDOWN && pMsg->message != WM_SYSKEYDOWN)
return FALSE;
// also check that it's one of our children with the focus
// not another popup window
CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);
CWnd* pMainWnd = GetCWnd();
CWnd* pTopParent = pWnd->GetParentOwner();
if (pTopParent != pMainWnd)
return FALSE;
switch (pMsg->wParam)
{
case VK_CONTROL:
case VK_SHIFT:
case VK_MENU:
case VK_NUMLOCK:
case VK_SCROLL:
case VK_CAPITAL:
return FALSE;
// don't handle return/cancel keys
case VK_RETURN:
case VK_CANCEL:
return FALSE;
case VK_MBUTTON:
break;
// shortcut keys
default:
{
// don't process messages destined for hotkey controls!
if (CWinClasses::IsClass(pMsg->hwnd, WC_HOTKEY))
return FALSE;
// get DWORD shortcut
BOOL bExtKey = (pMsg->lParam & 0x01000000);
DWORD dwShortcut = GetShortcut((WORD)pMsg->wParam, bExtKey);
// look it up
UINT nCmdID = 0;
if (!m_mapShortcut2ID.Lookup(dwShortcut, nCmdID) || !nCmdID)
return FALSE;
// check if HKCOMB_EDITCTRLS is set and a edit has the focus
// and the shortcut clashes
if (m_wInvalidComb & HKCOMB_EDITCTRLS)
{
if (CWinClasses::IsEditControl(pMsg->hwnd))
{
// 1. check does not clash with edit shortcuts
if (IsEditShortcut(dwShortcut))
return FALSE;
//WORD wVirtKeyCode = LOWORD(dwShortcut);
WORD wModifiers = HIWORD(dwShortcut);
// 2. can be a function key
if (pMsg->wParam >= VK_F1 && pMsg->wParam <= VK_F24)
{
// ok
}
// 3. else must have <ctrl> or <alt>
else if (!(wModifiers & (HOTKEYF_ALT | HOTKEYF_CONTROL)))
return FALSE;
}
}
// return command ID
if (m_bAutoSendCmds)
SendMessage(WM_COMMAND, nCmdID);
if (pShortcut)
*pShortcut = dwShortcut;
return nCmdID;
}
}
return FALSE;
}
BOOL CShortcutManager::IsEditShortcut(DWORD dwShortcut)
{
switch (dwShortcut)
{
case MAKELONG('C', HOTKEYF_CONTROL): // copy
case MAKELONG('V', HOTKEYF_CONTROL): // paste
case MAKELONG('X', HOTKEYF_CONTROL): // cut
case MAKELONG('Z', HOTKEYF_CONTROL): // undo
case MAKELONG(VK_LEFT, HOTKEYF_CONTROL | HOTKEYF_EXT): // left one word
case MAKELONG(VK_RIGHT, HOTKEYF_CONTROL | HOTKEYF_EXT): // right one word
case MAKELONG(VK_DELETE, 0):
return TRUE;
}
// all else
return FALSE;
}
LRESULT CShortcutManager::WindowProc(HWND hRealWnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
case WM_INITMENUPOPUP:
{
// default processing so all text changes
// are complete before we have a go
LRESULT lr = Default();
PrepareMenuItems(CMenu::FromHandle((HMENU)wp));
return lr;
}
break;
case WM_DESTROY:
{
// must call rest of chain first
LRESULT lr = CSubclassWnd::WindowProc(hRealWnd, msg, wp, lp);
HookWindow(NULL);
SaveSettings();
return lr;
}
break;
}
return CSubclassWnd::WindowProc(hRealWnd, msg, wp, lp);
}
void CShortcutManager::PrepareMenuItems(CMenu* pMenu) const
{
if (!pMenu || !pMenu->GetSafeHmenu())
return;
// we iterate all the menu items
// if we find a match we get the menu text and add the shortcut
// first removing any existing one
int nItem = pMenu->GetMenuItemCount();
while (nItem--)
{
UINT nCmdID = pMenu->GetMenuItemID(nItem);
DWORD dwShortcut = GetShortcut(nCmdID);
if (!nCmdID || nCmdID == (UINT)-1)
continue;
// note: we must handle items without shortcuts as well
// as ones with
// get the menu text
MENUITEMINFO minfo;
minfo.cbSize = sizeof(minfo);
minfo.fMask = MIIM_FTYPE | MIIM_STRING;
minfo.fType = MFT_STRING;
minfo.dwTypeData = NULL;
minfo.cch = 0;
::GetMenuItemInfo(pMenu->GetSafeHmenu(), nItem, TRUE, &minfo);
CString sMenuText;
if (!minfo.cch)
continue; // ??
minfo.cch++;
minfo.dwTypeData = sMenuText.GetBuffer(minfo.cch);
::GetMenuItemInfo(pMenu->GetSafeHmenu(), nItem, TRUE, &minfo);
sMenuText.ReleaseBuffer();
// look for '\t' indicating existing hint
int nTab = sMenuText.Find('\t');
// remove it
if (nTab >= 0)
sMenuText = sMenuText.Left(nTab);
// else if it didn't have one and it has no shortcut then continue
else if (!dwShortcut)
continue;
// add new hint
CString sShortcut = GetShortcutText(dwShortcut);
if (!sShortcut.IsEmpty())
{
sMenuText += '\t';
sMenuText += sShortcut;
}
// update menu item text
minfo.dwTypeData = (LPSTR)(LPCTSTR)sMenuText;
::SetMenuItemInfo(pMenu->GetSafeHmenu(), nItem, TRUE, &minfo);
}
}
int CShortcutManager::BuildMapping(UINT nMenuID, CStringArray& aMapping, char cDelim)
{
CMenu menu;
if (!menu.LoadMenu(nMenuID) || !menu.GetMenuItemCount())
return 0;
return BuildMapping(&menu, NULL, aMapping, cDelim);
}
int CShortcutManager::BuildMapping(CMenu* pMenu, LPCTSTR szParentName,
CStringArray& aMapping, char cDelim)
{
int nItems = pMenu->GetMenuItemCount();
for (int nItem = 0; nItem < nItems; nItem++)
{
CString sMenuText, sItemText;
pMenu->GetMenuString(nItem, sMenuText, MF_BYPOSITION);
if (szParentName && *szParentName)
sItemText.Format("%s > %s", szParentName, sMenuText);
else
sItemText = sMenuText;
UINT nCmdID = pMenu->GetMenuItemID(nItem);
if (nCmdID == (UINT)-1) // sub menu
{
CMenu* pSubMenu = pMenu->GetSubMenu(nItem);
BuildMapping(pSubMenu, sItemText, aMapping, cDelim);
}
else
{
DWORD dwShortcut = GetShortcut(nCmdID);
if (dwShortcut)
{
CString sShortcut = GetShortcutText(dwShortcut), sItem;
sItem.Format("%s%c%s", sShortcut, cDelim, sItemText);
// remove single '&'
// rather painful way to do it to avoid replacing '&&'
sItem.Replace("&&", "~~");
sItem.Remove('&');
sItem.Replace("~~", "&&");
aMapping.Add(sItem);
}
}
}
// add a space between sections unless already added
if (aMapping.GetSize() && !aMapping[aMapping.GetSize() - 1].IsEmpty())
aMapping.Add("");
return aMapping.GetSize();
}
DWORD CShortcutManager::GetShortcut(UINT nCmdID) const
{
DWORD dwShortcut = 0;
m_mapID2Shortcut.Lookup(nCmdID, dwShortcut);
return (dwShortcut == NO_SHORTCUT) ? 0 : dwShortcut;
}
void CShortcutManager::DeleteShortcut(UINT nCmdID)
{
DWORD dwShortcut = 0;
if (m_mapID2Shortcut.Lookup(nCmdID, dwShortcut))
{
m_mapID2Shortcut.RemoveKey(nCmdID);
// remove reverse mapping too
m_mapShortcut2ID.RemoveKey(dwShortcut);
}
}
CString CShortcutManager::GetShortcutTextByCmd(UINT nCmdID)
{
return GetShortcutText(GetShortcut(nCmdID));
}
CString CShortcutManager::GetShortcutText(DWORD dwShortcut)
{
if (!dwShortcut || dwShortcut == NO_SHORTCUT)
return "";
CString sText;
WORD wVirtKeyCode = LOWORD(dwShortcut);
WORD wModifiers = HIWORD(dwShortcut);
if (wModifiers & HOTKEYF_CONTROL)
{
sText += GetKeyName(VK_CONTROL);
sText += "+";
}
if (wModifiers & HOTKEYF_SHIFT)
{
sText += GetKeyName(VK_SHIFT);
sText += "+";
}
if (wModifiers & HOTKEYF_ALT)
{
sText += GetKeyName(VK_MENU);
sText += "+";
}
CString sKey = GetKeyName(wVirtKeyCode, (wModifiers & HOTKEYF_EXT));
if (!sKey.IsEmpty())
sText += sKey;
else
sText.Empty();
return sText;
}
CString CShortcutManager::GetKeyName(WORD wVirtKeyCode, BOOL bExtended)
{
const int KEYNAMELEN = 64;
static char szKeyName[64];
WORD wScanCode = (WORD)MapVirtualKey(wVirtKeyCode, 0);
// build lParam to send to GetKeyNameText
LPARAM lParam = (wScanCode * 0x00010000);
if (bExtended)
lParam += 0x01000000;
GetKeyNameText(lParam, szKeyName, KEYNAMELEN);
return CString(szKeyName);
}
void CShortcutManager::LoadSettings()
{
// load shortcuts overriding any defaults
int nItem = AfxGetApp()->GetProfileInt("KeyboardShortcuts", "NumItems", 0);
while (nItem--)
{
CString sKey;
sKey.Format("KeyboardShortcuts\\Item%02d", nItem);
UINT nCmdID = (UINT)AfxGetApp()->GetProfileInt(sKey, "CmdID", 0);
DWORD dwShortcut = (DWORD)AfxGetApp()->GetProfileInt(sKey, "Shortcut", 0);
if (nCmdID && dwShortcut)
SetShortcut(nCmdID, dwShortcut);
}
}
void CShortcutManager::SaveSettings()
{
AfxGetApp()->WriteProfileInt("KeyboardShortcuts", "NumItems", m_mapID2Shortcut.GetCount());
POSITION pos = m_mapID2Shortcut.GetStartPosition();
int nItem = 0;
while (pos)
{
UINT nCmdID = 0;
DWORD dwShortcut = 0;
m_mapID2Shortcut.GetNextAssoc(pos, nCmdID, dwShortcut);
if (nCmdID && dwShortcut)
{
CString sKey;
sKey.Format("KeyboardShortcuts\\Item%02d", nItem);
AfxGetApp()->WriteProfileInt(sKey, "CmdID", nCmdID);
AfxGetApp()->WriteProfileInt(sKey, "Shortcut", dwShortcut);
nItem++;
}
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?