Macintosh用アプリケーションでのコントロール(Control)の活用を、数回に分けて解説してみたいと思います。今回はその第一回目です。コントロールの仕組みが、Mac OSの進化と共にどのように変化して現在に至ったかをお話します。
この連載で利用する開発環境は「すっぴん」の「Metrowerks CodeWarrior」と「Resorcerer」(リソース編集アプリケーション)を予定しています。Resorcererは「ResEdit」でも代用できますが、コントロール定義の機能が限定されてしまいます。Metrowerks PowerPlant等のFrameWorkや、何かしらのRAD(Rapid Application Devellopment)環境を利用されている方は、Resorcererでシコシコとリソースを編集する必要はないかもしれません。しかし、コントロールの詳しい仕組みと挙動を知っておけば、将来何かの役に立つこともあるでしょう。しばらくの間おつきあいくださいませ。Mac OS Xの登場をにらみ、Carbon環境の最新の動向もからめて話を進めたいと思います。
言うまでもなく、コントロールとはボタン、チェックボックス、ラジオボタン、スクロールバーなどの事です。ダイアログやドキュメントウィンドウ上に配置し、アプリケーションを効率よくナビゲートするために使います。アプリケーションでユーザインターフェースをつかさどる部品としては、メニューと並んで重要な位置を占めています。コントロールはToolBoxの「Control Manager」に属するAPIでハンドリングします。Control ManagerはSystem 7.xからMac OS 8へ切り替わった時に、Appearance Managerの導入に伴い大きく機能アップされました。その種類も大幅に増え、見栄えもグレーを基調とした立体的な外見へと変化しました。
当時、貧弱なコントロールしか提供されていない時代があまりにも長かったので、各デベロッパーはCDEF(Control Definition Function...自分独自のコントロールが定義できる仕組み)を用い、独自のコントローラを構築していました。おかげで、Macintoshのユーザインターフェースの統一性が崩壊しかかっていたのも事実です。筆者としてはそれはイヤでしたので、ディフォルト以外のコントロールはあまり使わないように心掛けていました。しかし、System 6/7時代のコントロールの見栄えはあまりにも悪く、見栄えの良いコントロールを使ったアプリケーションが登場するのを見るにつけてクヤシイ思いをしていたわけです。ですから、Mac OS 8で格好良いコントロールが登場した時には拍手喝采でした。しかし、物事はそうはうまく行きません。(図1)

図1-見栄えの悪かったSystem 7.x時代の旧コントロール達
Mac OS 8がリリースされたと言っても、世の中はSystem 7.x環境でアプリケーションを使っているユーザがほとんどです。ボタン、チェックボックス、ラジオボタン、スクロールバーなどの旧コントロールは、Appearance Managerの働きで、OSが切り替わるとその表示も自動的に切り替わりました。なにやら異様にコントロールだけが浮いて見えるという問題点はありましたが(笑)特別な事をしなくてもその恩恵を受けることができたわけです。ところが、新規に導入されたBevelボタン、スライダー、Tabグループ、日付入力カラム、パスワード入力カラムなどなど、筆者が喉から手が出るほど欲しかったコントロールを使ってしまうと、そのアプリケーションはMac OS 8上でしか起動できなくなってしまいます。コントロールの話しに限った事ではありませんが、旧OSバージョンを利用しているユーザ数を考慮すると、市販パッケージにすぐさま新機能を採用するというのはなかなか難しい問題を含んでいます。
PowerPlantなどのFrameWorkは、コントロールの違いを内部ルーチンで吸収してしまいましたので、そうしたユーザは短期間のうちに問題を解決できました。ところが、すっぴん好みの私としてはそうは行きません。とは言っても、世の中にどんどん見栄えの良いコントロールの配置したアプリケーションが出回るわけですから、さすがに何か手を打たないとマズイと言うことになりました。ひょっとするとAppearance SDKに含まれる「Appearance Extension」をアプリケーションと同時配布すれば、System7.xユーザでも新しいコントロールが使えたかもしれません。しかし、こうした手段はメンテナンスを考えるとリスキーです。しょうがないので、System 7.xでもMac OS 8でも利用できる「Control Managerもどき」を自作し、System 7.xを使うユーザが絶滅するのを気長に待つことにしました。つまり、PowerPlantがやている事を自分自身でやってしまったわけです(笑)。(図2)

図2-自作「Control Managerもどき」で表示したコントローラ
時代は流れ、8.5、8.6、9.0とMac OSのバージョンも上がり、いよいよMac OS Xがリリースされようとしている今日この頃です。今なら大手を振って新コントロールのみを利用しても大丈夫でしょう!(と言うか、今までよく我慢した...)System 7.xユーザのみなさんごめんなさい。先に謝っておきます(笑)2000年9月12日現在、Control Managerに関する最新ドキュメントは、Appleの以下のサイトからダウンロードすることができます。
http://developer.apple.com/techpubs/macos8/pdf/pdf.html
System 7.xからの変更点は、「Control Manager Reference, Mac OS 8 」に詳しく記載されています。ダウンロード後のPDFファイル名は「ControlMgrRef.pdf」です。また、その上の「Control Manager, Programming With」(ウェブでは途中で表記が切れていると思われる)には、Mac OS 8.5になって拡張された部分が記載されています。こちらのPFDファイル名は「MacOS8.5ControlMgr.pdf」です。また、HTMLで記載された最新リファレンスは、以下のサイトで閲覧することができます。
http://developer.apple.com/techpubs/macos8/HumanInterfaceToolbox/ControlManager/controlmanager.html
新コントロールの種類や機能を確認するためのサンプルも配布されています。(Appleには珍しくできが良い)ひとつは以下のApple SDKサイトからダウンロードできる「Appearance SDK 1.0.4」の「Appearance Sample Code」フォルダに含まれる「AppearanceSample.proj」です。(図3)
http://developer.apple.com/sdk/

図3-ppearance SDK 1.0.4の内容
ところが、このプロジェクトをコンパイル&リンクしてMac OS 9.04で起動するとハングアップします。(うちの環境だけかもしれない?)そうした場合には、まったく同じサンプルが、同サイトにある「CarbonLib 1.0.4 SDK」の「Sample Code」フォルダの「AppearanceSample」にあるので、こちらを利用してください。機能拡張フォルダにCarbonLib 1.04がインストールされていれば、実に安定して動きます。またCarbon化のサンプルとしても参考になりますので、こちらの方がお薦めかもしれません。(図4)(図5)

図4-サンプルアプリケーションで表示した旧コントロール

図5-サンプルアプリケーションで表示した新コントロール
次に、旧コントロールとMac OS 8以降のそれとの大きな違いを説明しておきます。旧コントロールは実にシンプルでした!各コントロールが保持している情報は、表示位置(矩形領域)と名称、表示のON/OFF、それに初期値、最小値、最大値、種類、ユーザ定義値(refCon)だけでした。新規にコントロールを作成するためのNewControl() APIの引数を見ると、そのシンプルさが良く理解できます。(図6)

図6-お馴染みNewControl()ルーチン
最新のUniversal Interfacesでは、お馴染みのControlHandleをControlRefと、WindowPtrをWindowRefと記載します。おしゃれな書き方ですが内容は同じですので心配は無用です。例えばチェックボックスなら、最小値にゼロ、最大値に1を代入し、初期値がゼロならチェックされていない、1ならされている状態を表示します。
ところが、System 7でPopupメニューコントロールが導入された時に、Appleはとんでもない事をやらかしました。NewControl()の引数、もしくはCNTLリソースで設定する初期値、最小値、最大値のパラメータに、元来の目的でないパラメータを代入させるという暴挙です。これは、Popupメニューコントロールが、今までのパラメータの種類だけでは定義できなかったところに原因があります。具体的言うと、固定幅Popupメニューでは、初期値にフォントのスタイルコード、最小値に表示されるメニューのMenu ID、最大値にメニューのタイトルの幅(ピクセル数)を代入することで作成します。ところが、ユーザに選択されたメニューアイテム番号は、他のコントロール同様にGetControlValue()ルーチンで得ることになります。実に美しくない仕様です。
Popupメニューを導入した時に、Appleは思い切って新しいAPIを導入すべきでした。Mac OS 8での新コントロールでもこの悪しき伝統は継続され、NewControl()の初期値、最小値、最大値の3つのパラメータは本来の意味を持たなくなりました。複雑すぎて、ドキュメントを参照しながら代入しないと何がなんだかわからない状態です(涙)。また、コントロールの種類(複雑な物)によっては、3つのパラメータだけで状態変化を定義することが不可能になり、新規のパラメータを設定する必要が出てきました。そのための策として、SetControlData()という新しいAPIが導入されています。引数のResType(Tagと呼ぶ)のところに、受け渡したいしたいパラメータのタイプを代入し、そのデータのポインタと長さを渡してパラメータを設定します。つまり、コントロールになんでも渡せるオールマイティのAPIと言ったところです。逆に現在の特定の状態を得るのにはGetControlData() APIを使います。(図7)

図7-SetControlData()とGetControlData()ルーチン
たとえば、日付カラムに入力された年月日の内容は、SetControlValue()でなく(当たり前だ...)以下のようにSetControlData()で設定できます。(図8)

図8-日付データをコントロールへ設定する
コントロールを作成する時に設定する初期値、最小値、最大値がどんな意味を持つのか?SetControlData()で渡せるパラメータにはどんな種類があるのか?については、最新の「Universal Headers 3.3.2」に含まれる「ControlDefinitions.h」にすべて定義されています。このヘッダーファイルのコメント行には、それらの利用方法も紹介されていますので、プリントアウトして一読されることをお薦めします。多分、その多さと複雑さにビックリされるでしょう、Control Mnagerを呼んでいるアプリケーションをコンパイルする時、今までなら「Controls.h」のみをincludeしておけばよかったのですが、現在では同時にControlDefinitions.hもincludeします。でないと、コントロール関係のAPIを利用している場所で、ビシバシとコンパイルエラーが出る可能性がありますので注意してください。(図9)

図9-ControlDefinitions.hのTag定義の一例(Bevelボタン用)
次回は、実際にResorcererを使い、ダイアログにいくつかのコントロールを配置する過程を説明します。そんな事は簡単だと思われるかもしれませんが、これはこれで色々と秘密が隠されているのです。こうご期待!
copyright 2000 Ottimo, Inc. All rights reserved
無断転載・引用禁止
Contact Us: koike@ottimo.co.jp