Xtを使っていると、コード中に似たような記述が含まれることに気づきます。 普通それは関数にまとめて再利用する形で使うのですが、 Motifについては ウィジェットの生成部分について余りにもよく使うというので 専用の言語が用意されています。
それがUIL(User Interface Language)です。
UILは次のXt関数の組み合わせを簡単にしてくれます。
API | 役割 |
---|---|
XtCreateWidget XtVaCreateWidget XtCreateManagedWidget | ウィジェットの生成 |
XtSetValues XtVaSetValues | リソースの設定 |
XtAddCallback XtAddCallbacks | コールバックの登録 |
XtCreatePopupShell XtVaCreatePopupShell | ポップアップシェルの生成 † |
XtPopup | ポップアップシェルの表示 † |
さらにMotif関係についても利点があります。
リソース ファイルと機能的に重なりますが、次の事もできます。
バランス問題と着目点の違いだとおもいますが、 次の性質は知っていたほうがでしょう。
† これはMotifウィジェットの性質によるものなので、ここに含めるか迷いました。 ここで意識しているのはXmDialogをXtManageすることにより表示されることです。 ‡ それぞれのロケールに応じたUILを準備するか、 テキスト関係はリソース ファイルに記述するかして対応します。
箇条書にまとめてみました。
MRM(Motif Resource Manager)は、UILをCで扱うためのAPI群です。 命名規則により、それがMRMのAPIかどうかが すぐにわかります。
UIの動作確認は次の手順になります。 簡単なアプリケーションでは正式な実装もあまり違いはありません。
UILの文法は、附属マニュアルのUIL(5)にて参照できます。 ほとんどの人は英語版しか入手できないと思いますが、 一度は一通り目を通した方がよいと思います。 同様の内容が「OSF/Motif Programmer's Reference」でも参照できます。
もちろん、このメモ書きを頼りにするだけでも それなりのコーディングはできることでしょう。
UILのコーディングの単位の一つにモジュールがあります。 モジュールはコンパイル単位でもあり、名前空間の管理の単位でもあります。
UILのモジュールの宣言は次のようになります。
module module_name
! オプションの指定
names = case_sensitive
!ここにモジュールごとの定義を記述する
end module;
module 〜 end module 間に必要な記述を含めます。
コメントはCの形式と同様の/* 〜 */ 形式か、「!」から改行まで という形式が使えます。
☆
uilコンパイラの引数で渡すファイルには、必ずこのmodule文が含まれるようにしなくてはなりません。 include fileの対象となるUIL定義ファイル(後述)は存在しなくても構いません。
CからはMrmOpenHierarchyPerDisplayを使って、関連するモジュールをまとめて取り込みます。 モジュールの操作は MrmHierarchy 型の hierarchy_id経由で行ないます。
☆
モジュール定義にはいくつかオプション指定があります。
当面は name = case_sensitive; を付けておけばよいでしょう。 この意味は「識別子の文字列のケースの違いを考慮する」ということです。 この指定がなければ(あるいは case_insensitive であれば)、 全て大文字に変換された上で扱われます。
オブジェクトの定義が、よく利用され、かつ重要な部分です。 ウィジェッとのリソースと親子関係とコールバックを定義します。
名前とウィジェットの種類と属性を、ブレスで囲みながら、入れ子構造に定義していきます。
object
foo_main_window : XmMainWindow {
arguments {
XmNworkWindow = foo_canvas;
};
controls {
XmMenuBar foo_menu_bar;
foo_canvas : XmDrawingArea {
arguments {
XmNwidth = foo_width;
XmNheight = foo_height;
};
};
}; !controls for main_window
};
object
foo_menu_bar : XmMenuBar {
arguments {
XmNmenuHelpWidget = foo_help_item;
};
controls {
foo_canvas_item : XmCascadeButtonGadget {
arguments {
XmNsubMenuId = foo_canvas_pulldown_menu;
};
};
foo_help_item : XmCascadeButtonGadget {
arguments {
XmNsubMenuId = foo_help_pulldown_menu;
};
};
}; !controls for menu_bar
};
object
foo_canvas_pulldown_menu : XmPulldownMenu {
controls {
foo_close : XmPushButtonGadget {
callbacks {
XmNactivateCallback = procedure foo_canvas_close(foo_closure);
};
};
foo_refresh : XmPushButtonGadget {
callbacks {
XmNactivateCallback = procedure foo_canvas_refresh(foo_closure);
};
};
};
};
/* help_pulldown_menu については
canvas_pulldown_menu 類似のため略 */
それぞれのウィジェットの定義は
object ウィジェット名 : ウィジェットの種類 { パラメータ定義リスト };となります。
当然ながら、(標準では)Motifでサポートしているウィジェットしか指示できません。
パラメータの定義には
そのウィジェットがサポートしていないものについてargumentsやcallbacksを指定すると エラーや警告扱いになります。
リソースとパラメータの型の組み合わせが違っていても知らせてくれます。 例えば数値を必要とするところに文字列を指定したり、 フォントを扱うところに計算式を指定したりするとエラーになります。
親によって指示できるコンストレント リソースは変化します。 例えば親がXmPanedWindowであれば、そのウィジェットで XmNskipAdjustが指示できるようになります。
ウィジェットを指示するリソースには、 uilファイル中で定義してるウィジェット名を与える事ができます。
ウィジェットがコンポサイトを継承したものである場合に限って controlsを指定できます。 controlsに含める子の定義は入れ子になるように記述してもよいですし、
[unmanaged] ウィジェットの種類 ウィジェット名;あるいは
[unmanaged] ウィジェットの種類 { パラメータ定義リスト };このような形式で指定しても構いません。
ウィジェット名を省略すると、適当な名前が振られます。
(上の例では使っていませんが)ウィジェットの種類の前に unmanaged を添えると MrmFetchWidgetの際にXtManageWidgetが呼ばれない状態で生成されます。 unmanagedは、ダイアログを定義する時に利用します。
親子の組み合わせによっては、うまく動作しないものがあるのですが、 明らかに想定外の使い方をしている部分については エラーや警告を出してくれる事もあります。
プログラムの動作時に値を決定する必要のあるパラメータについては identifierについてその識別子を宣言しておき、UIL中で利用することができます。 一般にはコールバックのclosureパラメータとして渡す 値をこれで指定することになると思います。
! IDの宣言
identifier
hierarchy_id;
foo_closure;
Cでこのidentifierに対応する値の設定は、用途に応じて MrmRegisterNames あるいは MrmRegisterNamesInHierarchy を利用します。
UILの記述で繰り返し利用される文字列や数値は、 あらかじめ宣言しておいて、マクロ定義のように繰り返し利用できます。
この段階で利用するか、Xtリソース ファイルで利用するかは プログラム作成者によって判断がわかれるところでしょう。
(個人的にはメニューの見出しに使われるテキストは UILではなく、リソース ファイルの仕掛けを利用したほうがよいと思っています。)
! IDに対応する定数の宣言
value
foo_width : exported 500;
foo_height : 400;
式にexportedを沿えると、別モジュールから参照できるようになります。
逆に別モジュールの値を扱う時には、importedにその型を沿えて宣言します。
! 別モジュールからの取り込み
value
foo_width : imported integer;
複数人数で開発したり、ファイルが大きくなってきたり、 特定の内容を使い回したりする時には、複数のファイルに分割しておいて、 適宜取り込むという手法が使えます。
UILでは include file 文で、それを実現します。
! 別ファイルの定義を読み込む
include file 'target-file-name.uil';
依存関係はMakefileにでも定義しておくとよいでしょう。
対処となるファイルは uilコマンドの -I オプションで検索パスを指定します。