// xkeymacsdll.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"
#include "resource.h"
#include "Utils.h"
#include "Commands.h"
#include 
#include 
#include 

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


static AFX_EXTENSION_MODULE XkeymacsdllDLL = { NULL, NULL };

static HINSTANCE g_hDllInst = NULL;

HINSTANCE GetThisHInst()
{
	return g_hDllInst;
}

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
	g_hDllInst = hInstance;
	
	// Remove this if you use lpReserved
	UNREFERENCED_PARAMETER(lpReserved);

	if (dwReason == DLL_PROCESS_ATTACH) {
		TRACE0("XKEYMACSDLL.DLL Initializing!\n");
		
		// Extension DLL one-time initialization
		if (!AfxInitExtensionModule(XkeymacsdllDLL, hInstance)) {
			return 0;
		}

		// Insert this DLL into the resource chain
		// NOTE: If this Extension DLL is being implicitly linked to by
		//  an MFC Regular DLL (such as an ActiveX Control)
		//  instead of an MFC application, then you will want to
		//  remove this line from DllMain and put it in a separate
		//  function exported from this Extension DLL.  The Regular DLL
		//  that uses this Extension DLL should then explicitly call that
		//  function to initialize this Extension DLL.  Otherwise,
		//  the CDynLinkLibrary object will not be attached to the
		//  Regular DLL's resource chain, and serious problems will
		//  result.

		new CDynLinkLibrary(XkeymacsdllDLL);
	} else if (dwReason == DLL_PROCESS_DETACH) {
		TRACE0("XKEYMACSDLL.DLL Terminating!\n");
		// Terminate the library before destructors are called
		AfxTermExtensionModule(XkeymacsdllDLL);
	}
	return 1;   // ok
}

//////////////////////////////////////////////////////////////////////
// CXkeymacsDll Class
//////////////////////////////////////////////////////////////////////

#include "xkeymacsDll.h"
#pragma data_seg(".xkmcs")
	HHOOK	CXkeymacsDll::m_hHookKeyboard = NULL;
	HHOOK	CXkeymacsDll::m_hHookCallWnd = NULL;
	HHOOK	CXkeymacsDll::m_hHookGetMessage = NULL;
	HHOOK	CXkeymacsDll::m_hHookShell = NULL;
	int		CXkeymacsDll::m_nCommandID[MAX_APP][MAX_COMMAND_TYPE][MAX_KEY] = {'\0'};
	int		CXkeymacsDll::m_nFunctionID[MAX_APP][MAX_COMMAND_TYPE][MAX_KEY] = {'\0'};
	char	CXkeymacsDll::m_szSpecialApp[MAX_APP][CLASS_NAME_LENGTH] = {'\0'};
	BOOL	CXkeymacsDll::m_bRightControl	= FALSE;
	BOOL	CXkeymacsDll::m_bRightAlt		= FALSE;
	BOOL	CXkeymacsDll::m_bRightShift		= FALSE;
	BOOL	CXkeymacsDll::m_bHook			= TRUE;
	BOOL	CXkeymacsDll::m_bDefiningMacro	= FALSE;
	DWORD	CXkeymacsDll::m_dwOldMessage[MAX_ICON_TYPE] = {'\0'};
	NOTIFYICONDATA CXkeymacsDll::m_stNtfyIcon[MAX_ICON_TYPE] = {'\0'};
	NOTIFYICONDATA CXkeymacsDll::m_stOldNtfyIcon[MAX_ICON_TYPE] = {'\0'};
	HICON	CXkeymacsDll::m_hIcon[MAX_ICON_TYPE][MAX_STATUS] = {'\0'};
	BOOL	CXkeymacsDll::m_bIcon[MAX_ICON_TYPE] = {'\0'};
	int		CXkeymacsDll::m_nKillRingMax[MAX_APP] = {'\0'};
	BOOL	CXkeymacsDll::m_bUseDialogSetting[MAX_APP] = {'\0'};
	CList CXkeymacsDll::m_oKillRing;
	CObList CXkeymacsDll::m_Macro;
	int		CXkeymacsDll::m_nKillRing = 0;
	int		CXkeymacsDll::m_nOriginal[MAX_COMMAND_TYPE][MAX_KEY] = {'\0'};
	int		CXkeymacsDll::m_nApplicationID = 0;
	int		CXkeymacsDll::m_nSettingStyle[MAX_APP] = {'\0'};
	BOOL	CXkeymacsDll::m_bIgnoreUndefinedMetaCtrl[MAX_APP] = {'\0'};
	BOOL	CXkeymacsDll::m_bIgnoreUndefinedC_x[MAX_APP] = {'\0'};
//	int		CXkeymacsDll::m_nPassThrough = 0;
	char	CXkeymacsDll::m_szApplicationName[MAX_PATH] = {'\0'};
	char	CXkeymacsDll::m_szOldApplicationName[MAX_PATH] = {'\0'};
	BOOL	CXkeymacsDll::m_bEnableCUA[MAX_APP] = {'\0'};
	char	CXkeymacsDll::m_szFunctionDefinition[MAX_FUNCTION][MAX_DEFINITION] = {'\0'};

	BOOL	CXkeymacsData::m_b106Keyboard = FALSE;
#pragma data_seg()

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CXkeymacsDll::CXkeymacsDll()
{
	CUtils::InitCUtils();
}

CXkeymacsDll::~CXkeymacsDll()
{

}

int CXkeymacsDll::ModifyShell_NotifyIcon(ICON_TYPE icon, BOOL bNewStatus)
{
	if (m_stNtfyIcon[icon].hIcon == (bNewStatus ? m_hIcon[icon][ON_ICON] : m_hIcon[icon][OFF_ICON])) {
		return TRUE;
	} else {
		m_stNtfyIcon[icon].hIcon = (bNewStatus ? m_hIcon[icon][ON_ICON] : m_hIcon[icon][OFF_ICON]);
	}
	return DoShell_NotifyIcon(icon, NIM_MODIFY);
}

void CXkeymacsDll::SetNotifyIconData(ICON_TYPE icon, NOTIFYICONDATA stNtfyIcon, HICON hEnable, HICON hDisableTMP, HICON hDisableWOCQ, HICON hDisable, BOOL bEnable)
{
	m_hIcon[icon][STATUS_ENABLE] = hEnable;
	m_hIcon[icon][STATUS_DISABLE_TMP] = hDisableTMP;
	m_hIcon[icon][STATUS_DISABLE_WOCQ] = hDisableWOCQ;
	m_hIcon[icon][STATUS_DISABLE] = hDisable;
	m_bIcon[icon] = bEnable;
	m_stNtfyIcon[icon] = stNtfyIcon;
	AddShell_NotifyIcon(icon);
}

void CXkeymacsDll::SetNotifyIconData(ICON_TYPE icon, NOTIFYICONDATA stNtfyIcon, HICON hOn, HICON hOff, BOOL bEnable)
{
	m_hIcon[icon][ON_ICON] = hOn;
	m_hIcon[icon][OFF_ICON] = hOff;
	m_bIcon[icon] = bEnable;
	m_stNtfyIcon[icon] = stNtfyIcon;
	AddShell_NotifyIcon(icon);
}

void CXkeymacsDll::EnableShell_NotifyIcon(ICON_TYPE icon, BOOL bEnable)
{
	DeleteShell_NotifyIcon(icon);
	m_bIcon[icon] = bEnable;
	AddShell_NotifyIcon(icon);
}


BOOL CXkeymacsDll::DoShell_NotifyIcon(ICON_TYPE icon, DWORD dwMessage)
{
	if (m_bIcon[icon]
	 &&	(m_dwOldMessage[icon] != dwMessage
	  || memcmp(&m_stOldNtfyIcon[icon], &m_stNtfyIcon[icon], sizeof(m_stNtfyIcon[icon])))) {
		m_dwOldMessage[icon] = dwMessage;
		m_stOldNtfyIcon[icon] = m_stNtfyIcon[icon];
		return ::Shell_NotifyIcon(dwMessage, &m_stNtfyIcon[icon]);
	} else {
		return TRUE;
	}
}

void CXkeymacsDll::AddShell_NotifyIcon(ICON_TYPE icon)
{
	DoShell_NotifyIcon(icon, NIM_ADD);
}

void CXkeymacsDll::DeleteShell_NotifyIcon(ICON_TYPE icon)
{
	DoShell_NotifyIcon(icon, NIM_DELETE);
}

// set keyboard hook
BOOL CXkeymacsDll::SetKeyboardHook()
{
	m_hHookKeyboard = ::SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardProc, GetThisHInst(), 0);
	if (!m_hHookKeyboard) {
		return FALSE;
	}

	m_hHookCallWnd = ::SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)CallWndProc, GetThisHInst(), 0);

	m_hHookGetMessage = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, GetThisHInst(), 0);

	m_hHookShell = ::SetWindowsHookEx(WH_SHELL, (HOOKPROC)ShellProc, GetThisHInst(), 0);

	AddShell_NotifyIcon(MAIN_ICON);

	return TRUE;
}

// release keyboard hook
BOOL CXkeymacsDll::ReleaseKeyboardHook()
{
	BOOL bKeyboard = TRUE;
	BOOL bCallWnd = TRUE;
	BOOL bGetMessage = TRUE;
	BOOL bShell = TRUE;

	if (m_hHookKeyboard) {
		bKeyboard = ::UnhookWindowsHookEx(m_hHookKeyboard);
	}
	m_hHookKeyboard = NULL;

	if (m_hHookCallWnd) {
		bCallWnd = ::UnhookWindowsHookEx(m_hHookCallWnd);
	}
	m_hHookCallWnd = NULL;

	if (m_hHookGetMessage) {
		bGetMessage = ::UnhookWindowsHookEx(m_hHookGetMessage);
	}
	m_hHookGetMessage = NULL;

	if (m_hHookShell) {
		bShell = ::UnhookWindowsHookEx(m_hHookShell);
	}
	m_hHookShell = NULL;

	return bKeyboard && bCallWnd && bGetMessage && bShell;
}

void CXkeymacsDll::SetKeyboardHookFlag(BOOL bFlag)
{
	m_bHook = bFlag;

	if (m_bHook) {
		if (CCommands::IsTemporarilyDisableXKeymacs()) {
			m_stNtfyIcon[MAIN_ICON].hIcon = m_hIcon[MAIN_ICON][STATUS_DISABLE_TMP];
		} else {
			m_stNtfyIcon[MAIN_ICON].hIcon = m_hIcon[MAIN_ICON][STATUS_ENABLE];
		}
	} else {
		m_stNtfyIcon[MAIN_ICON].hIcon = m_hIcon[MAIN_ICON][STATUS_DISABLE_WOCQ];
	}
	if (m_nSettingStyle[m_nApplicationID] == SETTING_DISABLE
	 || (!stricmp(m_szSpecialApp[m_nApplicationID], "Default")
	  && CUtils::IsDefaultIgnoreApplication())) {
		m_stNtfyIcon[MAIN_ICON].hIcon = m_hIcon[MAIN_ICON][STATUS_DISABLE];
	}
	DoShell_NotifyIcon(MAIN_ICON, NIM_MODIFY);
}

// if be keyboard hook, return TRUE
BOOL CXkeymacsDll::IsKeyboardHook()
{
	if (m_bHook) {
		return TRUE;
	}

	return FALSE;
}

LRESULT CALLBACK CXkeymacsDll::CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if (0 <= nCode) {
		CWPSTRUCT &cwps = *(CWPSTRUCT *)lParam;
		switch (cwps.message) {
		case WM_IME_STARTCOMPOSITION:
			InitKeyboardProc(TRUE);
			break;
		case WM_IME_ENDCOMPOSITION:
			InitKeyboardProc(FALSE);
			break;
		case WM_SETFOCUS:
			if (cwps.hwnd == GetForegroundWindow()) {
				InitKeyboardProc(FALSE);
				SetKeyboardHookFlag(m_bHook);
			}
			break;
		case WM_NCACTIVATE:
			if (cwps.wParam) {
				if (cwps.hwnd == GetForegroundWindow()) {
					InitKeyboardProc(FALSE);
					SetKeyboardHookFlag(m_bHook);
				}
			}
			break;
		default:
			break;
		}
	}
	return CallNextHookEx(m_hHookCallWnd, nCode, wParam, lParam);
}

LRESULT CALLBACK CXkeymacsDll::GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	MSG &msg = (*(MSG *)lParam);

	switch (msg.message) {
	case WM_IME_STARTCOMPOSITION:
		InitKeyboardProc(TRUE);
		break;
	case WM_IME_ENDCOMPOSITION:
		InitKeyboardProc(FALSE);
		break;
	}
	return CallNextHookEx(m_hHookGetMessage, nCode, wParam, lParam);
}

LRESULT CALLBACK CXkeymacsDll::ShellProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	switch (nCode) {
	case HSHELL_WINDOWACTIVATED:
	{
		TCHAR className[256];
		::GetClassName((HWND)wParam, className, 255);
		if (!stricmp(className, "ConsoleWindowClass")) {
			InitKeyboardProc(FALSE);
			SetKeyboardHookFlag(m_bHook);
		}
		break;
	}
	default:
		break;
	}
	return CallNextHookEx( m_hHookShell, nCode, wParam, lParam );
}

// return true if the key is down
BOOL CXkeymacsDll::IsDown(BYTE bVk, BOOL bPhysicalKey)
{
	if (bPhysicalKey) {
		return GetAsyncKeyState(bVk) < 0;
	} else {
		return GetKeyState(bVk) < 0;
	}
}

// Do keybd_event
void CXkeymacsDll::DoKeybd_event(BYTE bVk, DWORD dwFlags)
{
	// Set KEYEVENTF_EXTENDEDKEY if needed
	switch (bVk) {
	case VK_CONTROL:
		if (m_bRightControl) {		// Right Ctrl
			dwFlags |= KEYEVENTF_EXTENDEDKEY;
		}
		break;

	case VK_MENU:
		if (m_bRightAlt) {			// Right Alt
			dwFlags |= KEYEVENTF_EXTENDEDKEY;
		}
		break;

	case VK_SHIFT:
		if (m_bRightShift) {		// Right Shift
			dwFlags |= KEYEVENTF_EXTENDEDKEY;
		}
		break;

	case VK_PAUSE:
		if (IsDown(VK_CONTROL, FALSE)) {	// Break
			dwFlags |= KEYEVENTF_EXTENDEDKEY;
		}
		break;

	case VK_INSERT:
	case VK_DELETE:
	case VK_HOME:
	case VK_END:
	case VK_NEXT:
	case VK_PRIOR:
	case VK_UP:
	case VK_DOWN:
	case VK_RIGHT:
	case VK_LEFT:
	case VK_NUMLOCK:
	case VK_PRINT:
		dwFlags |= KEYEVENTF_EXTENDEDKEY;
		break;

	default:
		break;
	}

	keybd_event(bVk, 0, dwFlags, GetMessageExtraInfo());
}

// the key is being depressed
void CXkeymacsDll::DepressKey(BYTE bVk, BOOL bOriginal)	// bVk is virtual-key code, MSDN said
{
	if (bOriginal) {
		int nCommandType = NONE;
		if (IsDown(VK_CONTROL)) {
			nCommandType |= CONTROL;
		}
		if (IsDown(VK_MENU)) {
			nCommandType |= META;
		}
		Original(nCommandType, bVk, 1);
	}

	DoKeybd_event(bVk, 0);
}

// the key is being released
void CXkeymacsDll::ReleaseKey(BYTE bVk)	// bVk is virtual-key code, MSDN said
{
	DoKeybd_event(bVk, KEYEVENTF_KEYUP);
}

// bVk down, bVk up
void CXkeymacsDll::Kdu(BYTE bVk, DWORD n, BOOL bOriginal)
{
	while (n--) {
		DepressKey(bVk, bOriginal);
		ReleaseKey(bVk);
	}
}

void CXkeymacsDll::InitKeyboardProc(BOOL bImeComposition)
{
	if (CUtils::IsFindDialog()) {
		static BOOL bImeCompositionOld = FALSE;
		if (!bImeComposition
		 && bImeCompositionOld) {
			DepressKey(VK_END);
			ReleaseKey(VK_END);
		}
		bImeCompositionOld = bImeComposition;
	}

	CUtils::SetApplicationName(bImeComposition);

	if (strnicmp(m_szSpecialApp[m_nApplicationID], CUtils::GetApplicationName(), 0xF)) {	// PROCESSENTRY32 has only 0xF bytes of Name
		for (m_nApplicationID = 0; m_nApplicationID < MAX_APP; ++m_nApplicationID) {
			if (!strnicmp(m_szSpecialApp[m_nApplicationID], CUtils::GetApplicationName(), 0xF)) {	// PROCESSENTRY32 has only 0xF bytes of Name
				break;
			}
		}
		if (m_nApplicationID == MAX_APP) {
			for (m_nApplicationID = 0; m_nApplicationID < MAX_APP; ++m_nApplicationID) {
				if (!stricmp(m_szSpecialApp[m_nApplicationID], "Default")) {
					break;
				}
			}
			if (m_nApplicationID == MAX_APP) {
				m_nApplicationID = 0;
			}
		}
	}

	if (m_nSettingStyle[m_nApplicationID] != SETTING_DISABLE
	 && (stricmp(m_szSpecialApp[m_nApplicationID], "Default") || !CUtils::IsDefaultIgnoreApplication())
	 && !bImeComposition
	 && CUtils::IsDialog()) {
		// Use Dialog Setting
		if (m_bUseDialogSetting[m_nApplicationID]) {
			int nOriginalApplicationID = m_nApplicationID;
			for (m_nApplicationID = 0; m_nApplicationID < MAX_APP; ++m_nApplicationID) {
				if (!stricmp(m_szSpecialApp[m_nApplicationID], "Dialog")) {
					break;
				}
			}
			if (m_nApplicationID == MAX_APP) {
				m_nApplicationID = nOriginalApplicationID;
			}
		}
	}

	ModifyShell_NotifyIcon(CX_ICON, FALSE);
	ModifyShell_NotifyIcon(META_ICON, FALSE);
	CCommands::SetMark(FALSE);
	CCommands::SetTemporarilyDisableXKeymacs(FALSE);
	CCommands::Reset();
}

// emulate emacs	// cf virtual-key code
LRESULT CALLBACK CXkeymacsDll::KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	ASSERT(0 <= wParam && wParam <= UCHAR_MAX);

	int nCommandType = NONE;
	BYTE nKey = (BYTE)wParam;

	static BOOL bLocked = FALSE;
	static const BYTE RECURSIVE_KEY = 0x07;
	static int (*fCommand)() = NULL;
	static BYTE nOneShotModifier[MAX_KEY] = {'\0'};
	static BOOL bCherryOneShotModifier = FALSE;

//	CUtils::Log("nCode = %#x, nKey = %#x, lParam = %#x", nCode, nKey, lParam);

	if (nCode < 0 || nCode == HC_NOREMOVE) {
		goto DO_NOTHING;
	}

	if (nKey == RECURSIVE_KEY) {
		if (lParam & BEING_RELEASED) {
			goto HOOK_RECURSIVE_KEY;
		} else {
			goto RECURSIVE_COMMAND;
		}
	}

	{
		static BOOL bShift = FALSE;
		if (IsDepressedShiftKeyOnly(nKey)) {
			if (lParam & BEING_RELEASED) {
				if (bShift) {
					CCommands::SetMark(FALSE);
				}
			} else {
				bShift = TRUE;
			}
		} else {
			bShift = FALSE;
		}
	}

	if (CUtils::IsNT()) {
		switch (nKey) {
		case VK_CONTROL:
			if (lParam & EXTENDED_KEY) {
				nKey = VK_RCONTROL;
			} else {
				nKey = VK_LCONTROL;
			}
			break;
		case VK_MENU:
			if (lParam & EXTENDED_KEY) {
				nKey = VK_RMENU;
			} else {
				nKey = VK_LMENU;
			}
			break;
		case VK_SHIFT:
			if (lParam & EXTENDED_KEY) {
				nKey = VK_RSHIFT;
			} else {
				nKey = VK_LSHIFT;
			}
			break;
		default:
			break;
		}
	}

	if (lParam & BEING_RELEASED) {
		if (nKey == VK_LWIN
		 || nKey == VK_RWIN
		 || nKey == VK_APPS) {
			for (int i = 0; i < MAX_COMMAND_TYPE; ++i) {
				if (Commands[m_nCommandID[m_nApplicationID][i][nKey]].fCommand) {
					goto HOOK;
				}
			}
		}

		if (nOneShotModifier[nKey]) {
			ReleaseKey(nOneShotModifier[nKey]);
			nOneShotModifier[nKey] = 0;

			if (bCherryOneShotModifier) {
				bCherryOneShotModifier = FALSE;
				Kdu(nKey);
			}
		}

		goto DO_NOTHING;
	}

	if (m_nSettingStyle[m_nApplicationID] == SETTING_DISABLE) {
		goto DO_NOTHING;
	}

	// Do Nothing for Meadow, Mule for Win32, ... if those use default setting.
	if (!stricmp(m_szSpecialApp[m_nApplicationID], "Default")
	 && CUtils::IsDefaultIgnoreApplication()) {
		goto DO_NOTHING;
	}

	switch (IsPassThrough(nKey)) {
	case GOTO_DO_NOTHING:
		goto DO_NOTHING;
	case GOTO_HOOK:
		goto HOOK;
	case CONTINUE:
		break;
	default:
		ASSERT(0);
		break;
	}

	// set command type
	{
		nCommandType = NONE;
		if (IsDown(VK_SHIFT)) {
			nCommandType |= SHIFT;
		}
		if (IsControl()) {
			nCommandType |= CONTROL;
		}
		if (IsMeta()) {
			nCommandType |= META;
		}
		if (CCommands::bC_x()) {
			nCommandType |= CONTROLX;
		}

		// Ignore undefined C-x ?
		if (nCommandType & CONTROLX) {
			if (Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand == NULL
			 && m_nFunctionID[m_nApplicationID][nCommandType][nKey] < 0) {
				if (m_bIgnoreUndefinedC_x[m_nApplicationID]) {
					CCommands::Reset(GOTO_HOOK);
					goto HOOK;
				}
				nCommandType &= ~CONTROLX;
			}
		}

		// Ignore undefined Meta Ctrl+?
		if (CCommands::bM_() && (nCommandType & CONTROL)) {
			if (Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand == NULL
			 && m_nFunctionID[m_nApplicationID][nCommandType][nKey] < 0) {
				if (m_bIgnoreUndefinedMetaCtrl[m_nApplicationID]) {
					if (Original(CONTROL, nKey)) {
						Original(CONTROL, nKey, -1);
						goto DO_NOTHING;
					}
					CCommands::Reset(GOTO_HOOK);
					goto HOOK;
				}
				nCommandType &= ~META;
			}
		}
	}

	{
		int nVirtualCommandType = NONE;
		if (IsDown(VK_CONTROL, FALSE)) {
			nVirtualCommandType |= CONTROL;
		}
		if (IsDown(VK_MENU, FALSE)) {
			nVirtualCommandType |= META;
		}
		if (Original(nVirtualCommandType, nKey)) {
			Original(nVirtualCommandType, nKey, -1);
			goto DO_NOTHING;
		}
	}

	if (Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand == CCommands::EnableOrDisableXKeymacs) {
		SetKeyboardHookFlag(!m_bHook);
		goto HOOK;
	}
	if (Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand == CCommands::EnableXKeymacs) {
		if (!m_bHook) {
			SetKeyboardHookFlag(!m_bHook);
		}
		goto HOOK;
	}
	if (Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand == CCommands::DisableXKeymacs) {
		if (m_bHook) {
			SetKeyboardHookFlag(!m_bHook);
		}
		goto HOOK;
	}
	if (!m_bHook) {
		goto DO_NOTHING;
	}

	if (CCommands::bC_u()) {
		if ((nCommandType == NONE) && ('0' <= nKey) && (nKey <= '9')) {
			CCommands::NumericArgument(nKey - '0');
			goto HOOK0_9;
		}
		if ((nCommandType == NONE) && (nKey == 0xBD)) {
			CCommands::NumericArgumentMinus();
			goto HOOK0_9;
		}
	}

	if (Commands[m_nCommandID[m_nApplicationID][nCommandType & ~CONTROL][nKey]].fCommand == CCommands::OneShotModifierCtrl) {
		nOneShotModifier[nKey] = VK_LCONTROL;
		DepressKey(nOneShotModifier[nKey]);
		bCherryOneShotModifier = TRUE;
		goto HOOK;
	} else if (Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand == CCommands::OneShotModifierCtrlRepeat) {
		nOneShotModifier[nKey] = VK_LCONTROL;
		DepressKey(nOneShotModifier[nKey]);
		bCherryOneShotModifier = TRUE;
		goto HOOK;
	} else if (Commands[m_nCommandID[m_nApplicationID][nCommandType & ~CONTROL][nKey]].fCommand == CCommands::OneShotModifierCtrlRepeat) {
		ReleaseKey(nOneShotModifier[nKey]);
		bCherryOneShotModifier = FALSE;
		Kdu(nKey);
		goto HOOK;
	} else if (Commands[m_nCommandID[m_nApplicationID][nCommandType & ~META][nKey]].fCommand == CCommands::OneShotModifierAlt) {
		nOneShotModifier[nKey] = VK_LMENU;
		DepressKey(nOneShotModifier[nKey]);
		bCherryOneShotModifier = TRUE;
		goto HOOK;
	} else if (Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand == CCommands::OneShotModifierAltRepeat) {
		nOneShotModifier[nKey] = VK_LMENU;
		DepressKey(nOneShotModifier[nKey]);
		bCherryOneShotModifier = TRUE;
		goto HOOK;
	} else if (Commands[m_nCommandID[m_nApplicationID][nCommandType & ~META][nKey]].fCommand == CCommands::OneShotModifierAltRepeat) {
		ReleaseKey(nOneShotModifier[nKey]);
		bCherryOneShotModifier = FALSE;
		Kdu(nKey);
		goto HOOK;
	} else if (Commands[m_nCommandID[m_nApplicationID][nCommandType & ~SHIFT][nKey]].fCommand == CCommands::OneShotModifierShift) {
		nOneShotModifier[nKey] = VK_SHIFT;
		DepressKey(nOneShotModifier[nKey]);
		bCherryOneShotModifier = TRUE;
		goto HOOK;
	} else if (Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand == CCommands::OneShotModifierShiftRepeat) {
		nOneShotModifier[nKey] = VK_SHIFT;
		DepressKey(nOneShotModifier[nKey]);
		bCherryOneShotModifier = TRUE;
		goto HOOK;
	} else if (Commands[m_nCommandID[m_nApplicationID][nCommandType & ~SHIFT][nKey]].fCommand == CCommands::OneShotModifierShiftRepeat) {
		ReleaseKey(nOneShotModifier[nKey]);
		bCherryOneShotModifier = FALSE;
		Kdu(nKey);
		goto HOOK;
	} else {
		for (int i = 0; i < MAX_KEY; ++i) {
			if (nOneShotModifier[i] == nKey) {
				break;
			}
		}
		if (i == MAX_KEY) {
			bCherryOneShotModifier = FALSE;
		}
	}

	if (!Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand) {
		if (0 <= m_nFunctionID[m_nApplicationID][nCommandType][nKey]
		 && m_nFunctionID[m_nApplicationID][nCommandType][nKey] < MAX_FUNCTION
		 && strlen(m_szFunctionDefinition[m_nFunctionID[m_nApplicationID][nCommandType][nKey]])) {
			CallFunction(m_nFunctionID[m_nApplicationID][nCommandType][nKey]);
			goto HOOK;
		}

		if (nKey == VK_CONTROL
		 || nKey == VK_LCONTROL
		 || nKey == VK_RCONTROL
		 || nKey == VK_MENU
		 || nKey == VK_LMENU
		 || nKey == VK_RMENU
		 || nKey == VK_SHIFT
		 || nKey == VK_LSHIFT
		 || nKey == VK_RSHIFT) {
			goto DO_NOTHING;
		}

		if (!(nCommandType & SHIFT)) {
			if (CCommands::IsSetMark()) {
				if (CCommands::MoveCaret(nKey, nCommandType & CONTROL) != CONTINUE) {
					CCommands::ClearNumericArgument();
					goto HOOK;
				}
				CCommands::SetMark(FALSE);
			}
		}

		if (1 < CCommands::GetNumericArgument()) {
			Kdu(nKey, CCommands::GetNumericArgument());
			CCommands::ClearNumericArgument();
			goto HOOK;
		}

		goto DO_NOTHING;
	}

	if (CCommands::IsTemporarilyDisableXKeymacs()
	 && Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand != CCommands::KeyboardQuit) {
		CCommands::SetTemporarilyDisableXKeymacs(FALSE);
		goto DO_NOTHING;
	}

	m_bRightControl	= IsDown(VK_RCONTROL);
	m_bRightAlt		= IsDown(VK_RMENU);
	m_bRightShift	= IsDown(VK_RSHIFT);

	if (!bLocked) {
		bLocked = TRUE;
		fCommand = Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand;
RECURSIVE_COMMAND:
		switch (fCommand()) {
		case GOTO_DO_NOTHING:
			bLocked = FALSE;
			goto DO_NOTHING;
		case GOTO_HOOK:
			bLocked = FALSE;
			goto HOOK;
		case GOTO_RECURSIVE:
			goto RECURSIVE;
		case GOTO_HOOKX:
			bLocked = FALSE;
			goto HOOKX;
		case GOTO_HOOK0_9:
			bLocked = FALSE;
			goto HOOK0_9;
		default:
			ASSERT(0);
			bLocked = FALSE;
			goto DO_NOTHING;
		}
	} else {
		goto HOOK_RECURSIVE_KEY;
	}

DO_NOTHING:
	ModifyShell_NotifyIcon(SHIFT_ICON, IsDown(VK_SHIFT, FALSE));
	ModifyShell_NotifyIcon(CTRL_ICON, IsControl());
	ModifyShell_NotifyIcon(ALT_ICON, IsDown(VK_MENU, FALSE));

	{
		static BOOL bDefiningMacro = FALSE;
		if (m_bDefiningMacro) {
			static BOOL bDown[MAX_KEY] = {'\0'};

			if (!bDefiningMacro) {
				while (m_Macro.GetHeadPosition()) {
					void *p = m_Macro.GetAt(m_Macro.GetHeadPosition());
					m_Macro.RemoveHead();
					delete p;
					p = NULL;
				}
				memset(bDown, 0, sizeof(bDown));
			}

			if ((!(lParam & BEING_RELEASED)) || bDown[wParam]) {
				KbdMacro_t *pKbdMacro = new KbdMacro;
				pKbdMacro->nCode = nCode;
				pKbdMacro->wParam = wParam;
				pKbdMacro->lParam = lParam;
				pKbdMacro->bOriginal = TRUE;
				m_Macro.AddTail((CObject *)pKbdMacro);
				if (!(lParam & BEING_RELEASED)) {
					bDown[wParam] = TRUE;
				}
			}
		}
		bDefiningMacro = m_bDefiningMacro;
	}

	return ::CallNextHookEx(m_hHookKeyboard, nCode, wParam, lParam);

RECURSIVE:
	Kdu(RECURSIVE_KEY, 1, FALSE);
	ModifyShell_NotifyIcon(SHIFT_ICON, IsDown(VK_SHIFT, FALSE));
	ModifyShell_NotifyIcon(CTRL_ICON, IsControl());
	ModifyShell_NotifyIcon(ALT_ICON, IsDown(VK_MENU, FALSE));
	return TRUE;

HOOK:
	CCommands::SetLastCommand(fCommand);
HOOK0_9:
HOOKX:
	ModifyShell_NotifyIcon(SHIFT_ICON, IsDown(VK_SHIFT, FALSE));
	ModifyShell_NotifyIcon(CTRL_ICON, IsControl());
	ModifyShell_NotifyIcon(ALT_ICON, IsDown(VK_MENU, FALSE));
HOOK_RECURSIVE_KEY:
	return TRUE;
}

//////////////////////////////////////////////////////////////////////
// CXkeymacsData Class
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CXkeymacsData::CXkeymacsData()
{
	ClearAll();
}

CXkeymacsData::~CXkeymacsData()
{

}

// set application name
void CXkeymacsData::SetApplicationName(LPCTSTR lpszApplicationName)
{
	m_strApplicationName.Format(lpszApplicationName);
}

// return application name
CString CXkeymacsData::GetApplicationName()
{
	return m_strApplicationName;
}

// set hook or not
void CXkeymacsData::SetCommandID(int nCommandType, int nKey, int nCommandID)
{
	ASSERT(0 <= nCommandType	|| nCommandType	< MAX_COMMAND_TYPE);
	ASSERT(0 <= nKey			|| nKey			< MAX_KEY);

	m_nCommandID[nCommandType][nKey] = nCommandID;
}

// return hook or not
int CXkeymacsData::GetCommandID(int nCommandType, int nKey)
{
	ASSERT(0 <= nCommandType	|| nCommandType	< MAX_COMMAND_TYPE);
	ASSERT(0 <= nKey			|| nKey			< MAX_KEY);

	return m_nCommandID[nCommandType][nKey];
}

// clear all data
void CXkeymacsData::ClearAll()
{
	ZeroMemory(m_nCommandID, sizeof(m_nCommandID));
	m_strApplicationName.Empty();
}

// set application name
void CXkeymacsDll::SetApplicationName(int nApplicationID, CString szApplicationName)
{
	ZeroMemory(m_szSpecialApp[nApplicationID], sizeof(m_szSpecialApp[nApplicationID]));
	strncpy(m_szSpecialApp[nApplicationID], szApplicationName, sizeof(m_szSpecialApp[nApplicationID]));
}

// set m_nCommandID[nApplicationID][nCommandType][nKey] nCommandID
void CXkeymacsDll::SetCommandID(int nApplicationID, int nCommandType, int nKey, int nCommandID)
{
	m_nCommandID[nApplicationID][nCommandType][nKey] = nCommandID;
}

void CXkeymacsDll::SetKillRingMax(int nApplicationID, int nKillRingMax)
{
	m_nKillRingMax[nApplicationID] = nKillRingMax;
}

void CXkeymacsDll::SetUseDialogSetting(int nApplicationID, BOOL bUseDialogSetting)
{
	m_bUseDialogSetting[nApplicationID] = bUseDialogSetting;
}

// Clear data of nApplicationID
void CXkeymacsDll::Clear(int nApplicationID)
{
	if (0 <= nApplicationID && nApplicationID < MAX_APP) {
		ZeroMemory(m_szSpecialApp[nApplicationID], sizeof(m_szSpecialApp[nApplicationID]));
		ZeroMemory(m_nCommandID[nApplicationID], sizeof(m_nCommandID[nApplicationID]));
		m_nKillRingMax[nApplicationID] = 0;
		m_bUseDialogSetting[nApplicationID] = FALSE;
		m_nSettingStyle[nApplicationID] = 0;
	} else {
		ASSERT(0);
	}
}

void CXkeymacsData::SetApplicationTitle(LPCTSTR lpszApplicationTitle)
{
	m_strApplicationTitle.Format(lpszApplicationTitle);

	// delete white space at the end of the application title.
	while (!m_strApplicationTitle.IsEmpty()
		&& isspace(m_strApplicationTitle.GetAt(m_strApplicationTitle.GetLength() - 1))) {
		m_strApplicationTitle.Delete(m_strApplicationTitle.GetLength() - 1);
	}
}

CString CXkeymacsData::GetApplicationTitle()
{
	return m_strApplicationTitle;
}

void CXkeymacsData::SetKillRingMax(int nKillRingMax)
{
	m_nKillRingMax = nKillRingMax;
}

int CXkeymacsData::GetKillRingMax()
{
	return m_nKillRingMax;
}

BOOL CXkeymacsDll::IsValidKey(BYTE bVk)
{
	if (bVk == 0xf0) {	// 0xf0: Eisu key. GetAsyncKeyState returns the wrong state of Eisu key.
		return FALSE;
	}

	if (CUtils::IsNT()) {
		switch (bVk) {
		case VK_CONTROL:
		case VK_MENU:
		case VK_SHIFT:
			return FALSE;
		default:
			break;
		}
	}

	return TRUE;
}

BOOL CXkeymacsDll::IsFoo(BOOL (__cdecl *bF_)(void), int (__cdecl *Foo)(void))
{
	if (bF_()) {
		return TRUE;
	}

	BYTE bVk = 0;
	do {
		if (!IsValidKey(bVk)) {
			continue;
		}
		if (!IsDown(bVk)) {
			continue;
		}
		if (Commands[m_nCommandID[m_nApplicationID][NONE][bVk]].fCommand == Foo) {
			return TRUE;
		}
	} while (++bVk);
	return FALSE;
}

BOOL CXkeymacsDll::IsDepressedShiftKeyOnly(BYTE nKey)
{
	if (nKey != VK_SHIFT
	 && nKey != VK_LSHIFT
	 && nKey != VK_RSHIFT) {
		return FALSE;
	}

	BYTE bVk = 0;
	do {
		if (bVk == VK_SHIFT
		 || bVk == VK_LSHIFT
		 || bVk == VK_RSHIFT) {
			continue;
		}

		if (IsDown(bVk)) {
			return FALSE;
		}
	} while (++bVk);
	return TRUE;
}

BOOL CXkeymacsDll::IsControl()
{
	return IsFoo(CCommands::bC_, CCommands::C_);
}

BOOL CXkeymacsDll::IsMeta()
{
	return IsFoo(CCommands::bM_, CCommands::MetaAlt);
}

void CXkeymacsDll::AddKillRing(BOOL bNewData)
{
	if (m_nKillRingMax[m_nApplicationID] == 0) {
		return;
	}

	CClipboardSnap *pSnap = new CClipboardSnap;
	if( !pSnap ) return;

	BOOL bCapture = pSnap->Capture();
	bCapture = pSnap->Capture();	// for "office drawing shape format". Can CClipboardSnap care this problem?

	if( bCapture ) {
		if (bNewData) {
			m_oKillRing.AddHead(pSnap);
		} else {
			if (m_oKillRing.IsEmpty()) {
				m_oKillRing.AddHead(pSnap);
			} else {
				for (CClipboardSnap *pParent = m_oKillRing.GetHead(); pParent->GetNext(); pParent = pParent->GetNext()) {
					;
				}
				pParent->SetNext(pSnap);
			}
		}
	} else {
		delete pSnap;
		pSnap = NULL;
	}

	m_nKillRing = 0;

	if (m_nKillRingMax[m_nApplicationID] < m_oKillRing.GetCount()) {
		CClipboardSnap *pSnap = m_oKillRing.GetTail();
		delete pSnap;
		pSnap = NULL;
		m_oKillRing.RemoveTail();
	}
}

// Return TRUE if there is another data
// Return FALSE if there is no more data
CClipboardSnap* CXkeymacsDll::GetKillRing(CClipboardSnap* pSnap, BOOL bForce)
{
	if (m_nKillRingMax[m_nApplicationID] == 0) {
		return NULL;
	}

	if (m_oKillRing.IsEmpty()) {
		return NULL;
	}

	m_nKillRing %= m_oKillRing.GetCount();

	if (!bForce) {
		CClipboardSnap oCurrentSnap;
		oCurrentSnap.Capture();

		CClipboardSnap *pKillRing = m_oKillRing.GetAt(m_oKillRing.FindIndex(m_nKillRing));
		if (!pKillRing) {
			return NULL;
		}
		for (; pKillRing->GetNext(); pKillRing = pKillRing->GetNext()) {
			;
		}
		if (*pKillRing != oCurrentSnap) {
			return NULL;
		}
	}

	if (!pSnap) {
		pSnap = m_oKillRing.GetAt(m_oKillRing.FindIndex(m_nKillRing));
	}
	pSnap->Restore();

	return pSnap->GetNext();
}

void CXkeymacsDll::Original(int nCommandType, BYTE bVk, int nOriginal)
{
	nCommandType &= ~SHIFT;

	if (CUtils::IsNT()) {
		switch (bVk) {
		case VK_CONTROL:
			bVk = VK_LCONTROL;
			break;
		case VK_MENU:
			bVk = VK_LMENU;
			break;
		case VK_SHIFT:
			bVk = VK_LSHIFT;
			break;
		default:
			break;
		}
	}

	m_nOriginal[nCommandType][bVk] += nOriginal;
}

int CXkeymacsDll::Original(int nCommandType, BYTE bVk)
{
	nCommandType &= ~SHIFT;

	if (CUtils::IsNT()) {
		switch (bVk) {
		case VK_CONTROL:
			bVk = VK_LCONTROL;
			break;
		case VK_MENU:
			bVk = VK_LMENU;
			break;
		case VK_SHIFT:
			bVk = VK_LSHIFT;
			break;
		default:
			break;
		}
	}

	return m_nOriginal[nCommandType][bVk];
}

void CXkeymacsDll::IncreaseKillRingIndex(int nKillRing)
{
	m_nKillRing += nKillRing;
}

// move CCommands
CString CXkeymacsData::GetCommandName(int nCommandID)
{
	CString szCommandName(Commands[nCommandID].szCommandName);
	return szCommandName;
}

// move CCommands
int CXkeymacsData::GetDefaultCommandType(int nCommandID, int nIndex)
{
	if (nCommandID < 0 || sizeof(Commands) / sizeof(Commands[0]) <= nCommandID
	 || nIndex < 0 || sizeof(Commands[nCommandID].keybind) / sizeof(Commands[nCommandID].keybind[0]) <= nIndex) {
		ASSERT(0);
		return NONE;
	}

	int nCommandType	= Commands[nCommandID].keybind[nIndex].nCommandType;
	int bVk				= Commands[nCommandID].keybind[nIndex].bVk;

	if (m_b106Keyboard) {
		if (nCommandType & SHIFT) {	// Shift
			switch (bVk) {
			case '2':
			case '6':
			case 0xBA:		// VK_OEM_1		Used for miscellaneous characters; it can vary by keyboard. 
							//				Windows 2000/XP: For the US standard keyboard, the ';:' key
				nCommandType &= ~SHIFT;
				break;
			default:
				break;
			}
		} else {					// Normal
			switch (bVk) {
			case 0xBB:		// VK_OEM_PLUS	Windows 2000/XP: For any country/region, the '+' key
			case 0xC0:		// VK_OEM_3		Used for miscellaneous characters; it can vary by keyboard. 
							//				Windows 2000/XP: For the US standard keyboard, the '`~' key
			case 0xDE:		// VK_OEM_7		Used for miscellaneous characters; it can vary by keyboard. 
							//				Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key
				nCommandType |= SHIFT;
				break;
			default:
				break;
			}
		}
	}

	return nCommandType;
}

// move CCommands
int CXkeymacsData::GetDefaultCommandKey(int nCommandID, int nIndex)
{
	if (nCommandID < 0 || sizeof(Commands) / sizeof(Commands[0]) <= nCommandID
	 || nIndex < 0 || sizeof(Commands[nCommandID].keybind) / sizeof(Commands[nCommandID].keybind[0]) <= nIndex) {
		ASSERT(0);
		return 0;
	}

	int nCommandType	= Commands[nCommandID].keybind[nIndex].nCommandType;
	int bVk				= Commands[nCommandID].keybind[nIndex].bVk;

	if (m_b106Keyboard) {
		if (nCommandType & SHIFT) {	// Shift
			switch (bVk) {
			case '0':
				bVk = '9';
				break;
			case '2':
				bVk = 0xC0;	// VK_OEM_3		Used for miscellaneous characters; it can vary by keyboard. 
							//				Windows 2000/XP: For the US standard keyboard, the '`~' key
				break;
			case '6':
				bVk = 0xDE;	// VK_OEM_7		Used for miscellaneous characters; it can vary by keyboard. 
							//				Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key
				break;
			case '7':
				bVk = '6';
				break;
			case '8':
				bVk = 0xBA;	// VK_OEM_1		Used for miscellaneous characters; it can vary by keyboard. 
							//				Windows 2000/XP: For the US standard keyboard, the ';:' key
				break;
			case '9':
				bVk = '8';
				break;
			case 0xBD:		// VK_OEM_MINUS	Windows 2000/XP: For any country/region, the '-' key
				bVk = 0xE2;	// VK_OEM_102	Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard
				break;
			case 0xC0:
				bVk = 0xDE;	// VK_OEM_7		Used for miscellaneous characters; it can vary by keyboard. 
							//				Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key
				break;
			case 0xDE:		// VK_OEM_7		Used for miscellaneous characters; it can vary by keyboard. 
							//				Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key
				bVk = '2';
				break;
			default:
				break;
			}
		} else {					// Normal
			switch (bVk) {
			case 0xBA:		// VK_OEM_1		Used for miscellaneous characters; it can vary by keyboard. 
							//				Windows 2000/XP: For the US standard keyboard, the ';:' key
				bVk = 0xBB;	// VK_OEM_PLUS	Windows 2000/XP: For any country/region, the '+' key
				break;
			case 0xBB:		// VK_OEM_PLUS	Windows 2000/XP: For any country/region, the '+' key
				bVk = 0xBD;	// VK_OEM_MINUS	Windows 2000/XP: For any country/region, the '-' key
				break;
			case 0xDE:		// VK_OEM_7		Used for miscellaneous characters; it can vary by keyboard. 
							//				Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key
				bVk = '7';
				break;
			default:
				break;
			}
		}
	}

	return bVk;
}

// move CCommands
int CXkeymacsData::GetDefaultControlID(int nCommandID, int nIndex)
{
	if (nCommandID < 0 || sizeof(Commands) / sizeof(Commands[0]) <= nCommandID
	 || nIndex < 0 || sizeof(Commands[nCommandID].keybind) / sizeof(Commands[nCommandID].keybind[0]) <= nIndex) {
		ASSERT(0);
		return 0;
	}

	return Commands[nCommandID].keybind[nIndex].nControlID;
}

// nobody use
int CXkeymacsDll::GetMickey(int nDifferential, int nThreshold1, int nThreshold2, int nAcceleration, int nSpeed)
{
	nDifferential = nDifferential * 10 / nSpeed;

	switch (nAcceleration) {
	case 2:
		if (nThreshold2 < fabs((double)(nDifferential / 4))) {
			nDifferential /= 4;
			break;
		}
		// Do NOT write break; here.
	case 1:
		if (nThreshold1 < fabs((double)(nDifferential / 2))) {
			nDifferential /= 2;
		}
		break;
	case 0:
		break;
	default:
		ASSERT(0);
		break;
	}

	return nDifferential;
}


int CXkeymacsData::GetSettingStyle()
{
	return m_nSettingStyle;
}

void CXkeymacsData::SetSettingStyle(int nSettingStyle)
{
	m_nSettingStyle = nSettingStyle;
}

void CXkeymacsDll::SetSettingStyle(int nApplicationID, int nSettingStyle)
{
	m_nSettingStyle[nApplicationID] = nSettingStyle;
}

int CXkeymacsData::GetCategoryID(int nCommandID)
{
	return Commands[nCommandID].nCategoryID;
}

void CXkeymacsData::SetIgnoreUndefinedMetaCtrl(BOOL bIgnoreUndefinedMetaCtrl)
{
	m_bIgnoreUndefinedMetaCtrl = bIgnoreUndefinedMetaCtrl;
}

BOOL CXkeymacsData::GetIgnoreUndefinedMetaCtrl()
{
	return m_bIgnoreUndefinedMetaCtrl;
}

void CXkeymacsDll::SetIgnoreUndefinedMetaCtrl(int nApplicationID, BOOL bIgnoreUndefinedMetaCtrl)
{
	m_bIgnoreUndefinedMetaCtrl[nApplicationID] = bIgnoreUndefinedMetaCtrl;
}

void CXkeymacsData::SetIgnoreUndefinedC_x(BOOL bIgnoreUndefinedC_x)
{
	m_bIgnoreUndefinedC_x = bIgnoreUndefinedC_x;
}

BOOL CXkeymacsData::GetIgnoreUndefinedC_x()
{
	return m_bIgnoreUndefinedC_x;
}

void CXkeymacsDll::SetIgnoreUndefinedC_x(int nApplicationID, BOOL bIgnoreUndefinedC_x)
{
	m_bIgnoreUndefinedC_x[nApplicationID] = bIgnoreUndefinedC_x;
}

void CXkeymacsData::SetEnableCUA(BOOL bEnableCUA)
{
	m_bEnableCUA = bEnableCUA;
}

BOOL CXkeymacsData::GetEnableCUA()
{
	return m_bEnableCUA;
}

void CXkeymacsDll::SetEnableCUA(int nApplicationID, BOOL bEnableCUA)
{
	m_bEnableCUA[nApplicationID] = bEnableCUA;
}

BOOL CXkeymacsDll::GetEnableCUA()
{
	return m_bEnableCUA[m_nApplicationID];
}

void CXkeymacsData::SetUseDialogSetting(BOOL bUseDialogSetting)
{
	m_bUseDialogSetting = bUseDialogSetting;
}

BOOL CXkeymacsData::GetUseDialogSetting()
{
	return m_bUseDialogSetting;
}

int CXkeymacsData::GetDescriptionID(int nCommandID)
{
	return Commands[nCommandID].nDescriptionID;
}

int CXkeymacsData::GetToolTipID(int nCommandID)
{
	return Commands[nCommandID].nToolTipID;
}

void CXkeymacsDll::DefiningMacro(BOOL bDefiningMacro)
{
	m_bDefiningMacro = bDefiningMacro;

	if (bDefiningMacro) {	// start-kbd-macro
		if (CCommands::bC_u()) {
			ReleaseKey(VK_SHIFT);
			CallMacro();
		}
	} else {				// end-kbd-macro
		while (!m_Macro.IsEmpty()) {
			KbdMacro_t *pKbdMacro = (KbdMacro_t *)m_Macro.GetTail();
			if (pKbdMacro->lParam & BEING_RELEASED) {
				break;
			} else {
				m_Macro.RemoveTail();
				delete pKbdMacro;
				pKbdMacro = NULL;
			}
		}

//		CUtils::Log("Macro MemMap: start");
		if (!m_Macro.IsEmpty()) {
			static HANDLE hMacro = NULL;
			if (!hMacro) {
				hMacro = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 0x3000, "macro");
			}
			if (hMacro) {
//				CUtils::Log("Macro MemMap: 1");
				PVOID pView = MapViewOfFile(hMacro, FILE_MAP_ALL_ACCESS, 0, 0, 0);
//				CUtils::Log("Macro MemMap: 2");
				if (pView) {
//					CUtils::Log("Macro MemMap: 2.5");
					for (int i = 0; i < m_Macro.GetCount(); ++i) {
//						CUtils::Log("Macro MemMap: 3-1 %d", i);
						KbdMacro_t *pKbdMacro = (KbdMacro_t *)m_Macro.GetAt(m_Macro.FindIndex(i));
//						CUtils::Log("Macro MemMap: 3-2 %d", i);
						memcpy((char *) pView + i * sizeof(KbdMacro_t), pKbdMacro, sizeof(KbdMacro_t));
//						CUtils::Log("Macro MemMap: 3-3 %d", i);
					}
//					CUtils::Log("Macro MemMap: 4");
					UnmapViewOfFile(pView);
//					CUtils::Log("Macro MemMap: 5");
				} else {
//					CUtils::Log("Macro MemMpa: error: %d", GetLastError());
				}
			} else {
//				CUtils::Log("Macro MemMap: 6");
				ASSERT(0);
			}
		}
	}
}

BOOL CXkeymacsDll::DefiningMacro()
{
	return m_bDefiningMacro;
}

/**/ 
void CXkeymacsDll::CallMacro()
{
	BOOL bIsCtrlDown = IsDown(VK_CONTROL);
	if (bIsCtrlDown) {
		ReleaseKey(VK_CONTROL);
	}
	BOOL bIsAltDown = IsDown(VK_MENU);
	if (bIsAltDown) {
		ReleaseKey(VK_MENU);
	}
	BOOL bIsShiftDown = IsDown(VK_SHIFT);
	if (bIsShiftDown) {
		ReleaseKey(VK_SHIFT);
	}

	for (POSITION pos = m_Macro.GetHeadPosition(); pos; ) {
		KbdMacro_t *pKbdMacro = (KbdMacro_t *)m_Macro.GetNext(pos);
		if (pKbdMacro->lParam & BEING_RELEASED) {
			ReleaseKey((BYTE)pKbdMacro->wParam);
		} else {
			DepressKey((BYTE)pKbdMacro->wParam, pKbdMacro->bOriginal);
		}
	}

	if (bIsCtrlDown) {
		DepressKey(VK_CONTROL);
	}
	if (bIsAltDown) {
		DepressKey(VK_MENU);
	}
	if (bIsShiftDown) {
		DepressKey(VK_SHIFT);
	}
}

/*/
void CXkeymacsDll::CallMacro()	// for debug
{
	CString sz;
	for (POSITION pos = m_Macro.GetHeadPosition(); pos; ) {
		KbdMacro_t *pKbdMacro = (KbdMacro_t *)m_Macro.GetNext(pos);
		if (pKbdMacro->lParam & BEING_RELEASED) {
			CString t;
			t.Format("0x%xu ", pKbdMacro->wParam);
			sz += t;
		} else {
			CString t;
			t.Format("0x%xd ", pKbdMacro->wParam);
			sz += t;
		}
	}
//	CUtils::Log(sz);
}
/**/

void CXkeymacsData::Set106Keyboard(BOOL b106Keyboard)
{
	m_b106Keyboard = b106Keyboard;
}

BOOL CXkeymacsData::Is106Keyboard()
{
	return m_b106Keyboard;
}

int CXkeymacsDll::IsPassThrough(BYTE nKey)
{
	BYTE bVk = 0;
	do {
		if (IsDown(bVk)
		 && (Commands[m_nCommandID[m_nApplicationID][NONE][bVk]].fCommand == CCommands::PassThrough)) {
			if (bVk == nKey) {
				return GOTO_HOOK;
			}

			return GOTO_DO_NOTHING;
		}
	} while (++bVk);
	return CONTINUE;
}

void CXkeymacsDll::SetKeyboardHookFlag()
{
	SetKeyboardHookFlag(m_bHook);
}

void CXkeymacsDll::SetFunctionKey(int nFunctionID, int nApplicationID, int nCommandType, int nKey)
{
	if (nApplicationID	< 0 || MAX_APP			<= nApplicationID
	 || nCommandType	< 0 || MAX_COMMAND_TYPE	<= nCommandType
	 || nKey			< 0 || MAX_KEY			<= nKey) {
		return;
	}

	m_nFunctionID[nApplicationID][nCommandType][nKey] = nFunctionID;
}

void CXkeymacsDll::ClearFunctionDefinition()
{
	memset(m_nFunctionID, -1, sizeof(m_nFunctionID));
	memset(m_szFunctionDefinition, 0, sizeof(m_szFunctionDefinition));
}

void CXkeymacsDll::SetFunctionDefinition(int nFunctionID, CString szDefinition)
{
	if (nFunctionID < 0 || MAX_FUNCTION <= nFunctionID) {
		return;
	}

	memset(m_szFunctionDefinition[nFunctionID], 0, sizeof(m_szFunctionDefinition[nFunctionID]));
	sprintf(m_szFunctionDefinition[nFunctionID], "%s", szDefinition);

	return;

}

void CXkeymacsDll::CallFunction(int nFunctionID)
{
	if (nFunctionID < 0 || MAX_FUNCTION <= nFunctionID || !strlen(m_szFunctionDefinition[nFunctionID])) {
		return;
	}

	BOOL bIsCtrlDown = CXkeymacsDll::IsDown(VK_CONTROL);
	BOOL bIsAltDown = CXkeymacsDll::IsDown(VK_MENU);
	BOOL bIsShiftDown = CXkeymacsDll::IsDown(VK_SHIFT);

	if (bIsCtrlDown) {
		CUtils::UpdateKeyboardState(VK_CONTROL, 0);
		ReleaseKey(VK_CONTROL);
	}

	if (bIsAltDown) {
		ReleaseKey(VK_MENU);
	}

	if (bIsShiftDown) {
		ReleaseKey(VK_SHIFT);
	}

	if (m_szFunctionDefinition[nFunctionID][0] == '"' && m_szFunctionDefinition[nFunctionID][strlen(m_szFunctionDefinition[nFunctionID]) - 1] == '"') {
		for (unsigned int i = 1; i < strlen(m_szFunctionDefinition[nFunctionID]) - 1; ++i) {	// skip '"'
			ParseKey(nFunctionID, i);
		}
	} else if (m_szFunctionDefinition[nFunctionID][0] == '[' && m_szFunctionDefinition[nFunctionID][strlen(m_szFunctionDefinition[nFunctionID]) - 1] == ']') {
		for (unsigned int i = 1; i < strlen(m_szFunctionDefinition[nFunctionID]) - 1; ++i) {	// skip '[' and ']'
			if (m_szFunctionDefinition[nFunctionID][i] == '?') {	// [?f ?o ?o]
				++i;
				ParseKey(nFunctionID, i);
			} else {												// [ControlCharacter]
				for (int nKeyID = 0; nKeyID < sizeof(ControlCharacters) / sizeof(ControlCharacters[0]); ++nKeyID) {
					if (!strncmp(m_szFunctionDefinition[nFunctionID] + i, ControlCharacters[nKeyID].name, strlen(ControlCharacters[nKeyID].name))) {
						Kdu(ControlCharacters[nKeyID].bVk);
						i += strlen(ControlCharacters[nKeyID].name);
						break;
					}
				}
			}
		}
	} else {
		return;
	}

	if (bIsShiftDown) {
		DepressKey(VK_SHIFT);
	}

	if (bIsAltDown) {
		DepressKey(VK_MENU);
	}

	if (bIsCtrlDown) {
		DepressKey(VK_CONTROL);
		CUtils::UpdateKeyboardState(VK_CONTROL, 1);
	}
	return;
}

void CXkeymacsDll::ParseKey(const int nFunctionID, unsigned int &i)	// FIXME: Do NOT call Kdu but return command type and key.
{
	int nCommandType = NONE;
	if (m_szFunctionDefinition[nFunctionID][i] == '\\') {
		++i;
		BOOL bFound = FALSE;
		do {
			bFound = FALSE;
			for (int ModifierID = 0; ModifierID < sizeof(Modifiers) / sizeof(Modifiers[0]); ++ModifierID) {
				if (!strncmp(m_szFunctionDefinition[nFunctionID] + i, Modifiers[ModifierID].name, strlen(Modifiers[ModifierID].name))
				 && strlen(Modifiers[ModifierID].name) < strlen(m_szFunctionDefinition[nFunctionID] + i)) {
					nCommandType |= Modifiers[ModifierID].id;
					i+= strlen(Modifiers[ModifierID].name);
					bFound = TRUE;
				}
			}
		} while (bFound);
	}
	if (IsShift(m_szFunctionDefinition[nFunctionID][i]) && !(nCommandType & (WIN_CTRL | WIN_ALT))) {
		nCommandType |= SHIFT;
	}

	for (int nKeyID = 0; nKeyID < sizeof(ControlCharacters) / sizeof(ControlCharacters[0]); ++nKeyID) {
		if (!strncmp(m_szFunctionDefinition[nFunctionID] + i, ControlCharacters[nKeyID].name, strlen(ControlCharacters[nKeyID].name))) {
			i += strlen(ControlCharacters[nKeyID].name);
			break;
		}
	}
	BYTE nKey = NULL;
	if (nKeyID < sizeof(ControlCharacters) / sizeof(ControlCharacters[0])) {
		nKey = ControlCharacters[nKeyID].bVk;
	} else {
		nKey = a2v(m_szFunctionDefinition[nFunctionID][i]);
	}

	if (nCommandType < MAX_COMMAND_TYPE && Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand) {
		while (Commands[m_nCommandID[m_nApplicationID][nCommandType][nKey]].fCommand() == GOTO_RECURSIVE) {
			;
		}
	} else {
		Kdu(nCommandType, nKey);
	}
}

void CXkeymacsDll::Kdu(int nCommandType, BYTE bVk)
{
	if (nCommandType < MAX_COMMAND_TYPE && Commands[m_nCommandID[m_nApplicationID][nCommandType][bVk]].fCommand) {
		Commands[m_nCommandID[m_nApplicationID][nCommandType][bVk]].fCommand();
	} else {	// FIXME
		if (nCommandType & WIN_CTRL) {
			DepressKey(VK_CONTROL);
		}
		if (nCommandType & WIN_ALT) {
			DepressKey(VK_MENU);
		}
		if (nCommandType & SHIFT) {
			DepressKey(VK_SHIFT);
		}

		Kdu(bVk);

		if (nCommandType & SHIFT) {
			ReleaseKey(VK_SHIFT);
		}
		if (nCommandType & WIN_ALT) {
			ReleaseKey(VK_MENU);
		}
		if (nCommandType & WIN_CTRL) {
			ReleaseKey(VK_CONTROL);
		}
	}
	return;
}

BOOL CXkeymacsDll::IsShift(char nAscii)
{
	switch (nAscii) {
	case ' ':
		return FALSE;
	case '!':
	case '"':
	case '#':
	case '$':
	case '%':
	case '&':
		return TRUE;
	case '\'':
		return CXkeymacsData::Is106Keyboard();
	case '(':
	case ')':
	case '*':
	case '+':
		return TRUE;
	case ',':
	case '-':
	case '.':
	case '/':
	case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
		return FALSE;
	case ':':
		return !CXkeymacsData::Is106Keyboard();
	case ';':
		return FALSE;
	case '<':
		return TRUE;
	case '=':
		return CXkeymacsData::Is106Keyboard();
	case '>':
	case '?':
		return TRUE;
	case '@':
		return !CXkeymacsData::Is106Keyboard();
	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': 
	case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': 
	case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': 
		return TRUE;
	case '[':
	case '\\':
	case ']':
		return FALSE;
	case '^':
		return !CXkeymacsData::Is106Keyboard();
	case '_':
		return TRUE;
	case '`':
		return CXkeymacsData::Is106Keyboard();
	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': 
	case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': 
	case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': 
		return FALSE;
	case '{':
	case '|':
	case '}':
	case '~':
		return TRUE;
	default:
		return FALSE;
	}
}

BYTE CXkeymacsDll::a2v(char nAscii)
{
	switch (nAscii) {
	case ' ':
		return VK_SPACE;
	case '!':
		return '1';
	case '"':
		return CXkeymacsData::Is106Keyboard() ? '2' : (BYTE) 0xde;	// VK_OEM_7
	case '#':
		return '3';
	case '$':
		return '4';
	case '%':
		return '5';
	case '&':
		return CXkeymacsData::Is106Keyboard() ? '6' : '7';
	case '\'':
		return CXkeymacsData::Is106Keyboard() ? '7' : (BYTE) 0xde;	// VK_OEM_7
	case '(':
		return CXkeymacsData::Is106Keyboard() ? '8' : '9';
	case ')':
		return CXkeymacsData::Is106Keyboard() ? '9' : '0';
	case '*':
		return CXkeymacsData::Is106Keyboard() ? (BYTE) 0xba : '8';	// VK_OEM_1
	case '+':
		return 0xbb;	// VK_OEM_PLUS
	case ',':
		return 0xbc;	// VK_OEM_COMMA
	case '-':
		return 0xbd;	// VK_OEM_MINUS
	case '.':
		return 0xbe;	// VK_OEM_PERIOD
	case '/':
		return 0xbf;	// VK_OEM_2
	case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
		return nAscii;
	case ':':
		return 0xba;	// VK_OEM_1
	case ';':
		return CXkeymacsData::Is106Keyboard() ? (BYTE) 0xbb : (BYTE) 0xba;	// VK_OEM_PLUS	VK_OEM_1
	case '<':
		return 0xbc;	// VK_OEM_COMMA
	case '=':
		return CXkeymacsData::Is106Keyboard() ? (BYTE) 0xbd : (BYTE) 0xbb;	// VK_OEM_MINUS	VK_OEM_PLUS
	case '>':
		return 0xbe;	// VK_OEM_PERIOD
	case '?':
		return 0xbf;	// VK_OEM_2
	case '@':
		return CXkeymacsData::Is106Keyboard() ? (BYTE) 0xc0 : '2';
	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': 
	case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': 
	case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': 
		return nAscii;
	case '[':
		return 0xdb;	// VK_OEM_4
	case '\\':
		return 0xdc;	// VK_OEM_5
	case ']':
		return 0xdd;	// VK_OEM_6
	case '^':
		return CXkeymacsData::Is106Keyboard() ? (BYTE) 0xde : '6';	// VK_OEM_7
	case '_':
		return CXkeymacsData::Is106Keyboard() ? (BYTE) 0xe2 : (BYTE) 0xbd;	// VK_OEM_102	VK_OEM_MINUS
	case '`':
		return 0xc0;	// VK_OEM_3
	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': 
	case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': 
	case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': 
		return (BYTE) (nAscii - ('a' - 'A'));
	case '{':
		return 0xdb;	// VK_OEM_4
	case '|':
		return 0xdc;	// VK_OEM_5
	case '}':
		return 0xdd;	// VK_OEM_6
	case '~':
		return CXkeymacsData::Is106Keyboard() ? (BYTE) 0xde : (BYTE) 0xc0;	// VK_OEM_7	VK_OEM_3
	default:
		return 0;
	}
}

void CXkeymacsDll::DeleteAllShell_NotifyIcon()
{
	for (int icon = 0; icon < MAX_ICON_TYPE; ++icon) {
		DeleteShell_NotifyIcon((ICON_TYPE)icon);
	}
}

void CXkeymacsDll::AddAllShell_NotifyIcon()
{
	for (int icon = 0; icon < MAX_ICON_TYPE; ++icon) {
		AddShell_NotifyIcon((ICON_TYPE)icon);
	}
}

void CXkeymacsData::SetWindowText(LPCTSTR lpszWindowText)
{
	m_strWindowText.Format(lpszWindowText);
}

CString CXkeymacsData::GetWindowText()
{
	return m_strWindowText;
}

void CXkeymacsData::SetWindowTextType(int nWindowTextType)
{
	m_nWindowTextType = nWindowTextType;
}

int CXkeymacsData::GetWindowTextType()
{
	return m_nWindowTextType;
}

(C) 2001-2005 oishi@cam.hi-ho.ne.jp