#include "stdafx.h"
#include "Rnsf7.h"
#include "helpdlg.h"
#include "opendlg.h"
#include "refresh.h"
#include "resource.h"
#include "commfunc.h"

#define ADDSTRLEN	20480

HWND   hHelpWindow;
static char *rtf, *hlpwndtitle;
static size_t rtf_len, rtf_size;
static BOOL lastcall = 0; // 直前に呼ばれたものが helpdoc_section(0)か、helpdoc_text(1)か。

static struct DEFTOOLTIP helpfindtooltips[] = {
	{IDC_CHECK_GYAKU, "上方向に検索します。ここをチェックしないと、下方向に検索されます"},
	{IDC_EDIT1, "検索したい文字列を入力します"}, 
	{IDC_CHECK_CASESENSITIVE, "半角アルファベットの大文字・小文字を区別せずに検索します"},
	{IDOK,      "上に入力された文字列で検索を開始します"},
	{IDCANCEL,  "検索を開始せずに、このダイアログを閉じます"},
	{0,0}};

// *rtfと、もろもろの変数を初期化
static void rtf_clear(void)
{
	safefree(rtf);
	rtf_len = rtf_size = 0;
}

// *rtfに文字列を足す関数（追加する文字列の長さがわかっている場合）
static BOOL rtf_addstr2(const char *str, size_t addstrlen)
{
	if ( str ) {
		if ( rtf ) {
			size_t newstrlen = ((rtf_len + addstrlen) / ADDSTRLEN + 1) * ADDSTRLEN;

			if ( newstrlen > rtf_size ) {
				char *tmp = (char *)realloc(rtf, newstrlen);
				if ( tmp ) {
					rtf = tmp;
					rtf_size = newstrlen;
				}
				else {
					return FALSE;
				}
			}
			memcpy(rtf+rtf_len, str, addstrlen);
			rtf[rtf_len += addstrlen] = 0;
			return TRUE;
		}
		else {
			rtf_size = (addstrlen / ADDSTRLEN + 1) * ADDSTRLEN;

			rtf = (char *)malloc(rtf_size);
			if ( rtf ) {
				memcpy(rtf, str, addstrlen);
				rtf[rtf_len = addstrlen] = 0;
				return TRUE;
			}
			else {
				rtf_size = 0;
				return FALSE;
			}
		}
	}
	else {
		return FALSE;
	}
}

// *rtfに文字列を足す関数
static BOOL rtf_addstr(const char *str)
{
	if ( str ) {
		return rtf_addstr2(str, strlen(str));
	}
	else {
		return FALSE;
	}
}

// ヘルプテキストの作成を開始
EXTERN BOOL helpdoc_start( const char *title, BYTE mode )
{
	lastcall = 0;

	rtf_clear();			 // すでにバッファが確保されていたら解放
	safefree( hlpwndtitle ); // ヘルプウィンドウのタイトルバー
	                         // hlpwndtitle = NULL だとタイトルバーには「ヘルプ」とだけ表示される

	if ( title ) {
		hlpwndtitle = zalloc( strlen(title) + 9 );
		if ( hlpwndtitle ) {
			lstrcpy( hlpwndtitle, title );
			if ( mode == 0 ) {
				lstrcat( hlpwndtitle, "のヘルプ" );
			}
		}
	}

	// リッチテキスト ver.1 を開始
	return rtf_addstr( "{\\rtf1\\ansi\\ansicpg932\\deff0\\deflang1033\\deflangfe1041\\fs20{\\colortbl;\\red0\\green0\\blue0;\\red0\\green0\\blue255;\\red0\\green255\\blue255;\\red0\\green255\\blue0;\\red255\\green0\\blue255;\\red255\\green0\\blue0;\\red255\\green255\\blue0;\\red255\\green255\\blue255;\\red0\\green0\\blue127;\\red0\\green127\\blue127;\\red0\\green127\\blue0;\\red127\\green0\\blue127;\\red127\\green0\\blue0;\\red127\\green127\\blue0;\\red127\\green127\\blue127;\\red192\\green192\\blue192;}" );
}

// ヘルプテキストに太字を追加する(&があると排除される. \n, \t は \\par, \\tab に変換される.)
EXTERN BOOL helpdoc_section( const char *str, BYTE mode )
{
	int  i, j;
	char *str2;
	BOOL rv = TRUE, sw;

	if ( rtf == NULL ) { // バッファを確保していなかったら、確保する
		if ( FALSE == helpdoc_start( NULL, mode ) ) {
			return FALSE;
		}
	}

	// 太字で文字列を追加する (直前に呼ばれたのがtextの方なら改行を入れる)
	if (!rtf_addstr( mode==1?(lastcall!=0?"\\par\\par{\\b\\fs24\\cf9 ":"{\\b\\fs24\\cf9 "):
	                         (lastcall!=0?"\\par\\par{\\b\\fs22\\cf9 ":"{\\b\\fs22\\cf9 ") )) {
		return FALSE;
	}

	if ( mode == 0 ) {
		// アクセラレーション文字を表す&が含まれていたら、それを排除したうえで、その後の1文字に下線をつける
		// ただし、SHIFT JIS漢字の第2バイト目だった場合は排除しない
		// &が2つ続いた場合も2つめのは削除しない
		sw = 0; // &が2つ続いていた場合も2つめの&は削除しないためのフラグ
		str2 = (char *)malloc( strlen(str)*6+1 );
		if ( str2 ) {
			for(i=j=0 ; str[i] ; i++) {
				if ( str[i] == '&' ) {
					if ( i > 0 && (sw == 1 || isKanji1( str, i-1 )) ) { // 直前が&でなく、かつ漢字の第１バイト目でない
						str2[j++] = str[i];
						if ( sw == 1 ) { // 直前が&だった場合は、下線をつけるが、その下線モードを解除する
							str2[j++] = '}';
							sw = 0;
						}
					}
					else {
						str2[j++] = '{';
						str2[j++] = '\\';
						str2[j++] = 'u';
						str2[j++] = 'l';
						str2[j++] = ' ';
						sw = 1;
					}
				}
				else {
					str2[j++] = str[i];
					if ( sw == 1 ) { // 直前が&だった場合は、下線をつけるが、その下線モードを解除する
						str2[j++] = '}';
						sw = 0;
					}
				}
			}
			str2[j] = 0;
			// 文字列の追加は helpdoc_text に任せる( \\par, \\tab の処理があるので )
			lastcall = 1;
			rv = helpdoc_text( str2, mode );
			lastcall = 0;
			safefree( str2 );
		}
	}
	else {
		lastcall = 1;
		rv = helpdoc_text( str, mode );
		lastcall = 0;
	}

	// \\b〜 の閉じカッコ
	
	if ( !rtf_addstr2("}", 1) ) {
		return FALSE;
	}

	return rv;
}

// ヘルプテキストに文字列を追加する(\n, \t は \\par, \\tab に変換される.)
EXTERN BOOL helpdoc_text( const char *str, BYTE mode )
{
	UINT lastr = 0; // rtf に追加すべき文字数
	UINT i = 0;     // カウンタ
	char *astr;		// rtf に追加すべき文字を指すことになるポインタ

	if ( rtf == NULL ) { // バッファを確保していなかったら、確保する
		if ( FALSE == helpdoc_start( NULL, mode ) ) {
			return FALSE;
		}
	}

	// rtfに追加すべき文字数をカウントする
	if ( lastcall != 1 ) { // 直前に呼ばれたのがsectionの方なら改行(\\par )を入れる
		lastr = 6; // 文末の\0も数えている
	}
	else {
		lastr = 1; // 文末の\0を数えている
	}

	for(i=0 ; str[i] ; i++) {
		switch( str[i] ) {
			case '\n': // \nは\\par に置き換える
			case '\t': // \tは\\tab に置き換える
				lastr += 5;
				break;

			case '{': // { は \\{ に置き換えるかもしれない
			case '}': // } は \\} に置き換えるかもしれない
			case '\\': // \\ は \\\\ に置き換えるかもしれない
				lastr += 2;

			default:  // 通常の文字
				lastr++;
		}
	}
	// 最後に改行を付加
	lastr += 5;

	// 追加すべき文字数は、多くとも lastr 文字であることがわかったのでその分のメモリを確保する
	astr = zalloc( lastr );
	if ( astr == NULL ) {
		return FALSE;
	}

	// rtfに追加すべき文字を作っていく
	if ( lastcall != 1 ) { // 直前に呼ばれたのがsectionの方なら改行(\\par )を入れる
		lstrcat(astr, "\\par ");
	}

	for(i=0 ; str[i] ; i++) {
		switch( str[i] ) {
			case '\n': // \nは\\par に置き換える
				lstrcat(astr, "\\par ");
				break;

			case '\t': // \tは\\tab に置き換える
				lstrcat(astr, "\\tab ");
				break;

			default:  // 通常の文字
				if ( mode != 0 || ISKANJI1M(str[i]) ) { // 漢字の第1バイトめが見つかり、その次が { } \ のいずれかだったら、直前に \ を入れる
														// mode != 0 の場合は、漢字とは関係なく入れる
					UINT len = lstrlen(astr);

					if ( mode == 0 ) { // mode == 0 の場合は、漢字の第2バイト目の判定になる
						astr[len++] = str[i++];
					}

					if ( str[i] == 0 )
						;
					else if ( str[i] == '{' || str[i] == '}' || str[i] == '\\' ) {
						astr[len++] = '\\';
						astr[len] = str[i];
					}
					else {
						astr[len] = str[i];
					}
					break;
				}
				astr[ (UINT)lstrlen(astr) ] = str[i];
				break;				
		}
	}
	// 最後に改行を付加
	lstrcat(astr, "\\par ");

	// 最後に一気に追加
	if ( !rtf_addstr(astr) ) {
		safefree(astr);
		return FALSE;
	}
	safefree(astr);

	lastcall = 1;
	return TRUE;
}

// ヘルプテキストへの文字追加を終了する
EXTERN BOOL helpdoc_end( void )
{
	if ( rtf == NULL ) { // バッファを確保していなかったら、確保する
		if ( FALSE == helpdoc_start( NULL, 0 ) ) {
			return FALSE;
		}
	}

	// \\rtf1 の閉じカッコ
	if ( rtf_addstr2("}", 1) ) {
		return TRUE;
	}
	else {
		return FALSE;
	}
}

static WNDPROC re_OldProc;

// RichEditのウィンドウプロシージャ(サブクラス化)
EXTERN LRESULT CALLBACK re_NewProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch( message ) {
		case WM_KEYDOWN:
			switch( wParam ) {
				case VK_ESCAPE: // 終了のための [Esc] 受け取りのため
					DestroyWindow( GetParent(hWnd) );
					return 0; // デフォルトのプロシージャを呼ばずにそのまま終了

				case VK_F3: // 検索のための [F3] 受け取りのため
					SendMessage(GetParent(hWnd), WM_COMMAND, (WPARAM)MAKEWPARAM(IDM_FINDNEXT, 0), 0);
					return 0; // デフォルトのプロシージャを呼ばずにそのまま終了
			}
			break;

		case WM_CHAR: // キー入力受け取り
			switch( wParam ) {
				case CTRL_F: // 検索
					SendMessage(GetParent(hWnd), WM_COMMAND, MAKEWPARAM(IDM_FIND, 0), 0);
					return 0; // デフォルトのプロシージャを呼ばずにそのまま終了

				case CTRL_S: // セーブ（ファイルへ保存）
					SendMessage(GetParent(hWnd), WM_COMMAND, MAKEWPARAM(IDM_HELPSAVEFILE, 0), 0);
					return 0; // デフォルトのプロシージャを呼ばずにそのまま終了

				case CTRL_W: // 閉じる
					SendMessage(GetParent(hWnd), WM_COMMAND, MAKEWPARAM(IDM_CLOSE, 0), 0);
					return 0; // デフォルトのプロシージャを呼ばずにそのまま終了
			}
			break;

		// システムメニューやウィンドウのメニューをキーボードから扱うための仕掛け
		case WM_KEYUP:
		case WM_SYSKEYDOWN:
		case WM_SYSCHAR:
		//case WM_SYSCOMMAND:	// WM_SYSCOMMAND まで止めてしまうと、スクロールバーの左クリック系操作がすべて無効になってしまう様子。
		case WM_SYSKEYUP:
			return SendMessage(GetParent(hWnd), message, wParam, lParam);
	}

	// デフォルトのプロシージャをコール
	return CallWindowProc(re_OldProc, hWnd, message, wParam, lParam);
}

// ヘルプを開く
static BYTE helpdoc_open_mode = 0;
EXTERN HWND helpdoc_open( HWND hParent, BYTE mode )
{
	if ( hHelpWindow ) { // すでにヘルプウィンドウが開いていたら、何もしない
		DestroyWindow( hHelpWindow );
	}

	helpdoc_open_mode = mode;
	hHelpWindow = CreateWindow( "helpdlg_rnsf7", hlpwndtitle?hlpwndtitle:"ヘルプ",  WS_CAPTION | WS_POPUPWINDOW | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
		                        (GetSystemMetrics(SM_CXFULLSCREEN)-635)/2, 32, 630, GetSystemMetrics(SM_CYFULLSCREEN)-64, hParent, NULL, hInst, NULL);

	if ( hHelpWindow == NULL ) {
		MessageBox( hParent, "システムエラーにより、ビューウィンドウを開けません。", hlpwndtitle?hlpwndtitle:"ヘルプ", MB_OK | MB_ICONSTOP );
		return NULL;
	}

	ShowWindow(hHelpWindow, SW_SHOWNORMAL);
	UpdateWindow( hHelpWindow );

	return hHelpWindow;
}

static size_t byteread = 0; // EditStreamCallback は何バイトまで読んだか
static DWORD CALLBACK EditStreamCallback(DWORD, LPBYTE pbBuf, LONG cb, LONG *pcb)
{
	char *rtfbuf = rtf + byteread;
	size_t len;

	len = rtf_len - byteread;

	if ( (size_t)cb < len ) {
		len = (size_t)cb;
	}

	*pcb = (LONG)len;
	byteread += len;

	memcpy( pbBuf, rtfbuf, len );

	return 0;
}

// ダイアログのアンカー等に関する情報
const DLGITEM dlgitems[] = {
	{IDC_EDIT1,					ANCHOR_TOPLEFTRIGHT},
	{IDOK,						ANCHOR_TOPRIGHT},
	{IDCANCEL,					ANCHOR_TOPRIGHT},
	{0,0}
};

// 検索ダイアログプロシージャ
static char **findbuf, *checkbox, *checkboxtip;
static BOOL *hf_up, *hf_ncs;
static LRESULT CALLBACK findProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
	static ComboboxQueue *cbq;
	static SIZEDLG *sizedlg;

	switch (message) {
		case WM_INITDIALOG:
			// 現在は、ダイアログは MS P ゴシックで固定
			// if ( rnsf7RegRead("NoDefFont", 0) == 0 ) {
			// 	SetChildWindowFont( hDlg );
			// }
			helpfindtooltips[0].tip = checkboxtip?checkboxtip:"";
			makeToolTip(hDlg, helpfindtooltips);
			if ( *findbuf ) { // 検索文字列の初期設定
                SetDlgItemText( hDlg, IDC_EDIT1, *findbuf );
				if ( *findbuf[0] ) { // 1文字以上あったら[検索開始]を有効にする
                    EnableWindow( GetDlgItem(hDlg, IDOK), TRUE );
				}
			}
			if ( checkbox ) {
				ShowWindow( GetDlgItem(hDlg, IDC_CHECK_GYAKU), SW_SHOW );
				SetDlgItemText(hDlg, IDC_CHECK_GYAKU, checkbox);
				if ( *hf_up ) { // 上へ検索
					SendDlgItemMessage(hDlg, IDC_CHECK_GYAKU, BM_SETCHECK, BST_CHECKED, 0);
				}
			}
			if ( *hf_ncs ) { // case-sensitiveでない/その他のチェック
				SendDlgItemMessage(hDlg, IDC_CHECK_CASESENSITIVE, BM_SETCHECK, BST_CHECKED, 0);
			}

			// ダイアログアイコンを設定
			SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(hInst, (LPCSTR)IDI_FILE_BROWSE) );

			// コンボボックスのリストを準備
			cbq = new ComboboxQueue();
			cbq->Init(hDlg, IDC_EDIT1, 16, HKEY_CURRENT_USER, RNSF7REGKEY, "scriptsearch_combo");

			// ダイアログのコントロールのアンカーを登録
			sizedlg = new SIZEDLG(hDlg, dlgitems);
			sizedlg->SetMinMaxInfo(sizedlg->GetDefWidth(), sizedlg->GetDefHeight(), -1, sizedlg->GetDefHeight());

			return TRUE;

		case WM_DROPFILES: // 編集ボックスがファイルをドロップされた
			{
				int n; // ドロップされたファイルの数
				int i; // カウンタ
				int len; // 編集ボックス内にすでに存在する文字列のバイト数
				char fname[MAX_PATH*2];
				char *buf;

				n = DragQueryFile((HDROP)wParam, (UINT)-1, NULL, 0);
				for(i=0 ; i<n ; i++) {
					if (_countof(fname) > DragQueryFile((HDROP)wParam, i, fname, _countof(fname))) {
                        len = GetWindowTextLength( GetDlgItem(hDlg, IDC_EDIT1) );
						if ( len > 0 ) { // すでに文字列が入っていた場合は、そこにスペースを加えてから追加
							buf = (char *)malloc( len + lstrlen(fname) + 2 );
							if ( buf ) {
								GetDlgItemText( hDlg, IDC_EDIT1, buf, len+1 );
								lstrcat(buf, " ");
								lstrcat(buf, fname);
								SetDlgItemText(hDlg, IDC_EDIT1, buf);
								safefree( buf );
							}
						}
						else { // まだ文字列が入っていない場合は、そのまま設定
                            SetDlgItemText(hDlg, IDC_EDIT1, fname);
						}
					}
				}
			}
			break;

		case WM_DESTROY:
			// リサイズのための情報を破棄
			delete sizedlg;
			sizedlg = NULL;

			// コンボボックスの情報を破棄
			delete cbq;
			cbq = NULL;

			return TRUE;

		case WM_GETMINMAXINFO:
			// ダイアログの最小サイズを決める
			sizedlg->GetMinMaxInfo(lParam);
			return TRUE;

		case WM_SIZE:
			// リサイズ
			sizedlg->Resize(1);
			return TRUE;

		case WM_COMMAND:
			switch ( LOWORD(wParam) ) {
				case IDOK:
					safefree( *findbuf );
					{
						int len = GetWindowTextLength(GetDlgItem(hDlg, IDC_EDIT1)) + 1;
						*findbuf = zalloc( len );
						if ( *findbuf ) {
							GetDlgItemText( hDlg, IDC_EDIT1, *findbuf, len );
							*hf_up = (BST_CHECKED == SendDlgItemMessage(hDlg, IDC_CHECK_GYAKU, BM_GETCHECK, 0, 0))?1:0;
							*hf_ncs = (BST_CHECKED == SendDlgItemMessage(hDlg, IDC_CHECK_CASESENSITIVE, BM_GETCHECK, 0, 0))?1:0;

							// コンボボックスへ登録
							cbq->Add(*findbuf);

							EndDialog(hDlg, 1);
							return TRUE;
						}
					}
                    MessageBox( hDlg, "メモリが足りません。", "検索", MB_OK | MB_ICONSTOP );					
					// そのまま下へ
				case IDCANCEL:
					EndDialog(hDlg, 0);
					return TRUE;

				case IDC_EDIT1:
					if (HIWORD(wParam) == CBN_EDITCHANGE) {
						EnableWindow(GetDlgItem(hDlg,IDOK),(GetWindowTextLength(GetDlgItem(hDlg, IDC_EDIT1)))?TRUE:FALSE);
					}
					else if (HIWORD(wParam) == CBN_SELCHANGE) {
						EnableWindow(GetDlgItem(hDlg,IDOK), TRUE);
					}
					return TRUE;
			}
			break;
	}
	return FALSE;
}

EXTERN int findDialog( HWND hWnd, char **findbuf_l, BOOL *hf_up_l, BOOL *hf_ncs_l, char *checkbox_l, char *checkboxtip_l )
{
	findbuf = findbuf_l;
	hf_up = hf_up_l;
	hf_ncs = hf_ncs_l;
	checkbox = checkbox_l;
	checkboxtip = checkboxtip_l;
	return (int)DialogBox(hInst, (LPCTSTR)IDD_HELPFIND, hWnd, (DLGPROC)findProc);
}

EXTERN LRESULT CALLBACK HlpProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND hRichEdit;
	static HMODULE hLibRichEdit;
	static HMENU hContextHelpMenu;
	static char *findbuf;
	static BOOL hf_up, hf_ncs;
	static HFONT hFont;
	BOOL   findflag = 0;

	switch (message) {
		case WM_CREATE:
			{
				RECT rc;

				GetClientRect( hWnd, &rc );

				// リッチエディットコントロールの生成
				hLibRichEdit = LoadLibrary_System32( "RICHED20.DLL" );
				hRichEdit = CreateWindowEx( WS_EX_CLIENTEDGE, RICHEDIT_CLASS, "Dynamic Help for Rnsf7", WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NOIME | ES_LEFT | ES_MULTILINE | ES_READONLY | WS_VSCROLL | ES_NOHIDESEL | (helpdoc_open_mode==1?WS_HSCROLL:0),
					                        rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hWnd, (HMENU)IDC_RICHEDIT, hInst, NULL );
				if ( !hRichEdit ) {
					DestroyWindow( hWnd );
				}
				else {
					//URL検出機能を組み込む
					SendMessage(hRichEdit, EM_AUTOURLDETECT, (WPARAM)TRUE, 0);

					// リッチエディットのイベントマスク設定
					LRESULT dwEvent = SendMessage(hRichEdit, EM_GETEVENTMASK, 0, 0);
					dwEvent |= ENM_MOUSEEVENTS | ENM_SELCHANGE | ENM_LINK;
					SendMessage(hRichEdit, EM_SETEVENTMASK, 0, (LPARAM)dwEvent);

					// 標準のフォントを設定
					if ( helpdoc_open_mode == 1 ) {
						hFont = CreateFont(16,0,0,0,FW_MEDIUM,0,0,0,SHIFTJIS_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FIXED_PITCH | FF_DONTCARE, "ＭＳ ゴシック");
					}
					else {
						hFont = CreateFont(18,0,0,0,FW_MEDIUM,0,0,0,SHIFTJIS_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,VARIABLE_PITCH | FF_DONTCARE, "ＭＳ Ｐゴシック");
					}
					SendMessage( hRichEdit, WM_SETFONT, (WPARAM)hFont, 0 );
					if ( rtf ) {
						EDITSTREAM eds;
						eds.dwCookie = 0;
						eds.dwError = 0;
						eds.pfnCallback = (EDITSTREAMCALLBACK)EditStreamCallback;

						// リッチエディットのサイズ上限を 64,000 から適切なサイズへ変更
						Edit_LimitText(hRichEdit, lstrlen(rtf)+16);

						// リッチエディットに rtf の内容を与える
						byteread = 0;
						SendMessage( hRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&eds);
					}

					re_OldProc = (WNDPROC)GetWindowLongPtr( hRichEdit, GWLP_WNDPROC );
					SetWindowLongPtr( hRichEdit, GWLP_WNDPROC, (LONG_PTR)re_NewProc );

					SetFocus( hRichEdit );
				}


				if ( helpdoc_open_mode == 0 ) { // ソースビューモードではファイルの保存を隠す
					EnableMenuItem(GetSubMenu(GetMenu(hWnd),0), IDM_HELPSAVEFILE, MF_BYCOMMAND | MF_ENABLED);
				}

				// コンテキストメニューのロード
				hContextHelpMenu = LoadMenu(hInst,(LPSTR)IDR_CONTEXT_MENUHELP);
			}
			break;

		case WM_NOTIFY:
			if (wParam == (WPARAM)IDC_RICHEDIT) { // リッチエディットからの通知メッセージ
				LPNMHDR lpN = (LPNMHDR)lParam;
				ENLINK *penlk;
				char szURL[2048]; // URL最大長は2048文字まで。それ以上は無反応とする。

				switch (lpN->code) { // URL的な文字列をクリックした場合の処理
					case EN_LINK:
						penlk = (ENLINK *)lParam;
						if (penlk->msg == WM_LBUTTONDOWN) {
							// 文字列が長すぎたら無反応
							if (penlk->chrg.cpMax != -1 && penlk->chrg.cpMax - penlk->chrg.cpMin < _countof(szURL)) {
								SendMessage(hRichEdit, EM_EXSETSEL, 0, (LPARAM)(&(penlk->chrg)));
								if (0 < SendMessage(hRichEdit, EM_GETSELTEXT, 0, (LPARAM)szURL)) {   // 文字列取得
									ShellExecute(hWnd, "open", szURL, NULL, NULL, SW_SHOWNORMAL); // デフォルトブラウザの呼び出し
								}
							}
						}
						break;
				}
			}
			break;

		case WM_SIZE: // ウィンドウのサイズ変更
			{
				RECT rc;

				GetClientRect( hWnd, &rc );
				MoveWindow( hRichEdit, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE );
			}
			break;

		case WM_DESTROY:
			// 各種解放
			if ( hRichEdit ) {
				SetWindowLongPtr( hRichEdit, GWLP_WNDPROC, (LONG_PTR)re_OldProc ); // サブクラス化解除
				DestroyWindow( hRichEdit );	// ウィンドウの破棄
				hRichEdit = NULL;
			}
			if ( hLibRichEdit ) {
				FreeLibrary( hLibRichEdit );
				hLibRichEdit = NULL;
			}
			if ( hContextHelpMenu ) {
				DestroyMenu( hContextHelpMenu );
				hContextHelpMenu = NULL;
			}
			if ( hFont ) {
				DeleteObject( hFont );
				hFont = NULL;
			}
			hHelpWindow = NULL;
			break;

		case WM_CONTEXTMENU:
			if ( (HWND)wParam == hRichEdit ) {
				POINT pt;
				HMENU hMenu = GetSubMenu(hContextHelpMenu, 0);

				if ( hMenu ) {
					GetCursorPos(&pt);
	
					// メニューを表示
					TrackPopupMenuEx(hMenu, 0, pt.x, pt.y, hWnd, NULL);
				}
			}
			else {
				// システムメニューをマウスから扱うための仕掛け
				return DefWindowProc( hWnd, message, wParam, lParam );
			}
			break;

		case WM_COMMAND:
			switch ( LOWORD(wParam) ) {
				case IDM_CLOSE: // 閉じる
					DestroyWindow( hWnd );
					break;

				case IDM_COPY: // コピー（キーボードでCtrl+Cを押すことをエミュレートする）
					SetFocus( hRichEdit );
					Sleep(100);	// なんとなく間を置く
					keybd_event( VK_LCONTROL, 0, 0, 0 );
					keybd_event( 'C',	  0, 0, 0 );
					keybd_event( 'C',	  0, KEYEVENTF_KEYUP, 0 );
					keybd_event( VK_LCONTROL, 0, KEYEVENTF_KEYUP, 0 );
					break;

				case IDM_ALLSELECT: // すべて選択
					SendMessage( hRichEdit, EM_SETSEL, (WPARAM)0, (LPARAM)-1 );  
					break;

				case IDM_HELPSAVEFILE: // ファイルへ保存
					if ( rtf && helpdoc_open_mode == 0 ) {
						char fname[MAX_PATH*2] = "";
						HANDLE hFile;	// ファイルハンドル
						DWORD  wtn;

						if ( saveDialog( hWnd, "リッチテキストファイル(*.rtf)\0*.rtf\0すべてのファイル(*.*)\0*.*\0", fname, _countof(fname), "ヘルプをファイルへ保存", "rtf" ) ) {
							hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
							if ( hFile == INVALID_HANDLE_VALUE ) {
								MessageBox( hWnd, "指定されたファイルを開けません。", "ヘルプをファイルへ保存", MB_OK | MB_ICONSTOP );
							}
							else {
								if ( WriteFile( hFile, rtf, lstrlen(rtf), &wtn, NULL ) ) {
									MessageBox( hWnd, "ヘルプをファイルへ保存しました。", "ヘルプをファイルへ保存", MB_OK | MB_ICONINFORMATION );
								}
								else {
									MessageBox( hWnd, "指定されたファイルへ書き込めませんでした。", "ヘルプをファイルへ保存", MB_OK | MB_ICONSTOP );
								}
								CloseHandle( hFile );
							}
						}
					}
					break;

				case IDM_FINDNEXT: // 次を検索
					if ( findbuf ) {
						// 検索の文字列入力をスキップするためのフラグを設定
						findflag = 1;
					}
					// ただし、まだ一度も検索文字列を入力したことがなければ、新しく入力する
					// そのまま下へ
				case IDM_FIND: // 検索
					hf_ncs = rnsf7RegRead( "hf_ncsh", 0 );
					while(1) {
						if ( findflag || findDialog(hWnd, &findbuf, &hf_up, &hf_ncs, NULL, NULL) ) {
							FINDTEXT ft;
							int pos;

							if ( findbuf ) {
								rnsf7RegWrite( "hf_ncsh", hf_ncs );
								SendMessage( hRichEdit, EM_GETSEL, 0, (LPARAM)(&(ft.chrg.cpMin)) );
								ft.chrg.cpMax = -1;
								ft.lpstrText = findbuf;

								pos = (int)SendMessage( hRichEdit, EM_FINDTEXT, (WPARAM)(FR_DOWN | (hf_ncs?0:FR_MATCHCASE)), (LPARAM)&ft );
								if ( pos != -1 ) { // みつかった
									SendMessage( hRichEdit, EM_SETSEL, (WPARAM)pos, (LPARAM)pos + lstrlen(findbuf) ); // 見つかった文字列を選択
									SendMessage( hRichEdit, EM_SCROLLCARET, 0, 0 ); // カーソルの見える位置までスクロール
								}
								else {
									MessageBox( hWnd, "検索文字列は見つかりませんでした。", "検索", MB_OK | MB_ICONEXCLAMATION );
								}

								// 検索ダイアログを開いていたときは、検索ダイアログを再表示する
								if ( findflag == 0 ) {
                                    continue;
								}
							}
						}
						break;
					}
					break;
			}
			break;

		case WM_ACTIVATE:
			SetFocus( hRichEdit );
			break;

		case WM_HELP: // 多重ヘルプの呼び出しは禁止
			return TRUE;

		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}
