雑学講座 第二回

[フォント...]ってどんな動作をしているの?前回の回答

答えは[例えばの3]でした。

[例えばの2]がいちばん簡単そうですが、ボタンに割り当てる番号って慣例がありまして、あまり勝手な番号を付けることってできなかったりもするんですよ。

ただ、[例えばの2]の方法は、コードが少なくて済むので、
独自の[フォント選択]ダイアログを持っているアプリにパッチを当てる場合に使える技ではあります。この辺はそのうち解説できれば良いですね。

例えば、SplashIDというアプリケーションでは、Small/Tinyフォントを自前で用意して、独自の[フォント選択]のダイアログを持っています。
ただ、悲しいかな海外版のアプリなため、TungstenシリーズなどでTsPatchを当ててSmall/Tinyフォントを持たせていても、自前のフォントを使ってしまう為日本語の小さな表示ができないのです。

さて、本題の[例えばの3]の解説に移りましょう。

PalmIncが公開しているOSのソースコードからFontSelectを抜粋しましょう

FontID FontSelect (FontID fontID)
{
 UInt16 index;
 UInt16 ctlID;
 UInt16 buttonHit;
 UInt16 numObjects;
 FormPtr frm;
 ControlPtr ctl;

 frm = FrmInitForm (FontSelectorForm);

 ctlID = FontSelector1Button;

 // Find the push button the has the specified font id.
 numObjects = FrmGetNumberOfObjects (frm);
 for (index = 0; index < numObjects; index++)
  {
  if (FrmGetObjectType (frm, index) == frmControlObj)
   {
   ctl = FrmGetObjectPtr (frm, index);
   if (ctl->group == FontSelectorFontGroup && ctl->font == fontID)
    {
    ctlID = ctl->id;
    }
   }
  }

 FrmSetControlGroupSelection (frm, FontSelectorFontGroup, ctlID);

 // Display the font selector.
 buttonHit = FrmDoDialog (frm);

 // Get the font setting from the dialog if the "OK" button
 // has pressed. We will return the font id of the push button that was
 // selected
 if (buttonHit == FontSelectorOKButton)
  {
  index = FrmGetControlGroupSelection (frm, FontSelectorFontGroup);
  ctl = FrmGetObjectPtr (frm, index);
  fontID = ctl->font;
  }

 FrmDeleteForm (frm);

 return (fontID);
}

なにやら複雑怪奇に見えますが、概要を述べると...

1.現在のフォントに相当するボタンを選択された状態に設定する
2.
[フォント選択]のダイアログを表示する
3.[OK]が押されたら、ユーザが選択したボタンに表示されているフォントが何かを調べて
4.その値を呼び出しもとのアプリケーションに返す

では、順を追って...

まず、

FontID FontSelect (FontID fontID)

これは、関数の定義です。
特に解説はしませんが、「この形式でこの関数は使われますよ」とコンパイラに教えているところです。

次に

UInt16 index;
UInt16 ctlID;
UInt16 buttonHit;
UInt16 numObjects;
FormPtr frm;
ControlPtr ctl;

ここは、この関数の内部で使う変数の定義。
「ここで挙げた変数を使うので、この変数を格納する領域を確保してくださいね」とコンパイラに教えている部分です。

その次は

frm = FrmInitForm (FontSelectorForm);

ここで、[フォント選択]のダイアログを初期化しています。お約束ごとと思ってください。
FontSelectorFormというのは、実際は11900という定数になっていて、この番号は[フォント選択]のダイアログにつけられた番号です。
つまり、11900番のフォームである[フォント選択]のダイアログを初期化してくださいということですね。

さて、お次は

ctlID = FontSelector1Button;

これは飛ばして、

// Find the push button the has the specified font id.
numObjects = FrmGetNumberOfObjects (frm);
for (index = 0; index < numObjects; index++)
 {
 if (
FrmGetObjectType (frm, index) == frmControlObj)
  {
  ctl =
FrmGetObjectPtr (frm, index);
  if (ctl->group == FontSelectorFontGroup && ctl->font == fontID)
   {
   ctlID = ctl->id;
   }
  }
 }

ここで、FontSelect関数が呼ばれたときに括弧の中に入っているフォントの番号に対応するボタンを選択された状態に設定しています。

一行目のFrmGetNumberOfObjectsで、[フォント選択]のダイアログには幾つのオブジェクトがあるかをOSに教えてもらいます。
(オブジェクトというのは一つの概念で、ボタン、リスト、チェックボックス、文字入力フィールドなど、フォームの中にある部品のことと捉えて下さい)

で、次のfor (index = 0; ind...の中で、一つずつオブジェクトのタイプを調べて行きます。

一つ目のif (FrmGetObjectType (fr...ではオブジェクトがfrmControlObjかどうかを調べて、frmControlObjであれば次のif...に入ってゆきます。
(frmControlObjとは、ボタン、チェックボックスなどの動作をコントロールする部品のことです)

二つ目のif (ctl->group == Fo...では、オブジェクトに表示されているフォントのIDがFontSelect関数が呼ばれたときに括弧の中に入っているフォントの番号と等しいかどうかを見ています。
ここで、等しければctlIDという変数にそのオブジェクトの番号を格納して、次のブロック

FrmSetControlGroupSelection (frm, FontSelectorFontGroup, ctlID);

で、そのオブジェクト(ボタン)を選択された状態に設定します。

ここまできてやっと

// Display the font selector.
buttonHit = FrmDoDialog (frm);

[フォント選択]のダイアログが表示されることになります。

ここで、FrmDoDialogという関数は、フォームを表示して、[OK]や[Cancel]などのボタンが押されるまで待って、
ボタンが押されたら、表示したフォームを消して、どのボタンが押されたのかを返してくれるという動作をします。
今回の[フォント選択]の場合は、[OK]か[キャンセル]のボタンの値が帰ってくることになりますね。

もうすこしです。がんばって次に行きましょう。

// Get the font setting from the dialog if the "OK" button
// has pressed. We will return the font id of the push button that was
// selected
if (buttonHit == FontSelectorOKButton)
 {
 index =
FrmGetControlGroupSelection (frm, FontSelectorFontGroup);
 ctl =
FrmGetObjectPtr (frm, index);
 fontID = ctl->font;
 }

ここからが、前回の回答に相当する部分です。

まず、

if (buttonHit == FontSelectorOKButton)

これは、[キャンセル]だったら何もしないと読み替えると判りやすいかも。

次の、index = FrmGetC...で、ユーザが選択していたボタンがどれかをOSに教えてもらいます。
ここでは、ボタンに割り当てられたindexという値が帰ってきますので、
次の行の、ctl = FrmGetObjectPtr (frm, index);で、実際にそのボタンの情報にアクセスするための入口を改めてOSに教えてもらいます。
ここでは、ctlという変数がその入口になりますね。

※この入口というのは、メモリ上のアドレスに相当します。
で、そこに何が入っているかというと、この場合は
ボタンのさまざまな属性の情報です。
例えば、画面上の位置座標、表示/非表示の状態、選択/比選択の状態、
表示している文字のフォント...

つまり、これ以降は、ctlという変数(ポインタ)を使って、そのボタンの様々な情報にアクセスできるということです。

で、肝心かなめの、次の一文、

fontID = ctl->font;

ここで、選択されているボタンに表示しているフォントが何であるかを取得して、fontIDという変数にその値を格納。
ctl->fontという記述は、「ctlという入口から入るとfontというラベルのついた数値があるので、その値を持ってきてくださいね」というような意味です。

で、最後

return (fontID);

はあ、やっと終わりました。

ここまでの話をまとめると、FontSelectという関数は、

ユーザが選択したボタンに表示されている文字のフォントの番号を返してくれる。
ボタンにはどんな番号を付けていても、どこに置いてあっても、どんな大きさでもかまわない。
(というか、このへんの情報は一切見ていません)

ということになります。

たとえば、超巨大なフォントがあったとしたら、どこかのボタンをそのフォントで表示する設定にしておけば、
OSは何も考えずにそのフォントの番号を返してくれるということになるわけです。

稚拙のSelectFontの動作、なんとなく見えてきました?

ボタンを追加して、Tiny/Smallのフォントを表示する設定にした[フォント選択]のダイアログを用意しているだけなんです。

なんとなく見えてきたところで、
次回は、OSが用意している
[フォント選択]のダイアログをどうやってすげ替えるかについて解説しようとおもいます。