/*

 EBminiSearch.txc
 ebmini書籍検索 for WZ Editor 5.0

 (c) 2005-2009 Kiichiro Kawano
 無保証です。自身の責任のもとでご利用ください。


◎ebminiのセットアップ
 ebmini.exeと必要なDLL、検索用バッチファイルをパスの通ったディレクトリに
 置き、設定ファイルを任意のディレクトリに置く。バッチファイルと設定ファイル
 中でパスを指定している部分(EBMINI_CFGFILEとBookPathの行)は、実際の環境に
 合わせて書き換える。

◎マクロの割り当て
 タイトルコメントに★のついている以下の関数が直接呼び出せる関数なので、必要な
 ものをキーなどに割り当てる。
 ・config
  設定ダイアログを開く。
 ・reference
  現在のカーソル位置より後方にある最初の参照リンクを辿る。
 ・back/forward
  検索結果のヒストリを一つ前へ戻る/先へ進む。
 ・main
  メイン関数。デフォルトの書籍を選択して起動。
 ・book01〜
  番号の書籍を直接指定した状態で起動。

 ※book01以降は、関数を複製して数字の部分を書き換えることで、特定の書籍を
  直接指定して起動する関数を追加可能。

◎マクロの設定
 設定ダイアログで以下の設定をする。

 ○書籍一覧
  編集コマンドで任意の番号に書籍データを割り当てる。
  ・書籍名
   書籍の名前を指定。
  ・検索用コマンド名
   検索用バッチファイルのファイル名を指定。
  ・参照リンクのID文字列
   ebmini用設定ファイルの%BeginReferenceや%EndReferenceの項目に記述した、
   各書籍の参照リンク内に表示するID文字列を指定。
   reference関数で参照リンクを辿るのに必要。
   (串刺し検索用の設定の場合はそれ自体にID文字列を指定する必要はないが、
    検索結果から各書籍の参照リンクを辿るためには、各書籍を単体で検索する
    設定も別に作っておく必要あり)
  ・IMEの状態
   起動時のIMEの挙動を指定。

 ○コマンドインタプリタ
  使用するコマンドインタプリタと実行時のウインドウの状態を指定する。

  ※実行時のウインドウについて
  SW_HIDEやSW_MINIMIZEを指定すると、コマンドインタプリタのウインドウを表に
  出さずに実行することができる。
  ただ、コマンドインタプリタにcommand.comを使う場合はウインドウが表にないと
  速度が大幅に低下することがあるらしい(WindowsMeで発生。9X系固有の現象か)
  ので、SW_SHOWNORMALを指定して表に出したほうがいいかもしれない。
  ウインドウが目障りな場合は、command.comのプロパティでフォントサイズ2×4
  などを選ぶとウインドウが小さくなるので、その状態で画面の右下あたりに
  出るようにしておくとあまり目立たなくなる。

◎作業ファイル
 検索結果とヒストリ用のファイルはテンポラリディレクトリに溜まる仕様。
 以下のファイルを削除すればクリアできる。
 ヒストリ情報   EBminiSearch.wz.histinfo.txt
 ヒストリデータ  EBminiSearch.wz.histdata.txt
 検索結果     ebdata-wz-????????????.ebr(?は数字)

*/

#include    <windows.h>

extern "user32.dll" {
    BOOL WINAPI SetForegroundWindow(HWND hWnd);
}



//. グローバル変数

//.. 書籍テーブル
#define BOOK_MAX    40

typedef struct {
    txstr   szBookName;             // 書籍名
    txstr   szCommand;              // 検索用バッチコマンド名
    txstr   szReferenceId;          // 参照リンクのID
    int     iImeState;              // 検索ダイアログオープン時のIMEの状態
} BOOK_STRUCT;

BOOK_STRUCT g_book_table[BOOK_MAX + 1];

permanent   int     p_iDefaultBookNumber = 1;

mchar   *g_pszImeStateChoice[] = {
    "変更しない",
    "OFFにする",
    "ONにする",
    NULL,
};
#define IME_STATE_NOCHANGE      0
#define IME_STATE_OFF           1
#define IME_STATE_ON            2

//.. 本文の表示選択肢
mchar   *g_pszTextViewChoice[] = {
    "非表示",
    "標準",
    "20項目",
    "256項目",
    "最大",
    NULL,
};
mchar   *g_pszTextView[] = {
    "-",
    "+",
    "20",
    "256",
    "max",
};
int     g_iTextViewIndex;
permanent   int     p_iTextViewIndex = 1;

//.. 見出し一覧の表示選択肢
mchar   *g_pszListViewChoice[] = {
    "非表示",
    "20項目",
    "256項目",
    "最大",
    NULL,
};
mchar   *g_pszListView[] = {
    "-",
    "20",
    "256",
    "max",
};
int     g_iListViewIndex;
permanent   int     p_iListViewIndex = 1;

//.. 使用するコマンドインタプリタ選択肢
mchar   *g_pszCmdShellChoice[] = {
    "cmd.exe(WindowsXP系)",
    "command.com(Windows9X系)",
    NULL,
};
mchar   *g_pszCmdShell[] = {
    "cmd.exe /c",
    "command.com /e:4096 /c",
};
permanent   int     p_iCmdShellIndex = 0;

//.. 実行時のウインドウの状態選択肢
mchar   *g_pszCmdShowChoice[] = {
    "SW_SHOWNORMAL(通常のウインドウ)",
    "SW_MINIMIZE(最小化)",
    "SW_HIDE(非表示)",
    NULL,
};
int     g_iCmdShow[] = {
    SW_SHOWNORMAL,
    SW_MINIMIZE,
    SW_HIDE,
};
permanent   int     p_iCmdShowIndex = 0;

//.. その他
mchar   *g_pszConfigFileName = "EBminiSearch.ini";      // 設定ファイル名

int     g_iTargetBookNumber;        // 検索する書籍
BOOL    g_fUseClipBoard;            // クリップボードから検索語を取得するかどうか

mchar   g_szResultFileName[] = "ebdata-wz-";
    // 検索結果ファイル名の固定部分
    // これに日付を加えたものが検索結果ファイル名となる
mchar   g_szResultFileExt[] = ".ebr";
    // 検索結果ファイル拡張子
    // 結果ファイル専用の設定を作る場合は、ここで指定した拡張子を登録する

mchar   g_szHistInfoFile[] = "EBminiSearch.wz.histinfo.txt";
    // 検索結果ヒストリの行数と現在位置を記録するファイルの名前
mchar   g_szHistDataFile[] = "EBminiSearch.wz.histdata.txt";
    // 検索結果ヒストリを記録するファイルの名前


//. サブ関数

//.. クリップボード取得
void    get_clipboard(txstr szClipBoardString)
{
    TX  *temp_text;

    temp_text = textnew();
    txPaste(temp_text);
    txSelect(temp_text);
    txJumpFileTop(temp_text);
    txGetWord(temp_text, szClipBoardString);
    textclose(temp_text);
}

//.. 検索語内の文字を変換
struct {
    mchar   *pszOrg;
    mchar   *pszNew;
}
g_conv_table[] = {
    { "\\",  "\\\\" },
    { "!",   "" },
    { "\"",  "" },
    { "'",   "" },
    { ",",   "" },
    { "-",   "" },
    { ".",   "" },
    { "?",   "" },
    { "__",  "" },
    { "_",   "" },
    { "~",   "" },
    { "・",  "" },
    { NULL,  NULL }
};

void    conv_search_word(txstr szSearchWord)
{
    txstr   szNewWord = "";
    mchar   *psz;
    int     i;

    psz = szSearchWord;
    while (*psz) {
        BOOL    fConverted = FALSE;
        for (i = 0; g_conv_table[i].pszOrg != NULL; ++i) {
            if (strncmp(psz, g_conv_table[i].pszOrg, strlen(g_conv_table[i].pszOrg)) == 0) {
                szNewWord += g_conv_table[i].pszNew;
                psz += strlen(g_conv_table[i].pszOrg);
                fConverted = TRUE;
                break;
            }
        }
        if (!fConverted) {
            szNewWord += chartostr(*psz);
            ++psz;
        }
    }

    szSearchWord = szNewWord;
}

//.. 文字列から単語を取得
// pszStr先頭部分のスペース・タブはスキップ
// 以降の文字列を次に半角空白文字が出現するか'\0'に達するまでpszBuffにコピー
// 半角空白文字または'\0'を指すポインタを返す
mchar   *getword_by_space(mchar *pszBuff, mchar *pszStr)
{
    mchar   *psz1, *psz2;

    psz1 = strGetWordTop(pszStr);
    psz2 = pszBuff;
    while (*psz1) {
        if (isspace(*psz1))
            break;
        *psz2++ = *psz1++;
    }
    *psz2 = '\0';

    return psz1;
}

//.. 区切り文字列を指定して単語を取得
// pszStr先頭部分のスペース・タブはスキップ
// 以降の文字列を指定した区切り文字列が出現するか'\0'に達するまでpszBuffにコピー
// 区切り文字列の次の文字または'\0'を指すポインタを返す
mchar   *getword_by_delim(mchar *pszBuff, mchar *pszStr, mchar *pszDelim)
{
    mchar   *psz1, *psz2;

    psz1 = strGetWordTop(pszStr);
    psz2 = pszBuff;
    while (*psz1) {
        if (strncmp(psz1, pszDelim, strlen(pszDelim)) == 0)
            break;
        *psz2++ = *psz1++;
    }
    *psz2 = '\0';
    psz1 += strlen(pszDelim);

    return psz1;
}

//.. 子プロセスの起動(終了待ちあり)
BOOL    ChildExec32(mchar *pszCmdLine, int nCmdShow, DWORD dwTimeout)
{
    PROCESS_INFORMATION processinfo;
    STARTUPINFO startinfo;
    memset(&startinfo, 0, sizeof(startinfo));
    startinfo.cb = sizeof(startinfo);
    startinfo.dwFlags = STARTF_USESHOWWINDOW;
    startinfo.wShowWindow = nCmdShow;
    BOOL    ret = TRUE;

    if (!CreateProcess(NULL, pszCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &startinfo, &processinfo)) {
        ret = FALSE;
    }
    else {
        if (WaitForSingleObject(processinfo.hProcess, dwTimeout) == WAIT_TIMEOUT) {
            ret = FALSE;
        }
        CloseHandle(processinfo.hThread);
        CloseHandle(processinfo.hProcess);
    }

    return ret;
}


//. 設定

//.. 設定の読み込み
void    config_read()
{
    int     i;
    txstr   szConfigFilePath;
    TX      *cfg_text;
    mchar   szpara[MAXPARASIZE];
    txstr   szstr;
    mchar   *psz1;

    for (i = 1; i <= BOOK_MAX; ++i)
        structClear(g_book_table[i]);

    pathFullConfig(szConfigFilePath, g_pszConfigFileName);
    if (fileIsExist(szConfigFilePath)) {
        cfg_text = textopen(szConfigFilePath);
        do {
            txGetParaEx(cfg_text, szpara, MAXPARASIZE);
            psz1 = getword_by_delim(szstr, szpara, ",");
            i = atoi(szstr);
            if (1 <= i && i <= BOOK_MAX) {
                psz1 = getword_by_delim(szstr, psz1, ",");
                g_book_table[i].szBookName = szstr;
                psz1 = getword_by_delim(szstr, psz1, ",");
                g_book_table[i].szCommand = szstr;
                psz1 = getword_by_delim(szstr, psz1, ",");
                g_book_table[i].szReferenceId = szstr;
                psz1 = getword_by_delim(szstr, psz1, ",");
                g_book_table[i].iImeState = atoi(szstr);
                switch (g_book_table[i].iImeState) {
                    case IME_STATE_NOCHANGE:
                    case IME_STATE_OFF:
                    case IME_STATE_ON:
                        break;
                    default:
                        g_book_table[i].iImeState = IME_STATE_NOCHANGE;
                        break;
                }
            }
        } while(txNextPara(cfg_text));
        textclose(cfg_text);
    }
    else {
        statprintf("設定ファイルは未作成です");
    }
}

//.. 設定の書き込み
void    config_write()
{
    TX      *cfg_text;
    int     i;
    txstr   szConfigFilePath;

    cfg_text = textnew();
    for (i = 1; i <= BOOK_MAX; ++i) {
        if (g_book_table[i].szBookName != "") {
            txInsertf(cfg_text, "%d,%s,%s,%s,%d\n",
                i,
                g_book_table[i].szBookName,
                g_book_table[i].szCommand,
                g_book_table[i].szReferenceId,
                g_book_table[i].iImeState
            );
        }
    }
    pathFullConfig(szConfigFilePath, g_pszConfigFileName);
    txSaveTo(cfg_text, szConfigFilePath);
    textclose(cfg_text);
}

//.. 項目の編集ダイアログ
void    itemedit_dialog(int index)
{
    int     i;
    txstr       sz1;

    // IME関係の選択肢を文字列ブロックで作成
    HSTRBLK     sb_ime;
    sb_ime = sbNewAlloc(1024);
    for (i = 0; g_pszImeStateChoice[i] != NULL; ++i)
        sbAdd(sb_ime, g_pszImeStateChoice[i]);

    HDIALOG     hd;
    sprintf (sz1, "項目%02dの編集", index);
    hd = dialog(sz1);

    dialogTxstr(hd, "書籍名(&B):", 20, g_book_table[index].szBookName, 40);
    dialogTxstr(hd, "検索用コマンド名(&C):", 20, g_book_table[index].szCommand, 40);
    dialogTxstr(hd, "参照リンクのID文字列(&R):", 20, g_book_table[index].szReferenceId, 40);
    dialogControlStrblk(hd, sb_ime);
    dialogChoice(hd, "IMEの状態(&I):", 20, &g_book_table[index].iImeState, 20, NULL);

    dialogSetPosY(hd, dialogGetPosY(hd) + 10);
    dialogSetPosX(hd, dialogGetPosX(hd) + 150);
    dialogSetH(hd);
    dialogOK(hd, 12);
    dialogCancel(hd, 12);

    dialogOpen(hd);

    sbDelete(sb_ime);
}

//.. ★設定ダイアログ
void    config(TX *text)
{
    TX          *booklist_text;
    int         iBookNumber;
    int         i;
    BOOK_STRUCT item_temp;

    booklist_text = textnew();

    // コマンドインタプリタ関係の選択肢を文字列ブロックで作成
    HSTRBLK     sb_cmdshell, sb_cmdshow;
    sb_cmdshell = sbNewAlloc(1024);
    for (i = 0; g_pszCmdShellChoice[i] != NULL; ++i)
        sbAdd(sb_cmdshell, g_pszCmdShellChoice[i]);
    sb_cmdshow = sbNewAlloc(1024);
    for (i = 0; g_pszCmdShowChoice[i] != NULL; ++i)
        sbAdd(sb_cmdshow, g_pszCmdShowChoice[i]);

    config_read();
    iBookNumber = 1;

    for (;;) {
        // 書籍一覧を裏テキストに作成
        txDeleteText(booklist_text);
        for (i = 1; i <= BOOK_MAX; ++i)
            txInsertf(booklist_text, "%02d: %s\n", i, g_book_table[i].szBookName);
        txJumpNpara(booklist_text, iBookNumber);

        HDIALOG     hd;
        int         id;
        int         id_edit, id_delete, id_up, id_down;
        hd = dialog("EBminiSearchの設定");
        dialogList(hd, "書籍一覧(&B)", booklist_text, 50, 20);
        dialogLFV(hd);
        dialogSetPosY(hd, dialogGetPosY(hd) + 12);
        id_edit = dialogCmd(hd, "編集(&E)", 10);
        id_delete = dialogCmd(hd, "消去(&D)", 10);
        id_up = dialogCmd(hd, "↑(&[)", 10);
        id_down = dialogCmd(hd, "↓(&])", 10);
        dialogLF(hd);

        dialogHeadline(hd, "コマンドインタプリタ", 60);
        dialogControlStrblk(hd, sb_cmdshell);
        dialogChoice(hd, "使用コマンド(&C):", 20, &p_iCmdShellIndex, 35, NULL);
        dialogControlStrblk(hd, sb_cmdshow);
        dialogChoice(hd, "実行時のウインドウ(&W):", 20, &p_iCmdShowIndex, 35, NULL);

        dialogSetPosY(hd, dialogGetPosY(hd) + 10);
        dialogSetPosX(hd, dialogGetPosX(hd) + 140);
        dialogSetH(hd);
        dialogOK(hd, 12);
        dialogCancel(hd, 12);

        id = dialogOpen(hd);

        if (id == 0) {
            break;
        }
        else if (id == IDOK) {
            config_write();
            break;
        }
        else if (id == id_edit) {
            iBookNumber = booklist_text->npara;
            itemedit_dialog(iBookNumber);
        }
        else if (id == id_delete) {
            iBookNumber = booklist_text->npara;
            structClear(g_book_table[iBookNumber]);
        }
        else if (id == id_up) {
            iBookNumber = booklist_text->npara;
            if (iBookNumber > 1) {
                i = iBookNumber - 1;
                item_temp = g_book_table[iBookNumber];
                g_book_table[iBookNumber] = g_book_table[i];
                g_book_table[i] = item_temp;
                iBookNumber = i;
            }
        }
        else if (id == id_down) {
            iBookNumber = booklist_text->npara;
            if (iBookNumber < BOOK_MAX) {
                i = iBookNumber + 1;
                item_temp = g_book_table[iBookNumber];
                g_book_table[iBookNumber] = g_book_table[i];
                g_book_table[i] = item_temp;
                iBookNumber = i;
            }
        }
    }

    textclose(booklist_text);
    sbDelete(sb_cmdshell);
    sbDelete(sb_cmdshow);
}


//. 検索

//.. 指定したもの以外で閲覧中の検索結果ファイルを閉じる
void    close_prev_results(mchar *pszResultFile)
{
    HWND    temp_hwnd = GetWindowDesktopChild();
    mchar   szBuff[CCHPATHNAME];
    while (temp_hwnd) {
        GetWindowText(temp_hwnd, szBuff, CCHPATHNAME);
        if (strstr(szBuff, g_szResultFileName) != NULL && strstr(szBuff, g_szResultFileExt) != NULL) {
            if (strstr(szBuff, pszResultFile) == NULL)
                PostMessage(temp_hwnd, WM_CLOSE, 0, 0);
        }
        temp_hwnd = GetWindow(temp_hwnd, GW_HWNDNEXT);
    }
}

//.. コマンド実行&結果オープン
void    search_exec(mchar *pszSearchCmd, mchar *pszSearchMethod, mchar *pszSearchInput)
{
    // 検索結果ファイルのパスを設定
    txstr   szResultPath;
    txstr   szResultFile;
    GetTempPath(CCHPATHNAME, szResultPath);
    pathSetDir(szResultPath);
    sprintf(szResultFile, "%s%02d%02d%02d%02d%02d%02d%s",
        g_szResultFileName,
        timeGetYear() % 100, timeGetMonth(), timeGetDay(),
        timeGetHour(), timeGetMinute(), timeGetSecond(),
        g_szResultFileExt
    );
    szResultPath += szResultFile;

    // 検索コマンド実行
    txstr   szCmdLine;
    sprintf(szCmdLine, "%s %s %s %s %s %s %s",
        g_pszCmdShell[p_iCmdShellIndex],
        pszSearchCmd,
        szResultPath,
        pszSearchMethod,
        g_pszTextView[g_iTextViewIndex],
        g_pszListView[g_iListViewIndex],
        pszSearchInput
    );
//  information("%s\n%d\n", szCmdLine, strlen(szCmdLine));
    ChildExec32(szCmdLine, g_iCmdShow[p_iCmdShowIndex], 60000);

    // 検索結果をオープン
    TX  *result_text = textFrameOpenEx(szResultPath, "/v /j1");
    SetForegroundWindow(result_text->hwndbase);
    // ↑小窓からの起動時などに開かれたWZが裏へ行ってしまう場合があるようなので、
    // 表へもってくる

    // ヒストリへの書き込み
    txstr   szHistInfoPath, szHistDataPath;
    txstr   sztemp;
    GetTempPath(CCHPATHNAME, sztemp);
    pathSetDir(sztemp);
    szHistInfoPath = sztemp + g_szHistInfoFile;
    szHistDataPath = sztemp + g_szHistDataFile;

    TX      *histinfo_text, *histdata_text;
    int     iHistFileMax, iHistFileCurrent;
    txstr   szpara;
    mchar   *psz1;
    iHistFileMax = 0;
    iHistFileCurrent = 0;
    if (!fileIsExist(szHistInfoPath) || !fileIsExist(szHistDataPath)) {
        histinfo_text = textnew();
        histdata_text = textnew();
    }
    else {
        histinfo_text = textopen(szHistInfoPath);
        txGetPara(histinfo_text, szpara);
        psz1 = strGetWordTop(szpara);
        iHistFileMax = atoi(psz1);
        while (!isspace(*psz1) && (*psz1 != '\0'))
            ++psz1;
        iHistFileCurrent = atoi(psz1);
        if (iHistFileCurrent > iHistFileMax)
            iHistFileCurrent = iHistFileMax;
        histdata_text = textopen(szHistDataPath);
    }

    if (iHistFileCurrent > 0) {
        txJumpNpara(histdata_text, iHistFileCurrent);
        txNextPara(histdata_text);
    }
    txInsert(histdata_text, szResultPath);
    txInsert(histdata_text, "\n");
    txSelectEx(histdata_text, CLIP_CHAR);
    txJumpFileEnd(histdata_text);
    txSelectDelete(histdata_text);
    ++iHistFileCurrent;
    iHistFileMax = iHistFileCurrent;

    txDeleteText(histinfo_text);
    txInsertf(histinfo_text, "%d\t%d\n", iHistFileMax, iHistFileCurrent);

    txSaveTo(histinfo_text, szHistInfoPath);
    txSaveTo(histdata_text, szHistDataPath);
    textclose(histinfo_text);
    textclose(histdata_text);

    // 以前に検索したときの検索結果ファイルが開かれていたら閉じる
    close_prev_results(szResultFile);
}

//.. 検索ダイアログ
void    search_main(TX *text)
{
    int         i;
    txstr       szstr;

    // 選択肢表示用文字列ブロックの初期化
    HSTRBLK     sb_booklist, sb_textview, sb_listview;
    sb_booklist = sbNewAllocEx(4096);
    sb_textview = sbNewAlloc(1024);
    sb_listview = sbNewAlloc(1024);

    config_read();

    // 検索語の初期値をセット
    txstr   szSearchWord = "";
    if (g_fUseClipBoard)
        get_clipboard(szSearchWord);

    // 検索語の入力
    HDIALOG     hd;
    BOOL        fImeOld;
    int         id;
    int         id_set_view_default, id_set_book_default;
    int         id_Zenpou, id_Kouhou, id_Phrase, id_Menu, id_Copyright;
    for (;;)
    {
        hd = dialog("ebminiで検索");

        dialogControlID(hd, 100);
        dialogSetFocus(hd, 100);
        dialogControlHist(hd, HIST_SEARCH);
        dialogTxstr(hd, "検索(&S):", 10, szSearchWord, 40);

        g_iTextViewIndex = p_iTextViewIndex;
        sbDelAll(sb_textview);
        for (i = 0; g_pszTextViewChoice[i] != NULL; ++i) {
            szstr = g_pszTextViewChoice[i];
            if (i == p_iTextViewIndex)
                szstr += "*";
            sbAdd(sb_textview, szstr);
        }
        dialogControlStrblk(hd, sb_textview);
        dialogChoice(hd, "本文表示(&T):", 10, &g_iTextViewIndex, 15, NULL);

        g_iListViewIndex = p_iListViewIndex;
        sbDelAll(sb_listview);
        for (i = 0; g_pszListViewChoice[i] != NULL; ++i) {
            szstr = g_pszListViewChoice[i];
            if (i == p_iListViewIndex)
                szstr += "*";
            sbAdd(sb_listview, szstr);
        }
        dialogControlStrblk(hd, sb_listview);
        dialogChoice(hd, "一覧表示(&L):", 10, &g_iListViewIndex, 15, NULL);

        id_set_view_default = dialogCmd(hd, "現在の表示形式をデフォルトにする(&V)", 35);

        dialogSetPosY(hd, dialogGetPosY(hd) + 10);
        dialogHeadline(hd, "書籍の選択", 55);

        int         iBookIndex;
        iBookIndex = g_iTargetBookNumber - 1;
        sbDelAll(sb_booklist);
        for (i = 1; i <= BOOK_MAX; ++i) {
            sprintf(szstr, "%02d: ", i);
            szstr += g_book_table[i].szBookName;
            if (i == p_iDefaultBookNumber)
                szstr += "*";
            sbAdd(sb_booklist, szstr);
        }
        dialogControlStrblk(hd, sb_booklist);
        dialogChoice(hd, "書籍(&B):", 10, &iBookIndex, 40, NULL);
        id_set_book_default = dialogCmd(hd, "現在の書籍をデフォルトにする(&D)", 35);

        dialogLFV(hd);
        id_Zenpou = dialogCmdDefault(hd, "前方(&W)", 12);
        id_Kouhou = dialogCmd(hd, "後方(&E)", 12);
        id_Phrase = dialogCmd(hd, "フレーズ(&P)", 12);
        id_Menu = dialogCmd(hd, "メニュー(&M)", 12);
        id_Copyright = dialogCmd(hd, "著作権(&R)", 12);
        dialogCancel(hd, 12);

        fImeOld = wndImeGetOpen(text->hwndtext);
        if (g_book_table[g_iTargetBookNumber].iImeState == IME_STATE_OFF)
            wndImeSetOpen(text->hwndtext, FALSE);
        else if (g_book_table[g_iTargetBookNumber].iImeState == IME_STATE_ON)
            wndImeSetOpen(text->hwndtext, TRUE);
        id = dialogOpen(hd);
        wndImeSetOpen(text->hwndtext, fImeOld);

        g_iTargetBookNumber = iBookIndex + 1;

        if (id == id_set_book_default) {
            p_iDefaultBookNumber = g_iTargetBookNumber;
        }
        else if (id == id_set_view_default) {
            p_iTextViewIndex = g_iTextViewIndex;
            p_iListViewIndex = g_iListViewIndex;
        }
        else {
            break;
        }
    }

    sbDelete(sb_booklist);
    sbDelete(sb_textview);
    sbDelete(sb_listview);

    if (id != 0) {
//      txSetSearchContinue(text, szSearchWord, SEARCH_FORWARD);
        txSetSearchContinue(text, ".■", SEARCH_FORWARD);

        conv_search_word(szSearchWord);

        // コマンドライン用データのセットアップ
        txstr   szSearchMethod;
        txstr   szSearchInput;
        txstr   szword;
        mchar   *psz1;
        if (id == id_Zenpou || id == id_Kouhou || id == id_Phrase) {
            if      (id == id_Zenpou)   szSearchMethod = "w";
            else if (id == id_Kouhou)   szSearchMethod = "e";
            else if (id == id_Phrase)   szSearchMethod = "p";

            szSearchInput = "";
            psz1 = szSearchWord;
            for (;;) {
                psz1 = getword_by_space(szword, psz1);
                if (szword == "")
                    break;
                szSearchInput += ("-s\"" + szword + "\"");
                szSearchInput += " ";
            }
        }
        else if (id == id_Menu) {
            szSearchMethod = "M";
            szSearchInput = "-s\"dummy\"";
        }
        else if (id == id_Copyright) {
            szSearchMethod = "C";
            szSearchInput = "-s\"dummy\"";
        }

        // 検索を実行
        search_exec(g_book_table[g_iTargetBookNumber].szCommand, szSearchMethod, szSearchInput);
    }
}

//.. ★参照リンクを検索
void    reference(TX *text)
{
    g_iTextViewIndex = p_iTextViewIndex;
    g_iListViewIndex = p_iListViewIndex;

    // 現在のカーソル位置より後方にある最初の参照リンクを探す
    if (txSearchEx(text, ">", SEARCH_FORWARD) == 0) {
        statprintf("参照リンクが見つかりません");
    }
    else if (txSearchEx(text, "<", SEARCH_PREV) == 0) {
        statprintf("参照リンクが見つかりません");
    }
    else {
        txRight(text);

        int     iTargetBook;
        txstr   szSearchMethod;
        txstr   szSearchInput;
        int     i;
        txstr   szpos;

        // 参照リンクのIDをチェック
        iTargetBook = 0;
        for (i = 1; i <= BOOK_MAX; ++i) {
            if (g_book_table[i].szReferenceId == "")
                continue;
            if (txCmpCur(text, g_book_table[i].szReferenceId) != 0) {
                iTargetBook = i;
                break;
            }
        }
        if (iTargetBook == 0) {
            statprintf("無効な参照リンクです");
        }
        else {
            // 参照位置を取得
            txSetUndispEx(text);
            txSearchEx(text, "|", SEARCH_FORWARD);
            txRight(text);
            txSelectEx(text, CLIP_CHAR);
            txSearchEx(text, ">", SEARCH_FORWARD);
            txGetWord(text, szpos);
            txSelectQuit(text);
            txSetDispEx(text);

            szSearchMethod = "l";
            szSearchInput = "-s" + szpos;

            // 検索を実行
            search_exec(g_book_table[iTargetBook].szCommand, szSearchMethod, szSearchInput);
        }
    }
}

//.. ヒストリ移動操作
#define HIST_MOVE_BACK      1
#define HIST_MOVE_FORWARD   2
void    hist_move(int iDirection)
{
    txstr   szHistInfoPath, szHistDataPath;
    txstr   sztemp;
    GetTempPath(CCHPATHNAME, sztemp);
    pathSetDir(sztemp);
    szHistInfoPath = sztemp + g_szHistInfoFile;
    szHistDataPath = sztemp + g_szHistDataFile;

    if (!fileIsExist(szHistInfoPath)) {
        statprintf("エラー:ヒストリ情報ファイルが存在しません");
    }
    else if (!fileIsExist(szHistDataPath)) {
        statprintf("エラー:ヒストリデータファイルが存在しません");
    }
    else {
        TX      *histinfo_text;
        int     iHistFileMax, iHistFileCurrent;
        txstr   szpara;
        mchar   *psz1;

        histinfo_text = textopen(szHistInfoPath);
        txGetPara(histinfo_text, szpara);
        psz1 = strGetWordTop(szpara);
        iHistFileMax = atoi(psz1);
        while (!isspace(*psz1) && (*psz1 != '\0'))
            ++psz1;
        iHistFileCurrent = atoi(psz1);

        if (iHistFileMax == 0 || iHistFileCurrent == 0 || iHistFileCurrent > iHistFileMax) {
            statprintf("エラー:ヒストリ情報ファイルの内容に異常があります");
        }
        else {
            if (iDirection == HIST_MOVE_BACK && iHistFileCurrent == 1) {
                statprintf("これ以上戻れません");
            }
            else if (iDirection == HIST_MOVE_FORWARD && iHistFileCurrent == iHistFileMax) {
                statprintf("これ以上進めません");
            }
            else {
                iHistFileCurrent += ((iDirection == HIST_MOVE_BACK) ? -1 : 1);

                TX      *histdata_text;
                txstr   szResultPath;
                histdata_text = textopen(szHistDataPath);
                txJumpNpara(histdata_text, iHistFileCurrent);
                txGetPara(histdata_text, szResultPath);
                textclose(histdata_text);

                TX  *result_text = textFrameOpenEx(szResultPath, "/v");
                SetForegroundWindow(result_text->hwndbase);

                txDeleteText(histinfo_text);
                txInsertf(histinfo_text, "%d\t%d\n", iHistFileMax, iHistFileCurrent);
                txSaveTo(histinfo_text, szHistInfoPath);

                // 以前の検索結果ファイルを閉じる
                close_prev_results(pathGetFileName(szResultPath));
            }
        }

        textclose(histinfo_text);
    }
}

//.. ★戻る
void    back(TX *text)
{
    hist_move(HIST_MOVE_BACK);
}

//.. ★進む
void    forward(TX *text)
{
    hist_move(HIST_MOVE_FORWARD);
}


//. ★メイン関数
// デフォルトの書籍を選択して起動
// _clipboardは小窓割り当て用
void    main(TX *text)
{
    g_iTargetBookNumber = p_iDefaultBookNumber;
    g_fUseClipBoard = FALSE;
    search_main(text);
}

void    main_clipboard(TX *text)
{
    g_iTargetBookNumber = p_iDefaultBookNumber;
    g_fUseClipBoard = TRUE;
    search_main(text);
}

//. ★書籍を直接指定して起動する関数
// _clipboardは小窓割り当て用

void    book01(TX *text)
{
    g_iTargetBookNumber = 1;
    g_fUseClipBoard = FALSE;
    search_main(text);
}

void    book01_clipboard(TX *text)
{
    g_iTargetBookNumber = 1;
    g_fUseClipBoard = TRUE;
    search_main(text);
}

void    book02(TX *text)
{
    g_iTargetBookNumber = 2;
    g_fUseClipBoard = FALSE;
    search_main(text);
}

void    book02_clipboard(TX *text)
{
    g_iTargetBookNumber = 2;
    g_fUseClipBoard = TRUE;
    search_main(text);
}

void    book03(TX *text)
{
    g_iTargetBookNumber = 3;
    g_fUseClipBoard = FALSE;
    search_main(text);
}

void    book03_clipboard(TX *text)
{
    g_iTargetBookNumber = 3;
    g_fUseClipBoard = TRUE;
    search_main(text);
}

void    book04(TX *text)
{
    g_iTargetBookNumber = 4;
    g_fUseClipBoard = FALSE;
    search_main(text);
}

void    book04_clipboard(TX *text)
{
    g_iTargetBookNumber = 4;
    g_fUseClipBoard = TRUE;
    search_main(text);
}