#define	STRICT
#include	<windows.h>
#include	<windowsx.h>
#include	<commctrl.h>
#include	<tchar.h>
#include	"kctrl.h"
#include	"fepbox.h"
#include	"fep.h"

extern "C" {
	LRESULT CALLBACK	FepBoxWndProc( HWND hWnd,  UINT msg, WPARAM wParam, LPARAM lParam ) ;
} ;

extern	DWORD	PreWait ;
extern	DWORD	PostWait ;
extern	DWORD	F_QuickSend ;
extern	DWORD	F_JIS ;
extern	FepCtrl	g_Fep ;

#define		is_shift_pressed()	(0x80 & GetKeyState( VK_SHIFT ))

static	HWND	g_hWndParent = 0 ;	/* Parentのウィンドウハンドル */
static	DWORD	g_dSendMode = 0 ;

class FepBox {
private:
	HWND	m_hWnd ;			/* FEP変換領域のウィンドウハンドル */
	DWORD	m_cw ;				/* FEP変換領域の幅 */
	BOOL	m_fSend ;			/* Parentへの送信中フラグ */
	BOOL	m_fAddItem ;		/* 辞書項目追加中フラグ */
	CHAR	m_sText[ 128 ] ;	/* 確定文字列 */
	DWORD	m_dLen ;			/* 確定文字列内の挿入ポイント */

	void	CodeConvSub( LPCTSTR org ) ;
	BOOL	CopyToClipboard( LPCTSTR org ) ;
	BOOL	GetFromClipboard( LPTSTR unicode, DWORD dstsize ) ;
	BOOL	is_text() const { return m_sText[0] ; }
	void	AddText( LPCSTR sjis ) ;
	void	AddTextNoSend( LPCSTR sjis ) ;
	void	SendText() ;

public:
	FepBox() ;
	~FepBox() ;

	BOOL	Create() { return FALSE ; }

	inline	void	WMCreate( HWND hWnd ) ;
	inline	void	WMPaint() ;
	inline	void	WMChar( TCHAR c, LONG keydata ) ;
	inline	void	WMKeyDown( int nVirtKey, LONG lKeyData ) ;
	inline	BOOL	WMSysKeyDown( int nVirtKey, LONG lKeyData ) ;

	LRESULT	WMCommand( HWND hWnd, UINT msg, WPARAM wp, LPARAM lp ) ;

	void	SendTextToParent( LPCSTR sjis ) ;
	void	StartAddItem() ;

	inline	BOOL	KeySpace() ;
	inline	BOOL	KeyBS() ;
	inline	BOOL	KeyDEL() ;
	inline	BOOL	KeyESC() ;
	inline	BOOL	KeyEnter() ;
	inline	BOOL	KeyLeft() ;
	inline	BOOL	KeyRight() ;
	inline	BOOL	KeyUp() ;
	inline	BOOL	KeyDown() ;
	void	Hide( BOOL fDirect ) const ;
	BOOL	HideIfNeeded( BOOL fForce = FALSE ) const ;

	inline	void	WMMouseDown( WPARAM wParam, LPARAM lParam ) ;

	BOOL	is_sending() const { return m_fSend ; }
} ;
typedef	FepBox	*PFepBox ;

FepBox::FepBox()
{
	m_fSend = FALSE ;
	m_fAddItem = FALSE ;
	m_cw = 30 ;
	m_sText[0] = 0 ;
	m_dLen = 0 ;
}

FepBox::~FepBox()
{
}

void
FepBox::WMCreate( HWND hWnd )
{
	RECT	rect ;

	m_hWnd = hWnd ;
	GetClientRect( hWnd, &rect ) ;
	DWORD rc = GetFontHW() ;
	m_cw = rect.right / GetFontHW() ;
}

void
FepBox::WMPaint()
{
	PAINTSTRUCT	ps ;
	HDC			hDC ;
	RECT		rect ;
	DWORD		len = 0 ;
	TCHAR		unicode[ 256 ] ;
	CHAR		sjis[ 256 ], *dstptr = sjis ;

	hDC = ::BeginPaint( m_hWnd, &ps ) ;
	GetClientRect( m_hWnd, &rect ) ;
	len = strlen( m_sText ) ;
	if ( m_fAddItem ) {
		strcpy( dstptr, "登録:" ), dstptr += 5 ;
		strcpy( dstptr, m_sText ), dstptr += len ;
		strcpy( dstptr, ":よみ=" ), dstptr += 6 ;
		g_Fep.get_fep_line( dstptr, m_cw - strlen( sjis ) ) ;
	} else {
		memcpy( dstptr, m_sText, m_dLen ), dstptr += m_dLen ;
		*dstptr++ = '|' ;
		if ( len > m_dLen ) {
			strcpy( dstptr, &m_sText[ m_dLen ] ), dstptr += len - m_dLen ;
		}
		*dstptr++ = '>' ;
		*dstptr   = 0 ;
		g_Fep.get_fep_line( dstptr, m_cw - strlen( sjis ) ) ;
	}
	sjis2unicode( (LPBYTE) sjis, unicode, sizeof unicode ) ;
	rect.top += 2 ;		rect.left += 2 ;
	rect.bottom -= 2 ;	rect.right -= 2 ;
	KDrawText( hDC, unicode, -1, &rect, 0 ) ;
	::EndPaint( m_hWnd, &ps ) ;
}

void
FepBox::WMMouseDown( WPARAM wParam, LPARAM lParam )
{
	if ( GetFocus() != m_hWnd ) {
		SetFocus( m_hWnd ) ;
	}
}

void
FepBox::WMChar( TCHAR chCharCode, LONG lKeyData )
{
	CHAR	sjis[ 128 ] ;

	switch ( chCharCode ) {
	case TEXT(' '):
		KeySpace() ;
		break ;
	case TEXT('U') - TEXT('@'):
	case TEXT('I') - TEXT('@'):
	case TEXT('O') - TEXT('@'):
	case TEXT('P') - TEXT('@'):
		if ( m_fAddItem ) {
			/* 辞書登録中は無効 */
			break ;
		}
		g_Fep.set_muhenkan( (CHAR) chCharCode ) ;
		::InvalidateRect( m_hWnd, NULL, TRUE ) ;
		break ;
	default:
		if ( chCharCode > TEXT(' ') ) {
			if ( g_Fep.is_muhenkan() ) {
				/* 無変換モード内容を出力する */
				g_Fep.drop_muhenkan( sjis ) ;
				AddText( sjis ) ;
			} else if ( g_Fep.is_converting() ) {
				/* 変換内容を出力する */
				g_Fep.confirm() ;
				g_Fep.drop_all( sjis ) ;
				AddText( sjis ) ;
			}
			g_Fep.addchar( (CHAR) chCharCode ) ;
			::InvalidateRect( m_hWnd, NULL, TRUE ) ;
		}
		break ;
	}
}

void
FepBox::WMKeyDown( int nVirtKey, LONG lKeyData )
{
	LPARAM	lParam ;
	BOOL	ret = TRUE ;
	BOOL	fShift = 0x80 & GetKeyState( VK_SHIFT ) ;
	BOOL	fControl = 0x80 & GetKeyState( VK_CONTROL ) ;

	switch ( nVirtKey ) {
	case VK_BACK:		ret = KeyBS() ;		break ;
	case VK_DELETE:		ret = KeyDEL() ;	break ;
	case VK_ESCAPE:		ret = KeyESC() ;	break ;
	case VK_RETURN:		ret = KeyEnter() ;	break ;
	case VK_LEFT:		ret = KeyLeft() ;	break ;
	case VK_RIGHT:		ret = KeyRight() ;	break ;
	case VK_UP:			ret = KeyUp() ;		break ;
	case VK_DOWN:		ret = KeyDown() ;	break ;

	case VK_HOME: case VK_END: case VK_PRIOR: case VK_NEXT:
		ret = FALSE ;
		break ;
	case TEXT('C'):	case TEXT('X'):	case TEXT('V'):
		if ( !fShift && fControl ) {
			ret = FALSE ;
		}
		break ;
	}
	if ( !ret && g_hWndParent && (g_dSendMode >= IDM_FEPOK) ) {
		lParam = nVirtKey ;
		lParam |= fShift ? 0x8000 : 0 ;
		lParam |= fControl ? 0x4000 : 0 ;
		SendMessage( g_hWndParent, WM_COMMAND, IDM_FEPKEY, lParam ) ;
	}
}

BOOL
FepBox::WMSysKeyDown( int nVirtKey, LONG lKeyData )
{
	if ( !(lKeyData & 0x20000000) ) {
		return FALSE ;
	} else if ( (lKeyData & 0x0000FFFF) > 1 ) {
		return FALSE ;
	}
	switch ( nVirtKey ) {
	case TEXT(' '):
		/* ALT+SPACEが押された場合 */
		Hide( TRUE ) ;
		return TRUE ;
	case TEXT('A'):
		/* ALT+Aが押された場合 */
		StartAddItem() ;
		return TRUE ;
	}
	return FALSE ;
}

LRESULT
FepBox::WMCommand( HWND hWnd, UINT msg, WPARAM wp, LPARAM lp )
{
	return 0 ;
}

/*
 * コード変換用のサブルーチン
 */
void
FepBox::CodeConvSub( LPCTSTR ptr )
{
	TCHAR	c ;

	while ( c = *ptr++ ) {
		unicode_jis_char( c ) ;
	}
	unicode_jis_char( 0 ) ;
}

/*
 * SJIS文字列をコード変換してクリップボードに格納する
 */
BOOL
FepBox::CopyToClipboard( LPCTSTR unicode )
{
	LPTSTR		ptr ;
	DWORD		size ;

	if ( F_JIS ) {
		/* コード変換に必要なメモリ数を得る */
		setup_codeconv( 0 ) ;
		CodeConvSub( unicode ) ;
		size = getcount_codeconv() ;
		if ( !size ) {
			/* 変換対象がない場合 */
			return FALSE ;
		}
	} else {
		size = _tcslen( unicode ) + 1 ;
	}
	size *= sizeof (TCHAR) ;
	/* クリップボードオープン */
	if ( !OpenClipboard( m_hWnd ) ) {
		return FALSE ;
	} else if ( !EmptyClipboard() ) {
		CloseClipboard() ;
		return FALSE ;
	}
	/* 必要な分だけメモリ確保を行う */
#ifdef	_WIN32_WCE
	HLOCAL	hGMem = LocalAlloc( LMEM_FIXED, size ) ;
	ptr = (LPTSTR) hGMem ;
#else	/* _WIN32_WCE */
	HGLOBAL	hGMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size ) ;
	ptr = (LPTSTR) GlobalLock( hGMem ) ;
#endif	/* _WIN32_WCE */

	if ( F_JIS ) {
		/* UNICODE->JIS変換し結果をメモリに書き込む */
		setup_codeconv( ptr ) ;
		CodeConvSub( unicode ) ;
	} else {
		_tcscpy( ptr, unicode ) ;
	}
	/* クリップボードに結果をセットする */
	SetClipboardData( CF_UNICODETEXT, hGMem ) ;
#ifndef	_WIN32_WCE
	GlobalUnlock( hGMem ) ;
#endif	/* _WIN32_WCE */
	/* クリップボードクローズ */
	CloseClipboard() ;
	return TRUE ;
}

/*
 * クリップボードからUnicode文字列を取得する
 */
BOOL
FepBox::GetFromClipboard( LPTSTR unicode, DWORD dstsize )
{
	TCHAR	c ;
	DWORD	cbsize ;
	LPCTSTR	cbptr, src ;
	BOOL	ret = FALSE ;

	/* 格納先の領域サイズを文字数に変換する */
	dstsize /= sizeof (TCHAR) ;
	if ( !dstsize ) {
		return FALSE ;
	}
	/* クリップボードをオープンする */
	if ( !OpenClipboard( m_hWnd ) ) {
		return FALSE ;
	}
#ifdef	_WIN32_WCE
	HLOCAL	hGMem = GetClipboardData( CF_UNICODETEXT ) ;
	cbsize = LocalSize( hGMem ) ;
	cbptr = (LPCTSTR) hGMem ;
#else	/* _WIN32_WCE */
	HGLOBAL	hGMem = GetClipboardData( CF_UNICODETEXT ) ;
	cbsize = GlobalSize( hGMem ) ;
	cbptr = (LPCTSTR) GlobalLock( hGMem ) ;
#endif	/* _WIN32_WCE */
	if ( !cbsize ) {
		goto exit ;
	}
	/* コード変換開始 */
	setup_codeconv( unicode ) ;
	src = cbptr ;
	cbsize /= sizeof (TCHAR) ;
	do {
		c = *src++ ;
		jis_unicode_char( c ) ;
	} while ( -- cbsize && -- dstsize && c ) ;
	jis_unicode_char( 0 ) ;
	ret = TRUE ;

exit:
#ifndef	_WIN32_WCE
	GlobalUnlock( hGMem ) ;
#endif	/* _WIN32_WCE */
	CloseClipboard() ;
	return ret ;
}

/*
 * 文字列を入力対象ウィンドウに送る
 */
void
FepBox::SendTextToParent( LPCSTR sjis )
{
	COPYDATASTRUCT	cds ;
	HWND			hWnd ;
	TCHAR			unicode[ 128 ], c, *ptr ;

	if ( !g_hWndParent ) {
		return ;
	}
	sjis2unicode( (LPBYTE) sjis, unicode, sizeof unicode ) ;
	/* 送信中はHIDEしない */
	m_fSend = TRUE ;
	if ( g_dSendMode == IDM_UNKNOWN ) {
		FepSetParent( GetCenterWindow(), FALSE ) ;
		if ( g_dSendMode == IDM_UNKNOWN ) {
			g_dSendMode = 0 ;
		}
	}
	/* 送信方法に応じて送信を行う */
	if ( g_dSendMode == IDM_FEPOK ) {
		/* WM_COPYDATAで送信する場合 */
		cds.dwData = 0 ;
		cds.cbData = (_tcslen( unicode ) + 1) * sizeof (TCHAR) ;
		cds.lpData = (PBYTE) unicode ;
		SendMessage( g_hWndParent, WM_COPYDATA, (WPARAM) m_hWnd, (LPARAM) &cds ) ;
	} else if ( g_dSendMode == IDM_FEPCHAR ) {
		/* IDM_FEPCHARで送信する場合 */
		ptr = unicode ;
		while ( c = *ptr++ ) {
			SendMessage( g_hWndParent, WM_COMMAND, (WPARAM) IDM_FEPCHAR, (LPARAM) c ) ;
		}
	} else if ( g_dSendMode == IDM_WMCHAR ) {
		/* WM_CHARで送信する場合 */
		ptr = unicode ;
		while ( c = *ptr++ ) {
			SendMessage( g_hWndParent, WM_CHAR, (WPARAM) c, 0 ) ;
		}
	} else if ( g_dSendMode != IDM_UNKNOWN ) {
		/* クリップボードにコピーする */
		CopyToClipboard( unicode ) ;
		/* 送信先のウィンドウをアクティブにする */
		hWnd = GetForegroundWindow() ;
		SetForegroundWindow( g_hWndParent ) ;
		SetActiveWindow( g_hWndParent ) ;
		if ( g_dSendMode == IDM_EXPLORE ) {
			Sleep( 100 ) ;
			SetForegroundWindow( hWnd ) ;
			SetActiveWindow( m_hWnd ) ;
			Sleep( 100 ) ;
			SetForegroundWindow( g_hWndParent ) ;
			SetActiveWindow( g_hWndParent ) ;
		}
		Sleep( PreWait * 100 ) ;
		if ( GetForegroundWindow() == hWnd ) {
			MessageBeep( MB_ICONASTERISK ) ;
		}
		if ( !*unicode || !_tcscmp( unicode, TEXT("\n") ) ) {
			keybd_event( VK_RETURN, 0, 0, 0 ) ;
			keybd_event( VK_RETURN, 0, KEYEVENTF_KEYUP, 0 ) ;
		} else {
			/* ペースト用のキーコードを送信する */
			keybd_event( VK_CONTROL, 0, 0, 0 ) ;
			keybd_event( 0x56, 0, 0, 0 ) ;
			keybd_event( 0x56, 0, KEYEVENTF_KEYUP, 0 ) ;
			keybd_event( VK_CONTROL, 0, KEYEVENTF_KEYUP, 0 ) ;
		}
		Sleep( PostWait * 100 ) ;
		/* FEPウィンドウをアクティブにする */
		SetForegroundWindow( hWnd ) ;
		SetActiveWindow( m_hWnd ) ;
	}
	m_fSend = FALSE ;
}

/*
 * Spaceキー押下時の処理
 */
BOOL
FepBox::KeySpace()
{
	if ( m_fAddItem ) {
		/* 辞書登録中は無効 */
		return TRUE ;
	} else if ( g_Fep.is_muhenkan() ) {
		/* 無変換モードをクリアする */
		g_Fep.set_muhenkan( 0 ) ;
	}
	if ( g_Fep.is_input() ) {
		g_Fep.space() ;
	} else if ( is_shift_pressed() ) {
		AddText( "　" ) ;
	} else {
		AddText( " " ) ;
	}
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	return TRUE ;
}

/*
 * BSキー押下時の処理
 */
BOOL
FepBox::KeyBS()
{
	DWORD	len ;
	CHAR	sjis[ 128 ] ;
	TCHAR	unicode[ 128 ] ;

	if ( g_Fep.is_muhenkan() ) {
		/* 無変換モードをクリアする */
		g_Fep.set_muhenkan( 0 ) ;
	} else if ( g_Fep.is_converting() ) {
		/* 変換候補をクリアする */
		g_Fep.clear_proposed() ;
	} else if ( g_Fep.is_input() ) {
		/* 文字を後ろから削除する */
		g_Fep.chop() ;
	} else if ( m_fAddItem ) {
		/* 辞書登録中は無効 */
		return TRUE ;
	} else if ( is_text() ) {
		/* 確定済み文字列を削除する */
		memcpy( sjis, m_sText, m_dLen ), sjis[ m_dLen ] = 0 ;
		sjis2unicode( (LPBYTE) sjis, unicode, sizeof unicode ) ;
		unicode[ _tcslen( unicode ) - 1 ] = 0 ;
		unicode2sjis( unicode, (LPBYTE) sjis, sizeof sjis ) ;
		len = strlen( sjis ) ;
		strcpy( &sjis[ len ], &m_sText[ m_dLen ] ) ;
		strcpy( m_sText, sjis ) ;
		m_dLen = len ;
	} else {
		return FALSE ;
	}
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	return TRUE ;
}

/*
 * DELキー押下時の処理
 */
BOOL
FepBox::KeyDEL()
{
	DWORD	len ;
	CHAR	sjis[ 128 ] ;
	TCHAR	unicode[ 128 ] ;

	if ( g_Fep.is_muhenkan() ) {
		/* 無変換モードをクリアする */
		return TRUE ;
	} else if ( g_Fep.is_converting() ) {
		/* 変換候補をクリアする */
		return TRUE ;
	} else if ( g_Fep.is_input() ) {
		/* 文字を後ろから削除する */
		return TRUE ;
	} else if ( m_fAddItem ) {
		/* 辞書登録中は無効 */
		return TRUE ;
	} else if ( is_text() ) {
		/* 確定済み文字列を削除する */
		len = strlen( m_sText ) ;
		if ( m_dLen >= len ) {
			return TRUE ;
		}
		strcpy( sjis, &m_sText[ m_dLen ] ) ;
		sjis2unicode( (LPBYTE) sjis, unicode, sizeof unicode ) ;
		unicode2sjis( &unicode[ 1 ], (LPBYTE) sjis, sizeof sjis ) ;
		strcpy( &m_sText[ m_dLen ], sjis ) ;
	} else {
		return FALSE ;
	}
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	return TRUE ;
}

/*
 * ESCキー押下時の処理
 */
BOOL
FepBox::KeyESC()
{
	if ( m_fAddItem ) {
		/* 辞書登録をキャンセルする */
		m_fAddItem = FALSE ;
		/* 入力文字列をクリアする */
		g_Fep.clear() ;
	} else if ( g_Fep.is_muhenkan() ) {
		/* 無変換モードをクリアする */
		g_Fep.set_muhenkan( 0 ) ;
	} else if ( g_Fep.is_converting() ) {
		/* 変換候補をクリアする */
		g_Fep.clear_proposed() ;
	} else if ( g_Fep.is_input() ) {
		/* 入力中のものを全てクリアする */
		g_Fep.clear() ;
		if ( HideIfNeeded() ) {
			/* Window自体を閉じる */
			return TRUE ;
		}
	} else if ( is_text() ) {
		/* 確定文字列をクリアする */
		m_sText[0] = 0 ;
		m_dLen = 0 ;
		if ( HideIfNeeded() ) {
			/* Window自体を閉じる */
			return TRUE ;
		}
	} else {
		/* Window自体を閉じる */
		Hide( FALSE ) ;
		return TRUE ;
	}
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	return TRUE ;
}

/*
 * Enterキー押下時の処理
 */
BOOL
FepBox::KeyEnter()
{
	CHAR	sjis[ 128 ] ;

	if ( m_fAddItem ) {
		/* 辞書登録を行う */
		m_fAddItem = FALSE ;
		/* 辞書に登録する */
		g_Fep.add_item( m_sText ) ;
		/* 入力文字列をクリアする */
		g_Fep.clear() ;
	} else if ( g_Fep.is_muhenkan() ) {
		/* 無変換モード内容を出力する */
		g_Fep.drop_muhenkan( sjis ) ;
		AddText( sjis ) ;
	} else if ( g_Fep.is_converting() || g_Fep.is_input() ) {
		g_Fep.confirm() ;
		g_Fep.drop_all( sjis ) ;
		AddText( sjis ) ;
		if ( HideIfNeeded() ) {
			/* Window自体を閉じる */
			return TRUE ;
		}
	} else if ( is_text() ) {
		SendText() ;
		if ( HideIfNeeded() ) {
			/* Window自体を閉じる */
			return TRUE ;
		}
	} else {
		SendTextToParent( "\n" ) ;
	}
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	return TRUE ;
}

BOOL
FepBox::KeyDown()
{
	CHAR	sjis[ 128 ] ;

	if ( m_fAddItem ) {
		/* 辞書登録中は無効 */
		return TRUE ;
	} else if ( g_Fep.is_muhenkan() ) {
		/* 無変換モード内容を出力する */
		g_Fep.drop_muhenkan( sjis ) ;
		AddText( sjis ) ;
	} else if ( g_Fep.is_converting() ) {
		g_Fep.confirm() ;
		g_Fep.drop_conv( sjis ) ;
		AddText( sjis ) ;
	} else if ( g_Fep.is_input() ) {
		g_Fep.drop_top( sjis ) ;
		AddText( sjis ) ;
	} else if ( is_text() ) {
		SendText() ;
	} else {
		return FALSE ;
	}
	if ( g_Fep.is_muhenkan() ) {
		/* 無変換モードをクリアする */
		g_Fep.set_muhenkan( 0 ) ;
	}
	if ( g_Fep.is_input() ) {
		g_Fep.space() ;
	}
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	return TRUE ;
}

BOOL
FepBox::KeyLeft()
{
	CHAR	sjis[ 128 ] ;
	TCHAR	unicode[ 128 ] ;

	if ( m_fAddItem ) {
		/* 辞書登録中は無効 */
		return TRUE ;
	} else if ( g_Fep.is_muhenkan() ) {
		/* 無変換モードをクリアする */
		g_Fep.set_muhenkan( 0 ) ;
	} else if ( g_Fep.is_converting() ) {
		/* 変換中の場合 */
		g_Fep.shrink() ;
		g_Fep.space() ;
	} else if ( g_Fep.is_input() ) {
		/* 変換対象が残っている場合 */
		return TRUE ;
	} else if ( is_text() ) {
		/* 確定文字列が残っている場合 */
		if ( !m_dLen ) {
			return TRUE ;
		}
		memcpy( sjis, m_sText, m_dLen ), sjis[ m_dLen ] = 0 ;
		sjis2unicode( (LPBYTE) sjis, unicode, sizeof unicode ) ;
		unicode[ _tcslen( unicode ) - 1 ] = 0 ;
		unicode2sjis( unicode, (LPBYTE) sjis, sizeof sjis ) ;
		m_dLen = strlen( sjis ) ;
	} else {
		return FALSE ;
	}
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	return TRUE ;
}

BOOL
FepBox::KeyRight()
{
	DWORD	len ;
	CHAR	sjis[ 128 ] ;
	TCHAR	unicode[ 128 ] ;

	if ( m_fAddItem ) {
		/* 辞書登録中は無効 */
		return TRUE ;
	} else if ( g_Fep.is_muhenkan() ) {
		/* 無変換モードをクリアする */
		g_Fep.set_muhenkan( 0 ) ;
	} else if ( g_Fep.is_converting() ) {
		/* 変換中の場合 */
		g_Fep.grow() ;
		g_Fep.space() ;
	} else if ( g_Fep.is_input() ) {
		/* 変換対象が残っている場合 */
		return TRUE ;
	} else if ( is_text() ) {
		/* 確定文字列が残っている場合 */
		len = strlen( m_sText ) ;
		if ( m_dLen >= len ) {
			return TRUE ;
		}
		strcpy( sjis, &m_sText[ m_dLen ] ) ;
		sjis2unicode( (LPBYTE) sjis, unicode, sizeof unicode ) ;
		unicode2sjis( &unicode[ 1 ], (LPBYTE) sjis, sizeof sjis ) ;
		m_dLen = len - strlen( sjis ) ;
	} else {
		return FALSE ;
	}
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	return TRUE ;
}

BOOL
FepBox::KeyUp()
{
	if ( m_fAddItem ) {
		/* 辞書登録中は無効 */
		return TRUE ;
	} else if ( g_Fep.is_muhenkan() ) {
		/* 無変換モードをクリアする */
		g_Fep.set_muhenkan( 0 ) ;
	} else if ( g_Fep.is_input() ) {
		g_Fep.conv_prev() ;
	} else if ( is_text() ) {
		return TRUE ;
	} else {
		return FALSE ;
	}
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	return TRUE ;
}

/*
 * 確定文字列に文字列を追加し必要があれば送信する
 */
void
FepBox::AddText( LPCSTR sjis )
{
	AddTextNoSend( sjis ) ;
	if ( F_QuickSend ) {
		SendText() ;
	}
}

/*
 * 確定文字列に文字列を追加する
 */
void
FepBox::AddTextNoSend( LPCSTR sjis )
{
	CHAR	buf[ 128 ] ;
	DWORD	len1 = strlen( m_sText ) ;
	DWORD	len2 = strlen( sjis ) ;

	if ( len2 && len1 + len2 < sizeof m_sText - 10 ) {
		strcpy( buf, &m_sText[ m_dLen ] ) ;
		strcpy( &m_sText[ m_dLen ], sjis ) ;
		m_dLen += len2 ;
		strcpy( &m_sText[ m_dLen ], buf ) ;
	}
}

/*
 * 確定文字列を送信する
 */
void
FepBox::SendText()
{
	SendTextToParent( m_sText ) ;
	m_sText[0] = 0 ;
	m_dLen = 0 ;
}

/*
 * 辞書登録を開始する
 */
void
FepBox::StartAddItem()
{
	CHAR	sjis[ 128 ] ;
	TCHAR	unicode[ 128 ] ;

	if ( m_fAddItem ) {
		return ;
	} else if ( g_Fep.is_muhenkan() ) {
		/* 無変換モード内容を出力する */
		g_Fep.drop_muhenkan( sjis ) ;
		AddTextNoSend( sjis ) ;
	} else if ( g_Fep.is_converting() || g_Fep.is_input() ) {
		g_Fep.confirm() ;
		g_Fep.drop_all( sjis ) ;
		AddTextNoSend( sjis ) ;
	} else if ( !is_text() ) {
		memset( unicode, 0, sizeof unicode ) ;
		if ( GetFromClipboard( unicode, sizeof unicode - sizeof (TCHAR) ) ) {
			unicode2sjis( unicode, (LPBYTE) sjis, sizeof sjis ) ;
			AddTextNoSend( sjis ) ;
		}
	}
	if ( !is_text() ) {
		/* 項目が入力されていない場合は無視 */
		return ;
	}
	m_fAddItem = TRUE ;
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
}

void
FepBox::Hide( BOOL fDirect ) const
{
	HWND	hWndApp ;
	HWND	hWndMain = GetParent( m_hWnd ) ;

	/* fDirectにしたがって親ウィンドウにアイコンの変更を通知する */
	if ( fDirect ) {
		PostMessage( hWndMain, WM_COMMAND, IDM_FEPSETMODE, 1 ) ;
	}
	if ( g_dSendMode >= IDM_FEPOK ) {
		hWndApp = GetParent( g_hWndParent ) ;
		if ( !hWndApp ) {
			hWndApp = g_hWndParent ;
		}
	} else {
		hWndApp = g_hWndParent ;
	}
	SetForegroundWindow( hWndApp ) ;
	SetActiveWindow( hWndApp ) ;
	ShowWindow( hWndMain, SW_HIDE ) ;
}

BOOL
FepBox::HideIfNeeded( BOOL fForce ) const
{
	if ( (g_dSendMode < IDM_FEPOK) && (g_dSendMode != IDM_PEXCEL) ) {
		return FALSE ;
	} else if ( fForce || !is_text() ) {
		Hide( FALSE ) ;
		return TRUE ;
	}
	return FALSE ;
}

BOOL
FepBoxRegisterClass( HINSTANCE hInst )
{
	WNDCLASS	wc ;

	wc.style         = NULL ;
	wc.lpfnWndProc   = (WNDPROC) FepBoxWndProc ;
	wc.cbClsExtra    = 0 ;
	wc.cbWndExtra    = 4 ;
	wc.hInstance     = hInst ;
	wc.hIcon         = NULL ;
	wc.hCursor       = NULL ;
	wc.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH ) ;
	wc.lpszMenuName  = 0 ;
	wc.lpszClassName = CTRL_FEPBOX ;
	return ::RegisterClass( &wc ) ;
}

LRESULT CALLBACK
FepBoxWndProc( HWND hWnd,  UINT msg, WPARAM wParam, LPARAM lParam )
{
	PFepBox	pWindow ;

	if ( msg == WM_CREATE ) {
		pWindow = new FepBox() ;
		SetWindowLong( hWnd, 0, (LONG) pWindow ) ;
		pWindow->WMCreate( hWnd ) ;
		return 0 ;
	}
	pWindow = (PFepBox) GetWindowLong( hWnd, 0 ) ;
	switch ( msg ) {
	case WM_DESTROY:
		delete pWindow ;
		SetWindowLong( hWnd, 0, (LONG) 0 ) ;
		break ;
	case WM_COMMAND:
		return pWindow->WMCommand( hWnd, msg, wParam, lParam ) ;
	case WM_LBUTTONDOWN:	pWindow->WMMouseDown( wParam, lParam ) ;		break ;
	case WM_PAINT:			pWindow->WMPaint() ;		break ;
	case WM_CHAR:			pWindow->WMChar( (TCHAR) wParam, lParam ) ;		break ;
	case WM_KEYDOWN:		pWindow->WMKeyDown( (int) wParam, lParam ) ;	break ;
	case WM_SYSKEYDOWN:
		if ( !pWindow->WMSysKeyDown( (int) wParam, lParam ) ) {
			return DefWindowProc( hWnd, msg, wParam, lParam ) ;
		}
		break ;
	case WM_GETDLGCODE:		return DLGC_WANTALLKEYS ;

	case FEP_ISSENDING:		return pWindow->is_sending() ;
	default:
		return DefWindowProc( hWnd, msg, wParam, lParam ) ;
	}
	return 0 ;
}

HWND
GetCenterWindow()
{
	POINT	point ;

	point.x = GetSystemMetrics( SM_CXSCREEN ) / 2 ;
	point.y = GetSystemMetrics( SM_CYSCREEN ) / 2 ;
	return WindowFromPoint( point ) ;
}

/*
 * 入力対象となるウィンドウを設定する
 */
void
FepSetParent( HWND hWnd, BOOL bForce )
{
	TCHAR	tmpbuf[ 128 ] ;
	BOOL	bUnknown = FALSE ;
	HWND	hWndTmp, hWndPrev = hWnd ;

	g_hWndParent = hWnd ;
	if ( !bForce ) {
		while ( 1 ) {
			*tmpbuf = 0 ;
			GetClassName( g_hWndParent, tmpbuf, 128 ) ;
			if ( !_tcsicmp( tmpbuf, TEXT("BOOKWIN") ) ) {
				g_dSendMode = IDM_PEXCEL ;
				return ;
			} else if ( !_tcsicmp( tmpbuf, TEXT("Pocket Word") ) ) {
				g_dSendMode = 0 ;
				return ;
			} else if ( !_tcsicmp( tmpbuf, TEXT("Explore") ) ) {
				g_dSendMode = 1 ;
				return ;
			} else if ( !_tcsicmp( tmpbuf, TEXT("Calendar") ) ) {
				g_dSendMode = 0 ;
				return ;
			} else if ( !_tcsicmp( tmpbuf, TEXT("Dialog") ) ) {
				*tmpbuf = 0 ;
				GetWindowText( g_hWndParent, tmpbuf, 128 ) ;
				if ( !_tcsicmp( tmpbuf, TEXT("Confirm Name") ) ) {
					g_dSendMode = 0 ;
					return ;
				} else if ( !*tmpbuf ) {
					goto next ;
				}
//				MessageBox( NULL, tmpbuf, TEXT("(2)"), MB_OK ) ;
				hWndTmp = GetParent( g_hWndParent ) ;
				if ( hWndTmp ) {
					*tmpbuf = 0 ;
					GetClassName( hWndTmp, tmpbuf, 128 ) ;
					if ( !_tcsicmp( tmpbuf, TEXT("Calendar") ) ) {
						g_dSendMode = 0 ;
						g_hWndParent = bUnknown ? hWndPrev : g_hWndParent ;
						return ;
					} else if ( !_tcsicmp( tmpbuf, TEXT("Tasks Application") ) ) {
						g_dSendMode = 0 ;
						g_hWndParent = bUnknown ? hWndPrev : g_hWndParent ;
						return ;
					} else if ( !_tcsicmp( tmpbuf, TEXT("Contacts") ) ) {
						g_dSendMode = 0 ;
						return ;
					} else if ( !_tcsicmp( tmpbuf, TEXT("Dialog") ) ) {
						bUnknown = TRUE ;
					} else if ( !_tcsicmp( tmpbuf, TEXT("BOOKWIN") ) ) {
						/* Pocket Excel Find Dialog */
						g_dSendMode = 0 ;
						return ;
					} else {
//						MessageBox( NULL, tmpbuf, TEXT("(1)"), MB_OK ) ;
					}
				}
			} else if ( !_tcsicmp( tmpbuf, TEXT("ASForm") ) ) {
			} else if ( !_tcsncmp( tmpbuf, TEXT("ATL:"), 4 ) ) {
			} else {
//				MessageBox( NULL, tmpbuf, TEXT("(4)"), MB_OK ) ;
			}
next:
			hWndPrev = g_hWndParent ;
			g_hWndParent = GetParent( g_hWndParent ) ;
			if ( !g_hWndParent ) {
				if ( !_tcsicmp( tmpbuf, TEXT("static") ) ) {
					g_dSendMode = IDM_UNKNOWN ;
				} else {
					g_dSendMode = 0 ;
				}
				g_hWndParent = hWndPrev ;
//				wsprintf( tmpbuf2, TEXT("%d"), g_hWndParent ) ;
//				MessageBox( NULL, tmpbuf2, tmpbuf, MB_OK ) ;
				break ;
			}
		}
	}
	g_dSendMode = SendMessage( g_hWndParent, WM_COMMAND, IDM_FEPOK, 0 ) ;
}

FepCtrl	g_Fep ;				/* FEP制御オブジェクト */
FepDict	g_Dict ;			/* 辞書オブジェクト */

/*
 * 辞書を読み込む
 */
BOOL
FepSetDict( LPCTSTR dict_path )
{
	/* 辞書ファイルをクローズする */
	g_Dict.close() ;
	/* 辞書ファイル名をセットする */
	g_Dict.set( dict_path ) ;
	/* トップキーを読み込む */
	if ( !g_Dict.read_offset() ) {
		return FALSE ;
	}
	/* FEPに辞書をアタッチする */
	g_Fep.set_dict( &g_Dict ) ;
	return TRUE ;
}
