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

extern "C" {
	extern	int	strwidth( LPCSTR str ) ;
} ;

class SoftKey {
protected:
	HWND	m_hWnd ;
	HWND	m_hWndParent ;	/* コントロールの親ウィンドウ */
	WORD	m_idCtrl ;		/* コントロールの識別番号 */
	HBITMAP	m_hBmp ;
	Key		*m_pKeyList ;
	Key		*m_pKey ;
	DWORD	m_dwCur ;
	DWORD	m_dwFontH ;
	DWORD	m_dwTopPos ;
	HBRUSH	m_hBrushSel ;

public:
	SoftKey() ;
	~SoftKey() ;

	void	WMCreate( HWND hWnd, LPCREATESTRUCT lpcs ) ;
	void	WMMouseDown( WPARAM wParam, LPARAM lParam ) ;
	void	WMMouseUp( WPARAM wParam, LPARAM lParam ) ;
	void	WMPaint() const ;
	Key*	InKey( WORD x, WORD y ) ;
	inline	void	DispKey( HDC hDC, Key* key, BOOL fCur ) const ;
	void	Reverse( BOOL fRev ) const ;
	inline	void	SetBitmap( HBITMAP hBmp ) ;
	inline	void	SetList( Key* pKeyList ) ;
	inline	DWORD	GetCur() const ;
	inline	void	SetCur( DWORD index ) ;
	inline	Key*	GetCurCand() const ;
	inline	void	SetSelColor( COLORREF color ) ;
	inline	void	SetParent( HWND hWnd ) ;

	friend	LRESULT CALLBACK	SoftKeyWndProc( HWND hWnd,  UINT msg, WPARAM wParam, LPARAM lParam ) ;
} ;
typedef	SoftKey	*PSoftKey ;

SoftKey::SoftKey()
{
	m_hBmp = 0 ;
	m_pKeyList = m_pKey = 0 ;
	m_dwCur = 1 ;
	m_dwFontH = GetFontH() ;
	m_dwTopPos = 20 >= m_dwFontH ? (20 - m_dwFontH) / 2 : 0  ;
	m_hBrushSel = CreateSolidBrush( RGB( 128, 128, 128 ) ) ;
}

SoftKey::~SoftKey()
{
	if ( m_hBmp ) {
		DeleteObject( m_hBmp ) ;
	}
	if ( m_hBrushSel ) {
		DeleteObject( m_hBrushSel ) ;
	}
}

void
SoftKey::WMCreate( HWND hWnd, LPCREATESTRUCT lpcs )
{
	m_hWnd = hWnd ;
	m_hWndParent = lpcs->hwndParent ;
	m_idCtrl = (WORD) lpcs->hMenu ;
}

void
SoftKey::WMMouseDown( WPARAM wParam, LPARAM lParam )
{
	WORD	xPos = LOWORD( lParam ) ;	// SoftKey内のX座標
	WORD	yPos = HIWORD( lParam ) ;	// SoftKey内のX座標

	if ( m_hWndParent ) {
		m_pKey = InKey( xPos, yPos ) ;
		if ( m_pKey ) {
			::SetCapture( m_hWnd ) ;
			Reverse( TRUE ) ;
			if ( m_hBmp ) {
				SendMessage( m_hWndParent, WM_COMMAND, m_idCtrl, (LPARAM) m_pKey ) ;
			}
		}
	}
}

void
SoftKey::WMMouseUp( WPARAM wParam, LPARAM lParam )
{
	WORD	xPos = LOWORD( lParam ) ;	// SoftKey内のX座標
	WORD	yPos = HIWORD( lParam ) ;	// SoftKey内のX座標
	Key		*key ;

	if ( m_pKey ) {
		Reverse( FALSE ) ;
		::ReleaseCapture() ;
		if ( !m_hBmp ) {
			key = InKey( xPos, yPos ) ;
			if ( key == m_pKey ) {
				SendMessage( m_hWndParent, WM_COMMAND, m_idCtrl, (LPARAM) m_pKey ) ;
			}
		}
		m_pKey = NULL ;
	}
}

void
SoftKey::WMPaint() const
{
	PAINTSTRUCT	ps ;
	HBITMAP		oldBmp ;
	HDC			hDC, hDCBits ;

	hDC = ::BeginPaint( m_hWnd, &ps ) ;
	hDCBits = ::CreateCompatibleDC( hDC ) ;
	if ( m_hBmp ) {
		oldBmp = (HBITMAP) SelectObject( hDCBits, m_hBmp ) ;
		BitBlt( hDC, 0, 0, 240, 80, hDCBits, 0, 0, SRCCOPY ) ;
		SelectObject( hDCBits, oldBmp ) ;
	} else if ( m_pKeyList ) {
		DWORD	cur = 1 ;
		Key*	key = m_pKeyList ;
		while ( key->width ) {
			DispKey( hDC, key, m_dwCur == cur ) ;
			cur ++ ;
			key ++ ;
		}
	}
	DeleteDC( hDCBits ) ;
	::EndPaint( m_hWnd, &ps ) ;
}

Key*
SoftKey::InKey( WORD x, WORD y )
{
	int		i ;

	if ( !m_pKeyList ) {
		return NULL ;
	}
	for ( i = 0 ; m_pKeyList[i].width ; i ++ ) {
		if ( inkey( &m_pKeyList[i], x, y ) ) {
			return &m_pKeyList[i] ;
		}
	}
	return NULL ;
}

void
SoftKey::DispKey( HDC hDC, Key* key, BOOL fCur ) const
{
	RECT	rect ;
	HBRUSH	hOldBrush ;
	TCHAR	unicode[ 256 ] ;

	rect.left = key->x ;
	rect.top = key->y ;
	rect.right = key->x+key->width-1 ;
	rect.bottom = key->y+key->height ;
	Rectangle( hDC, key->x, key->y, key->x+key->width-1, key->y+key->height ) ;
//	DrawEdge( hDC, &rect, EDGE_RAISED, BF_RECT|BF_SOFT ) ;
	rect.left = key->x + (key->width - strwidth(key->str)) / 2 - 1 ;
	rect.top = key->y + m_dwTopPos ;
	rect.right = rect.left + strwidth(key->str) ;
	rect.bottom = rect.top + m_dwFontH ;
	sjis2unicode( (LPBYTE) key->str, unicode, sizeof unicode ) ;
	KDrawText( hDC, unicode, -1, &rect, 0 ) ;
	if ( fCur ) {
		hOldBrush = (HBRUSH) SelectObject( hDC, m_hBrushSel ) ;
		PatBlt( hDC, key->x+1, key->y+1, key->width-4, key->height-3, PATINVERT ) ;
		SelectObject( hDC, hOldBrush ) ;
	}
}

void
SoftKey::Reverse( BOOL fRev ) const
{
	HDC		hDC ;
	RECT	rect ;
	HBRUSH	hOldBrush ;

	if ( !IsWindowVisible( m_hWnd ) ) {
		return ;
	}
	hDC = ::GetDC( m_hWnd ) ;
	if ( m_hBmp ) {
		hOldBrush = (HBRUSH) SelectObject( hDC, m_hBrushSel ) ;
		PatBlt( hDC, m_pKey->x, m_pKey->y, m_pKey->width, m_pKey->height, PATINVERT ) ;
		SelectObject( hDC, hOldBrush ) ;
	} else if ( m_pKeyList ) {
		rect.left = m_pKey->x ;
		rect.top = m_pKey->y ;
		rect.right = m_pKey->x + m_pKey->width - 1 ;
		rect.bottom = m_pKey->y + m_pKey->height ;
		DrawEdge( hDC, &rect, fRev ? EDGE_SUNKEN : EDGE_RAISED, BF_RECT ) ;
	}
	::ReleaseDC( m_hWnd, hDC ) ;
}

void
SoftKey::SetBitmap( HBITMAP hBmp )
{
	m_hBmp = hBmp ;
}

void
SoftKey::SetList( Key* pKeyList )
{
	m_pKeyList = pKeyList ;
}

DWORD
SoftKey::GetCur() const
{
	return m_dwCur ;
}

void
SoftKey::SetCur( DWORD index )
{
	m_dwCur = index ;
	::InvalidateRect( m_hWnd, NULL, TRUE ) ;
}

Key*
SoftKey::GetCurCand() const
{
	DWORD	cur = 1 ;
	Key*	key = m_pKeyList ;

	while ( key->width ) {
		if ( m_dwCur == cur ) {
			return key ;
		}
		cur ++ ;
		key ++ ;
	}
	return NULL ;
}

void
SoftKey::SetSelColor( COLORREF color )
{
	if ( m_hBrushSel ) {
		DeleteObject( m_hBrushSel ) ;
	}
	m_hBrushSel = CreateSolidBrush( color ) ;
}

void
SoftKey::SetParent( HWND hWnd )
{
	m_hWndParent = hWnd ;
}

BOOL
SoftKeyRegisterClass( HINSTANCE hInst )
{
	WNDCLASS	wc ;

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

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

	if ( msg == WM_CREATE ) {
		LPCREATESTRUCT	lpStruct = (LPCREATESTRUCT) lParam ;
		pWindow = new SoftKey() ;
		SetWindowLong( hWnd, 0, (LONG) pWindow ) ;
		pWindow->WMCreate( hWnd, lpStruct ) ;
		return 0 ;
	}
	pWindow = (PSoftKey) 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_PAINT:
		pWindow->WMPaint() ;
		break ;
	case WM_LBUTTONDOWN:
		pWindow->WMMouseDown( wParam, lParam ) ;
		break ;
	case WM_LBUTTONUP:
		pWindow->WMMouseUp( wParam, lParam ) ;
		break ;
	case SOFTKEY_SETBITMAP:
		pWindow->SetBitmap( (HBITMAP) lParam ) ;
		break ;
	case SOFTKEY_SETLIST:
		pWindow->SetList( (Key*) lParam ) ;
		break ;
	case SOFTKEY_GETCUR:
		return (LRESULT) pWindow->GetCur() ;
	case SOFTKEY_SETCUR:
		pWindow->SetCur( (DWORD) lParam ) ;
		break ;
	case SOFTKEY_GETCURCAND:
		return (LRESULT) pWindow->GetCurCand() ;
	case SOFTKEY_SETCOLSEL:
		pWindow->SetSelColor( (COLORREF) lParam ) ;
		break ;
	case SOFTKEY_SETPARENT:
		pWindow->SetParent( (HWND) wParam ) ;
		break ;
	default:
		return DefWindowProc( hWnd, msg, wParam, lParam ) ;
	}
	return 0 ;
}
