#include <vcl.h>
#pragma hdrstop
#include "FrmSetup.h"
#include "../kbtim/kbstr.h"
#include "../kbtim/kbini.h"

#pragma resource "*.dfm"
TSetupForm *SetupForm;
//---------------------------------------------------------------------------
enum {
	RESAMPLE_CSPLINE,  //0 = C スプライン補間
	RESAMPLE_LAGRANGE, //1 = ラグランジュ補間
	RESAMPLE_GAUSS,    //2 = ガウス補間(デフォルト)
	RESAMPLE_NEWTON,   //3 = ニュートン補間
	RESAMPLE_LINEAR,   //4 = 線形補間
	RESAMPLE_NONE      //5 = なし
};

static const char* __fastcall kbGetAppPath(void);
static void __fastcall kbGetFileTimeFromFileName(const char *cszFileName, FILETIME *pft);
static void __fastcall ExecExplorer(const char* cszPath);
static void __fastcall GetIniFileNameFromTemplateName(const char *cszTemplateName, char *pszIniFile, int nSize);
static void __fastcall GetKpiFileNameFromTemplateName(const char *cszTemplateName, char *pszKpiFile, int nSize);

static const char* __fastcall kbGetAppPath(void)
{
    static char szAppPath[MAX_PATH*2];
    if(!szAppPath[0]){
        char szAppName[MAX_PATH*2];
        GetModuleFileName(NULL, szAppName, sizeof(szAppName));
        kbExtractFilePath(szAppPath, szAppName, sizeof(szAppPath));
    }
    return szAppPath;
}
static void __fastcall kbGetFileTimeFromFileName(const char *cszFileName, FILETIME *pft)
{
    pft->dwHighDateTime = pft->dwLowDateTime = 0;
    HANDLE hFile = CreateFile(cszFileName,
                              GENERIC_READ,
                              FILE_SHARE_READ|FILE_SHARE_WRITE,
                              NULL,
                              OPEN_EXISTING,
                              FILE_ATTRIBUTE_NORMAL,
                              NULL);
    if(hFile != INVALID_HANDLE_VALUE){
        GetFileTime(hFile, NULL, NULL, pft);
        CloseHandle(hFile);
    }
}
static bool __fastcall kbFileExists(const char *cszName)
{
    DWORD dwCode = GetFileAttributes(cszName);
    if(dwCode != 0xffffffff){
        if(!(FILE_ATTRIBUTE_DIRECTORY & dwCode)){
            return true;
        }
    }
    return false;
}
static void __fastcall ExecExplorer(const char* cszPath)
{//cszPath を Windows エクスプローラで開く
 //cszPath がファイルの場合は cszPath が存在するフォルダを開く
    char szParam[MAX_PATH*2];
    char szExplorer[MAX_PATH*2];
    char szPath[MAX_PATH*2];
    if(kbFileExists(cszPath)){
    //ファイルが存在する場合、ファイルが存在するフォルダを開く
        kbExtractFilePath(szPath, cszPath, sizeof(szPath));
    }
    else{
        kbStrLCpy(szPath, cszPath, sizeof(szPath));
    }
    kbStrLCpy(szParam, "/e,", sizeof(szParam));
    kbStrLCat(szParam, "\"", sizeof(szParam));
    kbStrLCat(szParam, szPath, sizeof(szParam));
    kbStrLCat(szParam, "\"", sizeof(szParam));

    GetWindowsDirectory(szExplorer, sizeof(szExplorer));
    kbStrLCat(szExplorer, "\\Explorer.exe", sizeof(szExplorer));
    ShellExecute(NULL, "open", szExplorer, szParam, NULL, SW_SHOWNORMAL);
}
static void __fastcall GetIniFileNameFromTemplateName(const char *cszTemplateName, char *pszIniFile, int nSize)
{//テンプレート名 から INI ファイル名を取得する
 //cszTemplateName == "Default" の場合、INI ファイル名は kbtim.ini になる
 //それ以外の場合は kbtim_TemplateName.ini になる
    static const char cszKbtim[] = "kbtim";
    static const char cszIni[] = ".ini";
    kbStrLCpy(pszIniFile, kbGetAppPath(), nSize);
    kbStrLCat(pszIniFile, cszKbtim, nSize);
    if(kbStrCmpI(cszTemplateName, "Default") != 0){
        kbStrLCat(pszIniFile, "_", nSize);
        kbStrLCat(pszIniFile, cszTemplateName, nSize);
    }
    kbStrLCat(pszIniFile, cszIni, nSize);
}
static void __fastcall GetKpiFileNameFromTemplateName(const char *cszTemplateName, char *pszKpiFile, int nSize)
{//KbSettingTimidity::szTemplateName から KPI ファイル名を取得する
 //cszTemplateName == "Default" の場合、KPI ファイル名は kbtim.kpi になる
 //それ以外の場合は kbtim_TemplateName.kpi になる
    static const char cszKbtim[] = "kbtim";
    static const char cszKpi[] = ".kpi";
    kbStrLCpy(pszKpiFile, kbGetAppPath(), nSize);
    kbStrLCat(pszKpiFile, cszKbtim, nSize);
    if(kbStrCmpI(cszTemplateName, "Default") != 0){
        kbStrLCat(pszKpiFile, "_", nSize);
        kbStrLCat(pszKpiFile, cszTemplateName, nSize);
    }
    kbStrLCat(pszKpiFile, cszKpi, nSize);
}
static void __fastcall ReadSetting(KbSettingTimidity *pSetting)
{
    char szIniFile[MAX_PATH*2];
    GetIniFileNameFromTemplateName(pSetting->szTemplateName, szIniFile, sizeof(szIniFile));
    pSetting->LoadSettings(szIniFile);
}
static void __fastcall WriteSetting(KbSettingTimidity *pSetting)
{
    char szIniFile[MAX_PATH*2];
    GetIniFileNameFromTemplateName(pSetting->szTemplateName, szIniFile, sizeof(szIniFile));
    pSetting->SaveSettings(szIniFile);
}
static BOOL __fastcall CheckTemplateName(char *szTemplateName)
{//テンプレート名が正しいかどうか調べる
 //テンプレート名からプラグインファイル名が決まるので、ファイル名に使用出来ない
 //文字はテンプレート名に使用出来ない
    static const char cszIllegal[] = "\\/:*?\"<>|";

    kbStrTrim(kbCRLFtoSpace(szTemplateName));
    if(!szTemplateName[0]){
        Application->MessageBox("テンプレート名を入力して下さい。", "エラー", MB_OK);
        return FALSE;
    }
    if(kbStrCmpI(szTemplateName, "Default") == 0){
        Application->MessageBox("テンプレート名 \"Default\" は使用できません。", "エラー", MB_OK);
        return FALSE;
    }
    const char *p = szTemplateName;
    while(*p){
        if(kbIsLeadByte(*p)){
            p += 2;
            continue;
        }
        int i;
        for(i = 0; cszIllegal[i]; i++){
            if(*p == cszIllegal[i]){
                char sz[256];
                wsprintf(sz, "テンプレート名には以下の文字は使えません。\r\n\r\n"
                             "%s", cszIllegal);
                Application->MessageBox(sz, "エラー", MB_OK);
                return FALSE;
            }
        }
        p++;
    }
    return TRUE;
}

//---------------------------------------------------------------------------
__fastcall TSetupForm::TSetupForm(TComponent* Owner)
    : TForm(Owner)
{
    kbStr_Initialize();
    m_bChanging = TRUE;
    m_bModified = FALSE;
    m_bModified2 = FALSE;

    KbSettingTimidity *pSetting = new KbSettingTimidity;
    kbStrLCpy(pSetting->szTemplateName, "Default", sizeof(pSetting->szTemplateName));
    Edit->MaxLength = sizeof(pSetting->szTemplateName) - 2;
    Memo->MaxLength = sizeof(pSetting->ConfigFile) - 2;
    TreeView->Items->Add(NULL, "Default");
    TreeView->Items->Item[0]->Data = pSetting;
    //音源モード コンボボックスの初期化
    int i = 0;
    while(1){
        const char *cszModule = KbSettingTimidity::GetModuleNameFromIndex(i);
        if(!cszModule){
            break;
        }
        ComboBox6->Items->Append(cszModule);
        i++;
    }
    //kbtim_xxx.kpi を検索
    WIN32_FIND_DATA fd;
    char szSearch[MAX_PATH*2];
    kbStrLCpy(szSearch, kbGetAppPath(), sizeof(szSearch));
    kbStrLCat(szSearch, "kbtim_*.kpi", sizeof(szSearch));
    HANDLE hFind = FindFirstFile(szSearch, &fd);
    if(hFind != INVALID_HANDLE_VALUE){
        int nCount = 0;
        do{
            if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
                continue;
            }
            pSetting = new KbSettingTimidity;
            //kbtim_xxx.kpi のテンプレート名は xxx
            //fd.cFileName + strlen("kbtim_") に対して拡張子除去すればテンプレート名
            //strlen("kbtim_") == 6;
            kbExtractReplacedFileExt(pSetting->szTemplateName, fd.cFileName + 6, "");

            TreeView->Items->Add(NULL, pSetting->szTemplateName);
            TreeView->Items->Item[++nCount]->Data = pSetting;
        }while(FindNextFile(hFind, &fd));
    }
    m_bChanging = FALSE;
    DoCopyData(ParamStr(1).c_str());
}
//---------------------------------------------------------------------------
__fastcall TSetupForm::~TSetupForm(void)
{
    const int nCount = TreeView->Items->Count;
    int i;
    for(i = 0; i < nCount; i++){
        KbSettingTimidity *pSetting = (KbSettingTimidity*)TreeView->Items->Item[i]->Data;
        TreeView->Items->Item[i]->Data = NULL;
        delete pSetting;
    }
}
//---------------------------------------------------------------------------
void __fastcall TSetupForm::EditChange(TObject *Sender)
{
    if(m_bChanging){
        return;
    }
    if(!m_bModified && Sender){
        m_bModified = TRUE;
        btnApply->Enabled = true;
        btnUndo->Enabled = true;
    }
    if(!Sender || (Sender == ComboBox1)){//リバーブ
        CheckBox11->Enabled = (ComboBox1->ItemIndex != 0);
        Edit1->Enabled = CheckBox11->Enabled;
    }
    if(!Sender || (Sender == ComboBox2)){//コーラス
        CheckBox12->Enabled = (ComboBox2->ItemIndex == 1);
        Edit2->Enabled = CheckBox12->Enabled;
    }
    if(!Sender || (Sender == CheckBox16)){//L&R ディレイ
        BOOL bEnabled = CheckBox16->Checked;
        RadioButton1->Enabled = bEnabled;
        RadioButton2->Enabled = bEnabled;
        RadioButton3->Enabled = bEnabled;
        Label5->Enabled = bEnabled;
        Edit4->Enabled = bEnabled;
    }
}
//---------------------------------------------------------------------------
BOOL __fastcall TSetupForm::Apply(TTreeNode *pNode)
{
    if(!pNode){
        return FALSE;
    }

    const BOOL bIsDefault = (pNode == TreeView->Items->Item[0]);
    KbSettingTimidity *pSetting = (KbSettingTimidity*)pNode->Data;
    KbSettingTimidity setting;
    memcpy(&setting, pSetting, sizeof(KbSettingTimidity));
    
    kbStrLCpy(setting.szTemplateName, Edit->Text.c_str(), sizeof(setting.szTemplateName));
    kbStrLCpy(setting.ConfigFile, Memo->Text.c_str(), sizeof(setting.ConfigFile));
    kbStrTrim(kbCRLFtoSpace(setting.szTemplateName));
    kbStrTrim(kbCRLFtoSpace(setting.ConfigFile));
    int nReverbType = ComboBox1->ItemIndex;
    int nReverbLevel = 0;
    if(nReverbType > 0 &&
       CheckBox11->Checked){
        nReverbLevel = kbStrToIntDef(Edit1->Text.c_str(), 1);
    }
    setting.SetReverbLevel(nReverbType, nReverbLevel);

    int nChorusType = ComboBox2->ItemIndex;
    int nChorusLevel = 0;
    if(//nChorusType == 1 &&
       CheckBox12->Checked){
        nChorusLevel = kbStrToIntDef(Edit2->Text.c_str(), 1);
    }
    setting.SetChorusLevel(nChorusType, nChorusLevel);

    setting.opt_delay_control =  ComboBox3->ItemIndex;
    setting.opt_lpf_def =    ComboBox4->ItemIndex;
    setting.noise_sharp_type = kbStrToIntDef(Edit3->Text.c_str(), g_noise_sharp_type_default);
    {//L&R ディレイ
        int nLRDelayMode = -1;
        if(CheckBox16->Checked){
            if(RadioButton1->Checked){
                nLRDelayMode = 0;
            }
            else if(RadioButton2->Checked){
                nLRDelayMode = 1;
            }
            else{
                nLRDelayMode = 2;
            }
        }
        int nLRDelayMS = kbStrToIntDef(Edit4->Text.c_str(), g_effect_lr_delay_msec_default);
        setting.SetLRDelay(nLRDelayMode, nLRDelayMS);
    }
    setting.modify_release = kbStrToIntDef(Edit5->Text.c_str(), g_modify_release_default);
    setting.voices = kbStrToIntDef(Edit6->Text.c_str(), g_voices_default);
    setting.amplification = kbStrToIntDef(Edit7->Text.c_str(), g_amplification_default);
    setting.control_ratio = kbStrToIntDef(Edit8->Text.c_str(), g_control_ratio_default);
    setting.opt_drum_power = kbStrToIntDef(Edit9->Text.c_str(), g_opt_drum_power_default);
    setting.opt_default_module = KbSettingTimidity::GetModuleFromIndex(ComboBox6->ItemIndex);
    //デフォルト規格
    if(RadioButton5->Checked){
        setting.opt_default_mid = 0x41;//GS
    }
    else if(RadioButton6->Checked){
        setting.opt_default_mid = 0x43;//XG
    }
    else{
        setting.opt_default_mid = 0x7e;//GM
    }
    int i;
    for(i = 0; i < sizeof(setting.b); i++){
        char szCheckBox[16];
        wsprintf(szCheckBox, "CheckBox%d", i+1);
        TCheckBox *cb = dynamic_cast<TCheckBox*>(FindComponent(szCheckBox));
        if(cb){
            setting.bBoolValues[i] = cb->Checked;
        }
    }
    setting.resample_type = ComboBox5->ItemIndex;
    setting.DoCheckParameter();
    //テンプレート名が正しいかどうか調べる
    if(!bIsDefault &&
       !CheckTemplateName(setting.szTemplateName)){
        return FALSE;
    }
    const int nCount = TreeView->Items->Count;
    for(i = 0; i < nCount; i++){
    //テンプレート名の重複チェック
        KbSettingTimidity *pSettingI = (KbSettingTimidity*)TreeView->Items->Item[i]->Data;
        if(pSettingI != pSetting &&
           kbStrCmpI(setting.szTemplateName, pSettingI->szTemplateName) == 0){
            Application->MessageBox("テンプレート名が重複しています。", "エラー", MB_OK);
            return FALSE;
        }
    }
    char szIniFileName[MAX_PATH*2];
    GetIniFileNameFromTemplateName(setting.szTemplateName, szIniFileName, sizeof(szIniFileName));
    if(szIniFileName[0]){
        //INI に保存する
        char szOldIniFileName[MAX_PATH*2];
        GetIniFileNameFromTemplateName(pSetting->szTemplateName, szOldIniFileName, sizeof(szIniFileName));

        if(kbStrCmpI(szOldIniFileName, szIniFileName) != 0){
        //テンプレート名が変更されている
        //古い kbtim_templatename.ini/kpi を削除する
            if(kbFileExists(szOldIniFileName)){
                if(!DeleteFile(szOldIniFileName)){//古い INI の削除
                    char sz[1024];
                    wsprintf(sz, "\"%s\" を削除出来ません。\r\n"
                                 "テンプレート名の変更を適用出来ませんでした。", szOldIniFileName);
                    Application->MessageBox(sz, "エラー", MB_OK);
                    return FALSE;
                }
            }
            kbExtractReplacedFileExt(szIniFileName, szOldIniFileName, ".kpi");
            if(kbFileExists(szIniFileName)){
                if(!DeleteFile(szIniFileName)){
                    WriteSetting(pSetting);//設定を復元
                    char sz[1024];
                    wsprintf(sz, "\"%s\" を削除出来ません。\r\n"
                                 "KbMedia Player が使用中の可能性があります。\r\n"
                                 "テンプレート名の変更を適用出来ませんでした。", szIniFileName);
                    Application->MessageBox(sz, "エラー", MB_OK);
                    return FALSE;
                }
            }
            m_bModified2 = TRUE;
        }
        else if(pSetting->bNew){
            m_bModified2 = TRUE;
        }
        WriteSetting(&setting);
        memcpy(pSetting, &setting, sizeof(KbSettingTimidity));
    }
    TreeViewChange(NULL, pNode);
    char szKbTim[MAX_PATH*2+16];
    kbStrLCpy(szKbTim, kbGetAppPath(), MAX_PATH*2);
    lstrcat(szKbTim, "kbtim.kpi");
    if(kbFileExists(szKbTim)){
        //ファイルが更新されているものに関しては、
        //新しく kbtim_templatename.kpi を作成する
        FILETIME ftKbTim;
        kbGetFileTimeFromFileName(szKbTim, &ftKbTim);
        for(i = nCount; i > 0; i--){
            pSetting = (KbSettingTimidity*)TreeView->Items->Item[i-1]->Data;
            char szKbTimTemplate[MAX_PATH*2];
            GetKpiFileNameFromTemplateName(pSetting->szTemplateName, szKbTimTemplate, sizeof(szKbTimTemplate));
            FILETIME ft;
            kbGetFileTimeFromFileName(szKbTimTemplate, &ft);
            if(CompareFileTime(&ftKbTim, &ft) != 0){
                CopyFile(szKbTim, szKbTimTemplate, FALSE);
            }
        }
    }
    btnApply->Enabled = false;
    btnUndo->Enabled = false;
    m_bModified = FALSE;
    return TRUE;
}
BOOL __fastcall TSetupForm::CheckModified2(void)
{
    if(m_bModified2){
    //テンプレートの追加・削除・名称変更がされていれば TRUE を返す
    //ただし、KbMedia Player が動作していない場合は FALSE を返す
    //テンプレートの追加・削除・名称変更がされている場合、設定の変更が
    //反映されるのは KbMedia Player 本体を再起動した後になる
    //この関数が呼ばれた時点で KbMedia Player が起動しているなら、再起動を促す
    //メッセージを表示するべきだが、もし起動していないならその必要はないので、
    //FALSE を返すようにしても構わない
        HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "KbMedia Player");
        if(!hMutex){
            m_bModified2 = FALSE;
        }
        else{
            CloseHandle(hMutex);
        }
    }
    return m_bModified2;
}
BOOL __fastcall TSetupForm::Delete(TTreeNode *pNode)
{
    if(!pNode)return FALSE;
    if(pNode == TreeView->Items->Item[0]){
        return FALSE;
    }
    KbSettingTimidity *pSetting = (KbSettingTimidity*)pNode->Data;
    char szIniFileName[MAX_PATH*2];
    GetIniFileNameFromTemplateName(pSetting->szTemplateName, szIniFileName, sizeof(szIniFileName));
    if(szIniFileName[0]){
        if(kbFileExists(szIniFileName)){
            if(!DeleteFile(szIniFileName)){
                return FALSE;
            }
        }
        char szKpiFileName[MAX_PATH*2];
        kbExtractReplacedFileExt(szKpiFileName, szIniFileName, ".kpi");
        if(kbFileExists(szKpiFileName)){
            if(!DeleteFile(szKpiFileName)){
                WriteSetting(pSetting);
                char szMes[1024];
                wsprintf(szMes, "%s \r\n\r\nを削除出来ません。\r\n"
                                "KbMedia Player が使用中の可能性があります。\r\n"
                                "テンプレート \"%s\" を削除出来ませんでした。\r\n"
                                , szKpiFileName, pSetting->szTemplateName);
                Application->MessageBox(szMes, "エラー", MB_OK);
                return FALSE;
            }
        }
    }
    pNode->Data = NULL;
    delete pSetting;
    pNode->Delete();
    m_bModified2 = TRUE;
    return TRUE;
}
//---------------------------------------------------------------------------
void __fastcall TSetupForm::TreeViewChanging(TObject *Sender,
      TTreeNode *Node, bool &AllowChange)
{
    if(m_bModified){
        char szMes[1024];
        TTreeNode *Sel = TreeView->Selected;
        if(Sel){
            wsprintf(szMes, "%s への変更を保存しますか？", (char*)((KbSettingTimidity*)Sel->Data)->szTemplateName);
            int ret = Application->MessageBox(szMes, "", MB_YESNOCANCEL|MB_DEFBUTTON3);
            if(ret == IDYES){
                if(Apply(Sel)){
                    AllowChange = true;
                }
            }
            else if(ret == IDNO){
                AllowChange = true;
            }
            else if(ret == IDCANCEL){
                AllowChange = false;
            }
        }
    }
}
//---------------------------------------------------------------------------
void __fastcall TSetupForm::UpdateGUI(KbSettingTimidity *pSetting, TTreeNode *pNode)
{//pSetting の設定に従って画面を表示する
    m_bChanging = TRUE;
    pSetting->DoCheckParameter();

    Edit->Text = kbStrTrim(kbCRLFtoSpace(pSetting->szTemplateName));
    Memo->Text = kbStrTrim(kbCRLFtoSpace(pSetting->ConfigFile));

    int i;
    int nReverbType, nReverbLevel;
    int nChorusType, nChorusLevel;

    pSetting->GetReverbLevel(&nReverbType, &nReverbLevel);
    pSetting->GetChorusLevel(&nChorusType, &nChorusLevel);
    ComboBox1->ItemIndex = nReverbType;
    ComboBox2->ItemIndex = nChorusType;
    ComboBox3->ItemIndex = pSetting->opt_delay_control;
    ComboBox4->ItemIndex = pSetting->opt_lpf_def;
    ComboBox5->ItemIndex = pSetting->resample_type;
    ComboBox6->ItemIndex = KbSettingTimidity::GetIndexFromModule(pSetting->opt_default_module);
    Edit1->Text = nReverbLevel;
    Edit2->Text = nChorusLevel;
    Edit3->Text = pSetting->noise_sharp_type;
    Edit4->Text = pSetting->effect_lr_delay_msec;
    Edit5->Text = pSetting->modify_release;
    Edit6->Text = pSetting->voices;
    Edit7->Text = pSetting->amplification;
    Edit8->Text = pSetting->control_ratio;
    Edit9->Text = pSetting->opt_drum_power;
    int effect_lr_mode = pSetting->effect_lr_mode;
    TRadioButton *rbs[] = {RadioButton1, RadioButton2, RadioButton3};
    CheckBox16->Checked = (effect_lr_mode >= 0);
    for(i = 0; i < 3; i++){
        bool bChecked = (i == effect_lr_mode);
        if(effect_lr_mode < 0 && i == 2){
            bChecked = true;
        }
        rbs[i]->Checked = bChecked;
    }
    for(i = 0; i < sizeof(pSetting->b); i++){
        char szCheckBoxName[64];
        wsprintf(szCheckBoxName, "CheckBox%d", i+1);
        TCheckBox *cb = dynamic_cast<TCheckBox*>(FindComponent(szCheckBoxName));
        if(cb){
            cb->Checked = pSetting->bBoolValues[i];
        }
    }
    //デフォルト規格
    switch(pSetting->opt_default_mid){
        case 0x41: //GS
            RadioButton5->Checked = true;
            break;
        case 0x43: //XG
            RadioButton6->Checked = true;
            break;
        default:   //GM
            RadioButton4->Checked = true;
            break;
    }
    char szKpiFileName[MAX_PATH*2];
    GetKpiFileNameFromTemplateName(pSetting->szTemplateName, szKpiFileName, sizeof(szKpiFileName));
    Label2->Caption = szKpiFileName;
    pNode->Text = pSetting->szTemplateName;
    m_bChanging = FALSE;

    EditChange(NULL);
}
void __fastcall TSetupForm::TreeViewChange(TObject *Sender, TTreeNode *pNode)
{
    KbSettingTimidity *pSetting = (KbSettingTimidity*)pNode->Data;
    if(!pSetting){
        return;
    }
    ReadSetting(pSetting);
    UpdateGUI(pSetting, pNode);

    btnApply->Enabled = false;
    btnUndo->Enabled = false;
    BOOL bIsDefault = (pNode == TreeView->Items->Item[0]);
    btnDel->Enabled = !bIsDefault;
    Edit->Enabled = !bIsDefault;
    btnAutoDef->Enabled = !bIsDefault;
    m_bModified = FALSE;
}
//---------------------------------------------------------------------------
void __fastcall TSetupForm::btnClick(TObject *Sender)
{
    if(Sender == btnCfg){//.CFG File
        if(OpenDialog->Execute()){
            Memo->Text = OpenDialog->FileName;
            EditChange(Sender);
        }
    }
    else if(Sender == btnEdit){//編集
        char szPath[MAX_PATH*2];
        kbStrLCpy(szPath, Memo->Text.c_str(), sizeof(szPath));
        ExecExplorer(szPath);
    }
    else if(Sender == btnNew){//新規作成
        char szNewTemplateName[256];
        kbStrLCpy(szNewTemplateName, "Template01", sizeof(szNewTemplateName));
        const int nCount = TreeView->Items->Count;
        int t = 1;
        int i = 1;
        while(i < nCount){
            KbSettingTimidity *setting = (KbSettingTimidity*)TreeView->Items->Item[i]->Data;
            if(kbStrCmpI(setting->szTemplateName, szNewTemplateName) == 0){
                if(nCount > 32){
                    return;
                }
                wsprintf(szNewTemplateName, "Template%02d", ++t);
                i = 1;
                continue;
            }
            i++;
        }
        TTreeNode *pNode = TreeView->Items->Add(NULL, szNewTemplateName);
        KbSettingTimidity *pSetting = new KbSettingTimidity;
        kbStrLCpy(pSetting->szTemplateName, szNewTemplateName, sizeof(pSetting->szTemplateName));
        pNode->Data = pSetting;
        TreeView->Selected = pNode;
        pSetting->bNew = TRUE;
    }
    else if(Sender == btnApply){//適用
        Apply(TreeView->Selected);
    }
    else if(Sender == btnUndo){//元に戻す
        TTreeNode *pNode = TreeView->Selected;
        if(pNode){
            TreeViewChange(NULL, pNode);
        }
    }
    else if(Sender == btnDefault){//初期設定に戻す
        TTreeNode *pNode = TreeView->Selected;
        if(pNode){
            KbSettingTimidity *pSetting = (KbSettingTimidity*)pNode->Data;
            KbSettingTimidity DefaultSetting;//コンストラクタで初期設定になっている
            kbStrLCpy(DefaultSetting.szTemplateName, pSetting->szTemplateName, sizeof(DefaultSetting.szTemplateName));
            kbStrLCpy(DefaultSetting.ConfigFile, pSetting->ConfigFile, sizeof(DefaultSetting.ConfigFile));
            UpdateGUI(&DefaultSetting, pNode);
            EditChange(Sender);
        }
    }
    else if(Sender == btnDel){//削除
        char szMes[1024];
        wsprintf(szMes, "\"%s\" を削除してもよろしいですか？\r\n"
                        "この操作は元に戻せません。",
                    (char*)((KbSettingTimidity*)TreeView->Selected->Data)->szTemplateName);
        int ret = Application->MessageBox(szMes, "確認", MB_YESNO|MB_DEFBUTTON2);
        if(ret == IDYES){
            Delete(TreeView->Selected);
        }
    }
    else if(Sender == btnAutoLQ){//低音質低負荷
        TTreeNode *pNode = TreeView->Selected;
        if(pNode){
            KbSettingTimidity setting;
            //明示的に指定しないものはデフォルト値に戻る
            setting.opt_reverb_control = 0;
            setting.opt_chorus_control = 0;
            setting.opt_surround_chorus = 0;
            setting.opt_delay_control = 0;
            setting.opt_lpf_def = 0;
            setting.noise_sharp_type = 0;
            setting.voices = 128;
            setting.amplification = g_amplification_default;
            setting.control_ratio = g_control_ratio_default;
            setting.effect_lr_mode = -1;
            setting.effect_lr_delay_msec = g_effect_lr_delay_msec_default;
            setting.resample_type = RESAMPLE_NONE;

            setting.b.opt_modulation_wheel = false;
            setting.b.opt_portamento = false;
            setting.b.opt_channel_pressure = false;
            setting.b.opt_drum_effect = false;
            setting.b.opt_nrpn_vibrato = false;
            setting.b.opt_tva_attack = false;
            setting.b.opt_tva_decay = false;
            setting.b.opt_tva_release = false;
            setting.b.opt_eq_control = false;
            setting.b.opt_insertion_effect = false;

            setting.b.opt_overlap_voice_allow = false;
            setting.b.opt_modulation_envelope = false;
            setting.b.opt_pan_delay = false;

            setting.b.antialiasing_allowed = false;
            setting.b.opt_realtime_playing = false;
            setting.b.free_instruments_afterwards = false;

            kbStrLCpy(setting.szTemplateName, Edit->Text.c_str(), sizeof(setting.szTemplateName));
            kbStrLCpy(setting.ConfigFile, Memo->Text.c_str(), sizeof(setting.ConfigFile));
            UpdateGUI(&setting, pNode);
            EditChange(Sender);
        }
    }
    else if(Sender == btnAutoHQ){//高音質高負荷
        TTreeNode *pNode = TreeView->Selected;
        if(pNode){
            KbSettingTimidity setting;
            //明示的に指定しないものはデフォルト値に戻る
            setting.opt_reverb_control = 3;
            setting.opt_chorus_control = 0;
            setting.opt_surround_chorus = 1;
            setting.opt_delay_control = 1;
            setting.opt_lpf_def = g_opt_lpf_def_default;
            setting.noise_sharp_type = 4;
            setting.voices = 512;
            setting.amplification = g_amplification_default-10;//音割れしやすいので少し小さくする
            setting.control_ratio = g_control_ratio_default;
            setting.effect_lr_mode = -1;
            setting.effect_lr_delay_msec = g_effect_lr_delay_msec_default;
            setting.resample_type = RESAMPLE_GAUSS;

            setting.b.opt_modulation_wheel = true;
            setting.b.opt_portamento = true;
            setting.b.opt_channel_pressure = true;
            setting.b.opt_drum_effect = true;
            setting.b.opt_nrpn_vibrato = true;
            setting.b.opt_tva_attack = true;
            setting.b.opt_tva_decay = true;
            setting.b.opt_tva_release = true;
            setting.b.opt_eq_control = true;
            setting.b.opt_insertion_effect = false;

            setting.b.opt_overlap_voice_allow = true;
            setting.b.opt_modulation_envelope = true;
            setting.b.opt_pan_delay = true;
            setting.b.antialiasing_allowed = true;
            setting.b.opt_realtime_playing = true;
            setting.b.free_instruments_afterwards = true;

            kbStrLCpy(setting.szTemplateName, Edit->Text.c_str(), sizeof(setting.szTemplateName));
            kbStrLCpy(setting.ConfigFile, Memo->Text.c_str(), sizeof(setting.ConfigFile));
            UpdateGUI(&setting, pNode);
            EditChange(Sender);
        }
    }
    else if(Sender == btnAutoDef){//Default と同じ
        TTreeNode *pNode = TreeView->Selected;
        if(pNode){
            KbSettingTimidity *pDataDefault = (KbSettingTimidity*)(TreeView->Items->Item[0]->Data);
            KbSettingTimidity setting;
            ReadSetting(pDataDefault);
            memcpy(&setting, pDataDefault, sizeof(KbSettingTimidity));
            kbStrLCpy(setting.szTemplateName, Edit->Text.c_str(), sizeof(setting.szTemplateName));
            kbStrLCpy(setting.ConfigFile, Memo->Text.c_str(), sizeof(setting.ConfigFile));
            UpdateGUI(&setting, pNode);
            EditChange(Sender);
        }
    }
    else if(Sender == btnDefAll){//すべてDefaultと同じ
        const int nCount = TreeView->Items->Count;
        if(nCount < 2){
            return;
        }
        char szMes[1024];
        kbStrLCpy(szMes, "全てのテンプレートの設定を \"Default\" の設定と同じくしてもよろしいですか？\r\n"
                         "この操作は元に戻せません。", sizeof(szMes));
        int ret = Application->MessageBox(szMes, "確認", MB_YESNO|MB_DEFBUTTON2);
        if(ret == IDYES){
            int i;
            KbSettingTimidity *pDataDefault = (KbSettingTimidity*)(TreeView->Items->Item[0]->Data);
            ReadSetting(pDataDefault);
            for(i = nCount - 1; i > 0; i--){
                KbSettingTimidity setting;
                memcpy(&setting, pDataDefault, sizeof(KbSettingTimidity));
                KbSettingTimidity *pSetting = (KbSettingTimidity*)(TreeView->Items->Item[i]->Data);
                ReadSetting(pSetting);
                kbStrLCpy(setting.szTemplateName, pSetting->szTemplateName, sizeof(setting.szTemplateName));
                kbStrLCpy(setting.ConfigFile, pSetting->ConfigFile, sizeof(setting.ConfigFile));
                memcpy(pSetting, &setting, sizeof(KbSettingTimidity));
                WriteSetting(pSetting);
            }
            TTreeNode *pNodeSelected = TreeView->Selected;
            if(pNodeSelected){
                KbSettingTimidity *pDataSelected = (KbSettingTimidity*)pNodeSelected->Data;
                UpdateGUI(pDataSelected, pNodeSelected);
                btnApply->Enabled = false;
                btnUndo->Enabled = false;
                m_bModified = FALSE;
            }
        }
    }
    else if(Sender == btnOK){//OK
        TTreeNode *pNode = TreeView->Selected;
        if(pNode){
            if(Apply(pNode)){
                Close();
            }
        }
        else{
            Close();
        }
    }
    else if(Sender == btnCancel){//キャンセル
        Close();
    }
    else if(Sender == btnHelp){//ヘルプ
    }
    if(CheckModified2()){
        m_bModified2 = FALSE;
        Application->MessageBox("テンプレートが追加・削除または名称変更されました。\r\n"
                                "設定の変更を反映させるには、\r\n"
                                "・「表示」メニューから「最新の情報に更新」を実行する\r\n"
                                "・KbMedia Player を再起動する\r\n"
                                "のいずれかを行う必要があります。",
                                "kbtim 設定", MB_OK);
    }
}
void __fastcall TSetupForm::WMCopyData(TMessage &Message)
{
    COPYDATASTRUCT *cds = (COPYDATASTRUCT *)Message.LParam;
    if(cds && cds->lpData){
        DoCopyData((const char*)cds->lpData);
    }
}
//---------------------------------------------------------------------------
void __fastcall TSetupForm::DoCopyData(const char *cszCommand)
{
    static BOOL bReenterChecker = FALSE;
    if(bReenterChecker){//再入防止
        return;
    }
    const char *cszKpi = cszCommand;
    bReenterChecker = TRUE;
    if(cszKpi && *cszKpi){
        char szTmpFileName[MAX_PATH*2];
        const int nCount = TreeView->Items->Count;
        int i;
        for(i = 0; i < nCount; i++){
            KbSettingTimidity *pSetting = (KbSettingTimidity*)TreeView->Items->Item[i]->Data;
            GetKpiFileNameFromTemplateName(pSetting->szTemplateName, szTmpFileName, sizeof(szTmpFileName));
            if(kbStrCmpI(cszKpi, szTmpFileName) == 0){
                TreeView->Items->Item[i]->Selected = true;
                break;
            }
        }
    }
    else{
        TreeView->Items->Item[0]->Selected = true;
    }
    bReenterChecker = FALSE;
}





