#define		STRICT
#include	<windows.h>
#include	<tchar.h>
#include	"tools.h"
#include	"pobox.h"
#include	"jpconv.h"
#include	"search.h"

#define		iskanji(x)		(((x)>=0x81 && (x)<=0x9F)||((x)>=0xE0 && (x)<=0xFC))
#define		MAX_PREVWORD	8
#define		MAX_PHRASELIST	256
#define		MAX_WORDLIST	256
#define		MAX_OFFSET		128
#define		MAX_DIC			16

static	void	erasecand() ;
static	int		addcand( LPCSTR cstr, LPCSTR ystr ) ;
static	void	cutstr( LPSTR buf, int size ) ;
CHAR		g_szHiragana[ 128 ] = "" ;
CHAR		g_szKatakana[ 128 ] = "" ;
CHAR		g_szYomi[ 128 ] = "" ;
CHAR		g_szNotfound[ 128 ] = "" ;
CHAR		g_szLastFound[ 128 ] = "" ;
CandPos		g_CandPos = { 0, 0, CANDWIDTH, CANDHEIGHT } ;
BOOL		g_bAbort = FALSE ;

extern "C" {
	extern	BOOL	g_fSaveDic ;		/* 辞書学習フラグ */
	extern	BOOL	g_fPredict ;		/* 予測表示フラグ */
	extern	TCHAR	MessageBuf[ 512 ] ;
	extern	BOOL	check_next_key() ;
} ;

class FileList {
public:
	BOOL	m_fEof ;
	HANDLE	m_hFile[ MAX_DIC ] ;
	CHAR	m_rBuf[ 2048 ] ;
	DWORD	m_dwWordPos, m_dwWordLen ;
	DWORD	m_dwRin, m_dwRout, m_dwPos, m_dwTopChar ;

public:
	FileList() ;
	~FileList() ;

	BOOL	Open( DWORD topChar, LPCTSTR base ) ;
	BOOL	Open( LPCSTR prefix, LPCTSTR base ) ;
	void	Close() ;
	BOOL	GetChar( CHAR *pC ) ;
	BOOL	MoveTop() ;
	BOOL	InsertTop( LPCSTR str ) ;

	static	DWORD	GetIndex( LPCSTR prefix ) ;
} ;

FileList::FileList()
{
	int		i ;

	m_dwTopChar = (DWORD) -1 ;
	for ( i = 0 ; i < MAX_DIC ; i ++ ) {
		m_hFile[ i ] = 0 ;
	}
}

FileList::~FileList()
{
	Close() ;
}

DWORD
FileList::GetIndex( LPCSTR prefix )
{
	BYTE	c = *prefix++ ;

	if ( iskanji( c ) ) {
		return (DWORD) (*prefix) % MAX_DIC ;
	}
	return 0 ;
}

BOOL
FileList::Open( DWORD topChar, LPCTSTR base )
{
	TCHAR	fn[ MAX_PATH ] ;

	m_dwRin = m_dwRout = m_dwPos = 0 ;
	m_fEof = FALSE ;
	if ( m_hFile[ topChar ] ) {
		m_dwTopChar = topChar ;
		SetFilePointer( m_hFile[ topChar ], 0, NULL, FILE_BEGIN ) ;
		return TRUE ;
	}
	wsprintf( fn, TEXT("%s\\%s\\%s%02X"), g_szDictBase, base, base, topChar ) ;
	m_hFile[ topChar ] = CreateFile( fn,
									 GENERIC_READ|GENERIC_WRITE,
									 FILE_SHARE_READ|FILE_SHARE_WRITE,
									 NULL,
									 OPEN_ALWAYS,
									 FILE_ATTRIBUTE_NORMAL,
									 0 ) ;
	if ( m_hFile[ topChar ] == INVALID_HANDLE_VALUE ) {
		m_hFile[ topChar ] = 0 ;
		return FALSE ;
	}
	m_dwTopChar = topChar ;
	return TRUE ;
}

BOOL
FileList::Open( LPCSTR prefix, LPCTSTR base )
{
	return Open( GetIndex( prefix ), base ) ;
}

void
FileList::Close()
{
	int		i ;

	for ( i = 0 ; i < MAX_DIC ; i ++ ) {
		if ( m_hFile[ i ] ) {
			CloseHandle( m_hFile[ i ] ) ;
			m_hFile[ i ] = 0 ;
		}
	}
	m_dwTopChar = (DWORD) -1 ;
}

BOOL
FileList::GetChar( CHAR *pC )
{
	BOOL	ret ;
	DWORD	nRead ;
	HANDLE	hFile ;

	if ( m_fEof ) {
		return FALSE ;
	} else if ( m_dwTopChar == (DWORD) -1 ) {
		return FALSE ;
	}
	hFile = m_hFile[ m_dwTopChar ] ;
	if ( m_dwRin == m_dwRout ) {
		m_dwRin = m_dwRout = 0 ;
		ret = ReadFile( hFile, m_rBuf, sizeof m_rBuf, &nRead, NULL ) ;
		if ( !ret || !nRead ) {
			m_fEof = TRUE ;
			return FALSE ;
		}
		m_dwRin = nRead ;
		m_dwRin %= sizeof m_rBuf ;
	}
	*pC = m_rBuf[ m_dwRout ] ;
	m_dwRout ++ ;
	m_dwRout %= sizeof m_rBuf ;
	m_dwPos ++ ;
	return TRUE ;
}

/*
 * m_dwWordPosからm_dwWordLenまでをファイル先頭に移動させる
 * ※ MessageBuf が512バイト以上あることを仮定
 */
BOOL
FileList::MoveTop()
{
	HANDLE	hFile ;
	LPBYTE	buf1 = (LPBYTE) MessageBuf ;
	LPBYTE	buf2 = &((LPBYTE) MessageBuf)[ 256 ] ;
	DWORD	ret, nWrite, buf1len, buf2len, tmp, readSize, rest ;

	if ( !m_dwWordLen ) {
		return FALSE ;
	} else if ( !m_dwWordPos ) {
		return TRUE ;
	} else if ( m_dwTopChar == (DWORD) -1 ) {
		return FALSE ;
	}
	hFile = m_hFile[ m_dwTopChar ] ;
	SetFilePointer( hFile, m_dwWordPos, NULL, FILE_BEGIN ) ;
	ret = ReadFile( hFile, buf1, m_dwWordLen, &buf1len, NULL ) ;
	if ( !ret || buf1len != m_dwWordLen ) {
		return FALSE ;
	}
	SetFilePointer( hFile, 0, NULL, FILE_BEGIN ) ;
	rest = m_dwWordPos ;
	while ( rest ) {
		/* 読み込みバッファにデータを読み込む */
		if ( rest >= 256 ) {
			readSize = 256 ;
		} else {
			readSize = rest ;
		}
		rest -= readSize ;
		ReadFile( hFile, buf2, readSize, &buf2len, NULL ) ;
		/* 書き込みバッファを満杯にする */
		tmp = 256 - buf1len ;
		if ( buf2len < tmp ) {
			tmp = buf2len ;
		}
		if ( tmp ) {
			memcpy( &buf1[buf1len], buf2, tmp ) ;
		}
		buf1len += tmp ;
		/* 読み込みバッファを調整する */
		tmp = buf2len - tmp ;
		if ( tmp ) {
			memcpy( buf2, &buf2[buf2len - tmp], tmp ) ;
		}
		buf2len = tmp ;
		/* 書き込みバッファの内容を格納する */
		SetFilePointer( hFile, -1 * (int) readSize, NULL, FILE_CURRENT ) ;
		WriteFile( hFile, buf1, buf1len, &nWrite, NULL ) ;
		/* 読み込みバッファの内容を書き込みバッファに移す */
		memcpy( buf1, buf2, buf2len ) ;
		buf1len = buf2len ;
	}
	if ( buf1len ) {
		WriteFile( hFile, buf1, buf1len, &nWrite, NULL ) ;
	}
	return TRUE ;
}

BOOL
FileList::InsertTop( LPCSTR str )
{
	HANDLE	hFile ;
	LPBYTE	buf1 = (LPBYTE) MessageBuf ;
	LPBYTE	buf2 = &((LPBYTE) MessageBuf)[ 256 ] ;
	DWORD	ret, nWrite, buf1len, buf2len, tmp, pos ;

	if ( m_dwTopChar == (DWORD) -1 ) {
		return FALSE ;
	}
	hFile = m_hFile[ m_dwTopChar ] ;
	buf1len = strlen( str ) ;
	memcpy( buf1, str, buf1len ) ;
	pos = 0 ;
	while ( 1 ) {
		SetFilePointer( hFile, pos, NULL, FILE_BEGIN ) ;
		ret = ReadFile( hFile, buf2, 256, &buf2len, NULL ) ;
		if ( !ret || !buf2len ) {
			break ;
		}
		/* 書き込みバッファを満杯にする */
		tmp = 256 - buf1len ;
		if ( buf2len < tmp ) {
			tmp = buf2len ;
		}
		if ( tmp ) {
			memcpy( &buf1[buf1len], buf2, tmp ) ;
		}
		buf1len += tmp ;
		/* 読み込みバッファを調整する */
		tmp = buf2len - tmp ;
		if ( tmp ) {
			memcpy( buf2, &buf2[buf2len - tmp], tmp ) ;
		}
		buf2len = tmp ;
		/* 書き込みバッファの内容を格納する */
		SetFilePointer( hFile, pos, NULL, FILE_BEGIN ) ;
		WriteFile( hFile, buf1, buf1len, &nWrite, NULL ) ;
		pos += buf1len ;
		/* 読み込みバッファの内容を書き込みバッファに移す */
		memcpy( buf1, buf2, buf2len ) ;
		buf1len = buf2len ;
	}
	if ( buf1len ) {
		WriteFile( hFile, buf1, buf1len, &nWrite, NULL ) ;
	}
	return TRUE ;
}

class Phrase : public FileList {

public:
	BOOL	Open( DWORD topChar ) ;
	BOOL	Open( LPCSTR prefix ) ;
	BOOL	GetLine( LPSTR prefix, LPSTR cand, LPSTR candyomi ) ;
	BOOL	InsertTop( LPCSTR prefix, LPCSTR cand, LPCSTR candyomi ) ;
} ;

BOOL
Phrase::Open( DWORD topChar )
{
	return FileList::Open( topChar, PHRASEFILE ) ;
}

BOOL
Phrase::Open( LPCSTR prefix )
{
	return Open( GetIndex( prefix ) ) ;
}

BOOL
Phrase::GetLine( LPSTR prefix, LPSTR cand, LPSTR candyomi )
{
	int		mode = 0 ;
	LPSTR	ptr = prefix ;
	CHAR	c, buf[ 256 ] ;

	m_dwWordPos = m_dwPos ;
	*prefix = *cand = *candyomi = 0 ;
	while ( GetChar( &c ) ) {
		if ( c == ' ' ) {
			if ( ptr ) {
				*ptr++ = 0 ;
			}
			switch ( mode ++ ) {
			case 0:		ptr = cand ;		break ;
			case 1:		ptr = candyomi ;	break ;
			default:	ptr = 0 ;			break ;
			}
		} else if ( c == '\n' ) {
			break ;
		} else if ( c != '\r' && ptr ) {
			*ptr++ = c ;
		}
	}
	if ( ptr ) {
		*ptr = 0 ;
	}
	if ( !*prefix ) {
		m_dwWordLen = 0 ;
		return FALSE ;
	}
	m_dwWordLen = m_dwPos - m_dwWordPos ;
	hiraNormalize( candyomi, buf, FALSE ) ;
	strcpy( candyomi, buf ) ;
	return TRUE ;
}

BOOL
Phrase::InsertTop( LPCSTR prefix, LPCSTR cand, LPCSTR candyomi )
{
	CHAR	buf[ 256 ], *ptr = buf ;

	strcpy( ptr, prefix ), ptr += strlen( ptr ) ;
	*ptr++ = ' ' ;
	strcpy( ptr, cand ), ptr += strlen( ptr ) ;
	*ptr++ = ' ' ;
	strcpy( ptr, candyomi ), ptr += strlen( ptr ) ;
	*ptr++ = '\r' ;
	*ptr++ = '\n' ;
	*ptr = 0 ;
	return FileList::InsertTop( buf ) ;
}

class Word : public FileList {

public:
	BOOL	Open( DWORD topChar ) ;
	BOOL	Open( LPCSTR prefix ) ;
	BOOL	GetLine( LPSTR cand, LPSTR candyomi ) ;
	BOOL	InsertTop( LPCSTR cand, LPCSTR candyomi ) ;
} ;

BOOL
Word::Open( DWORD topChar )
{
	return FileList::Open( topChar, WORDFILE ) ;
}

BOOL
Word::Open( LPCSTR prefix )
{
	return Open( GetIndex( prefix ) ) ;
}

BOOL
Word::GetLine( LPSTR cand, LPSTR candyomi )
{
	int		mode = 0 ;
	LPSTR	ptr = candyomi ;
	CHAR	c, buf[ 256 ] ;

	m_dwWordPos = m_dwPos ;
	*cand = *candyomi = 0 ;
	while ( GetChar( &c ) ) {
		if ( c == ' ' ) {
			if ( ptr ) {
				*ptr++ = 0 ;
			}
			switch ( mode ++ ) {
			case 0:		ptr = cand ;		break ;
			default:	ptr = 0 ;			break ;
			}
		} else if ( c == '\n' ) {
			break ;
		} else if ( c != '\r' && ptr ) {
			*ptr++ = c ;
		}
	}
	if ( ptr ) {
		*ptr = 0 ;
	}
	if ( !*candyomi ) {
		m_dwWordLen = 0 ;
		return FALSE ;
	}
	m_dwWordLen = m_dwPos - m_dwWordPos ;
	hiraNormalize( candyomi, buf, FALSE ) ;
	strcpy( candyomi, buf ) ;
	return TRUE ;
}

BOOL
Word::InsertTop( LPCSTR cand, LPCSTR candyomi )
{
	CHAR	buf[ 256 ], *ptr = buf ;

	strcpy( ptr, candyomi ), ptr += strlen( ptr ) ;
	*ptr++ = ' ' ;
	strcpy( ptr, cand ), ptr += strlen( ptr ) ;
	*ptr++ = '\r' ;
	*ptr++ = '\n' ;
	*ptr = 0 ;
	return FileList::InsertTop( buf ) ;
}

Phrase	g_Phrase ;
Word	g_Word ;

/*
 * PHRASE.Cより移行
 */
int
readphrases()
{
	int		i ;

	for ( i = 0 ; i < MAX_DIC ; i ++ ) {
		if ( !g_Phrase.Open( i ) ) {
			break ;
		}
		if ( !g_Word.Open( i ) ) {
			break ;
		}
	}
	if ( i != MAX_DIC ) {
		g_Phrase.Close() ;
		g_Word.Close() ;
		return 1 ;
	}
	return 0 ;
}

int
writephrases()
{
	g_Phrase.Close() ;
	g_Word.Close() ;
	return 0 ;
}

/*
	([プレフィクス], 侯補, 読み)の組を追加する。
*/
LPCSTR
addphrase( LPCSTR p, LPCSTR c, LPCSTR y, BOOL fForce )
{
	static CHAR	retcand[ 128 ], yy[ 128 ], pp[ 128 ] ;
	static CHAR	prefix[ 128 ], cand[ 128 ], yomi[ 128 ] ;
	LPCSTR		ret ;
	BOOL		fMoveTop = FALSE ;

	if ( fForce ) {
		*g_szNotfound = 0 ;
	}
	hiraNormalize( y, yy, FALSE ) ;
	if ( !*yy ) {
		return NULL ;
	}
	ret = NULL ;
	if ( p && *p && *c ) {
		/* 全く同じエントリがあればサルベージする */
		strcpy( pp, p ) ;
		cutstr( pp, MAX_PREVWORD ) ;
		if ( g_Phrase.Open( pp ) ) {
			while ( g_Phrase.GetLine( prefix, cand, yomi ) ) {
				if ( !strcmp( pp, prefix ) && !strcmp( c, cand ) && !strcmp( yy, yomi ) ) {
					strcpy( retcand, cand ) ;
					ret = retcand ;
					g_Phrase.MoveTop() ;
					fMoveTop = TRUE ;
					break ;
				}
			}
		}
		if ( !fMoveTop && !fForce && g_fSaveDic ) {
			g_Phrase.InsertTop( pp, c, yy ) ;
			strcpy( retcand, c ) ;
			ret = retcand ;
		}
	}
	if ( c && *c ) {
		/* 全く同じエントリがあればサルベージする */
		if ( g_Word.Open( yy ) ) {
			while ( g_Word.GetLine( cand, yomi ) ) {
				if ( !strcmp( c, cand ) && !strcmp( yy, yomi ) ) {
					if ( !ret ) {
						strcpy( retcand, cand ) ;
						ret = retcand ;
					}
					g_Word.MoveTop() ;
					fMoveTop = TRUE ;
					break ;
				}
			}
		}
		if ( !fMoveTop && (fForce || g_fSaveDic) ) {
			g_Word.InsertTop( c, yy ) ;
			if ( !ret ) {
				strcpy( retcand, c ) ;
				ret = retcand ;
			}
		}
	}
	return ret ;
}

/*
 * CAND.Cより移行
 */
CHAR	hstr[128] = "" ;				/* 入力された未確定平仮名文字列 */
CHAR	outstr[128] = "" ;				/* 確定文字列 */
Key		candkey[ MAXCANDKEYS + 1 ] ;	/* 侯補表示 */
int		g_dwNumCand = 0 ;				/* 表示侯補数 */
DWORD	g_dwCandIndex = 0 ;
DWORD	g_dwPOffset[ MAX_OFFSET ] ;
DWORD	g_dwWOffset[ MAX_OFFSET ] ;

static	int	candpos[ MAXCANDLINES ] ;

static	void
erasecand()
{
	int		i ;

	g_dwNumCand = 0 ;
	for ( i = 0 ; i < MAXCANDKEYS ; i ++ ) {
		candkey[ i ].width = 0 ;
	}
	for ( i = 0 ; i < MAXCANDLINES ; i ++ ) {
		candpos[ i ] = 0 ;
	}
}

BOOL
check_abort( void )
{
	return g_bAbort ;
}

/*
	未確平仮名定文字列 h から侯補文字列の集合を計算して表示
*/
int
makecand( CandType candtype, BOOL fForce )
{
	BYTE	c ;
	LPCSTR	prev ;
	CHAR	pat[100] ;
	DWORD	skip, offset ;
	BOOL	fFull = FALSE, fFound = FALSE ;
	static CHAR	prefix1[ 128 ], cand1[ 128 ], yomi1[ 128 ] ;

	g_bAbort = FALSE ;
	switch ( candtype ) {
	case NEWCAND:
		g_dwCandIndex = 0 ;
		g_dwPOffset[ g_dwCandIndex ] = 0 ;
		g_dwWOffset[ g_dwCandIndex ] = 0 ;
		break ;
	case NEXTCAND:
		if ( *g_szNotfound ) {
			return 1 ;
		}
		*g_szLastFound = 0 ;
		if ( g_dwCandIndex + 1 < MAX_OFFSET ) {
			g_dwCandIndex ++ ;
		}
		break ;
	case PREVCAND:
		if ( !g_dwCandIndex ) {
			return 1 ;
		}
		*g_szNotfound = 0 ;
		*g_szLastFound = 0 ;
		g_dwCandIndex -- ;
		break ;
	}
	cutstr( outstr, MAX_PREVWORD ) ;
	hiraNormalize( hstr, pat, inputmode == JAPANESE ) ;
	if ( !*pat ) {
		/* 入力文字がない場合 */
		*g_szLastFound = 0 ;
		erasecand() ;
		if ( !g_fPredict ) {
			return 1 ;
		}
		for ( prev = outstr ; !fFull && (c = *prev) ; ) {
			if ( g_Phrase.Open( prev ) ) {
				while ( g_Phrase.GetLine( prefix1, cand1, yomi1 ) ) {
					if ( !strcmp( prev, prefix1 ) ) {
						if ( !addcand( cand1, yomi1 ) ) {
							fFull = TRUE ;
							break ;
						}
					}
if ( !fForce && check_next_key() ) {
	g_bAbort = TRUE ;
	return 0 ;
}
				}
			}
			prev += iskanji( c ) ? 2 : 1 ;
		}
		return 1 ;
	} else if ( !strcmp( pat, g_szLastFound ) ) {
		/* 直前のものと同じだった場合 */
		goto skip2 ;
	}
	strcpy( g_szLastFound, pat ) ;
	strcpy( g_szYomi, pat ) ;
	strcpy( g_szHiragana, hstr ) ;
	/* ひらがなを登録 */
	erasecand() ;
	addcand( g_szHiragana, g_szYomi ) ;
	/* 曖昧検索の表を作成 */
	strcat( pat, " " ) ;
	makepat( (unsigned char*) pat, 0 ) ;
	if ( *g_szNotfound && !strncmp( hstr, g_szNotfound, strlen( g_szNotfound ) ) ) {
		goto skip1 ;
	}
	*g_szNotfound = 0 ;

	skip = g_dwPOffset[ g_dwCandIndex ] ;
	offset = 0 ;
	for ( prev = outstr ; !fFull && (c = *prev) ; ) {
		if ( g_Phrase.Open( prev ) ) {
			while ( g_Phrase.GetLine( prefix1, cand1, yomi1 ) ) {
				if ( !strcmp( prev, prefix1 ) &&
					 match( (unsigned char*) yomi1 ) ) {
					if ( skip ) {
						skip -- ;
					} else {
						if ( !addcand( cand1, yomi1 ) ) {
							fFull = TRUE ;
							break ;
						}
						offset ++ ;
						fFound = TRUE ;
					}
				}
if ( !fForce && check_next_key() ) {
	g_bAbort = TRUE ;
	return 0 ;
}
			}
		}
		prev += iskanji( c ) ? 2 : 1 ;
	}
	if ( g_dwCandIndex + 1 < MAX_OFFSET ) {
		g_dwPOffset[ g_dwCandIndex + 1 ] = g_dwPOffset[ g_dwCandIndex ] + offset ;
	}

	skip = g_dwWOffset[ g_dwCandIndex ] ;
	offset = 0 ;
	if ( g_Word.Open( g_szYomi ) ) {
		while ( g_Word.GetLine( cand1, yomi1 ) && !fFull ) {
			if ( match( (unsigned char*) yomi1 ) ) {
				if ( skip ) {
					skip -- ;
				} else {
					if ( !addcand( cand1, yomi1 ) ) {
						fFull = TRUE ;
						break ;
					}
					offset ++ ;
					fFound = TRUE ;
				}
			}
if ( !fForce && check_next_key() ) {
	g_bAbort = TRUE ;
	return 0 ;
}
		}
	}
	if ( g_dwCandIndex + 1 < MAX_OFFSET ) {
		g_dwWOffset[ g_dwCandIndex + 1 ] = g_dwWOffset[ g_dwCandIndex ] + offset ;
	}

	if ( !fFound ) {
		strcpy( g_szNotfound, hstr ) ;
	}

skip1:
	if ( !fFull ) {
		hira2kata( g_szHiragana, g_szKatakana ) ;
		addcand( g_szKatakana, g_szKatakana ) ;
	}

skip2:
	return 1 ;
}

static	int
addcand( LPCSTR cstr, LPCSTR ystr )
{
	BOOL	fInsert ;
	int		i, j, hlen, width, lines, candposx, candposy ;

	if ( g_dwNumCand >= MAXCANDKEYS ) {
		return 0 ;
	}
	hlen = strlen( hstr ) ;
	/* 既にその侯補が侯補リストにあればスキップ */
	for ( i = 0 ; i < g_dwNumCand ; i ++ ) {
		if ( strcmp( cstr, candkey[i].str ) == 0 ) {
			return 1 ;
		}
	}
	width = strwidth(cstr) + 6 ;
	candposx = g_CandPos.x ;
	candposy = g_CandPos.y ;
	lines = g_CandPos.height / CAND1LINE ;
	for ( i = 0 ; i < lines ; i ++ ) {
		if ( candpos[i] == 0 || candposx+candpos[i]+width < g_CandPos.width ) {
			break ;
		}
	}
	if ( i >= lines ) {
		return 0 ;
	}
	/* candkey配列に追加する */
	candposx += candpos[ i ] ;
	candposy += i * CAND1LINE ;
	candpos[ i ] += width ;
	fInsert = FALSE ;
	for ( j = 0 ; j < MAXCANDKEYS ; j ++ ) {
		if ( !candkey[ j ].width ) {
			break ;
		} else if ( candkey[ j ].y > candposy ) {
			fInsert = TRUE ;
			break ;
		}
	}
	if ( fInsert ) {
		for ( i = g_dwNumCand ; i > j ; i -- ) {
			memcpy( &candkey[ i ], &candkey[ i - 1 ], sizeof (Key) ) ;
		}
	} else {
		j = g_dwNumCand ;
	}
	candkey[ j ].x      = candposx ;
	candkey[ j ].y      = candposy ;
	candkey[ j ].width  = width ;
	candkey[ j ].height = CAND1LINE ;
	strcpy( candkey[ j ].str,  cstr ) ;
	strcpy( candkey[ j ].yomi, ystr ) ;
	/* candkey配列の最後をクリアする */
	g_dwNumCand ++ ;
	candkey[ g_dwNumCand ].width = 0 ;
	if ( g_dwNumCand >= MAXCANDKEYS ) {
		return 0 ;
	}
	return 1 ;
}

/*
 * 指定された文字列をsizeバイトに切りつめる
 */
void
cutstr( LPSTR buf, int size )
{
	BYTE	c ;
	LPSTR	ptr ;
	int		len = strlen( buf ), wordlen ;

	if ( len <= size ) {
		/* 切りつめる必要がない場合 */
		return ;
	}
	len -= size ;
	ptr = buf ;
	while ( (c = *ptr++) ) {
		if ( iskanji( c ) ) {
			wordlen = 2 ;
			ptr ++ ;
		} else {
			wordlen = 1 ;
		}
		if ( len <= wordlen ) {
			strcpy( buf, ptr /*- wordlen*/ ) ;
			break ;
		}
		len -= wordlen ;
	}
}
