[[BORLAND C++]]
INDEX
Section.12 EditControl
実は、ここには、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のソースコード

PostMessageSendMessageの違い。
ここに書く内容かってゆーと、?なのだが、PostMessageSendMessageの違いを書いておく。…忘れない内に、この両関数は、結果的には、相手のWindowプロシージャにメッセージを送るという同じ動作をするのだが、何故、2種類存在するのか?という事に注目してほしい。これは、両者の戻り値の違いから伺い知る事が出来る。Post…の方はBOOL,Send…の方はLRESULTである。即ち、Sendの方は、相手のWindowプロシージャの戻り値を受け取れる訳だ。逆に、Postの方は、メッセージが正しく送信出来ればTRUEになる。動作的には、モーダルダイヤログとモードレスダイヤログの違いに似ていて、Postの方は、メッセージを送ったら終わりなのに対して、Sendの方は、メッセージを送ったら、相手のウィンドウプロシージャが、処理を終わるまで、その関数の中で待つ訳だ。従って、SendMessageは、結果をもらえる点が優れているが、自分に対して使うと、永久に終わらないループに入っちゃうので注意しよう!って、ここんとこ気を付けよう。
2002/04/30
HomeSweetHome2
Ozzy's Software