実は、ここには、CWCCの試みが書かれる筈だったのだが、番外編にしてしまったので、CWCCで使用しているEditについて、小技を幾つか紹介しておくのだ。
- エディットとは?
簡単に言えば、Windowsのメモ帳のクライアント部分だと思えば良い。テキストの入力/編集が出来る箱である。勿論,これに対して、SetWindowText()を行えば、文字列を入力出来るし、GetWindowText()を行えば、編集された文字列を取得する事も出来るのである。
- エディットの生成
エディットも、ボタン等と同様に、CreateWindow()にて生成する。勿論,テキストを表示させたり、編集結果を取得したりするので、CreateWindow()の戻り値であるハンドルは保存しておくのが普通である。
例)
hCnsDisplay=CreateWindow("EDIT",NULL,
ES_MULTILINE|WS_CHILD|WS_VISIBLE|WS_VSCROLL,
0,0,0,0,hMainWin,(HMENU)CWCC_IDM_CONSOLE,hInstance,NULL) ;
|
※最初に、何か文字を入力しておきたい場合は、2番目の引数に指定すれば良い
属性について、1行(改行を入れない)場合は、ES_MULTILINEを指定しなければ良いし、スクロールしなくても良ければ、WS_VSCROLLは要らないという事になる。表示座標について、上記例で、0,0,0,0としているのは、後で、親ウインドウのサイズに合わせて調整する為であるので、もし、1行だけで使うとか、もう少し、小さめのエリアに納めたい等という場合は、ボタン等と同様に座標を指定しておけばよい。
- 親Windowへのサイズ合わせ
表示開始時,及び親Windowのサイズが変更された場合、WM_SIZEのイベントが発生するので、MoveWindow()を使用して、サイズ合わせを行えば良い。変更されたサイズは、ウインドウ・プロシージャ(WndProc)の4番目の引数である、lParamにて渡され、上位16bitが高さ,下位16bitが幅であるので、この値を利用して調整するのである。
例)親Windowぴったりに合わせる。
MoveWindow(hCnsDisplay,0,0,LOWORD(lParam),HIWORD(lParam),TRUE) ;
|
もし、親Windowぴったりに調整するのでは無くて、少々隙間を空けたいのであれば、この値を調整すれば良い。例えば、エディットの下に10ドット程の隙間を開けたいのであれば、
例)親Windowぴったりに合わせる。
EditHight=HIWORD(lParam) ;
if(EditHight<=20) EditHight=20 ;
MoveWindow(hCnsDisplay,0,0,LOWORD(lParam),EditHight-10,TRUE) ;
|
の様にすれば良いだろう。但し、この場合、高さが10以下だとエディット自体が表示されなくなってしまうので、上記例の2行目の様な判断が必要になってくる。
- 表示追加
エディットに対して文字を追加する処理は、SendMessage(),WM_CHARを使用して行う。例えば、"Hello World"という文字列を表示させたい場合は、この11文字を、最初から1文字ずつ切り出して、SendMessage()すれば良いのである。即ち、EDITタイプのWindowに対して、WM_CHARというイベントを発生させて、EDITのポインタ位置に、文字を入力していく訳である。実は、エディットに対して、ユーザが編集を行う場合も、これと同じ動作を行っているのである。
例)
int i ;
char buff[]="Hello World"
for(i=0 ; i<strlen(buff) ; i++){
SendMessage(hCnsDisplay,WM_CHAR,buff[i],NULL) ;
}
|
但し、この方法だと、漢字コードが正しく表示されないので、漢字を表示させる場合は、少々小細工が必要なのだ。
ここで、漢字コードについて、ちょっと書いておく。EUC(ユニコード)については、詳しく知らないが、JISコードとShiftJISコードについて書いておく。
例えば『亜』という漢字コードは、ShiftJISでは0x88,0x9Fという2バイトのコードで示される。また、JISコードでも、0x30,0x21という2バイトのコードで示されるが、JISコードの場合、この2バイトは、'0'(0x30)及び'!'(0x21)の様に、ASCIIコードの7ビットコードを使うので、"0123亜"の様に、半角/全角混じりの文字列では、0x30,0x31,0x33,0x30,0x21の様になり、何処からが漢字か区別が付かないのである。従って、JISコードは、保存される際に、シフトイン(0x1B,0x24)及び、シフトアウト(0x0F)のコードを付加して、0x1B,0x24,0x30,0x21,0x0Fの様に保存されるのである。
その点、ShiftJISでは、漢字を示す2バイトの1バイト目は、必ず0x80〜0x9f若しくは0xe0〜0xfcの範囲に有るので、他のコードと区別が出来るのである。従って、保存される場合も、0x88,0x9Fの様に保存されるのである。
BC++にて、扱われる漢字コードは、ほぼShiftJISの方である。即ち、GetWindowTextで取得したもの,SetWindowTextにて書き込むもの,constで宣言するものは、(自分の実行結果から)全てShiftJISと考えて良い。省スペースという意味でも,文字数のカウントしやすさ等から考えても、この選択は、妥当だと思う。
さて、そこで、EDITのコントロールに、漢字コードを送るにはどうしたら良いのだろうか?実は、漢字コードはShiftJISのままで良くて、上記の例では、送り方が違うのである。Windows(32bitアプリに限定,16bitアプリについては未検証)では、漢字コードも1文字として扱うので、『亜』を送りたければ、一度に0x889fを送らなければいけないのである。しかしながら、
SendMessage(hCnsDisplay,WM_CHAR,0x889F,0) ;
では、『亜』は表示されないのだ。
SendMessage(hCnsDisplay,WM_CHAR,0x9F88,0) ;
としなければ、いけないのである。どうして、こんな分かり難い方法を取るのだろうか?と疑問を持つ人も多いかも知れないが、int型だから当然だろ…という人は除いて、一度、int型の変数の記憶方法を再確認してみるのも良いだろう。多分,char型の配列を(unsigned int *)型のポインタにキャストした場合に、(unsigned int *)型のポインタから得られる値が、この形だからだと推測出来る。以下に、漢字混じりの文字列をEDITに送る例を2点紹介するが、自分の好みで使い分ければ良いだろう。
例)共用体を利用した例
typedef union _UINTX{
unsigned int ix ;
struct{
unsigned char bl ;
unsigned char bh ;
}bt ;
}UINTX ;
BOOL IsShiftJISKanji(int x)
{
x&=0x00ff ;
if(x>=0x80 && x<=0x9f) return(TRUE) ;
if(x>=0xe0 && x<=0xfc) return(TRUE) ;
return(FALSE) ;
}
void SendString(char *buff)
{
unsigned int i,c ;
UINTX cx ;
for(i=0 ; buff[i]!='\0' ; i++){
c=buff[i] ;
if(IsShiftJISKanji(buff[i])){ //漢字コードの処理
if(buff[i+1]!='\0'){
cx.bt.bl=c ;
cx.bt.bh=buff[++i] ;
c=cx.ix ;
}
}
SendMessage(hdisp,WM_CHAR,c,0) ;
}
}
|
例)ポインタにキャストする場合(IsShiftJISKanjiについては上と同様)
void SendString(char *buff)
{
unsigned int i,c,*ch ;
for(i=0 ; buff[i]!='\0' ; i++){
c=buff[i] ;
if(IsShiftJISKanji(buff[i])){ //漢字コードの処理
if(buff[i+1]!='\0'){
ch=(unsigned int *)&buff[i] ;
c=*ch ;
i++ ;
}
}
SendMessage(hdisp,WM_CHAR,c,0) ;
}
}
|
- 動作例
実際のアプリでの動作例については、CWCCの方を参照であるが、上述した項目の動作確認用に作成したサンプルが有るので、こちらで動作を確認してほしい。(BCC32 -W test013でコンパイルして下さい)
サンプル013のソースコード
PostMessageとSendMessageの違い。
ここに書く内容かってゆーと、?なのだが、PostMessageとSendMessageの違いを書いておく。…忘れない内に、この両関数は、結果的には、相手のWindowプロシージャにメッセージを送るという同じ動作をするのだが、何故、2種類存在するのか?という事に注目してほしい。これは、両者の戻り値の違いから伺い知る事が出来る。Post…の方はBOOL,Send…の方はLRESULTである。即ち、Sendの方は、相手のWindowプロシージャの戻り値を受け取れる訳だ。逆に、Postの方は、メッセージが正しく送信出来ればTRUEになる。動作的には、モーダルダイヤログとモードレスダイヤログの違いに似ていて、Postの方は、メッセージを送ったら終わりなのに対して、Sendの方は、メッセージを送ったら、相手のウィンドウプロシージャが、処理を終わるまで、その関数の中で待つ訳だ。従って、SendMessageは、結果をもらえる点が優れているが、自分に対して使うと、永久に終わらないループに入っちゃうので注意しよう!って、ここんとこ気を付けよう。
|