X作成講座 on TC(7) 外部ウィンドウを作る(ドキュメント)


 外部命令の華(?)、外部ウィンドウです。

 外部ウィンドウを扱うにはポインタや構造体の扱いに精通している必要があり、またイベント駆動という概念も理解していなければなりません。仮にそれらがマスターできていたとしても、ウィンドウの管理はひとつのアプリケーションを作るくらいに面倒で大変な作業です。覚悟して下さい。

 取りあえずここではテンプレートとして使えそうなソースを挙げます。
 全てのイベントには対応していませんが、このソースを元にしていろいろな外部ウィンドウを作ることが出来ると思います。
 ここで作るのは「ドキュメントウィンドウ」です。パレットを作る方法はまた項を改めて説明しますが、小変更で対応できます。

// --------------------------------------
//    xDocWin template  1998.3.11 ( c )UDI
//         for THINK C 7.1
// --------------------------------------

#include "HyperXCmd.h"
#include "SetUpA4.h"

/* Prototypes */
void createWindow( XCmdPtr paramPtr );
void doEvent( XCmdPtr paramPtr );
WindowPtr getWinPtr( Str255 targetStrName );

pascal void  main( XCmdPtr paramPtr )
{
	RememberA0( );
	SetUpA4( );
    
	if ( paramPtr->paramCount > -1 )
	{
		createWindow( paramPtr );
	} else {
		doEvent( paramPtr );
	}

	RestoreA4( );
}

void  createWindow( XCmdPtr paramPtr )
{
	Str255		tempStr, winName;
	short		theLeft, theTop, theRight, theBottom, picID;
	Rect		winRect;
	PicHandle	myPicHand;
	WindowPtr	myWindowPtr;

	ZeroToPas( paramPtr, *paramPtr->params[0], winName );
	if( getWinPtr( winName ) ){
		SysBeep(1);
		return;
	}
	ZeroToPas( paramPtr, *paramPtr->params[1], tempStr );
	picID = StrToNum( paramPtr, tempStr );

	myPicHand = GetPicture( picID );
	if ( myPicHand == nil ){
		paramPtr->returnValue = PasToZero( paramPtr, "\pNot found pict" );
		return;
	}

	if ( paramPtr->paramCount > 2 ){
		ZeroToPas( paramPtr, *paramPtr->params[2], tempStr );
		StrToRect( paramPtr, tempStr, &winRect );
	} else {
		winRect = ( **myPicHand ).picFrame;
		OffsetRect( &winRect, 100,100 );    // move winRect anyplace
	}

	myWindowPtr = NewXWindow(paramPtr, &winRect, winName, false, 8, true, false);
	if ( myWindowPtr == nil ){
		paramPtr->returnValue = PasToZero( paramPtr, "\pCan't create win" );
		return;
	}
	SetWRefCon( myWindowPtr, ( long )myPicHand );
}

void  doEvent( XCmdPtr paramPtr ){

	GrafPtr			savePort;
	EventRecord		myEvent;
	WindowPtr		myWindowPtr;
	XWEventInfoPtr	myXWEventInfoPtr;
	PicHandle		myPicHand;

	myXWEventInfoPtr = ( XWEventInfoPtr)(paramPtr->params[0] );
	myWindowPtr = myXWEventInfoPtr->eventWindow;
	myEvent = myXWEventInfoPtr->event;

	GetPort( &savePort) ;
	SetPort( myWindowPtr );

	switch ( myEvent.what ){
		case mouseDown:
			switch ( FindWindow( myEvent.where, &myWindowPtr ) ){
				case inGoAway:		// closeBox
					if ( TrackGoAway( myWindowPtr, myEvent.where ) ){
						CloseXWindow( paramPtr, myWindowPtr );
					}
					break;

				case inDrag:		// titleBar
					paramPtr->passFlag = true;
					break;

					case inContent:		// window content
					SelectWindow( myWindowPtr );
					break;
			}
			break;

		case xOpenEvt:
			XWAllowReEntrancy(paramPtr, myWindowPtr, true, true);
			ShowWindow( myWindowPtr );
			paramPtr->passFlag = true;
			break;

		case xCloseEvt:
			paramPtr->passFlag = true;
			break;

		case updateEvt:
			BeginUpdate( myWindowPtr );
			myPicHand = ( PicHandle )GetWRefCon( myWindowPtr );
			DrawPicture( myPicHand, &( **myPicHand ).picFrame  );
			EndUpdate( myWindowPtr );  
			break;

		case activateEvt:
			break;

		case app4Evt:
			break;

		case xCursorWithin:
			paramPtr->passFlag = true;            // change arrowCursor by HC
			break;
		}

	SetPort( savePort );
}

WindowPtr getWinPtr( Str255 targetStrName ){
	Str255		titleStr;
	WindowPtr	theWinPtr;

	theWinPtr = ( WindowPtr)LMGetWindowList();
	while( theWinPtr ){
		GetWTitle( theWinPtr, titleStr );
		if( EqualString( targetStrName, titleStr, true, true ) ){
			return theWinPtr;
		}
		theWinPtr = ( WindowPtr)( ( ( CWindowPeek)theWinPtr )->nextWindow );
	}
	return ( WindowPtr)0;
}


 コンパイルする前にプロジェクト型の設定を忘れずに。リソースタイプは XCMD になります。
 呼び出すボタンのスクリプトは
    on mouseUp
      xWin "testWin", 1000, "100,100,200,200"
      if the result is not empty then answer the result
    end mouseUp

てな感じです。第一引数がウィンドウ名、第二引数が表示するピクトID、第三引数はウィンドウレクトです。第三引数を省略すると、ピクトの大きさのウィンドウを作ります。エラーは the result に返ってきます。


 まずおおまかな流れから理解して行きましょう。 

 外部ウィンドウを作る時に HyperTalk からXを呼び出しますが、Xはそれ以外に、そのウィンドウ向けのイベントが起きた時に HyperCard からも呼び出されます。

 Xで作った外部ウィンドウは、半ば HyperCard の管理下に置かれます。例えば通常のアプリケーションでは WaitNextEvent などを使ってイベントを取り出してそれに応じた処理をするのが普通ですが、外部ウィンドウでは HyperCard からイベントが送られて来ます。またウィンドウを閉じるときには CloseWindow は使わずに CloseXWindow というコールバックを使います。このコールバックは HyperCard に「このウィンドウを閉じてくれ」と要請するものです。

 X自身が HyperTalk から呼ばれたのか、それともイベントによって HyperCard から呼ばれたのかを調べるには paramPtr->paramCount の値を読みます。通常のX呼び出しではここに引数の数が入っていますから値は 0 から 15 までの間であるはずですが、HyperCard から呼ばれた場合はここが -1 になっています。 main ルーチンでは paramPtr->paramCount が -1 よりも大きいならウィンドウ作成ルーチンに、それ以外(つまり -1 )だった場合はイベント処理ルーチンに分岐するという処理をしています。


 ウィンドウ作成ルーチンは通常のアプリケーションでウィンドウを作るのと余り変わりません。 NewWindow の代わりにコールバックの NewXWindow を使います。また HyperTalk から渡された引数を読んで、Pict リソースのハンドルをウィンドウ構造体の refCon に保存しています。

 イベント処理ルーチンでは HyperCard から渡されたイベントを処理します。通常のX呼び出しでは paramPtr->params[0] に HyperTalk から渡される第1引数が入っていますが、イベント処理時はここからイベントの起きたウィンドウの WindowPtr と、実際のイベントの内容を持った EventRecord とを取り出ます。そしてそのイベントレコードからイベントの種類を取り出して、その処理をします。


 具体的にそれぞれのイベント処理を見て行きましょう。

 mouseDown: マウスボタンが押された時に送られてきます。通常のウィンドウでの処理と同じですが、クローズボックスがクリックされた時に CloseWindow ではなくコールバックの CloseXWindow を使っている点が異なります。クローズボックスやクリック、ウィンドウのドラッグなどの処理は全てここで行います。

 xOpenEvt: ウィンドウがオープンされた時に最初に1回だけ送られて来ます。ここでウィンドウの初期化などをします。

 xCloseEvt: HyperCard がウィンドウを閉じようとする時に送られて来ます。(ウィンドウを閉じる処理そのものは HyperCard が行います) paramPtr->passFlag に true を渡してXを抜ければ「クローズOK」になりますが、false を渡した場合はクローズ処理を拒否したことになりウィンドウは閉じられません。

 updateEvt: ウィンドウの再描画が必要な時に送られて来ます。普通のウィンドウの再描画処理と同じです。 BeginUpdate と EndUpdate で挟んで描画ルーチンを呼び出します。

 activateEvt: ウィンドウが最前面になった場合や、最前面から後ろへ回った時に送られて来ます。普通のウィンドウのアクティベート処理と同じです。最前面になって再描画が必要な場合は updateEvt も送られてくるので、再描画処理をここに書く必要はありません。ウィンドウがアクティブ(やデアクティブ)になった時に何か特別なことをしたい場合だけここにその処理を書きます。

 app4Evt: アプリケーションが最前面になった場合や、バックグラウンドに回った時に送られて来ます。普通のアクティベート処理と同じです。ハイパカがアクティブ(やデアクティブ)になった時に何か特別なことをしたい場合だけここにその処理を書きます。

 xCursorWithin: カーソルがウィンドウの領域内に入った時に送られて来ます。ここでカーソルの変更を行って paramPtr->passFlag に false を渡すと、ウィンドウ独自のカーソルを使うことが出来ます。true を渡した場合はカーソルの処理は HyperCard に任されます(通常は Arrow カーソルになります)。


 外部ウィンドウに独特のイベントは他にもいくつかあり、テキスト編集を支援するものもあります。これらについて詳しくは HyperCard のマニュアルの p538 からを参照して下さい。

 ウィンドウの扱いになれていない場合は「 Macintosh アプリケーションプログラミング」(新居雅行著)の「ウィンドウとコントロール」「イベント処理」「グラフィックス環境」辺りを眺めてみて下さい。
 またそれぞれのイベントの処理をひとつづつ削除したり、SysBeep(1) を入れたりして実験してみると、いつイベントが起きているのか、イベント処理をしないとどうなるのかが分かるようになり、イベント処理を理解する手がかりになると思います。

inserted by FC2 system
Next



THINK C Lab.

UDI's HomePage