/*
* TTX KanjiMenu Plugin
* Copyright (C) 2007 Sunao HARA (naoh@nagoya-u.jp)
* (C) 2007- TeraTerm Project
*/
//// ORIGINAL SOURCE CODE: ttxtest.c
/* Tera Term extension mechanism
Robert O'Callahan (roc+tt@cs.cmu.edu)
Tera Term by Takashi Teranishi (teranishi@rikaxp.riken.go.jp)
*/
#include "teraterm.h"
#include "tttypes.h"
#include "ttplugin.h"
#include "tt_res.h"
#include "i18n.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "inifile_com.h"
#define IniSection "TTXKanjiMenu"
#define ORDER 5000
#define ID_MI_KANJIRECV 54009
#define ID_MI_KANJISEND 54109
#define ID_MI_USEONESETTING 54200
#define UpdateRecvMenu(val) \
CheckMenuRadioItem(pvar->hmEncode, \
ID_MI_KANJIRECV + IdSJIS, \
ID_MI_KANJIRECV + IdUTF8, \
ID_MI_KANJIRECV + (val), \
MF_BYCOMMAND)
#define UpdateSendMenu(val) \
CheckMenuRadioItem(pvar->hmEncode, \
ID_MI_KANJISEND + IdSJIS, \
ID_MI_KANJISEND + IdUTF8, \
ID_MI_KANJISEND + (val), \
MF_BYCOMMAND)
// メニュー項目名の情報
typedef struct {
int menuID;
const char *menuStr;
} KmTextInfo;
// 受信漢字コード (日本語)
static const KmTextInfo MenuNameRecvJ[] = {
{ ID_MI_KANJIRECV + IdSJIS, "Recv: &Shift_JIS" },
{ ID_MI_KANJIRECV + IdEUC, "Recv: &EUC-JP" },
{ ID_MI_KANJIRECV + IdJIS, "Recv: &JIS" },
{ ID_MI_KANJIRECV + IdUTF8, "Recv: &UTF-8" },
};
// 受信漢字コード (韓国語)
static const KmTextInfo MenuNameRecvK[] = {
{ ID_MI_KANJIRECV + IdSJIS, "Recv: &KS5601" },
{ ID_MI_KANJIRECV + IdUTF8, "Recv: &UTF-8" },
};
// 送信漢字コード (日本語)
static const KmTextInfo MenuNameSendJ[] = {
{ ID_MI_KANJISEND + IdSJIS, "Send: S&hift_JIS" },
{ ID_MI_KANJISEND + IdEUC, "Send: EU&C-JP" },
{ ID_MI_KANJISEND + IdJIS, "Send: J&IS" },
{ ID_MI_KANJISEND + IdUTF8, "Send: U&TF-8" }
};
// 送信漢字コード (韓国語)
static const KmTextInfo MenuNameSendK[] = {
{ ID_MI_KANJISEND + IdSJIS, "Send: K&S5601" },
{ ID_MI_KANJISEND + IdUTF8, "Send: U&TF-8" }
};
// 送受信漢字コード (日本語)
static const KmTextInfo MenuNameOneJ[] = {
{ ID_MI_KANJIRECV + IdSJIS, "Recv/Send: &Shift_JIS" },
{ ID_MI_KANJIRECV + IdEUC, "Recv/Send: &EUC-JP" },
{ ID_MI_KANJIRECV + IdJIS, "Recv/Send: &JIS" },
{ ID_MI_KANJIRECV + IdUTF8, "Recv/Send: &UTF-8" },
};
// 送受信漢字コード (韓国語)
static const KmTextInfo MenuNameOneK[] = {
{ ID_MI_KANJIRECV + IdSJIS, "Recv/Send: &KS5601" },
{ ID_MI_KANJIRECV + IdUTF8, "Recv/Send: &UTF-8" },
};
// メニュー自体の国際化用情報 (日本語)
static DlgTextInfo MenuTitleInfoJ[] = {
{ -1, "MENU_KANJI" }
};
// メニュー自体の国際化用情報 (韓国語)
static DlgTextInfo MenuTitleInfoK[] = {
{ -1, "MENU_KANJI_K" }
};
// メニュー内の各項目の国際化用情報 (送受信分離時/日本語)
static const DlgTextInfo MenuInfoSeparateJ[] = {
// 受信
{ ID_MI_KANJIRECV + IdSJIS, "MENU_RECV_SJIS" },
{ ID_MI_KANJIRECV + IdEUC, "MENU_RECV_EUCJP" },
{ ID_MI_KANJIRECV + IdJIS, "MENU_RECV_JIS" },
{ ID_MI_KANJIRECV + IdUTF8, "MENU_RECV_UTF8" },
// 送信
{ ID_MI_KANJISEND + IdSJIS, "MENU_SEND_SJIS" },
{ ID_MI_KANJISEND + IdEUC, "MENU_SEND_EUCJP" },
{ ID_MI_KANJISEND + IdJIS, "MENU_SEND_JIS" },
{ ID_MI_KANJISEND + IdUTF8, "MENU_SEND_UTF8" },
// UseOneSetting
{ ID_MI_USEONESETTING, "MENU_USE_ONE_SETTING" }
};
// メニュー内の各項目の国際化用情報 (送受信分離時/韓国語)
static const DlgTextInfo MenuInfoSeparateK[] = {
// 受信
{ ID_MI_KANJIRECV + IdSJIS, "MENU_RECV_KS5601" },
{ ID_MI_KANJIRECV + IdUTF8, "MENU_RECV_UTF8" },
// 送信
{ ID_MI_KANJISEND + IdSJIS, "MENU_SEND_KS5601" },
{ ID_MI_KANJISEND + IdUTF8, "MENU_SEND_UTF8" },
// UseOneSetting
{ ID_MI_USEONESETTING, "MENU_USE_ONE_SETTING" }
};
// メニュー内の各項目の国際化用情報 (送受信共通時/日本語)
static const DlgTextInfo MenuInfoOneJ[] = {
// 送受信
{ ID_MI_KANJIRECV + IdSJIS, "MENU_SJIS" },
{ ID_MI_KANJIRECV + IdEUC, "MENU_EUCJP" },
{ ID_MI_KANJIRECV + IdJIS, "MENU_JIS" },
{ ID_MI_KANJIRECV + IdUTF8, "MENU_UTF8" },
// UseOneSetting
{ ID_MI_USEONESETTING, "MENU_USE_ONE_SETTING" }
};
// メニュー内の各項目の国際化用情報 (送受信共通時/韓国語)
static const DlgTextInfo MenuInfoOneK[] = {
// 送受信
{ ID_MI_KANJIRECV + IdSJIS, "MENU_KS5601" },
{ ID_MI_KANJIRECV + IdUTF8, "MENU_UTF8" },
// UseOneSetting
{ ID_MI_USEONESETTING, "MENU_USE_ONE_SETTING" }
};
static HANDLE hInst; /* Instance handle of TTX*.DLL */
typedef struct {
PTTSet ts;
PComVar cv;
HMENU hmEncode;
PSetupTerminal origSetupTermDlg;
PReadIniFile origReadIniFile;
PWriteIniFile origWriteIniFile;
BOOL UseOneSetting;
BOOL NeedResetCharSet;
} TInstVar;
static TInstVar *pvar;
/* WIN32 allows multiple instances of a DLL */
static TInstVar InstVar;
/*
* 初期化
*/
static void PASCAL TTXInit(PTTSet ts, PComVar cv) {
pvar->ts = ts;
pvar->cv = cv;
pvar->origReadIniFile = NULL;
pvar->origWriteIniFile = NULL;
pvar->UseOneSetting = TRUE;
pvar->NeedResetCharSet = FALSE;
}
/*
* 端末設定ダイアログのフック関数1: UseOneSetting 用
*
* 送信と受信の漢字コード設定が同じになるように調整する。
*/
static BOOL PASCAL TTXKanjiMenuSetupTerminal(HWND parent, PTTSet ts) {
WORD orgRecvCode, orgSendCode;
BOOL ret;
orgRecvCode = pvar->ts->KanjiCode;
orgSendCode = pvar->ts->KanjiCodeSend;
ret = pvar->origSetupTermDlg(parent, ts);
if (ret) {
if (orgRecvCode == pvar->ts->KanjiCode && orgSendCode != pvar->ts->KanjiCodeSend) {
// 送信コードのみ変更した場合は送信コードに合わせる
pvar->ts->KanjiCode = pvar->ts->KanjiCodeSend;
}
else {
// それ以外は受信コードに合わせる
pvar->ts->KanjiCodeSend = pvar->ts->KanjiCode;
}
}
return ret;
}
/*
* 端末設定ダイアログのフック関数2: 内部状態リセット用
*
* 端末設定ダイアログをフックし、設定ダイアログを開かずに TRUE を返す事によって
* 設定ダイアログ呼出の後処理のみを利用する。
*/
static BOOL PASCAL ResetCharSet(HWND parent, PTTSet ts) {
(void)parent;
(void)ts;
pvar->NeedResetCharSet = FALSE;
return TRUE;
}
static void PASCAL TTXGetUIHooks(TTXUIHooks *hooks) {
if (pvar->NeedResetCharSet) {
// 内部状態リセットの為に呼び出された場合
*hooks->SetupTerminal = ResetCharSet;
}
else if (pvar->UseOneSetting && (pvar->ts->Language == IdJapanese || pvar->ts->Language == IdKorean)) {
// UseOneSetting が TRUE の時は端末設定ダイアログの後処理の為にフックする
pvar->origSetupTermDlg = *hooks->SetupTerminal;
*hooks->SetupTerminal = TTXKanjiMenuSetupTerminal;
}
}
/*
* 漢字コード関連の内部状態のリセット
* TTXからはTera Termの内部状態を直接いじれない為、端末設定ダイアログの後処理を利用する。
*/
static void CallResetCharSet(HWND hWin){
pvar->NeedResetCharSet = TRUE;
SendMessage(hWin, WM_COMMAND, MAKELONG(ID_SETUP_TERMINAL, 0), 0);
}
/*
* 設定の読み込み
*/
static void PASCAL TTXKanjiMenuReadIniFile(const wchar_t *fn, PTTSet ts) {
char buff[20];
/* Call original ReadIniFile */
pvar->origReadIniFile(fn, ts);
GetPrivateProfileStringAFileW(IniSection, "UseOneSetting", "on", buff, sizeof(buff), fn);
if (_stricmp(buff, "off") == 0) {
pvar->UseOneSetting = FALSE;
}
else {
pvar->UseOneSetting = TRUE;
// UseOneSetting が on の場合は、送受信設定が同じになるように調整する
if (pvar->ts->Language == IdJapanese) {
pvar->ts->KanjiCodeSend = pvar->ts->KanjiCode;
}
else if (pvar->ts->Language == IdKorean) {
pvar->ts->KanjiCodeSend = pvar->ts->KanjiCode;
}
}
return;
}
/*
* 設定の保存
*/
static void PASCAL TTXKanjiMenuWriteIniFile(const wchar_t *fn, PTTSet ts) {
/* Call original WriteIniFile */
pvar->origWriteIniFile(fn, ts);
WritePrivateProfileStringAFileW(IniSection, "UseOneSetting", pvar->UseOneSetting?"on":"off", fn);
return;
}
/*
* 設定の読み書きをフックする
*/
static void PASCAL TTXGetSetupHooks(TTXSetupHooks *hooks) {
pvar->origReadIniFile = *hooks->ReadIniFile;
*hooks->ReadIniFile = TTXKanjiMenuReadIniFile;
pvar->origWriteIniFile = *hooks->WriteIniFile;
*hooks->WriteIniFile = TTXKanjiMenuWriteIniFile;
}
/*
* 特定のIDの子を持つメニューの位置を返す
*/
static int GetMenuPosByChildId(HMENU menu, UINT id) {
UINT i, j, items, subitems, cur_id;
HMENU m;
items = GetMenuItemCount(menu);
for (i=0; i<items; i++) {
if (m = GetSubMenu(menu, i)) {
subitems = GetMenuItemCount(m);
for (j=0; j<subitems; j++) {
cur_id = GetMenuItemID(m, j);
if (cur_id == id) {
return i;
}
}
}
}
return -1;
}
/*
* 送信漢字コード設定用のメニュー項目を追加する
*
* UseOneSetting が off の時に使う
*/
static void InsertSendKcodeMenu(HMENU menu) {
// UINT flag = MF_BYPOSITION | MF_STRING | MF_CHECKED;
int i;
if (pvar->ts->Language == IdJapanese) {
for (i = 0; i < _countof(MenuNameSendJ); i++) {
InsertMenu(pvar->hmEncode, ID_MI_USEONESETTING, MF_BYCOMMAND | MF_STRING,
MenuNameSendJ[i].menuID, MenuNameSendJ[i].menuStr);
}
}
else { // IdKorean
for (i = 0; i < _countof(MenuNameSendK); i++) {
InsertMenu(pvar->hmEncode, ID_MI_USEONESETTING, MF_BYCOMMAND | MF_STRING,
MenuNameSendK[i].menuID, MenuNameSendK[i].menuStr);
}
}
InsertMenu(menu, ID_MI_USEONESETTING, MF_BYCOMMAND | MF_SEPARATOR, 0, NULL);
}
/*
* 送信漢字コード設定用のメニュー項目を削除する
*
* UseOneSetting が on にされた時に呼ばれる
*/
static void DeleteSendKcodeMenu(HMENU menu) {
int i;
if (pvar->ts->Language == IdJapanese) {
for (i=0; i < _countof(MenuNameSendJ); i++) {
DeleteMenu(menu, MenuNameSendJ[i].menuID, MF_BYCOMMAND);
}
// 受信メニューの直後に有るセパレータを削除する
DeleteMenu(menu, _countof(MenuNameRecvJ), MF_BYPOSITION);
}
else { // IdKorean
for (i=0; i < _countof(MenuNameSendK); i++) {
DeleteMenu(menu, MenuNameSendK[i].menuID, MF_BYCOMMAND);
}
// 受信メニューの直後に有るセパレータを削除する
DeleteMenu(menu, _countof(MenuNameRecvK), MF_BYPOSITION);
}
}
/*
* メニュー項目の更新
*
* 以下の二つについてメニュー項目を更新する。
* 1. UseOneSetting の設定に基づいて、受信用メニュー項目を受信専用/送受信兼用の切り替えを行う
* 2. メニュー項目の国際化を行う
*
* 通常は 1 で設定した項目名は 2 で上書き更新されるが、lng ファイルが設定されていない、
* または lng ファイルにメニュー項目名が含まれていない場合への対応として 1 を行っている。
*/
static void UpdateMenuCaption(HMENU menu, BOOL UseOneSetting) {
#define doUpdateMenu(nameInfo, i18nInfo) { \
UINT i, id; \
for (i=0; i < _countof(nameInfo); i++) { \
id = (nameInfo)[i].menuID; \
ModifyMenu(menu, id, MF_BYCOMMAND, id, (nameInfo)[i].menuStr); \
} \
SetI18nMenuStrs(IniSection, menu, (i18nInfo), _countof(i18nInfo), pvar->ts->UILanguageFile); \
}
if (pvar->ts->Language == IdJapanese) {
if (UseOneSetting) {
doUpdateMenu(MenuNameOneJ, MenuInfoOneJ);
}
else {
doUpdateMenu(MenuNameRecvJ, MenuInfoSeparateJ);
}
}
else { // IdKorean
if (UseOneSetting) {
doUpdateMenu(MenuNameOneK, MenuInfoOneK);
}
else {
doUpdateMenu(MenuNameRecvK, MenuInfoSeparateK);
}
}
}
/*
* This function is called when Tera Term creates a new menu.
*/
static void PASCAL TTXModifyMenu(HMENU menu) {
// 言語が日本語または韓国語の時のみメニューに追加する
if (pvar->ts->Language != IdJapanese && pvar->ts->Language != IdKorean) {
return;
}
{
MENUITEMINFO mi;
int pos, i;
pvar->hmEncode = CreateMenu();
// Windows 95 でメニューが表示されないのでバージョンチェックを入れる (2009.2.18 maya)
if (IsWindows2000OrLater()) {
memset(&mi, 0, sizeof(MENUITEMINFO));
mi.cbSize = sizeof(MENUITEMINFO);
}
else {
memset(&mi, 0, sizeof(MENUITEMINFO)-sizeof(HBITMAP));
mi.cbSize = sizeof(MENUITEMINFO)-sizeof(HBITMAP);
}
mi.fMask = MIIM_TYPE | MIIM_SUBMENU;
mi.fType = MFT_STRING;
mi.hSubMenu = pvar->hmEncode;
if (pvar->ts->Language == IdJapanese) {
mi.dwTypeData = "&KanjiCode";
}
else { // IdKorean
mi.dwTypeData = "Coding(&K)";
}
InsertMenuItem(menu, ID_HELPMENU, FALSE, &mi);
if (pvar->ts->Language == IdJapanese) {
for (i = 0; i < _countof(MenuNameRecvJ); i++) {
AppendMenu(pvar->hmEncode, MF_STRING, MenuNameRecvJ[i].menuID, MenuNameRecvJ[i].menuStr);
}
}
else { // IdKorean
for (i = 0; i < _countof(MenuNameRecvK); i++) {
AppendMenu(pvar->hmEncode, MF_STRING, MenuNameRecvK[i].menuID, MenuNameRecvK[i].menuStr);
}
}
AppendMenu(pvar->hmEncode, MF_SEPARATOR, 0, NULL);
AppendMenu(pvar->hmEncode, MF_STRING, ID_MI_USEONESETTING , "Use &one setting");
if (!pvar->UseOneSetting) {
InsertSendKcodeMenu(pvar->hmEncode);
}
pos = GetMenuPosByChildId(menu, ID_MI_KANJIRECV + IdSJIS);
if (pos > 0) {
if (pvar->ts->Language == IdJapanese) {
MenuTitleInfoJ->nIDDlgItem = pos;
SetI18nMenuStrs(IniSection, menu, MenuTitleInfoJ, _countof(MenuTitleInfoJ), pvar->ts->UILanguageFile);
}
else {
MenuTitleInfoK->nIDDlgItem = pos;
SetI18nMenuStrs(IniSection, menu, MenuTitleInfoK, _countof(MenuTitleInfoK), pvar->ts->UILanguageFile);
}
}
UpdateMenuCaption(pvar->hmEncode, pvar->UseOneSetting);
}
}
/*
* ラジオメニュー/チェックメニューの状態を設定に合わせて更新する。
*/
static void PASCAL TTXModifyPopupMenu(HMENU menu) {
// メニューが呼び出されたら、最新の設定に更新する。(2007.5.25 yutaka)
(void)menu;
UpdateRecvMenu(pvar->ts->KanjiCode);
if (!pvar->UseOneSetting) {
UpdateSendMenu(pvar->ts->KanjiCodeSend);
}
CheckMenuItem(pvar->hmEncode, ID_MI_USEONESETTING, MF_BYCOMMAND | (pvar->UseOneSetting)?MF_CHECKED:0);
}
/*
* This function is called when Tera Term receives a command message.
*/
static int PASCAL TTXProcessCommand(HWND hWin, WORD cmd) {
WORD val;
if ((cmd > ID_MI_KANJIRECV) && (cmd <= ID_MI_KANJIRECV+IdUTF8)) {
val = cmd - ID_MI_KANJIRECV;
pvar->cv->KanjiCodeEcho = pvar->ts->KanjiCode = val;
if (pvar->UseOneSetting) {
pvar->cv->KanjiCodeSend = pvar->ts->KanjiCodeSend = val;
}
CallResetCharSet(hWin);
return UpdateRecvMenu(pvar->ts->KanjiCode)?1:0;
}
else if ((cmd > ID_MI_KANJISEND) && (cmd <= ID_MI_KANJISEND+IdUTF8)) {
val = cmd - ID_MI_KANJISEND;
pvar->cv->KanjiCodeSend = pvar->ts->KanjiCodeSend = val;
if (pvar->UseOneSetting) {
pvar->cv->KanjiCodeEcho = pvar->ts->KanjiCode = val;
CallResetCharSet(hWin);
return UpdateRecvMenu(pvar->ts->KanjiCode)?1:0;
}
else {
CallResetCharSet(hWin);
return UpdateSendMenu(pvar->ts->KanjiCodeSend)?1:0;
}
}
else if (cmd == ID_MI_USEONESETTING) {
if (pvar->UseOneSetting) {
pvar->UseOneSetting = FALSE;
InsertSendKcodeMenu(pvar->hmEncode);
}
else {
pvar->UseOneSetting = TRUE;
val = pvar->ts->KanjiCode;
pvar->cv->KanjiCodeSend = pvar->ts->KanjiCodeSend = val;
DeleteSendKcodeMenu(pvar->hmEncode);
}
UpdateMenuCaption(pvar->hmEncode, pvar->UseOneSetting);
return 1;
}
return 0;
}
/*
* This record contains all the information that the extension forwards to the
* main Tera Term code. It mostly consists of pointers to the above functions.
* Any of the function pointers can be replaced with NULL, in which case
* Tera Term will just ignore that function and assume default behaviour, which
* means "do nothing".
*/
static TTXExports Exports = {
/* This must contain the size of the structure. See below for its usage. */
sizeof(TTXExports),
/* This is the load order number of this DLL. */
ORDER,
/* Now we just list the functions that we've implemented. */
TTXInit,
TTXGetUIHooks,
TTXGetSetupHooks,
NULL, // TTXOpenTCP,
NULL, // TTXCloseTCP,
NULL, // TTXSetWinSize,
TTXModifyMenu,
TTXModifyPopupMenu,
TTXProcessCommand,
NULL, // TTXEnd,
NULL // TTXSetCommandLine
};
BOOL __declspec(dllexport) PASCAL TTXBind(WORD Version, TTXExports *exports) {
int size = sizeof(Exports) - sizeof(exports->size);
/* do version checking if necessary */
/* if (Version!=TTVERSION) return FALSE; */
(void)Version;
if (size > exports->size) {
size = exports->size;
}
memcpy((char *)exports + sizeof(exports->size),
(char *)&Exports + sizeof(exports->size),
size);
return TRUE;
}
BOOL WINAPI DllMain(HANDLE hInstance,
ULONG ul_reason_for_call,
LPVOID lpReserved)
{
(void)lpReserved;
switch( ul_reason_for_call ) {
case DLL_THREAD_ATTACH:
/* do thread initialization */
break;
case DLL_THREAD_DETACH:
/* do thread cleanup */
break;
case DLL_PROCESS_ATTACH:
/* do process initialization */
hInst = hInstance;
pvar = &InstVar;
break;
case DLL_PROCESS_DETACH:
/* do process cleanup */
break;
}
return TRUE;
}
/* vim: set ts=4 sw=4 ff=dos : */