X作成講座 on FB[08]  外部ウィンドウ パレット編[1]


 今回はパレットタイプのウィンドウを作って、そこにピクトリソースを表示します。


 ソース入力は全部で4つです。

 まず例によって HyperXcmd.MAIN を書き換えます。 XCMD 名は "PicPalette" とでもしましょう。 ID は tasu と重複しないものを付けておいて下さい。

 次にソースを分ける練習も兼ねて、自前のサブルーチンを myX.INCL というファイルに保存します。ソースが長くなる時はこのようにすると見通しが良くなります。

 構造体をひとつ宣言するので、myX.GLBL というファイルも作ってみましょう。構造体宣言やグローバル宣言のような各ファイルから参照されるものは、このように独立したファイルにしておくと管理し易くなります。

 最後にウィンドウのイベントを扱う HyperXcmd Events.INCL を書き換えます。外部ウィンドウの処理のほとんどはここに書くことになります。


 この章では HyperXcmd.MAIN の書き換えと、myX.INCL、myX.GLBL の作成までを扱います。イベント処理は次の章にて。

             −・−・−


 まずメインルーチンを書き換えましょう。

 HyperXcmd.MAIN の中で書き換えが必要なところを XonFB[02] に従って整えます。今回は XFCN ではなく XCMD として作ります。名前は "picPalette"、ID は tasu と重複しないものを付けて下さい。

 自前のルーチンを別ファイルに作りますので、

00047         CASE ELSE
00048                                             'handle normal xcmd parameters
 の 00048 行を
00048           FN myX( xCmdPtr&, str255$(0), str255$(1) )
 とします。HyperTalk から受け取った2つの引数をそのままサブルーチンに渡しています。 xCmdPtr& も一緒に渡すのをお忘れなく。

 そしてこのサブルーチンを書いたファイルを読み込むために、
00013 INCLUDE "HyperXcmd Events.INCL"             'load your own xcmd routines
 のあとに
      INCLUDE "myX.INCL"
 という一行を加えます。


             −・−・−


 次にサブルーチン本体を書く myX.INCL ファイルを作ります。
 ファイルメニューから「新規」を選び、これを myX.INCL という名前で一旦保存します。保存形式は「テキスト」ね。

 このファイルに以下のソースを入力して下さい。

00001 '
00002 ' myX.INCL
00003 '
00004 ' --------------------------------
00005 
00006 INCLUDE FILE _resIncl
00007 
00008 GLOBALS "HyperXcmd.GLBL"
00009 GLOBALS "myX.GLBL"
00010 END GLOBALS
00011 INCLUDE "HyperXcmd Glue.INCL"
00012 
00013 
00014 
00015 ' --------------------- FUNCTIONS -----------------------
00016 ' err message
00017 LOCAL FN setErr( xCmdPtr&, errMes$ )
00018   xCmdPtr&.returnValue& = FN PasToZero ( xCmdPtr&, errMes$ )
00019 END FN
00020 
00021 ' main function
00022 LOCAL FN myX( xCmdPtr&, tgPic$, myLoc$ )
00023   DIM picRect.8
00024   DIM myPt.4
00025   
00026   ' get PICT resource id
00027   CALL SETRESLOAD( _false )
00028   resHand& = FN GETNAMEDRESOURCE( _"PICT", tgPic$ )
00029   LONG IF resHand& = 0
00030     resHand& = FN GETRESOURCE( _"PICT", FN StrToNum( xCmdPtr&, tgPic$ ) )
00031   END IF
00032   CALL SETRESLOAD( _zTrue )
00033   LONG IF resHand& = 0
00034     FN setErr( xCmdPtr&, "Error : Not found PICT resource" )
00035     EXIT FN
00036   END IF
00037   CALL GETRESINFO( resHand&, resID%, resType&, title$ )
00038   '
00039   ' get PICT handle
00040   picHand& = FN GETPICTURE( resID% )
00041   LONG IF picHand& = 0
00042     FN setErr( xCmdPtr&, "Error : Not enough memory" )
00043     EXIT FN
00044   END IF
00045   picHand& = FN HANDTOHAND( picHand& )
00046   LONG IF picHand& = 0
00047     FN setErr( xCmdPtr&, "Error : Not enough memory" )
00048     EXIT FN
00049   END IF
00050   '
00051   ' get PICT rect
00052   picRect;8 = [ picHand& ] + _picFrame
00053   '
00054   ' create window
00055   xWndPtr& = FN NewXWindow ( xCmdPtr&, picRect, title$, _false, _palNoGrowProc_toggleHilite, _zTrue, _zTrue )
00056   LONG IF xWndPtr& = 0
00057     FN setErr( xCmdPtr&, "Error : Can't create window" )
00058     EXIT FN
00059   END IF
00060   '
00061   ' set window location
00062   FN StrToPoint( xCmdPtr&, myLoc$, myPt )
00063   CALL LOCALTOGLOBAL( myPt )
00064   CALL MOVEWINDOW( xWndPtr&, myPt.h, myPt.v, _false )
00065   '
00066   ' set my data to window's refCon
00067   myDataHand& = FN NEWHANDLE( 4 + 4 )
00068   myDataHand&..fPicHand& = picHand&
00069   creator$ = "Who"
00070   myDataHand&..fCreatorHand& = FN PasToZero( xCmdPtr&, creator$ )
00071   CALL SETWREFCON( xWndPtr&, myDataHand& )
00072 END FN
00073 

 では順を追って説明してみましょう。

00008 GLOBALS "HyperXcmd.GLBL"
00009 GLOBALS "myX.GLBL"
00010 END GLOBALS
00011 INCLUDE "HyperXcmd Glue.INCL"
 最初に必要なグローバル変数ファイルを読み込みます。 XCMD を作るときは HyperXcmd.GLBL は必須です。今回は myX.GLBL というファイル(あとで作ります)も読み込みます。
 HyperXcmd Glue.INCL をインクルードするのも XCMD ではオヤクソク。


00016 ' err message
00017 LOCAL FN setErr( xCmdPtr&, errMes$ )
00018   xCmdPtr&.returnValue& = FN PasToZero ( xCmdPtr&, errMes$ )
00019 END FN
 エラーメッセージを HyperTalk の読めるところに書き込むためのルーチンです。リソースを使わずに直接 HyperTalk に文字列を返したい時はこのようにします。今回は独自のエラーメッセージはこのルーチンを使って HyperTalk に返しています。他のテンプレートで処理しているメッセージはそのままです。(手抜きです(^^;))


00021 ' main function
00022 LOCAL FN myX( xCmdPtr&, tgPic$, myLoc$ )
00023   DIM picRect.8
00024   DIM myPt.4
 ここからサブルーチンがスタートします。 tgPic$ には HyperTalk から渡された第1引数が入ります。同様に、myLoc$ には第2引数の文字列が入ります。


00026   ' get PICT resource id
00027   CALL SETRESLOAD( _false )
00028   resHand& = FN GETNAMEDRESOURCE( _"PICT", tgPic$ )
00029   LONG IF resHand& = 0
00030     resHand& = FN GETRESOURCE( _"PICT", FN StrToNum( xCmdPtr&, tgPic$ ) )
00031   END IF
00032   CALL SETRESLOAD( _zTrue )
00033   LONG IF resHand& = 0
00034     FN setErr( xCmdPtr&, "Error : Not found PICT resource" )
00035     EXIT FN
00036   END IF
00037   CALL GETRESINFO( resHand&, resID%, resType&, title$ )
 この XCMD では第1引数にリソース名、もしくはリソース ID を渡すことが出来ます。指定されたリソースの ID を得るには、以下の手順を踏みます。

 まずリソース情報だけを読むために、最初に ToolBox の SetResLoad を呼び出して、「リソース本体をメモリに読み込まない」設定をします。こうすることで余計なディスクアクセスやメモリ移動を抑えられます。
 次に ToolBox の GetNamedResource を使って、tgPic$ という名前のリソースハンドルを得ます。 GetNamedResource はリソースを名前で検索するルーチンです。
 もしリソースハンドルがゼロなら該当する(指定した名前の)リソースが無いと言うことなので、次に ToolBox の GetResource を使ってリソースハンドルを得てみます。 GetResource は ID を使ってリソースを検索するルーチンです。この第2引数には StrToNum を使って tgPic$ を数値に変換したものを渡します。
  最後に SetResLoad を true に戻しておくのをお忘れなく。

 どちらの方法でもリソースが見つからない時は、エラーメッセージにその旨を書き込んで、サブルーチンを抜けます。

 こうして得られたハンドルを ToolBox の GetResInfo に渡すと、そのリソースの情報を各パラメータに得ることが出来ます。 resID% に返ってきた値が目的の PICT の ID ですね。


00039   ' get PICT handle
00040   picHand& = FN GETPICTURE( resID% )
00041   LONG IF picHand& = 0
00042     FN setErr( xCmdPtr&, "Error : Not enough memory" )
00043     EXIT FN
00044   END IF
00045   picHand& = FN HANDTOHAND( picHand& )
00046   LONG IF picHand& = 0
00047     FN setErr( xCmdPtr&, "Error : Not enough memory" )
00048     EXIT FN
00049   END IF
 先ほど得ておいた resID% を使って、ToolBox の GetPicture で PICT リソースを読み込みます。読み込んだ上で、更に ToolBox の HandToHand で複製しておきます。リソースのハンドルはいつ誰に破棄されるか分からないので、こうして独立したハンドルとして保持しておくのが確実です。
 FBの HandToHand の仕様はちょっと変わっていて、返値が複製後のハンドルになっています。 ToolBox の解説書で説明しているのとは動作が異なるので注意して下さい。
 メモリが足りなかったらその旨をエラーメッセージとして書き込んでサブルーチンを抜けます。こういう処理を省くとあとで痛い目に遭います。


00051   ' get PICT rect
00052   picRect;8 = [ picHand& ] + _picFrame
 ウィンドウの大きさを決定するために、読み込んだピクトのサイズを得ます。ピクトリソースはオフセット _picFrame の位置に自身のレクト情報を持っているので、これを picRect へ転送します。このレクトはそのままウィンドウの大きさとして使います。
 picRect;8 = アドレス (アドレスの位置から8バイトを picRect へ転送する)と言う書き方は最初ちょっと面食らいましたが、慣れると分かり易いかもですね。リファレンスの BLOCKMOVE の項参照。
 ピクトハンドルはピクト情報へのハンドル、つまり「ポインタのポインタ」ですから、ピクト情報へのポインタを得るために [ ] を使っています。
 希に原点のずれているピクトリソースがありますが、ここでは対応していません。普通に作ったピクトならこれで表示出来るはずです。


00054   ' create window
00055   xWndPtr& = FN NewXWindow ( xCmdPtr&, picRect, title$, _false, _palNoGrowProc_toggleHilite, _zTrue, _zTrue )
00056   LONG IF xWndPtr& = 0
00057     FN setErr( xCmdPtr&, "Error : Can't create window" )
00058     EXIT FN
00059   END IF
 実際にウィンドウを作っています。 ToolBox の解説書では NewWindow を使え、と書いてあると思いますが、XCMD ではグルールーチンの NewXWindow を使います。(同様に、ウィンドウの破棄は CloseWindow ではなく CloseXWindow です)  外部ウィンドウは HyperCard が管理するウィンドウなので、プログラマが勝手に作ったり破棄したりしてはいけません。 HyperCard に「作って下さい」「破棄して下さい」とお願いするのです。
 NewXWindow の引数については XonFB[06] で簡単に解説していますので参照して下さい。


00061   ' set window location
00062   FN StrToPoint( xCmdPtr&, myLoc$, myPt )
00063   CALL LOCALTOGLOBAL( myPt )
00064   CALL MOVEWINDOW( xWndPtr&, myPt.h, myPt.v, _false )
 この XCMD では第2引数にウィンドウの座標を指定することが出来ます。ここでは作成したウィンドウの座標を指定座標に移動する処理をしています。
 HyperTalk から渡された myLoc$ は座標を表す文字列です。最初にこれをグルールーチンの StrToPoint を使って座標値( myPt )に直します。
 次にそれを ToolBox の LocalToGlobal でグローバル座標に変換して、ToolBox の MoveMindow に渡します。
 ウィンドウ作成時に visible を false にして作りましたので、この時点ではウィンドウは画面に見えていません。ウィンドウの表示は全ての準備が整ってからですね。


00066   ' set my data to window's refCon
00067   myDataHand& = FN NEWHANDLE( 4 + 4 )
00068   myDataHand&..fPicHand& = picHand&
00069   creator$ = "Who"
00070   myDataHand&..fCreatorHand& = FN PasToZero( xCmdPtr&, creator$ )
00071   CALL SETWREFCON( xWndPtr&, myDataHand& )
 さてこれがまたちょっとややこしいのですが・・・ ToolBox の SetWRefCon を使って、ウィンドウの "refCon" に独自のデータのハンドルを書き込んでいます。

 ウィンドウにピクトを表示するにはピクトハンドルが必要ですが、この情報をどこに保存したらいいでしょう? グローバル変数? 残念ながら、外部ウィンドウのイベント処理ではグローバル変数が使えません。イベント処理時にちゃんとデータを読めるのは、実はこの refCon だけなのです。

 refCon は「ウィンドウごとに」4バイトの領域を持っています。この4バイトを何に使うかはプログラマ次第です。例えば2バイトの情報を2つ入れてもいいし、4バイトのデータを直接入れることも出来ます。今回のように4バイト以上のデータを入れたい場合は、データをハンドルに持たせて、そのハンドル(これも4バイト)を入れることになります。

 今回はピクトハンドル(4バイト)の情報と、練習用に "Who" という文字列を入れたハンドル(4バイト)をウィンドウに持たせます。この自前のデータ構造にアクセスしやすくするために、次に説明する myX.GLBL で
DIM RECORD myDataRec
  DIM fPicHand&
  DIM fCreatorHand&
DIM END RECORD .myDataRec
 というレコードを宣言します。

 プログラムでは、まず ToolBox の NewHandle で8バイトの領域を確保し、fPicHand& フィールドにピクトハンドル、fCreatorHand& フィールドに文字列ハンドルを入れています。ハンドル経由でフィールドにアクセスする時はドット (.) を2つ使います。

 これらのデータをいつどう使うのかは、次回解説するイベント処理( HyperXcmd Events.INCL )を見て下さい。


 myX.INCL は以上で終わりなので保存して閉じて下さい。


             −・−・−


 構造体をひとつ宣言するために、GLBL ファイルをひとつ作ります。
 ファイルメニューから「新規」を選び、これを myX.GLBL という名前で一旦保存します。保存形式は「テキスト」。

 このファイルに以下のソースを入力して下さい。

'
' myX GLBL
'-------------------------------

DIM RECORD myDataRec
  DIM fPicHand&
  DIM fCreatorHand&
DIM END RECORD .myDataRec
 なぜ myX.INCL 内に入れてしまわないかと言うと、この構造体はイベント処理時にも使うので、HyperXcmd Events.INCL からも読み込むからです。いくつかのファイルから共通で利用するグローバル変数や構造体は、このように別ファイルで管理するのが良いですね。


             −・−・−


 ここまででウィンドウの作成が済みました。外部ウィンドウを(見えない状態で)作り、(見えない状態のまま)指定座標に移動し、独自のデータを書き込んでいます。ピクトはまだ表示しません。このへんがイベント駆動の面白いところですね。

 次回 HyperXcmd Events.INCL にイベント処理本体を書いてソース入力は終了です。ウィンドウプログラミングは実はこっち(イベント処理)が本体とも言えます。



inserted by FC2 system
Next



FutureBASIC Lab.

UDI's HomePage