ホーム >プログラム >Delphi 6 ローテクTips

TStatusBarにUnicodeを表示

TStatusBarはAutoHintプロパティがTrueでフォーム上のコントロールのShowHintプロパティがTrueの場合そのコントロール上にマウスカーソルが乗っている間そのコントロールのHintプロパティの内容を表示します。
操作の手助けとなる情報をユーザーに提示できるので重宝します。
これをUnicodeな文字を表示できるようにしようというのがこのページの趣旨です。


やり方

OnDrawPanelイベントが起きるように

StatusBarにUnicodeを表示するにはOnDrawPanelイベントが起きるようにしなければなりません。
そのためにListBoxやComboBoxのようにStyleプロパティでオーナー描画モードにする必要があるのですが、StatusBarにはStyleプロパティがありません。
ではどうするのかというと、まずオブジェクトインスペクタでPanelsプロパティを選択して右の列をダブルクリックするか右端の「...」ボタンをクリックします。
するとパネルエディタが開くのでパネルを追加します。
次に追加したパネルを選択してオブジェクトインスペクタでStyleプロパティをpsOwnerDrawにします。

Panelsプロパティの右の列、(TStatusPanels)となっている所をダブルクリックします。

パネルエディタが開くので左端のアイコンをクリックして「新規追加」します。

追加されたパネルを選択してオブジェクトインスペクタでStyleプロパティをpsOwnerDrawにします。
その他のプロパティは変える必要はありません。

この他にSimplePanelプロパティをFalseにすることでStatusBarのOnDrawPanelイベントが起きるようになります。

AnsiStringにUnicodeな文字列を持つ

StatusBarにOnDrawPanelイベントが起きるようになったのは良いとして。
肝心のコントロールのHintプロパティにUnicodeな文字を持たせられなければ意味がありません。
D6のコントロールのHintプロパティには直接Unicodeな文字を持たせられないのでUnicodeな文字をAnsiStringに変換する処理を施します。
Unicodeな文字をAnsiStringに変換するにはウムラウトのようなUnicodeな文字だけを変換処理する独自関数を使います。

//--- WideString⇔AnsiString
//WideStringとAnsiStringの可逆変換関数
const
  l_csDEFAULT_CHAR = #1;
  l_csPREFIX_CHAR  = '$';
function gfnsWideToAnsiEx(const sWSrc: WideString): AnsiString;
{
WideString→AnsiString
WideStringをAnsiStringに変換
キャストするだけやWideCharToString関数と違ってウムラウトなどのUnicode文字を$+16進に変換して返す

http://yokohama.cool.ne.jp/chokuto/urawaza/api/WideCharToMultiByte.html
0x00000400 (WC_NO_BEST_FIT_CHARS)
Windows 2000/XP: 直接マルチバイト文字に変換できない文字をデフォルトキャラクタに置きかえます。
}

var
  i, li_Pos, li_Len: Integer;
  lb_Bool: LongBool;
  ls_PStr: PAnsiChar;
begin
  Result := '';
  if (sWSrc <> '') then begin
    li_Len  := WideCharToMultiByte(CP_ACP, $400, PWideChar(sWSrc), -1, nil, 0, l_csDEFAULT_CHAR, @lb_Bool);
    if (lb_Bool = False) and (Pos(l_csPREFIX_CHAR, sWSrc) = 0) then begin
      //ウムラウトのようなUnicode文字がなくプリフィクス文字($)もない場合は変換する必要なし
      //$は2バイト文字の第一バイトにも第二バイトにも現れないのでPosでよい
      Result := sWSrc;
    end else begin
      ls_PStr := AllocMem(li_Len + 1);
      try
        li_Len := WideCharToMultiByte(CP_ACP, $400, PWideChar(sWSrc), -1, ls_PStr, li_Len, l_csDEFAULT_CHAR, nil);
        if (li_Len > 0) then begin
          Dec(li_Len);
          i := 0;       //ls_PStr のインデックス
          li_Pos := 1;  //sWSrc   のインデックス
          repeat
            if (ls_PStr[i] = l_csDEFAULT_CHAR) or (ls_PStr[i] = l_csPREFIX_CHAR) then begin
              //デフォルトキャラクタとプリフィクスキャラクタは $xxxx の書式に変換
              Result := Result + l_csPREFIX_CHAR + Format('%0.4x', [Ord(sWSrc[li_Pos])]);
            end else begin
              Result := Result + ls_PStr[i];
            end;
            if (IsDBCSLeadByte(Byte(ls_PStr[i]))) then begin
              //2バイト文字の先頭バイトなのでもう1バイトスキップ
              Result := Result + ls_PStr[i + 1];
              Inc(i, 2);
            end else begin
              Inc(i);
            end;
            Inc(li_Pos);
          until (i >= li_Len);  //untilは条件がTrueで終了
        end;
      finally
        FreeMem(ls_PStr);
      end;
    end;
  end;
end;

function gfnsAnsiToWideEx(const sSrc: AnsiString): WideString;
//gfnsWideToAnsiExの逆
var
  li_Pos, li_Len: Integer;
  ls_WStr, ls_WCnv: WideString;
begin
  Result := '';
  if (sSrc <> '') then begin
    ls_WStr := WideString(sSrc);
    li_Pos  := Pos(l_csPREFIX_CHAR, ls_WStr);
    if (li_Pos = 0) then begin
      //2008-02-12:プリフィクス文字($)がなければ変換する必要はない
      Result := ls_WStr;
    end else begin
      li_Len  := Length(ls_WStr);
      repeat
        //'$'までの文字列を足しこむ
        Result := Result + Copy(ls_WStr, 1, li_Pos - 1);
        try
          //'$'に続く(はずの)4文字の数値をUnicode文字に変換
          ls_WCnv := WideString(WideChar(StrToInt(Copy(ls_WStr, li_Pos, 5))));
          Result  := Result + ls_WCnv;
          ls_WStr := Copy(ls_WStr, li_Pos +4 +1, li_Len);  //4文字の数値の次からコピー開始
        except
          //イレギュラー
          Result  := Result + WideString(l_csPREFIX_CHAR);  //イレギュラー時はそのままにする
          ls_WStr := Copy(ls_WStr, li_Pos +1, li_Len);        //'$'の次からコピー開始
        end;
        //次の'$'までの位置を取得
        li_Pos := Pos(l_csPREFIX_CHAR, ls_WStr);
        Application.ProcessMessages;
      until (li_Pos = 0);  //untilは条件がTrueで終了
      Result := Result + ls_WStr;
    end;
  end;
end;

OnDrawPanel

肝心のOnDrawPanelの処理内容です。

procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel; const Rect: TRect);
//Unicode対応ステータスバー
const
  lci_MARGIN = 2;
var
  lrc_Rect : TRect;
begin
  lrc_Rect := Rect;
  Inc(lrc_Rect.Left, lci_MARGIN);
  DrawTextW(StatusBar1.Canvas.Handle, PWideChar(gfnsAnsiToWideEx(Panel.Text)), -1, lrc_Rect, DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER);
end;

コントロールのHintプロパティには独自関数で変換したものをセットしますがUnicodeな文字がない場合はそのままをセットしてもOKです。
オブジェクトインスペクタでHintプロパティをセットする時にUnicodeな文字の必要がなければ特別な処理を行う必要がないということです。
逆にオブジェクトインスペクタでHintプロパティをセットする際にUnicodeな文字をセットしたい場合は'$'に続けてUnicodeな文字の4桁のコード番号を記述します。
下の例で言うと 'Queensr$00FFche.mp3' をセットすればステータスバーには 'Queensrÿche.mp3' と表示されます。

procedure TForm1.Button1Click(Sender: TObject);
begin
  //ウムラウトのような文字がある場合は独自関数で変換したものをセット
  //最初の文字列をWideStringでキャストしないとAnsiStringな文字列として処理されてしまう
  Button1.Hint := gfnsWideToAnsiEx(WideString('Queensr') + WideChar($00FF) + 'che.mp3'); //'Queensrÿche.mp3'

  //ウムラウトのようなUnicodeな文字がなければそのままセット(独自関数で変換しても問題はない)
  Self.Hint    := 'テスト';
end;

ウムラウトのようなUnicode文字「ÿ」がちゃんと表示されています。