Alertでトラップ(for only KT7.5.5)

 手元にOS 8.xが転がっているなら、「おいらのプログラムはOS 8.x以降だけを
サポートするよん(^^;;」とかうそぶいていられるのだが……。遺憾なことに、
自宅にあるのはKT7.5.5のみ。
 会社のOS 9なマシンでは何の問題もないぞ。ってのも何か腹立たしいので、対策
してみました。

 以下はつまりOS 8以降なら(OS 9以降か?もしかして)不要な処理ってこと。

ここで問題点とは:
 「アラート表示中にESCキーを押しても(KT7だと)何も起こらない」

#実際にはESCだけでなくCommand + '.'にも対処する必要あり

===
 解決策はあります。前のソースでxxAlert(128, nil);って呼び出しをしてた
でしょう?
 「128」ってのはリソースの番号。それはよろしいですよね?では。nilは何かと
言えば、コールバック関数(Macではフィルタ関数と呼ぶようですが)のアドレスなん
ですな(今更な蛇足ですが、nilってのは「0」の別名です)。

@コールバック関数って何?
 OSが勝手に何かやる前に(プログラマが)ちょっかいかけられる仕組み。ですな。
 MacでもWindowsでもこういうのはまま、あります。ANSI Cでもqsortとかでコール
 バック関数が要求されます。OSなり、コンパイラなりに、必要なら呼び出して
 ね(はーと)とお願いするという代物です。

 以下のソースは最初の、コールバック付きアラートの結果を次のコールバックなしの
アラートで表示する。という極簡単なサンプルです。

 ちなみにこのソース、68Kでコンパイルしないと動かないと思います(たとえPPCマシンで
あっても)。やってみてないので本当かどうかは闇の中。ですけど。

 で。普通Enter押すと[OK]ボタンが一瞬反転する。というリアクションが表示されるで
しょう?
 Alert()の第2引数がnilの場合はプログラマは何もする必要、ありません。が。
コールバ、、あ。いえフィルタ関数を指定した場合、プログラマがその処理も肩代わり
してやんなきゃならないようなんですな。

#信じられない。とか思ったけど、本当みたい。参考文献のサンプルもそーやってるし

 そのための関数がpushButton()。ちなみに反転時間である「8」というのはOSで定めて
ある定数。確かUIガイドとかにもこの「8」という数字が直接書いてあった。

#何故、そんな大事な定数をユーザプログラム内で#defineしなきゃならんのだ?とか
 思う?いや。私も同感なんだが……。そーゆーもんらしい。深く追求してないんで、
 システムのincludeファイルのどっかに定義してあるのかもしれないけど。

 余談だけど引数がintでなくshortってAPIが多いのはやっぱ128K Macな環境を連綿と
引きずっているからなんでしょうねぇ。

/*
    alerts
*/
 
#include <limits.h>
#include <Resources.h>
#include <Windows.h>
//#include <Menus.h>
#include <Dialogs.h>
//#include <Devices.h>
//#include <ToolUtils.h>
//#include <Memory.h>
//#include <Processes.h>
//#include <SegLoad.h>
//#include <Packages.h>
//#include <Traps.h>
 
QDGlobals qd;
 
/* OS defined constants */
#define kVisualDelay 8  // Key push wait
 
/*
    emulate button push
*/
static void pushButton(DialogPtr dlg, short item)
{
    short cTyp;
    ControlHandle hCtl;
    Rect rc;
    unsigned long dmy;
 
    GetDialogItem(dlg, item, &cTyp, (Handle*) &hCtl, &rc);
    HiliteControl(hCtl, 1);
    Delay(kVisualDelay, &dmy);
    HiliteControl(hCtl, 0);
}
 
/*
    modal dialog filter
*/
static Boolean pascal dlgProc(DialogPtr theDlg, EventRecord *getEv, short *item)
{
    //short iType;
    //Handle hCtl;
    //Rect rc;
 
    *item = 0;
    if (getEv->what == keyDown){
        switch (getEv->message & charCodeMask){
            case kEnterCharCode:        // Enter
            case kReturnCharCode:       // Return
                *item = ok;     // == 1
                break;
 
            case '.':                   // Command + '.'
                if (!(getEv->modifiers & cmdKey)) break;
            case kEscapeCharCode:       // ESC
                *item = cancel; // == 2
        };
        if (*item != 0){
            pushButton(theDlg, *item);
            return true;
        };
 
/*      if ((getEv->message & keyCodeMask) == 0x3500){  // ESC
            //GetDialogItem(theDlg, 2, &iType, &hCtl, &rc);
            //HiliteControl((ControlHandle) hCtl, 10);
            *item = cancel+3;
            return true;
        };
        switch (getEv->message & keyCodeMask){
            case 0x4C00:    // enter  (13)
            case 0x2400:    // return (3)
                //GetDialogItem(theDlg, 1, &iType, &hCtl, &rc);
                //HiliteControl((ControlHandle) hCtl, 10);
                // *item = ok+3;
                *item = getEv->message & charCodeMask;
                return true;
        };
        if (((getEv->message & charCodeMask) == '.') &&
            (getEv->modifiers & cmdKey)) {
            *item = cancel;
            return true;
        }*/
    };
    //theDlg = theDlg; // dummy for warning
    return false;
}
 
int main(void)
{
    int iRet = 17;  // no mean
    Str15 s;
 
    InitGraf(&qd.thePort);
    InitFonts();
    InitWindows();
    InitCursor();
 
    ParamText("\pfoo", "\pCaution", "\p", "\p");
    iRet = CautionAlert(128, dlgProc);
 
    //sprintf((char*)s, ".#=%d", iRet);
    //s[0] = (unsigned char) strlen((char*) s+1);
    sprintf((char*)s, "#=%d", iRet);
    c2pstr(s);  // C文字列からPascal文字列への変換。sprintf()の引数をコメントと
                // なっている2行と比較のこと!先頭の'.'がない点に注目
    ParamText(s, "\pNote", "\p", "\p");
    iRet = NoteAlert(128, nil);
 
    //ExitToShell();
    return 0;
}
 
/* eof */

 さて。このソースをコンパイルしてお試しあれ。KT7.5.5でも今度はちゃんとESCに
応答するでしょう?

#え?動かないって?う〜む。もしかしてPPCマシンですか?だったら当然かも。68Kで
 コンパイルしてもらえます?
 68Kマシンでは何も考えなくても良いソースがPPCマシンでは細工、しなきゃ動かない。って
 ことがあるようですから(コールバック関数とか……)。
#でもね。68Kでコンパイルしたら実行時エラーでOSが死にまくるのに、PPCでコンパイル
 したら何事もなく正常動作する。ってのも実際体験してる。
 …これがまた、sprintf()なんだよな。こんな基本的なものでバグを出すってのは余程の間抜け
 だろうから、それはつまりあたしのミスなんだろうが良く分からない。

 ところで。ResEditでアラートリソースを定義している時に、
ALRT->Set 'ALRT' Stage Infoを開いてみたかな?同じアラートが何度も出る場合に
デフォルトボタンを変更したり、そもそも同じアラートが何度も出ること自体を
抑制できる。という機能です。
 こんな機能、Windowsにはないです。う〜ん。便利と言うか何と言うか。

#余り何度もこれを実行してると何故かOSがすっ飛ぶみたい。何か非常に基本的なミスを犯して
 いるのでは?とか思ってますが、細かい所を気にしてても先に進めないので今は無視します。
 (正しい理由をご存じの方はメールして教えて下さいませm(_o_)m)

#ごちゃごちゃやってる部分がコメントになってますが、試行錯誤で悪戦苦闘した
 様子をそのままお目にかけております(笑)
 初めてのOSで初めての処理を作る場合、(あっしの場合は)これぐらい紆余曲折
 するってことで(苦笑)

(EOF)