# Palm用インラインPOBox

現在のところ、 公開している部分(マニュアル/掲示板)と 内部使用の部分(ソース)が 分離している。

## ■ TODO

公開している 要望掲示板および 要望番付をもとにしている。

## ■ 更新履歴

更新履歴

## ■ 辞書について

### 辞書の構造

“{コンテクスト\001}検索パタン{\002単語}\n” というエントリの集合を辞書とする。
例文辞書の場合はコンテクストを指定する。
POBox 検索パタンと単語が一致する場合(英語の場合など)は検索パタンは省略できる。

PDBのレコード番号で辞書を区別している。

- 0x00〜0xff
  単語辞書。読み(スペル)の最初の文字がレコード番号。 (e.g. 辞書エントリ'masui\002増井'のレコード番号は0x6d)
- 0x100〜0x8ff
  例文辞書。コンテクスト(カーソル位置の左の文字列)長 × 0x100 ＋ コンテクストの最後の文字コード。 (e.g. 辞書エントリ'よろしく\001onegai\002お願い'のレコード番号は0x8ad)
- 0x900〜0x9ff
  環境辞書。アプリから8ビットのハッシュを計算したものに0x900を足す。

1.1.4以降は このような構造 になっている。

### 辞書のバックアップ

PDBのバックアップビットをプレファレンスで設定可能としている。 バックアップビットを立てるとHotSyncでバックアップされる。

1.4.4以降で PBinlearnDBという学習専用辞書を用意した。 ( 記事)

### データベース形式について

メモリ内のデータベースとPDB, PRCはほとんど同じ形をしている。 PRCを作るにははbuild-prcが使えるが PDBを作るにはword2pdbのように自力で作る。
PDB, PRC内のバックアップビットが立っているとHotSyncで自動的に バックアップされる。

■ 関連文書

DB関連マニュアル (PDF)

プログラム

    #pragma pack(2)

    #define SEARCHAGAIN	// 候補選択後、次候補を表示するかどうか
    #define SHOWPATINWIN	// 入力パタンをもとFieldでなく候補枠に表示する

    #if SDK_VERSION >= 35
    #include
    #ifdef USEVFS
    #include
    #endif
    #else
    #include
    #include "NewTypes.h"
    #endif

    #include
    #include
    #include
    #include

    #include "DropModule.h" // Drag&Dropモジュール対応のため

    #include "pbinline.h"
    #include "search.h"
    #include "ctype.h" // ToUpper(), etc.
    #include "dict.h"
    #include "id.h"
    #include "dispmode.h"

    // #include "sony.h"
    #include "SonyChars.h"

    #ifdef WNN_PALM_OS
    #include "libwnn.h"
    #endif

    Err PBInline(FieldPtr fld, EventPtr e);
    Err PBEnable(PointType *start, PointType *end);
    void GsiEnableHack(Boolean enable);
    void GsiSetShiftStateHack(UInt16 lockstate, UInt16 tempshift);

    static void OpenPBInline(Global *g);
    static void ClosePBInline(Global *g);
    static Boolean AppHandleEvent(Global *g,EventPtr e);
    static void EraseCand(Global *g);
    static Boolean AddCand(Global *g,unsigned char *cstr,int study);
    static void SelCand(Global *g, int n);
    static void SearchCand(Global *g, int kind);
    static void InsertPat(Global *g);
    static void InsertPatAndFix(Global *g);
    static void InsertPatAndSearch(Global *g);
    static void RegisterPhrase(Global *g, DmOpenRef dic, int n);
    static int NextDictRecord(Global *g, int resid, int c);

    //void OpenRomaTable(Global *g);
    //void CloseRomaTable(Global *g);
    //unsigned char *Roma2Kana(Global *g,unsigned char *s, int len);
    int CurrentEnvironment();

    static Boolean ASCIIStr(unsigned char *s);
    static Boolean AlphaStr(unsigned char *s);

デバッグ用のソースとかは小さなフォントにしておこう。

    static int HexChar(int n)
    {
    	return (n < 10 ? '0'+n : 'A'+(n-10));
    }

    void
    DumpHex(int val, int x, int y)
    {
    	unsigned char buf[10];
    	buf[0] = HexChar((val >> 12) & 0xf);
    	buf[1] = HexChar((val >> 8) & 0xf);
    	buf[2] = HexChar((val >> 4) & 0xf);
    	buf[3] = HexChar(val & 0xf);
    	WinDrawChars(buf,4,x,y);
    }

    VoidPtr GetObjPtr(Int id)
    {
    	VoidPtr p = NULL;
    	FormPtr frm;
    	UInt16 idx;
    	if((frm = FrmGetActiveForm())){
    		idx = FrmGetObjectIndex(frm, id);
    		ErrFatalDisplayIf(idx==NULL,"GetObjPtr fail");
    		p = FrmGetObjectPtr(frm, idx);
    		ErrFatalDisplayIf(p==NULL,"Null Ptr");
    	}
    	return p;
    }

    Boolean BackupBit(DmOpenRef dic)
    {
    	LocalID id;
    	UInt16 cardno;
    	UInt16 attr;

    	DmOpenDatabaseInfo(dic,&id,NULL,NULL,&cardno,NULL);
    	DmDatabaseInfo(cardno,id,NULL,&attr,NULL,NULL,NULL,NULL,NULL,NULL,NULL,	NULL,NULL);
    	return (attr & dmHdrAttrBackup ? true : false);
    }

    void SetBackupBit(DmOpenRef dic,int backup)
    {
    	LocalID id;
    	UInt16 cardno;
    	UInt16 attr;

    	DmOpenDatabaseInfo(dic,&id,NULL,NULL,&cardno,NULL);
    	DmDatabaseInfo(cardno,id,NULL,&attr,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
    	attr = (backup ? (attr | dmHdrAttrBackup) : (attr & ~dmHdrAttrBackup));
    	DmSetDatabaseInfo(cardno,id,NULL,&attr,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
    }

    Boolean Lock(void) // HackのLockをこころみ、成功すればtrueを返す
    {
    	Err err;
    	UInt32 locked;
    	err = FtrGet(CREATORID,FEATURE_LOCK,&locked);
    	if(err) locked = 0;
    	if(locked == 0){
    		FtrSet(CREATORID,FEATURE_LOCK,1); // Lock
    		return true;
    	}
    	return false;
    }

    void Unlock(void)
    {
    	FtrSet(CREATORID,FEATURE_LOCK,0); // UnLock
    }

    static void GetFeaturePref(int option, UInt32 *featurep, int defaultval)
    //
    //	Featureとしてセーブされたオプションを取得する。
    //	設定されていない場合はデフォルト値を設定する。
    //
    {
    	Err err;
            err = FtrGet(CREATORID,option,featurep);
    	if(err){
    		*featurep = (UInt32)defaultval;
    		FtrSet(CREATORID,option,*featurep);
    	}
    }

    #ifdef USEVFS
    FileRef CheckVFSDict()
    //
    //	CLIEのメモリースティックのような外部ファイルシステムが
    //	存在するか調べ、存在する場合はFileRefを返す
    //
    {
    	Err err;
    	UInt32 vfsMgrVersion;
    	UInt32 volIterator;
    	UInt16 volRefNum;
    	FileRef fileRef;

    	// VFS Managerが存在するかチェック
    	err = FtrGet(sysFileCVFSMgr, vfsFtrIDVersion, &vfsMgrVersion);
    	if(err) return NULL; // 外部メモリが存在しない

    	// マウントされているボリュームを取得する。複数マウントされて
    	// いる場合は最後のものを返す。
    	volIterator = expIteratorStart;
    	for(;;){
    		err = VFSVolumeEnumerate(&volRefNum,&volIterator);
    		if(err) return NULL;
    		if(volIterator == expIteratorStop) break;
    	}

    	// POBox辞書をオープン
    	err = VFSFileOpen(volRefNum,"/PALM/Programs/POBox/PBInlineDB.pdb",
    		vfsModeRead,&fileRef);
    	if(err) return NULL; // POBox辞書が存在しない

    	return fileRef;
    }
    #endif

## メインルーチン

    Err PBInline(FieldPtr fld, EventPtr e)
    //
    //	sysTrapFldHandleEvent() の置き換え(Hack)関数
    //
    {
    	EventType event;
    	Global *g;
    	UInt32 ftrvalue;
    	UInt32 enable;
    	UInt32 shift;
    	UInt32 selection; // 選択領域 - 前に選択した領域が現在の選択領域と同じならばPOBox継続と思う
    	FormPtr frm;
    	FldHandleEventTrapFunc *trapfunc;
    	UInt16 c;
    	Err err;
    	Boolean poboxinprogress;
    	int i;
    //	POBoxPref pref;
    	UInt16 prefsize;

    	UInt16 wb,we;
    	UInt dbmode;

    	MemHandle h;
    	UChar *s;

    	// 2000/11/11 04:47:17
    	UInt alarmamp,sysamp,defamp;       // デフォルトの音量
    	UInt pbalarmamp,pbsysamp,pbdefamp; // 音を消すためのもの

    	if(e->eType == nilEvent) return 1; // 2001/7/23 Stowaway Keyboard対応?

    	SndGetDefaultVolume(&alarmamp,&sysamp,&defamp);
    	pbalarmamp = pbsysamp = pbdefamp = 0;

    	// オリジナルのトラップを取得
    	FtrGet(CREATORID,1000,&ftrvalue);
    	trapfunc = (FldHandleEventTrapFunc*)ftrvalue;

    	// POBoxを使用するモードかどうか調べる
    	GetFeaturePref(FEATURE_ENABLE,&enable,false);

    	// Command+Space, キーボード呼出文字などの場合トグルする
    	if(e->eType == keyDownEvent &&
    	   (e->data.keyDown.chr == 0xf2   || // Alt-X for Palm Portable Keyboard
    	    e->data.keyDown.chr == 0x13ac || // Alt-X for Happy Hacking Keyboard
    	    e->data.keyDown.chr == 0x00d7 || // Alt-X for GoType!
    	    e->data.keyDown.chr == TOGGLECHR
    		)){
    		// Palm Portable KBはCommand-なんとかとかControl-なんとかを出力しないので
    		// 上記のような変なコードを割り当てている。
    		if(!enable){
    			// POBoxが動いていないときのシフトインジケータの状態を得て保存
    			shift = (UInt32)GsiEnabled();
    			FtrSet(CREATORID,FEATURE_SHIFT,shift);
    		}
    		enable = !enable;
    		DisplayPOBoxMode((int)enable);
    		FtrSet(CREATORID,FEATURE_ENABLE,enable);
    		if(!enable){
    			GsiEnable(shift ? true : false);
    		}
    		return 0;
    	}

    	// 上ドラッグ-下ドラッグ, 下ドラッグ-上ドラッグでon/offするように (2000/11/07 12:29:39)
    	if(e->eType == keyDownEvent){
    		if(e->data.keyDown.chr == nextFieldChr){
    			enable = true;
    			DisplayPOBoxMode(enable);
    			FtrSet(CREATORID,FEATURE_ENABLE,enable);
    			return 0;
    		}
    		else if(e->data.keyDown.chr == prevFieldChr){
    			enable = false;
    			DisplayPOBoxMode(enable);
    			FtrSet(CREATORID,FEATURE_ENABLE,enable);
    			FtrGet(CREATORID,FEATURE_SHIFT,&shift);
    			GsiEnable(shift ? true : false);
    			return 0;
    		}
    	}

    	if(! enable){
    		GsiEnable(true);
    		return (*trapfunc)(fld,e);
    	}
    	GsiEnable(false);

    	DisplayPOBoxMode(true); // 2001/7/23 StowawayキーボードのドライバがnilEventを吐くのに

    	// キーダウンイベントのときだけPOBox起動
    	if(e->eType != keyDownEvent){
    		UInt32 selection = 0xffffffff;
    		// POBox入力継続中止
    		FtrSet(CREATORID,FEATURE_SELECTION,selection);
    		// もともとのシステムコール呼出し
    		return (*trapfunc)(fld,e);
    	}
    	//
    	// Keyboard, Keyboard hackなどでもうまく動くようにする工夫。
    	// POBox終了時の選択領域がPOBox開始時の選択領域と一致した場合は
    	// POBoxがまだ実行中だということで、空白文字や後退文字でも
    	// POBox的処理を行なう。そうでない場合はアルファベットが入力
    	// されたときだけPOBox枠を表示して変換を開始する。
    	//
    	poboxinprogress = false;
    	FldGetSelection(fld,&wb,&we);
    	err = FtrGet(CREATORID,FEATURE_SELECTION,&selection);
    	if(!err){
    		if(wb != we &&
    		  (wb == (UInt16)((selection & 0xffff0000) >> 16)) &&
    		  (we == (UInt16)(selection & 0xffff))){ // 前の選択と同じ
    			poboxinprogress = true;
    		}
    	}
    	if(! poboxinprogress){
    		c = e->data.keyDown.chr;
    		if(c < 0x20 || c >= 0x100 || (c == 0x20 && wb == we)){
    			return (*trapfunc)(fld,e);
    		}
    	}

    	if(! Lock()){ // 二重よびだし禁止
    		return (*trapfunc)(fld,e);
    	}

    	DisplayPOBoxMode(true);

    	if((frm = FrmGetActiveForm()) == NULL) return 0;
    	if(FrmGetFocus(frm) == -1) return 0;

    #ifdef WNN_PALM_OS
    	if(wnn_palm_init()) return 0;
    	if(wnn_dic_enable()){
    		wnn_palm_term();
    		return 0;
    	}
    #endif

    	// グローバルデータ領域確保
    	g = (Global*)MemPtrNew(sizeof(Global));
    	ErrFatalDisplayIf(g==NULL,"Can't alloc global data");

    	//
    	// 「大域変数」をFeatureに格納しておく
    	//
    	FtrSet(CREATORID,FEATURE_GLOBAL,(UInt32)g);

    	g->origform = frm;
    	g->field = fld;

    	//
    	// 空白文字を入力すると遅延変換
    	//
    	if(e->data.keyDown.chr != 0x20){
    		g->useselection =  poboxinprogress;
    		EvtAddEventToQueue(e); // Fieldに入力されたイベント(キー入力とか)がPOBox枠で処理されるようにする。
    	}
    	else {
    		g->useselection = true;
    	}

    	/////////////////////////////////////////////////////////////////
    	//                       辞書関連初期化                        //
    	/////////////////////////////////////////////////////////////////

    	//
    	// (もしあれば)学習辞書データベースオープン 2000/11/11 21:49:39
    	//
    	g->ldic = DmOpenDatabaseByTypeCreator(LDICTYPE,CREATORID,dmModeReadWrite);

    	// 辞書データベースオープン
    	//
    	// 学習辞書があるときは拡張辞書は読み出し専用にする。
    	// (TRG Proのフラッシュに焼けるようになることを期待) 2001/1/15
    	//
    	// g->dicはNULLかもしれないので注意。(1.4.x以前ではg->dicは常にnon-NULL)
    	//
    	dbmode = (g->ldic ? dmModeReadOnly : dmModeReadWrite);
    	g->dic = DmOpenDatabaseByTypeCreator(DICTYPE,CREATORID,dbmode);

    #ifdef USEVFS
    	// (もしあれば)外部メモリ辞書オープン 2001/5/20
    	//
    	g->vdic = CheckVFSDict();
    #endif

    	// 学習辞書と固定辞書(もしあれば)の存在チェック
    	if(g->ldic){
    #ifdef USEVFS
    		ErrFatalDisplayIf(g->dic == NULL && g->vdic == NULL,
    			"Learning dict with no static dictionary");
    #else
    		ErrFatalDisplayIf(g->dic == NULL,
    			"Learning dict with no static dictionary");
    #endif
    	}
    	else {
    		ErrFatalDisplayIf(g->dic == NULL, "No learning dictionary");
    	}

    	//
    	//	拡張辞書の数と先頭文字を調査
    	//
    #ifdef USEVFS
    	if(g->vdic){
    		//
    		// VFS(メモリースティックなど)内部に固定辞書DBがある場合
    		//
    		Int32 pos;
    		UInt8 c;
    		UInt32 *offset,dummy;
    		Boolean extraindexexists;
    		UInt16 nrecords;

    		// レコード数を取得
    		VFSFileDBInfo(g->vdic,NULL,
    			NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
    			&nrecords);

    		// 拡張辞書のインデクスが存在するか調べる
    		err = VFSFileDBGetRecord(g->vdic,nrecords-1,&h,(UInt8*)NULL,(UInt32*)NULL);
    		ErrFatalDisplayIf(err,"Can't get VFS handle");
    		ErrFatalDisplayIf(h==NULL,"Can't open extra dictionary index");
    		s = MemHandleLock(h);
    		extraindexexists = (*s == '\0');
    		if(extraindexexists){
    			StrCopy(g->extra,s+1);
    			g->nextra = StrLen(g->extra);
    		}
    		MemHandleUnlock(h);
    		MemHandleFree(h);

    		if(!extraindexexists){
    			// VFSFileDBGetRecord()が遅いのでPDBファイルの内容を自力で読む。
    			// PDBファイルヘッダは78バイトで、その後にポインタ等が
    			// レコードの数だけ並んでいる。
    			pos = 78 + (8 * 0xa00);
    			VFSFileSeek(g->vdic,fsOriginBeginning,pos);
    			g->nextra = nrecords - 0xa00;
    			if(g->nextra > MAXEXTRA) g->nextra = MAXEXTRA;
    			offset = MemPtrNew(sizeof(UInt32) * (nrecords - 0xa00));
    			for(i=0;i< g->nextra; i++){
    				VFSFileRead(g->vdic,4,&offset[i],NULL);
    				VFSFileRead(g->vdic,4,&dummy,NULL);
    			}
    			for(i=0;i< g->nextra; i++){
    				VFSFileSeek(g->vdic,fsOriginBeginning,offset[i]);
    				VFSFileRead(g->vdic,1,&c,NULL);
    				g->extra[i] = c;
    			}
    			MemPtrFree(offset);
    		}
    	/*
    		// メモリ内データベースと同様のやり方を適用した場合。
    		// 拡張辞書エントリをひとつずつ調べると非常に遅い!
    		// VFSFileDBGetRecord()がいちいちメモリに展開するからと思われる
    		for(i=0;i< g->nextra;i++){
    			err = VFSFileDBGetRecord(g->vdic,0xa00+i,&h,(UInt8*)NULL,(UInt32*)NULL);
    			ErrFatalDisplayIf(err,"Can't get VFS handle");
    			ErrFatalDisplayIf(h==NULL,"Can't open extra dictionary area");
    			s0 = MemHandleLock(h);
    			g->extra[i] = *s0;
    			MemHandleUnlock(h);
    			MemHandleFree(h);
    		}
    	*/
    	}
    	else
    #endif
    	if(g->dic){
    		g->nextra = DmNumRecords(g->dic) - 0xa00;
    		if(g->nextra > MAXEXTRA) g->nextra = MAXEXTRA;
    		for(i=0;i< g->nextra;i++){
    			h = DmQueryRecord(g->dic,0xa00+i);
    			ErrFatalDisplayIf(h==NULL,"Can't open extra dictionary area");
    			s = MemHandleLock(h);
    			g->extra[i] = *s;
    			MemHandleUnlock(h);
    		}
    	}
    	else { // NOTREACHED
    		ErrFatalDisplayIf(true,"No dict");
    	}

    	// Formを呼びだせるようにするために共通データベースをオープン
    	g->db = DmOpenDatabaseByTypeCreator(APPTYPE,CREATORID,
    					    dmModeReadWrite);
    	ErrFatalDisplayIf(g->db==NULL,"Can't open PBInline DB");

    	//
    	//	プレファレンス
    	//
    	prefsize = sizeof(POBoxPref);
    	if(PrefGetAppPreferences(CREATORID,0,&(g->pref),&prefsize,true) == noPreferenceFound){
    		g->pref.savedict = false;
    		g->pref.dispbottom = false;
    		g->pref.spacefortankanji = false;
    		g->pref.righthanded = true;
    		PrefSetAppPreferences(CREATORID,0,1,&(g->pref),sizeof(POBoxPref),true);
    	}

    	g->prevwascommandkey = false;
    	g->prevwordlen = 0;
    	g->exact = 0;

    	g->searchlevel[0] = 0;

    //	OpenRomaTable(g);
    	g->rktable = InitRomaTable();

    	OpenPBInline(g); // PBInline画面初期化

    	SearchCand(g,NEWCAND); // 何故かこれが無いと動かない??
    #ifdef XXXX
    	g->patlen = 0;
    	g->pat[g->patlen++] = (char)e->data.keyDown.chr;
    	InsertPatAndSearch(g);
    #endif

    //GrfSetState(true,false,false);
    	g->finish = false;

    	/////////////////////////////////////////////////////////////////
    	//                  PBInlineのイベント処理ループ               //
    	/////////////////////////////////////////////////////////////////

    	do {
    		UInt16 error;

    		// 2000/11/11 04:50:02
    		// フォーム外をタップするとエラー音がするので
    		// 一時的に音量をゼロにする
    		SndSetDefaultVolume(&pbalarmamp,&pbsysamp,&pbdefamp);

    		EvtGetEvent(&event, evtWaitForever);

    		SndSetDefaultVolume(&alarmamp,&sysamp,&defamp);
    //if(event.eType == nilEvent) continue;
    		if(event.eType == penDownEvent &&
    		  (event.screenY < 0 ||
    		  (event.screenY > 20 && g->pbform->window.windowBounds.topLeft.y + event.screenY < 160))){
    			MenuBarType *menu;
    			menu = MenuGetActiveMenu();
    			if(! (menu && menu->attr.visible)){ // メニューが出ているとき以外は枠外をタップされると終了する
    				EventType evt;
    				evt.eType = keyDownEvent;
    				// \n, \r などによりPOBox枠の終了を指示する。
    				// \r のときは選択領域を変化させない。(次回に継続)
    				evt.data.keyDown.chr = '\n'; // 窓終了文字
    				evt.data.keyDown.chr = '\r'; // 窓終了文字
    				evt.data.keyDown.modifiers = 0;
    				EvtAddEventToQueue(&evt);
    				EvtAddEventToQueue(&event); // 本来のイベントを使う。(荒井氏による指摘)
    			}
    			continue;
    		}
    		if (AppHandleEvent(g,&event)) continue;
    		if (SysHandleEvent(&event)) continue;
    		if (MenuHandleEvent(NULL, &event, &error)) continue;
    		FrmDispatchEvent(&event);
    	}
    	//
    	// Applicationボタンを押した場合はすぐに別アプリに移るように
    	// してみていたが、TODOなどではappStopEventの処理が足りないせいか
    	// FatalErrorになってしまうので、POBox枠が出ているときは
    	// 終了できないことにする。
    	//
    #ifndef FINISH_IF_APPBUTTON_IS_PRESSED
    	while (! g->finish);
    #else
    	while (! g->finish && event.eType != appStopEvent);
    	if(event.eType == appStopEvent){
    		EvtAddEventToQueue(&event);
    	}
    #endif

    //	CloseRomaTable(g);

    	if(g->dic) DmCloseDatabase(g->dic);
    	if(g->ldic) DmCloseDatabase(g->ldic);
    #ifdef USEVFS
    	if(g->vdic) VFSFileClose(g->vdic);
    #endif
    	DmCloseDatabase(g->db); /////

    	Unlock();
    	MemPtrFree(g->rktable);
    	MemPtrFree(g);

    //	DisplayPOBoxMode(true);
    	FtrGet(CREATORID,FEATURE_ENABLE,&enable);
    	if(enable){
    		DisplayPOBoxMode(true);
    	}
    	else {
    		DisplayPOBoxMode(false);
    		FtrGet(CREATORID,FEATURE_SHIFT,&shift);
    		GsiEnable(shift ? true : false);
    	}

    	//
    	// 次回のPBInline()呼出しがこの呼出しに引き続くものかどうか
    	// 判断できるようにするため、現在の領域選択状況をFeatureに記憶しておく。
    	// この値が変化すればPOBox処理は継続でないと判断する。
    	//
    	FldGetSelection(fld,&wb,&we);
    	selection = ((((UInt32)wb) << 16) & 0xffff0000) + ((UInt32)(we) & 0xffff);
    	FtrSet(CREATORID,FEATURE_SELECTION,selection);

    #ifdef WNN_PALM_OS
    	wnn_dic_disable();
    	wnn_palm_term();
    #endif

    	return 1;
    }

    /*
    PBInline画面開始/終了

    */

    static Boolean MainFormEvent(EventPtr e) // ダミー
    {
    	return false;
    }

    static Boolean PrefFormEvent(EventPtr e)
    {
    	Boolean handled = false;
    	ControlPtr cp;
    	UInt16 c;

    	Global *g;
    	Err err;

    	err = FtrGet(CREATORID,FEATURE_GLOBAL,(UInt32*)(&g));
    	ErrFatalDisplayIf(err,"Can't get global value");

    	switch(e->eType){
    	case ctlSelectEvent:
    		handled = true;
    		c = e->data.ctlSelect.controlID;
    		cp = e->data.ctlSelect.pControl;
    		ErrNonFatalDisplayIf(cp==NULL,"Invalid CtrPtr");
    		switch(c){
    		case ID_CHECKBOX_BACKUP:
    			g->tmppref.savedict = (CtlGetValue(cp) ? true : false);
    			break;
    		case ID_BUTTON_NEARCURSOR:
    			g->tmppref.dispbottom = (CtlGetValue(cp) ? false : true);
    			break;
    		case ID_BUTTON_BOTTOM:
    			g->tmppref.dispbottom = (CtlGetValue(cp) ? true : false);
    			break;
    		case ID_BUTTON_USESPACE:
    			g->tmppref.spacefortankanji = (CtlGetValue(cp) ? true : false);
    			break;
    		case ID_BUTTON_USERETURN:
    			g->tmppref.spacefortankanji = (CtlGetValue(cp) ? false : true);
    			break;
    		case ID_BUTTON_RIGHTHANDED:
    			g->tmppref.righthanded = (CtlGetValue(cp) ? true : false);
    			break;
    		case ID_BUTTON_LEFTHANDED:
    	      		g->tmppref.righthanded = (CtlGetValue(cp) ? false : true);
    			break;
    		default:
    			handled = false;
    			break;
    		}
    	default:
    		break;
    	}
    	return handled;
    }

    static void OpenPBInline(Global *g)
    {
    	short x,y,ny;
    	UInt16 wb,we;
    	int cb,ce; // begin/end of context
    	UChar *fieldtext;
    	UInt16 bytes;
    	unsigned char c;

    Boolean capslock,numlock,autoshifted;
    UInt16 tmpshift;

    GrfGetState(&capslock,&numlock,&tmpshift,&autoshifted);

    	DisplayPOBoxMode(true);

    	g->pbform = FrmInitForm(ID_MAINFORM);
    	ErrFatalDisplayIf(g->pbform==NULL,"Can't init Form");
    //	FrmSetEventHandler(g->pbform, MainFormEvent);
    //	FrmSetActiveForm(g->pbform);

    	g->patlen = 0;
    	g->pat[g->patlen] = '\0';

    	// 現在のコンテクストを取得
    	fieldtext = FldGetTextPtr(g->field);
    	if(fieldtext){
    		FldGetSelection(g->field,&wb,&we);
    		if(wb==we) wb = we = FldGetInsPtPosition(g->field);
    		if(g->useselection){
    			int i;
    			g->patlen = we - wb;
    			for(i=0;i<=g->patlen;i++){
    				g->searchlevel[i] = 0;
    			}
    		}
    		else {
    			g->patlen = 0;
    			if(wb != we){
    				// 2000/11/11 04:43:00
    				// カット対象が無いときFldCutを呼ぶと
    				// エラー音がするようなので
    				FldCut(g->field);
    			}
    			g->useselection = true;
    		}
    		StrNCopy(g->pat,fieldtext+wb,g->patlen);
    		g->pat[g->patlen] = '\0';
    		cb = ce = (int)wb;
    		while(cb > 0 && fieldtext[cb-1]!='\n') cb--;
    		while(ce > 0 && fieldtext[ce-1]==' ') ce--;
    		while(ce-cb > 8){
    			cb += (fieldtext[cb] & 0x80 ? 2 : 1);
    		}
    		g->clen = ce - cb;
    		StrNCopy(g->context,fieldtext+cb,g->clen);
    		g->context[g->clen] = '\0';

    		for(;;){
    			c = fieldtext[cb];
    			bytes = ((c & 0x80) ? 2 : 1);
    			if(cb + bytes >= ce) break;
    			cb += bytes;
    		}
    		g->alphaend = ((c & 0x80) == 0 && IsAlpha(c) && cb < ce) ? true : false;

    		if(g->prevwordlen == 0){
    			StrNCopy(g->prevword,g->context,g->clen);
    			g->prevwordlen = g->clen;
    		}

    	}
    	else {	// Fieldが完全に空だとFldGetTextPtr()はNULLを返す
    		g->clen = 0;
    	}
    	g->context[g->clen] = '\0';

    //	EraseCand(g);

    	if(g->pref.dispbottom){
    		g->pbform->window.windowBounds.topLeft.y = 144;
    	}
    	else {
    		InsPtGetLocation(&x,&y);
    		ny = y + 13;
    		if(ny + 14 >= 160) ny = y - 14;
    		g->pbform->window.windowBounds.topLeft.y = ny;
    	}

    FrmSetEventHandler(g->pbform, MainFormEvent);
    FrmSetActiveForm(g->pbform);
    EraseCand(g);

    	FrmDrawForm(g->pbform);

    }

    static void ClosePBInline(Global *g)
    {
    	Boolean capslock,numlock,autoshifted;
    	UInt16 tmpshift;

    	GrfGetState(&capslock,&numlock,&tmpshift,&autoshifted);

    	FrmReturnToForm(NULL); // アプリケーション画面に復帰
    	// このときにGraffitiのシフト状態が初期化されてしまうようなので
    	// 前のフォームでのの状態を回復するようにする。

    	GrfSetState(capslock,numlock,tmpshift);
    	DisplayPOBoxMode(true);
    }

    static void regword(Global *g)
    {
    	unsigned char buf[200];
    	MemHandle h;
    	unsigned char *p;
    	UInt16 length;
    	FormPtr frm;
    	int x,y;
    	UInt16 id;
    	DmOpenRef d;

    	d = (g->ldic ? g->ldic : g->dic);

    	h = ClipboardGetItem(clipboardText,&length);
    	if(h == NULL || length > 30){
    		FrmAlert(ID_ALERT_REGWORD);
    		return;
    	}
    	p = MemHandleLock(h);
    	ErrFatalDisplayIf(p == NULL,"Null clipboard text");

    	frm = FrmInitForm(ID_REGWORDFORM);
    	ErrFatalDisplayIf(frm==NULL,"Null frm");
    	FrmSetActiveForm(frm);
           	FrmDrawForm(frm);

    	x = 12; y = 33;
    	WinDrawChars("\x81\x75",2,x,y);		x += 10;
    	WinDrawChars(p,length,x,y);		x += FntCharsWidth(buf,length);
    	WinDrawChars("\x81\x76",2,x,y);		x += 10;
    	WinDrawChars("\x81\x75",2,x,y);		x += 10;
    	WinDrawChars(g->pat,g->patlen,x,y);	x += FntCharsWidth(g->pat,g->patlen);
    	WinDrawChars("\x81\x76",2,x,y);

    	id = FrmDoDialog(frm);
    	if(id == ID_BUTTON_REGW_OK){
    		int ind;
    		g->pat[g->patlen] = '\0';
    		StrToLower(buf,g->pat);	// 2000/11/12 13:45:44
    		//StrNCopy(buf,g->pat,g->patlen);
    		StrCopy(buf+g->patlen,"\002");
    		StrNCopy(buf+g->patlen+1,p,length);
    		buf[g->patlen+1+length] = '\0';
    		ind = buf[0];
    		if(IsUpper(ind)) ind = ToLower(ind);
    		AddDicEntry(d,ind,buf,true);
    		FrmReturnToForm(NULL); // 最初のフォームに戻る
    		ClosePBInline(g);
    		OpenPBInline(g);
    		SearchCand(g,NEWCAND);
    	}
    	else {
    		FrmReturnToForm(NULL); // 最初のフォームに戻る
    	}
    	MemHandleUnlock(h);
    }

    static void delent(Global *g, unsigned char *ent)
    {
    	register unsigned char *s,*p,*entry;
    	MemHandle h;
    	int ind;
    	unsigned char *s0;
    	DmOpenRef d,ds;

    	d = (g->ldic ? g->ldic : g->dic);
    	ds = d;

    	ind = ent[0];
    	if(IsUpper(ind)) ind = ToLower(ind);
    	while(ind){
    		h = DmQueryRecord(ds,ind);
    		ErrFatalDisplayIf(h == NULL,"Null handle...");
    		s0 = MemHandleLock(h);
    		ErrFatalDisplayIf(s0 == NULL,"Null dict entry");

    		s=s0;
    		while(*s){
    //			if(StrCompare(s,buf)==0) break;
    			p = ent;
    			entry = s;
    			for(;*s;s++,p++){
    				if(*s != *p) break;
    			}
    			if(*s == '\0' && *p == '\0'){
    				s = entry;
    				break;
    			}
    			for(;*s;s++);
    			s++;
    		}
    		MemHandleUnlock(h);

    		if(*s){
    			DeleteDicEntry(d,ind,s-s0);
    		}
    		ind = NextDictRecord(g,ind,(int)ent[0]);
    		ds = g->dic;
    	}

    	for(ind=0x900;ind<=0x9ff;ind++){ // 環境辞書
    		h = DmQueryRecord(d,ind);
    		if(h && (s0 = MemHandleLock(h))){
    			s=s0;
    			while(*s){
    //				if(StrCompare(s,buf)==0) break;
    				p = ent;
    				entry = s;
    				for(;*s;s++,p++){
    					if(*s != *p) break;
    				}
    				if(*s == '\0' && *p == '\0'){
    					s = entry;
    					break;
    				}
    				for(;*s;s++);
    				s++;
    			}
    			MemHandleUnlock(h);
    			if(*s){
    				DeleteDicEntry(d,ind,s-s0);
    			}
    		}
    	}
    }

    static void delword(Global *g) // 2000/11/08 04:03:24
    {
    //	register unsigned char *s,*p,*entry;
    	unsigned char buf[200];
    	unsigned char *pat,*word;
    	int plen,wlen;
    	FormPtr frm;
    	int x,y;
    	UInt16 id;
    	int ind;

    	if(g->ncands <= 0) return;

    	ind = 0;
    	if(g->candindex > 0) ind = g->candindex-1;
    	pat = g->candspell[ind];
    	word = g->candstr[ind];
    	if(pat == NULL || *pat == '\0' ||
    	   word == NULL || *word == '\0') return;
    	plen = StrLen(pat);
    	wlen = StrLen(word);

    	frm = FrmInitForm(ID_DELWORDFORM);
    	ErrFatalDisplayIf(frm==NULL,"Null frm");
    	FrmSetActiveForm(frm);
           	FrmDrawForm(frm);

    	x = 12; y = 33;
    	WinDrawChars("\x81\x75",2,x,y);		x += 10;
    	WinDrawChars(pat,plen,x,y);		x += FntCharsWidth(pat,plen);
    	WinDrawChars("\x81\x76",2,x,y);		x += 10;
    	WinDrawChars("\x81\x75",2,x,y);		x += 10;
    	WinDrawChars(word,wlen,x,y);		x += FntCharsWidth(word,wlen);
    	WinDrawChars("\x81\x76",2,x,y);

    	id = FrmDoDialog(frm);
    	if(id == ID_BUTTON_DELW_OK){
    		StrCopy(buf,pat);
    		StrCat(buf,"\002");
    		StrCat(buf,word);

    		delent(g,buf);

    		if(StrCompare(pat,word) == 0){ // ASCIIエントリの場合
    			StrCopy(buf,pat);
    			delent(g,buf);
    		}

    		FrmReturnToForm(NULL); // 最初のフォームに戻る
    		ClosePBInline(g);
    		OpenPBInline(g);
    		SearchCand(g,NEWCAND);
    	}
    	else {
    		FrmReturnToForm(NULL); // 最初のフォームに戻る
    	}
    }

    static void setpref(Global *g)
    {
    	UInt16 id;
    	ControlPtr cp;

    	g->prefform = FrmInitForm(ID_PREFFORM);
    	ErrFatalDisplayIf(g->prefform==0,"No prefform");
    	FrmSetActiveForm(g->prefform);
    	FrmSetEventHandler(g->prefform,PrefFormEvent);

    	g->tmppref = g->pref;
    	g->tmppref.savedict = (g->dic ? BackupBit(g->dic) : false);

    	cp = GetObjPtr(ID_CHECKBOX_BACKUP);
    	CtlSetValue(cp,g->tmppref.savedict ? 1 : 0);
    	cp = GetObjPtr(ID_BUTTON_BOTTOM);
    	CtlSetValue(cp,g->tmppref.dispbottom ? 1 : 0);
    	cp = GetObjPtr(ID_BUTTON_NEARCURSOR);
    	CtlSetValue(cp,g->tmppref.dispbottom ? 0 : 1);
    	cp = GetObjPtr(ID_BUTTON_USESPACE);
    	CtlSetValue(cp,g->tmppref.spacefortankanji ? 1 : 0);
    	cp = GetObjPtr(ID_BUTTON_USERETURN);
    	CtlSetValue(cp,g->tmppref.spacefortankanji ? 0 : 1);
    	cp = GetObjPtr(ID_BUTTON_RIGHTHANDED);
    	CtlSetValue(cp,g->tmppref.righthanded ? 1 : 0);
    	cp = GetObjPtr(ID_BUTTON_LEFTHANDED);
    	CtlSetValue(cp,g->tmppref.righthanded ? 0 : 1);

    	FrmDrawForm(g->prefform);
    	id = FrmDoDialog(g->prefform);
    	FrmReturnToForm(NULL);
    	if(id == ID_BUTTON_PREF_OK){
    		g->pref = g->tmppref;
    		PrefSetAppPreferences(CREATORID,0,1,&(g->pref),sizeof(POBoxPref),true);
    		if(g->dic) SetBackupBit(g->dic,g->tmppref.savedict);
    	}
    }

    /*

    PBInlineのイベント処理

    */

    static Boolean AppHandleEvent(Global *g,EventPtr e)
    {
    	Boolean handled = true;
    	UInt16 c;
    	UInt16 w1,w2;
    	unsigned char cc;
    	ControlPtr cp;
    	UInt16 idx;
    	UInt32 enable;

    	switch(e->eType){
    //	case nilEvent:
    //		// 2001/6/2
    //		// nilEventが来たとき何故か再描画しないと表示されない。要調査。
    //		SearchCand(g,NEWCAND);
    //		break;
    	case nilEvent:
    		break;
    	case ctlSelectEvent:
    		c = e->data.ctlSelect.controlID;
    		switch(c){
    		case ID_BUTTON1:	case ID_BUTTON2:
    		case ID_BUTTON3:	case ID_BUTTON4:
    		case ID_BUTTON5:	case ID_BUTTON6:
    		case ID_BUTTON7:	case ID_BUTTON8:
    		case ID_BUTTON9:	case ID_BUTTON10:
    			SelCand(g,c-ID_BUTTON);
    			break;
    		case ID_BUTTON_PREVCANDS:
    			SearchCand(g,PREVCAND); // 前候補表示
    			break;
    		case ID_BUTTON_NEXTCANDS:
    			SearchCand(g,NEXTCAND); // 次候補表示
    			break;
    		default:
    			handled = false;
    			break;
    		}
    		break;
    	case keyDownEvent:
    		c = e->data.keyDown.chr;
    		if(g->prevwascommandkey){ // 直前にコマンドキーを押していた場合
    			g->prevwascommandkey = false;
    			switch(c){
    			case '1': case '2': case '3':
    			case '4': case '5': case '6':
    			case '7': case '8': case '9':
    				SelCand(g,c-'1');
    				return handled = true;
    			case '0':
    				ClosePBInline(g);
    				FldGetSelection(g->field,&w1,&w2);
    				if(w1 != w2){
    					FldCut(g->field);
    				}
    				g->patlen = 0;
    				g->pat[g->patlen] = '\0';
    				g->finish = true;
    				return handled = true;
    			case 'r': // pref
    				setpref(g);
    				return handled = true;
    			case 'w': // register  changed 2000/11/08 03:34:32
    				regword(g);
    				return handled = true;
    			case 'd': // delete
    				delword(g);
    				return handled = true;
    			default:
    				break;
    			}
    		}
    //		if(e->data.keyDown.modifiers & commandKeyMask || c == commandChr){  ! ハードキーを押すとcommandKeyMaskが立つ
    		if(c == commandChr){
    			g->prevwascommandkey = true;
    		}

    if(c == 0x0c){
    	SearchCand(g,NEWCAND);
    }
    else
    		if(c >= 0x21 && c <= 0x7e){
    			if(g->candindex > 0){
    				SelCand(g,g->candindex-1);
    			}
    			else if(g->exact == 1){ // 直前にスペースやリターンなどで単漢字指定されていればひらがなに確定してしまう
    				ClosePBInline(g);
    				FldGetSelection(g->field,&w1,&w2);
    				if(w1 != w2){
    					FldCut(g->field);
    				}
    //				g->kana = Roma2Kana(g,g->pat,g->patlen);
    //				if(g->kana){
    //					FldInsert(g->field,g->kana,StrLen(g->kana));
    //				}
    				g->pat[g->patlen] = '\0';
    				Roma2Kana(g->pat,g->kana,g->rktable);
    				FldInsert(g->field,g->kana,StrLen(g->kana));
    				OpenPBInline(g);
    			}
    			if(g->patlen < MAXWORDLEN-1){
    				g->pat[g->patlen++] = (unsigned char)c;
    				g->pat[g->patlen] = '\0';
    				// !!!!!
    				g->searchlevel[g->patlen] = g->searchlevel[g->patlen-1];
    			}
    			g->exact = 0;
    			InsertPatAndSearch(g);
    		}
    		else {
    			switch(c){
    case '\0':
    	SearchCand(g,NEWCAND);
    	break;
    			case vchrJogBack: // Backボタンでキャンセル 2001/7/23
    				ClosePBInline(g);
    				g->finish = true;
    				break;
    			case vchrJogRelease:
    			case '\n': // 確定 or 単漢字
    				if(g->candindex == 0 && (g->pref.spacefortankanji ||
    				   g->exact == 1 ||
    				   g->patlen == 0)){ // 確定
    					ClosePBInline(g);
    #ifdef ASCIIKAKUTEI
    					w2 = FldGetInsPtPosition(g->field);
    					FldSetSelection(g->field,w2,w2);
    #else
    					FldGetSelection(g->field,&w1,&w2);
    					if(w1 != w2){
    						FldCut(g->field);
    					}
    //					if(AlphaStr(g->pat) && (g->kana = Roma2Kana(g,g->pat,g->patlen))){
    					if(AlphaStr(g->pat)){
    						g->pat[g->patlen] = '\0';
    						Roma2Kana(g->pat,g->kana,g->rktable);
    						FldInsert(g->field,g->kana,StrLen(g->kana));
    					}
    					else
    						FldInsert(g->field,g->pat,g->patlen);
    #endif
    					g->finish = true;
    				}
    				else if(g->candindex == 0 && g->exact != 1){ // 単漢字
    					g->exact = 1;
    					SearchCand(g,NEWCAND);
    				}
    				else { // カーソルで選択されている候補
    					SelCand(g,g->candindex-1);
    				}
    				break;
    			case TOGGLECHR:
    				enable = false;
    				FtrSet(CREATORID,FEATURE_ENABLE,enable);
    				// fall through
    			case '\r': // 確定 !!!!!!
    				ClosePBInline(g);
    #ifdef SHOWPATINWIN
    				InsertPat(g);
    #endif
    				g->finish = true;
    				break;
    			case 0x08: case 0x7f: // BS/DEL
    				if(g->candindex == 0 && g->pageindex == 0){
    					g->exact = 0;
    					if(g->patlen == 0){
    						ClosePBInline(g);
    						g->finish = true;
    					}
    					else if(g->patlen == 1){
    						ClosePBInline(g);
    						FldGetSelection(g->field,&w1,&w2);
    						if(w1 != w2){
    							FldCut(g->field);
    						}
    						//FldCut(g->field);
    						g->patlen = 0;
    						g->pat[g->patlen] = '\0';
    #ifdef	SEARCHAGAIN
    						OpenPBInline(g);
    						SearchCand(g,NEWCAND);
    						if(g->ncands == 0){ // 例文から次候補をみつけられない
    							ClosePBInline(g);
    							g->finish = true;
    						}
    #else
    						g->finish = true;
    #endif
    					}
    					else {
    						g->patlen--;
    						g->pat[g->patlen] = '\0';
    //						InsertPatAndSearch(g);
    						SearchCand(g,NOCAND);
    					}
    					break;
    				}
    				// それ以外は左矢印と同じ動作
    			case vchrJogUp:
    			case leftArrowChr:
    				if(c == vchrJogUp && g->candindex == 0){ // ASCII確定 2000/11/12 13:03:14
    					ClosePBInline(g);
    					InsertPatAndFix(g);
    					g->finish = true;
    					break;
    				}
    				if(g->candindex > 0){
    					// BSの処理と似ている。共通にする?
    					idx = FrmGetObjectIndex(g->pbform,ID_BUTTON+g->candindex-1);
    					cp = FrmGetObjectPtr(g->pbform,idx);
    					CtlSetValue(cp,0);
    					g->candindex--;
    				}
    				if(g->candindex > 0){
    					idx = FrmGetObjectIndex(g->pbform,ID_BUTTON+g->candindex-1);
    					cp = FrmGetObjectPtr(g->pbform,idx);
    					CtlSetValue(cp,1);
    				}
    				else if(g->pageindex > 0){
    					SearchCand(g,PREVCAND);
    					if(g->ncands > 0){
    						g->candindex = g->ncands;
    						idx = FrmGetObjectIndex(g->pbform,ID_BUTTON+g->candindex-1);
    						cp = FrmGetObjectPtr(g->pbform,idx);
    						CtlSetValue(cp,1);
    					}
    				}
    				break;
    			case spaceChr:
    				if(g->pref.spacefortankanji && !g->exact){
    					g->exact = 1;
    					SearchCand(g,NEWCAND);
    					break;
    				}
    				// 空白文字で単漢字でない場合は右矢印と同じ動作をさせる
    			case vchrJogDown:
    			case rightArrowChr:
    				if(g->candindex < g->ncands){
    					if(g->candindex > 0){
    						idx = FrmGetObjectIndex(g->pbform,ID_BUTTON+g->candindex-1);
    						cp = FrmGetObjectPtr(g->pbform,idx);
    						CtlSetValue(cp,0);
    					}
    					g->candindex++;
    					idx = FrmGetObjectIndex(g->pbform,ID_BUTTON+g->candindex-1);
    					cp = FrmGetObjectPtr(g->pbform,idx);
    					CtlSetValue(cp,1);
    				}
    				else {
    					SearchCand(g,NEXTCAND);
    					if(g->ncands > 0){
    						g->candindex = 1;
    						idx = FrmGetObjectIndex(g->pbform,ID_BUTTON+g->candindex-1);
    						cp = FrmGetObjectPtr(g->pbform,idx);
    						CtlSetValue(cp,1);
    					}
    				}
    				break;
    			case vchrJogPageDown:	// ジョグ左回し
    			case nextFieldChr:	// 上ドラッグ-下ドラッグ
    			case downArrowChr:	// 下向き矢印キー
    			case pageDownChr:	// 下スクロールボタン
    				SearchCand(g,NEXTCAND);
    				break;
    			case vchrJogPageUp:	// ジョグ右回し
    			case prevFieldChr:	// 下ドラッグ-上ドラッグ
    			case upArrowChr:	// 上向き矢印キー
    			case pageUpChr:		// 上スクロールボタン
    				if(g->pageindex > 0){
    					SearchCand(g,PREVCAND); // 前候補表示
    				}
    				else { // ASCII確定
    					ClosePBInline(g);
    					InsertPatAndFix(g);
    					g->finish = true;
    					if(g->patlen == 0 && c == prevFieldChr){ // 2000/11/07 13:34:47
    						UInt32 enable;
    						enable = false;
    						DisplayPOBoxMode(enable);
    						FtrSet(CREATORID,FEATURE_ENABLE,enable);
    					}
    				}
    				break;
    			case hard1Chr:
    				if(! g->pref.righthanded){
    					g->exact = 1;
    					SearchCand(g,NEWCAND);
    					break;
    				}
    				SelCand(g,0);
    				break;
    			case hard2Chr:
    			case hard3Chr:
    				SelCand(g,g->pref.righthanded ? c-hard1Chr : c-hard2Chr);
    				break;
    			case hard4Chr:
    				if(g->pref.righthanded){
    					g->exact = 1;
    					SearchCand(g,NEWCAND);
    					break;
    				}
    				SelCand(g,2);
    				break;
    #ifdef MENUCHR
    			case menuChr:
    				g->prefform = FrmInitForm(ID_PREFFORM);
    				ErrFatalDisplayIf(g->prefform==0,"No prefform");
    				FrmSetActiveForm(g->prefform);
    				FrmSetEventHandler(g->prefform,PrefFormEvent);

    				cp = GetObjPtr(ID_CHECKBOX_BACKUP);
    				CtlSetValue(cp,g->dic ? (BackupBit(g->dic) ? 1 : 0) : 0);

    				FrmDrawForm(g->prefform);
    				id = FrmDoDialog(g->prefform);
    				FrmReturnToForm(NULL);
    				break;
    #endif
    			default:
    				if(c >= 0x20 && c < 0x7f){
    					ClosePBInline(g);
    					w2 = FldGetInsPtPosition(g->field);
    					FldSetSelection(g->field,w2,w2);
    					cc = (unsigned char)c;
    					FldInsert(g->field,&cc,1);
    					g->exact = 0;
    					g->finish = true;
    				}
    				else {
    					handled = false;
    				}
    			}
    		}
    		break;
    	case menuEvent:
    		switch(e->data.menu.itemID){
    		case ID_MENU_ABOUT:		FrmAlert(ID_ALERT_ABOUTPOBOX);	break;
    		case ID_MENU_MAINHELP:		FrmHelp(ID_MAINHELP);		break;
    		case ID_MENU_MOJIHELP:		FrmHelp(ID_MOJIHELP);		break;
    		case ID_MENU_TANHELP:		FrmHelp(ID_TANHELP);		break;
    		case ID_MENU_KANAHELP:		FrmHelp(ID_KANAHELP);		break;
    		case ID_MENU_DICHELP:		FrmHelp(ID_DICHELP);		break;
    		case ID_MENU_KIGOUHELP:		FrmHelp(ID_KIGOUHELP);		break;
    		case ID_MENU_ABBREVHELP:	FrmHelp(ID_ABBREVHELP);		break;
    		case ID_MENU_REGWORDHELP:	FrmHelp(ID_REGWORDHELP);	break;
    		case ID_MENU_DELWORDHELP:	FrmHelp(ID_DELWORDHELP);	break;
    		case ID_MENU_TIPSHELP:		FrmHelp(ID_TIPSHELP);		break;
    		case ID_MENU_PREFERENCES:	setpref(g);			break;
    		case ID_MENU_REGWORD:		regword(g);			break;
    		case ID_MENU_DELWORD:		delword(g);			break;
    		default:							break;
    		}
    		break;
    	default:
    		handled = false;

    		break;
    	}
    	return handled;
    }

    ////////////////////////////////////////////////////////////
    //		候補単語表示
    ////////////////////////////////////////////////////////////

    static void EraseCand(Global *g) // 候補クリヤ
    {
    	int i;
    	UInt16 idx;
    	g->ncands = 0;
    	g->candpos = 0;
    	for(i=0;ipbform,ID_BUTTON+i);
    		CtlHideControl(FrmGetObjectPtr(g->pbform,idx));
    		g->candstr[i][0] = '\0';
    	}
    }

    static Boolean AddCand(Global *g,unsigned char *cstr,int study)
    //
    //	cstrを候補リストに加えて表示。余裕がなければfalseを返す。
    //	呼ばれる度に候補ボタンとして表示するので、候補はみつかる毎に
    //	五月雨式に表示されることになる。
    //
    {
    	int i, width;
    	ControlPtr cp;
    	UInt16 idx;
    	unsigned char *word,*pat;
    	unsigned char buf[200];
    	int len;

    	if(g->ncands >= MAXCANDS) return false;

    	// 読み→word, パタン→pat

    	StrCopy(buf,cstr);
    	word = pat = buf;
    	for(;*word;word++){
    		if(*word == '\002'){
    			*word = '\0';
    			word++;
    			break;
    		}
    	}
    	if(! *word) word = cstr;

    	//
    	//  Drag&Dropモジュール呼び出し
    	//
    	len = StrLen(word);
    	if(len >= 6 && word[len-1] == '}'){
    		//  モジュールはCreatorIDを中括弧でくくって指定する。
    		//  e.g. "ds#{PBTS}"  (ユーザ定義辞書の場合)
    		//  不定長の場合は、何でもマッチするテキストとして空白文字を使って
    		//  "1...#{CTAB}"
    		//  のように定義する。モジュールが複数機能をサポートしている場合は
    		//  "1...#{CTAB2}"
    		//  のようにする。
    		//  また、"@word" などとプレフィクスをつけたときにモジュールを
    		//  呼び出せるようにする場合は
    		//  "@...#@{CTAB2}"
    		//  のように共通プレフィクスを指定すると、
    		//  プレフィクスを除いた文字列がモジュールにわたされる。
    		//
    		unsigned char *s,*p;
    		int start;
    		s = g->pat;
    		p = word;
    		for(start=0;s[start]==p[start];start++); // プレフィクススキップ
    		if(p[start]=='{' && len-start >= 6){
    			Err err;
    			unsigned short cardNo;
    			unsigned long result;
    			LocalID dbID;
    			DmSearchStateType searchState;
    			tDropLaunchCmdParamRec param;
    			unsigned long creatorID;

    //			creatorID = ((p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]); intになってしまうのか? 動かない。
    			creatorID  = word[start+1];	creatorID <<= 8;
    			creatorID |= word[start+2];	creatorID <<= 8;
    			creatorID |= word[start+3];	creatorID <<= 8;
    			creatorID |= word[start+4];

    			// アプリケーションの検索
    			err = DmGetNextDatabaseByTypeCreator (true, &searchState,
    				sysFileTApplication, creatorID, true, &cardNo, &dbID);
    			if(err) return true;
    			param.selector = ((word[start+5] >= '0' && word[start+5] <= '9') ? word[start+5]-'0' : 0); // モジュールの何番目の機能か
    			g->pat[g->patlen] = '\0';
    			param.srcTextLength = g->patlen - start;
    			param.srcText = g->pat + start;
    			param.resultTextLength = 0;
    			param.resultText = NULL;
    			param.resultBehavior = 0;

    			err = SysAppLaunch (cardNo, dbID, 0,
    				dDropLaunchCmd, (Ptr)¶m, &result);
    			ErrFatalDisplayIf(err, "Error sending lookup action to app");
    			if(! param.resultText) return true;
    			buf[param.resultTextLength] = '\0';
    			StrCopy(buf,param.resultText);
    			study = CAND_NOSTUDY;
    			MemPtrFree(param.resultText); // 受け取った文字列はFreeするというD&Dの仕様
    			word = buf;
    		}
    	}

    	// 既にその侯補が侯補リストにあれば何もしない
    	for(i=0;incands;i++)
    		if(StrCompare(word,g->candstr[i]) == 0)
    			return true;

    	// 新しい候補文字列をボタンに登録/表示
    	width = FntCharsWidth(word,StrLen(word)) + 4;
    	if(width < 8) width = 8;
    //	if(g->candpos + 2 + width >= 156) return false;
    	if(g->candpos + 2 + width >= 140) return false;
    	StrCopy(g->candstr[g->ncands],word);
    	StrCopy(g->candspell[g->ncands],pat);
    	g->candtype[g->ncands] = study;
    	idx = FrmGetObjectIndex(g->pbform,ID_BUTTON+g->ncands);
    	cp = FrmGetObjectPtr(g->pbform,idx);
    	cp->bounds.topLeft.x = g->candpos +
    		(g->pref.righthanded ? 2 : 15);
    	cp->bounds.extent.x = width;
    	cp->text = g->candstr[g->ncands];
    	CtlSetValue(cp,0);
    	CtlShowControl(cp);

    	g->candpos += width+3;
    	g->ncands++;
    	return true;
    }

    static int NextDictRecord(Global *g, int resid, int c)
    {
    //DumpHex(resid,40,20);
    //DumpHex(c,60,20);
    	if(resid == c){
    		resid = 0xa00;
    	}
    	else {
    		resid++;
    	}
    	for(;resid < 0xa00 + g->nextra;resid++){
    		if(g->extra[resid - 0xa00] == c) return resid;
    	}
    	return 0;
    }

    static void Hira2Kata(unsigned char *h, unsigned char *buf)
    {
            unsigned char *k = buf;
            int c1,c2,j1,j2;
            while(*h){
                    if(*h < 0x80){
                            *k++ = *h++;
                    }
                    else {
                            c1 = *h++; c2 = *h++;
                            if(c1 >= 0xe0) c1 -= 0x40;
                            if(c2 >= 0x80) c2--;
                            j1 = (c1-0x81) * 2 + (c2>=0x9e ? 1 : 0) + 0x21;
                            j2 = (c2 >= 0x9e ? c2-0x9e : c2-0x40) + 0x21;
                            if(j1 == 0x24) j1++;
                            j2 = (j1 & 1 ? j2-0x21+0x40 : j2-0x21+0x9e);
                            j1 = (j1 - 0x21) / 2 + 0x81;
                            c1 = (j1 >= 0xa0 ? j1 + 0x40 : j1);
                            c2 = (j2 >= 0x7f ? j2 + 1 : j2);
                            *k++ = c1; *k++ = c2;
                    }
            }
            *k = '\0';
    }

    static Boolean PenIsDownOrQueueIsNotEmpty(void)
    {
    //	ペン動作中は検索などを行なわないようにしていたが
    //	ThumbTypeで問題があるので消してしまった。
    //	Int16 x,y;
    //	Boolean down;
    //	EvtGetPen(&x,&y,&down);
    //	if(down) return true;
    	return !EvtKeyQueueEmpty();
    }

    static int
    consonant(int c)
    {
    //	char *consonants = "bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ";
    	char *consonants = "bcdfghjklmpqrstvwxyz";
    	char *s;
    	for(s=consonants;*s;s++){
    		if(*s == c) return 1;
    	}
    	return 0;
    }

    //static int
    //XStrLen(unsigned char *s) // \002か\000までの文字列長を計算
    //{
    //	int i=0;
    //	for(;*s != '\002' && *s != '\0';s++,i++);
    //	return i;
    //}

    //
    //	SearchCand()の方針について
    //
    // パタン形式や、候補がみつかるかどうかによって検索手法はいろいろ
    // 変わってくる。ショートカットやD&Dモジュールを呼び出す場合も有る。
    //
    // 新しいパタンに対する最初の呼出し(kind=NEWCAND)のときに検索モードを決定し、
    // 次候補などを検索する場合(kind=NEXTCAND/PREVCAND)は前の検索結果以降から
    // 検索を再開する。
    //
    // 以下のような検索モードがある。
    //
    // 	単語の前方マッチのみ			exact=0,
    // 	ひらがな/カタカナ + 完全マッチ		exact=1,
    // 	ショートカット文字列 + 前方マッチ	exact=0, shortcut=1
    // 	Drag&Drop呼出文字列 + 前方マッチ	exact=0,            dd=1
    //
    // 後の三者では単語検索以外にも候補を表示する必要があるので、
    // kind=NEWCANDにおける検索時に候補単語列をあらかじめ検索しておく。
    //
    // 曖昧検索に失敗した場合、何度も曖昧検索を実行するのは時間がかかるので、
    // searchlevel[パタン長]という配列に検索レベルを入れておき、パタンが
    // 追加された場合は前の検索レベル以降を試す。検索レベルは
    //
    // 	1: 通常検索(前方マッチ)
    // 	2: 送り仮名検索
    // 	3: 曖昧検索
    //
    // である。検索レベルも新規検索時(kind=NEWCAND)のときに決定する。
    // 候補画面選択時(kind=PREVCAND/NEXTCAND)には変更しない。
    //
    // 	初期状態  ←→  1文字目入力  ←→  2文字目入力  ←→
    //                      次候補↓↑前候補         ↓↑
    //                           2画面目            2画面目
    //                      次候補↓↑前候補         ↓↑
    //

    /*
    	メモリースティック辞書などの扱い

    1.4以前
    	内蔵PBinlineDB	PBinlearnDB
    	●		◯				PBinlearnDBだけ学習
    	◯		×				PBinlineDBで学習
    	×		◯				NG?
    	×		×				NG (学習できない)

    1.5以降
    	内蔵PBinlineDB	PBinlearnDB	外部PBinlineDB
    ★	(●)		◯		●		PBinlearnDBだけ学習 内蔵PBinlineDBを無視
    ★	●		◯		×		PBinlearnDBだけ学習
    ★	◯		×		(◯)		内蔵PBinlineDBで学習
    ★	◯		×		×		内蔵PBinlineDBで学習
    ★	×		◯		●		PBinlearnDBだけ学習
    	×		◯		×		NG?
    	×		×		◯		NG (学習できない)
    	×		×		×		NG

    方針:	・PBinlearnDBが有る場合は学習辞書に使う
    	  外部PBinlineDBが有る場合はこれを固定辞書に使う (type3)
    	  内部PBinlineDBが有る場合はこれを固定辞書に使う (type2)
    	  両方無い場合はエラー
    	・PBinlearnDBが無い場合は
    	  内部PBinlineDBですべて学習する。(type1)
    	  これが無い場合はエラー
    */

    static void SearchCand(Global *g, int kind)
    //
    //	動的曖昧検索を実行して候補単語リストを表示
    //
    {
    #define SIMPLE
    //#define USEFUNC

    #ifndef SIMPLE
    #ifndef USEFUNC
    	register UInt32 match_i0;
    	register UInt32 match_e;
    	register UInt32 *match_p;
    	register UChar match_c;
    #endif
    #endif
    	register UChar *p,*s,*s0;
    	UChar pat[100];
    	unsigned char patchar;
    	UInt16 resid;
    	UInt16 offset;
    	MemHandle h;
    	Boolean found = false; //////
    //	ULong size;
    	int mismatch; // 曖昧度
    	int i;
    	Boolean havespace = true;
    	ControlPtr cp;
    	int patwidth;
    	unsigned char buf[100];
    	unsigned char k[100];
    	int objindex;
    	RectangleType bounds;

    	DmOpenRef d,ds;

    	Err err;

    	d = (g->ldic ? g->ldic : g->dic);

    	patchar = (IsUpper(*(g->pat)) ? ToLower(*(g->pat)) : *(g->pat));

    	switch(kind){
    	case NEWCAND:
    		g->pageindex = 0;
    		for(i=0;ipageoffset[i] = 0;
    			g->ppageoffset[i] = 0;
    			g->lpageoffset[i] = 0;
    			g->pageresid[i] = patchar;

    			g->opageoffset[i] = 0;
    			g->opageresid[i] = patchar;
    		}
    		g->searchlevel[0] = 0;
    		g->searchlevel[1] = 0;
    		break;
    	case NEXTCAND:
    		if(g->pageindex >= MAXCANDPAGES-1) return;
    		g->pageindex++;
    		break;
    	case PREVCAND:
    		if(g->pageindex <= 0) return;
    		g->pageindex--;
    		break;
    	default:
    	}

    	g->candindex = 0;

    if(PenIsDownOrQueueIsNotEmpty()) return;

    	//
    	// 前候補/次候補の矢印ボタン表示 (右利き/左利きで場所を変える)
    	//
    	objindex = FrmGetObjectIndex(g->pbform,ID_BUTTON_PREVCANDS);
    	cp = FrmGetObjectPtr(g->pbform,objindex);
    	CtlSetLabel(cp,g->pageindex == 0 ? "\003" : "\001");
    	bounds.topLeft.x = (g->pref.righthanded ? 143 : 0);
    	bounds.topLeft.y = 0;
    	bounds.extent.x = 12;
    	bounds.extent.y = 8;
    	FrmSetObjectBounds(g->pbform,objindex,&bounds);
    	CtlShowControl(cp);

    	objindex = FrmGetObjectIndex(g->pbform,ID_BUTTON_NEXTCANDS);
    	cp = FrmGetObjectPtr(g->pbform,objindex);
    	// CtlSetLabel(cp,"\002");
    	bounds.topLeft.x = (g->pref.righthanded ? 143 : 0);
    	bounds.topLeft.y = 7;
    	bounds.extent.x = 12;
    	bounds.extent.y = 8;
    	FrmSetObjectBounds(g->pbform,objindex,&bounds);
    	CtlShowControl(cp);

    	EraseCand(g);

    #ifdef SHOWPATINWIN
    	//
    	// 入力した読みをFieldにコピーするかわりにPOBox枠で表示する場合。
    	// インライン的ではなくなるが速度がかなり向上する。
    	//
    	if(g->pageindex == 0 && g->patlen > 0){
    		int offset = (g->pref.righthanded ? 3 : 16);
    		WinDrawChars("                    ",20,offset,1);
    		patwidth = FntCharsWidth(g->pat,g->patlen);
    		WinDrawChars(g->pat,g->patlen,offset,1);
    		WinDrawChars("    ",4,offset+patwidth,1);
    		g->candpos = patwidth + 4;
    	}
    	else {
    		int offset = (g->pref.righthanded ? 3 : 16);
    		WinDrawChars("                    ",20,offset,1);
    	}
    #endif

    	if(kind == NOCAND) return;

    	////////////////////////////////////////////////////////////////////////////////
    	//
    	// 特殊な候補の作成
    	//
    	if(g->pageindex == 0){
    		g->ndc[0] = 0;
    		g->nc = 0;
    //		if(g->patlen > 0 && g->exact){
    		if(1){
    			//
    			// 単漢字変換指定の場合はひらがな/カタカナと無変換文字列も候補にする。
    			//
    			StrNCopy(pat,g->pat,g->patlen);
    			pat[g->patlen] = '\0';
    //			g->kana = Roma2Kana(g,g->pat,g->patlen);
    //			if(g->kana == NULL) g->kana = "";
    // 			ローマ字にできないときは出さない...
    			if(Roma2Kana(g->pat,g->kana,g->rktable)){
    //			if(! ASCIIStr(g->kana)){
    				StrNCopy(buf,g->pat,g->patlen);
    				StrCopy(buf+g->patlen,"\002");
    				StrCopy(buf+g->patlen+1,g->kana);
    				g->extracandsstudy[g->nc] = CAND_STUDY;
    				StrCopy(g->extracands[g->nc++],buf);

    				Hira2Kata(buf,k);
    				g->extracandsstudy[g->nc] = CAND_STUDY;
    				StrCopy(g->extracands[g->nc++],k);
    			}
    			StrNCopy(pat,g->pat,g->patlen);
    			pat[g->patlen] = '\0';
    			g->extracandsstudy[g->nc] = CAND_STUDY;
    			StrCopy(g->extracands[g->nc++],pat);
    		}
    	}
    	//
    	// 特殊候補を表示
    	//
    	g->ndc[g->pageindex + 1] = g->ndc[g->pageindex];
    	if(g->patlen > 0 && g->exact){
    		if(g->ndc[g->pageindex] < g->nc){
    			int n = g->ndc[g->pageindex];
    			for(;havespace && n < g->nc ;n++){
    				havespace = AddCand(g,g->extracands[n],g->extracandsstudy[n]);
    				if(havespace) g->ndc[g->pageindex+1]++;
    			}
    		}
    	}

    	////////////////////////////////////////////////////////////////////////////////
    	//
    	// ここから以降が通常の辞書検索
    	//

    	//
    	// 環境辞書を検索
    	//
    if(PenIsDownOrQueueIsNotEmpty()) return;
    	if(havespace && g->patlen > 0 && g->pageindex == 0){ // ちょっとずるい
    		resid = 0x900 + CurrentEnvironment();
    		h = DmQueryRecord(d,resid);
    		if(h && (s0 = MemHandleLock(h))){
    			StrNCopy(pat,g->pat,g->patlen);
    			if(! g->exact)
    				StrCopy(pat+g->patlen," ");
    			MakePat(g,pat,0);
    			s = s0;
    			while(*s){
    				if(Match0(g,s)){
    					havespace = AddCand(g,s,CAND_STUDY);
    					if(!havespace) break;
    				}
    				for(;*s;s++);
    				s++;
    			}
    			MemHandleUnlock(h);
    		}
    	}
    if(PenIsDownOrQueueIsNotEmpty()) return;
    	//
    	// 例文学習辞書を検索
    	//
    	if(g->ldic){
    		if(g->pageindex+1 < MAXCANDPAGES)
    			g->lpageoffset[g->pageindex+1] = 0;
    		if(! g->exact && havespace){
    			for(i=0;i< g->clen && havespace;i++){
    				int clen;
    				unsigned char lastc = g->context[g->clen-1];

    				clen = g->clen - i;
    				StrNCopy(pat,g->context+i,clen);
    				StrCopy(pat+clen,"\001");
    				resid =  clen * 0x100 + lastc;
    				h = DmQueryRecord(g->ldic,resid);
    				if(h && (s0 = MemHandleLock(h))){
    					s = s0 + g->lpageoffset[g->pageindex];
    					while(*s){
    						if(StrNCompare(pat,s,clen+1)==0){
    							if(StrNCompare(s+clen+1,g->pat,g->patlen)==0 || g->patlen == 0){
    								havespace = AddCand(g,s+clen+1,CAND_STUDY);
    								if(!havespace) break;
    							}
    						}
    						for(;*s;s++);
    						s++;
    					}
    					if(g->pageindex+1 < MAXCANDPAGES)
    						g->lpageoffset[g->pageindex+1] = s - s0;
    					MemHandleUnlock(h);
    				}
    			}
    		}
    		if(PenIsDownOrQueueIsNotEmpty()) return;
    	}

    if(PenIsDownOrQueueIsNotEmpty()) return;
    	//
    	// 例文固定?辞書を検索
    	//
    	if(g->pageindex+1 < MAXCANDPAGES)
    		g->ppageoffset[g->pageindex+1] = 0;
    	if(! g->exact && havespace){
    		for(i=0;i< g->clen && havespace;i++){
    			int clen;
    			unsigned char lastc = g->context[g->clen-1];

    			clen = g->clen - i;
    			StrNCopy(pat,g->context+i,clen);
    			StrCopy(pat+clen,"\001");
    			resid =  clen * 0x100 + lastc;
    #ifdef USEVFS
    			if(g->vdic){
    				err = VFSFileDBGetRecord(g->vdic,resid,&h,(UInt8*)NULL,(UInt32*)NULL);
    				ErrFatalDisplayIf(err,"Can't get VFS handle");
    			}
    			else
    #endif
    			{
    				h = DmQueryRecord(g->dic,resid);
    			}
    			// h = DmQueryRecord(g->dic,resid);
    			if(h && (s0 = MemHandleLock(h))){
    				s = s0 + g->ppageoffset[g->pageindex];
    				while(*s){
    					if(StrNCompare(pat,s,clen+1)==0){
    						if(StrNCompare(s+clen+1,g->pat,g->patlen)==0 || g->patlen == 0){
    							havespace = AddCand(g,s+clen+1,CAND_STUDY);
    							if(!havespace) break;
    						}
    					}
    					for(;*s;s++);
    					s++;
    				}
    				if(g->pageindex+1 < MAXCANDPAGES)
    					g->ppageoffset[g->pageindex+1] = s - s0;
    				MemHandleUnlock(h);
    #ifdef USEVFS
    				if(g->vdic){
    					MemHandleFree(h);
    				}
    #endif
    			}
    		}
    	}
    	if(PenIsDownOrQueueIsNotEmpty()) return;

    	//
    	// 単語辞書を検索
    	//
    	if(havespace && g->patlen > 0){
    		StrNCopy(pat,g->pat,g->patlen);
    		pat[g->patlen] = '\0';
    #ifndef SIMPLE
    		if(! g->exact)
    			StrCat(pat," ");
    #else
    		for(s=pat;*s;s++){
    			if(IsUpper(*s)) *s = ToLower(*s);
    		}
    #endif
    		//
    		// 単語辞書からの検索
    		//

    		found = false;
    		//
    		// 基本単語辞書/拡張単語辞書の完全マッチを試す
    		//
    		if(g->searchlevel[g->patlen] == 0){
    			MakePat(g,pat,mismatch=0);
    #ifndef SIMPLE
    #ifndef USEFUNC
    			match_p = g->shiftpat;
    #endif
    #endif
    			resid = g->pageresid[g->pageindex];
    			offset = g->pageoffset[g->pageindex];
    			ds = (resid < 0x100 && g->ldic ? g->ldic : g->dic); // 2000/11/12 18:01:24
    			// ds = d;
    			while(havespace && resid){
    #ifdef USEVFS
    				if(ds == g->ldic || ! g->vdic){
    					h = DmQueryRecord(ds,resid);
    				}
    				else {
    					err = VFSFileDBGetRecord(g->vdic,resid,&h,(UInt8*)NULL,(UInt32*)NULL);
    					ErrFatalDisplayIf(err,"Can't get VFS handle");
    				}
    #else
    				h = DmQueryRecord(ds,resid);
    #endif
    				if(h && (s0 = MemHandleLock(h))){
    					s = s0 + offset;
    					while(*s){ // DBの最後はヌルになってると仮定
    						unsigned char *sw;
    //if(PenIsDownOrQueueIsNotEmpty()) break;
    						sw = s;
    #ifdef SIMPLE
    						for(p = pat;*p && (*p == *s || *s == ' '); p++,s++);
    						if(!*p && (!g->exact || g->exact && (*s == '\002' || !*s))){
    #else
    #ifdef USEFUNC
    						if(Match0(g,s)){
    #else
    						match_i0 = INITPAT;
    						match_e = g->epsilon;
    						for(;(match_c = *s) && match_c != '\002';s++){
    							match_i0 = (match_i0 & match_e) | ((match_i0 & match_p[match_c]) >> 1);
    						}
    						if(match_i0 & g->acceptpat){
    #endif
    #endif
    							found = true;
    							havespace = AddCand(g,sw,CAND_STUDY);
    							if(!havespace){
    								s = sw;
    								break;
    							}
    						}
    						for(;*s;s++);
    						s++;
    					}
    					MemHandleUnlock(h);
    #ifdef USEVFS
    					if(ds != g->ldic && g->vdic
    						){
    						MemHandleFree(h);
    					}
    #endif
    					if(g->pageindex+1 < MAXCANDPAGES){
    						g->pageresid[g->pageindex+1] = resid;
    						g->pageoffset[g->pageindex+1] = s - s0;
    					}
    				}
    				if(!havespace) break;
    				//
    				// 次の単語辞書をさがす
    				//
    				resid = NextDictRecord(g,resid,(int)patchar);
    				ds = g->dic;
    				offset = 0;
    			}
    		}
    		if(!found && g->pageindex == 0){
    			for(i=g->patlen;isearchlevel[i] = 1;
    //			g->searchlevel[g->patlen] = 1;/////////
    //			g->searchlevel[g->patlen+1] = 1;/////////
    		}
    #ifdef WNN_PALM_OS
    		if(havespace && g->pageindex == 0){
    			Char buf[200],convbuf[200],*s,*d;
    			UInt16 len;
    			len = StrLen(g->pat);
    			StrCopy(buf,g->pat);
    			buf[len] = '\002';
    			Roma2Kana(g->pat,g->kana,g->rktable);
    			if(jl_ren_conv(g->kana,convbuf,200,0x01) >= 0){
    				for(s=convbuf,d=buf+len+1;*s;){
    					if(*s != 0x01){
    						*d++ = *s++;
    					}
    					else {
    						s++;
    					}
    				}
    				*d = '\0';
    				havespace = AddCand(g,buf,CAND_STUDY);
    			}
    			jl_flush();
    		}
    #endif
    		//
    		// 送りがなのマッチを調べる。
    		// "tab 食" という辞書エントリがあるとき"taberu"が入力されると
    		// "食べる"を候補とする...  パタンの最後が子音の場合。
    		//
    		if(havespace && !consonant(g->pat[g->patlen-1])){
    //		if(g->searchlevel[g->patlen] == 1 && havespace){/////////
    //		if(g->searchlevel[g->patlen] == 1 && !found && havespace){/////////
    //		if(g->searchlevel[g->patlen] == 0 && havespace && Roma2Kana(g->pat,g->kana,g->rktable)){ // かなに変換可能なときだけ
    			resid = g->opageresid[g->pageindex]; ////////
    			offset = g->opageoffset[g->pageindex];
    			ds = (resid < 0x100 && g->ldic ? g->ldic : g->dic); // 2000/11/12 18:01:24
    			// ds = d;
    			while(havespace && resid){
    #ifdef USEVFS
    				if(ds == g->ldic || ! g->vdic){
    					h = DmQueryRecord(ds,resid);
    				}
    				else {
    					err = VFSFileDBGetRecord(g->vdic,resid,&h,(UInt8*)NULL,(UInt32*)NULL);
    					ErrFatalDisplayIf(err,"Can't get VFS handle");
    				}
    #else
    				h = DmQueryRecord(ds,resid);
    #endif
    				//h = DmQueryRecord(ds,resid);
    				if(h && (s0 = MemHandleLock(h))){
    					s = s0 + offset;
    					while(*s){
    //						int len;
    if(PenIsDownOrQueueIsNotEmpty()) break;

    //						if(PMatch(g,s) && ((len = XStrLen(s)-1) >= 1) && consonant(s[len])){

    //						for(p = pat;*p == *s && (*s != '\002') && *s; p++,s++);
    						for(p = pat;*p == *s; p++,s++);
    						if(*s == '\002' && consonant(*(s-1)) && (p-pat > 1) &&
    						  (*(s+1) & 0x80)){ // 日本語のときだけ){
    							StrNCopy(buf,g->pat,g->patlen);
    							StrCopy(buf+g->patlen,s);
    							Roma2Kana(g->pat+(p-pat-1),g->kana,g->rktable);
    							StrCat(buf,g->kana);
    							found = true;
    							havespace = AddCand(g,buf,CAND_STUDY);
    							if(! havespace) break;
    						}
    						for(;*s;s++);
    						s++;
    					}
    					MemHandleUnlock(h);
    #ifdef USEVFS
    					if(ds != g->ldic && g->vdic
    						){
    						MemHandleFree(h);
    					}
    #endif
    					if(g->pageindex+1 < MAXCANDPAGES){
    						g->opageresid[g->pageindex+1] = resid;
    						g->opageoffset[g->pageindex+1] = s - s0;
    					}
    				}
    				if(!havespace) break;
    				//
    				// 次の単語辞書をさがす
    				//
    				resid = NextDictRecord(g,resid,(int)patchar);
    				ds = g->dic;
    				offset = 0;
    			}
    		}
    //		if(!found && g->pageindex == 0) g->searchlevel[g->patlen] = 2;
    		if(!found && g->pageindex == 0) g->searchlevel[g->patlen] = 1;
    		//
    		// それでもみつからなかった場合、基本単語中をかなりの曖昧度で試す
    		// 'dsg' => 'd s g ', etc.
    		//
    		// 曖昧検索はPOBoxの特徴のひとつなのだが、時間がかかって操作性が
    		// 悪くなることがある。
    		//
    //		if(g->searchlevel[g->patlen] == 2 && !found && havespace && g->patlen < 6 // 長いパタンだと曖昧検索をやめる
    		if(g->searchlevel[g->patlen] == 1 && !found && havespace && g->patlen < 6 // 長いパタンだと曖昧検索をやめる
    				){
    			resid = patchar;
    			h = DmQueryRecord(d,resid);
    			if(h && (s0 = MemHandleLock(h))){
    				for(p=pat,s=g->pat;s < g->pat+g->patlen;s++){
    					*p++ = *s;
    					*p++ = ' ';
    				}
    				*p = '\0';
    				for(mismatch=0;mismatch<2 && havespace && !found;mismatch++){ // 動的曖昧検索実行
    					if(PenIsDownOrQueueIsNotEmpty())
    						break;
    					MakePat(g,pat,mismatch);
    					s = s0 + g->pageoffset[g->pageindex];
    					while(*s){
    if(PenIsDownOrQueueIsNotEmpty()) break;
    						if(Match(g,s)){
    							found = true;
    							havespace = AddCand(g,s,CAND_STUDY);
    							if(! havespace) break;
    						}
    						for(;*s;s++);
    						s++;
    					}
    				}
    				MemHandleUnlock(h);
    				if(g->pageindex+1 < MAXCANDPAGES){
    					g->pageresid[g->pageindex+1] = patchar;
    					g->pageoffset[g->pageindex+1] = s - s0;
    				}
    			}
    		}
    		if(!found && g->pageindex == 0) g->searchlevel[g->patlen] = 3;
    	}
    	//
    	// どうしても何も予測できない場合は助詞を表示する。
    	// 文の先頭などでは表示しない。
    	//
    	if(g->ncands < 3 && g->patlen == 0 && !g->alphaend && g->clen > 0){
    		if(havespace) havespace = AddCand(g,"no\002\202\314",CAND_STUDY); // の
    		if(havespace) havespace = AddCand(g,"wo\002\202\360",CAND_STUDY); // を
    		if(havespace) havespace = AddCand(g,"ga\002\202\252",CAND_STUDY); // が
    		if(havespace) havespace = AddCand(g,"ha\002\202\315",CAND_STUDY); // は
    		if(havespace) havespace = AddCand(g,"ni\002\202\311",CAND_STUDY); // に
    		if(havespace) havespace = AddCand(g,"to\002\202\306",CAND_STUDY); // と
    		if(havespace) havespace = AddCand(g,"to\002\202\305",CAND_STUDY); // で
    		if(havespace) havespace = AddCand(g,"to\002\202\310",CAND_STUDY); // な
    	}
    	//
    	// カタカナ候補などを表示
    	//
    	if(g->patlen > 0){
    		if(g->ndc[g->pageindex] < g->nc){
    			int n = g->ndc[g->pageindex];
    			for(;havespace && n < g->nc ;n++){
    				havespace = AddCand(g,g->extracands[n],g->extracandsstudy[n]);
    				if(havespace) g->ndc[g->pageindex+1]++;
    			}
    		}
    	}
    }

    static Boolean ASCIIStr(unsigned char *s)
    {
    	if(s == NULL) return false;
    	for(;*s;s++){
    		if(*s >= 0x80) return false;
    	}
    	return true;
    }

    static Boolean AlphaStr(unsigned char *s)
    {
    	if(s == NULL) return false;
    	for(;*s;s++){
    		if(*s >= 'a' && *s <= 'z') continue;
    		if(*s >= 'A' && *s <= 'Z') continue;
    		if(*s == '\'') continue;
    		if(*s == '-') continue;
    		return false;
    	}
    	return true;
    }

    static void SelCand(Global *g, int n) // Select nth candidate and insert/end
    {
    	unsigned char *s;
    	int slen;
    	int offset;
    	unsigned char buf[200];
    	int ind;
    	DmOpenRef d;

    	d = (g->ldic ? g->ldic : g->dic);

    	ClosePBInline(g);
    	if(n < g->ncands){
    		s = g->candstr[n];
    		slen = StrLen(s);
    		if(ASCIIStr(s) && g->alphaend)
    			FldInsert(g->field," ",1);
    		FldInsert(g->field,s,slen);
    		StrCopy(buf,g->candspell[n]);
    		if(StrCompare(g->candspell[n],g->candstr[n])){
    			StrCopy(buf+StrLen(g->candspell[n]),"\002");
    			StrCopy(buf+StrLen(g->candspell[n])+1,g->candstr[n]);
    		}
    		ind = buf[0];
    		if(IsUpper(ind)) ind = ToLower(ind);
    		if(g->candtype[n] == CAND_STUDY){
    			AddDicEntry(d,ind,buf,true);
    			AddDicEntry(d,CurrentEnvironment()+0x900,buf,true);

    			RegisterPhrase(g,d,n);
    		}

    		offset = 0;
    		while(slen-offset > 8){
    			offset += (s[offset] & 0x80 ? 2 : 1);
    		}
    		StrNCopy(g->prevword,s+offset,slen-offset);
    		g->prevwordlen = slen-offset;
    	}
    	g->exact = 0;
    	g->patlen = 0;
    	g->pat[g->patlen] = '\0';

    #ifdef SEARCHAGAIN
    	OpenPBInline(g);
    	SearchCand(g,NEWCAND);
    	if(g->ncands == 0){ // 例文から次候補をみつけられない
    		ClosePBInline(g);
    		g->finish = true;
    	}
    #else
    	g->finish = true;
    #endif
    }

    static void RegisterPhrase(Global *g, DmOpenRef dic, int n)
    //
    //	n番目の候補を例文登録
    //
    {
    	UInt16 resid;
    	UChar buf[200];

    	if(dic == NULL) return;

    	if(g->prevwordlen == 0 || g->prevwordlen > 8) return;
    	resid = g->prevwordlen * 0x100 + g->prevword[g->prevwordlen-1];

    	StrNCopy(buf,g->prevword,g->prevwordlen);
    	StrCopy(buf+g->prevwordlen,"\001");
    	StrCat(buf,g->candspell[n]);
    	StrCat(buf,"\002");
    	StrCat(buf,g->candstr[n]);

    	AddDicEntry(dic,resid,buf,true);
    }

    static void InsertPat(Global *g)
    {
    	UInt16 w1,w2;
    	FldGetSelection(g->field,&w1,&w2);
    	if(w1 == w2) w1=w2= FldGetInsPtPosition(g->field);
    	FldInsert(g->field,g->pat,g->patlen);
    	w2 = FldGetInsPtPosition(g->field);
    	FldSetSelection(g->field,w1,w2);
    }

    static void InsertPatAndFix(Global *g)
    {
    	UInt16 w1,w2;
    	InsertPat(g);
    	FldGetSelection(g->field,&w1,&w2);
    	FldSetSelection(g->field,w2,w2);
    }

    static void InsertPatAndSearch(Global *g)
    {
    if(PenIsDownOrQueueIsNotEmpty()) return;

    #ifdef SHOWPATINWIN
    //	FrmEraseForm(g->pbform);
    //	FrmDrawForm(g->pbform);
    #else
    //	ClosePBInline(g);

    //FrmEraseForm(g->pbform);
    FrmSetActiveForm(g->origform);
    	InsertPat(g);
    //	FldGetSelection(g->field,&w1,&w2);
    //	if(w1 == w2) w1=w2= FldGetInsPtPosition(g->field);
    //	FldInsert(g->field,g->pat,g->patlen);
    //	w2 = FldGetInsPtPosition(g->field);
    //	FldSetSelection(g->fie
    ld,w1,w2);
    FrmSetActiveForm(g->pbform);
    FrmDrawForm(g->pbform);

    //	OpenPBInline(g);
    #endif

    	SearchCand(g,NEWCAND);
    /*
    	// SearchCand()に取り込む
    	if(g->ncands == 0){ // 荒井氏による指摘.. 候補が無いとカタカナ候補を出す
    		g->exact = 2;
    		SearchCand(g,NEWCAND);
    	}
    */
    /*
    	if(g->ncands == 0){
    		ClosePBInline(g);
    		w2 = FldGetInsPtPosition(g->field);
    		FldSetSelection(g->field,w2,w2);
    		g->finish = true;
    	}
    */
    }

    //
    //	アプリの開いているデータベース名から
    //	現在の環境のハッシュ値を得る
    //
    int CurrentEnvironment()
    {
    	DmOpenRef db;
    	LocalID id;
    	UInt cardno;
    	unsigned char name[40];
    	int i;
    	unsigned int hash = 0;

    	db = DmNextOpenDatabase(NULL);
    	while(db){
    		DmOpenDatabaseInfo(db,&id,
    			NULL,NULL,&cardno,NULL);
    		DmDatabaseInfo(cardno,id,
    			name,NULL,NULL,
    			NULL,NULL,NULL,
    			NULL,NULL,NULL,
    			NULL,NULL);
    		for(i=0;i<32 && name[i];i++){
    			hash += name[i];
    		}
    		db = DmNextOpenDatabase(db);
    	}
    	return hash & 0xff;
    }

    #ifdef APP
    static Boolean MainAppFormEvent(EventPtr e)
    {
    	Boolean handled = false;
    //	UInt16 c;
    //	ControlPtr cp;

    	switch(e->eType){
    	case ctlSelectEvent:
    //		c = e->data.ctlSelect.controlID;
    //		cp = e->data.ctlSelect.pControl;
    //		if(CtlGetValue(cp)){
    //		}
    //		else {
    //		}
    //		handled = true;
    		break;
    	default:
    		break;
    	}
    	return handled;
    }

    static Boolean ApplicationHandleEvent(EventPtr e)
    {
    	Boolean handled = false;
    	FormPtr frm;
    	UInt16 c;
    	ControlPtr cp;
    	POBoxPref pref;
    	UInt16 prefsize;
    //	Err err;
    	UInt32 use,olduse;
    	UInt32 trap;
    	DmOpenRef dic;

    //printf("AppHandleEvent(%d)\n",(int)(e->eType));
    	switch(e->eType){
    	case ctlSelectEvent:
    		c = e->data.ctlSelect.controlID;
    		cp = e->data.ctlSelect.pControl;
    		PrefGetAppPreferences(CREATORID,0,&pref,&prefsize,true);
    		switch(c){
    		case ID_CHECKBOX_USEPOBOX:
    			//
    			// アプリケーション設定画面で「POBoxを使う」設定にすることにより
    			// HackMasterによる設定と同じことを行なう。
    			//
    			GetFeaturePref(FEATURE_USEPOBOX,&olduse,false);
    			use = (CtlGetValue(cp) ? true : false);
    			if(! olduse && use){
    				// 現在のトラップアドレスを保存してPOBox関数を登録する
    				trap = (UInt32)SysGetTrapAddress(sysTrapFldHandleEvent);
    				FtrSet(CREATORID,1000,trap);
    				SysSetTrapAddress(sysTrapFldHandleEvent,PBInline);
    				trap = (UInt32)SysGetTrapAddress(sysTrapEvtProcessSoftKeyStroke);
    				FtrSet(CREATORID,1001,trap);
    				SysSetTrapAddress(sysTrapEvtProcessSoftKeyStroke,PBEnable);

    				trap = (UInt32)SysGetTrapAddress(sysTrapGsiEnable);
    				FtrSet(CREATORID,1002,trap);
    				SysSetTrapAddress(sysTrapGsiEnable,GsiEnableHack);
    				trap = (UInt32)SysGetTrapAddress(sysTrapGsiSetShiftState);
    				FtrSet(CREATORID,1003,trap);
    				SysSetTrapAddress(sysTrapGsiSetShiftState,GsiSetShiftStateHack);
    			}
    			else if(olduse && ! use){
    				// トラップアドレスをもとにもどす
    				FtrGet(CREATORID,1000,&trap);
    				SysSetTrapAddress(sysTrapFldHandleEvent,(FldHandleEventTrapFunc*)trap);
    				FtrGet(CREATORID,1001,&trap);
    				SysSetTrapAddress(sysTrapEvtProcessSoftKeyStroke,(EvtProcessSoftKeyStrokeTrapFunc*)trap);
    				FtrGet(CREATORID,1002,&trap);
    				SysSetTrapAddress(sysTrapGsiEnable,(GsiEnableTrapFunc*)trap);
    				FtrGet(CREATORID,1003,&trap);
    				SysSetTrapAddress(sysTrapGsiSetShiftState,(GsiSetShiftStateTrapFunc*)trap);
    			}
    			FtrSet(CREATORID,FEATURE_USEPOBOX,use);
    			handled = true;
    			break;
    		case ID_CHECKBOX_BACKUP:
    			pref.savedict = (CtlGetValue(cp) ? true : false);
    			dic = DmOpenDatabaseByTypeCreator(DICTYPE,CREATORID,dmModeReadWrite);
    			if(dic){ // 本体に辞書があるときだけバックアップ
    				SetBackupBit(dic,pref.savedict);
    				DmCloseDatabase(dic);
    			}
    			break;
    		case ID_BUTTON_NEARCURSOR:
    			pref.dispbottom = (CtlGetValue(cp) ? false : true);
    			break;
    		case ID_BUTTON_BOTTOM:
    			pref.dispbottom = (CtlGetValue(cp) ? true : false);
    			break;
    		case ID_BUTTON_USESPACE:
    			pref.spacefortankanji = (CtlGetValue(cp) ? true : false);
    			break;
    		case ID_BUTTON_USERETURN:
    			pref.spacefortankanji = (CtlGetValue(cp) ? false : true);
    			break;
    		case ID_BUTTON_RIGHTHANDED:
    			pref.righthanded = (CtlGetValue(cp) ? true : false);
    			break;
    		case ID_BUTTON_LEFTHANDED:
    	      		pref.righthanded = (CtlGetValue(cp) ? false : true);
    			break;
    		default:
    			break;
    		}
    		PrefSetAppPreferences(CREATORID,0,1,&pref,sizeof(pref),true);
    		break;
    	case frmLoadEvent:
    		frm = FrmInitForm(e->data.frmLoad.formID);
    		ErrFatalDisplayIf(frm==NULL,"Null FormPtr");
    	        FrmSetActiveForm(frm);
    		FrmSetEventHandler(frm, MainAppFormEvent);
    		handled = true;
    	        break;
    	case frmOpenEvent:
    		GetFeaturePref(FEATURE_USEPOBOX,&use,false);
    		cp = GetObjPtr(ID_CHECKBOX_USEPOBOX);
    		CtlSetValue(cp,use ? 1 : 0);

    		PrefGetAppPreferences(CREATORID,0,&pref,&prefsize,true);
    		cp = GetObjPtr(ID_CHECKBOX_BACKUP);
    		CtlSetValue(cp,pref.savedict ? 1 : 0);
    		cp = GetObjPtr(ID_BUTTON_BOTTOM);
    		CtlSetValue(cp,pref.dispbottom ? 1 : 0);
    		cp = GetObjPtr(ID_BUTTON_NEARCURSOR);
    		CtlSetValue(cp,pref.dispbottom ? 0 : 1);
    		cp = GetObjPtr(ID_BUTTON_USESPACE);
    		CtlSetValue(cp,pref.spacefortankanji ? 1 : 0);
    		cp = GetObjPtr(ID_BUTTON_USERETURN);
    		CtlSetValue(cp,pref.spacefortankanji ? 0 : 1);
    		cp = GetObjPtr(ID_BUTTON_RIGHTHANDED);
    		CtlSetValue(cp,pref.righthanded ? 1 : 0);
    		cp = GetObjPtr(ID_BUTTON_LEFTHANDED);
    		CtlSetValue(cp,pref.righthanded ? 0 : 1);

            	FrmDrawForm(FrmGetActiveForm());
    		handled = true;
    	        break;
    	default:
    		break;
    	}
    	return handled;
    }

    void InitApplication()
    {
    	POBoxPref pref;
    	DmOpenRef dic;
    	UInt16 prefsize;

    	FrmGotoForm(ID_APPFORM);

    	dic = DmOpenDatabaseByTypeCreator(DICTYPE,CREATORID,dmModeReadWrite);
    	// ErrFatalDisplayIf(dic==NULL,"Can't open PBInline DB");
    	if(dic){
    		pref.savedict = BackupBit(dic);
    		DmCloseDatabase(dic);
    	}
    	else
    		pref.savedict = false;

    	if(PrefGetAppPreferences(CREATORID,0,&pref,&prefsize,true) == noPreferenceFound){
    		pref.dispbottom = false;
    		pref.spacefortankanji = false;
    		pref.righthanded = true;
    		PrefSetAppPreferences(CREATORID,0,1,&pref,sizeof(pref),true);
    	}
    }

    void StopApplication()
    {
    }

    UInt32 PilotMain(UInt16 cmd, Ptr cmdPBP, UInt16 launchFlags)
    {
    	EventType event;
    	UInt16 error;
    	if (cmd == sysAppLaunchCmdNormalLaunch)
    	{
    		InitApplication();
    		do {
    			EvtGetEvent(&event, evtWaitForever);
    			if (SysHandleEvent(&event)) continue;
    			if (MenuHandleEvent(NULL, &event, &error)) continue;
    			if (ApplicationHandleEvent(&event)) continue;
    			FrmDispatchEvent(&event);
    		}
    		while (event.eType != appStopEvent);
    		StopApplication();
    	}
    	return 0;
    }
    #endif