即習:メニューを作ろう!(ResEdit(3))			2000.Sep.

ということで(いささか強引な気もするが)メニュー、作ってみましょう。
必要なリソースはMENUとMBARですね。先ずMENUかな。

最初はApple menu。アバウトダイアログのために1つだけ項目を作る。
次は Fileメニュー。最低でもQuitは付けるべきでしょうね。

大体は感性で操作すればできるんで(できるはず)説明をはしょります。
新しいメニューとか項目を作る時はResourceメニューを開けば大概何とか
なるはずです。

メニューアイテム(CutとかPasteとか)を挿入するのは新規に作ったアイテム
(一番下にできる)をドラッグして移動させればOK。

ここで注意。アイテムがない所をクリックするとResEditが自爆します。
う〜む。ResEdit、開発終了してるらしいけどこんなバグは直して欲しいぞ。

次にMBAR。さっき作ったMENUのリソースIDを並べるだけ。項目の追加・挿入は
1) *****とかの表示部分をクリックしてResourceメニューを使います。

ちなみにMBARはトップレベルのメニューのみ登録します。サブメニューがある
場合はどーするか。というと。

とりあえずMENUリソースでサブメニューのアイテムを作成。サブメニューの
タイトルは無視されるので適当に付ければ良いでしょう。
ここで。Resource->Get Resource InfoにてリソースのIDを変更(200とか)
します。

#いや別に変更しなくても一向に構わないのですが、自分が混乱しないように。
 という配慮です。

すると何やら文句を言うダイアログが出てきます。リソースIDとメニューIDが
違うけど、一致させるか?です。ややこしいことにメニューには2種類のIDが
あるんですね。んで、どっちを使えば良いのか。などとゆー下らないことに気を
使うのは馬鹿げているのでここは素直に一致させる[OK]を選んでおきます。

ちなみにMENU->Edit Menu & MDEF IDってメニューがありますのでご確認を。

で。親メニュー(サブメニューがあるメニューですぞ)のアイテムでhas Submenu
にチェック入れればIDを入れるところが出てくるので、サブメニューのID
(200とか)を入れる訳ですね。

ResEditでやることはここまで。後はソース。メニューを生成する部分はこんな
感じとなります。
サブメニューは挿入処理をしてやる必要があります。どこに挿入するか
(InsertMenu()の引数)は-1を指定すればサブメニューだな。とOSが判断して
くれます。
そしてそのサブメニューの親はどこだ。ってのは上に書いたような仕儀で自明
ということです。

#だったらサブメニューも自動的にロードせい!とか思うけど、これは必要な
 時にサブメニューをメモリにロードすることで少しでもメモリ使用量を
 減らしたい。という親心かな?
@仮想メモリやまして64Mや128M程度な物理メモリはあって当然。という昨今の
 情勢だと、まぁアレですけど

#define rMenu 128   // MBARリソースのID
#define mApple 128  // AppleメニューのID
#define mSUB 200    // サブメニューのID

で、main()の中でこうするんです。

    Handle menu;
    MenuHandle mh;
 
    // Create menu
    menu = GetNewMBar(rMenu);
    if (menu == nil) return 1;
 
    SetMenuBar(menu);
    DisposeHandle(menu);
    AppendResMenu(GetMenuHandle(mApple), 'DRVR');   
    /* いきなり'DRVR'とはどーゆーつもりか?とか思うけど、これで正解なん
       だからしょうがない(でもこれ、Cの文法ぢゃないよぉ) */
 
    // Adds submenus
    mh = GetMenu(mSUB);
    InsertMenu(mh, -1);
 
    DrawMenuBar();

さて。メニューはマウスとキーボードで操作できます。とゆーことは
それぞれでプログラムが必要。こんな感じ。

// mouse
Boolean doMouseDown(EventRecord *event)
{
    WindowPtr win;
 
    switch (FindWindow(event->where, &win)){
        case inMenuBar:
            doMenu(MenuSelect(event->where));
            break;
... (以下略)
 
// keyboard
Boolean doKeyEvents(EventRecord* event)
{
    // menu short cut
    if (event->modifiers & cmdKey){
        if (event->what == keyDown)
            doMenu(MenuKey(event->message & charCodeMask));
        return true;
    }
... (以下略)

で、メニュー自身の処理はとゆーと、こんなんです。
ちなみにアイテムの途中にセパレータバーがあるとメニューアイテム番号は
飛びます。どーゆーこっちゃといえば:
----------------------
○ File	FooBar
--------+-----+-------
	|hoge |
	|foo  |
	+-----+
	|bar  |
	+-----+
とゆーメニューがあった場合、fooのアイテム番号は2で、barは4。という
ことです。

#下のソースは上の図とは思いっきり違うメニュー構造を処理しています。
 念のため

#define mFile 129   // FileメニューのID
#define iAbout 1    // Appleメニューの最初のアイテム
#define iOpen 1     // Fileメニューの最初のアイテム
#define iQuit 2     // Fileメニューの次のアイテム
#define kAbout 130  // アバウトダイアログ(アラート)のリソースID
void doOpen(WindowPtr win); // メニューに応じて何かするルーチン
extern Boolean done;    // これがtrueになるとアプリケーション終了
 
// menu handling
void doMenu(int menu)
{
    short mitem;
    WindowPtr win;
    Str255 DAnam;
 
    // ToDo: enable/disable menu here
    GetPort(&win);
    mitem = LoWord(menu);
 
    switch (HiWord(menu)){
        case mApple:    // Appleメニュー
            switch (mitem){
                case iAbout:
                    Alert(kAbout, nil);
                    break;
                default:
                    GetMenuItemText(GetMenuHandle(mApple), mitem, DAnam);
                    OpenDeskAcc(DAnam);
            }
            break;
        case mFile: // Fileメニュー
            switch (mitem){
                case iOpen:
                    doOpen(win);
                    break;
                case iQuit:
                    done = true;
                    break;
        case mSub:  // サブメニュー
        switch (mitem){
        ... (中略)
            }
            break;
    }
    HiliteMenu(0);  // 反転しているメニューを元に戻す
}

(EOF)