#include "stdafx.h"
#include "Rnsf7.h"
#include "MainWindow.h"
#include "OpenSetting.h"
#include "interprete.h"
#include "addctrl.h"
#include "refresh.h"
#include "SelIcon.h"
#include "lvedit.h"
#include "dlllib.h"
#include "helpdlg.h"
#include "opendlg.h"
#include "symbol.h"
#include "splash.h"
#include "MainSearchRsc.h"
#include "MainListview.h"
#include "filetypes.h"
#include "commfunc.h"
#include "chkaccr.h"
#include "pre_interprete.h"
#include "lvcsv.h"

#define R7OSSC_TIMERID	634
#define R7OSSC_TIMERWAIT 200 // スムーススクロール後に再描画を行うための待ち時間

// #define DEBUG_LOGMODE				1	// ログ出力を行う場合(doutを利用する)

// インターバルタイマのとび先ラベル
EXTERN char *pszEditDialogTimerLabel;

/* 低レベルR-Scriptへの変換後の様子を見る場合は、
   Rnsf7のレジストリキーにDWORDのエントリ「DEBUG_LOWLEVEL_INTERPRETE」を作り、
   値を 1 にして下さい */

// 関数
static void  movectrl( HWND hWnd, int vsa, int vsb );	 // スクロールバーを操作したあとにコントロールを移動する

static short galabel( HWND hWnd, const char *rscname, const char *id, const char *alabel ); // id+alabel というラベルがあったらジャンプ

static bool listview_edit( HWND hWnd, int iCtrl );		 // リストビュー：アイテムの編集(2カラム)
static bool listview_delete( HWND hWnd, int iCtrl );	 // リストビュー：アイテムの削除(2カラム)
static bool listview_new( HWND hWnd, int iCtrl );		 // リストビュー：アイテムの作成(2カラム)

static bool listview_n_edit( HWND hWnd, int iCtrl );	 // リストビュー：アイテムの編集(nカラム)
// static bool listview_n_delete( HWND hWnd, int iCtrl );	 // リストビュー：アイテムの削除(nカラム)
// やっていることは n も 2 も同じなので単純に置き換え
#define listview_n_delete(h,i) listview_delete(h,i)
static bool listview_n_new( HWND hWnd, int iCtrl );		 // リストビュー：アイテムの作成(nカラム)

static int CALLBACK listview_pfnCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort); // リストビュー：ソート中に呼び出される関数
volatile bool running_settingdialog = 0;

// 編集ダイアログのデフォルトフォーカスのコントロールID
static const char *MainEditDlgProc_Focus;

bool bNotAddRecent;		// 「最近使った機能項目」への追加（更新）を行わないフラグ

// 編集ダイアログを開く
bool OpenSetting( HWND hParentWnd, const MLVI *mlvi, HWND hStatusBar )
{
	bool rv;

	if ( !mlvi )
		return false;

	rscname = mlvi->rsc_fname;
	g_item = mlvi->item;
	helpfile = mlvi->help;
	contextid = mlvi->contextid;

	running_settingdialog = 1;
	MainEditDlgProc_Focus = NULL;
	if ( -1 == DialogBox(hInst, (LPCTSTR)IDD_DIALOG1, hParentWnd, (DLGPROC)MainEditDlgProc) ) {
		// ↑現状では、エラーが起きたときは、事前に原因付きのダイアログが表示されるので、ここでエラーメッセージは必要ない
		rv = false;
	}
	else {
		rv = true;
	}
	running_settingdialog = 0;

	if ( mlvi == NULL ) {
		safefree( g_item );
		safefree( helpfile );
	}

	if ( hStatusBar ) {
		SendMessage(hStatusBar, SB_SETTEXT, 0, (LPARAM)(bCategory?"左のカテゴリ一覧から分類名を選択し、右の一覧から設定を編集したい項目を開いて下さい。":"設定を編集したい項目を開いて下さい。"));
	}
	return rv;
}

// 「ファイルを指定して開く」またはコマンドラインで指定された場合。
int  openrsc( HWND hWnd, const char *arg_rscname, const char *FocusID )
{
	int rv = 0, l;

	// " に囲まれていたら、" を取り除く
	if ( arg_rscname[0] == '\"' ) {
		rscname = _strdup( arg_rscname + 1 );
		if ( rscname ) {
			rv = lstrlen( rscname );
			if ( rscname[rv-1] == '\"' )
				rscname[rv-1] = 0;
		}
	}
	else {
		rscname = _strdup( arg_rscname );
	}

	l = lstrlen( rscname );
	if ( l > 4 && 0 == _stricmp(&rscname[l-4], ".rnd") ) {
		// ダイアログ状態定義として解析が必要
		HANDLE hFile;
		char *pbuf;
		DWORD it, tmp;
		BOOL  bflag;

		rndname = rscname;
		// * ではじまっていたら、ダイアログ状態をファイルへ保存する
		// : ではじまっていたら、ダイアログ状態をファイルから読み込んでレジストリへ保存する
		if ( rndname[0] == '*' || rndname[0] == ':' ) {
			// rndファイル名からrscファイル名を生成する
			for( tmp = lstrlen(rndname) ; (isKanji1(rndname,tmp-2) || rndname[tmp-1] != '\\') && rndname[tmp-1] != '*' ; tmp-- )
				;
			rscname = _strdup( &rndname[tmp] );
			tmp = lstrlen( rscname );
			if ( tmp > 2 ) {
				rscname[ tmp-2 ] = 's';
				rscname[ tmp-1 ] = 'c';
			}
		}
		else {
			hFile = CreateFile(rndname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
			if ( hFile == INVALID_HANDLE_VALUE ) {
				destroy_splash_window();
				MessageBox( hWnd, "指定されたファイルを開けません。", "ダイアログ状態をファイルから読み込み", MB_OK | MB_ICONSTOP );
				return FALSE;
			}

			// ヘッダのチェック(DIALOGHEADERSIZE bytes)
			pbuf = zalloc( DIALOGHEADERSIZE+1 );
			if ( !pbuf ) {
				CloseHandle( hFile );
				destroy_splash_window();
				MessageBox( hWnd, "動作を完了させるためのメモリが不足しています。", "ダイアログ状態をファイルから読み込み", MB_OK | MB_ICONSTOP );
				return FALSE;
			}
			bflag = ReadFile( hFile, pbuf, DIALOGHEADERSIZE, &tmp, NULL );
			if ( bflag == 0 || (bflag != 0 && tmp == 0) ) {
				pbuf[0] = 0;
			}
			pbuf[DIALOGHEADERSIZE-1] = 0;
			pbuf[DIALOGHEADERIGNR] = '-'; // 上位互換性のための読み飛ばし

			if ( strcmp(pbuf, DIALOGHEADER) != 0 ) {
				CloseHandle( hFile );
				destroy_splash_window();
				MessageBox( hWnd, "指定されたファイルは状態が保存されたファイルではないか、対応していないバージョンです。", "ダイアログ状態をファイルから読み込み", MB_OK | MB_ICONSTOP );
				return FALSE;
			}
			safefree( pbuf );

			// rscファイル名を読み込む
			bflag = ReadFile(hFile, (LPVOID)&it, sizeof(it), &tmp, NULL);
			if ( bflag == 0 || (bflag != 0 && tmp == 0) ) it = 1;
		
			pbuf = zalloc( it );
			if ( pbuf ) {
				bflag = ReadFile(hFile, pbuf, it, &tmp, NULL);
				if ( bflag == 0 || (bflag != 0 && tmp == 0) ) it = 1;

				rscname = pbuf;
				// rscname に引き継ぐので pbuf を解放してはならない
			}
			else {
				destroy_splash_window();
				MessageBox( hWnd, "ダイアログ状態読み込みのために必要なメモリを確保できませんでした。", "ダイアログ状態をファイルから読み込み", MB_OK | MB_ICONSTOP );
				return FALSE;
			}

			CloseHandle( hFile );
		}
	}
	else {
		rndname = NULL;
	}

	int ec = LR_USER_ERROR;
	{ // lrはメモリを食うので、このスコープ内での実行に留める
		LOAD_RSC lr;
		if ( LR_OK == lr.source_load(rscname) ) {
			lr.searchItem(LR_SI_LOAD, NULL, 0, ec);
			if (ec == LR_OK) {
				g_item = _strdup( lr.searchItem(LR_SI_ITEM, rscname, 0, ec) ); // タイトル
				helpfile = _strdup( lr.searchItem(LR_SI_HELP, "none", 0, ec) ); // ヘルプファイル
				contextid = atou( lr.searchItem(LR_SI_CONTEXTID, "0", 0, ec) ); // ヘルプID (.hlpの場合)
			}
			else {
				lr.errmes(hWnd);
			}
		}
		else {
			lr.errmes(hWnd);
		}
	}

	if (ec == LR_OK) {
		running_settingdialog = 1;
		MainEditDlgProc_Focus = FocusID;
		rv = (int)DialogBox(hInst, (LPCTSTR)IDD_DIALOG1, hWnd, (DLGPROC)MainEditDlgProc);
		running_settingdialog = 0;
	}

	safefree( g_item );
	safefree( helpfile );
	safefree( rscname );
	safefree( rndname );

	return rv?1:0;
}

// コントロールの変更が生じた
void ctrlChange( HWND hDlg, unsigned ctrl_no )
{
	if ( ctrl_no < nctrls ) {
		if ( !(rctrls[ctrl_no].flag_opt & CTRL_NOEFFECT ) ) {
			if ( rctrls[ctrl_no].flag_opt & 3 ) {
				system_refresh(GetParent(hDlg), hDlg, (BYTE)(rctrls[ctrl_no].flag_opt & 3));
			}
			stdButtonsStatus( hDlg, TRUE, -1, TRUE, TRUE, -1, -1, -1 );
		}
		rctrls[ctrl_no].changed = 1;
		if ( rctrls[ctrl_no].id[0] == ':' ) { // IDがラベルだったら、ラベルへジャンプ
			if ( 2 == rsc( hDlg, rscname, rctrls[ctrl_no].id, 0, NULL ) ) { // abortが返ってきたら、編集作業は中断
				free_controls( hDlg, 0 );
				EndDialog(hDlg, 2);
			}
		}
	}
}

// OK/適用ボタンの押下（ダイアログ内容の有効化）
// id: IDOK[OK]/IDYES[適用] - どちらのボタンが押されてこの関数が呼ばれるか
void okApply( HWND hDlg, int btnId )
{
	BOOL status_idcancel, status_idhelp, status_idretry, status_idsave, status_idload;
	HCURSOR OldCursor;
	short ret;

	SendMessage( GetParent(hDlg), R7MW_STATUS_BAR, 0, (LPARAM)"終了処理を行っています..." );
	OldCursor = SetCursor( LoadCursor(NULL, IDC_WAIT) );

	// 通知処理は長いので、ボタンはグレイアウトしておく。
	stdButtonStatus(hDlg, IDOK, FALSE);
	stdButtonStatus(hDlg, IDYES, FALSE);
	status_idcancel = stdButtonStatus(hDlg, IDCLOSE, FALSE);
	status_idretry = stdButtonStatus(hDlg, IDRETRY, FALSE);
	status_idhelp = stdButtonStatus(hDlg, IDHELP, FALSE);
	status_idsave = stdButtonStatus(hDlg, IDSAVE, FALSE);
	status_idload = stdButtonStatus(hDlg, IDLOAD, FALSE);

	/* レジストリへ書き込み */
	ret = rsc( hDlg, rscname, ":OK", 0, NULL );

	// スクリプトからAbortが返ってきた場合は終了処理を中断
	// スクリプトがエラーだった場合も終了処理を中断
	// 正常終了または :OK ラベルが見つからない場合は、処理を続行
	if ( ret == 0 || ret == 3 ) {
		SendMessage( GetParent(hDlg), R7MW_STATUS_BAR, 0, (LPARAM)"設定を保存しています..." );
		if ( 1 == free_controls( hDlg, btnId != IDYES ? 1 : 2 ) ) {
			destroy_splash_window();
			MessageBox(hDlg, "一部のレジストリキーを開くことができなかったため、レジストリエントリへの自動書き込みをすることができませんでした。\nいくつかの設定値が失われたかもしれません。\n\n詳細なエラー情報を得るには、「設定」メニュー→「オプション」にある「設定保存時、レジストリ書込みでエラーが発生したら報告する」にチェックしてください。", "設定値の損失", MB_OK | MB_ICONEXCLAMATION );
		}
		// system_refresh:事前に予約しておいたリフレッシュを実行
		// system_refreshが1を返すと、再起動要求
		if ( system_refresh(GetParent(hDlg), hDlg, 4) == 1 || btnId != IDYES ) { // [OK]なら終了だが、[Apply](YES)なら続行
			SetCursor( OldCursor );
			EndDialog(hDlg, 1);
			return;
		}
	}
	else {
		stdButtonStatus(hDlg, IDOK, TRUE);
		stdButtonStatus(hDlg, IDYES, TRUE);
	}

	stdButtonsStatus(hDlg, -1, status_idcancel, -1, status_idretry, status_idsave, status_idload, status_idhelp);

	// [適用]を押して、レジストリへの一通りの書き込みが済んだら :APPRY を呼ぶ
	rsc( hDlg, rscname, ":APPRY", 0, NULL );
	// [適用]ボタンはグレイアウトするので、[キャンセル]ボタン上にカーソルを置く
	// (メニューバーが出ている場合は、何も起こらないだろう)
	SetFocus(GetDlgItem(hDlg, IDCLOSE));

	SetCursor( OldCursor );
	SendMessage( GetParent(hDlg), R7MW_STATUS_BAR, 0, (LPARAM)"" );
}

// 編集ダイアログ用プロシージャ
LRESULT CALLBACK MainEditDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	static UINT vsb;
	UINT vsa;
	SCROLLINFO si;
	static HMENU hLVMenu, hCBLMenu;
	static LONG  ddTop, ddHeight, ddLeft, ddWidth;
	static bool  abend_flag;
	static HWND  hHighLightCtrl;
	static HBRUSH hHighLightBrush;
	static COLORREF cHighLightColor = DEFAULT_HIGHLIGHTCOLOR;
	static UINT  iHighLightItem;

	switch (message) {
		case WM_INITDIALOG:
			if ( rndname && rndname[0] == '*' ) {
				char s[MAX_PATH*2+60];

				wsprintf( s, "%sを処理中です...　　　　　※[Shift]+[Esc]キーで中断します", rscname );
				SendMessage( GetParent(hDlg), R7MW_STATUS_BAR, 0, (LPARAM)s );
			}
			else {
				SendMessage( GetParent(hDlg), R7MW_STATUS_BAR, 0, (LPARAM)"編集ダイアログを開いています...　　　※[Shift]+[Esc]キーで中断します" );
			}

			SetWindowText(hDlg, g_item); // タイトルバーを設定
			helpdoc_start( g_item, 0 );
			abend_flag = 0;
			if ( 0 != rsc( hDlg, rscname, NULL, 0, NULL ) ) {
				// 正常終了でなければそのままサヨナラ
				abend_flag = 1; // 異常終了した場合は、ダイアログのサイズ等を保存しない
				EndDialog(hDlg, -1);
			}
			else { // 画面中央にダイアログを移動させる
				RECT rct;

				GetWindowRect(hDlg, &rct);	// 現在のダイアログの位置を得て、サイズを計算する
				// 現在ダイアログが置かれているモニタが基準
				HMONITOR hMon = MonitorFromRect(&rct, MONITOR_DEFAULTTONEAREST);
				MONITORINFO mi;
				mi.cbSize = sizeof(MONITORINFO);
				GetMonitorInfo(hMon, &mi);

				// 幅に計算し直し
				rct.right  -= rct.left;
				rct.bottom -= rct.top;

				// 新しい中央サイズを計算
				rct.left = mi.rcMonitor.left + (mi.rcMonitor.right  - mi.rcMonitor.left - rct.right ) / 2;
				rct.top  = mi.rcMonitor.top  + (mi.rcMonitor.bottom - mi.rcMonitor.top  - rct.bottom) / 2;

				ddTop    = rct.top;
				ddHeight = rct.bottom;
				ddLeft   = rct.left;
				ddWidth  = rct.right;

				// レジストリから、このダイアログのサイズを取得して、適用
				if ( rnsf7RegRead("NoSaveSettings", 0) == 0 ) {
					char fname[_MAX_FNAME*2], rscid[_MAX_FNAME*2+10];
					REG reg;

					if ( reg.OpenKey( HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_READONLY)) {
						_splitpath_s( rscname, NULL, 0, NULL, 0, fname, _countof(fname), NULL, 0 );
						lstrcpy(rscid, fname);
						lstrcat(rscid, ".dTop");
						rct.top = reg.ReadInteger(rscid, rct.top);

						lstrcpy(rscid, fname);
						lstrcat(rscid, ".dLeft");
						rct.left = reg.ReadInteger(rscid, rct.left);

						lstrcpy(rscid, fname);
						lstrcat(rscid, ".dHeight");
						rct.bottom = reg.ReadInteger(rscid, rct.bottom);
						if ( rct.bottom < ddHeight/5 ) {
							rct.bottom = ddHeight/5;
						}

						lstrcpy(rscid, fname);
						lstrcat(rscid, ".dWidth");
						rct.right = reg.ReadInteger(rscid, rct.right);

						reg.CloseKey();
					}
				}

				POINT pt = {rct.left, rct.top};
				if (NULL != MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
					// これから移動しようとするウィンドウの左上座標が画面内なら、そこへ移動
					MoveWindow(hDlg, rct.left, rct.top, rct.right, rct.bottom, TRUE);
				}
				else {
					// これから移動しようとするウィンドウの左上座標が画面外なら、改めて画面中央へ移動
					GetWindowRect(hDlg, &rct);	// 現在のダイアログの位置を得て、サイズを計算する

					int x  = mi.rcMonitor.left + (mi.rcMonitor.right - mi.rcMonitor.left) / 2 - (rct.right - rct.left) / 2;
					int y = mi.rcMonitor.top + (mi.rcMonitor.bottom - mi.rcMonitor.top) / 2 - (rct.bottom - rct.top) / 2;

					SetWindowPos(hDlg, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
				}

				vsb = 0;

				if ( strcmp( helpfile, "none" ) == 0 ) { // ヘルプがなければボタンを無効化
                    stdButtonStatus( hDlg, IDHELP, FALSE );
				}

				helpdoc_end();

				hLVMenu = LoadMenu(hInst,(LPSTR)IDR_HBRMENU);
				hCBLMenu = LoadMenu(hInst,(LPSTR)IDR_CONTEXT_CBLMENU);
				SendMessage( GetParent(hDlg), R7MW_STATUS_BAR, 0, (LPARAM)"" );

				// システムメニューに [OK],[適用],[戻す],[ヘルプ]] を追加する
				{
					HMENU hSysMenu;

					// 挿入によりメニューを加えていくので、逆順に追加していくことになる
					hSysMenu = GetSystemMenu( hDlg, FALSE );
					InsertMenu( hSysMenu, 0, MF_SEPARATOR | MF_BYPOSITION, 0      , NULL );
					InsertMenu( hSysMenu, 0, MF_STRING    | MF_BYPOSITION, IDC_AKEY, "アクセラレータキーの確認(&K)" );
					InsertMenu( hSysMenu, 0, MF_SEPARATOR | MF_BYPOSITION, 0      , NULL );
					InsertMenu( hSysMenu, 0, MF_STRING    | MF_BYPOSITION, IDHELP , "ヘルプ(&H)\tF1" );
					InsertMenu( hSysMenu, 0, MF_STRING    | MF_BYPOSITION, IDLOAD , "読込(&L)" );
					InsertMenu( hSysMenu, 0, MF_STRING    | MF_BYPOSITION, IDSAVE , "保存(&W)" );
					InsertMenu( hSysMenu, 0, MF_STRING    | MF_BYPOSITION, IDRETRY, "戻す(&R)" );
					InsertMenu( hSysMenu, 0, MF_STRING    | MF_BYPOSITION, IDYES  , "適用(&A)" );
					InsertMenu( hSysMenu, 0, MF_STRING    | MF_BYPOSITION, IDOK   , "&OK" );
					DrawMenuBar( hDlg );
				}

				// ダイアログ状態定義関連
				if ( rndname ) {
					if ( rndname[0] == '*' ) { // rndへの書き込み
						saveDialogStatus(hDlg, rndname+1);
						EndDialog( hDlg, 2 );
					}
					else if ( rndname[0] == ':' ) { // rndの読み込み+OKボタン押下
						loadDialogStatus(hDlg, rndname+1);
						okApply(hDlg, IDOK);
						EndDialog( hDlg, 2 );
					}
					else { // rndの読み込み
						loadDialogStatus(hDlg, rndname);
					}
				}

				// フォーカスの設定
				hHighLightCtrl = NULL;
				// ハイライトアイテムの指定は初期化
				iHighLightItem = ERRORVALUE;

				if (MainEditDlgProc_Focus) {
					UINT i;

					if ( ERRORVALUE != (i = searchID(MainEditDlgProc_Focus)) ) {
						// MainEditDlgProc_Focus で示されるコントロールが存在した場合
						hHighLightCtrl = rctrls[i].hCtrl;

						// コントロールの左側に、矢印アイコンを作成
						RECT rt;
						POINT pt;
						char snum[13];
						GetWindowRect(hHighLightCtrl, &rt);
						pt.x = rt.left; pt.y = rt.top;
						ScreenToClient(hDlg, &pt);
						wsprintf(snum, "#%d", IDI_FOCUS); // SS_ICONを初期(CreateWindow時)に与えるには、"#num" でリソース番号を与える
						CreateWindow( WC_STATIC, snum, WS_CHILD|WS_VISIBLE|SS_ICON|SS_REALSIZEIMAGE,
							0, pt.y, 0, 0, hDlg, (HMENU)IDC_STATIC, hInst, NULL);

						// ハイライト用の色を設定する
						cHighLightColor = rnsf7RegRead("RkyHighLightColor", DEFAULT_HIGHLIGHTCOLOR);

						switch (rctrls[i].ctrl) {
							case ICON_BUTTON:
							case LVC_BUTTON_NEW:
							case LVC_BUTTON_EDIT:
							case LVC_BUTTON_DEL:
							case REF_BUTTON_FILE:
							case REF_BUTTON_FOLDER:
							case GOTO_BUTTON:
							case TODEFAULT_BUTTON:
							case COLOR_BUTTON:
							case MIN_GOTO_BUTTON:
								/* - あんまりよいアイディアではなかったので、とりあえずやめておく
								// ボタンの背景色は設定できないため、
								// フラットスタイルにすることで他との区別をつける
								SetWindowLongPtr(hHighLightCtrl, GWL_STYLE, GetWindowLongPtr(hHighLightCtrl, GWL_STYLE) | BS_FLAT);
								*/
								// ハイライト用ブラシは作らない
								hHighLightBrush = NULL;
								break;

							default:
								// コントロールの背景色を目立たせるためのブラシを準備
								hHighLightBrush = CreateSolidBrush(cHighLightColor); // ハイライト用ブラシの作成
								break;
						}

						if ( rctrls[i].ctrl == LIST_CTRL ) {
							// チェックボックスの場合は、リストビューの背景色設定を行う
							ListView_SetBkColor(hHighLightCtrl, cHighLightColor);
							ListView_SetTextBkColor(hHighLightCtrl, cHighLightColor);
						}
						else if (rctrls[i].ctrl == CHECK_CTRL && rctrls[i].i != ERRORVALUE) {
							// チェックボックスリストの場合は、リストビューの背景色は薄める
							UINT c1, c2, c3;

							c1 = (cHighLightColor >> 16) & 0xff;
							c2 = (cHighLightColor >>  8) & 0xff;
							c3 =  cHighLightColor        & 0xff;

							c1 += ((0xff - c1) * 2 / 3);
							c2 += ((0xff - c2) * 2 / 3);
							c3 += ((0xff - c3) * 2 / 3);

							COLORREF cLightHighLight = (c1 << 16) | (c2 << 8) | c3;

							ListView_SetBkColor(hHighLightCtrl, cLightHighLight);
							ListView_SetTextBkColor(hHighLightCtrl, cLightHighLight);
							iHighLightItem = rctrls[i].i;
						}

						// 初期フォーカスを当てる
						SetFocus(hHighLightCtrl);

						// さらに、R-Scriptにフォーカスを当てさせる（チェックボックスリストの可能性があるので）
						SetFocusControl(MainEditDlgProc_Focus);
					}
					else {
						MessageBox(hDlg, "指定された項目は、現在の環境では利用できません。", "キーワード検索", MB_OK | MB_ICONWARNING);
					}
				}
			}
			// キーボードフォーカスの設定を行っている場合があり、FALSEを返す必要がある。
			// (スクリプト中でのSetFocusの使用 or MainEditDlgProc_FocusによるフォーカスID指定)
			return 0;

		//case WM_CTLCOLORBTN:	// ←WM_CTLCOLORBTNの使用は、全く意味がないらしい(MSDNヘルプ参照)
		case WM_CTLCOLOREDIT:
		case WM_CTLCOLORSTATIC:
			// 初期フォーカスの当たったコントロールだった場合は、ハイライト用ブラシで背景描画
			if ((HWND)lParam == hHighLightCtrl) {
				SetBkColor((HDC)wParam, cHighLightColor);
				return (LRESULT)hHighLightBrush;
			}
			else {
				// カラーボタンのサンプル
				for( UINT i=0 ; i<nctrls ; i++ ) {
					if (rctrls[i].ctrl == COLOR_BUTTON && (HWND)lParam == (HWND)rctrls[i].lParam) {
						if (rctrls[i].opt_p1 == 0 || rctrls[i].opt_u1 != rctrls[i].opt_u2) { // ブラシを生成
							if (rctrls[i].opt_p1) DeleteObject(rctrls[i].opt_p1);
							rctrls[i].opt_p1 = CreateSolidBrush(GetColorButton(rctrls[i].id));
						}
						return (LRESULT)rctrls[i].opt_p1;
					}
				}
			}
			return 0;

		case WM_INITMENU:
			// システムメニューの各項目の有効性を確認
			{
				HMENU hSysMenu;

				hSysMenu = GetSystemMenu( hDlg, FALSE );
				EnableMenuItem( hSysMenu, IDOK   , MF_BYCOMMAND | (stdButtonStatus(hDlg, IDOK   , -1) ? MF_ENABLED : MF_GRAYED));
				EnableMenuItem( hSysMenu, IDYES  , MF_BYCOMMAND | (stdButtonStatus(hDlg, IDYES  , -1) ? MF_ENABLED : MF_GRAYED));
				EnableMenuItem( hSysMenu, IDSAVE , MF_BYCOMMAND | (stdButtonStatus(hDlg, IDSAVE , -1) ? MF_ENABLED : MF_GRAYED));
				EnableMenuItem( hSysMenu, IDLOAD , MF_BYCOMMAND | (stdButtonStatus(hDlg, IDLOAD , -1) ? MF_ENABLED : MF_GRAYED));
				EnableMenuItem( hSysMenu, IDRETRY, MF_BYCOMMAND | (stdButtonStatus(hDlg, IDRETRY, -1) ? MF_ENABLED : MF_GRAYED));
				EnableMenuItem( hSysMenu, IDHELP , MF_BYCOMMAND | (stdButtonStatus(hDlg, IDHELP , -1) ? MF_ENABLED : MF_GRAYED));
			}
			return 1;

		case WM_SYSCOMMAND:
			// システムメニューの呼び出し
			switch( LOWORD(wParam) ) {
				case IDOK    :
				case IDYES   :
				case IDSAVE  :
				case IDLOAD  :
				case IDRETRY :
				case IDHELP  :
				case IDC_AKEY: // アクセラレータキーの確認
					return SendMessage( hDlg, WM_COMMAND, LOWORD(wParam), NULL);

				default:
					return 0;
			}

		case WM_SIZE:	// ウインドウのリサイズ完了
			if ( addctrl( hDlg, AC_RESIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) ) {
				// 0以外が返ってきたら、リサイズ後に位置調整する
				vsa = vsb;
				vsb = yctrls - dHeight;

				// バーの位置を更新
				si.cbSize = sizeof( SCROLLINFO );
				si.fMask = SIF_POS;
				si.nPos = vsb;
				SetScrollInfo(hDlg, SB_VERT, &si ,FALSE );
				movectrl(hDlg, vsa, vsb);
			}
			return 1;

		case WM_GETMINMAXINFO:	// ウィンドウのリサイズ幅・高さを制限
			{
				MINMAXINFO *pmmi = (MINMAXINFO *)lParam;
				if ( pmmi ) {
					// 編集ダイアログの幅の最小は固定（スクロールバーの有無で若干変化）、最大は指定しない
					pmmi->ptMinTrackSize.x = gDialogWidthMin;
					//pmmi->ptMaxTrackSize.x = 

					// 編集ダイアログの高さの最小は指定しない。最大は、ダイアログの最大長
					//pmmi->ptMinTrackSize.y =
					pmmi->ptMaxTrackSize.y = yctrls;
				}
				return 1;
			}

		case WM_NOTIFY:
			{ /* (HWND)lParam が、各コントロールのハンドル */
				LPNMHDR pnmh = (LPNMHDR)lParam;

				for( UINT i=0 ; i<nctrls ; i++ ) {
					if (rctrls[i].hCtrl == pnmh->hwndFrom) {
						if (rctrls[i].ctrl == LIST_CTRL) { // リストビューへのnotify
							switch(pnmh->code) {
								case NM_DBLCLK : // ダブルクリックだったら
									if ( GetLVCursor( rctrls[i].hCtrl ) >= 0 ) {

										if ( 2 != galabel( hDlg, rscname, rctrls[i].id, "_EDIT" ) ) {
											bool rv = 0;
											if ( rctrls[i].opt == 1 ) {
												rv = listview_n_edit( hDlg, i );
											}
											else {
												rv = listview_edit( hDlg, i );
											}

											if ( rv ) {
												ctrlChange( hDlg, i );
											}
										}
									}
									return 1;

								case LVN_COLUMNCLICK: // ヘッダがクリックされたら
									{// ComCtl ver.4.70より前で、NMLISTVIEW が正しく使えるのかどうかは…不明
										UINT i, m;
										LVITEM lvitem;

										m = ListView_GetItemCount( pnmh->hwndFrom );
										// まずは lParam に並べ替えに使う文字列を入れる
										lvitem.mask = LVIF_PARAM;
										lvitem.iSubItem = 0;
										for(i=0 ; i<m ; i++) {
											lvitem.iItem = i;
											lvitem.lParam = (LPARAM)lvGetItemString( pnmh->hwndFrom, i, ((LPNMLISTVIEW)lParam)->iSubItem );
											ListView_SetItem(pnmh->hwndFrom, &lvitem);
										}
										// 昇順でソート
										// (今後、ソート方法に工夫を加えるときは、 第3引数に変化を加えることにする
										ListView_SortItems(pnmh->hwndFrom, (PFNLVCOMPARE)listview_pfnCompare, 0);

										// 並べ替えに使った文字列を解放
										for(i=0 ; i<m ; i++) {
											lvitem.iItem = i;
											ListView_GetItem(pnmh->hwndFrom, &lvitem);

											if ( lvitem.lParam ) {
												free( (void *)lvitem.lParam );
											}
										}
									}
									return 1;
							}
						}
						else if (rctrls[i].ctrl == CHKLIST_CTRL) { // チェックボックスリストへのnotify
							switch(pnmh->code) {
								case LVN_KEYDOWN: // スペースキーを押下
									if ( ((LPNMLVKEYDOWN)lParam)->wVKey != VK_SPACE ) {
										break;
									}
									// そのまま下へ
								case NM_DBLCLK: // ダブルクリックだったら
								case NM_CLICK: // クリックだったら
									{ // (コンテキストメニューのところにも、同じ記載があります)
										int  cur = GetLVCursor( rctrls[i].hCtrl );
										UINT ci;

										if ( cur != -1 ) {
											// チェック状態の反転（ チェック [→半チェック] → チェック無し → … ）
											if ( ListCheck_Change( rctrls[i].hCtrl, cur, -1 ) ) {
												// リストビュー内のcur番目のデータが、何番目のコントロールなのかを調べ、
												// ciへ代入する
												for(ci=0 ; ci<nctrls ; ci++) {
													if ( rctrls[i].hCtrl == rctrls[ci].hCtrl && rctrls[ci].i == cur ) {
														break;
													}
												}

												// ListCheck_Change内ではctrlChangeを発行しないので、自力で発行する
												ctrlChange(hDlg, ci);
											}
										}
										return 1;
									}

								case NM_CUSTOMDRAW: // カスタムドロー
									if (rctrls[i].hCtrl == hHighLightCtrl && iHighLightItem != ERRORVALUE) {
										LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;

										if (lplvcd->nmcd.dwDrawStage == CDDS_PREPAINT) {
											// ダイアログボックスは素直に値を返せないので、
											// DWLP_MSGRESULT を経由して値を返す。returnにはTRUEを与える。
											SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_NOTIFYITEMDRAW);
											return TRUE;
										}
										else if (lplvcd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) {
											if (lplvcd->nmcd.dwItemSpec == iHighLightItem) {
												lplvcd->clrTextBk = cHighLightColor;
												SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_DODEFAULT);
												return TRUE;
											}
										}
									}
							}
						}
						break;
					}
				}
				return 0;
			}

		case OSP_CTRL_KEYDOWN: // コントロール内でキーを押されたら(アプリ定義 = WM_APP+1)
			// wParam: 仮想キーコード
			// lParam: LPOSPCKDP
			// もし、何らかの理由でCallWindowProcを呼ばせたいのであれば、OSP_CTRL_KEYDOWNは1を返す
			switch((int)wParam) {
				case VK_DELETE:
				case VK_INSERT:
				case VK_SPACE:
					if (((LPOSPCKDP)lParam)->ctrl == LIST_CTRL) { // リストビューへのnotify
						bool rv = 0;

						if ((int)wParam == VK_INSERT) {
							if ( 2 != galabel( hDlg, rscname, rctrls[((LPOSPCKDP)lParam)->iCtrl].id, "_NEW" ) ) {
								if ( rctrls[((LPOSPCKDP)lParam)->iCtrl].opt == 1 ) {
									rv = listview_n_new( hDlg, ((LPOSPCKDP)lParam)->iCtrl );
								}
								else {
									rv = listview_new( hDlg, ((LPOSPCKDP)lParam)->iCtrl );
								}
							}
						}
						else if ( GetLVCursor( ((LPOSPCKDP)lParam)->hCtrl ) >= 0 ) { // 1つ以上の選択がある
							if ((int)wParam ==  VK_DELETE) {
								if ( 2 != galabel( hDlg, rscname, rctrls[((LPOSPCKDP)lParam)->iCtrl].id, "_DELETE" ) ) {
									if ( rctrls[((LPOSPCKDP)lParam)->iCtrl].opt == 1 ) {
										rv = listview_n_delete( hDlg, ((LPOSPCKDP)lParam)->iCtrl );
									}
									else {
										rv = listview_delete( hDlg, ((LPOSPCKDP)lParam)->iCtrl );
									}
								}
							}
							else if ((int)wParam ==  VK_SPACE) {
								if ( 2 != galabel( hDlg, rscname, rctrls[((LPOSPCKDP)lParam)->iCtrl].id, "_EDIT" ) ) {
									if ( rctrls[((LPOSPCKDP)lParam)->iCtrl].opt == 1 ) {
										rv = listview_n_edit( hDlg, ((LPOSPCKDP)lParam)->iCtrl );
									}
									else {
										rv = listview_edit( hDlg, ((LPOSPCKDP)lParam)->iCtrl );
									}
								}
							}
						}

						if ( rv ) {
							ctrlChange(hDlg, ((LPOSPCKDP)lParam)->iCtrl);
						}
						((LPOSPCKDP)lParam)->retval = 0; // デフォルトのプロシージャを呼ばない
						return 0;
					}
					break;

				case VK_PRIOR:
				case VK_NEXT:
					// もし現在、複数行エディットボックスか、リストビューコントロールにフォーカスが
					// 当たっているのであれば、スクロールはコントロールに対して行う。
					if (((LPOSPCKDP)lParam)->ctrl == LIST_CTRL || ((LPOSPCKDP)lParam)->ctrl == MULTI_EDIT) { // リストビューまたは複数行編集ボックス
						SCROLLINFO si;

						ZeroMemory(&si, sizeof(SCROLLINFO));
						si.cbSize = sizeof(SCROLLINFO);
						si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
						GetScrollInfo(((LPOSPCKDP)lParam)->hCtrl, SB_VERT, &si);

						if ( ((int)wParam == VK_PRIOR && si.nPos > si.nMin) || ((int)wParam == VK_NEXT && si.nPos < si.nMax - (int)si.nPage) ) {
							((LPOSPCKDP)lParam)->retval = 1; // デフォルトのプロシージャを呼ぶ
							return 0;
						}
					}
					// そうでなければ、ダイアログに対してスクロールを行う。
					vsa = vsb;

					if ( (int)wParam ==  VK_PRIOR ) { // PageUp
						if (vsb > dHeight) {
							vsb -= dHeight;
						}
						else {
							vsb = 0;
						}
					}
					else if ((int)wParam ==  VK_NEXT) { // PageDown
						if (vsb+2*dHeight < yctrls) {
							vsb += dHeight;
						}
						else {
							vsb = yctrls - dHeight;
						}
					}

					// バーの位置を更新
					si.cbSize = sizeof( SCROLLINFO );
					si.fMask = SIF_POS;
					si.nPos = vsb;
					SetScrollInfo(hDlg, SB_VERT, &si ,TRUE );
					movectrl(hDlg, vsa, vsb);

					((LPOSPCKDP)lParam)->retval = 0; // デフォルトのプロシージャを呼ばない
					return 0;

				case 'W':
					if (GetAsyncKeyState(VK_CONTROL) < 0) {
						// Ctrl+W : 閉じる
						SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDCANCEL, 0), 0);
						((LPOSPCKDP)lParam)->retval = 0; // デフォルトのプロシージャを呼ばない
						return 0;
					}
					break;
			}
			((LPOSPCKDP)lParam)->retval = 1; // デフォルトのプロシージャを呼ぶ
			return 1;

		case OSP_CTRL_CHAR: // コントロール内でキーを押されたら、文字コードを受け取る(アプリ定義 = WM_APP+5)
			// wParam: 文字コード
			// lParam: LPOSPCKDP
			switch((int)wParam) {
				case CTRL_A: // すべて選択
					if (((LPOSPCKDP)lParam)->ctrl == LIST_CTRL) { // リストビューへのnotify
						if ( (rctrls[((LPOSPCKDP)lParam)->iCtrl].flag_opt & LV_MULTISELECT) && ListView_GetItemCount(((LPOSPCKDP)lParam)->hCtrl) > 0 ) { // 複数選択可で、かつ、項目が1つ以上存在する
							int n = ListView_GetItemCount(((LPOSPCKDP)lParam)->hCtrl);

							for(int j=0 ; j<n ; j++) {
								ListView_SetItemState(((LPOSPCKDP)lParam)->hCtrl, j, LVIS_SELECTED, LVIS_SELECTED );
							}
							((LPOSPCKDP)lParam)->retval = 0; // デフォルトのプロシージャを呼ばない
							return 0;
						}
					}
					else if (((LPOSPCKDP)lParam)->ctrl == EDIT_CTRL || ((LPOSPCKDP)lParam)->ctrl == MULTI_EDIT) {
						SendMessage( ((LPOSPCKDP)lParam)->hCtrl, EM_SETSEL, (WPARAM)0, (LPARAM)-1 );  
						((LPOSPCKDP)lParam)->retval = 0; // デフォルトのプロシージャを呼ばない
						return 0;
					}
					break;
			}
			((LPOSPCKDP)lParam)->retval = 1; // デフォルトのプロシージャを呼ぶ
			return 0;

		case WM_CONTEXTMENU: // 右クリックメニュー
			{
				for( UINT i=0 ; i<nctrls ; i++ ) {
					if (rctrls[i].hCtrl == (HWND)wParam) {
						if ( rctrls[i].ctrl == LIST_CTRL ) { // リストビュー
							HMENU hMenu;
							POINT pt;
							bool  rv = 0;

							GetCursorPos(&pt);
							hMenu = GetSubMenu(hLVMenu, 0);
							SetMenuDefaultItem(hMenu, IDC_BUTTON_EDIT, FALSE);

							if ( -1 != GetLVCursor( rctrls[i].hCtrl ) ) {
								EnableMenuItem(hMenu, IDC_BUTTON_EDIT, MF_ENABLED | MF_BYCOMMAND );
								EnableMenuItem(hMenu, IDC_BUTTON_DELETE, MF_ENABLED | MF_BYCOMMAND );
							}
							else { // 選択項目がない
								EnableMenuItem(hMenu, IDC_BUTTON_EDIT, MF_GRAYED | MF_BYCOMMAND );
								EnableMenuItem(hMenu, IDC_BUTTON_DELETE, MF_GRAYED | MF_BYCOMMAND );
								EnableMenuItem(hMenu, IDM_ALLSELECT, MF_GRAYED | MF_BYCOMMAND );
								EnableMenuItem(hMenu, IDM_SELECTCHANGE, MF_GRAYED | MF_BYCOMMAND );
							}

							if ( (rctrls[i].flag_opt & LV_MULTISELECT) && ListView_GetItemCount(rctrls[i].hCtrl) > 0 ) { // 複数選択可で、かつ、項目が1つ以上存在する
								EnableMenuItem(hMenu, IDM_ALLSELECT, MF_ENABLED | MF_BYCOMMAND );
								EnableMenuItem(hMenu, IDM_SELECTCHANGE, MF_ENABLED | MF_BYCOMMAND );
							}
							else {
								EnableMenuItem(hMenu, IDM_ALLSELECT, MF_GRAYED | MF_BYCOMMAND );
								EnableMenuItem(hMenu, IDM_SELECTCHANGE, MF_GRAYED | MF_BYCOMMAND );
							}

							switch( (int)TrackPopupMenuEx(hMenu, TPM_RETURNCMD, pt.x, pt.y, hDlg, NULL) ) {
								case IDC_BUTTON_EDIT: // 選択中のアイテムを編集
									if ( 2 != galabel( hDlg, rscname, rctrls[i].id, "_EDIT" ) ) {
										if ( rctrls[i].opt == 1 ) { // キーリストまたは一般のリストビュー
											rv = listview_n_edit( hDlg, i );
										}
										else { // 値リスト
											rv = listview_edit( hDlg, i );
										}
									}
									break;

								case IDC_BUTTON_DELETE: // 選択中のアイテムを削除
									if ( 2 != galabel( hDlg, rscname, rctrls[i].id, "_DELETE" ) ) {
										if ( rctrls[i].opt == 1 ) { // キーリストまたは一般のリストビュー
											rv = listview_n_delete( hDlg, i );
										}
										else { // 値リスト
											rv = listview_delete( hDlg, i );
										}
									}
									break;

								case IDC_BUTTON_NEW: // アイテムの新規作成
									if ( 2 != galabel( hDlg, rscname, rctrls[i].id, "_NEW" ) ) {
										if ( rctrls[i].opt == 1 ) { // キーリストまたは一般のリストビュー
											rv = listview_n_new( hDlg, i );
										}
										else { // 値リスト
											rv = listview_new( hDlg, i );
										}
									}
									break;

								case IDM_ALLSELECT: // 全てのアイテムを選択
									{
										int n = ListView_GetItemCount(rctrls[i].hCtrl);

										for(int j=0 ; j<n ; j++) {
											ListView_SetItemState(rctrls[i].hCtrl, j, LVIS_SELECTED, LVIS_SELECTED );
										}
									}
									break;

								case IDM_SELECTCHANGE: // アイテムの選択状態を切り替え
									{
										int n = ListView_GetItemCount(rctrls[i].hCtrl);

										for(int j=0 ; j<n ; j++) {
											if ( LVIS_SELECTED == ListView_GetItemState(rctrls[i].hCtrl, j, LVIS_SELECTED ) ) {
												ListView_SetItemState(rctrls[i].hCtrl, j, 0, LVIS_SELECTED );
											}
											else {
												ListView_SetItemState(rctrls[i].hCtrl, j, LVIS_SELECTED, LVIS_SELECTED );
											}
										}
									}
									break;

								case IDM_CSV_SAVE:	// CSVファイルへ出力
									lvSaveToCsv(hDlg, rctrls[i].hCtrl,
										        rctrls[i].opt != 1 ? 2 : lvCountColumn((const char **)rctrls[i].text));
									break;
								case IDM_CSV_LOAD:	// CSVファイルから入力（上書き）
									if (rctrls[i].id[0] == ':') {
										if (IDCANCEL == MessageBox(hDlg, "現在、表示されているデータは消去され、CSVファイルからデータが読みだされます。\r\n"
											                             "この編集項目は、特殊な入力形式を必要としている可能性があります。ファイルからのデータの読み込みを行った場合、データの内容によっては"
																		 "不整合が起きるかもしれません。\r\n"
																		 "続行しますか?", "CSVファイルから入力", MB_OKCANCEL | MB_ICONQUESTION)) {
											break;
										}
									}
									else {
										if (IDCANCEL == MessageBox(hDlg, "現在、表示されているデータは消去され、CSVファイルからデータが読みだされます。\r\n"
																		 "続行しますか?", "CSVファイルから入力", MB_OKCANCEL | MB_ICONQUESTION)) {
											break;
										}
									}
									rv = lvLoadFromCsv(hDlg, rctrls[i].hCtrl,
										        rctrls[i].opt != 1 ? 2 : lvCountColumn((const char **)rctrls[i].text));
									break;
								case IDM_CSV_APPEND: // CSVファイルから追加
									if (rctrls[i].id[0] == ':') {
										if (IDCANCEL == MessageBox(hDlg, "現在、表示されているデータの後ろに、CSVファイルからデータが読みだされ、追加されます。\r\n"
											                             "この編集項目は、特殊な入力形式を必要としている可能性があります。ファイルからのデータの読み込みを行った場合、データの内容によっては"
																		 "不整合が起きるかもしれません。\r\n"
																		 "続行しますか?", "CSVファイルから入力(追加)", MB_OKCANCEL | MB_ICONQUESTION)) {
											break;
										}
									}
									else {
										if (IDCANCEL == MessageBox(hDlg, "現在、表示されているデータの後ろに、CSVファイルからデータが読みだされ、追加されます。\r\n"
																		 "続行しますか?", "CSVファイルから入力(追加)", MB_OKCANCEL | MB_ICONQUESTION)) {
											break;
										}
									}
									rv = lvAppendFromCsv(hDlg, rctrls[i].hCtrl,
										        rctrls[i].opt != 1 ? 2 : lvCountColumn((const char **)rctrls[i].text));
									break;
							}

							if ( rv ) {
								ctrlChange(hDlg, i);
							}

							return 1;
						}
						else if ( rctrls[i].ctrl == CHKLIST_CTRL ) { // チェックボックスリストのコンテキストメニュー
							HMENU hMenu;
							POINT pt;
							int   cur = 0;
							UINT  ci, cj, cn;
							union CBL_FLAG fCbl;

							GetCursorPos(&pt);
							hMenu = GetSubMenu(hCBLMenu, 0);
							SetMenuDefaultItem(hMenu, ID_CBLMENU_CLICK, FALSE);

							cur = GetLVCursor( (HWND)wParam );
							EnableMenuItem(hMenu, ID_CBLMENU_CLICK, MF_GRAYED | MF_BYCOMMAND );
							EnableMenuItem(hMenu, ID_CBLMENU_ALLON, MF_GRAYED | MF_BYCOMMAND );
							EnableMenuItem(hMenu, ID_CBLMENU_ALLOFF, MF_GRAYED | MF_BYCOMMAND );
							EnableMenuItem(hMenu, ID_CBLMENU_ALLNODET, MF_GRAYED | MF_BYCOMMAND );

							// 選択中の項目がチェックボックスならば、「チェック状態の変更」を有効
							if ( -1 != cur ) {
								fCbl.u = lvGetLParam( (HWND)wParam, cur );
								if ( fCbl.b.disable == TRUE && (fCbl.b.type == 2 || fCbl.b.type == 3) ) {
									EnableMenuItem(hMenu, ID_CBLMENU_CLICK, MF_ENABLED | MF_BYCOMMAND );
								}
							}

							cn = ListView_GetItemCount( (HWND)wParam );
							// 半チェックになれる項目が1つ以上あれば、「すべて半チェック」を有効
							for(cj=0 ; cj<cn ; cj++) {
								fCbl.u = lvGetLParam( (HWND)wParam, cj );
								if ( fCbl.b.disable == TRUE && fCbl.b.type == 3 && fCbl.b.state != 2 ) {
									EnableMenuItem(hMenu, ID_CBLMENU_ALLNODET, MF_ENABLED | MF_BYCOMMAND );
									break;
								}
							}

							// チェック状態になっていない項目が1つ以上あれば、「すべてチェック」を有効
							for(cj=0 ; cj<cn ; cj++) {
								fCbl.u = lvGetLParam( (HWND)wParam, cj );
								if ( fCbl.b.disable == TRUE && (fCbl.b.type == 2 || fCbl.b.type == 3) && fCbl.b.state != 1 ) {
									EnableMenuItem(hMenu, ID_CBLMENU_ALLON, MF_ENABLED | MF_BYCOMMAND );
									break;
								}
							}

							// チェック状態になっていない項目が1つ以上あれば、「すべてチェック解除」を有効
							for(cj=0 ; cj<cn ; cj++) {
								fCbl.u = lvGetLParam( (HWND)wParam, cj );
								if ( fCbl.b.disable == TRUE && (fCbl.b.type == 2 || fCbl.b.type == 3) && fCbl.b.state != 0 ) {
									EnableMenuItem(hMenu, ID_CBLMENU_ALLOFF, MF_ENABLED | MF_BYCOMMAND );
									break;
								}
							}

							switch( (int)TrackPopupMenuEx(hMenu, TPM_RETURNCMD, pt.x, pt.y, hDlg, NULL) ) {
								case ID_CBLMENU_CLICK: // 選択状態の反転
									// チェック状態の反転（ チェック [→半チェック] → チェック無し → … ）
									if ( ListCheck_Change( (HWND)wParam, cur, -1 ) ) {
										// リストビュー内のcur番目のデータが、何番目のコントロールなのかを調べ、
										// ciへ代入する
										for(ci=0 ; ci<nctrls ; ci++) {
											if ( (HWND)wParam == rctrls[ci].hCtrl && rctrls[ci].i == cur ) {
												break;
											}
										}

										// ListCheck_Change内ではctrlChangeを発行しないので、自力で発行する
										ctrlChange(hDlg, ci);
									}
									break;

								case ID_CBLMENU_ALLON: // すべてチェック状態にする
									for(cj=0 ; cj<cn ; cj++) {
										fCbl.u = lvGetLParam( (HWND)wParam, cj );
										if ( fCbl.b.disable == TRUE && (fCbl.b.type == 2 || fCbl.b.type == 3) ) {
											if ( ListCheck_Change( (HWND)wParam, cj, 1 ) ) {
												// リストビュー内のcj番目のデータが、何番目のコントロールなのかを調べ、
												// ciへ代入する
												for(ci=0 ; ci<nctrls ; ci++) {
													if ( (HWND)wParam == rctrls[ci].hCtrl && rctrls[ci].i == cj ) {
														break;
													}
												}

												// ListCheck_Change内ではctrlChangeを発行しないので、自力で発行する
												ctrlChange(hDlg, ci);
											}
										}
									}
									break;

								case ID_CBLMENU_ALLOFF: // すべてチェック解除する
									for(cj=0 ; cj<cn ; cj++) {
										fCbl.u = lvGetLParam( (HWND)wParam, cj );
										if ( fCbl.b.disable == TRUE && (fCbl.b.type == 2 || fCbl.b.type == 3) ) {
											if ( ListCheck_Change( (HWND)wParam, cj, 0 ) ) {
												// リストビュー内のcj番目のデータが、何番目のコントロールなのかを調べ、
												// ciへ代入する
												for(ci=0 ; ci<nctrls ; ci++) {
													if ( (HWND)wParam == rctrls[ci].hCtrl && rctrls[ci].i == cj ) {
														break;
													}
												}

												// ListCheck_Change内ではctrlChangeを発行しないので、自力で発行する
												ctrlChange(hDlg, ci);
											}
										}
									}
									break;

								case ID_CBLMENU_ALLNODET: // 可能なものを全て半チェックにする
									for(cj=0 ; cj<cn ; cj++) {
										fCbl.u = lvGetLParam( (HWND)wParam, cj );
										if ( fCbl.b.disable == TRUE && fCbl.b.type == 3 ) {
											if ( ListCheck_Change( (HWND)wParam, cj, 2 ) ) {
												// リストビュー内のcj番目のデータが、何番目のコントロールなのかを調べ、
												// ciへ代入する
												for(ci=0 ; ci<nctrls ; ci++) {
													if ( (HWND)wParam == rctrls[ci].hCtrl && rctrls[ci].i == cj ) {
														break;
													}
												}

												// ListCheck_Change内ではctrlChangeを発行しないので、自力で発行する
												ctrlChange(hDlg, ci);
											}
										}
									}
									break;
							}

                            return 1;
						}
						break;
					}
				}

				{
					// 特にメニューを出すところではないところで右クリックが押された場合はシステムメニューを表示する
					POINT  pt;

					GetCursorPos( &pt );
					//  システムメニューの表示
					PostMessage( hDlg, 0x0313, 0, MAKELPARAM( pt.x, pt.y )); // 0x0313 : 未定義の値だがシステムメニューが表示される
				}
				return 1;
			}

		case OSP_CTRL_GETFOCUS: //子ウィンドウがフォーカスを受け取った(= WM_APP : アプリ定義メッセージ)
			// (int)wParam : 子ウィンドウのy座標
			// (int)lParam : 子ウィンドウの高さ
			vsa = vsb;
			if ( (int)wParam < 0 ) { // 子ウィンドウのy座標が0より小さかったら
				vsb += (int)wParam;
			}
			else { // 子ウィンドウのy座標が親ウィンドウの高さよりも大きかったら
				RECT rt;

				GetClientRect( hDlg, &rt );
				if ( rt.bottom < (int)wParam + (int)lParam ) {
					vsb += ((int)wParam + (int)lParam - rt.bottom);
				}
			}

			if ( vsa != vsb ) {
				// バーの位置を更新
				si.cbSize = sizeof( SCROLLINFO );
				si.fMask = SIF_POS;
				si.nPos = vsb;
				SetScrollInfo(hDlg, SB_VERT, &si ,TRUE );
				movectrl(hDlg, vsa, vsb);
			}
			return 1;

		case WM_VSCROLL:
			if ( (HWND)lParam == NULL ) { // 垂直スクロールバー
				if ( GetUpdateRect(hDlg, NULL, FALSE) == 0 ) {
					vsa = vsb;
					switch( LOWORD(wParam) ) {
						case SB_LINEDOWN:
							if (vsb+10 < yctrls - dHeight)
								vsb += 10;
							else
								vsb = yctrls - dHeight;
							break;

						case SB_LINEUP:
							if (vsb > 10)
								vsb -= 10;
							else
								vsb = 0;
							break;

						case SB_THUMBTRACK: // スクロールしながら表示するかどうか
							if ( 2 == rnsf7RegRead("NoQuickScrool", 0) )
								return 1;
							// そのまま下へ
						case SB_THUMBPOSITION: // スクロールを終えたときに表示する
							vsb = HIWORD( wParam );
							break;

						case SB_PAGEDOWN:
							if (vsb+2*dHeight < yctrls)
								vsb += dHeight;
							else
								vsb = yctrls - dHeight;
							break;

						case SB_PAGEUP:
							if (vsb > dHeight)
								vsb -= dHeight;
							else
								vsb = 0;
							break;

						default:
							return 0;
					}

					// バーの位置を更新
					si.cbSize = sizeof( SCROLLINFO );
					si.fMask = SIF_POS;
					si.nPos = vsb;
					SetScrollInfo(hDlg, SB_VERT, &si ,TRUE );
					movectrl(hDlg, vsa, vsb);
				}
				return 1;
			}
			else { // スピンコントロール
				UINT i;
				char s[13];

				for( i=0 ; i<nctrls ; i++ ) {
					if (rctrls[i].hCtrl == (HWND)lParam) {
						if (rctrls[i].ctrl == SPIN_CTRL) {
							UINT x;

							GetWindowText(rctrls[i-1].hCtrl, s, 12);
							x = atou( s );
							if (rctrls[i].byte_min < rctrls[i].bit_max) { // 最大・最小値の設定が通常だった場合
								// Shift, Ctrlが押されていたら、増減値調整
								if ( GetAsyncKeyState(VK_SHIFT)&0x8000 ) {
									x -= ((5000 - HIWORD(wParam)) * rctrls[i].opt_u2);
								}
								else if ( GetAsyncKeyState(VK_CONTROL)&0x8000 ) {
									if ( HIWORD(wParam) < 5000 ) {
										x = rctrls[i].byte_min;
									}
									else if ( HIWORD(wParam) > 5000 ) {
										x = rctrls[i].bit_max;
									}
								}
								else {
                                    x -= ((5000 - HIWORD(wParam)) * rctrls[i].opt_u1);
								}

								// 増減値の範囲チェックと代入
								char itbuf[16];
								if (rctrls[i].byte_min <= x && x <= rctrls[i].bit_max) {
									if ( 0 == _ultoa_s(x, itbuf, _countof(itbuf), 10) ) {
										SetWindowText(rctrls[i-1].hCtrl, itbuf );
									}
								}
								else {
									if ( 0 == _ultoa_s(rctrls[i].byte_min, itbuf, _countof(itbuf), 10) ) {
										SetWindowText(rctrls[i-1].hCtrl, itbuf );
									}
								}
							}
							else { // 最大・最小値の設定が逆だった場合 - ▲▼ボタンの効果を逆にする
								// Shift, Ctrlが押されていたら、増減値調整
								if ( GetAsyncKeyState(VK_SHIFT)&0x8000 ) {
									x += ((5000 - HIWORD(wParam)) * rctrls[i].opt_u2);
								}
								else if ( GetAsyncKeyState(VK_CONTROL)&0x8000 ) {
									if ( HIWORD(wParam) < 5000 ) {
										x = rctrls[i].bit_max;
									}
									else if ( HIWORD(wParam) > 5000 ) {
										x = rctrls[i].byte_min;
									}
								}
								else {
                                    x += ((5000 - HIWORD(wParam)) * rctrls[i].opt_u1);
								}

								// 増減値の範囲チェックと代入
								char itbuf[16];
								if (rctrls[i].bit_max <= x && x <= rctrls[i].byte_min) {
									if ( 0 == _ultoa_s(x, itbuf, _countof(itbuf), 10) ) {
										SetWindowText(rctrls[i-1].hCtrl, itbuf );
									}
								}
								else {
									if ( 0 == _ultoa_s(rctrls[i].byte_min, itbuf, _countof(itbuf), 10) ) {
										SetWindowText(rctrls[i-1].hCtrl, itbuf );
									}
								}
							}

							// スピンコントロールのポジションを初期化
							SendMessage(rctrls[i].hCtrl, UDM_SETPOS, 0, (LPARAM) MAKELONG((short) 5000, 0));
							break;
						}
					}
				}

				return 1;
			}

		case 0x020A: // マウスホイール (= WM_MOUSEWHEEL)
			{
				short zDelta = HIWORD(wParam);

				if ( GetUpdateRect(hDlg, NULL, FALSE) == 0 ) {
					vsa = vsb;
					int aLineHeight = SCALEY(32);
					if (zDelta < 0) { // 下に回転
						if (vsb+aLineHeight < yctrls - dHeight)
							vsb += aLineHeight;
						else
							vsb = yctrls - dHeight;
					}
					else { // 上に回転
						if (vsb > aLineHeight)
							vsb -= aLineHeight;
						else
							vsb = 0;
					}

					// バーの位置を更新
					si.cbSize = sizeof( SCROLLINFO );
					si.fMask = SIF_POS;
					si.nPos = vsb;
					SetScrollInfo(hDlg, SB_VERT, &si ,TRUE );
					movectrl(hDlg, vsa, vsb);
				}
				return 1;
			}			
			

		case WM_HELP:
			if ( 2 != rsc( hDlg, rscname, ":HELP", 0, NULL ) ) { // abortでなければ
				callhelp( hDlg, helpfile, contextid );
			}
			return 1;

		case WM_TIMER:
			switch((int)wParam) {
				case R7OSSC_TIMERID:
					// マウススクロールまたはスクロールバーによるスクロール後に呼ばれる
					// ダイアログ上に最前面ウィンドウがあった場合の対策
					KillTimer( hDlg, R7OSSC_TIMERID );
					InvalidateRect( hDlg, NULL, TRUE );
					return 1;

				case EDITDIALOG_TIMERID:
					// SetTimer 関数によるインターバルタイマ(スクリプト処理中は無視される)
					if ( pszEditDialogTimerLabel != NULL &&  pszEditDialogTimerLabel[0] == ':' ) {
						if ( 2 == rsc( hDlg, rscname, pszEditDialogTimerLabel, 0, NULL ) ) {
							// abortが返ってきたら、編集作業は中断
							free_controls( hDlg, 0 );
							EndDialog(hDlg, 2);
						}
					}
					return 1;
			}
			break;

		case WM_DESTROY:
			FreeDllLibrary();
			{ // メニューの破棄
				HMENU hMenu = GetMenu( hDlg );
				if ( hMenu ) {
                    SetMenu( hDlg, NULL );
					DestroyMenu( hMenu );
				}
			}
			DestroyMenu( hLVMenu );
			DestroyMenu( hCBLMenu );
			// このダイアログのサイズを取得して、レジストリへ保存
			if ( abend_flag == 0 ) { // 開始時異常終了していた場合は、ダイアログのサイズを保存しない
				RECT rt;
				char fname[_MAX_FNAME*2], rscid[_MAX_FNAME*2+10];
				REG reg;

				if ( reg.OpenKey( HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_CREATE)) {
					if ( reg.ReadInteger("NoSaveSettings", 0) == 0 ) {
						GetWindowRect(hDlg, &rt);

						_splitpath_s( rscname, NULL, 0, NULL, 0, fname, _countof(fname), NULL, 0 );

						// 編集ダイアログの高さを保存
						lstrcpy(rscid, fname);
						lstrcat(rscid, ".dHeight");
						if ( ddHeight != rt.bottom - rt.top ) {
							reg.WriteInteger(rscid, rt.bottom - rt.top);
						}
						else {
							reg.DeleteValue( rscid );
						}

						// 編集ダイアログの幅を保存
						lstrcpy(rscid, fname);
						lstrcat(rscid, ".dWidth");
						if ( ddWidth != rt.right - rt.left ) {
							reg.WriteInteger(rscid, rt.right - rt.left);
						}
						else {
							reg.DeleteValue( rscid );
						}

						// 編集ダイアログの上座標を保存
						lstrcpy(rscid, fname);
						lstrcat(rscid, ".dTop");
						if ( ddTop != rt.top ) {
							reg.WriteInteger(rscid, rt.top);
						}
						else {
							reg.DeleteValue( rscid );
						}

						// 編集ダイアログの左座標を保存
						lstrcpy(rscid, fname);
						lstrcat(rscid, ".dLeft");
						if ( ddLeft != rt.left ) {
							reg.WriteInteger(rscid, rt.left);
						}
						else {
							reg.DeleteValue( rscid );
						}
					}
					reg.CloseKey();
				}
				else {
					dispErrorMessage(hDlg, MB_OK | MB_ICONSTOP, "レジストリへの書き込み中エラー", "レジストリキー'HKEY_CURRENT_USER\\", RNSF7REGKEY,
									 "'を書き込みアクセスで開こうとしてエラーになりました。\nAPIが報告した原因：", getLastErrorString(tRegGetLastError()), NULL, NULL);
				}
			}
			// ダイアログアイコンの破棄
			{
				HICON hDialogIcon = (HICON)SendMessage(hDlg, WM_GETICON, ICON_SMALL, NULL);
				if ( hDialogIcon ) {
					SendMessage(hDlg, WM_SETICON, ICON_SMALL, NULL);
					DestroyIcon(hDialogIcon);
				}
			}
			// ハイライト用ブラシの削除
			if (hHighLightBrush) {
				DeleteObject(hHighLightBrush);
				hHighLightBrush = NULL;
			}
			// インターバルタイマのとび先ラベル解放
			if (pszEditDialogTimerLabel) {
				safefree(pszEditDialogTimerLabel);
				KillTimer(hDlg, EDITDIALOG_TIMERID);
			}

			return 1;

		case WM_DROPFILES:
			{
				POINT pt;
				RECT rt;

				GetCursorPos( &pt );
				// 子コントロール上でドロップされたものでないかをチェック
				// (子コントロールの中でドロップを扱うのは文字列編集ボックスのみ)
				for(UINT i=0 ; i<nctrls ; i++) {
					if ( (rctrls[i].ctrl == EDIT_CTRL && rctrls[i].opt != 1) || rctrls[i].ctrl == MULTI_EDIT ) {
						GetWindowRect( rctrls[i].hCtrl, &rt );
						if ( rt.left <= pt.x && pt.x <= rt.right && rt.top <= pt.y && pt.y <= rt.bottom ) {
                            return SendMessage( rctrls[i].hCtrl, message, wParam, lParam );
						}
					}
				}

				char rndfname[MAX_PATH*2];

				DragQueryFile((HDROP)wParam, 0, rndfname, _countof(rndfname));
				DragFinish((HDROP)wParam);

				if ( IDYES == MessageBox( hDlg, "現在のダイアログの状態は、ドロップされたファイルから読み出された状態で上書きされます。\nよろしいですか?\n\n※異なる環境で作られたrndファイルを読み込ませないでください。特にOSが異なる（Windows XPで作られたrndファイルをVistaに入れたり、x86版で作られたrndファイルをx64版に入れたりする）場合、レジストリの使われ方が異なることがあり、予期せぬ結果を招くことがあります。", "ダイアログの状態をファイルから読み込む", MB_YESNO | MB_ICONQUESTION ) ) {
					loadDialogStatus( hDlg, rndfname );
				}

				return 1;
			}

		case WM_COMMAND:
			{
				int pushedButton = LOWORD(wParam);

				switch ( pushedButton ) {
					case IDC_AKEY: // アクセラレータキーの確認
						show_chkaccr( hDlg );
						break;

					case IDCANCEL: // ウィンドウ右上の[×]ボタン
						if ( FALSE == stdButtonStatus(hDlg, IDOK, -1) || 0 == rnsf7RegRead("EscSave", 0) ) {
							; // pushedButton = IDCANCEL;
						}
						else {
							switch( MessageBox(hDlg, "現在編集中の内容は、保存・有効化されていません。\n保存・有効化しますか?", "保存の確認", MB_YESNOCANCEL | MB_ICONQUESTION) ) {
								case IDYES:
									pushedButton = IDYES;
									break;

								case IDNO:
									pushedButton = IDCLOSE;
									break;

								case IDCANCEL:
									return 1;
							}                            
						}
						// そのまま下へ
					case IDOK:	// OKボタン
						SetFocus( GetDlgItem(hDlg, LOWORD(wParam)) );
						if ( LOWORD(wParam) == IDOK ) {
							if ( rnsf7RegRead("CheckOK", 1) ) {
								if (IDYES != MessageBox( hDlg, "ここで設定した内容を保存・有効化し、このダイアログを閉じます。\nよろしいですか?", "[OK]の確認", MB_YESNO | MB_ICONQUESTION )) {
									return 1;
								}
							}
							pushedButton = IDYES;
						}
						// そのまま下へ
					case IDYES:	// 適用ボタン
						SetFocus( GetDlgItem(hDlg, LOWORD(wParam)) );
						if ( LOWORD(wParam) == IDYES ) {
							if ( rnsf7RegRead("CheckApply", 1) ) {
								if (IDYES != MessageBox( hDlg, "ここで設定した内容を保存し、有効化します。\nよろしいですか?", "[適用]の確認", MB_YESNO | MB_ICONQUESTION )) {
									return 1;
								}
							}
						}
						if ( pushedButton == IDYES ) {
							// pushdButtonは偽装されることが多いので、wParamから実際に押された
							// ボタンを取得している。
							okApply( hDlg, LOWORD(wParam) == IDYES ? IDYES : IDOK );
							return 1;
						}
						// そのまま下へ
					case IDCLOSE:	// キャンセルボタン
						SetFocus( GetDlgItem(hDlg, LOWORD(wParam)) );
						if ( LOWORD(wParam) == IDCLOSE || pushedButton == IDCANCEL ) {
							if ( stdButtonStatus(hDlg, IDOK, -1) && rnsf7RegRead("CheckESC", 1) ) {
								if (IDYES != MessageBox( hDlg, "ここで設定した内容を保存せず、このダイアログを閉じます。\nよろしいですか?", "[キャンセル]の確認", MB_YESNO | MB_ICONQUESTION ))
									return 1;
							}
						}
						if ( 2 != rsc( hDlg, rscname, ":CANCEL", 0, NULL ) ) {  // スクリプトからAbortが返ってきた場合は終了処理を中断
							free_controls( hDlg, 0 );
							EndDialog(hDlg, 2);
						}
						return 1;

					case IDRETRY:	// 戻す
						SetFocus( GetDlgItem(hDlg, LOWORD(wParam)) );
						if ( rnsf7RegRead("CheckRetry", 1) ) {
							if (IDYES != MessageBox( hDlg, "このダイアログを開いたときの状態に戻します。\nよろしいですか?", "[戻す]の確認", MB_YESNO | MB_ICONQUESTION ))
								return 1;
						}
						if ( 2 != rsc( hDlg, rscname, ":RETRY", 0,NULL ) ) { // abortでなければ
							restore_controls();
							rsc( hDlg, rscname, ":AFTERRETRY", 0, NULL );
							stdButtonStatus(hDlg, IDRETRY, FALSE);
						}
						// [OK][適用]ボタンはグレイアウトするので、[キャンセル]ボタン上にカーソルを置く
						// (メニューバーが出ている場合は、何も起こらないだろう)
						SetFocus(GetDlgItem(hDlg, IDCLOSE));
						return 1;

					case IDHELP:	// ヘルプボタン
						SetFocus( GetDlgItem(hDlg, LOWORD(wParam)) );
						if ( 2 != rsc( hDlg, rscname, ":HELP", 0, NULL ) ) { // abortでなければ
							callhelp( hDlg, helpfile, contextid );
						}
						return 1;

					case IDSAVE:	// 保存ボタン
						SetFocus( GetDlgItem(hDlg, LOWORD(wParam)) );
						if ( 2 != rsc( hDlg, rscname, ":IDSAVE", 0, NULL ) ) { // abortでなければ
							char fname[MAX_PATH*2], basefname[_MAX_FNAME*2];

							// デフォルトでrscファイル名の拡張子を.rndに変えたものが表示される。
							_splitpath_s(rscname, NULL, 0, NULL, 0, basefname, _countof(basefname), NULL, 0);
							_makepath_s(fname, _countof(fname), NULL, NULL, basefname, ".rnd");

							if ( saveDialog( hDlg, SOFTWARENAME"ダイアログ状態(*.rnd)\0*.rnd\0すべてのファイル(*.*)\0*.*\0", fname, _countof(fname), "ダイアログの状態をファイルへ保存", "rnd" ) ) {
								if ( saveDialogStatus( hDlg, fname ) ) {
									MessageBox( hDlg, "ダイアログの状態をファイルへ保存しました。", "ダイアログの状態を保存", MB_OK | MB_ICONINFORMATION );
								}
							}
						}
						return 1;

					case IDLOAD:	// 読込ボタン
						SetFocus( GetDlgItem(hDlg, LOWORD(wParam)) );
						if ( 2 != rsc( hDlg, rscname, ":IDLOAD", 0, NULL ) ) { // abortでなければ
							char fname[MAX_PATH*2], basefname[_MAX_FNAME*2];

							// デフォルトでrscファイル名の拡張子を.rndに変えたものが表示される。
							_splitpath_s(rscname, NULL, 0, NULL, 0, basefname, _countof(basefname), NULL, 0);
							_makepath_s(fname, _countof(fname), NULL, NULL, basefname, ".rnd");

							if ( openDialog( hDlg, SOFTWARENAME"ダイアログ状態(*.rnd)\0*.rnd\0すべてのファイル(*.*)\0*.*\0", fname, _countof(fname), "ダイアログの状態をファイルから読み込む", "rnd" ) ) {
								if ( IDYES == MessageBox( hDlg, "現在のダイアログの状態は、指定されたファイルから読み出された状態で上書きされます。\nよろしいですか?\n\n※異なる環境で作られたrndファイルを読み込ませないでください。特にOSが異なる（Windows XPで作られたrndファイルをVistaに入れたり、x86版で作られたrndファイルをx64版に入れたりする）場合、レジストリの使われ方が異なることがあり、予期せぬ結果を招くことがあります。", "ダイアログの状態をファイルから読み込む", MB_YESNO | MB_ICONQUESTION ) ) {
									if ( 1 == loadDialogStatus( hDlg, fname ) )
										MessageBox( hDlg, "ファイルからダイアログの状態を読み込みました。", "ダイアログの状態を読み込み", MB_OK | MB_ICONINFORMATION );
								}
							}
						}
						return 1;

					case IDNO: // 各コントロール
						{ /* (HWND)lParam が、各コントロールのハンドル */
							for( UINT i=0 ; i<nctrls ; i++ ) {
								if (rctrls[i].hCtrl == (HWND)lParam) {
									if (rctrls[i].ctrl == REF_BUTTON_FILE) { // ファイルの参照
										char fname[MAX_PATH*2] = "";

										GetWindowText(rctrls[i-1].hCtrl, fname, _countof(fname));
										if ( openDialog( hDlg, FILETYPES, fname, _countof(fname), "ファイルの選択", NULL ) ) {
											SetWindowText(rctrls[i-1].hCtrl, fname);
											SetFocus( rctrls[i-1].hCtrl );
										}
										break;
									}                                 
									else if (rctrls[i].ctrl == REF_BUTTON_FOLDER) { // フォルダの参照
										char chPutFolder[MAX_PATH*2] = "";

										GetWindowText(rctrls[i-1].hCtrl, chPutFolder, _countof(chPutFolder));
										if ( browseFolder( hDlg, chPutFolder, "フォルダの選択" ) ) {
											SetWindowText(rctrls[i-1].hCtrl, chPutFolder);
											SetFocus( rctrls[i-1].hCtrl );
										}
										break;
									}

									else if (rctrls[i].ctrl == TODEFAULT_BUTTON) { // デフォルトへ戻すボタン
										if ( rctrls[i].opt == 1 ) { // 数値の場合
											char itbuf[16];
											// ↓数値の場合にi-2なのは、アップダウンコントロールがi-1に設置されているため
											if ( 0 == _ultoa_s(rctrls[i-2].def_dw, itbuf, _countof(itbuf), 10) ) {
												SetWindowText(rctrls[i-2].hCtrl, itbuf);
											}
										}
										else {
											SetWindowText(rctrls[i-1].hCtrl, rctrls[i-1].def_sz);
										}
										SetFocus( rctrls[i-1].hCtrl );							
										break;
									}

									else if (rctrls[i].ctrl == ICON_BUTTON) { // アイコンの参照
										char *szIconName;
										int  length;

										length = GetWindowTextLength( rctrls[i].hCtrl );
										szIconName = zalloc(length + 1);
										if ( szIconName ) {
											GetWindowText(rctrls[i].hCtrl, szIconName, length+1);
											if ( IDOK == selicon( hDlg, &szIconName) ) {
												SetIconBox( rctrls[i].id, szIconName );
												ctrlChange(hDlg, i);
											}

											safefree( szIconName );
										}
										break;
									}

									else if ( rctrls[i].ctrl == LVC_BUTTON_NEW ) { // 新規
										if ( 2 != rsc( hDlg, rscname, rctrls[i].id, 0, NULL ) ) {
											bool rv = 0;

											if ( rctrls[i-1].opt == 1 ) // keylist
												rv = listview_n_new( hDlg, i-1 );
											else // valuelist
												rv = listview_new( hDlg, i-1 );

											if ( rv ) {
												ctrlChange(hDlg, i-1);
											}
										}
										break;
									}
									else if ( rctrls[i].ctrl == LVC_BUTTON_EDIT ) { // 編集
										if ( 2 != rsc( hDlg, rscname, rctrls[i].id, 0, NULL) ) {
											bool rv = 0;

											if ( rctrls[i-2].opt == 1 ) { // keylist
												rv = listview_n_edit( hDlg, i-2 );
											}
											else { // valuelist
												rv = listview_edit( hDlg, i-2 );
											}

											if ( rv ) {
												ctrlChange(hDlg, i-2);
											}
										}
										break;
									}
									else if ( rctrls[i].ctrl == LVC_BUTTON_DEL ) { // 削除
										if ( 2 != rsc( hDlg, rscname, rctrls[i].id, 0, NULL ) ) {
											bool rv = 0;

											if ( rctrls[i-3].opt == 1 ) { // keylist
												rv = listview_n_delete( hDlg, i-3 );
											}
											else { // valuelist
												rv = listview_delete( hDlg, i-3 );
											}

											if ( rv ) {
												ctrlChange(hDlg, i-3);
											}
										}
										break;
									}

									else if ( rctrls[i].ctrl == GOTO_BUTTON || rctrls[i].ctrl == MIN_GOTO_BUTTON ) { // ラベルへ飛ぶボタン
										ctrlChange(hDlg, i);
										break;
									}

									else if ( rctrls[i].ctrl == COLOR_BUTTON ) { // 色選択
										CHOOSECOLOR cc;
										static COLORREF CustColors[16] = {RGB(0,0,0), RGB(0,0,255), RGB(0,255,255), RGB(0,255,0), RGB(255,0,255), RGB(255,0,0), RGB(255,255,0), RGB(255,255,255), RGB(0,0,127), RGB(0,127,127), RGB(0,127,0), RGB(127,0,127), RGB(127,0,0), RGB(127,127,0), RGB(127,127,127), RGB(192,192,192)};

										// レジストリからカスタムカラーを取得
										REG reg;

										if (reg.OpenKey(HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_READONLY)) {
											COLORREF CustColors0[16];

											if (reg.ReadString("CustColors", (char *)CustColors0, sizeof(CustColors0))) {
												memcpy(CustColors, CustColors0, sizeof(CustColors0));
											}
											reg.CloseKey();
										}

										ZeroMemory(&cc, sizeof(cc));
										cc.lStructSize = sizeof(cc);
										cc.hwndOwner = hDlg;
										cc.rgbResult = GetColorButton( rctrls[i].id );
										cc.lpCustColors = CustColors;
										cc.Flags= CC_FULLOPEN | CC_RGBINIT;

										if ( ChooseColor( &cc ) ) {
											SetColorButton( rctrls[i].id, cc.rgbResult );
											ctrlChange(hDlg, i);

											// レジストリへカスタムカラーを保存
											if (reg.OpenKey(HKEY_CURRENT_USER, RNSF7REGKEY, TREG_OPEN_CREATE)) {
												reg.WriteBinary("CustColors", (char *)CustColors, sizeof(CustColors));
												reg.CloseKey();
											}
											else {
												dispErrorMessage(hDlg, MB_OK | MB_ICONSTOP, "レジストリへの書き込み中エラー", "レジストリキー'HKEY_CURRENT_USER\\", RNSF7REGKEY,
																 "'を書き込みアクセスで開こうとしてエラーになりました。\nAPIが報告した原因：", getLastErrorString(tRegGetLastError()), NULL, NULL);
											}

											// ダイアログを再描画
											InvalidateRect((HWND)rctrls[i].lParam, NULL, TRUE);
										}
										break;
									}

									else if ( rctrls[i].ctrl == CHECK_CTRL ) { // チェックボックス
										// Auto3stateでない場合(=3stateだけどON/OFFの状態にしか遷移させない場合)は、ここでチェック状態を変化させる
										if (rctrls[i].opt != 1) {
											if (BST_UNCHECKED != SendMessage(rctrls[i].hCtrl, BM_GETCHECK, 0, 0)) {
												SendMessage(rctrls[i].hCtrl, BM_SETCHECK, BST_UNCHECKED, 0);
											}
											else {
												SendMessage(rctrls[i].hCtrl, BM_SETCHECK, BST_CHECKED, 0);
											}
										}

										// コントロールの変更を通知
										ctrlChange(hDlg, i);

										break;
									}

									else if ( rctrls[i].ctrl == EDIT_CTRL || rctrls[i].ctrl == MULTI_EDIT ) { // 編集ボックス
										// 数値専用エディットボックスだった場合は、内容を監視する
										// 数値になっていなかったら、強制上書き
										if ( rctrls[i].opt == 1 ) {
											char *buf;
											volatile static bool sflag = 0;

											if ( sflag == 0 ) { // セマフォ
												int bsize;
												sflag = 1;

												bsize = GetWindowTextLength(rctrls[i].hCtrl) + 1;
												if ( bsize > 1 ) {
													buf = zalloc( bsize );
													if ( buf ) {
														DWORD curx;
														char itbuf[16];

														GetWindowText( rctrls[i].hCtrl, buf, bsize );
														// 入力数値の正規化
														if ( 0 == _ultoa_s(atou(buf), itbuf, _countof(itbuf), 10) ) {
															if ( _stricmp(itbuf, buf) != 0 ) {
																SendMessage( rctrls[i].hCtrl, EM_GETSEL, (WPARAM)&curx, NULL); // カーソル位置をゲットして
																SetWindowText( rctrls[i].hCtrl, itbuf );
																SendMessage( rctrls[i].hCtrl, EM_SETSEL, (WPARAM)curx, (LPARAM)curx); // 上でゲットしたカーソル位置をセット
															}
														}
														free ( buf );

														// 範囲チェック
														buf = zalloc( bsize = GetWindowTextLength(rctrls[i].hCtrl) + 1 );
														if ( buf ) {
															UINT  iint, min, max;

															GetWindowText( rctrls[i].hCtrl, buf, bsize );
															iint = atou( buf );

															if ( rctrls[i].bit_max < rctrls[i].byte_min ) {
																// 最大・最小が逆になっている場合
																min = rctrls[i].bit_max;
																max = rctrls[i].byte_min;
															}
															else {
																min = rctrls[i].byte_min;
																max = rctrls[i].bit_max;
															}

															if ( HIWORD(wParam) == EN_KILLFOCUS && iint < min ) {
																char itbuf[16];
																// 入力途中で最小値未満になる問題があり、現在は最小値のチェックはフォーカスが失われる瞬間のみ行われる
																SendMessage( rctrls[i].hCtrl, EM_GETSEL, (WPARAM)&curx, NULL); // カーソル位置をゲットして
																if ( 0 == _ultoa_s(min, itbuf, _countof(itbuf), 10) ) {
																	SetWindowText( rctrls[i].hCtrl, itbuf );
																}
																SendMessage( rctrls[i].hCtrl, EM_SETSEL, (WPARAM)curx, (LPARAM)curx); // 上でゲットしたカーソル位置をセット
															}
															else if ( iint > max ) {
																char itbuf[16];

																iint /= 10;
																if ( iint > max )
																	iint /= 10;

																SendMessage( rctrls[i].hCtrl, EM_GETSEL, (WPARAM)&curx, NULL); // カーソル位置をゲットして
																if ( 0 == _ultoa_s(iint, itbuf, _countof(itbuf), 10) ) {
																	SetWindowText( rctrls[i].hCtrl, itbuf );
																}
																SendMessage( rctrls[i].hCtrl, EM_SETSEL, (WPARAM)curx, (LPARAM)curx); // 上でゲットしたカーソル位置をセット
															}

															free ( buf );
														}
													}
												}
												sflag = 0;
											}
										}

										if ( HIWORD(wParam) == EN_CHANGE ) {
											ctrlChange(hDlg, i);
										}
										break;
									}

									else if ( rctrls[i].ctrl == COMBO_CTRL ) { // コンボボックス
										if ( HIWORD(wParam) == CBN_SELCHANGE ) {
											ctrlChange(hDlg, i);
										}
										break;
									}

								}
							}
						
						}
						return 1;
				}
				break;
			}
	}
	return 0;
}

// エラーメッセージ用出力
static void errout( HWND hWnd, const char *text, const char *text2, const char *text3 )
{
	char *szOuttext = zalloc( lstrlen(text) + lstrlen(text2) + lstrlen(text3) + 1 );

	if ( szOuttext ) {
		lstrcpy( szOuttext, text );
		lstrcat( szOuttext, text2 );
		lstrcat( szOuttext, text3 );

		destroy_splash_window();
		MessageBox(hWnd, szOuttext, "R-Script", MB_OK | MB_ICONEXCLAMATION);

		safefree( szOuttext );
	}
}

#if DEBUG_LOGMODE
void dout( const char *text ) //textをファイルへ書き出し（デバッグ用）
{
	FILE *fp;

	if ( NULL != (fp = fopen( "debugout.txt", "at") ) ) {
		fputs(text, fp);
		fputs("\n", fp);
		fclose( fp );
	}
}
#endif /* DEBUG_LOGMODE */

// スクリプトの読み出しと解釈 (戻り値 0:正常終了 1:異常終了 2:スクリプトからの中断指示 3:labelで指定したラベルが見つからない)
static short rsc_aux( HWND hWnd, const char *fname, const char *label, bool icall, void (*callbackfunc)() )
{
	short ra_rv = 0;
	char *rv = NULL;
	char buf[350];
	unsigned char f_var_must_def = 0;
	HICON	dialogIcon = NULL;		// ダイアログのアイコンハンドル
	LOAD_RSC lr;
	list<RSCLINES>::const_iterator vrli, vrlend, vrlitmp;
	stack<list<RSCLINES>::const_iterator> gosub_stack;

	// ソースをロード・解析する
	if ( LR_OK != lr.source_load(fname) ) {
		destroy_splash_window();
		lr.errmes(hWnd);
		return 1;
	}

	// プリインタプリト
	if ( LR_OK != lr.pre_interprete() ) {
		destroy_splash_window();
		lr.errmes(hWnd);
		return 1;
	}

	// 定義部の読み出し
	{ 
		const char *p;
		char *fnameico = NULL;
		int len, ec;
		
		p = lr.searchItem( LR_SI_MUSTDEFVAR, "OFF", 0, ec );
		if ( 0 == _stricmp( p, "ON" ) || 0 == strcmp( p, "1" ) || 0 == _stricmp( p, "YES" ) )
			f_var_must_def = 1;
				
		len = lstrlen( fname );
		if ( len > 3 ) {
            fnameico = _strdup( fname );
			if ( fnameico ) {
				fnameico[len-3] = 'i';
				fnameico[len-2] = 'c';
				fnameico[len-1] = 'o';
			}
		}
		if ( fnameico == NULL ) {
            fnameico = _strdup( "none" );
		}

		p = lr.searchItem( LR_SI_ICON, fnameico, 0, ec );
		/**/ // ↓自プログラムファイル名の時の処理をしなくちゃ
		LoadIconFromFileEx(p, NULL, &dialogIcon);
		if ( !dialogIcon ) {
			LoadIconFromFileEx(p, &dialogIcon, NULL);
		}
		safefree( fnameico );
	}

	vrlend = lr.vrl.end();
	if ( label ) { // labelが指定されていた場合はそのラベルから解釈を始める
		if ( dialogIcon ) {
			DestroyIcon( dialogIcon );
		}
			
		vrli = lr.GoTo( label );
		if ( vrli == vrlend ) { // ラベルが見つからなかった場合
			return 3;
		}

		// :uninst, :try が実行される場合は、変数は初期化する
		if ( icall && (_stricmp(label, ":uninst") == 0 || _stricmp(label, ":try") == 0) ) {
			free_variables();	// 前回のスクリプト実行で使った変数の初期化
		}

		must_def_value( f_var_must_def ); // 変数の初期化を必須とするかどうかは定義部に規定されている
	}
	else { // labelが指定されていなかった場合は、先頭から解釈を始める
		// 初期化等
		vrli = lr.vrl.begin();
		free_variables();	// 前回のスクリプト実行で使った変数の初期化
		addctrl(hWnd, AC_INIT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); // コントロールの追加を開始する
		system_refresh(GetParent(hWnd), hWnd, 0); // リフレッシュ方法について予約を解除しておく
		must_def_value( f_var_must_def ); // スクリプト内で変数の初期化を必須とするかどうかは定義部に規定されている
		// ダイアログへアイコンの設定
		if ( !callbackfunc ) {
			SendMessage( hWnd, WM_SETICON, ICON_SMALL, (LPARAM)(dialogIcon?dialogIcon:hDefaultRscSmallIcon) );
		}
		else {
			if ( dialogIcon ) {
				DestroyIcon( dialogIcon );
			}
		}
	}

	// lexがエラー回復に失敗することがあるようなのでダミーの文
	interprete( hWnd, "DUMMY=0\n" ); 

	// コールバック関数の呼び出し
	if ( callbackfunc ) {
		callbackfunc();
	}

	while( vrli != lr.vrl.end() ) {
		if ( vrli->line != "\n" ) {
			// destroy_splash_window();
			// char itbuf[16];
			// MessageBox( hWnd, sct[line_no], itoa10ex(line_no, itbuf), MB_OK); // Debug
			rv = interprete( hWnd, vrli->line.c_str() );

			// スクリプト実行中に Shift+Esc が押されたら、中断するかどうかを訪ねる
			if ( (GetAsyncKeyState(VK_SHIFT)&0x8000) && (GetAsyncKeyState(VK_ESCAPE)&0x8000) ) {
				int r;
				char buf[300+MAX_PATH*2];

				wsprintf(buf, "現在%sの%d行目を実行中です。\nShift+Escキーが押されましたので、中断の確認をします。\n\nスクリプトを正常終了(Exit)したい場合は[はい]を、\nスクリプトを異常終了(Abort)したい場合は[いいえ]を、\n中断指示を取り消して、スクリプトの実行を継続する場合は[キャンセル]を押して下さい。", vrli->fname.c_str(), vrli->line_num );
				destroy_splash_window();
				r = MessageBox( hWnd, buf, "中断確認", MB_YESNOCANCEL | MB_DEFBUTTON3 );
				if ( r == IDYES ) {
					rv = "exit";
				}
				else if ( r == IDNO ) {
					rv = "abort";
				}
			}
			if ( rv != NULL && rv[0] != 0 ) {
				if (rv[0] == ':') { // ':'で始まる文字列が返ってきたらlabelへジャンプ
					if ( vrlend == (vrlitmp = lr.GoTo(rv)) ) {
						wsprintf(buf, "%s %d :\n", vrli->fname.c_str(), vrli->line_num);
						errout( hWnd, buf, rv, "\nラベルが見つかりません。" );
						ra_rv = 1;
						break;
					}
					else {
						vrli = vrlitmp;
						continue;
					}
				}
				else if ( strncmp(rv, "GOSUB :", 7) == 0 ) { // "GOSUB :"で始まる文字列が返ってきたらlabelへサブルーチンコール
					gosub_stack.push( vrli );

					if (vrlend == (vrlitmp = lr.GoTo(rv+6))) {
						wsprintf(buf, "%s %d :\n", vrli->fname.c_str(), vrli->line_num);
						errout( hWnd, buf, rv+6, "\nGOSUBラベルが見つかりません。" );
						ra_rv = 1;
						break;
					}
					else {
						vrli = vrlitmp;
						continue;
					}
				}
				else if ( strcmp(rv, "return") == 0 ) { // "return" と返ってきたら、サブルーチンからの帰還
					if ( gosub_stack.empty() ) {
						wsprintf(buf, "ファイル:%s %d行目 :\n", vrli->fname.c_str(), vrli->line_num);
						errout( hWnd, buf, "", "\nエラー：不正なreturnが存在しています。\n\nGOSUBしていない状態でreturnが使われました。" );
						ra_rv = 1;
						break;
					}
					else {
						vrli = gosub_stack.top();
						gosub_stack.pop();
					}
				}
				else if ( strcmp(rv, "nextline") == 0 ) { // "nextline" と返ってきたら、次の行は飛ばす(旧if対策)
					++vrli;
				}
				else if ( strcmp(rv, "exit") == 0 ) { // "exit" と返ってきたら、終了
					ra_rv = 0;
					break; // 正常終了
				}
				else if ( strcmp(rv, "abort") == 0 ) { // "abort" と返ってきたら、強制中断
					ra_rv = 2; // 強制中断
					break;
				}
				else { // その他のエラー
					wsprintf(buf, "ファイル:%s %d行目 :\n", vrli->fname.c_str(), vrli->line_num);
					errout( hWnd, buf, rv, "" );
					ra_rv = 1;
					break;
				}
			}
		}
		++vrli;
	}

	if ( !label && !callbackfunc ) { // もし行頭から解釈を始めたのなら、正常終了の暁には[OK][キャンセル]などのボタンの追加と、終了処理を行う
		addctrl(hWnd, AC_FINISH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); // 終了

		// 「最近使った機能項目」の一覧への追加
		if (!bNotAddRecent) { // 抑止フラグが立っていない場合のみ、追加（更新）を行う
			REG reg;
			int i;
			char s[20], s2[20], *name;
			char v[20], v2[20];

			if ( reg.OpenSubKey(HKEY_CURRENT_USER, RNSF7REGKEY, "R7RECENT", TREG_OPEN_CREATE) ) {
				for(i=1 ; i<=RECENTMAX ; ++i) {
					wsprintf(s, "r7r_item%d", i);
					name = reg.new_ReadString(s);

					if ( name == NULL ) {
						break;
					}
					else if ( strcmp(name, g_item) == 0 ) {
						safedelete(name);
						break;
					}
					safedelete(name);
				}

				for(; i>=2 ; --i) {
					wsprintf(s, "r7r_item%d", i-1);
					wsprintf(s2, "r7r_item%d", i);
					wsprintf(v, "r7r_rsc%d", i-1);
					wsprintf(v2, "r7r_rsc%d", i);

					reg.RenValue(s, s2);
					reg.RenValue(v, v2);
				}
				reg.WriteString("r7r_item1", g_item, TREG_NORMAL_SZ);
				reg.WriteString("r7r_rsc1",  rscname);

				reg.CloseKey();

				// 「最近使った機能項目」メニューを増減させる
				bR7recentChange = 1;
			}
			else {
				dispErrorMessage(hWnd, MB_OK | MB_ICONSTOP, "レジストリへの書き込み中エラー", "レジストリキー'HKEY_CURRENT_USER\\", RNSF7REGKEY, "\\R7RECENT",
								 "'を書き込みアクセスで開こうとしてエラーになりました。\nAPIが報告した原因：", getLastErrorString(tRegGetLastError()), NULL);
			}
		}
	}

	return ra_rv; // 正常終了
}

// 1行だけスクリプトを実行し、結果を数値で返す
unsigned int rsc1line( HWND hWnd, const char *script )
{
	char *order;

	free_variables();	// 前回のスクリプト実行で使った変数の初期化
	must_def_value( 0 );	// 変数の初期化は必須としない
	interprete( hWnd, "DUMMY=0\n" ); // ダミー文実行

	order = zalloc( lstrlen(script) + 8 );
	if ( order ) {
		lstrcpy( order, "DUMMY=" );
		lstrcat( order, script );
		lstrcat( order, "\n" );
		interprete( hWnd, order );
		safefree( order );
	}

	return getVariable( "DUMMY" );
}

// rsc_aux() は、" で囲まれたファイル名を処理できない＆カレントディレクトリが固定なので、それらを処理するためのカプセル化関数
short rsc( HWND hWnd, const char *fname, const char *label, bool icall, void (*callbackfunc)() )
{
	static bool s; // セマフォ
	short rv = 1;

	if ( s == 0 ) {
		s = 1;
		{
			char *fname2 = NULL; // fname が " に囲まれていた場合の仮領域
			int  i;

			// " に囲まれていたら、" を取り除く
			if ( fname[0] == '\"' ) {
				fname2 = _strdup( fname + 1 );
				if ( fname2 ) {
					i = lstrlen( fname2 );
					if ( fname2[i-1] == '\"' )
						fname2[i-1] = 0;
				}
			}
			else {
				fname2 = _strdup( fname );
			}

			if ( fname2 ) {
				if( rnsf7RegRead("CDRSC", 0) == 1 && strchr(fname2, '\\' ) ) {
					// RSCファイルのある場所をカレントディレクトリとする
					char oldcd[MAX_PATH*2], newcd[MAX_PATH*2];
					char drv[_MAX_DRIVE*2], dir[_MAX_DIR*2];
			
					_splitpath_s(fname2, drv, _countof(drv), dir, _countof(dir), NULL, 0, NULL, 0);
					_makepath_s(newcd, _countof(newcd), drv, dir, NULL, NULL);
					GetCurrentDirectory( _countof(oldcd), oldcd );
					SetCurrentDirectory( newcd );
					rv = rsc_aux( hWnd, fname2, label, 0, NULL );
					SetCurrentDirectory( oldcd );
				}
				else {
					rv = rsc_aux( hWnd, fname2, label, icall, callbackfunc );
				}
				safefree( fname2 );
			}
		}
		s = 0;
	}
	return rv;
}



static HWND me_DlgWnd; // ↓この2つの関数でのみ使う

// ダイアログのスクロールバーが操作されたときに、コントロールを再配置する
static BOOL CALLBACK ecwEnumFunc( HWND hChild, LPARAM lParam)
{
	RECT rt;
	POINT pt;

	if ( GetParent( hChild ) == me_DlgWnd ) { // 孫ウィンドウは探さない
		GetWindowRect(hChild, &rt);
		pt.x = rt.left; pt.y = rt.top;
		ScreenToClient(me_DlgWnd, &pt);
		pt.y -= (int)lParam;
		SetWindowPos(hChild, 0, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOREDRAW);
	}
	return TRUE;
}

static void movectrl( HWND hWnd, int vsa, int vsb )
{
	// vsa:移動前y座標, vsb:移動後y座標
	int dy = vsb - vsa;

	if (dy != 0) { // 移動があったら再描画をはじめる
		HDC     hdc, memdc;
		HBITMAP hbmp;
		RECT	rt, rt2;
		int     noQuickScrool;

		me_DlgWnd = hWnd;
		EnumChildWindows(hWnd, (WNDENUMPROC)ecwEnumFunc, (LPARAM)(vsb-vsa));
		noQuickScrool = rnsf7RegRead("NoQuickScrool", 0);

		switch ( noQuickScrool ) {
			case 2: // スクロールバーのドラッグが終了した時点で再描画
			case 1:	// 常に画面全体を再描画する
				InvalidateRect(hWnd, NULL, TRUE);
				break;

			case 0: // 必要な部分だけを再描画するが、最前面ウィンドウ対策のために、最終的な再描画の200ms後にウィンドウ全体を再描画する
				KillTimer( hWnd, R7OSSC_TIMERID );
				// 部分的に再描画する
				GetClientRect(hWnd, &rt);

				hdc = GetDC( hWnd );
				memdc = CreateCompatibleDC( hdc );
				hbmp = CreateCompatibleBitmap(hdc, rt.right, rt.bottom);
				SelectObject(memdc, hbmp);

				rt2.left = 0;
				rt2.right = rt.right;

				if ( dy > 0 ) { // 下への移動だったら
					BitBlt(memdc, 0, 0, rt.right, rt.bottom-dy, hdc, 0, dy, SRCCOPY);
					BitBlt(hdc, 0, 0, rt.right, rt.bottom-dy, memdc, 0, 0, SRCCOPY);
					rt2.top = rt.bottom-dy;
					rt2.bottom = rt.bottom;
				}
				else { // 上への移動だったら( dyは負の数 )
					BitBlt(memdc, 0, 0, rt.right, rt.bottom+dy, hdc, 0, 0, SRCCOPY);
					BitBlt(hdc, 0, -dy, rt.right, rt.bottom, memdc, 0, 0, SRCCOPY);
					rt2.top = 0;
					rt2.bottom = -dy;
				}

				DeleteDC( memdc );
				DeleteObject(hbmp);
				ReleaseDC( hWnd, hdc );
				InvalidateRect(hWnd, &rt2, TRUE);

				// この描画が終わった後にWM_TIMERを呼び出し、画面全体を再描画させる
				// (ダイアログ上に最前面ウィンドウがあったときの対策)
				SetTimer( hWnd, R7OSSC_TIMERID, R7OSSC_TIMERWAIT, NULL );
				break;
		}
	}
}

// リストビュー：アイテムの編集(2カラム)
static bool listview_edit( HWND hWnd, int iCtrl )
{
	int m, n = GetLVCursor( rctrls[iCtrl].hCtrl );
	char *itemname, *itemdata;
	bool rv = false;

	if ( n == -1 ) {
		MessageBox(hWnd, "編集したい項目を選択して下さい。", "編集", MB_OK | MB_ICONSTOP);
		return false;
	}

	m = ListView_GetItemCount( rctrls[iCtrl].hCtrl );

	for(; n<m ; n++) {
		if ( LVIS_SELECTED == ListView_GetItemState(rctrls[iCtrl].hCtrl, n, LVIS_SELECTED) ) {
			itemname = lvGetItemString( rctrls[iCtrl].hCtrl, n, 0 );
			if ( itemname ) {
				itemdata = lvGetItemString( rctrls[iCtrl].hCtrl, n, 1 );
				if ( itemdata ) {
					if ( lv_edititem(hWnd, &itemname, &itemdata, rctrls[iCtrl].hCtrl, rctrls[iCtrl].def_sz, rctrls[iCtrl].text) ) {
						ListView_SetItemText(rctrls[iCtrl].hCtrl, n, 0, itemname);
						ListView_SetItemText(rctrls[iCtrl].hCtrl, n, 1, itemdata);
						SetFocus( rctrls[iCtrl].hCtrl );

						safefree( itemname );
						safefree( itemdata );
						rv = true;
					}
					else {
						safefree( itemname );
						safefree( itemdata );
						rv = false;
					}
				}
				else {
					MessageBox( hWnd, "編集に必要なメモリが足りません。", "編集", MB_OK | MB_ICONSTOP );
				}
				safefree( itemname );
			}
			else {
				MessageBox( hWnd, "編集に必要なメモリが足りません。", "編集", MB_OK | MB_ICONSTOP );
			}
		}
	}
	SetFocus( rctrls[iCtrl].hCtrl );

	return rv;
}

// リストビュー：アイテムの削除(2カラム)
static bool listview_delete( HWND hWnd, int iCtrl )
{
	int n = GetLVCursor( rctrls[iCtrl].hCtrl ), i, j, m;
	char *str, *itemname;
	
	if ( n == -1 ) {
		MessageBox(hWnd, "削除したい項目を選択して下さい。", "削除", MB_OK | MB_ICONSTOP);
		return false;
	}

	// 複数項目が選択されていないか?
	j = 1;
	m = ListView_GetItemCount( rctrls[iCtrl].hCtrl );

	for(i=n+1 ; i<m ; i++) {
		if ( LVIS_SELECTED == ListView_GetItemState(rctrls[iCtrl].hCtrl, i, LVIS_SELECTED) ) {
			j++;
		}
	}

	if ( j == 1 ) {
		itemname = ListView_GetText( rctrls[iCtrl].id, n, 0 );
		if ( itemname ) {
			str = zalloc( lstrlen(itemname) + 30 );
			if ( str ) {
				wsprintf(str, "項目 %s を削除します。", itemname);
				safefree( itemname );
				if ( IDOK == MessageBox(hWnd, str, "削除", MB_OKCANCEL | MB_ICONQUESTION ) ) {
					SetFocus( rctrls[iCtrl].hCtrl );
					safefree(str);
					return ( TRUE == ListView_DeleteItem(rctrls[iCtrl].hCtrl, n) );
				}
				else {
					safefree(str);
					return false;
				}
			}

			safefree(itemname);
		}

		MessageBox( hWnd, "削除に必要なメモリが足りません。", "削除", MB_OK | MB_ICONSTOP );
		return false;
	}
	else {
		char str[50];
		wsprintf(str, "%d個の項目を削除します。", j);
		if ( IDOK == MessageBox(hWnd, str, "削除", MB_OKCANCEL | MB_ICONQUESTION ) ) {
			SetFocus( rctrls[iCtrl].hCtrl );
			for(i=n ; i<m ; i++) {
				if ( LVIS_SELECTED == ListView_GetItemState(rctrls[iCtrl].hCtrl, i, LVIS_SELECTED) ) {
					if ( ListView_DeleteItem(rctrls[iCtrl].hCtrl, i) ) {
						m--;
						i--;
					}
				}
			}
			return true;
		}
		else {
			return false;
		}
	}
}

// リストビュー：アイテムの作成(2カラム)
static bool listview_new( HWND hWnd, int iCtrl )
{
	int n;
	char *itemname = NULL, *itemdata = NULL;
	LVITEM lvitem;

	if ( lv_edititem(hWnd, &itemname, &itemdata, rctrls[iCtrl].hCtrl, rctrls[iCtrl].def_sz, rctrls[iCtrl].text) ) {
		n = ListView_GetItemCount(rctrls[iCtrl].hCtrl);
		ListView_SetItemCount(rctrls[iCtrl].hCtrl, n+1);
		lvitem.mask = LVIF_TEXT;
		lvitem.iItem = n;
		lvitem.iSubItem = 0;
		lvitem.pszText = itemname;
		ListView_InsertItem(rctrls[iCtrl].hCtrl, &lvitem);
		ListView_SetItemText(rctrls[iCtrl].hCtrl,  n, 1, itemdata);
		ListView_SetItemState(rctrls[iCtrl].hCtrl, n, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
		SetFocus( rctrls[iCtrl].hCtrl );

		safefree( itemname );
		safefree( itemdata );
		return true;
	}
	else {
		return false;
	}
}

// リストビュー：アイテムの編集(nカラム)
static bool listview_n_edit( HWND hWnd, int iCtrl )
{
	bool rv = false;
	int  m, n = GetLVCursor( rctrls[iCtrl].hCtrl );

	if ( n == -1 ) {
		MessageBox(hWnd, "編集したい項目を選択して下さい。","編集",MB_OK | MB_ICONSTOP);
		return false;
	}

	m = ListView_GetItemCount( rctrls[iCtrl].hCtrl );

	for(; n<m ; n++) {
		if ( LVIS_SELECTED == ListView_GetItemState(rctrls[iCtrl].hCtrl, n, LVIS_SELECTED) ) {
			if ( lv_edititem_n( hWnd, rctrls[iCtrl].hCtrl, n, (char **)rctrls[iCtrl].text ) ) {
				rv = true;
			}
		}
	}
	SetFocus( rctrls[iCtrl].hCtrl );

	return rv;
}

// リストビュー：アイテムの削除(nカラム)
// 削除については、2カラムのものとやることは変わらないため、マクロとした
/*
static bool listview_n_delete( HWND hWnd, int iCtrl )
{
	
	return listview_delete( hWnd, iCtrl );
}*/

// リストビュー：アイテムの作成(nカラム)
static bool listview_n_new( HWND hWnd, int iCtrl )
{
	bool rv;

	rv = lv_edititem_n( hWnd, rctrls[iCtrl].hCtrl, -1, (char **)rctrls[iCtrl].text );
	SetFocus( rctrls[iCtrl].hCtrl );

	return rv;
}

// リストビュー用（違う用途でも良いけど）
// id が : で始まっていて、それに alabel を付加して、その名前のラベルが存在していたら、そのラベルを呼び出す
static short galabel( HWND hWnd, const char *rscname, const char *id, const char *alabel )
{
	if ( id[0] == ':' ) { // IDがラベルだったら、後ろに _EDIT を付加したラベルへジャンプ
		char *lvelabel = zalloc( lstrlen(id) + lstrlen(alabel) + 1 );

		if ( lvelabel ) {
			int rrv;

			lstrcpy( lvelabel, id );
			lstrcat( lvelabel, alabel );
			rrv = rsc( hWnd, rscname, lvelabel, 0, NULL );
			safefree( lvelabel );

			return (short)rrv;
		}

		return 1; // メモリ不足
	}

	return 0; // ラベルではなかった場合は正常終了となる
}

// リストビュー：ソート中に呼び出される関数(コールバック関数)
static int CALLBACK listview_pfnCompare(LPARAM lParam1, LPARAM lParam2, LPARAM)
{
	return strcmp((char *)lParam1, (char *)lParam2);
}
