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

#define		is_kanji(x)		(((x)>=0x81 && (x)<=0x9F)||((x)>=0xE0 && (x)<=0xFC))
#define		NO_LINE			((DWORD)0x00FFFFFF)
#define		MAX_POS_ARRAYS	(16)		/* 行番号ブロック数 */
#define		MAX_POS			(16384)		/* 行番号ブロック内の行数 */

typedef struct {
	unsigned	lineno: 24 ;	/* 行番号 */
	unsigned	title: 1 ;		/* タイトルフラグ */
	unsigned	checked: 1 ;	/* タイトルチェック済みフラグ */
} LineInfo, *LPLineInfo ;

KeyMapInfo	g_DefaultKeyMapInfo[] = {
	{ VK_RETURN, 0, FV_FUNC_ENTER },
	{ VK_ESCAPE, 0, FV_FUNC_ESC },
	{ VK_UP,     0, FV_FUNC_UP },
	{ VK_DOWN,   0, FV_FUNC_DOWN },
	{ VK_LEFT,   0, FV_FUNC_PRIOR },
	{ VK_RIGHT,  0, FV_FUNC_NEXT },
	{ VK_PRIOR,  0, FV_FUNC_PRIOR },
	{ VK_NEXT,   0, FV_FUNC_NEXT },
	{ VK_HOME,   0, FV_FUNC_HOME },
	{ VK_END,    0, FV_FUNC_END },
	{ VK_BACK,   0, FV_FUNC_PRIOR },
	{ ' ',       1, FV_FUNC_NEXT },
	{ 0,         0, 0 }
} ;

LPKeyMapInfo	g_KeyMapInfo = g_DefaultKeyMapInfo ;

class FileView {
protected:
	HWND	m_hwnd ;			/* コントロールのウィンドウハンドル */
	HWND	m_hwndParent ;		/* コントロールの親ウィンドウ */
	WORD	m_idCtrl ;			/* コントロールの識別番号 */
	DWORD	m_dwCols ;			/* 表示できる半角文字数 */
	DWORD	m_dwLines ;			/* 表示できる行数 */
	DWORD	m_dwFontH ;			/* 1行の高さ */
	DWORD	m_dwTabSize ;		/* TABのサイズ */
	DWORD	m_dwHMargin ;		/* 縦方向のマージン */

	LPLineInfo	m_LinePos[ MAX_POS_ARRAYS ] ;	/* 行番号情報 */
	DWORD	m_dwMaxLines ;		/* ファイルの論理行数 */
	DWORD	m_dwTop ;			/* 表示中の先頭行 */
	LPBYTE	m_szScreen ;		/* 画面バッファ */

	TCHAR	m_szFilename[ MAX_PATH ] ;			/* ファイル名 */
	HANDLE	m_hFile ;			/* ファイルハンドル */
	BYTE	m_szBuf[ 1024 ] ;	/* ファイルバッファ */
	DWORD	m_dwBufSiz ;		/* ファイルバッファ内のデータ数 */
	DWORD	m_dwBufCur ;		/* ファイルバッファの現在位置 */
	BOOL	m_bUnGetChar ;		/* 読み戻しフラグ */
	BYTE	m_chUnGetChar ;		/* 読み戻す文字 */
	DWORD	m_dwFilePos ;		/* ファイル内位置 */

	void	InitLinePos() ;
	void	FreeLinePos() ;
	void	AddLinePos( DWORD lineno, DWORD pos ) ;
	DWORD	GetLinePos( DWORD lineno ) const ;
	DWORD	GetLastLine() const ;
	DWORD	FindLinePos( DWORD pos ) const ;

	BOOL	OpenFile() ;
	void	CloseFile() ;
	BOOL	SetFilePos( DWORD lineno ) ;
	BOOL	GetChar( LPBYTE ptr ) ;
	void	UnGetChar() ;
	BOOL	GetLine( LPBYTE dst, DWORD len ) ;
	BOOL	MakeLinePos() ;
	void	MakeScreen() ;

	BOOL	ScrollUp( DWORD line ) ;
	BOOL	ScrollDown( DWORD line ) ;
	BOOL	PageUp( DWORD page ) ;
	BOOL	PageDown( DWORD page ) ;
	DWORD	WMCopySub( LPTSTR dst, DWORD from, DWORD lines ) ;
	void	ResetContent() ;

public:
	FileView() ;
	~FileView() ;

	BOOL	Create() { return FALSE ; }

	void	WMCreate( HWND hWnd, LPCREATESTRUCT lpcs ) ;
	void	WMPaint() ;
	LRESULT	WMCommand( HWND hWnd, UINT msg, WPARAM wp, LPARAM lp ) ;
	void	WMChar( TCHAR c, LONG keydata ) ;
	void	WMKeyDown( int nVirtKey, LONG lKeyData ) ;
	void	WMMouseDown( WPARAM wParam, LPARAM lParam ) ;
	void	WMVScroll( WPARAM wParam, LPARAM lParam ) ;
	void	WMSize( DWORD fwSize, WORD nWidth, WORD nHeight ) ;
	BOOL	WMCopy() ;

	void	SetScrollPos() const ;
	void	KeyDownSub( WORD functionID, int nVirtKey, BOOL fShift, BOOL fControl, BOOL fRepeat ) ;
	inline	void	KeyEnter() const ;
	inline	void	KeyESC() const ;
	inline	void	KeyTAB( BOOL fShift ) const ;
	inline	void	KeyHome() ;
	inline	void	KeyEnd() ;
	inline	void	KeyUp( DWORD line ) ;
	inline	void	KeyDown( DWORD line ) ;
	inline	void	KeyPrior( DWORD page ) ;
	inline	void	KeyNext( DWORD page ) ;

	void	Clear() ;
	BOOL	Load( LPCTSTR fn ) ;
	BOOL	Reload() ;
	BOOL	Open() ;
	void	Close() ;
	DWORD	GetTopLine() const { return m_dwTop ; }
	void	SetTopLine( DWORD lineno ) { m_dwTop = lineno ; }
	DWORD	GetTopPos() const { return GetLinePos( m_dwTop ) ; }
	void	SetTopPos( DWORD wParam ) ;
	DWORD	GetMaxLine() const { return m_dwLines ; }
	void	SetTab( DWORD wParam, BOOL bUpdate ) ;
	void	SetHMargin( DWORD wParam, BOOL bUpdate ) ;
	void	SetKeymap( LPKeyMapInfo keymap ) ;
	int		FindString( DWORD indexStart, LPCTSTR lpszFind ) const ;

	friend	LRESULT CALLBACK	FileViewWndProc( HWND hWnd,  UINT msg, WPARAM wParam, LPARAM lParam ) ;
} ;
typedef	FileView	*PFileView ;

FileView::FileView()
{
	m_dwHMargin = 1 ;
	m_dwFontH = GetFontH() + m_dwHMargin ;
	m_dwTabSize = 8 ;
	m_dwTop = 0 ;
	m_dwCols = m_dwLines = 0 ;
	*m_szFilename = 0 ;
	m_dwBufSiz = 0 ;
	m_dwBufCur = 0 ;
	InitLinePos() ;
	m_dwCols = m_dwLines = 0 ;
	m_dwMaxLines = 0 ;
	m_szScreen = 0 ;
	m_hFile = INVALID_HANDLE_VALUE ;
}

FileView::~FileView()
{
	FreeLinePos() ;
	if ( m_szScreen ) {
		LocalFree( m_szScreen ) ;
		m_szScreen = 0 ;
	}
}

/*
 * 　名称：行番号情報初期化
 * 　略称：InitLinePos
 * 　概要：行番号情報を初期化する
 * 引き数：なし
 * 戻り値：なし
 */
void
FileView::InitLinePos()
{
	int		i ;

	for ( i = 0 ; i < MAX_POS_ARRAYS ; i ++ ) {
		m_LinePos[ i ] = NULL ;
	}
}

/*
 * 　名称：行番号情報開放
 * 　略称：FreeLinePos
 * 　概要：行番号情報の為に確保した領域を開放する
 * 引き数：なし
 * 戻り値：なし
 */
void
FileView::FreeLinePos()
{
	int		i ;

	for ( i = 0 ; i < MAX_POS_ARRAYS ; i ++ ) {
		if ( m_LinePos[ i ] != NULL ) {
			LocalFree( m_LinePos[ i ] ) ;
			m_LinePos[ i ] = NULL ;
		}
	}
}

/*
 * 　名称：行番号情報追加
 * 　略称：AddLinePos
 * 　概要：行番号情報に新しい行を追加する
 * 引き数：lineno(DWORD) = 行番号
 * 　　　：pos(DWORD) = ファイル内位置
 * 戻り値：なし
 */
void
FileView::AddLinePos( DWORD lineno, DWORD pos )
{
	DWORD		i ;
	DWORD		idx1 = lineno / MAX_POS ;
	DWORD		idx2 = lineno % MAX_POS ;
	LPLineInfo	li ;

	if ( m_LinePos[ idx1 ] == NULL ) {
		m_LinePos[ idx1 ] = (LPLineInfo) LocalAlloc( LMEM_FIXED, MAX_POS * sizeof (LineInfo) ) ;
		li = m_LinePos[ idx1 ] ;
		for ( i = 0 ; i < MAX_POS ; i ++ ) {
			li->lineno = NO_LINE ;
			li->title = 0 ;
			li->checked = 0 ;
			li ++ ;
		}
	}
	m_LinePos[ idx1 ][ idx2 ].lineno = pos ;
}

/*
 * 　名称：行番号情報取得
 * 　略称：GetLinePos
 * 　概要：行番号情報からファイル内位置を取得する
 * 引き数：lineno(DWORD) = 行番号
 * 戻り値：DWORD = ファイル内位置
 */
DWORD
FileView::GetLinePos( DWORD lineno ) const
{
	DWORD	idx1 = lineno / MAX_POS ;
	DWORD	idx2 = lineno % MAX_POS ;

	if ( m_LinePos[ idx1 ] == NULL ) {
		return NO_LINE ;
	}
	return m_LinePos[ idx1 ][ idx2 ].lineno ;
}

/*
 * 　名称：行番号内最終行取得
 * 　略称：GetLastLine
 * 　概要：行番号情報に記録されている最後の行番号を取得する
 * 引き数：なし
 * 戻り値：DWORD = 行番号
 */
DWORD
FileView::GetLastLine() const
{
	LPLineInfo	lp ;
	DWORD		i, j, pos ;

	for ( i = MAX_POS_ARRAYS ; i ; i -- ) {
		lp = m_LinePos[ i - 1 ] ;
		if ( lp == NULL ) {
			continue ;
		}
		for ( j = MAX_POS ; j ; j -- ) {
			pos = lp[ j - 1 ].lineno ;
			if ( pos != NO_LINE ) {
				return (i - 1) * MAX_POS + (j - 1) ;
			}
		}
	}
	return 0 ;
}

/*
 * 指定されたファイル内位置を含む行を返す
 */
DWORD
FileView::FindLinePos( DWORD pos ) const
{
	DWORD	max = GetLastLine() ;
	DWORD	lastLine = 0, cur, i ;

	for ( i = 0 ; i <= max ; i ++ ) {
		cur = GetLinePos( i ) ;
		if ( cur == NO_LINE ) {
			continue ;
		}
		if ( cur > pos ) {
			return lastLine ;
		}
		lastLine = i ;
	}
	return NO_LINE ;
}

BOOL
FileView::OpenFile()
{
	m_dwTop = 0 ;
	m_dwMaxLines = 0 ;
	m_dwBufSiz = 0 ;
	m_dwBufCur = 0 ;
	m_dwFilePos = 0 ;
	m_bUnGetChar = FALSE ;
	m_chUnGetChar = 0 ;
	if ( m_hFile != INVALID_HANDLE_VALUE ) {
		if ( SetFilePointer( m_hFile, 0, NULL, FILE_BEGIN ) != 0xFFFFFFFF ) {
			return TRUE ;
		}
		CloseHandle( m_hFile ) ;
	}
	m_hFile = CreateFile( m_szFilename,
				GENERIC_READ,
				FILE_SHARE_READ,
				NULL, 
				OPEN_EXISTING,
				FILE_ATTRIBUTE_NORMAL,
				NULL ) ;
	return m_hFile != INVALID_HANDLE_VALUE ;
}

void
FileView::CloseFile()
{
	if ( m_hFile != INVALID_HANDLE_VALUE ) {
		CloseHandle( m_hFile ) ;
		m_hFile = INVALID_HANDLE_VALUE ;
	}
}

BOOL
FileView::SetFilePos( DWORD lineno )
{
	DWORD	pos ;

	if ( lineno >= m_dwMaxLines ) {
		return FALSE ;
	}
	pos = GetLinePos( lineno ) ;
	if ( pos == NO_LINE ) {
		return FALSE ;
	}
	if ( SetFilePointer( m_hFile, pos, NULL, FILE_BEGIN ) == 0xFFFFFFFF ) {
		return FALSE ;
	}
	m_dwFilePos = pos ;
	m_bUnGetChar = FALSE ;
	m_dwBufCur = m_dwBufSiz = 0 ;
	return TRUE ;
}

/*
 * 1文字取得する
 */
BOOL
FileView::GetChar( LPBYTE ptr )
{
	if ( m_bUnGetChar ) {
		m_bUnGetChar = FALSE ;
		m_dwFilePos ++ ;
		*ptr = m_chUnGetChar ;
		return TRUE ;
	}
	if ( m_dwBufCur >= m_dwBufSiz ) {
		m_dwBufSiz = m_dwBufCur = 0 ;
		ReadFile( m_hFile, m_szBuf, sizeof m_szBuf, &m_dwBufSiz, 0 ) ;
		if ( !m_dwBufSiz ) {
			return FALSE ;
		}
	}
	m_dwFilePos ++ ;
	m_chUnGetChar = m_szBuf[ m_dwBufCur ++ ] ;
	*ptr = m_chUnGetChar ;
	return TRUE ;
}

/*
 * 1文字戻す
 */
void
FileView::UnGetChar()
{
	m_dwFilePos -- ;
	m_bUnGetChar = TRUE ;
}

/*
 * 1行分のデータを取得する
 */
BOOL
FileView::GetLine( LPBYTE dst, DWORD len )
{
	BYTE	c ;
	BOOL	bKanji, bEof = TRUE ;
	DWORD	w, char_len, pos = 0 ;

	w = len ;
	while ( GetChar( &c ) ) {
		bEof = FALSE ;
		if ( c == '\t' ) {
			bKanji = FALSE ;
			char_len = m_dwTabSize - (pos % m_dwTabSize) ;
		} else {
			bKanji = is_kanji( c ) ;
			char_len = bKanji ? 2 : 1 ;
		}
		if ( c == '\r' ) {
			continue ;
		} else if ( c == '\n' ) {
			break ;
		} else if ( len < char_len ) {
			if ( c != '\t' ) {
				UnGetChar() ;
			}
			break ;
		}
		len -= char_len ;
		pos += char_len ;
		if ( dst ) {
			if ( c == '\t' ) {
				do {
					*dst ++ = ' ' ;
				} while ( -- char_len ) ;
			} else {
				*dst ++ = c ;
			}
		}
		if ( bKanji ) {
			if ( !GetChar( &c ) ) {
				break ;
			}
			if ( dst ) {
				*dst ++ = c ;
			}
		}
	}
	if ( dst ) {
		*dst = 0 ;
	}
	return !bEof ;
}

/*
 * 行番号情報を作成する
 */
BOOL
FileView::MakeLinePos()
{
	m_dwMaxLines = 0 ;
	while ( 1 ) {
		AddLinePos( m_dwMaxLines, m_dwFilePos ) ;
		if ( !GetLine( NULL, m_dwCols ) ) {
			break ;
		}
		m_dwMaxLines ++ ;
	}
	return TRUE ;
}

/*
 * 画面バッファの内容を作成する
 */
void
FileView::MakeScreen()
{
	DWORD	i ;
	BOOL	bFirst = TRUE ;
	LPBYTE	ptr = m_szScreen ;

	for ( i = 0 ; i < m_dwLines ; i ++ ) {
		if ( m_dwTop + i >= m_dwMaxLines ) {
			break ;
		}
		if ( bFirst ) {
			bFirst = FALSE ;
			SetFilePos( m_dwTop + i ) ;
		}
		GetLine( ptr, m_dwCols ) ;
		ptr += m_dwCols + 1 ;
	}
	for ( ; i < m_dwLines ; i ++ ) {
		ptr[0] = '~' ;
		ptr[1] = 0 ;
		ptr += m_dwCols + 1 ;
	}
}

BOOL
FileView::ScrollUp( DWORD line )
{
	DWORD	i ;
	HDC		hDC ;
	RECT	clip, scroll ;
	TCHAR	unicode[ 256 ] ;
	LPBYTE	prev = &m_szScreen[ (m_dwCols + 1) * (m_dwLines - 2) ] ;
	LPBYTE	next = prev + m_dwCols + 1 ;

	if ( !m_dwTop ) {
		return FALSE ;
	} else if ( m_dwTop >= line ) {
		m_dwTop -= line ;
	} else {
		line = m_dwTop ;
		m_dwTop = 0 ;
	}
	/* 画面バッファの内容を変更する */
	for ( i = 0 ; i < m_dwLines - line ; i ++ ) {
		strcpy( (LPSTR) next, (LPSTR) prev ) ;
		prev -= m_dwCols + 1 ;
		next -= m_dwCols + 1 ;
	}
	SetFilePos( m_dwTop ) ;
	next = m_szScreen ;
	for ( ; i < m_dwLines ; i ++ ) {
		strcpy( (LPSTR) next, "ERROR" ) ;
		GetLine( next, m_dwCols ) ;
		next += m_dwCols + 1 ;
	}
	/* 画面を上スクロールする */
	GetClientRect( m_hwnd, &clip ) ;
	clip.top ++ ;
	clip.bottom = clip.top + m_dwFontH * m_dwLines ;
	scroll = clip ;
	scroll.bottom -= m_dwFontH ;
	ScrollWindowEx( m_hwnd, 0, line * m_dwFontH, &scroll, &clip, NULL, NULL, SW_ERASE ) ;
	/* 最上行を描画する */
	hDC = ::GetDC( m_hwnd ) ;
	clip.bottom = clip.top + m_dwFontH * line ;
	FillRect( hDC, &clip, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
	prev = m_szScreen ;
	for ( i = 0 ; i < line ; i ++ ) {
		clip.bottom = clip.top + m_dwFontH - 1 ;
		sjis2unicode( prev, unicode, sizeof unicode ) ;
		KDrawText( hDC, unicode, -1, &clip, 0 ) ;
		prev += m_dwCols + 1 ;
		clip.top += m_dwFontH ;
	}
	ReleaseDC( m_hwnd, hDC ) ;
	/* スクロールバーの更新 */
	SetScrollPos() ;
	return TRUE ;
}

BOOL
FileView::ScrollDown( DWORD line )
{
	DWORD	i ;
	HDC		hDC ;
	RECT	clip, scroll ;
	TCHAR	unicode[ 256 ] ;
	LPBYTE	prev = m_szScreen ;
	LPBYTE	next = prev + m_dwCols + 1 ;

	if ( m_dwTop >= m_dwMaxLines - 1 ) {
		return FALSE ;
	} else if ( m_dwTop + line < m_dwMaxLines ) {
		m_dwTop += line ;
	} else {
		line = m_dwLines - 1 - m_dwTop ;
		m_dwTop = m_dwMaxLines - 1 ;
	}
	/* 画面バッファの内容を変更する */
	for ( i = 0 ; i < m_dwLines - line ; i ++ ) {
		strcpy( (LPSTR) prev, (LPSTR) next ) ;
		prev += m_dwCols + 1 ;
		next += m_dwCols + 1 ;
	}
	prev = &m_szScreen[ (m_dwCols + 1) * (m_dwLines - line) ] ;
	if ( m_dwTop + m_dwLines - line < m_dwMaxLines ) {
		SetFilePos( m_dwTop + m_dwLines - line ) ;
	}
	for ( ; i < m_dwLines ; i ++ ) {
		if ( m_dwTop + i < m_dwMaxLines ) {
			GetLine( prev, m_dwCols ) ;
		} else {
			strcpy( (LPSTR) prev, "~" ) ;
		}
		prev += m_dwCols + 1 ;
	}
	/* 画面を下スクロールする */
	GetClientRect( m_hwnd, &clip ) ;
	clip.top ++ ;
	clip.bottom = clip.top + m_dwFontH * m_dwLines ;
	scroll = clip ;
	scroll.top += m_dwFontH ;
	ScrollWindowEx( m_hwnd, 0, - (int) (line * m_dwFontH), &scroll, &clip, NULL, NULL, SW_ERASE ) ;
	/* 最下行を描画する */
	hDC = ::GetDC( m_hwnd ) ;
	clip.top = clip.bottom - m_dwFontH * line ;
	FillRect( hDC, &clip, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
	prev = &m_szScreen[ (m_dwCols + 1) * (m_dwLines - line) ] ;
	for ( i = 0 ; i < line ; i ++ ) {
		clip.bottom = clip.top + m_dwFontH - 1 ;
		sjis2unicode( prev, unicode, sizeof unicode ) ;
		KDrawText( hDC, unicode, -1, &clip, 0 ) ;
		prev += m_dwCols + 1 ;
		clip.top += m_dwFontH ;
	}
	ReleaseDC( m_hwnd, hDC ) ;
	/* スクロールバーの更新 */
	SetScrollPos() ;
	return TRUE ;
}

BOOL
FileView::PageUp( DWORD page )
{
	DWORD	oldTop = m_dwTop ;

	if ( m_dwTop >= (m_dwLines - 1) * page ) {
		m_dwTop -= (m_dwLines - 1) * page ;
	} else {
		m_dwTop = 0 ;
		if ( oldTop == m_dwTop ) {
			return FALSE ;
		}
	}
	MakeScreen() ;
	InvalidateRect( m_hwnd, NULL, TRUE ) ;
	/* スクロールバーの更新 */
	SetScrollPos() ;
	return TRUE ;
}

BOOL
FileView::PageDown( DWORD page )
{
	DWORD	oldTop = m_dwTop ;

	if ( m_dwTop + (m_dwLines - 1) * page < m_dwMaxLines ) {
		m_dwTop += (m_dwLines - 1) * page ;
	} else {
		m_dwTop = m_dwMaxLines - 1 ;
		if ( oldTop == m_dwTop ) {
			return FALSE ;
		}
	}
	MakeScreen() ;
	InvalidateRect( m_hwnd, NULL, TRUE ) ;
	/* スクロールバーの更新 */
	SetScrollPos() ;
	return TRUE ;
}

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

	m_hwnd = hWnd ;
	m_hwndParent = lpcs->hwndParent ;
	m_idCtrl = (WORD) lpcs->hMenu ;
	/* 1画面に表示できる行数を求める */
	GetClientRect( m_hwnd, &rect ) ;
	m_dwLines = (rect.bottom - 2) / m_dwFontH ;
	m_dwCols = (rect.right - 2) / GetFontHW() ;
	/* 画面バッファを作成する */
	m_szScreen = (LPBYTE) LocalAlloc( LMEM_FIXED, (m_dwCols + 1) * m_dwLines ) ;
	MakeScreen() ;
}

void
FileView::WMPaint()
{
	DWORD		i ;
	PAINTSTRUCT	ps ;
	HDC			hDC ;
	LPBYTE		ptr ;
	RECT		rect ;
	TCHAR		unicode[ 128 ] ;

	hDC = ::BeginPaint( m_hwnd, &ps ) ;
	GetClientRect( m_hwnd, &rect ) ;
	rect.top += 1 ;
	rect.left += 1 ;
	rect.right -= 1 ;
	ptr = m_szScreen ;
	for ( i = 0 ; i < m_dwLines ; i ++ ) {
		sjis2unicode( ptr, unicode, sizeof unicode ) ;
		rect.bottom = rect.top + m_dwFontH - 1 ;
		KDrawText( hDC, unicode, -1, &rect, 0 ) ;
		rect.top += m_dwFontH ;
		ptr += m_dwCols + 1 ;
	}
	::EndPaint( m_hwnd, &ps ) ;
}

void
FileView::KeyDownSub( WORD functionID, int nVirtKey, BOOL fShift, BOOL fControl, BOOL fRepeat )
{
	switch ( functionID ) {
	case FV_FUNC_ENTER:		KeyEnter() ;					break ;
	case FV_FUNC_ESC:		KeyESC() ;						break ;
	case FV_FUNC_UP:		KeyUp( fRepeat ? 2 : 1 ) ;		break ;
	case FV_FUNC_DOWN:		KeyDown( fRepeat ? 2 : 1 ) ;	break ;
	case FV_FUNC_PRIOR:		KeyPrior( fRepeat ? 2 : 1 ) ;	break ;
	case FV_FUNC_NEXT:		KeyNext( fRepeat ? 2 : 1 ) ;	break ;
	case FV_FUNC_HOME:		KeyHome() ;						break ;
	case FV_FUNC_END:		KeyEnd() ;						break ;
//	case FV_FUNC_FIND:		KeyFind() ;						break ;
	}
}

void
FileView::WMChar( TCHAR chCharCode, LONG lKeyData )
{
	DWORD			i ;
	WORD			functionID ;
	BOOL			fShift   = 0x80 & GetKeyState( VK_SHIFT ) ;
	BOOL			fControl = 0x80 & GetKeyState( VK_CONTROL ) ;
	BOOL			fRepeat = (lKeyData & 0x000F) > 1 ? TRUE : FALSE ;
	LPKeyMapInfo	km = g_KeyMapInfo ;

	if ( chCharCode == TEXT('C') - TEXT('@') ) {
		WMCopy() ;
		return ;
	}
	for ( i = 0 ; ; i ++, km ++ ) {
		functionID = km->functionID ;
		if ( functionID == FV_FUNC_NONE ) {
			return ;
		} else if ( km->keyMode != 1 ) {
			continue ;
		} else if ( km->keyCode == chCharCode ) {
			break ;
		}
	}
	KeyDownSub( functionID, chCharCode, fShift, fControl, fRepeat ) ;
}

void
FileView::WMKeyDown( int nVirtKey, LONG lKeyData )
{
	DWORD			i ;
	WORD			functionID ;
	BOOL			fShift   = 0x80 & GetKeyState( VK_SHIFT ) ;
	BOOL			fControl = 0x80 & GetKeyState( VK_CONTROL ) ;
	BOOL			fRepeat = (lKeyData & 0x000F) > 1 ? TRUE : FALSE ;
	LPKeyMapInfo	km = g_KeyMapInfo ;

	if ( nVirtKey == VK_TAB ) {
		KeyTAB( fShift ) ;
		return ;
	}
	for ( i = 0 ; ; i ++, km ++ ) {
		functionID = km->functionID ;
		if ( functionID == FV_FUNC_NONE ) {
			return ;
		} else if ( km->keyMode != 0 ) {
			continue ;
		} else if ( km->keyCode == nVirtKey ) {
			break ;
		}
	}
	KeyDownSub( functionID, nVirtKey, fShift, fControl, fRepeat ) ;
}

void
FileView::WMMouseDown( WPARAM wParam, LPARAM lParam )
{
	RECT	rect ;
	WORD	xPos = LOWORD( lParam ) ;
	WORD	yPos = HIWORD( lParam ) ;

	GetClientRect( m_hwnd, &rect ) ;
	if ( yPos < rect.bottom / 4 ) {
		PageUp( 1 ) ;
	} else if ( yPos < rect.bottom / 4 * 3 ) {
		ScrollDown( 1 ) ;
	} else {
		PageDown( 1 ) ;
	}
}

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

void
FileView::WMVScroll( WPARAM wParam, LPARAM lParam )
{
	DWORD	nPos = (DWORD) HIWORD(wParam) ;
	int		nScrollCode = (int) LOWORD(wParam) ;

	switch ( nScrollCode ) {
	case SB_TOP:			KeyHome() ;			break ;
	case SB_BOTTOM:			KeyEnd() ;			break ;
	case SB_LINEDOWN:		KeyDown( 1 ) ;		break ;
	case SB_LINEUP:			KeyUp( 1 ) ;		break ;
	case SB_PAGEDOWN:		KeyNext( 1 ) ;		break ;
	case SB_PAGEUP:			KeyPrior( 1 ) ;		break ;
	case SB_THUMBPOSITION:
		if ( nPos >= m_dwMaxLines ) {
			nPos = m_dwMaxLines - 1 ;
		}
		if ( m_dwTop != nPos ) {
			m_dwTop = nPos ;
			MakeScreen() ;
			InvalidateRect( m_hwnd, NULL, TRUE ) ;
			/* スクロールバーの更新 */
			SetScrollPos() ;
		}
		break ;
	}
}

void
FileView::WMSize( DWORD fwSize, WORD nWidth, WORD nHeight )
{
	RECT	rect ;

	/* 1画面に表示できる行数を求める */
	GetClientRect( m_hwnd, &rect ) ;
	m_dwLines = (rect.bottom - 2) / m_dwFontH ;
	m_dwCols = (rect.right - 2) / GetFontHW() ;
	/* 画面バッファを作成する */
	if ( m_szScreen ) {
		LocalFree( m_szScreen ) ;
	}
	m_szScreen = (LPBYTE) LocalAlloc( LMEM_FIXED, (m_dwCols + 1) * m_dwLines ) ;
	ResetContent() ;
}

DWORD
FileView::WMCopySub( LPTSTR dst, DWORD top, DWORD lines )
{
	BYTE	c, last = 0 ;
	WORD	sjis, unicode ;
	DWORD	to, count = 0 ;

	if ( top + lines < m_dwMaxLines ) {
		to = GetLinePos( top + lines ) ;
	} else {
		to = (DWORD) -1 ;
	}
	SetFilePos( top ) ;
	while ( 1 ) {
		if ( m_dwFilePos >= to ) {
			break ;
		} else if ( !GetChar( &c ) ) {
			break ;
		}
		if ( last ) {
			sjis = ((WORD) last << 8) | (WORD) c ;
			unicode = sjis2unicode_char( sjis ) ;
			last = 0 ;
		} else if ( is_kanji( c ) ) {
			last = c ;
			continue ;
		} else {
			unicode = 0xFF & c ;
		}
		if ( dst ) {
			*dst++ = unicode ;
		}
		count ++ ;
	}
	if ( dst ) {
		*dst = 0 ;
	}
	count ++ ;
	return count * sizeof (TCHAR) ;
}

/*
 * クリップボードにコピーする
 */
BOOL
FileView::WMCopy()
{
	LPTSTR	ptr ;
	DWORD	size ;
#ifdef	_WIN32_WCE
	HLOCAL	hGMem ;
#else	/* _WIN32_WCE */
	HGLOBAL	hGMem ;
#endif	/* _WIN32_WCE */

	size = WMCopySub( NULL, m_dwTop, m_dwLines ) ;
	if ( !OpenClipboard( m_hwnd ) ) {
		return FALSE ;
	} else if ( !EmptyClipboard() ) {
		CloseClipboard() ;
		return FALSE ;
	}
#ifdef	_WIN32_WCE
	hGMem = LocalAlloc( LMEM_FIXED, size ) ;
	ptr = (LPTSTR) hGMem ;
#else	/* _WIN32_WCE */
	hGMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size ) ;
	ptr = (LPTSTR) GlobalLock( hGMem ) ;
#endif	/* _WIN32_WCE */
	if ( !hGMem ) {
		CloseClipboard() ;
		return FALSE ;
	}
	WMCopySub( ptr, m_dwTop, m_dwLines ) ;
	/* クリップボードに結果をセットする */
	SetClipboardData( CF_UNICODETEXT, hGMem ) ;
#ifndef	_WIN32_WCE
	GlobalUnlock( hGMem ) ;
#endif	/* _WIN32_WCE */
	/* クリップボードクローズ */
	CloseClipboard() ;
	return TRUE ;
}

/*
 * 垂直スクロールバー制御
 */
void
FileView::SetScrollPos() const
{
	SCROLLINFO	si ;

	memset( &si, 0, sizeof si ) ;
	si.cbSize = sizeof si ;
	si.fMask = SIF_POS|SIF_RANGE ;
	si.nMin = 0 ;
	si.nMax = m_dwMaxLines - 1 ;
	si.nPos = m_dwTop ;
	SetScrollInfo( m_hwnd, SB_VERT, &si, TRUE ) ;
}

/*
 * Enterキーが押された場合
 */
void
FileView::KeyEnter() const
{
	SendMessage( m_hwndParent, WM_COMMAND, MAKEWPARAM( m_idCtrl, FVN_ENTER ), (LPARAM) m_hwnd ) ;
}

/*
 * ESCキーが押された場合
 */
void
FileView::KeyESC() const
{
	SendMessage( m_hwndParent, WM_COMMAND, MAKEWPARAM( m_idCtrl, FVN_ESC ), (LPARAM) m_hwnd ) ;
}

/*
 * TABキーが押された場合
 */
void
FileView::KeyTAB( BOOL fShift ) const
{
	WORD	cmd = fShift ? FVN_STAB : FVN_TAB ;
	SendMessage( m_hwndParent, WM_COMMAND, MAKEWPARAM( m_idCtrl, cmd ), (LPARAM) m_hwnd ) ;
}

/*
 * リストの先頭に移動する
 */
void
FileView::KeyHome()
{
	m_dwTop = 0 ;
	MakeScreen() ;
	InvalidateRect( m_hwnd, NULL, TRUE ) ;
}

/*
 * リストの最後に移動する
 */
void
FileView::KeyEnd()
{
	m_dwTop = m_dwMaxLines - 1 ;
	MakeScreen() ;
	InvalidateRect( m_hwnd, NULL, TRUE ) ;
}

/*
 * 一行上に移動する
 */
void
FileView::KeyUp( DWORD line )
{
	ScrollUp( line ) ;
}

/*
 * 一行下に移動する
 */
void
FileView::KeyDown( DWORD line )
{
	ScrollDown( line ) ;
}

/*
 * 一ページ前に移動する
 */
void
FileView::KeyPrior( DWORD page )
{
	PageUp( page ) ;
}

/*
 * 一ページ後に移動する
 */
void
FileView::KeyNext( DWORD page )
{
	PageDown( page ) ;
}

/*
 * 管理内容を初期化する
 */
void
FileView::Clear()
{
	CloseFile() ;
	FreeLinePos() ;
	*m_szFilename = 0 ;
}

BOOL
FileView::Load( LPCTSTR fn )
{
	Clear() ;
	_tcscpy( m_szFilename, fn ) ;
	return Reload() ;
}

BOOL
FileView::Reload()
{
	CloseFile() ;
	if ( !OpenFile() ) {
		return FALSE ;
	}
	if ( !MakeLinePos() ) {
		return FALSE ;
	}
	SetTopPos( 0 ) ;
	return TRUE ;
}

BOOL
FileView::Open()
{
	return OpenFile() ;
}

void
FileView::Close()
{
	CloseFile() ;
}

void
FileView::SetTopPos( DWORD pos )
{
	m_dwTop = pos == NO_LINE ? 0 : FindLinePos( pos ) ;
	MakeScreen() ;
	SetScrollPos() ;
}

/*
 * 画面を再構成する
 */
void
FileView::ResetContent()
{
	DWORD	pos ;

	if ( m_hFile != INVALID_HANDLE_VALUE ) {
		pos = GetTopPos() ;
		SetFilePos( 0 ) ;
		MakeLinePos() ;
		SetTopPos( pos ) ;
		InvalidateRect( m_hwnd, NULL, TRUE ) ;
	}
}

/*
 * TABのサイズを設定する
 */
void
FileView::SetTab( DWORD wParam, BOOL bUpdate )
{
	if ( m_dwTabSize == wParam ) {
		return ;
	}
	m_dwTabSize = wParam ;
	if ( bUpdate ) {
		ResetContent() ;
	}
}

/*
 * 縦方向のマージンを設定する
 */
void
FileView::SetHMargin( DWORD wParam, BOOL bUpdate )
{
	RECT	rect ;

	if ( m_dwHMargin == wParam ) {
		return ;
	}
	GetClientRect( m_hwnd, &rect ) ;
	m_dwHMargin = wParam ;
	m_dwFontH = GetFontH() + m_dwHMargin ;
	m_dwLines = (rect.bottom - 2) / m_dwFontH ;
	if ( bUpdate ) {
		ResetContent() ;
	}
}

/*
 * キーマップを登録する
 */
void
FileView::SetKeymap( LPKeyMapInfo keymap )
{
	if ( keymap == NULL ) {
		g_KeyMapInfo = g_DefaultKeyMapInfo ;
	} else {
		g_KeyMapInfo = keymap ;
	}
}

/*
 * 文字列を検索する
 */
int
FileView::FindString( DWORD indexStart, LPCTSTR lpszFind ) const
{
	return -1 ;
}

BOOL
FileViewRegisterClass( HINSTANCE hInst )
{
	WNDCLASS	wc ;

	wc.style         = 0 ;
	wc.lpfnWndProc   = (WNDPROC) FileViewWndProc ;
	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_FILEVIEW ;
	return ::RegisterClass( &wc ) ;
}

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

	if ( msg == WM_CREATE ) {
		LPCREATESTRUCT	lpStruct = (LPCREATESTRUCT) lParam ;
		pWindow = new FileView() ;
		SetWindowLong( hWnd, 0, (LONG) pWindow ) ;
		pWindow->WMCreate( hWnd, lpStruct ) ;
		return 0 ;
	}
	pWindow = (PFileView) 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_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_LBUTTONDOWN:	pWindow->WMMouseDown( wParam, lParam ) ;		break ;
	case WM_VSCROLL:		pWindow->WMVScroll( wParam, lParam ) ;			break ;
	case WM_SIZE:
		pWindow->WMSize( wParam, LOWORD(lParam), HIWORD(lParam) ) ;
		break ;
	case WM_GETDLGCODE:		return DLGC_WANTALLKEYS ;
	case WM_COPY:			pWindow->WMCopy() ;								break ;

	case FVM_CLEAR:			pWindow->Clear() ;								break ;
	case FVM_LOAD:			return pWindow->Load( (LPCTSTR) lParam ) ;
	case FVM_RELOAD:		return pWindow->Reload() ;
	case FVM_OPEN:			return pWindow->Open() ;
	case FVM_CLOSE:			pWindow->Close() ;								break ;
	case FVM_GETTOPLINE:	return pWindow->GetTopLine() ;
	case FVM_SETTOPLINE:	pWindow->SetTopLine( (DWORD) wParam ) ;			break ;
	case FVM_GETTOPPOS:		return pWindow->GetTopPos() ;
	case FVM_SETTOPPOS:		pWindow->SetTopPos( (DWORD) wParam ) ;			break ;
	case FVM_GETMAXLINE:	return pWindow->GetMaxLine() ;
	case FVM_SETTAB:		pWindow->SetTab( (DWORD) wParam, (BOOL) lParam ) ;		break ;
	case FVM_SETHMARGIN:	pWindow->SetHMargin( (DWORD) wParam, (BOOL) lParam ) ;	break ;
	case FVM_SETKEYMAP:		pWindow->SetKeymap( (LPKeyMapInfo) lParam ) ;	break ;
	case FVM_FINDSTRING:	return pWindow->FindString( (DWORD) wParam, (LPCTSTR) lParam ) ;

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