// Customize.cpp : implementation file
//

#include "stdafx.h"
#include "xkeymacs.h"
#include "Customize.h"
#include "Profile.h"

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

/////////////////////////////////////////////////////////////////////////////
// CCustomize dialog

HHOOK CCustomize::m_hKeyboardHook = NULL;
CEdit *CCustomize::m_pNewKey = NULL;
CButton *CCustomize::m_pAssign = NULL;
CStatic *CCustomize::m_pCurrentlyAssigned = NULL;
int CCustomize::m_nApplicationID = 0;
int CCustomize::m_nAssignCommandType = 0;
int CCustomize::m_nAssignKey = 0;
int CCustomize::m_nCommandID = 0;
int CCustomize::m_nCommandIDs[MAX_COMMAND_TYPE][MAX_KEY] = {'\0'};

CCustomize::CCustomize(CWnd* pParent /*=NULL*/)
        : CDialog(CCustomize::IDD, pParent)
{
        //{{AFX_DATA_INIT(CCustomize)
        m_nSettingStyle = -1;
        //}}AFX_DATA_INIT
}


void CCustomize::DoDataExchange(CDataExchange* pDX)
{
        CDialog::DoDataExchange(pDX);
        //{{AFX_DATA_MAP(CCustomize)
        DDX_Control(pDX, IDC_DESCRIPTION, m_cDescription);
        DDX_Control(pDX, IDC_CURRENTLY_ASSIGNED, m_cCurrentlyAssigned);
        DDX_Control(pDX, IDC_RESET_ALL, m_cResetAll);
        DDX_Control(pDX, IDC_REMOVE, m_cRemove);
        DDX_Control(pDX, IDC_ASSIGN, m_cAssign);
        DDX_Control(pDX, IDC_NEW_KEY, m_cNewKey);
        DDX_Control(pDX, IDC_CURRENT_KEYS, m_cCurrentKeys);
        DDX_Control(pDX, IDC_COMMANDS, m_cCommands);
        DDX_Control(pDX, IDC_CATEGORY, m_cCategory);
        DDX_Control(pDX, IDC_APPLICATION, m_cApplication);
        DDX_Radio(pDX, IDC_SETTING_DEFAULT, m_nSettingStyle);
        //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CCustomize, CDialog)
        //{{AFX_MSG_MAP(CCustomize)
        ON_BN_CLICKED(IDC_SETTING_DEFAULT, OnSettingDefault)
        ON_BN_CLICKED(IDC_SETTING_DISABLE, OnSettingDisable)
        ON_BN_CLICKED(IDC_SETTING_SPECIFIC, OnSettingSpecific)
        ON_BN_CLICKED(IDC_APPLY, OnApply)
        ON_CBN_SELCHANGE(IDC_APPLICATION, OnSelchangeApplication)
        ON_CBN_DROPDOWN(IDC_APPLICATION, OnDropdownApplication)
        ON_CBN_SELCHANGE(IDC_CATEGORY, OnSelchangeCategory)
        ON_LBN_SELCHANGE(IDC_COMMANDS, OnSelchangeCommands)
        ON_EN_SETFOCUS(IDC_NEW_KEY, OnSetfocusNewKey)
        ON_EN_KILLFOCUS(IDC_NEW_KEY, OnKillfocusNewKey)
        ON_BN_CLICKED(IDC_ASSIGN, OnAssign)
        ON_BN_CLICKED(IDC_REMOVE, OnRemove)
        ON_BN_CLICKED(IDC_RESET_ALL, OnResetAll)
        ON_LBN_SELCHANGE(IDC_CURRENT_KEYS, OnSelchangeCurrentKeys)
        //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCustomize message handlers

void CCustomize::OnSettingDefault() 
{
        // TODO: Add your control notification handler code here
        UpdateData();
        EnableControl();
}

void CCustomize::OnSettingDisable() 
{
        // TODO: Add your control notification handler code here
        UpdateData();
        EnableControl();
}

void CCustomize::OnSettingSpecific() 
{
        // TODO: Add your control notification handler code here
        UpdateData();
        EnableControl();

        if (CProfile::GetApplicationIndex(m_szCurrentApplication) == MAX_APP) {
                CString szDefault;
                szDefault.LoadString(IDS_DEFAULT);
                CProfile::CopyData(m_szCurrentApplication, szDefault);
                SetDialogData(m_szCurrentApplication);
        }
}

void CCustomize::EnableControl()
{
        BOOL bEnableWindow = (m_nSettingStyle == SETTING_SPECIFIC);

        m_cCategory.EnableWindow(bEnableWindow);
        m_cCommands.EnableWindow(bEnableWindow);
        m_cCurrentKeys.EnableWindow(bEnableWindow);
        m_cNewKey.EnableWindow(bEnableWindow);

        m_cAssign.EnableWindow(FALSE);
        m_cRemove.EnableWindow(FALSE);
        m_cResetAll.EnableWindow(bEnableWindow);
}

void CCustomize::UpdateDialogData(CString szApplicationName, BOOL bSaveAndValidate)
{
        m_nApplicationID = CProfile::GetApplicationIndex(szApplicationName, bSaveAndValidate, &m_nSettingStyle);
        if (m_nApplicationID == MAX_APP) {
                if (!bSaveAndValidate) {
//                      SetAllDialogData(1);
                }
                return;
        }

        InitCommandIDs();

        if (!bSaveAndValidate) {        // SetDialogData
                EnableControl();
        }
        if (CProfile::IsDefault(szApplicationName)) {
                EnableUseDefaultButton(FALSE);
        } else {
                EnableUseDefaultButton(TRUE);
        }

        // application title
        CProfile::UpdateApplicationTitle(&m_cApplication, m_szCurrentApplication, m_nApplicationID, bSaveAndValidate);

        if (!bSaveAndValidate) {        // SetDialogData
                SetCurrentKeys();
        }
}

void CCustomize::SetDialogData(CString szApplicationName)
{
        UpdateDialogData(szApplicationName, FALSE);
        UpdateData(FALSE);
}

void CCustomize::GetDialogData()
{
        UpdateData();
        if (m_nSettingStyle == SETTING_DEFAULT) {
                CProfile::ClearData(m_szCurrentApplication);
        } else {
                UpdateDialogData(m_szCurrentApplication, TRUE);
        }
}

void CCustomize::OnApply() 
{
        // TODO: Add your control notification handler code here
        GetDialogData();
        CProfile::SaveRegistryData();
}

void CCustomize::OnOK() 
{
        // TODO: Add extra validation here
        OnApply();
        
        CDialog::OnOK();
}

void CCustomize::OnSelchangeApplication() 
{
        // TODO: Add your control notification handler code here
        GetDialogData();

        CProfile::GetApplicationTitle(&m_cApplication, m_szCurrentApplication, m_cApplication.GetCurSel());
        int nStart;
        if ((nStart = m_szCurrentApplication.ReverseFind('(')) != -1) {
                nStart++;
                m_szCurrentApplication =
                        m_szCurrentApplication.Mid(nStart, m_szCurrentApplication.GetLength() - nStart - 1);
        }

        SetDialogData(m_szCurrentApplication);
}

void CCustomize::OnDropdownApplication() 
{
        // TODO: Add your control notification handler code here
        InitApplicationList();

        int nID = GetCurrentApplicationID();
        if (nID != CB_ERR) {
                m_cApplication.SetCurSel(nID);
        } else {
                GetDialogData();
                m_szCurrentApplication.LoadString(IDS_DEFAULT_TITLE);
                m_cApplication.SelectString(-1, m_szCurrentApplication);
                SetDialogData(m_szCurrentApplication);
        }
}

void CCustomize::InitApplicationList()
{
        CProfile::InitApplicationList(&m_cApplication);
}

void CCustomize::InitCategoryList()
{
        int nCategoryIDs[128] = {'\0'};

        // get all category type
        m_cCategory.ResetContent();
        for (int nCommandID = 1; ; nCommandID++) {
                CString szCommandName = CXkeymacsData::GetCommandName(nCommandID);
                if (szCommandName.IsEmpty()) {
                        break;
                }
                int nCategoryID = CXkeymacsData::GetCategoryID(nCommandID);
                if (!nCategoryID) {
                        continue;
                }

                BOOL bExist = FALSE;
                for (int i = 0; nCategoryIDs[i]; i++) {
                        if (nCategoryIDs[i] == nCategoryID) {
                                bExist = TRUE;
                        }
                }
                if (!bExist) {
                        nCategoryIDs[i] = nCategoryID;
                }
        }

        // sort category type in combo box
        int nMinimumID = 0;
        for (int i = 0; nCategoryIDs[i]; i++) {
                int nID = 0;
                for (int j = 0; nCategoryIDs[j]; j++) {
                        if (nMinimumID < nCategoryIDs[j] &&
                                (nID == 0 || nCategoryIDs[j] < nID)) {
                                nID = nCategoryIDs[j];
                        }
                }

                CString szCategory;
                szCategory.LoadString(nID);
                m_cCategory.InsertString(-1, szCategory);

                nMinimumID = nID;
        }

        m_cCategory.SetCurSel(0);
}

BOOL CCustomize::OnInitDialog() 
{
        CDialog::OnInitDialog();
        
        // TODO: Add extra initialization here
        SetForegroundWindow();

        InitApplicationList();
        InitCategoryList();
        SetCommands();

        CProfile::LoadRegistryData();
        m_szCurrentApplication.LoadString(IDS_DEFAULT);
        SetDialogData(m_szCurrentApplication);
        EnableUseDefaultButton(FALSE);

        m_cAssign.EnableWindow(FALSE);
        m_cRemove.EnableWindow(FALSE);

        m_cCurrentlyAssigned.SetWindowText("");

        return TRUE;  // return TRUE unless you set the focus to a control
                      // EXCEPTION: OCX Property Pages should return FALSE
}

int CCustomize::GetCurrentApplicationID()
{
        return CProfile::GetCurrentApplicationID(&m_cApplication, m_szCurrentApplication);
}

void CCustomize::EnableUseDefaultButton(BOOL bEnable)
{
        if (GetDlgItem(IDC_SETTING_DEFAULT)) {
                GetDlgItem(IDC_SETTING_DEFAULT)->EnableWindow(bEnable);
        }
}

void CCustomize::OnSelchangeCategory() 
{
        // TODO: Add your control notification handler code here
        SetCommands();
        ClearNewKey();
}

void CCustomize::SetCommands()
{
        m_cCommands.ResetContent();

        CString szCategory;
        m_cCategory.GetLBText(m_cCategory.GetCurSel(), szCategory);
        for (int nCommandID = 1; ; nCommandID++) {
                CString szCommandName = CXkeymacsData::GetCommandName(nCommandID);
                if (szCommandName.IsEmpty()) {
                        break;
                }

                CString sz;
                sz.LoadString(CXkeymacsData::GetCategoryID(nCommandID));
                if (sz == szCategory) {
                        m_cCommands.AddString(CXkeymacsData::GetCommandName(nCommandID));
                }
        }

        m_cCommands.SetCurSel(0);
        SetCurrentKeys();
}

void CCustomize::OnSelchangeCommands() 
{
        // TODO: Add your control notification handler code here
        SetCurrentKeys();
        ClearNewKey();
}

void CCustomize::SetCurrentKeys()
{
        m_cCurrentKeys.ResetContent();
        m_cRemove.EnableWindow(FALSE);

        CString szCurrentCommandName;
        m_cCommands.GetText(m_cCommands.GetCurSel(), szCurrentCommandName);

        for (int nCommandType = 0; nCommandType < MAX_COMMAND_TYPE; nCommandType++) {
                for (int nKey = 0; nKey < MAX_KEY; nKey++) {
                        if (CXkeymacsData::GetCommandName(CProfile::GetCommandID(m_nApplicationID, nCommandType, nKey)) == 
                                szCurrentCommandName) {
                                CString sz;
                                sz.Format("%s%s", CProfile::CommandType2String(nCommandType), CProfile::Key2String(nKey));
                                m_cCurrentKeys.AddString(sz);
                        }
                }
        }

        for (int nCommandID = 0; ; nCommandID++) {
                CString szCommandName = CXkeymacsData::GetCommandName(nCommandID);
                if (szCommandName.IsEmpty()) {
                        break;
                }
                if (szCommandName == szCurrentCommandName) {
                        m_nCommandID = nCommandID;
                        break;
                }
        }

        CString szDescription;
        szDescription.LoadString(CXkeymacsData::GetDescriptionID(m_nCommandID));
        m_cDescription.SetWindowText(szDescription);
}

void CCustomize::OnSetfocusNewKey() 
{
        // TODO: Add your control notification handler code here
        m_pNewKey = &m_cNewKey;
        m_pAssign = &m_cAssign;
        m_pCurrentlyAssigned = &m_cCurrentlyAssigned;
        m_hKeyboardHook = ::SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)CCustomize::KeyboardProc, NULL, AfxGetApp()->m_nThreadID);
}

void CCustomize::OnKillfocusNewKey() 
{
        // TODO: Add your control notification handler code here
        if (m_hKeyboardHook) {
                ::UnhookWindowsHookEx(m_hKeyboardHook);
        }
        m_hKeyboardHook = NULL;
}

LRESULT CALLBACK CCustomize::KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
        if (code < 0 || code == HC_NOREMOVE) {
                return ::CallNextHookEx(m_hKeyboardHook, code, wParam, lParam);
        }
        if (2 < (lParam & 0xff)) {
                return ::CallNextHookEx(m_hKeyboardHook, code, wParam, lParam);
        }

        CString szNewKey;

        static BOOL bC_x = FALSE;
        int nCommandType = NONE;
        if (Commands[m_nCommandID].fCommand != CCommands::C_) {
                if (bC_x) {
                        szNewKey += "Ctrl+X ";
                        nCommandType += CONTROLX;
                }
                if (GetKeyState(VK_CONTROL) < 0) {
                        szNewKey += "Ctrl+";
                        nCommandType += CONTROL;
                }
                if (GetKeyState(VK_MENU) < 0) {
                        szNewKey += "Meta+";
                        nCommandType += META;
                }
                if (GetKeyState(VK_SHIFT) < 0) {
                        szNewKey += "Shift+";
                        nCommandType += SHIFT;
                }
        }

        static BOOL bNewKey = FALSE;

        if (Commands[m_nCommandID].fCommand != CCommands::C_ &&
                (wParam == VK_CONTROL ||
                 wParam == VK_MENU ||
                 wParam == VK_SHIFT)) {
                if (lParam & 0x80000000) {      // Key Up
                        if (!bNewKey) {
                                m_pNewKey->SetWindowText(szNewKey);
                        }
                } else {                                        // Key Down
                        m_pNewKey->SetWindowText(szNewKey);

                        bNewKey = FALSE;
                        m_pAssign->EnableWindow(bNewKey);
                        m_pCurrentlyAssigned->SetWindowText("");
                        m_nAssignCommandType = 0;
                        m_nAssignKey = 0;
                }
        } else {
                if (lParam & 0x80000000) {      // Key Up
                } else {                                        // Key Down
                        szNewKey += CProfile::Key2String(wParam);
                        m_pNewKey->SetWindowText(szNewKey);

                        m_nAssignCommandType = nCommandType;
                        m_nAssignKey = wParam;
                        bNewKey = TRUE;
                        m_pAssign->EnableWindow(bNewKey);

                        CString szCurrentlyAssigned;
                        szCurrentlyAssigned.Format("Currently assigned to:\n%s",
                                                                           CXkeymacsData::GetCommandName(m_nCommandIDs[nCommandType][wParam]));
                        m_pCurrentlyAssigned->SetWindowText(szCurrentlyAssigned);

//                      if (CXkeymacsData::GetCommandName(CProfile::GetCommandID(m_nApplicationID, nCommandType, wParam)) == "C-x") {
                        if ((nCommandType & CONTROL) &&
                                !(nCommandType & META) &&
                                wParam == 'X') {
                                bC_x = TRUE;
                                m_pNewKey->SetWindowText("Ctrl+X");
                        } else {
                                bC_x = FALSE;
                        }
                }
        }
        return TRUE;
}

void CCustomize::OnAssign() 
{
        // TODO: Add your control notification handler code here
        CProfile::SetCommandID(m_nApplicationID, m_nAssignCommandType, m_nAssignKey, m_nCommandID);
        m_nCommandIDs[m_nAssignCommandType][m_nAssignKey] = m_nCommandID;

        CString sz;
        sz.Format("%s%s", CProfile::CommandType2String(m_nAssignCommandType), CProfile::Key2String(m_nAssignKey));
        m_cCurrentKeys.AddString(sz);

        ClearNewKey();
}

void CCustomize::OnRemove() 
{
        // TODO: Add your control notification handler code here
        CProfile::SetCommandID(m_nApplicationID, m_nRemoveCommandType, m_nRemoveKey, 0);
        m_nCommandIDs[m_nRemoveCommandType][m_nRemoveKey] = 0;

        m_cCurrentKeys.DeleteString(m_cCurrentKeys.GetCurSel());
        m_cRemove.EnableWindow(FALSE);
        m_nRemoveCommandType = 0;
        m_nRemoveKey = 0;
}

void CCustomize::OnResetAll() 
{
        // TODO: Add your control notification handler code here
        CProfile::LoadRegistryData();
        InitCommandIDs();
        SetCurrentKeys();
}

void CCustomize::OnSelchangeCurrentKeys() 
{
        // TODO: Add your control notification handler code here
        char szKeyBind[128] = {'\0'};
        m_cCurrentKeys.GetText(m_cCurrentKeys.GetCurSel(), szKeyBind);
        CProfile::ReadKeyBind(&m_nRemoveCommandType, &m_nRemoveKey, szKeyBind);
        m_cRemove.EnableWindow();
}

void CCustomize::InitCommandIDs()
{
        // initialize local static CommandID for static function i.e. KeyboardProc

        for (int nCommandType = 0; nCommandType < MAX_COMMAND_TYPE; nCommandType++) {
                for (int nKey = 0; nKey < MAX_KEY; nKey++) {
                        m_nCommandIDs[nCommandType][nKey] = CProfile::GetCommandID(m_nApplicationID, nCommandType, nKey);
                }
        }
}

void CCustomize::ClearNewKey()
{
        m_cNewKey.SetWindowText("");
        m_cAssign.EnableWindow(FALSE);
        m_nAssignCommandType = 0;
        m_nAssignKey = 0;
        m_cCurrentlyAssigned.SetWindowText("");
}

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