メニューに設定したアイテムの文字列の先頭番地は変数pm(選択したメニューのメッセージポインタ)に格納されます。したがってpmが指す番地を先頭とする文字列は,出力することが可能です。
1 ^^ "" ;入力窓やコマンドラインでも使用可能
!01 (r<0)?.
&?("%s",pm)
*p
01 "選択アイテムを出力",20,4
"りんご" "みかん" "かき" "もも"
もしもメニューに選択用ダイレクトキーをセットしている場合は,出力したい位置までのバイト数を足します。例えば,"A りんご" というアイテムで「りんご」だけを出力したい場合は,「&?("%s",pm+2)
」のように指定します。
メニューで正常にアイテムが選択された場合,メニューの返り値は選択されたアイテムの番号になります。アイテム番号は0から数え始めますので注意してください。
1 ^^ "" ?.
!01 (r<0)?.
&m("%d番のアイテムが選択されました.",r)
*p
01 "選択アイテム番号を表示",20,4
"りんご" "みかん" "かき" "もも"
メニューの構造は次のようになっています。便宜的にメニューの先頭をメニュー番号の位置とし,メニュー先頭を0とした相対番地で示します。
メニュー先頭からの相対番地 | 値を使用する要素 |
---|---|
0 | メニュー番号 |
1-2 | メニューのサイズ(下位・上位) |
3 | 横幅 |
4 | アイテム数 |
5 | パラメータ幅 |
6 | 選択アイテム番号(初期値0) |
通常,メニューのカーソルは初回起動時は一番上にあり,二回目以降は前回に選択したアイテム上にあります。マクロを起動して表示されたメニューのカーソルを常に特定の位置に固定するには,メニューに記録された情報を強制的に書き換える必要があります。
次の例は,メニューのカーソルを常に二番目のアイテムに置きます。
1 ^^ "" ?.
&> r.6=1, ;起動時にメニューのカーソル位置を設定
!01 (r<0)?.
&m("%sが好きです.",pm)
0: (2+$) ;--- Top of Menu ---
*p
01 "みかんが好き",20,4
"りんご" "みかん" "かき" "もも"
通常のメニューは,次のアクションのために複数のアイテムから選択させるという使い方をしますが,ヘルプメニューのような場合は選択の必要がなく,むしろメニューカーソルがあると見づらい,格好悪いということになります。このようなときはメニューを選択型でなく参照型とすることができます。
メニューの書式は次のようになっています。
番号 "[タイトル]",幅,アイテム数 [,パラメータ幅 [,-1] [;コメント] ]
"アイテム1" "アイテム2" …
大括弧[と]に囲まれた部分は省略可能な項目です。したがってメニューの先頭行に必要なのは,番号 "",幅,アイテム数だけです。以下の項目のうち「-1」とあるのが参照型メニューとするための特別な値です。これを記述する場合は,ひとつ前のパラメータ幅の項目が省略できないので,カンマだけは余分に入れます。カンマを忘れると,参照型とならずにカーソルが表示され,パラメータ幅が最大になって枠線を消してしまいますので,すぐに気付くでしょう。
1 ^^ "" ?.
!01
*p
1 "",20,4,,-1
"りんご" "みかん" "かき" "もも"
前項でメニューの書式を簡単に書きましたが,アイテムについてはかなり省略したので,改めてアイテムの書式を説明します。
ひとつのアイテムの詳細な書式は次のようになります。
"<キー>アイテム[<TAB><ON><SPC><OFF>]"[,#nn|&nn|!nn|オプション]
具体的にどのようなところで使われているかと言えば,モードメニューです。例えば次のような記述です。
* P メニュー
2 "",30,14,6
"1 システム情報の表示",Ds
"2 画面の横幅(2〜250)",WE
"3 ページング単位$(9)全画面 半画面",Eh
オプションの値が変更できるのは,このような形式で設定し,かつパラメータの表示幅を指定している場合(この例ではパラメータの表示幅は6文字が指定されています)です。それでは順に見ていきましょう。
1の例です。「"1 システム情報の表示",Ds
」のように,表示すべき値が「ON/OFF」で判別できる(あるいは「ON/OFF」でないと判別できない)スイッチの指定に使われます。[CR]([Enter])で選択すると「ON/OFF」を切り替え,変更後もメニュー表示を継続します。ダイレクトキーで選択した場合は,変更後にメニューから抜けます。
2の例です。「"2 画面の横幅(2〜250)",WE
」のように,値そのものを表示しなければならないオプションの指定に使われます。選択すると入力窓に切り替わって,入力後はメニューに戻ります。(ダイレクトキーで選択した場合は,即座にメニューから抜けます。)
3の例です。「"3 ページング単位$(9)全画面 半画面",Eh
」のように,「ON/OFF」よりも判別しやすい対の文字列をスペースで区切って設定することもできます。アイテムの文字列を工夫すれば「ON/OFF」でも十分な場合もあります。この例では「"3 半画面ページング",Eh」とすることもできるでしょう。
メニューから別のメニューを呼び出すことができます。メニューのアイテムに続けてカンマとメニューの呼出命令を記述します。
"N 次のメニュー",!02
サブメニューからオートセーブの設定を呼び出す具体例が製品版にありますので,参考にしてください。
3 "",26,13 #45 #52 #54 #66 #67 &26 &27 #68 &15 #79 #83 "S オートセーブの設定",!06 "$4EMS free/total: $ef/$em" 6 "【オートセーブ】",23,3,4 "T ディレイタイム",Qt "C 入力文字数",Qc "W 確認待ち時間",Qw
さて,メニュー同士をリンクさせていけば「リングメニュー」?になるであろうことは,特に説明を要するものではないと思います。ここではもう一歩踏み込んで,前後のメニューを簡単に渡り歩く手法を紹介します。
メニューマクロで最も一般的であるのが外部マクロ選択メニューだと思われます。その代表が見米快之・榊原知両氏によるEXMENU.DEFで,このマクロでは左右のカーソルキーで前後のメニューを渡り歩くことができるようになっています。
まず,左右の矢印キーを押すことで特定の動作をさせるには,メニュー表示中に特殊キーが押されたときの振る舞いを理解する必要があります。別の項目として用意してあるので,まず「メニューで押された特殊キーに応じた動作をさせるには」を参照してください。
それでは本題です。
左右カーソルキーに応じてメニュー番号の値を増減させ,1〜30の範囲でモジュール内に実在するメニューを探し,あればそのメニューを,なければ前後の一番近い番号のメニューを再度探して表示するという動作になります。
これを実現するコードをEXMENU.DEFから抜き出して構成すると,次のマクロのようになります。
= menutest =
* m
1 ^^ ""
t1, ;メニュー番号初期値
:m &Sp(pu,"!%02dr",t) &e(pu)
(r>=0)?{ n=r, >a }
(r+1)??.
n=r>>8 & $7F,
(r&$FF+256)>!{
(#s)&04 ;[<--]前メニュー
(#d)&05 ;[-->]次メニュー
(#h)(t=t>1) ;[BS]ルートメニュー
}
(t)?>m .
:a ;;以下は選択したアイテムに応じた動作をさせる
; - - - ローカルマクロ - - -;
; tはメニュー番号($E1〜$FEの30枚が通常メニューとして使用できる)
; ($FFはモジュールメニューの専用番号だから使用しない)
04: ;前メニュー
(t--)??t30, &10 ??>^
05: ;次メニュー
(t++>30)?t1, &10 ??>^
10: ;メニュー検索
; モジュール内のメニューをリンクポインタをたどって探す
; 難しいのでブラックボックスをごっくんということで
;-->
;t=メニュー番号
;<--
;r=0:メニュー無し
;r=その他:メニューの先頭アドレス
r=mg..2..0-3, ;実行中マクロの先頭
:A r+=r++..0+2,
(r.0)>!{(t+$E0)>B (0)>Z ($FF)>Z } >A
:B (r+r.4*2+9).
:Z (0).
* p
1 "壱",12,3
"アムロ" "セイラ" "シャア"
2 "弐",12,3
"マチルダ" "ガルマ" "カイ"
3 "参",12,3
"ララァ" "キシリア" "ギレン"
*
メニューは通常マクロファイルの内部に呼出番号もアイテム固定されていますが,これは静的なメニューにあたります。
動的なメニューは生成場所により,次の三種類に分類されます。
1の半動的とも言えるメニューは,単一のメニューで構造さえ理解していれば比較的容易に生成できます。歴史的にも,比較的早い段階に自然発生的に開発された技術です。古典とも言えるNOMAD氏のcalender.defが代表的な使用例です。メニューのヘッダ部分にはほとんど手を付けません。
2の内部バッファ方式,3の外部バッファ方式とも,メニューのヘッダ部分から構築しますのでアイテム数や表示幅なども自由に設定可能です。
内部バッファ方式の代表例はsaki...氏のstkHELP.defやTOM氏のvztags.defなどで,バッファを確保しているため環境による制約はありませんが,バッファの分だけマクロが大きくなります。
外部バッファ方式の代表例は,Yushi氏の高速カレンダーやEmmett氏のstkmenu.defなどで,その多くはラインバッファ上にメニューを構築しています。モジュール内部にメニュー用のバッファを確保しなくてもよいのでマクロのサイズが小さくできますが,環境によってラインバッファの大きさが異なることを念頭に置く必要があります。
動的メニューの構築方法はいずれも同じです。メニューの構造は決まっているため当然のことなのかもしれません。ですが,呼出方法はそれぞれのマクロで色々と工夫されていてさまざまです。詳しいことは,個々のマクロを参照してください。
次のマクロは,マクロの魔術師と呼ばれたYushi氏が動的メニューの原型を発表したときのものです。(nifty:FGALPK/mes/13/2685, 1996/01/27 01:16)
=T12= * M 1"" ?. &09 p.0=15, ; メニューの横幅 p.1=i=2, ; アイテム数 p+=2, (i*2+4){ p.0-, p++, } &Sc(p,"==MENU==") p=r+1, &Sc(p,"menu1") p=r+1, &Sc(p,"menu2") p=r+1, !01 9: p$-4, p+=5+p..0, 97: "$[900]" *