// Profile.cpp: implementation of the CProfile class
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "xkeymacs.h"
#include "Profile.h"
#include "MainFrm.h"
#include "DotXkeymacs.h"
#include 
#include 	// Windows NT/2000: Requires Windows 2000 (or Windows NT 4.0 with Internet Explorer 4.0 or later). 
						// Windows 95/98/Me: Requires Windows 98 (or Windows 95 with Internet Explorer 4.0 or later). 

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

#pragma data_seg(".xkmcs")
	CXkeymacsData CProfile::m_XkeymacsData[MAX_APP];
	TASK_LIST CProfile::m_TaskList[MAX_TASKS];
	DWORD CProfile::m_dwTasks;
	ScanCode_t CProfile::CurrentScanCodeMap[4][256];
	ScanCode_t CProfile::ScanCodeMap[4][256];
#pragma data_seg()

enum {INITIAL_SIZE	= 51200};
enum {EXTEND_SIZE	= 25600};

void CProfile::Item2AppName(CString *sz)
{
	if (IsTheString(*sz, IDS_DEFAULT_TITLE)) {
		sz->LoadString(IDS_DEFAULT);
	}

	if (IsTheString(*sz, IDS_DIALOG_TITLE)) {
		sz->LoadString(IDS_DIALOG);
	}

	int nStart, nEnd, nCount;

	nStart	= sz->ReverseFind('(') + 1;
	nEnd	= sz->Find(')', nStart) - 1;
	nCount	= (nEnd + 1) - nStart;
	*sz		= sz->Mid(nStart, nCount);
}

int CProfile::IsNotSameString(CComboBox *pApplication, CString szListItem)
{
	CString szItem, szList;
	szList = szListItem;
	Item2AppName(&szList);

	for (int i = 0; i < pApplication->GetCount(); ++i) {
		pApplication->GetLBText(i, szItem);
		Item2AppName(&szItem);
		if (!stricmp(szItem, szList)) {
			return 0;
		}
	}

	return 1;
}

int CProfile::CountSeparator(CString szMainString, CString szSeparator)
{
	int index	= 0;
	int counter	= 0;

	while ((index = szMainString.Find(szSeparator, index)) != -1) {
		++index;
		++counter;
	}

	return counter;
}

void CProfile::GetNthString(CString *szAppName, CString szWindowName, CString szSeparator, int n)
{
	int index = -1;

	while (--n) {
		index = szWindowName.Find(szSeparator, index + 1);
	}

	int nStart;
	if (index != -1) {
		nStart = index + szSeparator.GetLength();
	} else {
		nStart = 0;
	}

	int nEnd = szWindowName.Find(szSeparator, nStart);
	if (nEnd == -1) {
		nEnd = szWindowName.GetLength();
	}

	*szAppName = szWindowName.Mid(nStart, nEnd - nStart);
}

void CProfile::GetAppName(CString *szAppName, char *pWindowName)
{
	CString szWindowName(pWindowName);
	CString szSeparator(MAKEINTRESOURCE(IDS_SEPARATE_WINDOWTITLE));
	int nWord = CountSeparator(szWindowName, szSeparator) + 1;

	while (nWord) {
		GetNthString(szAppName, szWindowName, szSeparator, nWord);
		if (szAppName->GetAt(0) == '['
		 || szAppName->Find('.', 0) != -1		// for Microsoft Project
		 || szAppName->Find(']', 0) != -1) {	// for the file name like [foo - bar]
			--nWord;
		} else {
			return;
		}
	}

	*szAppName = szWindowName;
}

BOOL CALLBACK CProfile::EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
	CComboBox		*pApplication	= (CComboBox*)lParam;
	PTASK_LIST		pTask			= CProfile::m_TaskList;
	
	char szWindowName[WINDOW_NAME_LENGTH];
	char szClassName[CLASS_NAME_LENGTH];
	WINDOWPLACEMENT wpl;
	
	wpl.length = sizeof(WINDOWPLACEMENT);
	GetWindowText(hWnd, szWindowName, sizeof(szWindowName));
	GetClassName(hWnd, szClassName, sizeof(szClassName));

	CString szAppName;
	// Get Process Name
	DWORD dwProcessId = 0;
	GetWindowThreadProcessId(hWnd, &dwProcessId);
	for (DWORD i = 0; i < CProfile::m_dwTasks; ++i) {
		if (pTask[i].dwProcessId == dwProcessId) {

			// Get Application Name
			if (szWindowName[0] == '\0') {
				continue;
			}
			if (!strnicmp(pTask[i].ProcessName, CString(MAKEINTRESOURCE(IDS_B2)), sizeof(pTask[i].ProcessName))) {
				szAppName.LoadString(IDS_BECKY);
			} else if (!strnicmp(pTask[i].ProcessName, CString(MAKEINTRESOURCE(IDS_EXPLORER)), sizeof(pTask[i].ProcessName))) {
				szAppName.LoadString(IDS_PROGRAM_MANAGER);
			} else if (!strnicmp(pTask[i].ProcessName, CString(MAKEINTRESOURCE(IDS_MSIMN)), sizeof(pTask[i].ProcessName))) {
				szAppName.LoadString(IDS_OUTLOOK_EXPRESS);
			} else if (!strnicmp(pTask[i].ProcessName, CString(MAKEINTRESOURCE(IDS_PROJECT)), sizeof(pTask[i].ProcessName))
					|| !strnicmp(pTask[i].ProcessName, CString(MAKEINTRESOURCE(IDS_EXCEL)), sizeof(pTask[i].ProcessName))
					|| !strnicmp(pTask[i].ProcessName, "psp.exe", sizeof(pTask[i].ProcessName))) {
				GetNthString(&szAppName, szWindowName, CString(MAKEINTRESOURCE(IDS_SEPARATE_WINDOWTITLE)), 1);
			} else if (!strnicmp(pTask[i].ProcessName, "sakura.exe", sizeof(pTask[i].ProcessName))) {
				GetNthString(&szAppName, szWindowName, CString(MAKEINTRESOURCE(IDS_SEPARATE_WINDOWTITLE)), 2);	// '.' is included, so...
			} else if (!strnicmp(pTask[i].ProcessName, CString(MAKEINTRESOURCE(IDS_MSDN)), sizeof(pTask[i].ProcessName))) {
				szAppName = szWindowName;
			} else if (!strnicmp(pTask[i].ProcessName, "devenv.exe", sizeof(pTask[i].ProcessName))) {
				szAppName.Format("Microsoft Visual Studio .NET");
			} else if (!strnicmp(pTask[i].ProcessName, "vb6.exe", sizeof(pTask[i].ProcessName))) {
				szAppName.Format("Microsoft Visual Basic");
			} else if (!strnicmp(pTask[i].ProcessName, "ssexp.exe", sizeof(pTask[i].ProcessName))) {
				szAppName.LoadString(IDS_VISUAL_SOURCESAFE_EXPLORER);
			} else if (!strnicmp(pTask[i].ProcessName, "sh.exe", sizeof(pTask[i].ProcessName))) {
				szAppName.Format("MKS Korn Shell");
			} else if (!strnicmp(pTask[i].ProcessName, "csh.exe", sizeof(pTask[i].ProcessName))) {
				szAppName.Format("C Shell");
			} else {
				CUtils::SetCorrectApplicationName(pTask[i].ProcessName, sizeof(pTask[i].ProcessName), szWindowName, sizeof(szWindowName));
				GetAppName(&szAppName, szWindowName);
			}
			break;
		}
	}
	
	
	if ((IsWindowVisible(hWnd))									// Is visible?
	 && (GetWindow(hWnd, GW_OWNER) == NULL)						// Is top level window?
	 && (lstrlen(szWindowName) > 0)								// Have caption?
	 && (pApplication->FindString(-1, szClassName) == CB_ERR)) {// Is not same string?
		CString szListItem;
		szListItem.Format(IDS_APPLICATION_LIST_ITEM, szAppName, pTask[i].ProcessName);
		if (IsNotSameString(pApplication, szListItem)) {
			pApplication->AddString(szListItem);
		}
	}
	return TRUE;
}

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

CProfile::CProfile()
{

}

CProfile::~CProfile()
{

}

// This method initializes data in the registry, or retrieves and validates registry data.
// bSaveAndValidate specifies a flag that indicates 
// whether registry data is being initialized (FALSE) or data is being retrieved (TRUE). 
void CProfile::UpdateRegistryData(BOOL bSaveAndValidate)
{
	CString szEntry;
	CString szApplicationName;
	CString szApplicationTitle;
	CString szWindowText;
	CString szWindowTextType;

	BOOL bUseDialogSetting = FALSE;

	for (int nApplicationID = 0; nApplicationID < MAX_APP; ++nApplicationID) {
		// application name
		CString szSection(MAKEINTRESOURCE(IDS_REG_SECTION_APPLICATION));
		szEntry.Format(IDS_REG_ENTRY_APPLICATION, nApplicationID);
		if (bSaveAndValidate) {	// retrieve
			m_XkeymacsData[nApplicationID].ClearAll();
			szApplicationName = AfxGetApp()->GetProfileString(szSection, szEntry);
			if (szApplicationName.IsEmpty()) {
				if (nApplicationID) {
					if (!bUseDialogSetting) {
						szApplicationName.LoadString(IDS_DIALOG);
						bUseDialogSetting = TRUE;
					} else {
						continue;
					}
				} else {
					szApplicationName.LoadString(IDS_DEFAULT);
				}
			} else {
				if (szApplicationName == CString(MAKEINTRESOURCE(IDS_DIALOG))) {
					bUseDialogSetting = TRUE;
				}
			}
			m_XkeymacsData[nApplicationID].SetApplicationName(szApplicationName);
		} else {				// initialize
			szApplicationName = m_XkeymacsData[nApplicationID].GetApplicationName();
			if (szApplicationName.IsEmpty()) {
				continue;
			}
			AfxGetApp()->WriteProfileString(szSection, szEntry, szApplicationName);
		}

		// application title
		szEntry.LoadString(IDS_REG_ENTRY_APPLICATOIN_TITLE);
		if (bSaveAndValidate) {	// retrieve
			szApplicationTitle = AfxGetApp()->GetProfileString(szApplicationName, szEntry);
			m_XkeymacsData[nApplicationID].SetApplicationTitle(szApplicationTitle);
		} else {				// initialize
			szApplicationTitle = m_XkeymacsData[nApplicationID].GetApplicationTitle();
			while (!szApplicationTitle.IsEmpty() && szApplicationTitle.GetAt(0) == ' ') {
				szApplicationTitle.Delete(0);
			}
			AfxGetApp()->WriteProfileString(szApplicationName, szEntry, szApplicationTitle);
		}

		// window text
		szEntry.LoadString(IDS_REG_ENTRY_WINDOW_TEXT);
		if (bSaveAndValidate) {	// retrieve
			szWindowText = AfxGetApp()->GetProfileString(szApplicationName, szEntry);
			m_XkeymacsData[nApplicationID].SetWindowText(szWindowText);
		} else {				// initialize
			szWindowText = m_XkeymacsData[nApplicationID].GetWindowText();
			AfxGetApp()->WriteProfileString(szApplicationName, szEntry, szWindowText);
		}

		// window text type
		szEntry.LoadString(IDS_REG_ENTRY_WINDOW_TEXT_TYPE);
		if (bSaveAndValidate) {	// retrieve
			szWindowTextType = AfxGetApp()->GetProfileString(szApplicationName, szEntry);

			int nWindowTextType = IDS_WINDOW_TEXT_IGNORE;
			if (szWindowTextType == CString(MAKEINTRESOURCE(IDS_WINDOW_TEXT_MATCH))) {
				nWindowTextType = IDS_WINDOW_TEXT_MATCH;
			} else if (szWindowTextType == CString(MAKEINTRESOURCE(IDS_WINDOW_TEXT_MATCH_FORWARD))) {
				nWindowTextType = IDS_WINDOW_TEXT_MATCH_FORWARD;
			} else if (szWindowTextType == CString(MAKEINTRESOURCE(IDS_WINDOW_TEXT_MATCH_BACKWARD))) {
				nWindowTextType = IDS_WINDOW_TEXT_MATCH_BACKWARD;
			} else if (szWindowTextType == CString(MAKEINTRESOURCE(IDS_WINDOW_TEXT_MATCH_FULL))) {
				nWindowTextType = IDS_WINDOW_TEXT_MATCH_FULL;
			}

			m_XkeymacsData[nApplicationID].SetWindowTextType(nWindowTextType);
		} else {				// initialize
			szWindowTextType.LoadString(m_XkeymacsData[nApplicationID].GetWindowTextType());
			AfxGetApp()->WriteProfileString(szApplicationName, szEntry, szWindowTextType);
		}

		// on/off
		if (bSaveAndValidate) {	// retrieve
			for (int nCommandID = 1; nCommandID < sizeof(Commands) / sizeof(Commands[0]); ++nCommandID) {
				szEntry = CXkeymacsData::GetCommandName(nCommandID);
				if (szEntry.IsEmpty()) {
					break;
				}

				HKEY hKey = NULL;
				CString szSubKey(MAKEINTRESOURCE(IDS_REGSUBKEY_DATA));
				szSubKey += "\\" + szApplicationName + "\\" + szEntry;
				if (RegOpenKeyEx(HKEY_CURRENT_USER, szSubKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
					// Use registry data
					char szKeyBind[128] = {'\0'};
					DWORD dwKeyBind = sizeof(szKeyBind);
					FILETIME ft = {'\0'};	// not use
					for (DWORD dwIndex = 0;
						 RegEnumKeyEx(hKey, dwIndex, szKeyBind, &dwKeyBind, NULL, NULL, NULL, &ft) == ERROR_SUCCESS;
						 ++dwIndex) {
						int nCommandType = 0;
						int nKey = 0;
						ReadKeyBind(&nCommandType, &nKey, szKeyBind);
						m_XkeymacsData[nApplicationID].SetCommandID(nCommandType, nKey, nCommandID);

						memset(szKeyBind, 0, sizeof(szKeyBind));
						dwKeyBind = sizeof(szKeyBind);
					}
					RegCloseKey(hKey);
				} else {
					// Use default setting
					for (int i = 0; ; ++i) {
						if (CXkeymacsData::GetDefaultControlID(nCommandID, i) == IDC_CO2) {
							continue;
						}

						int nCommandType = CXkeymacsData::GetDefaultCommandType(nCommandID, i);
						int nKey = CXkeymacsData::GetDefaultCommandKey(nCommandID, i);
						if (nKey == 0) {
							break;
						}
						m_XkeymacsData[nApplicationID].SetCommandID(nCommandType, nKey, nCommandID);
					}
				}
			}
			for (int nFunctionID = 0; nFunctionID < CDotXkeymacs::GetFunctionNumber(); ++nFunctionID) {
				HKEY hKey = NULL;
				CString szSubKey(MAKEINTRESOURCE(IDS_REGSUBKEY_DATA));
				szSubKey += "\\" + szApplicationName + "\\" + CString(MAKEINTRESOURCE(IDS_REG_ORIGINAL_PREFIX)) + CDotXkeymacs::GetFunctionSymbol(nFunctionID);
				if (RegOpenKeyEx(HKEY_CURRENT_USER, szSubKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
					// Use registry data
					CDotXkeymacs::ClearKey(nFunctionID, nApplicationID);
					char szKeyBind[128] = {'\0'};
					DWORD dwKeyBind = sizeof(szKeyBind);
					FILETIME ft = {'\0'};	// not use
					for (DWORD dwIndex = 0; RegEnumKeyEx(hKey, dwIndex, szKeyBind, &dwKeyBind, NULL, NULL, NULL, &ft) == ERROR_SUCCESS; ++dwIndex) {
						int nCommandType = 0;
						int nKey = 0;
						ReadKeyBind(&nCommandType, &nKey, szKeyBind);
						CDotXkeymacs::SetKey(nFunctionID, nApplicationID, nCommandType, nKey);

						memset(szKeyBind, 0, sizeof(szKeyBind));
						dwKeyBind = sizeof(szKeyBind);
					}
					RegCloseKey(hKey);
				}
			}
		} else {				// initialize
			// create all commands
			for (int nCommandID = 1; nCommandID < sizeof(Commands) / sizeof(Commands[0]); ++nCommandID) {
				szEntry = CXkeymacsData::GetCommandName(nCommandID);
				if (szEntry.IsEmpty()) {
					break;
				}

				SaveCommand(szApplicationName, nCommandID);
			}
			for (int nCommandType = 0; nCommandType < MAX_COMMAND_TYPE; ++nCommandType) {
				for (int nKey = 0; nKey < MAX_KEY; ++nKey) {
					int nCommandID = m_XkeymacsData[nApplicationID].GetCommandID(nCommandType, nKey);
					SaveKeyBind(szApplicationName, nCommandID, nCommandType, nKey);
				}
			}
			for (int nFunctionID = 0; nFunctionID < CDotXkeymacs::GetFunctionNumber(); ++nFunctionID) {
				for (int nKeyID = 0; nKeyID < CDotXkeymacs::GetKeyNumber(nFunctionID, nApplicationID); ++nKeyID) {
					int nCommandType = 0;
					int nKey = 0;
					CDotXkeymacs::GetKey(nFunctionID, nApplicationID, nKeyID, &nCommandType, &nKey);
					SaveKeyBind(szApplicationName, CString(MAKEINTRESOURCE(IDS_REG_ORIGINAL_PREFIX)) + CDotXkeymacs::GetFunctionSymbol(nFunctionID), nCommandType, nKey);
				}
			}
		}

		// kill-ring-max
		szEntry.LoadString(IDS_REG_ENTRY_KILL_RING_MAX);
		if (bSaveAndValidate) {	// retrieve
			int nKillRingMax = AfxGetApp()->GetProfileInt(szApplicationName, szEntry, 1);
			m_XkeymacsData[nApplicationID].SetKillRingMax(nKillRingMax);
		} else {				// initialize
			int nKillRingMax = m_XkeymacsData[nApplicationID].GetKillRingMax();
			AfxGetApp()->WriteProfileInt(szApplicationName, szEntry, nKillRingMax);
		}

		// Use Dialog Setting
		szEntry.LoadString(IDS_REG_ENTRY_USE_DIALOG_SETTING);
		if (bSaveAndValidate) {	// retrieve
			BOOL bUseDialogSetting = AfxGetApp()->GetProfileInt(szApplicationName,szEntry, 1);
			m_XkeymacsData[nApplicationID].SetUseDialogSetting(bUseDialogSetting);
		} else {				// initialize
			BOOL bUseDialogSetting = m_XkeymacsData[nApplicationID].GetUseDialogSetting();
			AfxGetApp()->WriteProfileInt(szApplicationName, szEntry, bUseDialogSetting);
		}

		// Setting Style
		szEntry.LoadString(IDS_REG_ENTRY_DISABLE_XKEYMACS);
		if (bSaveAndValidate) {	// retrieve
			int nSettingStyle = SETTING_SPECIFIC;
			if (AfxGetApp()->GetProfileInt(szApplicationName, szEntry, 0) != 0) {
				nSettingStyle = SETTING_DISABLE;
			}
			m_XkeymacsData[nApplicationID].SetSettingStyle(nSettingStyle);
		} else {				// initialize
			BOOL bDisableXkeymacs = FALSE;
			if (m_XkeymacsData[nApplicationID].GetSettingStyle() == SETTING_DISABLE) {
				bDisableXkeymacs = TRUE;
			}
			AfxGetApp()->WriteProfileInt(szApplicationName, szEntry, bDisableXkeymacs);
		}

		// Ignore Meta Ctrl+? when it is undefined.
		szEntry.LoadString(IDC_REG_ENTRY_IGNORE_META_CTRL);
		if (bSaveAndValidate) {	// retrieve
			m_XkeymacsData[nApplicationID].SetIgnoreUndefinedMetaCtrl(AfxGetApp()->GetProfileInt(szApplicationName, szEntry, 0));
		} else {				// initialize
			AfxGetApp()->WriteProfileInt(szApplicationName, szEntry, m_XkeymacsData[nApplicationID].GetIgnoreUndefinedMetaCtrl());
		}

		// Ignore C-x ? when it is undefined.
		szEntry.LoadString(IDC_REG_ENTRY_IGNORE_C_X);
		if (bSaveAndValidate) {	// retrieve
			m_XkeymacsData[nApplicationID].SetIgnoreUndefinedC_x(AfxGetApp()->GetProfileInt(szApplicationName, szEntry, 0));
		} else {				// initialize
			AfxGetApp()->WriteProfileInt(szApplicationName, szEntry, m_XkeymacsData[nApplicationID].GetIgnoreUndefinedC_x());
		}

		// Enable CUA-mode
		szEntry.LoadString(IDC_REG_ENTRY_ENABLE_CUA);
		if (bSaveAndValidate) {	// retrieve
			m_XkeymacsData[nApplicationID].SetEnableCUA(AfxGetApp()->GetProfileInt(szApplicationName, szEntry, 0));
		} else {				// initialize
			AfxGetApp()->WriteProfileInt(szApplicationName, szEntry, m_XkeymacsData[nApplicationID].GetEnableCUA());
		}
	}
}

void CProfile::LoadRegistryData()
{
	CDotXkeymacs::Load();
	UpdateRegistryData(TRUE);
}

void CProfile::SaveRegistryData()
{
	DeleteAllRegistryData();
	UpdateRegistryData(FALSE);
	SetDllData();
}

void CProfile::SetDllData()
{
	CMainFrame *pMainFrame = (CMainFrame*)AfxGetMainWnd();

	pMainFrame->m_pXkeymacsDll->ClearFunctionDefinition();
	for (int nFunctionID = 0; nFunctionID < CDotXkeymacs::GetFunctionNumber(); ++nFunctionID) {
		pMainFrame->m_pXkeymacsDll->SetFunctionDefinition(nFunctionID, CDotXkeymacs::GetFunctionDefinition(nFunctionID));
	}

	for (int nApplicationID = 0; nApplicationID < MAX_APP; ++nApplicationID) {

		CString szApplicationName = m_XkeymacsData[nApplicationID].GetApplicationName();

		if (szApplicationName.IsEmpty()) {
			pMainFrame->m_pXkeymacsDll->Clear(nApplicationID);
			continue;
		}

		pMainFrame->m_pXkeymacsDll->SetApplicationName(nApplicationID, szApplicationName);
		pMainFrame->m_pXkeymacsDll->SetCommandID(nApplicationID, CONTROL, 'X', 0);

		for (int nCommandType = 0; nCommandType < MAX_COMMAND_TYPE; ++nCommandType) {
			for (int nKey = 0; nKey < MAX_KEY; ++nKey) {
				int nCommandID = m_XkeymacsData[nApplicationID].GetCommandID(nCommandType, nKey);
				pMainFrame->m_pXkeymacsDll->SetCommandID(nApplicationID, nCommandType, nKey, nCommandID);
				if ((nCommandType & CONTROLX) && nCommandID) {
					pMainFrame->m_pXkeymacsDll->SetCommandID(nApplicationID, CONTROL, 'X', 1);			// Commands[1] is C-x
				}
			}
		}

		for (int nFunctionID = 0; nFunctionID < CDotXkeymacs::GetFunctionNumber(); ++nFunctionID) {
			for (int nKeyID = 0; nKeyID < CDotXkeymacs::GetKeyNumber(nFunctionID, nApplicationID); ++nKeyID) {
				int nCommandType = 0;
				int nKey = 0;
				CDotXkeymacs::GetKey(nFunctionID, nApplicationID, nKeyID, &nCommandType, &nKey);
				pMainFrame->m_pXkeymacsDll->SetFunctionKey(nFunctionID, nApplicationID, nCommandType, nKey);
				if (nCommandType & CONTROLX) {
					pMainFrame->m_pXkeymacsDll->SetCommandID(nApplicationID, CONTROL, 'X', 1);			// Commands[1] is C-x
				}
			}
		}

		pMainFrame->m_pXkeymacsDll->SetKillRingMax(nApplicationID, m_XkeymacsData[nApplicationID].GetKillRingMax());
		pMainFrame->m_pXkeymacsDll->SetUseDialogSetting(nApplicationID, m_XkeymacsData[nApplicationID].GetUseDialogSetting());
		pMainFrame->m_pXkeymacsDll->SetSettingStyle(nApplicationID, m_XkeymacsData[nApplicationID].GetSettingStyle());
		pMainFrame->m_pXkeymacsDll->SetIgnoreUndefinedMetaCtrl(nApplicationID, m_XkeymacsData[nApplicationID].GetIgnoreUndefinedMetaCtrl());
		pMainFrame->m_pXkeymacsDll->SetIgnoreUndefinedC_x(nApplicationID, m_XkeymacsData[nApplicationID].GetIgnoreUndefinedC_x());
		pMainFrame->m_pXkeymacsDll->SetEnableCUA(nApplicationID, m_XkeymacsData[nApplicationID].GetEnableCUA());
	}
}

void CProfile::ReadKeyBind(int *pnCommandType, int *pnKey, char *szKeyBind)
{
	*pnCommandType = KeyBind2CommandType(szKeyBind);
	*pnKey = KeyBind2Key(szKeyBind + strlen(CommandType2String(*pnCommandType)));
}

CString CProfile::WriteKeyBind(int nCommandType, int nKey)
{
	CString szKeyBind;
	szKeyBind.Format("%s%s", CommandType2String(nCommandType), Key2String(nKey));
	return szKeyBind;
}

int CProfile::KeyBind2CommandType(char *szKeyBind)
{
	for (int nCommandType = MAX_COMMAND_TYPE - 1; nCommandType; --nCommandType) {
		if (IsCommandType(nCommandType, szKeyBind)) {
			return nCommandType;
		}
	}
	return NONE;
}

int CProfile::KeyBind2Key(char *szKey)
{
	for (int nKey = 1; nKey < 0xff; ++nKey) {
		if (!strcmp(szKey, Key2String(nKey))) {
			return nKey;
		}
	}
	return 0;
}

char * CProfile::CommandType2String(int nCommandType)
{
	if (nCommandType < 0 || sizeof(CommandTypes) / sizeof(CommandTypes[0]) <= nCommandType) {
		ASSERT(0);
		nCommandType = NONE;
	}
	return CommandTypes[nCommandType].szCommandTypeName;
}

char * CProfile::Key2String(int nKey)
{
	if (CProfile::Is106Keyboard()) {
		switch (nKey) {
		case 0xBA:
			return ":";
		case 0xBB:
			return ";";
		case 0xC0:
			return "@";
		case 0xDE:
			return "^";
		default:
			break;
		}
	}

	if (nKey < 0 || sizeof(KeyNames) / sizeof(KeyNames[0]) <= nKey) {
		ASSERT(0);
		nKey = 0;
	}
	return KeyNames[nKey].name;
}

BOOL CProfile::IsCommandType(int nCommandType, char *szKeyBind)
{
	char *szCommandType = CommandType2String(nCommandType);

	if (!strnicmp(szKeyBind, szCommandType, strlen(szCommandType))) {
		return TRUE;
	}

	return FALSE;
}

void CProfile::SaveKeyBind(CString szApplicationName, int nCommandID, int nCommandType, int nKey)
{
	if (!nCommandID) {
		return;
	}

	CString szCommandName = CXkeymacsData::GetCommandName(nCommandID);
	if (szCommandName.IsEmpty()) {
		return;
	}

	SaveKeyBind(szApplicationName, szCommandName, nCommandType, nKey);
}

void CProfile::SaveKeyBind(CString szApplicationName, CString szCommandName, int nCommandType, int nKey)
{
	CString szKeyBind = WriteKeyBind(nCommandType, nKey);
	CString szSubKey(MAKEINTRESOURCE(IDS_REGSUBKEY_DATA));
	szSubKey += "\\" + szApplicationName + "\\" + szCommandName;
	if (!szKeyBind.IsEmpty()) {
		szSubKey += "\\" + szKeyBind;
	}

	HKEY hKey = NULL;
	if (RegCreateKeyEx(HKEY_CURRENT_USER, szSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) == ERROR_SUCCESS) {
		RegCloseKey(hKey);
	}
}

void CProfile::SaveCommand(CString szApplicationName, int nCommandID)
{
	SaveKeyBind(szApplicationName, nCommandID, 0, 0);
}

void CProfile::AddKeyBind2C_(CString szApplicationName, BYTE bVk)
{
	for (int nCommandID = 0; nCommandID < sizeof(Commands) / sizeof(Commands[0]); ++nCommandID) {
		if (Commands[nCommandID].fCommand == CCommands::C_) {
			break;
		}
	}

	SaveKeyBind(szApplicationName, nCommandID, NONE, bVk);
}

void CProfile::LevelUp()
{
	const int nDefalutLevel = 0;
	const int nLatestLevel = 3;

	CString szSection;
	CString szEntry;
	szEntry.Format("Level");

//	const int nCurrentLevel = AfxGetApp()->GetProfileInt(szSection, szEntry, nDefalutLevel);
	switch (AfxGetApp()->GetProfileInt(szSection, szEntry, nDefalutLevel)) {
	case nDefalutLevel:
		{
			for (int nApplicationID = 0; nApplicationID < MAX_APP; ++nApplicationID) {
				CString szEntry;
				szEntry.Format(IDS_REG_ENTRY_APPLICATION, nApplicationID);

				CString szApplicationName;
				szApplicationName = AfxGetApp()->GetProfileString(CString(MAKEINTRESOURCE(IDS_REG_SECTION_APPLICATION)), szEntry);
				if (szApplicationName.IsEmpty()) {
					continue;
				}

				AddKeyBind2C_(szApplicationName, VK_LCONTROL);
				AddKeyBind2C_(szApplicationName, VK_RCONTROL);
			}
		}
		// Do NOT write break; here.
	case 1:
		{
			for (int nApplicationID = 0; nApplicationID < MAX_APP; ++nApplicationID) {
				CString szEntry;
				szEntry.Format(IDS_REG_ENTRY_APPLICATION, nApplicationID);

				CString szApplicationName;
				szApplicationName = AfxGetApp()->GetProfileString(CString(MAKEINTRESOURCE(IDS_REG_SECTION_APPLICATION)), szEntry);
				if (szApplicationName.IsEmpty()) {
					continue;
				}

				// Set kill-ring-max 1 if it is 0.
				if (!AfxGetApp()->GetProfileInt(szApplicationName, CString(MAKEINTRESOURCE(IDS_REG_ENTRY_KILL_RING_MAX)), 0)) {
					AfxGetApp()->WriteProfileInt(szApplicationName, CString(MAKEINTRESOURCE(IDS_REG_ENTRY_KILL_RING_MAX)), 1);
				}
			}
		}
		// Do NOT write break; here.
	case 2:
		{
			for (int nApplicationID = 0; nApplicationID < MAX_APP; ++nApplicationID) {
				CString szEntry;
				szEntry.Format(IDS_REG_ENTRY_APPLICATION, nApplicationID);

				CString szApplicationName;
				szApplicationName = AfxGetApp()->GetProfileString(CString(MAKEINTRESOURCE(IDS_REG_SECTION_APPLICATION)), szEntry);
				if (szApplicationName.IsEmpty()) {
					continue;
				}

				// Chaged a label from Enter to newline.
				CString szSrcSubKey(MAKEINTRESOURCE(IDS_REGSUBKEY_DATA));
				szSrcSubKey += "\\" + szApplicationName + "\\" + "Enter";
				CString szDestSubKey(MAKEINTRESOURCE(IDS_REGSUBKEY_DATA));
				szDestSubKey += "\\" + szApplicationName + "\\" + "newline";
				HKEY hKeyDest = NULL;
				if (RegCreateKeyEx(HKEY_CURRENT_USER, szDestSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyDest, NULL) == ERROR_SUCCESS) {
					SHCopyKey(HKEY_CURRENT_USER, szSrcSubKey, hKeyDest, NULL);
					SHDeleteKey(HKEY_CURRENT_USER, szSrcSubKey);
					RegCloseKey(hKeyDest);
				}
			}
		}
		// Do NOT write break; here.
//	case 3:
//		foo();
//	...
//	case nLatestLevel-1:
//		bar();
		AfxGetApp()->WriteProfileInt(szSection, szEntry, nLatestLevel);
		break;
	default:
		break;
	}
}

void CProfile::InitDllData()
{
	LevelUp();
	LoadRegistryData();
	SetDllData();
}

void CProfile::ClearData(CString szCurrentApplication)
{
	for (int nApplicationID = 0; nApplicationID < MAX_APP; ++nApplicationID) {
		if (m_XkeymacsData[nApplicationID].GetApplicationName() == szCurrentApplication) {
			break;
		}
	}
	if (nApplicationID < MAX_APP) {
		m_XkeymacsData[nApplicationID].ClearAll();
	}
}

// return count of saved settings
int CProfile::GetSavedSettingCount()
{
	int nSavedSetting = 0;

	for (int nApplicationID = 0; nApplicationID < MAX_APP; ++nApplicationID) {
		CString szApplicationName;
		szApplicationName = m_XkeymacsData[nApplicationID].GetApplicationName();
		if (!szApplicationName.IsEmpty()) {
			++nSavedSetting;
		}
	}

	return nSavedSetting;
}

void CProfile::InitApplicationList(CComboBox *cApplicationList)
{
	cApplicationList->ResetContent();

	m_dwTasks = GetTaskList(m_TaskList, MAX_TASKS);

	EnumWindows(EnumWindowsProc, (LPARAM)cApplicationList);

	for (int i = 0; i < MAX_APP; ++i) {
		CString szApplicationName	= m_XkeymacsData[i].GetApplicationName();
		CString szApplicationTitle	= m_XkeymacsData[i].GetApplicationTitle();

		CString szListItem;
		szListItem.Format(IDS_APPLICATION_LIST_ITEM, szApplicationTitle, szApplicationName);
		if (IsNotSameString(cApplicationList, szListItem)
		 && !IsDefault(szApplicationName)
		 && !IsDialog(szApplicationName)
		 && !szApplicationName.IsEmpty()) {
			cApplicationList->AddString(szListItem);
		}
	}

	// Add IME
	HKL hKL = GetKeyboardLayout(0);
	if (ImmIsIME(hKL)) {
		char szDummy;

		UINT nIMEDescription = ImmGetDescription(hKL, &szDummy, 0);
		nIMEDescription += sizeof(char);	// for NULL
		char *szIMEDescription = new char[nIMEDescription];
		ImmGetDescription(hKL, szIMEDescription, nIMEDescription);

		UINT nIMEFileName = ImmGetIMEFileName(hKL, &szDummy, 0);
		nIMEFileName += sizeof(char);
		char *szIMEFileName = new char[nIMEFileName];
		ImmGetIMEFileName(hKL, szIMEFileName, nIMEFileName);

		CString szIMETitle;
		szIMETitle.Format(IDS_APPLICATION_LIST_ITEM, szIMEDescription, szIMEFileName);
		if (IsNotSameString(cApplicationList, szIMETitle)
		 && (nIMEDescription || nIMEFileName)) {
			cApplicationList->AddString(szIMETitle);
		}

		delete[] szIMEDescription;
		szIMEDescription = NULL;
		delete[] szIMEFileName;
		szIMEFileName = NULL;
	}

	// Add Dialog
	cApplicationList->InsertString(0, CString(MAKEINTRESOURCE(IDS_DIALOG_TITLE)));

	// Add Default
	cApplicationList->InsertString( 0, CString(MAKEINTRESOURCE(IDS_DEFAULT_TITLE)));
	cApplicationList->SelectString(-1, CString(MAKEINTRESOURCE(IDS_DEFAULT_TITLE)));
}

DWORD CProfile::GetTaskList(PTASK_LIST pTask, DWORD dwNumTasks)
{
	for (int i = 0; i < MAX_TASKS; ++i) {
		ZeroMemory(&pTask[i], sizeof(PTASK_LIST));
	}

	OSVERSIONINFO verInfo = {0};
	verInfo.dwOSVersionInfoSize = sizeof (verInfo);
	GetVersionEx(&verInfo);
	if (verInfo.dwPlatformId == VER_PLATFORM_WIN32_NT
	 && verInfo.dwMajorVersion < 5) {
		return GetTaskListNT(pTask, dwNumTasks);
	}

	HMODULE hKernel = GetModuleHandle("KERNEL32.DLL");
	if (!hKernel) {
		return 0;
	}

	CREATESNAPSHOT pCreateToolhelp32Snapshot = (CREATESNAPSHOT)GetProcAddress(hKernel, "CreateToolhelp32Snapshot");
	if (!pCreateToolhelp32Snapshot) {
		return 0;
	}

	PROCESSWALK pProcess32First = (PROCESSWALK)GetProcAddress(hKernel, "Process32First");
	if (!pProcess32First) {
		return 0;
	}

	PROCESSWALK pProcess32Next = (PROCESSWALK)GetProcAddress(hKernel, "Process32Next");
	if (!pProcess32Next) {
		return 0;
	}

	HANDLE hProcessSnap = pCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hProcessSnap == (HANDLE)-1) {
		return 0;
	}

	DWORD dwTaskCount = 0;
	PROCESSENTRY32 processEntry32 = {sizeof(PROCESSENTRY32)};
	if (pProcess32First(hProcessSnap, &processEntry32)) {
		do {
			LPSTR pCurChar;
			for (pCurChar = processEntry32.szExeFile + lstrlen(processEntry32.szExeFile); *pCurChar != '\\' && pCurChar != processEntry32.szExeFile; --pCurChar) {
				;
			}
			if (*pCurChar == '\\') {
				++pCurChar;
			}

			lstrcpy(pTask->ProcessName, pCurChar);
			pTask->dwProcessId = processEntry32.th32ProcessID;

			++dwTaskCount;
			++pTask;
		} while (dwTaskCount < dwNumTasks && pProcess32Next(hProcessSnap, &processEntry32));
	}

	CloseHandle(hProcessSnap);
	return dwTaskCount;
}

LPBYTE CProfile::GetCounters()
{
	LANGID lid = MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL);
	CString szSubKey;
	szSubKey.Format(CString(MAKEINTRESOURCE(IDS_REGSUBKEY_PERF)), lid);
	HKEY hKey = NULL;
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szSubKey, 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
		return NULL;
	}

	DWORD dwSize = 0;
	if (RegQueryValueEx(hKey, CString(MAKEINTRESOURCE(IDS_REGSUBKEY_COUNTERS)), NULL, NULL, NULL, &dwSize) != ERROR_SUCCESS) {
		RegCloseKey(hKey);
		return NULL;
	}
	LPBYTE pCounters = (LPBYTE) calloc(dwSize, sizeof(BYTE));
	if (pCounters == NULL) {
		RegCloseKey(hKey);
		return NULL;
	}
	if (RegQueryValueEx(hKey, CString(MAKEINTRESOURCE(IDS_REGSUBKEY_COUNTERS)), NULL, NULL, pCounters, &dwSize) != ERROR_SUCCESS) {
		RegCloseKey(hKey);
		free(pCounters);
		return NULL;
	}
	RegCloseKey(hKey);
	return pCounters;
}

BOOL CProfile::GetProcessInfo(CString *szProcessName, DWORD *dwProcessId)
{
	LPBYTE pCounters = GetCounters();
	if (!pCounters) {
		return FALSE;
	}
	LPSTR pTopOfString = (LPSTR)pCounters;
	while (*pTopOfString) {
		if (stricmp(pTopOfString, CString(MAKEINTRESOURCE(IDS_PROCESS))) == 0) {
			for (LPSTR p2 = pTopOfString - 2; isdigit(*p2); --p2) {
				;
			}
			szProcessName->Format("%s", p2 + 1);	// 230
		} else if (stricmp(pTopOfString, CString(MAKEINTRESOURCE(IDS_PROCESSID))) == 0) {
			for (LPSTR p2 = pTopOfString - 2; isdigit(*p2); --p2) {
				;
			}
			*dwProcessId = atol(p2 + 1);			// 784
		}
		pTopOfString += (strlen(pTopOfString) + 1);
	}
	free(pCounters);
	return TRUE;
}

PPERF_DATA_BLOCK CProfile::GetPerformanceData(CString szProcessName)
{
	DWORD dwSize = INITIAL_SIZE;
	PPERF_DATA_BLOCK pPerformanceData = (PPERF_DATA_BLOCK) calloc(dwSize, sizeof(BYTE));
	if (pPerformanceData == NULL) {
		return NULL;
	}

	for (;;) {
		switch (RegQueryValueEx(HKEY_PERFORMANCE_DATA, szProcessName, NULL, NULL, (LPBYTE)pPerformanceData, &dwSize)) {
		case ERROR_SUCCESS:
			if (0 < dwSize
			 && pPerformanceData->Signature[0] == (WCHAR)'P'
			 && pPerformanceData->Signature[1] == (WCHAR)'E'
			 && pPerformanceData->Signature[2] == (WCHAR)'R'
			 && pPerformanceData->Signature[3] == (WCHAR)'F') {
				return pPerformanceData;
			}
		case ERROR_MORE_DATA:
			dwSize += EXTEND_SIZE;
			pPerformanceData = (PPERF_DATA_BLOCK) realloc(pPerformanceData, dwSize);
			if (!pPerformanceData) {
				return NULL;
			}
			memset(pPerformanceData, 0, dwSize);
			break;
		default:
			free(pPerformanceData);
			return NULL;
		}
	}
}

// Add running application's names to the list
// only for _Windows NT_
DWORD CProfile::GetTaskListNT(PTASK_LIST pTask, DWORD dwNumTasks)
{
	CString szProcessName;
	DWORD dwProcessIdTitle = 0;
	if (!GetProcessInfo(&szProcessName, &dwProcessIdTitle)) {
		return dwNumTasks;
	}

	PPERF_DATA_BLOCK pPerformanceData = GetPerformanceData(szProcessName);
	if (!pPerformanceData) {
		return dwNumTasks;
	}

	PPERF_OBJECT_TYPE pObj = (PPERF_OBJECT_TYPE) ((DWORD)pPerformanceData + pPerformanceData->HeaderLength);
	PPERF_COUNTER_DEFINITION pCounterDef = (PPERF_COUNTER_DEFINITION) ((DWORD)pObj + pObj->HeaderLength);
	DWORD dwProcessIdCounter = 0;
	for (DWORD i = 0; i < pObj->NumCounters; ++i) {
		if (pCounterDef->CounterNameTitleIndex == dwProcessIdTitle) {
			dwProcessIdCounter = pCounterDef->CounterOffset;
			break;
		}
		++pCounterDef;
	}

	dwNumTasks = min(dwNumTasks - 1, (DWORD)pObj->NumInstances);
	PPERF_INSTANCE_DEFINITION pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD)pObj + pObj->DefinitionLength);
	for (i = 0; i < dwNumTasks; ++i) {
		LPSTR pProcessName = (LPSTR) ((DWORD)pInst + pInst->NameOffset);

		CHAR szProcessName[MAX_PATH] = {'\0'};
		if (!WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pProcessName, -1, szProcessName, sizeof(szProcessName), NULL, NULL)) {
			strcpy(pTask->ProcessName, CString(MAKEINTRESOURCE(IDS_UNKNOWN_TASK)));
		}

		if (strlen(szProcessName)+4 <= sizeof(pTask->ProcessName)) {
			strcpy(pTask->ProcessName, szProcessName);
			strcat(pTask->ProcessName, CString(MAKEINTRESOURCE(IDS_EXTENSION_EXECUTABLE)));
		}

		PPERF_COUNTER_BLOCK pCounter = (PPERF_COUNTER_BLOCK) ((DWORD)pInst + pInst->ByteLength);
		pTask->dwProcessId = *((LPDWORD) ((DWORD)pCounter + dwProcessIdCounter));
		if (pTask->dwProcessId == 0) {
			pTask->dwProcessId = (DWORD) -2;
		}

		++pTask;
		pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD)pCounter + pCounter->ByteLength);
	}

	free(pPerformanceData);
	return dwNumTasks;
}

// return application index
// and update setting style
// if there is NOT the application in the data, this function takes care of it.
int CProfile::GetApplicationIndex(CString szApplicationName, BOOL bSaveAndValidate, int *nSettingStyle)
{
	if (!bSaveAndValidate) {	// SetDialogData
		*nSettingStyle = SETTING_UNDEFINED;
	}

	int nApplicationID = GetApplicationIndex(szApplicationName);

	if (nApplicationID == MAX_APP) {
		if (bSaveAndValidate) {	// GetDialogData
			for (nApplicationID = 0; nApplicationID < MAX_APP; ++nApplicationID) {
				CString sz = m_XkeymacsData[nApplicationID].GetApplicationName();
				if (sz.IsEmpty()) {
					m_XkeymacsData[nApplicationID].SetApplicationName(szApplicationName);
					break;
				}
			}
			if (nApplicationID == MAX_APP) {
				return nApplicationID;
			}
		} else {				// SetDialogData
			for (nApplicationID = 0; nApplicationID < MAX_APP; ++nApplicationID) {
				if (IsDefault(m_XkeymacsData[nApplicationID].GetApplicationName())) {
					*nSettingStyle = SETTING_DEFAULT;
					break;
				}
			}
			if (nApplicationID == MAX_APP) {
				return nApplicationID;
			}
		}
	}

	if (bSaveAndValidate) {	// GetDialogData
		m_XkeymacsData[nApplicationID].SetSettingStyle(*nSettingStyle);
	} else {				// SetDialogData
		if (*nSettingStyle == SETTING_UNDEFINED) {	// It means that *nSettingStyle != SETTING_DEFAULT.
			*nSettingStyle = m_XkeymacsData[nApplicationID].GetSettingStyle();
		}
	}

	return nApplicationID;
}

BOOL CProfile::IsTheString(CString sz, UINT nID)
{
	return sz == CString(MAKEINTRESOURCE(nID));
}

// if sz is "Default", return TRUE
BOOL CProfile::IsDefault(CString sz)
{
	return IsTheString(sz, IDS_DEFAULT);
}

// if sz is "Dialog", return TRUE
BOOL CProfile::IsDialog(CString sz)
{
	return IsTheString(sz, IDS_DIALOG);
}

void CProfile::GetApplicationTitle(CComboBox *cApplicationList, CString &rList, int nIndex)
{
	if (0 <= nIndex) {
		cApplicationList->GetLBText(nIndex, rList);
	} else {
		cApplicationList->GetWindowText(rList);
	}

	if (IsTheString(rList, IDS_DEFAULT_TITLE)) {
		rList.LoadString(IDS_DEFAULT);
	}

	if (IsTheString(rList, IDS_DIALOG_TITLE)) {
		rList.LoadString(IDS_DIALOG);
	}

	return;
}

void CProfile::UpdateApplicationTitle(CComboBox *cApplicationList, CString szCurrentApplication, int nApplicationID, BOOL bSaveAndValidate)
{
	static CString szApplicationTitle;
	if (bSaveAndValidate) {	// GetDialogData
		if (!CProfile::IsDefault(szCurrentApplication)) {
			m_XkeymacsData[nApplicationID].SetApplicationTitle(szApplicationTitle);
		}
		szApplicationTitle.Empty();
	} else {				// SetDialogData
		CString szListItem;
		CProfile::GetApplicationTitle(cApplicationList, szListItem);
		int nEndTitle = szListItem.ReverseFind('(');
		if (nEndTitle > 0) {
			szApplicationTitle = szListItem.Left(nEndTitle);
		}
	}
}

void CProfile::SetCommandID(int nApplicationID, int nCommandType, int nKey, int nCommandID)
{
	if (nKey == 0xf0 && Commands[nCommandID].fCommand == CCommands::C_) {
		// Change CommandID C_Eisu
		for (nCommandID = 1; nCommandID < sizeof(Commands) / sizeof(Commands[0]); ++nCommandID) {
			if (Commands[nCommandID].fCommand == CCommands::C_Eisu) {
				break;
			}
		}
	}
	m_XkeymacsData[nApplicationID].SetCommandID(nCommandType, nKey, nCommandID);
}

int CProfile::GetCommandID(int nApplicationID, int nCommandType, int nKey)
{
	int nCommandID = m_XkeymacsData[nApplicationID].GetCommandID(nCommandType, nKey);
	if (nKey == 0xf0 && Commands[nCommandID].fCommand == CCommands::C_Eisu) {
		// Change CommandID C_
		for (nCommandID = 1; nCommandID < sizeof(Commands) / sizeof(Commands[0]); ++nCommandID) {
			if (Commands[nCommandID].fCommand == CCommands::C_) {
				break;
			}
		}
	}
	return nCommandID;
}

void CProfile::SetKillRingMax(int nApplicationID, int nKillRingMax)
{
	m_XkeymacsData[nApplicationID].SetKillRingMax(nKillRingMax);
}

int CProfile::GetKillRingMax(int nApplicationID)
{
	return m_XkeymacsData[nApplicationID].GetKillRingMax();
}

void CProfile::SetUseDialogSetting(int nApplicationID, BOOL bUseDialogSetting)
{
	m_XkeymacsData[nApplicationID].SetUseDialogSetting(bUseDialogSetting);
}

BOOL CProfile::GetUseDefaultSetting(int nApplicationID)
{
	return m_XkeymacsData[nApplicationID].GetUseDialogSetting();
}

void CProfile::DeleteAllRegistryData()
{
	HKEY hkey = NULL;
	if (RegOpenKeyEx(HKEY_CURRENT_USER, CString(MAKEINTRESOURCE(IDS_REGSUBKEY_DATA)), 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS) {
		// I am sure that I have to do only one time, but...
		for (int nApplicationID = 0; nApplicationID < MAX_APP; ++nApplicationID) {
			DWORD dwIndex = 0;
			char szName[SUB_KEY_NAME_LENGTH] = {'\0'};
			DWORD dwName = sizeof(szName);
			FILETIME filetime;

			while (RegEnumKeyEx(hkey, dwIndex, szName, &dwName, NULL, NULL, NULL, &filetime) == ERROR_SUCCESS) {
//				RegDeleteKey(hkey, szName);
				SHDeleteKey(hkey, szName);
				ZeroMemory(szName, sizeof(szName));
				dwName = sizeof(szName);
			}
		}
		RegCloseKey(hkey);
	}
}

int CProfile::GetCurrentApplicationID(CComboBox *cApplicationList, CString szCurrentApplication)
{
	int nCounter = cApplicationList->GetCount();
	CString szListItem;
	int nCurSel = cApplicationList->GetCurSel();

	for (int i = 0; i < nCounter; ++i) {
		cApplicationList->SetCurSel(i);
		CProfile::GetApplicationTitle(cApplicationList, szListItem);
		if (szListItem.Find(szCurrentApplication) != -1) {
			cApplicationList->SetCurSel(nCurSel);
			return i;
		}
	}
	return -1;
}

void CProfile::CopyData(CString szDestinationApplication, CString szSourceApplication)
{
	int nSettingStyle = SETTING_SPECIFIC;
	int nDestinationApplication = GetApplicationIndex(szDestinationApplication, TRUE, &nSettingStyle);
	int nSourceApplication = GetApplicationIndex(szSourceApplication);

	CString szApplicationName = m_XkeymacsData[nDestinationApplication].GetApplicationName();
	CString szApplicationTitle = m_XkeymacsData[nDestinationApplication].GetApplicationTitle();
	CString szWindowText = m_XkeymacsData[nDestinationApplication].GetWindowText();
	int nWindowTextType = m_XkeymacsData[nDestinationApplication].GetWindowTextType();

	m_XkeymacsData[nDestinationApplication] = m_XkeymacsData[nSourceApplication];

	m_XkeymacsData[nDestinationApplication].SetApplicationName(szApplicationName);
	m_XkeymacsData[nDestinationApplication].SetApplicationTitle(szApplicationTitle);
	m_XkeymacsData[nDestinationApplication].SetWindowText(szWindowText);
	m_XkeymacsData[nDestinationApplication].SetWindowTextType(nWindowTextType);
}

// return application index
// if there is NOT the application in the data, return MAX_APP
int CProfile::GetApplicationIndex(CString szApplicationName)
{
	int nApplicationID = 0;
	for (nApplicationID = 0; nApplicationID < MAX_APP; ++nApplicationID) {
		if (m_XkeymacsData[nApplicationID].GetApplicationName() == szApplicationName) {
			break;
		}
	}
	return nApplicationID;
}

BOOL CProfile::Is106Keyboard()
{
	enum KEYBOARD_TYPE {UNKNOWN_KEYBOARD, ENGLISH_KEYBOARD, JAPANESE_KEYBOARD};
	static KEYBOARD_TYPE keyboard = UNKNOWN_KEYBOARD;

	if (keyboard == UNKNOWN_KEYBOARD) {
		OSVERSIONINFO verInfo = {0};
		verInfo.dwOSVersionInfoSize = sizeof (verInfo);
		GetVersionEx(&verInfo);

		DWORD subtype = 0;
		DWORD cbData = sizeof(subtype);

		if (verInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
			HKEY hKey = NULL;
			CString szSubKey("SYSTEM\\CurrentControlSet\\Services\\i8042prt\\Parameters");
			if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szSubKey, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
				static const CString szValueName("OverrideKeyboardSubtype");
				if (RegQueryValueEx(hKey, szValueName, NULL, NULL, (LPBYTE)&subtype, &cbData) != ERROR_SUCCESS) {
					subtype = 0;
				}
				RegCloseKey(hKey);
			}
		} else if (verInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
			subtype = GetPrivateProfileInt("keyboard", "subtype", 0, "system.ini");
		}

		keyboard = (subtype & 0x02) ? JAPANESE_KEYBOARD : ENGLISH_KEYBOARD;
	}

	return keyboard == JAPANESE_KEYBOARD;
}

void CProfile::LoadScanCodeMap()
{
	memset(ScanCodeMap, 0, sizeof(ScanCodeMap));
	memset(CurrentScanCodeMap, 0, sizeof(CurrentScanCodeMap));

	CString szSubKey;
	CString szValueName;
	if (IsNT()) {
		szSubKey.LoadString(IDS_REGSUBKEY_KEYBOARD_LAYOUT);
		szValueName.LoadString(IDS_SCANCODE_MAP);
	} else if (Is9x()) {
		szSubKey.LoadString(IDS_REGSUBKEY_KEY_REMAP);
		szValueName.LoadString(IDS_0);
	} else {
		return;
	}

	HKEY hKey = NULL;
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szSubKey, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
		// get data size
		DWORD dwType = REG_BINARY;
		DWORD dwData = 0;
		RegQueryValueEx(hKey, szValueName, NULL, &dwType, NULL, &dwData);

		// get data
		LPBYTE lpData = new BYTE[dwData];
		if (lpData) {
			RegQueryValueEx(hKey, szValueName, NULL, &dwType, lpData, &dwData);
		}
		RegCloseKey(hKey);

		if (IsNT()) {
			if (lpData && dwData) {
				DWORD offset = 0;
				offset += 8;	// skip Version Information and Flags
				DWORD *pdwMappings = (DWORD *)(lpData + offset);
				offset += 4;	// skip Number of Mappings
				DWORD *pdwNullTerminator = (DWORD *)(lpData + dwData - 4);

				if (4 * *pdwMappings + 12 != dwData) {
					// illegal data
				} else if (*pdwNullTerminator != 0) {
					// illegal data
				} else {
					while (offset < dwData - 4) {
						ScanCodeMapping_t *pScanCodeMapping = (ScanCodeMapping_t *)(lpData + offset);
						offset += 4;	// go to next data

						CurrentScanCodeMap[PrefixedScanCode2ID(pScanCodeMapping->original.nPrefixedScanCode)][pScanCodeMapping->original.nScanCode].nPrefixedScanCode = pScanCodeMapping->current.nPrefixedScanCode;
						CurrentScanCodeMap[PrefixedScanCode2ID(pScanCodeMapping->original.nPrefixedScanCode)][pScanCodeMapping->original.nScanCode].nScanCode = pScanCodeMapping->current.nScanCode;
						ScanCodeMap[PrefixedScanCode2ID(pScanCodeMapping->original.nPrefixedScanCode)][pScanCodeMapping->original.nScanCode].nPrefixedScanCode = pScanCodeMapping->current.nPrefixedScanCode;
						ScanCodeMap[PrefixedScanCode2ID(pScanCodeMapping->original.nPrefixedScanCode)][pScanCodeMapping->original.nScanCode].nScanCode = pScanCodeMapping->current.nScanCode;
					}
				}
			}
		}
		delete[] lpData;
		lpData = NULL;
	}
}


void CProfile::SaveScanCodeMap()
{
	CString szSubKey;
	CString szValueName;
	if (IsNT()) {
		szSubKey.LoadString(IDS_REGSUBKEY_KEYBOARD_LAYOUT);
		szValueName.LoadString(IDS_SCANCODE_MAP);
	} else if (Is9x()) {
		szSubKey.LoadString(IDS_REGSUBKEY_KEY_REMAP);
		szValueName.LoadString(IDS_0);
	} else {
		return;
	}

	HKEY hKey = NULL;
	if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL) == ERROR_SUCCESS) {
		if (IsNT()) {
			DWORD cbData = GetScanCodeLength();
			if (cbData <= 16) {
				RegDeleteValue(hKey, szValueName);
			} else {
				LPBYTE lpData = new BYTE[cbData];
				memset(lpData, 0, sizeof(BYTE) * cbData);

				{
					DWORD dwMappings = (cbData - 12) / 4;
					memmove(lpData + 8, &dwMappings, 4);
				}

				int offset = 12;
				for (int nPrefixedScanCodeID = 0; nPrefixedScanCodeID < 3; ++nPrefixedScanCodeID) {
					for (int nScanCode = 0; nScanCode < 256; ++nScanCode) {
						if (ScanCodeMap[nPrefixedScanCodeID][nScanCode].nScanCode) {
							ScanCodeMapping_t sScanCodeMapping = {'\0'};
							sScanCodeMapping.original.nPrefixedScanCode = PrefixedScanCodeID2Code(nPrefixedScanCodeID);
							sScanCodeMapping.original.nScanCode = (BYTE)nScanCode;
							sScanCodeMapping.current.nPrefixedScanCode = ScanCodeMap[nPrefixedScanCodeID][nScanCode].nPrefixedScanCode;
							sScanCodeMapping.current.nScanCode = ScanCodeMap[nPrefixedScanCodeID][nScanCode].nScanCode;
							memcpy(lpData + offset, &sScanCodeMapping, sizeof(sScanCodeMapping));
							offset += sizeof(sScanCodeMapping);
						}
					}
				}
				RegSetValueEx(hKey, szValueName, 0, REG_BINARY, lpData, cbData);

				delete[] lpData;
				lpData = NULL;
			}
		}
		RegCloseKey(hKey);
	}

	// Do you want to restart computer?
	if (ChangedKeyboardLayout()) {
		if (AfxMessageBox(CString(MAKEINTRESOURCE(IDS_RESTART_OR_NOT)), MB_YESNO | MB_ICONQUESTION) == IDYES) {
			RestartComputer();
		}
	}
}

// Return True if Windows 95, Windows 98, or Windows Me. 
BOOL CProfile::Is9x()
{
	OSVERSIONINFO info = {sizeof(OSVERSIONINFO)};
	GetVersionEx(&info);

	if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
		return TRUE;
	}
	return FALSE;
}

// Return True if Windows NT 4.0, Windows 2000, Windows XP, or Windows .NET Server.
BOOL CProfile::IsNT()
{
	OSVERSIONINFO info = {sizeof(OSVERSIONINFO)};
	GetVersionEx(&info);

	if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) {
		return TRUE;
	}
	return FALSE;
}

void CProfile::RestartComputer()
{
	if (!AdjustTokenPrivileges(SE_SHUTDOWN_NAME)) {
		return;
	}

	ExitWindowsEx(EWX_REBOOT, 0);
}

int CProfile::GetControlID(ScanCode_t scancode, BOOL bBase)
{
	for (int i = 0; ; ++i) {
		if (KeyboardLayouts[i].scancode.nPrefixedScanCode == scancode.nPrefixedScanCode
		 && KeyboardLayouts[i].scancode.nScanCode == scancode.nScanCode) {
			if (bBase) {
				return KeyboardLayouts[i].nBaseControlID;
			} else {
				return KeyboardLayouts[i].nCurrentControlID;
			}
		}
	}
	return 0;
}

int CProfile::GetBaseControlID(ScanCode_t scancode)
{
	return GetControlID(scancode, TRUE);
}

int CProfile::GetCurrentControlID(ScanCode_t scancode)
{
	return GetControlID(scancode, FALSE);
}

BOOL CProfile::GetScanCodeMap(ScanCode_t original, ScanCode_t *current)
{
	if (!current) {
		return FALSE;
	}
	*current = ScanCodeMap[PrefixedScanCode2ID(original.nPrefixedScanCode)][original.nScanCode];
	return 0 < current->nScanCode;
}

void CProfile::SetScanCodeMap(ScanCodeMapping_t ScanCodeMappeing)
{
	ScanCodeMap[PrefixedScanCode2ID(ScanCodeMappeing.original.nPrefixedScanCode)][ScanCodeMappeing.original.nScanCode] = ScanCodeMappeing.current;
}

int CProfile::PrefixedScanCode2ID(BYTE nPrefixedScanCode)
{
	int nID = 0;

	switch (nPrefixedScanCode) {
	case 0x00:
		nID = 0;
		break;
	case 0xe0:
		nID = 1;
		break;
	case 0xe1:
		nID = 2;
		break;
	default:
		// invalid scan code
		nID = 3;
		break;
	}

	return nID;
}

BYTE CProfile::PrefixedScanCodeID2Code(int nPrefixedScanCodeID)
{
	BYTE nCode = 0;

	switch (nPrefixedScanCodeID) {
	case 0:
		nCode = 0x00;
		break;
	case 1:
		nCode = 0xe0;
		break;
	case 2:
		nCode = 0xe1;
		break;
	default:
		ASSERT(0);
		break;
	}

	return nCode;
}

DWORD CProfile::GetScanCodeLength()
{
	DWORD dwScanCodeLength = 0;
	dwScanCodeLength += 4;	// Header: Version Information
	dwScanCodeLength += 4;	// Header: Flags
	dwScanCodeLength += 4;	// Header: Number of Mappings
	for (int nPrefixedScanCodeID = 0; nPrefixedScanCodeID < 3; ++nPrefixedScanCodeID) {
		for (int nScanCode = 0; nScanCode < 256; ++nScanCode) {
			if (ScanCodeMap[nPrefixedScanCodeID][nScanCode].nScanCode) {
				dwScanCodeLength += 4;	// Individual Mappings
			}
		}
	}
	dwScanCodeLength += 4;	// Null Terminator (0x00000000)
	return dwScanCodeLength;
}

BOOL CProfile::ChangedKeyboardLayout()
{
	for (int nPrefixedScanCodeID = 0; nPrefixedScanCodeID < 3; ++nPrefixedScanCodeID) {
		for (int nScanCode = 0; nScanCode < 256; ++nScanCode) {
			if (ScanCodeMap[nPrefixedScanCodeID][nScanCode].nPrefixedScanCode	!= CurrentScanCodeMap[nPrefixedScanCodeID][nScanCode].nPrefixedScanCode
			 || ScanCodeMap[nPrefixedScanCodeID][nScanCode].nScanCode			!= CurrentScanCodeMap[nPrefixedScanCodeID][nScanCode].nScanCode) {
				return TRUE;
			}
		}
	}

	return FALSE;
}

void CProfile::SetDraggingCursor()
{
	HCURSOR hCursor = (HCURSOR)LoadImage(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDC_DRAG_CURSOR),
										 IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
	SetCursor(hCursor);
}

void CProfile::SetNormalCursor()
{
	HCURSOR hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW),
										 IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
	SetCursor(hCursor);
}

void CProfile::SetNoCursor()
{
	HCURSOR hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_NO),
										 IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
	SetCursor(hCursor);
}

int CProfile::GetToolTipID(int nToolTipID)
{
	if (Is106Keyboard()) {
		switch (nToolTipID) {
		case IDS_EQUAL:			// ^
			nToolTipID = IDS_CARET;
			break;
		case IDS_SQUARE_BRA:	// @
			nToolTipID = IDS_AT_MARK;
			break;
		case IDS_SQUARE_CKET:	// [
			nToolTipID = IDS_SQUARE_BRA;
			break;
		case IDS_QUOTE:			// :
			nToolTipID = IDS_COLON;
			break;
		case IDS_BACK_QUOTE:	// Hankaku/Zenkaku
			nToolTipID = IDS_HANKAKU;
			break;
		case IDS_BACKSLASH:		// ]
			nToolTipID = IDS_SQUARE_CKET;
			break;
		}
	}

	return nToolTipID;
}

KeyboardLayout_t* CProfile::GetKeyboardLayouts(int nKey)
{
	for (int i = 0; i < sizeof(KeyboardLayouts) / sizeof(KeyboardLayouts[0]); ++i) {
		if (KeyboardLayouts[i].nBaseControlID == nKey
		 || KeyboardLayouts[i].nCurrentControlID == nKey) {
			return &KeyboardLayouts[i];
		}
	}
	return NULL;
}

BOOL CProfile::AdjustTokenPrivileges(LPCTSTR lpName)
{
	BOOL rc = TRUE;

	if (IsNT()) {
		HANDLE hToken;
		if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
			LUID luid;
			if (LookupPrivilegeValue(NULL, lpName, &luid)) {
				TOKEN_PRIVILEGES tp;
				tp.PrivilegeCount = 1;
				tp.Privileges[0].Luid = luid;
				tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

				if (!::AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
					rc = FALSE;
				}
			} else {
				rc = FALSE;
			}
			CloseHandle(hToken);
		} else {
			rc = FALSE;
		}
	}

	return rc;
}

BOOL CProfile::DiableTokenPrivileges()
{
	BOOL rc = TRUE;

	if (IsNT()) {
		HANDLE hToken;
		if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
			if (!::AdjustTokenPrivileges(hToken, TRUE, NULL, NULL, NULL, NULL)) {
				rc = FALSE;
			}
			CloseHandle(hToken);
		} else {
			rc = FALSE;
		}
	}

	return rc;
}

void CProfile::ExportProperties()
{
	if (!AdjustTokenPrivileges(SE_BACKUP_NAME)) {
		return;
	}

	CFileDialog oFileOpenDialog(FALSE, "reg", "xkeymacs", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, CString(MAKEINTRESOURCE(IDS_REGISTRATION_FILTER)));
	if (oFileOpenDialog.DoModal() == IDOK) {
		CString szCommandLine;
		szCommandLine.Format("regedit /e \"%s\" HKEY_CURRENT_USER\\%s", oFileOpenDialog.GetPathName(), CString(MAKEINTRESOURCE(IDS_REGSUBKEY_DATA)));
		CUtils::Run(szCommandLine, TRUE);	// regedit /e "x:\xkeymacs.reg" HKEY_CURRENT_USER\Software\Oishi\XKeymacs2
	}

	DiableTokenPrivileges();
	return;
}

void CProfile::ImportProperties()
{
	if (!AdjustTokenPrivileges(SE_RESTORE_NAME)) {
		return;
	}

	CFileDialog oFileOpenDialog(TRUE, "reg", "xkeymacs", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, CString(MAKEINTRESOURCE(IDS_REGISTRATION_FILTER)));
	if (oFileOpenDialog.DoModal() == IDOK) {
		CString szCommandLine;
		szCommandLine.Format("regedit \"%s\"", oFileOpenDialog.GetPathName());
		CUtils::Run(szCommandLine, TRUE);	// regedit "x:\xkeymacs.reg"
	}

	DiableTokenPrivileges();
	return;
}

BOOL CProfile::GetEnableCUA(int nApplicationID)
{
	return m_XkeymacsData[nApplicationID].GetEnableCUA();
}

void CProfile::SetEnableCUA(int nApplicationID, BOOL bEnableCUA)
{
	m_XkeymacsData[nApplicationID].SetEnableCUA(bEnableCUA);
}

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