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

class LineEdit {
protected:
	HWND	m_hWnd ;
	HWND	m_hWndParent ;		/* コントロールの親ウィンドウ */
	WORD	m_idCtrl ;			/* コントロールの識別番号 */
	TCHAR	m_sText[ 128 ] ;
	DWORD	m_cw, m_ch ;		/* 半角文字の幅と高さ */
	BOOL	m_fModify ;			/* 変更が加えられた場合*真* */
	BOOL	m_fOnFocus ;		/* フォーカスがある場合 */
	DWORD	m_dMaxWidth ;		/* 入力可能な領域の幅 */
	BOOL	m_fKanji ;

public:
	LineEdit() ;
	~LineEdit() ;

	BOOL	Create() { return FALSE ; }

	void	WMCreate( HWND hWnd, LPCREATESTRUCT lpcs ) ;
	void	WMSetFocus() ;
	void	WMKillFocus( HWND hWnd ) ;
	void	WMMouseDown( WPARAM wParam, LPARAM lParam ) ;
	void	WMPaint() ;
	void	WMChar( TCHAR c, LONG keydata ) ;
	void	WMKeyDown( int nVirtKey, LONG lKeyData ) ;
	BOOL	WMSysKeyDown( int nVirtKey, LONG lKeyData ) ;
	LRESULT	WMCommand( HWND hWnd, UINT msg, WPARAM wp, LPARAM lp ) ;
	BOOL	WMCopydata( PCOPYDATASTRUCT cds ) ;

	void	KeyDownSub( int nVirtKey, BOOL fShift, BOOL fControl ) ;
	inline	void	KeyBS() ;
	inline	void	KeyESC() ;
	inline	void	KeyEnter() const ;
	inline	void	KeyTAB( BOOL fShift ) const ;
	inline	void	KeyRight() ;
	DWORD	GetTextWidth() const ;
	void	GetText( LPTSTR buf, DWORD size ) const ;
	void	SetText( LPCTSTR buf ) ;
	BOOL	GetModify() const { return m_fModify ; }
	void	SetModify( BOOL fModify ) { m_fModify = TRUE ; }
	void	AddChar( TCHAR c ) ;
	void	AddText( LPCTSTR text ) ;

	friend	LRESULT CALLBACK	LineEditWndProc( HWND hWnd,  UINT msg, WPARAM wParam, LPARAM lParam ) ;
} ;
typedef	LineEdit	*PLineEdit ;

LineEdit::LineEdit()
{
	m_sText[0] = 0 ;
	m_cw = GetFontHW() ;
	m_ch = GetFontH() ;
	m_fModify = TRUE ;
	m_fOnFocus = FALSE ;
	m_dMaxWidth = 0 ;
	m_fKanji = FALSE ;
}

LineEdit::~LineEdit()
{
}

void
LineEdit::WMCreate( HWND hWnd, LPCREATESTRUCT lpcs )
{
	RECT	rect ;

	m_hWnd = hWnd ;
	m_hWndParent = lpcs->hwndParent ;
	m_idCtrl = (WORD) lpcs->hMenu ;
	GetClientRect( m_hWnd, &rect ) ;
	m_dMaxWidth = rect.right - rect.left - 2 ;
	_tcscpy( m_sText, lpcs->lpszName ) ;
}

void
LineEdit::WMSetFocus()
{
	m_fOnFocus = TRUE ;
	::CreateCaret( m_hWnd, NULL, 2, m_ch ) ;
	::ShowCaret( m_hWnd ) ;
	::SetCaretPos( GetTextWidth(), 1 ) ;
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	ShowSIP( TRUE ) ;
}

void
LineEdit::WMKillFocus( HWND hWnd )
{
	m_fOnFocus = FALSE ;
	::HideCaret( m_hWnd ) ;
	::DestroyCaret() ;
	PostMessage( m_hWndParent, WM_COMMAND, MAKEWPARAM( m_idCtrl, LEN_KILLFOCUS ), (LPARAM) m_hWnd ) ;
}

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

void
LineEdit::WMPaint()
{
	PAINTSTRUCT	ps ;
	HDC			hDC ;
	RECT		rect ;

	hDC = ::BeginPaint( m_hWnd, &ps ) ;
	GetClientRect( m_hWnd, &rect ) ;
	rect.top += 1 ;		rect.left += 1 ;
	rect.bottom -= 1 ;	rect.right -= 1 ;
	KDrawText( hDC, m_sText, -1, &rect, 0 ) ;
	if ( m_fOnFocus ) {
		DWORD	width = GetTextWidth() ;
		::SetCaretPos( width, 1 ) ;
	}
	::EndPaint( m_hWnd, &ps ) ;
}

void
LineEdit::WMChar( TCHAR chCharCode, LONG lKeyData )
{
	if ( Fep_WM_CHAR( m_hWnd, chCharCode, lKeyData ) ) {
		return ;
	} else if ( (chCharCode >= TEXT(' ')) ) {
		AddChar( chCharCode ) ;
		::InvalidateRect( m_hWnd, NULL, TRUE ) ;
		PostMessage( m_hWndParent, WM_COMMAND, MAKEWPARAM( m_idCtrl, LEN_CHANGE ), (LPARAM) m_hWnd ) ;
	}
}

void
LineEdit::WMKeyDown( int nVirtKey, LONG lKeyData )
{
	BOOL	fShift   = 0x80 & GetKeyState( VK_SHIFT ) ;
	BOOL	fControl = 0x80 & GetKeyState( VK_CONTROL ) ;

	KeyDownSub( nVirtKey, fShift, fControl ) ;
}

BOOL
LineEdit::WMSysKeyDown( int nVirtKey, LONG lKeyData )
{
	return Fep_WM_SYSCHAR( m_hWnd, nVirtKey, lKeyData ) ;
}

LRESULT
LineEdit::WMCommand( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	int		nVirtKey ;
	BOOL	fShift, fControl ;
	WORD	cmd = GET_WM_COMMAND_ID( wParam, lParam ) ;
	WORD	notifyCode = GET_WM_COMMAND_CMD( wParam, lParam ) ;

	switch ( cmd ) {
	case IDM_FEPOK:
		/* FEP受け入れ応答 */
		return IDM_FEPOK ;
	case IDM_FEPKEY:
		/* FEPからのキー入力があった場合 */
		nVirtKey = lParam & 0xFF ;
		fShift   = lParam & 0x8000 ;
		fControl = lParam & 0x4000 ;
		KeyDownSub( nVirtKey, fShift, fControl ) ;
		break ;
	}
	return 0 ;
}

BOOL
LineEdit::WMCopydata( PCOPYDATASTRUCT cds )
{
	AddText( (LPCTSTR) cds->lpData ) ;
	return TRUE ;
}

void
LineEdit::KeyDownSub( int nVirtKey, BOOL fShift, BOOL fControl )
{
	switch ( nVirtKey ) {
	case VK_BACK:		KeyBS() ;			break ;
	case VK_ESCAPE:		KeyESC() ;			break ;
	case VK_RETURN:		KeyEnter() ;		break ;
	case VK_TAB:		KeyTAB( fShift ) ;	break ;
	case VK_DELETE:							break ;
	case VK_RIGHT:
		KeyRight() ;
		//
	case VK_HOME:
	case VK_END:
	case VK_LEFT:
	case VK_UP:
	case VK_DOWN:
	case VK_PRIOR:
	case VK_NEXT:
		SendMessage( m_hWndParent, WM_COMMAND, MAKEWPARAM( m_idCtrl, LEN_KEYDOWN ), (LPARAM) nVirtKey ) ;
		break ;
	}
}

void
LineEdit::KeyBS()
{
	DWORD	len ;

	len = _tcslen( m_sText ) ;
	if ( !len ) {
		return ;
	}
	m_sText[ len - 1 ] = 0 ;
	m_fModify = TRUE ;
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	PostMessage( m_hWndParent, WM_COMMAND, MAKEWPARAM( m_idCtrl, LEN_CHANGE ), (LPARAM) m_hWnd ) ;
}

void
LineEdit::KeyESC()
{
	m_sText[0] = 0 ;
	m_fModify = TRUE ;
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	PostMessage( m_hWndParent, WM_COMMAND, MAKEWPARAM( m_idCtrl, LEN_CHANGE ), (LPARAM) m_hWnd ) ;
}

void
LineEdit::KeyEnter() const
{
	SendMessage( m_hWndParent, WM_COMMAND, MAKEWPARAM( m_idCtrl, LEN_ENTER ), (LPARAM) m_hWnd ) ;
}

void
LineEdit::KeyTAB( BOOL fShift ) const
{
	WORD	cmd = fShift ? LEN_STAB : LEN_TAB ;
	SendMessage( m_hWndParent, WM_COMMAND, MAKEWPARAM( m_idCtrl, cmd ), (LPARAM) m_hWnd ) ;
}

void
LineEdit::KeyRight()
{
}

/*
 * 入力されている文字列のビット単位での長さを求める
 */
DWORD
LineEdit::GetTextWidth() const
{
	TCHAR	c ;
	DWORD	result = 0 ;
	LPCTSTR	ptr = m_sText ;

	while ( c = *ptr++ ) {
		if ( is_hankaku( c ) ) {
			result ++ ;
		} else {
			result += 2 ;
		}
	}
	return result * m_cw ;
}

/*
 * 文字列を取得する
 */
void
LineEdit::GetText( LPTSTR buf, DWORD size ) const
{
	_tcsncpy( buf, m_sText, size ) ;
}

/*
 * 文字列を設定する
 */
void
LineEdit::SetText( LPCTSTR buf )
{
	_tcscpy( m_sText, buf ) ;
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
}

void
LineEdit::AddChar( TCHAR c )
{
	int		len ;

	len = _tcslen( m_sText ) ;
	m_sText[ len ] = c ;
	m_sText[ len + 1 ] = 0 ;
	if ( GetTextWidth() > m_dMaxWidth ) {
		m_sText[ len ] = 0 ;
		return ;
	}
	m_fModify = TRUE ;
}

void
LineEdit::AddText( LPCTSTR text )
{
	TCHAR	c ;

	while ( c = *text++ ) {
		if ( c == TEXT('\n') ) {
			KeyEnter() ;
		} else if ( c == 0x08 ) {
			KeyBS() ;
		} else if ( c >= TEXT(' ') || c == TEXT('\t') ) {
			AddChar( c ) ;
		}
	}
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
	PostMessage( m_hWndParent, WM_COMMAND, MAKEWPARAM( m_idCtrl, LEN_CHANGE ), (LPARAM) m_hWnd ) ;
}

BOOL
LineEditRegisterClass( HINSTANCE hInst )
{
	WNDCLASS	wc ;

	wc.style         = NULL ;
	wc.lpfnWndProc   = (WNDPROC) LineEditWndProc ;
	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_LINEEDIT ;
	return ::RegisterClass( &wc ) ;
}

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

	if ( msg == WM_CREATE ) {
		LPCREATESTRUCT	lpStruct = (LPCREATESTRUCT) lParam ;
		pWindow = new LineEdit() ;
		SetWindowLong( hWnd, 0, (LONG) pWindow ) ;
		pWindow->WMCreate( hWnd, lpStruct ) ;
		return 0 ;
	}
	pWindow = (PLineEdit) GetWindowLong( hWnd, 0 ) ;
	if ( !pWindow ) {
		return DefWindowProc( hWnd, msg, wParam, lParam ) ;
	}
	switch ( msg ) {
	case WM_DESTROY:
		delete pWindow ;
		SetWindowLong( hWnd, 0, (LONG) 0 ) ;
		break ;
	case WM_SETFOCUS:
		pWindow->WMSetFocus() ;
		return DefWindowProc( hWnd, msg, wParam, lParam ) ;
	case WM_KILLFOCUS:
		pWindow->WMKillFocus( (HWND) wParam ) ;
		return DefWindowProc( hWnd, msg, wParam, lParam ) ;
	case WM_COMMAND:
		return pWindow->WMCommand( hWnd, msg, wParam, lParam ) ;
	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 ) ) {
			DefWindowProc( hWnd, msg, wParam, lParam ) ;
		}
		break ;
	case WM_LBUTTONDOWN:	pWindow->WMMouseDown( wParam, lParam ) ;		break ;
	case WM_GETDLGCODE:		return DLGC_WANTARROWS|DLGC_WANTCHARS ;
	case WM_COPYDATA:
		return pWindow->WMCopydata( (PCOPYDATASTRUCT) lParam ) ;

	case WM_GETTEXT:		pWindow->GetText( (LPTSTR) lParam, (DWORD) wParam ) ;		break ;
	case WM_SETTEXT:		pWindow->SetText( (LPCTSTR) lParam ) ;						break ;
	case EM_GETMODIFY:		return pWindow->GetModify() ;
	case EM_SETMODIFY:		pWindow->SetModify( (BOOL) wParam ) ;						break ;
	default:
		return DefWindowProc( hWnd, msg, wParam, lParam ) ;
	}
	return 0 ;
}
